UNPKG

landmark-serve

Version:

Web Application Framework and Admin GUI / Content Management System built on Express.js and Mongoose

419 lines (336 loc) 10.1 kB
/*! * Module dependencies. */ var _ = require('underscore'), landmark = require('../../'), util = require('util'), cloudinary = require('cloudinary'), utils = require('landmark-utils'), super_ = require('../field'); /** * CloudinaryImage FieldType Constructor * @extends Field * @api public */ function cloudinaryimage(list, path, options) { this._underscoreMethods = ['format']; // TODO: implement filtering, usage disabled for now options.nofilter = true; // TODO: implement initial form, usage disabled for now if (options.initial) { throw new Error( 'Invalid Configuration\n\n' + 'CloudinaryImage fields (' + list.key + '.' + path + ') do not currently support being used as initial fields.\n' ); } cloudinaryimage.super_.call(this, list, path, options); // validate cloudinary config if (!landmark.get('cloudinary config')) { throw new Error( 'Invalid Configuration\n\n' + 'CloudinaryImage fields (' + list.key + '.' + this.path + ') require the "cloudinary config" option to be set.\n\n' + 'See http://getlandmarkproject.com/docs/cms/configuration/#cloudinary for more information.\n' ); } } /*! * Inherit from Field */ util.inherits(cloudinaryimage, super_); /** * Registers the field on the List's Mongoose Schema. * * @api public */ cloudinaryimage.prototype.addToSchema = function() { var field = this, schema = this.list.schema; var paths = this.paths = { // cloudinary fields public_id: this._path.append('.public_id'), version: this._path.append('.version'), signature: this._path.append('.signature'), format: this._path.append('.format'), resource_type: this._path.append('.resource_type'), url: this._path.append('.url'), width: this._path.append('.width'), height: this._path.append('.height'), secure_url: this._path.append('.secure_url'), // virtuals exists: this._path.append('.exists'), folder: this._path.append('.folder'), upload: this._path.append('_upload'), action: this._path.append('_action'), select: this._path.append('_select') }; var schemaPaths = this._path.addTo({}, { public_id: String, version: Number, signature: String, format: String, resource_type: String, url: String, width: Number, height: Number, secure_url: String }); schema.add(schemaPaths); var exists = function(item) { return (item.get(paths.public_id) ? true : false); }; // The .exists virtual indicates whether an image is stored schema.virtual(paths.exists).get(function() { return schemaMethods.exists.apply(this); }); var folder = function(item) { var folderValue = null; if (landmark.get('cloudinary folders')) { if (field.options.folder) { folderValue = field.options.folder; } else { var folderList = landmark.get('cloudinary prefix') ? [landmark.get('cloudinary prefix')] : []; folderList.push(field.list.path); folderList.push(field.path); folderValue = folderList.join('/'); } } return folderValue; }; // The .folder virtual returns the cloudinary folder used to upload/select images schema.virtual(paths.folder).get(function() { return schemaMethods.folder.apply(this); }); var src = function(item, options) { if (!exists(item)) { return ''; } options = ('object' === typeof options) ? options : {}; if (!('fetch_format' in options) && landmark.get('cloudinary webp') !== false) { options.fetch_format = "auto"; } if (!('progressive' in options) && landmark.get('cloudinary progressive') !== false) { options.progressive = true; } if (!('secure' in options) && landmark.get('cloudinary secure')) { options.secure = true; } return cloudinary.url(item.get(paths.public_id) + '.' + item.get(paths.format), options); }; var reset = function(item) { item.set(field.path, { public_id: '', version: 0, signature: '', format: '', resource_type: '', url: '', width: 0, height: 0, secure_url: '' }); }; var addSize = function(options, width, height, other) { if (width) options.width = width; if (height) options.height = height; if ('object' === typeof other) { _.extend(options, other); } return options; }; var schemaMethods = { exists: function() { return exists(this); }, folder: function() { return folder(this); }, src: function(options) { return src(this, options); }, tag: function(options) { return exists(this) ? cloudinary.image(this.get(field.path), options) : ''; }, scale: function(width, height, options) { return src(this, addSize({ crop: 'scale' }, width, height, options)); }, fill: function(width, height, options) { return src(this, addSize({ crop: 'fill' }, width, height, options)); }, lfill: function(width, height, options) { return src(this, addSize({ crop: 'lfill' }, width, height, options)); }, fit: function(width, height, options) { return src(this, addSize({ crop: 'fit' }, width, height, options)); }, limit: function(width, height, options) { return src(this, addSize({ crop: 'limit' }, width, height, options)); }, pad: function(width, height, options) { return src(this, addSize({ crop: 'pad' }, width, height, options)); }, lpad: function(width, height, options) { return src(this, addSize({ crop: 'lpad' }, width, height, options)); }, crop: function(width, height, options) { return src(this, addSize({ crop: 'crop' }, width, height, options)); }, thumbnail: function(width, height, options) { return src(this, addSize({ crop: 'thumb' }, width, height, options)); }, /** * Resets the value of the field * * @api public */ reset: function() { reset(this); }, /** * Deletes the image from Cloudinary and resets the field * * @api public */ delete: function() { cloudinary.uploader.destroy(this.get(paths.public_id), function() {}); reset(this); } }; _.each(schemaMethods, function(fn, key) { field.underscoreMethod(key, fn); }); // expose a method on the field to call schema methods this.apply = function(item, method) { return schemaMethods[method].apply(item, Array.prototype.slice.call(arguments, 2)); }; this.bindUnderscoreMethods(); }; /** * Formats the field value * * @api public */ cloudinaryimage.prototype.format = function(item) { return item.get(this.paths.url); }; /** * Detects whether the field has been modified * * @api public */ cloudinaryimage.prototype.isModified = function(item) { return item.isModified(this.paths.url); }; /** * Validates that a value for this field has been provided in a data object * * @api public */ cloudinaryimage.prototype.validateInput = function(data) { // TODO - how should image field input be validated? return true; }; /** * Updates the value for this field in the item from a data object * * @api public */ cloudinaryimage.prototype.updateItem = function(item, data) { var paths = this.paths; var setValue = function(key) { if (paths[key]) { var index = paths[key].indexOf("."); var field = paths[key].substr(0, index); // Note we allow implicit conversion here so that numbers submitted as strings in the data object // aren't treated as different values to the stored Number values if (data[field] && data[field][key] && data[field][key] != item.get(paths[key])) { item.set(paths[key], data[field][key] || null); } } }; _.each(['public_id', 'version', 'signature', 'format', 'resource_type', 'url', 'width', 'height', 'secure_url'], setValue); }; /** * Returns a callback that handles a standard form submission for the field * * Expected form parts are * - `field.paths.action` in `req.body` (`clear` or `delete`) * - `field.paths.upload` in `req.files` (uploads the image to cloudinary) * * @api public */ cloudinaryimage.prototype.getRequestHandler = function(item, req, paths, callback) { var field = this; if (utils.isFunction(paths)) { callback = paths; paths = field.paths; } else if (!paths) { paths = field.paths; } callback = callback || function() {}; return function() { if (req.body) { var action = req.body[paths.action]; if (/^(delete|reset)$/.test(action)) { field.apply(item, action); } } if (req.body && req.body[paths.select]) { cloudinary.api.resource(req.body[paths.select], function(result) { if (result.error) { callback(result.error); } else { item.set(field.path, result); callback(); } }); } else if (req.files && req.files[paths.upload] && req.files[paths.upload].size) { var tp = landmark.get('cloudinary prefix') || ''; if (tp.length) { tp += '_'; } var uploadOptions = { tags: [tp + field.list.path + '_' + field.path, tp + field.list.path + '_' + field.path + '_' + item.id] }; if (item.get(field.paths.folder)) { uploadOptions.folder = item.get(field.paths.folder); } if (landmark.get('cloudinary prefix')) { uploadOptions.tags.push(landmark.get('cloudinary prefix')); } if (landmark.get('env') !== 'production') { uploadOptions.tags.push(tp + 'dev'); } if (field.options.publicID) { var publicIdValue = item.get(field.options.publicID); if (publicIdValue) { uploadOptions.public_id = publicIdValue; } } if (field.options.autoCleanup && item.get(field.paths.exists)) { field.apply(item, 'delete'); } cloudinary.uploader.upload(req.files[paths.upload].path, function(result) { if (result.error) { callback(result.error); } else { item.set(field.path, result); callback(); } }, uploadOptions); } else { callback(); } }; }; /** * Immediately handles a standard form submission for the field (see `getRequestHandler()`) * * @api public */ cloudinaryimage.prototype.handleRequest = function(item, req, paths, callback) { this.getRequestHandler(item, req, paths, callback)(); }; /*! * Export class */ exports = module.exports = cloudinaryimage;