Skip to content

Commit

Permalink
feat: add cloudinary support
Browse files Browse the repository at this point in the history
  • Loading branch information
erquhart committed Dec 4, 2018
1 parent 53c0094 commit b2f84a7
Show file tree
Hide file tree
Showing 18 changed files with 315 additions and 23 deletions.
10 changes: 9 additions & 1 deletion dev-test/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ display_url: https://example.com

publish_mode: editorial_workflow
media_folder: assets/uploads
media_library:
name: cloudinary
config:
cloud_name: erquhart
api_key: 293667689287914
multiple: true
default_transformations:
- quality: auto
- format: auto

collections: # A list of collections the CMS should be able to edit
- name: 'posts' # Used in routes, ie.: /admin/collections/:slug/edit
Expand All @@ -31,7 +40,6 @@ collections: # A list of collections the CMS should be able to edit
widget: 'image'
required: false
tagname: ''

- { label: 'Body', name: 'body', widget: 'markdown', hint: 'Main content goes here.' }
meta:
- { label: 'SEO Description', name: 'description', widget: 'text' }
Expand Down
4 changes: 2 additions & 2 deletions packages/netlify-cms-core/src/actions/mediaLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ export function openMediaLibrary(payload = {}) {
const state = getState();
const mediaLibrary = state.mediaLibrary.get('externalLibrary');
if (mediaLibrary) {
const { controlID: id, value, config = Map(), forImage } = payload;
mediaLibrary.show({ id, value, config: config.toJS(), imagesOnly: forImage });
const { controlID: id, value, config = Map(), allowMultiple, forImage } = payload;
mediaLibrary.show({ id, value, config: config.toJS(), allowMultiple, imagesOnly: forImage });
}
dispatch({ type: MEDIA_LIBRARY_OPEN, payload });
};
Expand Down
11 changes: 9 additions & 2 deletions packages/netlify-cms-core/src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,12 @@ export const selectUnpublishedEntriesByStatus = (state, status) =>
export const selectIntegration = (state, collection, hook) =>
fromIntegrations.selectIntegration(state.integrations, collection, hook);

export const getAsset = (state, path) =>
fromMedias.getAsset(state.config.get('public_folder'), state.medias, path);
export const getAsset = (state, path) => {
/**
* If an external media library is in use, just return the path.
*/
if (state.mediaLibrary.get('externalLibrary')) {
return path;
}
return fromMedias.getAsset(state.config.get('public_folder'), state.medias, path);
};
3 changes: 3 additions & 0 deletions packages/netlify-cms-editor-component-image/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const image = {
label: 'Image',
name: 'image',
widget: 'image',
media_library: {
allow_multiple: false,
},
},
{
label: 'Alt Text',
Expand Down
23 changes: 23 additions & 0 deletions packages/netlify-cms-media-library-cloudinary/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

<a name="0.2.0"></a>
# 0.2.0 (2018-09-06)


### Features

* **media:** add external media library support, Uploadcare integration ([#1602](https://github.com/netlify/netlify-cms/tree/master/packages/netlify-cms-media-library-uploadcare/issues/1602)) ([0596904](https://github.com/netlify/netlify-cms/tree/master/packages/netlify-cms-media-library-uploadcare/commit/0596904))




<a name="0.2.0"></a>
# 0.2.0 (2018-09-06)


### Features

* **media:** add external media library support, Uploadcare integration ([#1602](https://github.com/netlify/netlify-cms/tree/master/packages/netlify-cms-media-library-uploadcare/issues/1602)) ([0596904](https://github.com/netlify/netlify-cms/tree/master/packages/netlify-cms-media-library-uploadcare/commit/0596904))
11 changes: 11 additions & 0 deletions packages/netlify-cms-media-library-cloudinary/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Docs coming soon!

Netlify CMS was recently converted from a single npm package to a "monorepo" of over 20 packages.
That's over 20 Readme's! We haven't created one for this package yet, but we will soon.

In the meantime, you can:

1. Check out the [main readme](https://github.com/netlify/netlify-cms/#readme) or the [documentation
site](https://www.netlifycms.org) for more info.
2. Reach out to the [community chat](https://gitter.im/netlify/netlifycms/) if you need help.
3. Help out and [write the readme yourself](https://github.com/netlify/netlify-cms/edit/master/packages/netlify-cms-media-library-cloudinary/README.md)!
35 changes: 35 additions & 0 deletions packages/netlify-cms-media-library-cloudinary/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "netlify-cms-media-library-cloudinary",
"description": "Cloudinary integration for Netlify CMS",
"version": "0.1.0",
"repository": "https://github.com/netlify/netlify-cms/tree/master/packages/netlify-cms-media-library-cloudinary",
"bugs": "https://github.com/netlify/netlify-cms/issues",
"main": "dist/netlify-cms-media-library-cloudinary.js",
"license": "MIT",
"keywords": [
"netlify",
"netlify-cms",
"cloudinary",
"image",
"images",
"media",
"assets",
"files",
"uploads"
],
"sideEffects": false,
"scripts": {
"watch": "webpack -w",
"develop": "npm run watch",
"build": "cross-env NODE_ENV=production webpack"
},
"devDependencies": {
"cross-env": "^5.2.0",
"webpack": "^4.16.1",
"webpack-cli": "^3.1.0"
},
"peerDependencies": {
"netlify-cms-lib-util": "^2.0.4"
},
"dependencies": {}
}
82 changes: 82 additions & 0 deletions packages/netlify-cms-media-library-cloudinary/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { pick } from 'lodash';
import { loadScript } from 'netlify-cms-lib-util';

const defaultOptions = {
use_secure_url: true,
use_transformations: true,
output_filename_only: false,
};
/**
* This configuration hash cannot be overriden, as the values here are required
* for the integration to work properly.
*/
const enforcedConfig = {
button_class: undefined,
inline_container: undefined,
insert_transformation: false,
z_index: '99999',
};

const defaultConfig = {
multiple: false,
};

function getAssetUrl(asset, { use_secure_url, use_transformations, output_filename_only }) {
/**
* Allow output of the file name only, in which case the rest of the url (including)
* transformations) can be handled by the static site generator.
*/
if (output_filename_only) {
return `${asset.public_id}.${asset.format}`;
}

/**
* Get url from `derived` property if it exists. This property contains the
* transformed version of image if transformations have been applied.
*/
const urlObject = asset.derived && use_transformations ? asset.derived[0] : asset;

/**
* Retrieve the `https` variant of the image url if the `useSecureUrl` option
* is set to `true` (this is the default setting).
*/
const urlKey = use_secure_url ? 'secure_url' : 'url';

return urlObject[urlKey];
}

async function init({ options, handleInsert }) {
const { config: providedConfig = {}, ...integrationOptions } = options;
const resolvedOptions = { ...defaultOptions, ...integrationOptions };
const cloudinaryConfig = { ...defaultConfig, ...providedConfig, ...enforcedConfig };
const cloudinaryBehaviorConfigKeys = ['default_transformations', 'max_files', 'multiple'];
const cloudinaryBehaviorConfig = pick(cloudinaryConfig, cloudinaryBehaviorConfigKeys);

await loadScript('https://media-library.cloudinary.com/global/all.js');

const insertHandler = data => {
const assets = data.assets.map(asset => getAssetUrl(asset, resolvedOptions));
handleInsert(cloudinaryConfig.multiple ? assets : assets[0]);
};

const mediaLibrary = window.cloudinary.createMediaLibrary(cloudinaryConfig, { insertHandler });

return {
show: ({ config: instanceConfig = {}, allowMultiple }) => {
/**
* Ensure multiple selection is not available if the field is configured
* to disallow it.
*/
if (allowMultiple === false) {
instanceConfig.multiple = false;
}
return mediaLibrary.show({ config: { ...cloudinaryBehaviorConfig, instanceConfig } });
},
hide: () => mediaLibrary.hide(),
enableStandalone: () => true,
};
}

const cloudinaryMediaLibrary = { name: 'cloudinary', init };

export default cloudinaryMediaLibrary;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const { getConfig } = require('../../scripts/webpack.js');

module.exports = getConfig();
8 changes: 5 additions & 3 deletions packages/netlify-cms-media-library-uploadcare/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,20 @@ async function init({ options = { config: {} }, handleInsert }) {
* On show, create a new widget, cache it in the widgets object, and open.
* No hide method is provided because the widget doesn't provide it.
*/
show: ({ value, config: instanceConfig = {}, imagesOnly }) => {
show: ({ value, config: instanceConfig = {}, allowMultiple, imagesOnly }) => {
const config = { ...baseConfig, imagesOnly, ...instanceConfig };
const multiple = allowMultiple === false ? false : !!config.multiple;
const resolvedConfig = { ...config, multiple };
const files = getFiles(value);

/**
* Resolve the promise only if it's ours. Only the jQuery promise objects
* from the Uploadcare library will have a `state` method.
*/
if (files && !files.state) {
files.then(result => openDialog(result, config, handleInsert));
files.then(result => openDialog(result, resolvedConfig, handleInsert));
} else {
openDialog(files, config, handleInsert);
openDialog(files, resolvedConfig, handleInsert);
}
},

Expand Down
1 change: 1 addition & 0 deletions packages/netlify-cms-widget-file/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"build": "cross-env NODE_ENV=production webpack"
},
"dependencies": {
"common-tags": "^1.8.0",
"uuid": "^3.3.2"
},
"devDependencies": {
Expand Down
31 changes: 29 additions & 2 deletions packages/netlify-cms-widget-file/src/withFileControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from 'react-emotion';
import { List } from 'immutable';
import { Map, List } from 'immutable';
import { once } from 'lodash';
import uuid from 'uuid/v4';
import { oneLine } from 'common-tags';
import { lengths, components, buttons } from 'netlify-cms-ui-default';

const MAX_DISPLAY_LENGTH = 50;
Expand Down Expand Up @@ -64,6 +66,15 @@ function isMultiple(value) {
return Array.isArray(value) || List.isList(value);
}

const warnDeprecatedOptions = once(field =>
console.warn(oneLine`
Netlify CMS config: ${field.get('name')} field: property "options" has been deprecated for the
${field.get('widget')} widget and will be removed in the next major release. Rather than
\`field.options.media_library\`, apply media library options for this widget under
\`field.media_library\`.
`),
);

export default function withFileControl({ forImage } = {}) {
return class FileControl extends React.Component {
static propTypes = {
Expand Down Expand Up @@ -126,12 +137,28 @@ export default function withFileControl({ forImage } = {}) {
handleChange = e => {
const { field, onOpenMediaLibrary, value } = this.props;
e.preventDefault();
let mediaLibraryFieldOptions;

/**
* `options` hash as a general field property is deprecated, only used
* when external media libraries were first introduced. Not to be
* confused with `options` for the select widget, which serves a different
* purpose.
*/
if (field.hasIn(['options', 'media_library'])) {
warnDeprecatedOptions(field);
mediaLibraryFieldOptions = field.getIn(['options', 'media_library'], Map());
} else {
mediaLibraryFieldOptions = field.get('media_library', Map());
}

return onOpenMediaLibrary({
controlID: this.controlID,
forImage,
privateUpload: field.get('private'),
value,
config: field.getIn(['options', 'media_library', 'config']),
allowMultiple: !!mediaLibraryFieldOptions.get('allow_multiple', true),
config: mediaLibraryFieldOptions.get('config'),
});
};

Expand Down
2 changes: 2 additions & 0 deletions packages/netlify-cms/src/media-libraries.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import cms from 'netlify-cms-core/src';
import uploadcare from 'netlify-cms-media-library-uploadcare/src';
import cloudinary from 'netlify-cms-media-library-cloudinary/src';

const { registerMediaLibrary } = cms;

registerMediaLibrary(uploadcare);
registerMediaLibrary(cloudinary);
Loading

0 comments on commit b2f84a7

Please sign in to comment.