Skip to content

Commit

Permalink
Merge pull request #8 from boundless-inc/filestack-transformation-sup…
Browse files Browse the repository at this point in the history
…port

Filestack Transformations Support
  • Loading branch information
mminkoff authored May 23, 2018
2 parents a7b03b3 + 1185908 commit 7747187
Show file tree
Hide file tree
Showing 6 changed files with 450 additions and 161 deletions.
191 changes: 140 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
# Ember-filestack
# ember-filestack

[![npm version](https://badge.fury.io/js/ember-filestack.svg)](http://badge.fury.io/js/ember-filestack)
[![Build Status](https://travis-ci.org/mminkoff/ember-filestack.svg)](https://travis-ci.org/mminkoff/ember-filestack.svg?branch=master)
[![Ember Observer Score](http://emberobserver.com/badges/ember-filestack.svg)](http://emberobserver.com/addons/ember-filestack)

Provides file picking, storing, and converting funtionality from [Filestack](https://www.filestack.com) using v3+ of their API.
Provides file picking, storing, and converting funtionality from [Filestack](https://www.filestack.com) using [v3+ of their API](https://www.filestack.com/docs/javascript-api/pick-v3).

This addon borrows from and builds heavily on [Ember-cli-filepicker](https://github.com/DudaDev/ember-cli-filepicker)
This addon borrows from and builds heavily on [ember-cli-filepicker](https://github.com/DudaDev/ember-cli-filepicker)

## Installation

* `ember install ember-filestack`

## Usage
## Configuration
### API Key
* Create your filestack.com key here: https://www.filestack.com/.
* Add your filestack.com key in your config/environment.js
```javascript
```js
//config/environment.js
module.exports = function(environment) {
var ENV = {
Expand All @@ -25,19 +26,48 @@ module.exports = function(environment) {
//...
}
```
* Use the filestack.com documentation for options like extensions and services.
* In your template:

### Custom CDN
To minimize your quota usage, you can put filestack behind your own CDNs.

There are two distinct filestack URL's you may want to proxy to.

#### https://cdn.filestackcontent.com
This is where files uploaded to Filestack live. [Filestack's CDN](https://www.filestack.com/features/cdn) is excellent, but you may want to configure a proxy to this origin in order to preserve CDN bandwidth on your Filestack account.

#### https://process.filestackapi.com
This is the entry point for dynamically transforming files. Each transformation produces a unique URL, but each `GET` to one of these URLs will count against your transformation quota regardless of whether or not it’s already been performed. Putting your own CDN in front of processing urls will dramatically reduce the number of transformation requests you make while still giving you the full power of Filestack’s Transformation APIs.

To preserve your quotas, setup two CDNs (e.g., AWS CloudFront) to point to these URLs. Then configure `ember-filestack` to use your CDNs in ENV:
```js
module.exports = function(environment) {
var ENV = {
//...
filestackProcessCDN: '<your-process-cdn.cloudfront.net>',
filestackContentCDN: '<your-content-cdn.cloudfront.net>',
};
```
We recommend that you set up both of these CDNs because `ember-filestack` will ensure that Filestack’s CDN and Transformation API are never accessed directly.
## Usage
### File Selection/Upload
* Use the [Filestack Pick Documentation](https://www.filestack.com/docs/javascript-api/pick-v3) to determine what `options` you want to configure.
#### Template
```handlebars
{{ember-filestack-picker options=options onSelection=(action 'fileSelected') onClose=(action 'onClose') onError=(action 'onError')}}
{{ember-filestack-picker options=filestackPickerOptions onSelection=(action 'fileSelected') onClose=(action 'onClose') onError=(action 'onError')}}
```
* You then pass the options and actions to determine the picker's behaviour.
#### Component
```js
export default Ember.Component.extend({
options: {
accept: ['image/*'],
maxSize: 10485760
},
const filestackPickerOptions = {
accept: ['image/*'],
maxSize: 10485760
};

export default Component.extend({
filestackPickerOptions,
actions: {
fileSelected(result) {
// the `filesUploaded` is an array of files you've just uploaded
Expand All @@ -52,40 +82,103 @@ export default Ember.Component.extend({
}
});
```
* Complete documentation of all the available options and response data can be found [here](https://www.filestack.com/docs/javascript-api/pick-v3).
Complete documentation of all the available options and response data can be found [here](https://www.filestack.com/docs/javascript-api/pick-v3).
## Custom CDN
To minimize your quota usage, you can put filestack behind your own CDNs.

There are two distinct filestack URL's you may want to proxy to.

1. https://process.filestackapi.com
2. https://cdn.filestackcontent.com

The first is for image transformations (the `{{filestack-image}}` helper lets you easily resize any image -- even arbitrary image URLs). But it renders a URL to that transformation each time, so any time someone visits a page with an image resized (eg https://process.filestackapi.com/resize=width:750/2h25ZGRHTfmQ2DBEt3yR), that will count against your transformation quota. Same for your bandwidth quota, when visiting a page that renders images with https://cdn.filestackcontent.com.

To preserve your quotas, setup two CDNs (eg cloudfront) to point to these URLs. Then configure `ember-filestack` to use your CDNs in ENV:
```javascript
module.exports = function(environment) {
var ENV = {
//...
filestackKey: '<your-filestack-key>'
filestackProcessCDN: '<your-process-cdn.cloudfront.net>',
filestackContentCDN: '<your-content-cdn.cloudfront.net>',
};
### Display
#### Template Examples
The `filestack-image` helper accepts a filestack handle or any image url plus any number of [transformations](#transformations) in the format `transformationName=(hash ...)`.
```handlebars
{{filestack-image filestackHandleOrUrl}}
{{filestack-image filestackHandleOrUrl resize=(hash width=50 height=50 fit='scale')}}
{{filestack-image filestackHandleOrUrl output=(hash format='jpg')}}
{{filestack-image filestackHandleOrUrl output=(hash format='png') resize=(hash width=500 height=500 fit='max')}}
```
This way, identical image process or display requests will be cached & come from your CDN, thus saving your quotas on filestack.
#### Component Examples
```js
export default Component.extend({
filestack: service(),
scaledImage: computed('imageUrl', function() {
let url = this.get('imageUrl');
let transformations = {
output: {
format: 'png',
},
resize: {
width: 500,
height: 500,
fit: 'max',
},
};

return this.get('filestack').imageUrl(url, transformations);
}),
});
```
## Notes
In order to have access to the `filestack` instance you can:
* If `Ember.inject.service` is supported then in your object you can use:
```javascript
export default Ember.Component.extend({
### Transformations
The Filestack Transformations API provides a way to dynamically request a transformed version of any file (even ones not uploaded to Filestack).
#### Image
[Filestack Image Transformation Docs](https://www.filestack.com/docs/image-transformations)
You can use any transformation, but be aware that it is your responsibility to produce a valid transformation. For example, Filestack will return a `400 Bad Request` if you attempt to resize an SVG.
Below you’ll find a list of transformations and notes on any further handling of the transformation that `ember-filestack` currently does.
| Transformation | Notes |
|:--------|:------------|
| [ascii](https://www.filestack.com/docs/image-transformations/ascii) | |
| [blackwhite](https://www.filestack.com/docs/image-transformations/filters#blackwhite) | |
| [blur_faces](https://www.filestack.com/docs/image-transformations/facial-detection#blur_faces) | |
| [blur](https://www.filestack.com/docs/image-transformations/filters#blur) | |
| [border](https://www.filestack.com/docs/image-transformations/borders-and-effects#border) | |
| [cache](https://www.filestack.com/docs/image-transformations/caching) | |
| [circle](https://www.filestack.com/docs/image-transformations/borders-and-effects#circle) | |
| [collage](https://www.filestack.com/docs/image-transformations/collage) | |
| [compress](https://www.filestack.com/docs/image-transformations/compress) | |
| [crop_faces](https://www.filestack.com/docs/image-transformations/facial-detection#crop_faces) | |
| [crop](https://www.filestack.com/docs/image-transformations/crop) | |
| [debug](https://www.filestack.com/docs/image-transformations/debug) | |
| [detect_faces](https://www.filestack.com/docs/image-transformations/facial-detection#detect_faces) | |
| [enhance](https://www.filestack.com/docs/image-transformations/enchancements#enhance) | |
| [flip](https://www.filestack.com/docs/image-transformations/rotate#flip) | |
| [flop](https://www.filestack.com/docs/image-transformations/rotate#flop) | |
| [modulate](https://www.filestack.com/docs/image-transformations/filters#modulate) | |
| [monochrome](https://www.filestack.com/docs/image-transformations/filters#monochrome) | |
| [negative](https://www.filestack.com/docs/image-transformations/filters#negative) | |
| [oil_paint](https://www.filestack.com/docs/image-transformations/filters#oil_paint) | |
| [output](https://www.filestack.com/docs/image-transformations/conversion) | |
| [partial_blur](https://www.filestack.com/docs/image-transformations/filters#partial_blur) | |
| [partial_pixelate](https://www.filestack.com/docs/image-transformations/filters#partial_pixelate) | |
| [pixelate_faces](https://www.filestack.com/docs/image-transformations/facial-detection#pixelate_faces) | |
| [pixelate](https://www.filestack.com/docs/image-transformations/filters#pixelate) | |
| [polaroid](https://www.filestack.com/docs/image-transformations/borders-and-effects#polaroid) | |
| [quality](https://www.filestack.com/docs/image-transformations/quality) | |
| [redeye](https://www.filestack.com/docs/image-transformations/enhancements#redeye) | |
| [resize](https://www.filestack.com/docs/image-transformations/resize) | A resize transformation will not be attempted unless `width` or `height` is provided. |
| [rotate](https://www.filestack.com/docs/image-transformations/rotate#rotate) | |
| [rounded_corners](https://www.filestack.com/docs/image-transformations/borders-and-effects#rounded-corners) | |
| [security](https://www.filestack.com/docs/image-transformations/security) | |
| [sepia](https://www.filestack.com/docs/image-transformations/filters#sepia) | |
| [shadow](https://www.filestack.com/docs/image-transformations/borders-and-effects#shadow) | |
| [sharpen](https://www.filestack.com/docs/image-transformations/filters#sharpen) | |
| [store](https://www.filestack.com/docs/image-transformations/store) | |
| [torn_edges](https://www.filestack.com/docs/image-transformations/borders-and-effects#torn-edges) | |
| [upscale](https://www.filestack.com/docs/image-transformations/enhancements#upscale) | |
| [urlscreenshot](https://www.filestack.com/docs/image-transformations/screenshot) | |
| [vignette](https://www.filestack.com/docs/image-transformations/borders-and-effects#vignette) | |
| [watermark](https://www.filestack.com/docs/image-transformations/watermark) | |
| [zip](https://www.filestack.com/docs/image-transformations/zip) | |
### Direct Filestack JS API Access
In order to access the browser’s `filestack` instance we recommend that you use `filestack.promise`.
```js
export default Component.extend({
//injecting the filestack object
filestack: Ember.inject.service(),
filestack: service(),

someFunction: function(){
// Use the promise in case you are not sure that your component will be initialized after filestack has been loaded
Expand All @@ -99,19 +192,15 @@ export default Ember.Component.extend({
});
```
## Running
## Contributing - Pull Requests Welcome!
### Adding Transformations
There are nearly forty transformations for images alone. If you’re looking for a place to contribute consider adding a transformation builder.
* `ember serve`
* Visit your app at [http://localhost:4200](http://localhost:4200).
A transformation builder accepts an `options` object (e.g., `{ "option1": 123, "option2": false }`) and returns a Filestack-compliant options string (e.g., `"option1:123,option2:false"`).
## Running Tests
Adding custom transformation builders `ember-filestack` can prevent users from creating invalid transformations. See the `transformationBuilders` object in the `filestack` service for examples.
### Tests
* `npm test` (Runs `ember try:each` to test your addon against multiple Ember versions)
* `ember test`
* `ember test --server`
## Building
* `ember build`
For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/).
63 changes: 4 additions & 59 deletions addon/helpers/filestack-image.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,13 @@
import Ember from 'ember';

const {
computed,
computed: { reads },
getOwner,
Helper,
inject: { service },
} = Ember;

const defaultProcessUrl = "https://process.filestackapi.com";
const defaultContentUrl = "https://cdn.filestackcontent.com";
const contentUrlRegex = new RegExp(defaultContentUrl);

export default Helper.extend({
config: computed(function() {
return getOwner(this).resolveRegistration('config:environment');
}),
key: reads('config.filestackKey'),
customProcessUrl: reads('config.filestackProcessCDN'),
customContentUrl: reads('config.filestackContentCDN'),
processUrl: computed(function(){
return this.get('customProcessUrl') || defaultProcessUrl;
}),
contentUrl: computed(function() {
return this.get('customContentUrl') || defaultContentUrl;
}),
compute([token], hash) {
let options = [];
let urlRoot, imageUrl;
if(!token) {
return '';
}

if(token.match(/http(s?):\/\//)) {
imageUrl = token;
if(this.get('contentUrl') !== defaultContentUrl && imageUrl.match(contentUrlRegex)) {
// avoid unnecessary hits to bandwitch quota
imageUrl = imageUrl.replace(defaultContentUrl, this.get('contentUrl'));
}
urlRoot = `${this.get('processUrl')}/${this.get('key')}`;
} else {
imageUrl = `${this.get('contentUrl')}/${token}`;
urlRoot = this.get('processUrl');
}

// if there is no width AND no height specified, there's no valid resize to do
if(!hash.width && !hash.height) {
return imageUrl;
}

Object.keys(hash).forEach((key) => {
let value = hash[key];
if(value) {
options.push(`${key}:${value}`);
}
});

options = `resize=${options.join(',')}`;

return [
urlRoot,
options,
token,
].filter((element) => {
return element;
}).join('/');
filestack: service(),
compute([handleOrUrl], transformations) {
return this.get('filestack').imageUrl(handleOrUrl, transformations);
}
});
Loading

0 comments on commit 7747187

Please sign in to comment.