landmark-serve
Version:
Web Application Framework and Admin GUI / Content Management System built on Express.js and Mongoose
339 lines (259 loc) • 7.92 kB
JavaScript
/*!
* Module dependencies.
*/
var _ = require('underscore'),
landmark = require('../../'),
util = require('util'),
cloudinary = require('cloudinary'),
utils = require('landmark-utils'),
super_ = require('../field'),
async = require('async');
/**
* CloudinaryImages FieldType Constructor
* @extends Field
* @api public
*/
function cloudinaryimages(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' +
'CloudinaryImages fields (' + list.key + '.' + path + ') do not currently support being used as initial fields.\n');
}
cloudinaryimages.super_.call(this, list, path, options);
// validate cloudinary config
if (!landmark.get('cloudinary config')) {
throw new Error('Invalid Configuration\n\n' +
'CloudinaryImages 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(cloudinaryimages, super_);
/**
* Registers the field on the List's Mongoose Schema.
*
* @api public
*/
cloudinaryimages.prototype.addToSchema = function() {
var mongoose = landmark.mongoose;
var field = this,
schema = this.list.schema;
var paths = this.paths = {
upload: this._path.append('_upload'),
uploads: this._path.append('_uploads'),
action: this._path.append('_action'),
order: this._path.append('_order')
};
var ImageSchema = new mongoose.Schema({
public_id: String,
version: Number,
signature: String,
format: String,
resource_type: String,
url: String,
width: Number,
height: Number,
secure_url: String
});
var src = function(img, options) {
if (landmark.get('cloudinary secure')) {
options = options || {};
options.secure = true;
}
return img.public_id ? cloudinary.url(img.public_id + '.' + img.format, options) : '';
};
var addSize = function(options, width, height) {
if (width) options.width = width;
if (height) options.height = height;
return options;
};
ImageSchema.method('src', function(options) {
return src(this, options);
});
ImageSchema.method('scale', function(width, height) {
return src(this, addSize({ crop: 'scale' }, width, height));
});
ImageSchema.method('fill', function(width, height) {
return src(this, addSize({ crop: 'fill' }, width, height));
});
ImageSchema.method('lfill', function(width, height) {
return src(this, addSize({ crop: 'lfill' }, width, height));
});
ImageSchema.method('fit', function(width, height) {
return src(this, addSize({ crop: 'fit' }, width, height));
});
ImageSchema.method('limit', function(width, height) {
return src(this, addSize({ crop: 'limit' }, width, height));
});
ImageSchema.method('pad', function(width, height) {
return src(this, addSize({ crop: 'pad' }, width, height));
});
ImageSchema.method('lpad', function(width, height) {
return src(this, addSize({ crop: 'lpad' }, width, height));
});
ImageSchema.method('crop', function(width, height) {
return src(this, addSize({ crop: 'crop' }, width, height));
});
ImageSchema.method('thumbnail', function(width, height) {
return src(this, addSize({ crop: 'thumb' }, width, height));
});
schema.add(this._path.addTo({}, [ImageSchema]));
this.removeImage = function(item, id, method, callback) {
var images = item.get(field.path);
if ('number' != typeof id) {
for (var i = 0; i < images.length; i++) {
if (images[i].public_id == id) {
id = i;
break;
}
}
}
var img = images[id];
if (!img) return;
if (method == 'delete') {
cloudinary.uploader.destroy(img.public_id, function() {});
}
images.splice(id, 1);
if (callback) {
item.save(('function' != typeof callback) ? callback : undefined);
}
};
this.underscoreMethod('remove', function(id, callback) {
field.removeImage(this, id, 'remove', callback);
});
this.underscoreMethod('delete', function(id, callback) {
field.removeImage(this, id, 'delete', callback);
});
this.bindUnderscoreMethods();
};
/**
* Formats the field value
*
* @api public
*/
cloudinaryimages.prototype.format = function(item) {
return _.map(item.get(this.path), function(img) {
return img.src();
}).join(', ');
};
/**
* Detects whether the field has been modified
*
* @api public
*/
cloudinaryimages.prototype.isModified = function(item) {
// TODO - how should this be detected?
return true;
};
/**
* Validates that a value for this field has been provided in a data object
*
* @api public
*/
cloudinaryimages.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
*/
cloudinaryimages.prototype.updateItem = function(item, data) {
// TODO - direct updating of data (not via upload)
};
/**
* Returns a callback that handles a standard form submission for the field
*
* Expected form parts are
* - `field.paths.action` in `req.body` in syntax `delete:public_id,public_id|remove:public_id,public_id`
* - `field.paths.upload` in `req.files` (uploads the images to cloudinary)
*
* @api public
*/
cloudinaryimages.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() {
// Order
if (req.body[paths.order]) {
var images = item.get(field.path),
newOrder = req.body[paths.order].split(',');
images.sort(function(a, b) {
return (newOrder.indexOf(a.public_id) > newOrder.indexOf(b.public_id)) ? 1 : -1;
});
}
// Removals & Deletes
if (req.body && req.body[paths.action]) {
var actions = req.body[paths.action].split('|');
actions.forEach(function(action) {
action = action.split(':');
var method = action[0],
ids = action[1];
if (!method.match(/^(remove|delete)$/) || !ids) return;
ids.split(',').forEach(function(id) {
field.removeImage(item, id, method);
});
});
}
// Upload References (direct uploading)
if (req.body[paths.uploads]) {
var uploads = JSON.parse(req.body[paths.uploads]);
uploads.forEach(function(file) {
item.get(field.path).push(file);
});
}
// Upload Data (form submissions)
if (req.files && req.files[paths.upload]) {
var files = _.flatten(req.files[paths.upload]);
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 (landmark.get('cloudinary prefix'))
uploadOptions.tags.push(landmark.get('cloudinary prefix'));
if (landmark.get('env') != 'production')
uploadOptions.tags.push(tp + 'dev');
async.each(files, function(file, next) {
if (!file.size) return next();
cloudinary.uploader.upload(file.path, function(result) {
if (result.error) {
return next(result.error);
} else {
item.get(field.path).push(result);
return next();
}
}, uploadOptions);
}, function(err) {
return callback(err);
});
} else {
return callback();
}
};
};
/**
* Immediately handles a standard form submission for the field (see `getRequestHandler()`)
*
* @api public
*/
cloudinaryimages.prototype.handleRequest = function(item, req, paths, callback) {
this.getRequestHandler(item, req, paths, callback)();
};
/*!
* Export class
*/
exports = module.exports = cloudinaryimages;