JedWatson/keystone — pull request #484 by JohnnyEstilles (2014-07-22 08:45:21+00:00)
Title: Feature/cloudinary/adding select from cloudinary

This PR adds the ability to select an existing image from your Cloudinary account, instead of having to upload one. I developed this feature because I had a use case in which I needed to assign several hundred images to Keystone list items. Instead of uploading the images individually using the Keystone Admin UI, I uploaded all the images using the Cloudinary Media Library interface (literally dragged-and-dropped them all), then used this feature to select them from the already uploaded images.

To enable this feature just set the `select` option to `true` in the CloudinaryImage field definition as follows:

List.add({
...
image: { type: Types.CloudinaryImage, select: true },
...
});

By default, this feature will only show images within the field's designated Cloudinary folder (see #456), or all uploaded images if the Keystone `cloudinary folders` option is not enabled. If you prefer to select images from a different folder (i.e. with a `public_id` that begins with a prefix other than designated folder) then you can explicitly specify a prefix using the `selectPrefix` option.

image: { type: Types.CloudinaryImage, select: true, selectPrefix: 'path/to/images' }

The `selectPrefix` feature works regardless of whether `cloudinary folders` is or not enabled.

The screenshot below shows an example of image being selected from Cloudinary. In this particular example, the `selectPrefix` is set to `temox/vendors/logo`.

![select-1](https://cloud.githubusercontent.com/assets/5514878/3653543/082b2f6a-1155-11e4-8c73-02435392361e.jpg)

This next screenshot shows the resulting view once an image is selected.

![select-2](https://cloud.githubusercontent.com/assets/5514878/3653544/0fc41f98-1155-11e4-8c73-7c50882021c4.jpg)

Here's a breakdown of the changes I made to implement this feature.

`lib/core/routes.js`

* Added routes to populate the data of the Select2 element

`lib/fieldTypes/cloudinaryimage.js`

* Refactored the code that determines the designated Cloudinary folder into a `.folder` virtual which I use in `templates/fields/cloudinaryimage/form.jade` to determine the default `prefix`
* Added a `_select` virtual to allow access to the `public_id` of the selected image.
* Added code to set the item field data from the selected image.
* Replace the code in `.getRequestHandler()` used to implement the `cloudinary folder` feature with a call to the refactored `.folder` virtual

`public/js/common/ui-cloudinaryimage.js`

* Added code to handle the Select2 element (NOTE: The Select2 query filter is applied in then `ajax.results` function because the Cloudinary API does not provide a good way to filter the resource list, other than by prefix.)
* Refactored the code in `$upload.change()` that checks "If we already have an image" into a `checkExistingImage()` function I reuse in my Select2 handling code

`routes/api/cloudinary.js`

* Added `autocomplete` and `get` API functions which are used to populate the Select2 data
* The existing code was indented with spaces instead of tabs (as per the Keystone de facto coding standards), so I went ahead and replaced them (a little bit of cleanup is never bad).

`templates/fields/cloudinaryimage/form.jade`

* Added HTML elements (`.image-select` and `.select-queued`) to implement his feature

`public/styles/keystone/forms.less`

* Added formatting for the HTML elements added in `templates/fields/cloudinaryimage/form.jade`

That's it in a nutshell! As usual, I'm open to suggestions, so feel free to tweak away.