function azurefile(list, path, options) {
throw new Error('The AzureFile field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('pre:upload');
this._underscoreMethods = ['format', 'uploadFile'];
this._fixedSize = 'full';
// 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\nAzureFile fields (' + list.key + '.' + path + ') do not currently support being used
as initial fields.\n');
}
var self = this;
options.filenameFormatter = options.filenameFormatter || function (item, filename) { return filename; };
options.containerFormatter = options.containerFormatter || function (item, filename) { return self.azurefileconfig.container; };//
eslint-disable-line no-unused-vars
azurefile.super_.call(this, list, path, options);
// validate azurefile config (has to happen after super_.call)
if (!this.azurefileconfig) {
throw new Error('Invalid Configuration\n\n'
+ 'AzureFile fields (' + list.key + '.' + path + ') require the "azurefile config" option to be set.\n\n'
+ 'See http://keystonejs.com/docs/configuration/#services-azure for more information.\n');
}
// TODO; this is really bad, we shouldn't be overwriting global env!
process.env.AZURE_STORAGE_ACCOUNT = this.azurefileconfig.account;
process.env.AZURE_STORAGE_ACCESS_KEY = this.azurefileconfig.key;
this.azurefileconfig.container = this.azurefileconfig.container || 'keystone';
// Could be more pre- hooks, just upload for now
if (options.pre && options.pre.upload) {
this.pre('upload', options.pre.upload);
}
*/
}
n/a
function boolean(list, path, options) { this._nativeType = Boolean; this._properties = ['indent']; this._fixedSize = 'full'; this.indent = (options.indent) ? true : false; boolean.super_.call(this, list, path, options); }
n/a
function cloudinaryimage(list, path, options) { this._underscoreMethods = ['format']; this._fixedSize = 'full'; this._properties = ['select', 'selectPrefix', 'autoCleanup']; if (options.filenameAsPublicID) { // Produces the same result as the legacy filenameAsPublicID option options.generateFilename = nameFunctions.originalFilename; options.whenExists = 'overwrite'; } options = assign({}, DEFAULT_OPTIONS, options); options.generateFilename = ensureCallback(options.generateFilename); cloudinaryimage.super_.call(this, list, path, options); // validate cloudinary config if (!keystone.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://keystonejs.com/docs/configuration/#services-cloudinary for more information.\n' ); } }
n/a
function cloudinaryimages(list, path, options) { this._underscoreMethods = ['format']; this._fixedSize = 'full'; this._properties = ['select', 'selectPrefix', 'autoCleanup', 'publicID', 'folder', 'filenameAsPublicID']; cloudinaryimages.super_.call(this, list, path, options); // validate cloudinary config if (!keystone.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://keystonejs.com/docs/configuration/#services-cloudinary for more information.\n'); } }
n/a
function code(list, path, options) { this._nativeType = String; this._defaultSize = 'full'; this.height = options.height || 180; this.lang = options.lang || options.language; this._properties = ['editor', 'height', 'lang']; this.codemirror = options.codemirror || {}; this.editor = assign({ mode: this.lang }, this.codemirror); code.super_.call(this, list, path, options); }
n/a
function color(list, path, options) { this._nativeType = String; color.super_.call(this, list, path, options); }
n/a
function datearray(list, path, options) { this._nativeType = [Date]; this._defaultSize = 'medium'; this._underscoreMethods = ['format']; this._properties = ['formatString']; this.parseFormatString = options.parseFormat || 'YYYY-MM-DD'; this.formatString = (options.format === false) ? false : (options.format || 'Do MMM YYYY'); if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.DateArray: options.format must be a string.'); } this.separator = options.separator || ' | '; datearray.super_.call(this, list, path, options); }
n/a
function date(list, path, options) { this._nativeType = Date; this._underscoreMethods = ['format', 'moment', 'parse']; this._fixedSize = 'medium'; this._properties = ['formatString', 'yearRange', 'isUTC', 'inputFormat']; this.parseFormatString = options.inputFormat || 'YYYY-MM-DD'; this.formatString = (options.format === false) ? false : (options.format || 'Do MMM YYYY'); this.yearRange = options.yearRange; this.isUTC = options.utc || false; if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.Date: options.format must be a string.'); } date.super_.call(this, list, path, options); }
n/a
function datetime(list, path, options) { this._nativeType = Date; this._underscoreMethods = ['format', 'moment', 'parse']; this._fixedSize = 'full'; this._properties = ['formatString', 'isUTC']; this.typeDescription = 'date and time'; this.parseFormatString = options.parseFormat || parseFormats; this.formatString = (options.format === false) ? false : (options.format || 'YYYY-MM-DD h:mm:ss a'); this.isUTC = options.utc || false; if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.DateTime: options.format must be a string.'); } datetime.super_.call(this, list, path, options); this.paths = { date: this.path + '_date', time: this.path + '_time', tzOffset: this.path + '_tzOffset', }; }
n/a
Email = function (options) {
if (typeof options === 'string') {
options = { templateName: options };
}
if (!utils.isObject(options)) {
throw new Error('The keystone.Email class requires a templateName or options argument to be provided');
}
/**
* Keystone < 0.4 Compatibility
*
* NOTE: Add warnings and enable them in 4.1 or 4.2 release. These patterns
* will be deprecated with the 0.5 release!
*/
// keystome.set('email transport', 'sometransport') -> options.transport
var emailTransport = keystone.get('email transport');
if (!options.transport && emailTransport) {
options.transport = emailTransport;
}
// mandrill used to be the default; provide it if the api key is set
var mandrillApiKey = keystone.get('mandrill api key');
if (!options.transport && mandrillApiKey) {
options.transport = 'mandrill';
options.apiKey = mandrillApiKey;
}
// templateExt -> engine
if (!options.engine) {
options.engine = options.templateExt;
}
// keystone.set('view engine', 'something') -> engine
if (!options.engine) {
var customEngine = keystone.get('custom engine');
var viewEngine = keystone.get('view engine');
if (typeof customEngine === 'function') {
// when customEngine is a function, viewEngine is probably the extension
options.engine = customEngine;
options.ext = options.ext || options.templateExt || viewEngine;
} else if (viewEngine) {
// otherwise, default the email engine to keystone's view engine
options.engine = viewEngine;
}
}
// keystone.set('emails', 'rootpath') -> root
var rootPath = keystone.get('emails');
if (rootPath && !options.root) {
options.root = rootPath;
}
// Try to use the keystone-email package and throw if it hasn't been installed
var KeystoneEmail;
try {
KeystoneEmail = require('keystone-email');
} catch (err) {
if (err.code === 'MODULE_NOT_FOUND') {
throw new Error('The "keystone-email" package needs to be installed to send emails');
} else {
throw err;
}
}
// Create the new email instance with the template name and options
var templateName = options.templateName;
delete options.templateName;
var email = new KeystoneEmail(templateName, options);
// Make email.send backwards compatible with old argument signature
var send = email.send;
email.send = function () {
var args = [arguments[0]];
if (typeof arguments[1] === 'function') {
// map .send(options, callback) => .send(locals, options, callback)
// TOOD: Deprecate this call signature
args.push(arguments[0]);
args.push(arguments[1]);
} else {
// map .send(locals options, callback) => .send(locals, options, callback)
args.push(arguments[1]);
args.push(arguments[2]);
}
send.apply(email, args);
};
return email;
}
n/a
function email(list, path, options) { this._nativeType = String; this._underscoreMethods = ['gravatarUrl']; this.typeDescription = 'email address'; email.super_.call(this, list, path, options); }
n/a
function embedly(list, path, options) { this._underscoreMethods = ['reset']; this._fixedSize = 'full'; this.fromPath = options.from; this.embedlyOptions = options.options || {}; // check and api key has been set, or bail. if (!keystone.get('embedly api key')) { throw new Error('Invalid Configuration\n\n' + 'Embedly fields (' + list.key + '.' + path + ') require the "embedly api key" option to be set.\n\n' + 'See http://keystonejs.com/docs/configuration/#services-embedly for more information.\n'); } // ensure a fromPath has been defined if (!options.from) { throw new Error('Invalid Configuration\n\n' + 'Embedly fields (' + list.key + '.' + path + ') require a fromPath option to be set.\n' + 'See http://keystonejs.com/docs/database/#fieldtypes-embedly for more information.\n'); } // embedly fields cannot be set as initial fields if (options.initial) { throw new Error('Invalid Configuration\n\n' + 'Embedly fields (' + list.key + '.' + path + ') cannot be set as initial fields.\n'); } embedly.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function file(list, path, options) { this._underscoreMethods = ['format', 'upload', 'remove', 'reset']; this._fixedSize = 'full'; if (!options.storage) { throw new Error('Invalid Configuration\n\n' + 'File fields (' + list.key + '.' + path + ') require storage to be provided.'); } this.storage = options.storage; file.super_.call(this, list, path, options); }
n/a
function geopoint(list, path, options) { this._fixedSize = 'medium'; geopoint.super_.call(this, list, path, options); }
n/a
function html(list, path, options) { this._nativeType = String; this._defaultSize = 'full'; this.wysiwyg = options.wysiwyg || false; this.height = options.height || 180; this._properties = ['wysiwyg', 'height']; html.super_.call(this, list, path, options); }
n/a
function key(list, path, options) { this._nativeType = String; this._defaultSize = 'medium'; this.separator = options.separator || '-'; key.super_.call(this, list, path, options); }
n/a
Keystone = function () { grappling.mixin(this).allowHooks('pre:static', 'pre:bodyparser', 'pre:session', 'pre:logger', 'pre:admin', 'pre:routes', 'pre:render ', 'updates', 'signin', 'signout'); this.lists = {}; this.fieldTypes = {}; this.paths = {}; this._options = { 'name': 'Keystone', 'brand': 'Keystone', 'admin path': 'keystone', 'compress': true, 'headless': false, 'logger': ':method :url :status :response-time ms', 'auto update': false, 'model prefix': null, 'module root': moduleRoot, 'frame guard': 'sameorigin', 'cache admin bundles': true, }; this._redirects = {}; // expose express this.express = express; // init environment defaults this.set('env', process.env.NODE_ENV || 'development'); this.set('port', process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || '3000'); this.set('host', process.env.HOST || process.env.IP || process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0'); this.set('listen', process.env.LISTEN); this.set('ssl', process.env.SSL); this.set('ssl port', process.env.SSL_PORT || '3001'); this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP); this.set('ssl key', process.env.SSL_KEY); this.set('ssl cert', process.env.SSL_CERT); this.set('cookie secret', process.env.COOKIE_SECRET); this.set('cookie signin', (this.get('env') === 'development') ? true : false); this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY); this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY); this.set('mandrill username', process.env.MANDRILL_USERNAME); this.set('google api key', process.env.GOOGLE_BROWSER_KEY); this.set('google server api key', process.env.GOOGLE_SERVER_KEY); this.set('ga property', process.env.GA_PROPERTY); this.set('ga domain', process.env.GA_DOMAIN); this.set('chartbeat property', process.env.CHARTBEAT_PROPERTY); this.set('chartbeat domain', process.env.CHARTBEAT_DOMAIN); this.set('allowed ip ranges', process.env.ALLOWED_IP_RANGES); if (process.env.S3_BUCKET && process.env.S3_KEY && process.env.S3_SECRET) { this.set('s3 config', { bucket: process.env.S3_BUCKET, key: process.env.S3_KEY, secret: process.env.S3_SECRET, region: process .env.S3_REGION }); } if (process.env.AZURE_STORAGE_ACCOUNT && process.env.AZURE_STORAGE_ACCESS_KEY) { this.set('azurefile config', { account: process.env.AZURE_STORAGE_ACCOUNT, key: process.env.AZURE_STORAGE_ACCESS_KEY }); } if (process.env.CLOUDINARY_URL) { // process.env.CLOUDINARY_URL is processed by the cloudinary package when this is set this.set('cloudinary config', true); } // init mongoose this.set('mongoose', require('mongoose')); this.mongoose.Promise = require('es6-promise').Promise; // Attach middleware packages, bound to this instance this.middleware = { api: require('./lib/middleware/api')(this), cors: require('./lib/middleware/cors')(this), }; }
n/a
function List(key, options) { if (!(this instanceof List)) return new List(key, options); this.keystone = keystone; var defaultOptions = { schema: { collection: keystone.prefixModel(key), }, noedit: false, nocreate: false, nodelete: false, autocreate: false, sortable: false, hidden: false, track: false, inherits: false, perPage: 100, searchFields: '__name__', searchUsesTextIndex: false, defaultSort: '__default__', defaultColumns: '__name__', }; // initialFields values are initialised on demand by the getter var initialFields; // Inherit default options from parent list if it exists if (options && options.inherits) { if (options.inherits.options && options.inherits.options.inherits) { throw new Error('Inherited Lists may not contain any inheritance'); } defaultOptions = utils.options(defaultOptions, options.inherits.options); if (options.inherits.options.track) { options.track = false; } } this.options = utils.options(defaultOptions, options); // init properties this.key = key; this.path = this.get('path') || utils.keyToPath(key, true); this.schema = new keystone.mongoose.Schema({}, this.options.schema); this.schemaFields = []; this.uiElements = []; this.underscoreMethods = {}; this.fields = {}; this.fieldsArray = []; this.fieldTypes = {}; this.relationshipFields = []; this.relationships = {}; this.mappings = { name: null, createdBy: null, createdOn: null, modifiedBy: null, modifiedOn: null, }; var self = this; // init mappings _.forEach(this.options.map, function (val, key) { self.map(key, val); }); // define property getters Object.defineProperty(this, 'label', { get: function () { return this.get('label') || this.set('label', utils.plural(utils.keyToLabel(key))); } }); Object.defineProperty(this, 'singular', { get: function () { return this.get('singular') || this.set('singular', utils.singular(this.label)); } }); Object.defineProperty(this, 'plural', { get: function () { return this.get('plural') || this.set('plural', utils.plural(this.singular)); } }); Object.defineProperty(this, 'namePath', { get: function () { return this.mappings.name || '_id'; } }); Object.defineProperty(this, 'nameField', { get: function () { return this.fields[this.mappings.name]; } }); Object.defineProperty(this, 'nameIsVirtual', { get: function () { return this.model.schema.virtuals[this.mappings.name] ? true : false; } }); Object.defineProperty(this, 'nameFieldIsFormHeader', { get: function () { return (this.fields[this.mappings.name] && this.fields[this.mappings.name].type === 'text') ? !this.fields[this.mappings.name ].noedit : false; } }); Object.defineProperty(this, 'nameIsInitial', { get: function () { return (this.fields[this.mappings.name] && this.fields[this.mappings.name].options.initial === undefined); } }); Object.defineProperty(this, 'initialFields', { get: function () { return initialFields || (initialFields = _.filter(this.fields, function (i) { return i.initial; })); } }); if (this.get('inherits')) { var parentFields = this.get('inherits').schemaFields; this.add.apply(this, parentFields); } }
n/a
function localfile(list, path, options) {
throw new Error('The LocalFile field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('move');
this._underscoreMethods = ['format', 'uploadFile'];
this._fixedSize = 'full';
this.autoCleanup = options.autoCleanup || false;
if (options.overwrite !== false) {
options.overwrite = true;
}
localfile.super_.call(this, list, path, options);
// validate destination dir
if (!options.dest) {
throw new Error('Invalid Configuration\n\n'
+ 'localfile fields (' + list.key + '.' + path + ') require the "dest" option to be set.');
}
// Allow hook into before and after
if (options.pre && options.pre.move) {
this.pre('move', options.pre.move);
}
if (options.post && options.post.move) {
this.post('move', options.post.move);
}
*/
}
n/a
function localfiles(list, path, options) {
throw new Error('The LocalFiles field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('move');
this._underscoreMethods = ['format', 'uploadFiles'];
this._fixedSize = 'full';
// 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'
+ 'localfiles fields (' + list.key + '.' + path + ') do not currently support being used as initial fields.\n');
}
if (options.overwrite !== false) {
options.overwrite = true;
}
localfiles.super_.call(this, list, path, options);
// validate destination dir
if (!options.dest) {
throw new Error('Invalid Configuration\n\n'
+ 'localfiles fields (' + list.key + '.' + path + ') require the "dest" option to be set.');
}
// Allow hook into before and after
if (options.pre && options.pre.move) {
this.pre('move', options.pre.move);
}
if (options.post && options.post.move) {
this.post('move', options.post.move);
}
*/
}
n/a
function location(list, path, options) { this._underscoreMethods = ['format', 'googleLookup', 'kmFrom', 'milesFrom']; this._fixedSize = 'full'; this._properties = ['enableMapsAPI']; this.enableMapsAPI = (options.enableImprove === true || (options.enableImprove !== false && keystone.get('google server api key '))) ? true : false; // Throw on invalid options in 4.0 (remove for 5.0) if ('geocodeGoogle' in options) { throw new Error('The geocodeGoogle option for Location fields has been renamed to enableImprove'); } if (!options.defaults) { options.defaults = {}; } if (options.required) { if (Array.isArray(options.required)) { // required can be specified as an array of paths this.requiredPaths = options.required; } else if (typeof options.required === 'string') { // or it can be specified as a comma-delimited list this.requiredPaths = options.required.replace(/,/g, ' ').split(/\s+/); } // options.required should always be simplified to a boolean options.required = true; } // default this.requiredPaths if (!this.requiredPaths) { this.requiredPaths = ['street1', 'suburb']; } location.super_.call(this, list, path, options); }
n/a
function markdown(list, path, options) { this._defaultSize = 'full'; this.toolbarOptions = options.toolbarOptions || {}; this.markedOptions = options.markedOptions || {}; this.height = options.height || 90; this.wysiwyg = ('wysiwyg' in options) ? options.wysiwyg : true; this._properties = ['wysiwyg', 'height', 'toolbarOptions']; markdown.super_.call(this, list, path, options); }
n/a
function money(list, path, options) { this.currency = options.currency; this._nativeType = Number; this._underscoreMethods = ['format']; this._properties = ['currency']; this._fixedSize = 'small'; this._formatString = (options.format === false) ? false : (options.format || '$0,0.00'); if (this._formatString && typeof this._formatString !== 'string') { throw new Error('FieldType.Money: options.format must be a string.'); } money.super_.call(this, list, path, options); }
n/a
function name(list, path, options) { this._fixedSize = 'full'; options.default = { first: '', last: '' }; name.super_.call(this, list, path, options); }
n/a
function numberarray(list, path, options) { this._nativeType = [Number]; this._underscoreMethods = ['format']; this._formatString = (options.format === false) ? false : (options.format || '0,0[.][000000000000]'); this._defaultSize = 'small'; if (this._formatString && typeof this._formatString !== 'string') { throw new Error('FieldType.NumberArray: options.format must be a string.'); } this.separator = options.separator || ' | '; numberarray.super_.call(this, list, path, options); }
n/a
function number(list, path, options) { this._nativeType = Number; this._fixedSize = 'small'; this._underscoreMethods = ['format']; this.formatString = (options.format === false) ? false : (options.format || '0,0[.][000000000000]'); if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.Number: options.format must be a string.'); } number.super_.call(this, list, path, options); }
n/a
function password(list, path, options) { this.options = options; this._nativeType = String; this._underscoreMethods = ['format', 'compare']; this._fixedSize = 'full'; // You can't sort on password fields options.nosort = true; this.workFactor = options.workFactor || 10; password.super_.call(this, list, path, options); for (var key in this.options.complexity) { if ({}.hasOwnProperty.call(this.options.complexity, key)) { if (key in regexChunk !== key in this.options.complexity) { throw new Error('FieldType.Password: options.complexity - option does not exist.'); } if (typeof this.options.complexity[key] !== 'boolean') { throw new Error('FieldType.Password: options.complexity - Value must be boolean.'); } } } if (this.options.max <= this.options.min) { throw new Error('FieldType.Password: options - min must be set at a lower value than max.'); } }
n/a
function relationship(list, path, options) { this.many = (options.many) ? true : false; this.filters = options.filters; this.createInline = (options.createInline) ? true : false; this._defaultSize = 'full'; this._nativeType = keystone.mongoose.Schema.Types.ObjectId; this._underscoreMethods = ['format', 'getExpandedData']; this._properties = ['isValid', 'many', 'filters', 'createInline']; relationship.super_.call(this, list, path, options); }
n/a
function s3file(list, path, options) {
throw new Error('The S3File field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('pre:upload');
this._underscoreMethods = ['format', 'uploadFile'];
this._fixedSize = 'full';
// 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'
+ 'S3File fields (' + list.key + '.' + path + ') do not currently support being used as initial fields.\n');
}
s3file.super_.call(this, list, path, options);
// validate s3 config (has to happen after super_.call)
if (!this.s3config) {
throw new Error('Invalid Configuration\n\n'
+ 'S3File fields (' + list.key + '.' + path + ') require the "s3 config" option to be set.\n\n'
+ 'See http://keystonejs.com/docs/configuration/#services-amazons3 for more information.\n');
}
// Could be more pre- hooks, just upload for now
if (options.pre && options.pre.upload) {
this.pre('upload', options.pre.upload);
}
*/
}
n/a
function select(list, path, options) { this.ui = options.ui || 'select'; this.numeric = options.numeric ? true : false; this._nativeType = (options.numeric) ? Number : String; this._underscoreMethods = ['format', 'pluck']; this._properties = ['ops', 'numeric']; if (typeof options.options === 'string') { options.options = options.options.split(','); } if (!Array.isArray(options.options)) { throw new Error('Select fields require an options array.'); } this.ops = options.options.map(function (i) { var op = typeof i === 'string' ? { value: i.trim(), label: utils.keyToLabel(i) } : i; if (!_.isObject(op)) { op = { label: '' + i, value: '' + i }; } if (options.numeric && !_.isNumber(op.value)) { op.value = Number(op.value); } return op; }); // undefined options.emptyOption defaults to true if (options.emptyOption === undefined) { options.emptyOption = true; } // ensure this.emptyOption is a boolean this.emptyOption = !!options.emptyOption; // cached maps for options, labels and values this.map = utils.optionsMap(this.ops); this.labels = utils.optionsMap(this.ops, 'label'); this.values = _.map(this.ops, 'value'); select.super_.call(this, list, path, options); }
n/a
function Storage(options) { // we're going to mutate options so get a clean copy of it options = assign({}, options); var AdapterType = options.adapter; delete options.adapter; if (typeof AdapterType !== 'function') { throw new Error('Invalid Storage Adapter\n' + 'The storage adapter specified is not a function. Did you ' + 'require the right package?\n'); } debug('Initialising Storage with adapter ' + AdapterType.name); // ensure the adapter compatibilityLevel of the adapter matches if (AdapterType.compatibilityLevel !== ADAPTER_COMPATIBILITY_LEVEL) { throw new Error('Incompatible Storage Adapter\n' + 'The storage adapter specified (' + AdapterType.name + ') ' + 'does not match the compatibility level required for this ' + 'version of Keystone.\n'); } // assign ensures the default schema constant isn't exposed as a property var schemaFields = assign({}, SCHEMA_FIELD_DEFAULTS, AdapterType.SCHEMA_FIELD_DEFAULTS, options.schema); delete options.schema; // Copy requested fields into local schema. var schema = this.schema = {}; for (var path in schemaFields) { if (!schemaFields[path]) continue; var type = AdapterType.SCHEMA_TYPES[path] || SCHEMA_TYPES[path]; if (!type) throw Error('Unknown type for requested schema field ' + path); schema[path] = type; } // ensure Storage schema features are supported by the Adapter if (schema.url && typeof AdapterType.prototype.getFileURL !== 'function') { throw Error('URL schema field is not supported by the ' + AdapterType.name + ' adapter'); } // create the adapter this.adapter = new AdapterType(options, schema); }
n/a
function textarray(list, path, options) { this._nativeType = [String]; this._underscoreMethods = ['format']; this.separator = options.separator || ' | '; textarray.super_.call(this, list, path, options); }
n/a
function text(list, path, options) { this.options = options; this._nativeType = String; this._properties = ['monospace']; this._underscoreMethods = ['crop']; text.super_.call(this, list, path, options); }
n/a
function textarea(list, path, options) { this._nativeType = String; this._underscoreMethods = ['format', 'crop']; this.height = options.height || 90; this.multiline = true; this._properties = ['height', 'multiline']; textarea.super_.call(this, list, path, options); }
n/a
function url(list, path, options) { this._nativeType = String; this._underscoreMethods = ['format']; url.super_.call(this, list, path, options); }
n/a
function View(req, res) { if (!req || req.constructor.name !== 'IncomingMessage') { throw new Error('Keystone.View Error: Express request object is required.'); } if (!res || res.constructor.name !== 'ServerResponse') { throw new Error('Keystone.View Error: Express response object is required.'); } this.req = req; this.res = res; this.initQueue = []; // executed first in series this.actionQueue = []; // executed second in parallel, if optional conditions are met this.queryQueue = []; // executed third in parallel this.renderQueue = []; // executed fourth in parallel }
n/a
addAsyncHooks = function () { var config = addHooks(this, _.flatten(_.toArray(arguments))); createHooks(this, config); return this; }
n/a
addHooks = function () { var config = addHooks(this, _.flatten(_.toArray(arguments))); createHooks(this, config); return this; }
n/a
addSyncHooks = function () { var config = addHooks(this, _.flatten(_.toArray(arguments))); createSyncHooks(this, config); return this; }
n/a
addThenableHooks = function () { var config = addHooks(this, _.flatten(_.toArray(arguments))); createThenableHooks(this, config); return this; }
n/a
allowHooks = function () { var args = _.flatten(_.toArray(arguments)); var q = this.__grappling.opts.qualifiers; _.each(args, function(hook) { if (!_.isString(hook)) { throw new Error('`allowHooks` expects (arrays of) Strings'); } var hookObj = parseHook(hook); var middleware = this.__grappling.middleware; if (hookObj.type) { if (hookObj.type !== q.pre && hookObj.type !== q.post) { throw new Error('Only "' + q.pre + '" and "' + q.post + '" types are allowed, not "' + hookObj.type + '"'); } middleware[hook] = middleware[hook] || []; } else { middleware[q.pre + ':' + hookObj.name] = middleware[q.pre + ':' + hookObj.name] || []; middleware[q.post + ':' + hookObj.name] = middleware[q.post + ':' + hookObj.name] || []; } }, this); return this; }
...
})(module.parent ? module.parent.paths[0] : module.paths[0]);
/**
* Keystone Class
*/
var Keystone = function () {
grappling.mixin(this).allowHooks('pre:static', 'pre:bodyparser',
x27;pre:session', 'pre:logger', 'pre:admin', 'pre:routes', 'pre:render', 'updates
', 'signin', 'signout');
this.lists = {};
this.fieldTypes = {};
this.paths = {};
this._options = {
'name': 'Keystone',
'brand': 'Keystone',
'admin path': 'keystone',
...
callAsyncHook = function () { //todo: decide whether we should enforce passing a callback var params = parseCallHookParams(this, _.toArray(arguments)); params.done = (_.isFunction(params.args[params.args.length - 1])) ? params.args.pop() : null; if(params.done){ var self = this; dezalgofy(function(safeDone) { iterateAsyncMiddleware(params.context, self.getMiddleware(params.hook), params.args, safeDone); }, params.done); }else{ iterateAsyncMiddleware(params.context, this.getMiddleware(params.hook), params.args); } return this; }
n/a
callHook = function () { //todo: decide whether we should enforce passing a callback var params = parseCallHookParams(this, _.toArray(arguments)); params.done = (_.isFunction(params.args[params.args.length - 1])) ? params.args.pop() : null; if(params.done){ var self = this; dezalgofy(function(safeDone) { iterateAsyncMiddleware(params.context, self.getMiddleware(params.hook), params.args, safeDone); }, params.done); }else{ iterateAsyncMiddleware(params.context, this.getMiddleware(params.hook), params.args); } return this; }
...
/**
* Applies Application updates
*/
Keystone.prototype.applyUpdates = function (callback) {
var self = this;
self.callHook('pre:updates', function (err) {
if (err) return callback(err);
require('./lib/updates').apply(function (err) {
if (err) return callback(err);
self.callHook('post:updates', callback);
});
});
};
...
callSyncHook = function () { var params = parseCallHookParams(this, _.toArray(arguments)); iterateSyncMiddleware(params.context, this.getMiddleware(params.hook), params.args); return this; }
n/a
callThenableHook = function () { var params = parseCallHookParams(this, _.toArray(arguments)); var deferred = {}; var thenable = this.__grappling.opts.createThenable(function(resolve, reject) { deferred.resolve = resolve; deferred.reject = reject; }); var self = this; dezalgofy(function(safeDone) { iterateAsyncMiddleware(params.context, self.getMiddleware(params.hook), params.args, safeDone); }, function(err) { if (err) { return deferred.reject(err); } return deferred.resolve(); }); return thenable; }
n/a
function createApplication() { var app = function(req, res, next) { app.handle(req, res, next); }; mixin(app, EventEmitter.prototype, false); mixin(app, proto, false); app.request = { __proto__: req, app: app }; app.response = { __proto__: res, app: app }; app.init(); return app; }
n/a
function Route(path) { this.path = path; this.stack = []; debug('new %s', path); // route handlers for various http methods this.methods = {}; }
n/a
express.Router = function (options) { var opts = options || {}; function router(req, res, next) { router.handle(req, res, next); } // mixin Router class functions router.__proto__ = proto; router.params = {}; router._params = []; router.caseSensitive = opts.caseSensitive; router.mergeParams = opts.mergeParams; router.strict = opts.strict; router.stack = []; return router; }
n/a
getMiddleware = function (qualifiedHook) { qualifyHook(parseHook(qualifiedHook)); var middleware = this.__grappling.middleware[qualifiedHook]; if (middleware) { return middleware.slice(0); } return []; }
n/a
hasMiddleware = function (qualifiedHook) { return this.getMiddleware(qualifiedHook).length > 0; }
n/a
hook = function () { var fns = _.toArray(arguments); var hook = fns.shift(); var output; qualifyHook(parseHook(hook)); if(fns.length){ output = this; }else{ output = this.__grappling.opts.createThenable(function(resolve){ fns = [resolve]; }); } addMiddleware(this, hook, fns); return output; }
n/a
hookable = function (qualifiedHook) { //eslint-disable-line no-unused-vars if (!this.__grappling.opts.strict) { return true; } var args = _.flatten(_.toArray(arguments)); return _.every(args, function(qualifiedHook) { qualifyHook(parseHook(qualifiedHook)); return !!this.__grappling.middleware[qualifiedHook]; }, this); }
n/a
function html(path, options) { html.super_.call(path, options); }
...
// Wrap the textarea
if (container.is('textarea')) {
container.before(editor);
textarea = container;
textarea.addClass('md-input');
editor.append(textarea);
} else {
var rawContent = typeof toMarkdown == 'function' ? toMarkdown(container.html
span>()) : container.html(),
currentContent = $.trim(rawContent);
// This is some arbitrary content that could be edited
textarea = $('<textarea/>', {
'class': 'md-input',
'val': currentContent
});
...
function Page(key, options) { if (!(this instanceof Page)) { return new Page(key, options); } this.options = assign({}, options); this.key = key; this.fields = {}; }
...
* ####Example:
*
* var homePage = new keystone.content.Page('home');
* // ...
* homePage.register();
*
* // later...
* var homePage = keystone.content.page('home');
*
* @api public
*/
Page.prototype.register = function () {
return keystone.content.page(this.key, this);
};
...
post = function () { var fns = _.toArray(arguments); var hookName = fns.shift(); var output; if(fns.length){ //old skool way with callbacks output = this; }else{ output = this.__grappling.opts.createThenable(function(resolve){ fns = [resolve]; }); } addMiddleware(this, qualifier + ':' + hookName, fns); return output; }
...
// Init API request helpers
router.use('/api', require('../middleware/apiError'));
router.use('/api', require('../middleware/logError'));
// #1: Session API
// TODO: this should respect keystone auth options
router.get('/api/session', require('../api/session/get'));
router.post('/api/session/signin', require('../api/session/signin'
;));
router.post('/api/session/signout', require('../api/session/signout'));
// #2: Session Routes
// Bind auth middleware (generic or custom) to * routes, allowing
// access to the generic signin page if generic auth is used
if (keystone.get('auth') === true) {
// TODO: poor separation of concerns; settings should be defaulted elsewhere
...
pre = function () { var fns = _.toArray(arguments); var hookName = fns.shift(); var output; if(fns.length){ //old skool way with callbacks output = this; }else{ output = this.__grappling.opts.createThenable(function(resolve){ fns = [resolve]; }); } addMiddleware(this, qualifier + ':' + hookName, fns); return output; }
...
} else {
doc.set(autokey.path, src);
return callback();
}
});
};
this.schema.pre('save', function (next) {
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
...
function text(path, options) { text.super_.call(path, options); }
...
btnClass = button.btnClass ? button.btnClass : 'md-editor__btn',
tabIndex = button.tabIndex ? button.tabIndex : '-1',
hotkey = typeof button.hotkey !== 'undefined' ? button.hotkey : '',
hotkeyCaption = typeof jQuery.hotkeys !== 'undefined' && hotkey !== '' ? ' (' +
hotkey + ')' : '';
// Construct the button object
buttonContainer = $('<button></button>');
buttonContainer.text(' ' + this.__localize(btnText)).addClass('md
-editor__btn').addClass(btnClass);
if (btnClass.match(/md-editor__btn\--(primary|success|info|warning|danger|link)/)) {
buttonContainer.removeClass('md-editor__btn');
}
buttonContainer.attr({
'type': 'button',
'title': this.__localize(button.title) + hotkeyCaption,
'tabindex': tabIndex,
...
unhook = function () {
var fns = _.toArray(arguments);
var hook = fns.shift();
var hookObj = parseHook(hook);
var middleware = this.__grappling.middleware;
var q = this.__grappling.opts.qualifiers;
if (hookObj.type || fns.length) {
qualifyHook(hookObj);
if (middleware[hook]) middleware[hook] = (fns.length ) ? _.without.apply(null, [middleware[hook]].concat(fns)) : [];
} else if (hookObj.name) {
/* istanbul ignore else: nothing _should_ happen */
if (middleware[q.pre + ':' + hookObj.name]) middleware[q.pre + ':' + hookObj.name] = [];
/* istanbul ignore else: nothing _should_ happen */
if (middleware[q.post + ':' + hookObj.name]) middleware[q.post + ':' + hookObj.name] = [];
} else {
_.each(middleware, function(callbacks, hook) {
middleware[hook] = [];
});
}
return this;
}
n/a
function UpdateHandler(list, item, req, options) { this.list = list; this.item = item; this.req = req; if (arguments.length === 5) { // old signature; set options to the last argument options = arguments[4]; } this.options = options || {}; }
n/a
function azurefile(list, path, options) {
throw new Error('The AzureFile field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('pre:upload');
this._underscoreMethods = ['format', 'uploadFile'];
this._fixedSize = 'full';
// 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\nAzureFile fields (' + list.key + '.' + path + ') do not currently support being used
as initial fields.\n');
}
var self = this;
options.filenameFormatter = options.filenameFormatter || function (item, filename) { return filename; };
options.containerFormatter = options.containerFormatter || function (item, filename) { return self.azurefileconfig.container; };//
eslint-disable-line no-unused-vars
azurefile.super_.call(this, list, path, options);
// validate azurefile config (has to happen after super_.call)
if (!this.azurefileconfig) {
throw new Error('Invalid Configuration\n\n'
+ 'AzureFile fields (' + list.key + '.' + path + ') require the "azurefile config" option to be set.\n\n'
+ 'See http://keystonejs.com/docs/configuration/#services-azure for more information.\n');
}
// TODO; this is really bad, we shouldn't be overwriting global env!
process.env.AZURE_STORAGE_ACCOUNT = this.azurefileconfig.account;
process.env.AZURE_STORAGE_ACCESS_KEY = this.azurefileconfig.key;
this.azurefileconfig.container = this.azurefileconfig.container || 'keystone';
// Could be more pre- hooks, just upload for now
if (options.pre && options.pre.upload) {
this.pre('upload', options.pre.upload);
}
*/
}
n/a
function boolean(list, path, options) { this._nativeType = Boolean; this._properties = ['indent']; this._fixedSize = 'full'; this.indent = (options.indent) ? true : false; boolean.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function cloudinaryimage(list, path, options) { this._underscoreMethods = ['format']; this._fixedSize = 'full'; this._properties = ['select', 'selectPrefix', 'autoCleanup']; if (options.filenameAsPublicID) { // Produces the same result as the legacy filenameAsPublicID option options.generateFilename = nameFunctions.originalFilename; options.whenExists = 'overwrite'; } options = assign({}, DEFAULT_OPTIONS, options); options.generateFilename = ensureCallback(options.generateFilename); cloudinaryimage.super_.call(this, list, path, options); // validate cloudinary config if (!keystone.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://keystonejs.com/docs/configuration/#services-cloudinary for more information.\n' ); } }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function cloudinaryimages(list, path, options) { this._underscoreMethods = ['format']; this._fixedSize = 'full'; this._properties = ['select', 'selectPrefix', 'autoCleanup', 'publicID', 'folder', 'filenameAsPublicID']; cloudinaryimages.super_.call(this, list, path, options); // validate cloudinary config if (!keystone.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://keystonejs.com/docs/configuration/#services-cloudinary for more information.\n'); } }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function code(list, path, options) { this._nativeType = String; this._defaultSize = 'full'; this.height = options.height || 180; this.lang = options.lang || options.language; this._properties = ['editor', 'height', 'lang']; this.codemirror = options.codemirror || {}; this.editor = assign({ mode: this.lang }, this.codemirror); code.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function color(list, path, options) { this._nativeType = String; color.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function datearray(list, path, options) { this._nativeType = [Date]; this._defaultSize = 'medium'; this._underscoreMethods = ['format']; this._properties = ['formatString']; this.parseFormatString = options.parseFormat || 'YYYY-MM-DD'; this.formatString = (options.format === false) ? false : (options.format || 'Do MMM YYYY'); if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.DateArray: options.format must be a string.'); } this.separator = options.separator || ' | '; datearray.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function date(list, path, options) { this._nativeType = Date; this._underscoreMethods = ['format', 'moment', 'parse']; this._fixedSize = 'medium'; this._properties = ['formatString', 'yearRange', 'isUTC', 'inputFormat']; this.parseFormatString = options.inputFormat || 'YYYY-MM-DD'; this.formatString = (options.format === false) ? false : (options.format || 'Do MMM YYYY'); this.yearRange = options.yearRange; this.isUTC = options.utc || false; if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.Date: options.format must be a string.'); } date.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function datetime(list, path, options) { this._nativeType = Date; this._underscoreMethods = ['format', 'moment', 'parse']; this._fixedSize = 'full'; this._properties = ['formatString', 'isUTC']; this.typeDescription = 'date and time'; this.parseFormatString = options.parseFormat || parseFormats; this.formatString = (options.format === false) ? false : (options.format || 'YYYY-MM-DD h:mm:ss a'); this.isUTC = options.utc || false; if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.DateTime: options.format must be a string.'); } datetime.super_.call(this, list, path, options); this.paths = { date: this.path + '_date', time: this.path + '_time', tzOffset: this.path + '_tzOffset', }; }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function email(list, path, options) { this._nativeType = String; this._underscoreMethods = ['gravatarUrl']; this.typeDescription = 'email address'; email.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function embedly(list, path, options) { this._underscoreMethods = ['reset']; this._fixedSize = 'full'; this.fromPath = options.from; this.embedlyOptions = options.options || {}; // check and api key has been set, or bail. if (!keystone.get('embedly api key')) { throw new Error('Invalid Configuration\n\n' + 'Embedly fields (' + list.key + '.' + path + ') require the "embedly api key" option to be set.\n\n' + 'See http://keystonejs.com/docs/configuration/#services-embedly for more information.\n'); } // ensure a fromPath has been defined if (!options.from) { throw new Error('Invalid Configuration\n\n' + 'Embedly fields (' + list.key + '.' + path + ') require a fromPath option to be set.\n' + 'See http://keystonejs.com/docs/database/#fieldtypes-embedly for more information.\n'); } // embedly fields cannot be set as initial fields if (options.initial) { throw new Error('Invalid Configuration\n\n' + 'Embedly fields (' + list.key + '.' + path + ') cannot be set as initial fields.\n'); } embedly.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function azurefile(list, path, options) {
throw new Error('The AzureFile field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('pre:upload');
this._underscoreMethods = ['format', 'uploadFile'];
this._fixedSize = 'full';
// 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\nAzureFile fields (' + list.key + '.' + path + ') do not currently support being used
as initial fields.\n');
}
var self = this;
options.filenameFormatter = options.filenameFormatter || function (item, filename) { return filename; };
options.containerFormatter = options.containerFormatter || function (item, filename) { return self.azurefileconfig.container; };//
eslint-disable-line no-unused-vars
azurefile.super_.call(this, list, path, options);
// validate azurefile config (has to happen after super_.call)
if (!this.azurefileconfig) {
throw new Error('Invalid Configuration\n\n'
+ 'AzureFile fields (' + list.key + '.' + path + ') require the "azurefile config" option to be set.\n\n'
+ 'See http://keystonejs.com/docs/configuration/#services-azure for more information.\n');
}
// TODO; this is really bad, we shouldn't be overwriting global env!
process.env.AZURE_STORAGE_ACCOUNT = this.azurefileconfig.account;
process.env.AZURE_STORAGE_ACCESS_KEY = this.azurefileconfig.key;
this.azurefileconfig.container = this.azurefileconfig.container || 'keystone';
// Could be more pre- hooks, just upload for now
if (options.pre && options.pre.upload) {
this.pre('upload', options.pre.upload);
}
*/
}
n/a
function boolean(list, path, options) { this._nativeType = Boolean; this._properties = ['indent']; this._fixedSize = 'full'; this.indent = (options.indent) ? true : false; boolean.super_.call(this, list, path, options); }
n/a
function cloudinaryimage(list, path, options) { this._underscoreMethods = ['format']; this._fixedSize = 'full'; this._properties = ['select', 'selectPrefix', 'autoCleanup']; if (options.filenameAsPublicID) { // Produces the same result as the legacy filenameAsPublicID option options.generateFilename = nameFunctions.originalFilename; options.whenExists = 'overwrite'; } options = assign({}, DEFAULT_OPTIONS, options); options.generateFilename = ensureCallback(options.generateFilename); cloudinaryimage.super_.call(this, list, path, options); // validate cloudinary config if (!keystone.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://keystonejs.com/docs/configuration/#services-cloudinary for more information.\n' ); } }
n/a
function cloudinaryimages(list, path, options) { this._underscoreMethods = ['format']; this._fixedSize = 'full'; this._properties = ['select', 'selectPrefix', 'autoCleanup', 'publicID', 'folder', 'filenameAsPublicID']; cloudinaryimages.super_.call(this, list, path, options); // validate cloudinary config if (!keystone.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://keystonejs.com/docs/configuration/#services-cloudinary for more information.\n'); } }
n/a
function code(list, path, options) { this._nativeType = String; this._defaultSize = 'full'; this.height = options.height || 180; this.lang = options.lang || options.language; this._properties = ['editor', 'height', 'lang']; this.codemirror = options.codemirror || {}; this.editor = assign({ mode: this.lang }, this.codemirror); code.super_.call(this, list, path, options); }
n/a
function color(list, path, options) { this._nativeType = String; color.super_.call(this, list, path, options); }
n/a
function date(list, path, options) { this._nativeType = Date; this._underscoreMethods = ['format', 'moment', 'parse']; this._fixedSize = 'medium'; this._properties = ['formatString', 'yearRange', 'isUTC', 'inputFormat']; this.parseFormatString = options.inputFormat || 'YYYY-MM-DD'; this.formatString = (options.format === false) ? false : (options.format || 'Do MMM YYYY'); this.yearRange = options.yearRange; this.isUTC = options.utc || false; if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.Date: options.format must be a string.'); } date.super_.call(this, list, path, options); }
n/a
function datearray(list, path, options) { this._nativeType = [Date]; this._defaultSize = 'medium'; this._underscoreMethods = ['format']; this._properties = ['formatString']; this.parseFormatString = options.parseFormat || 'YYYY-MM-DD'; this.formatString = (options.format === false) ? false : (options.format || 'Do MMM YYYY'); if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.DateArray: options.format must be a string.'); } this.separator = options.separator || ' | '; datearray.super_.call(this, list, path, options); }
n/a
function datetime(list, path, options) { this._nativeType = Date; this._underscoreMethods = ['format', 'moment', 'parse']; this._fixedSize = 'full'; this._properties = ['formatString', 'isUTC']; this.typeDescription = 'date and time'; this.parseFormatString = options.parseFormat || parseFormats; this.formatString = (options.format === false) ? false : (options.format || 'YYYY-MM-DD h:mm:ss a'); this.isUTC = options.utc || false; if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.DateTime: options.format must be a string.'); } datetime.super_.call(this, list, path, options); this.paths = { date: this.path + '_date', time: this.path + '_time', tzOffset: this.path + '_tzOffset', }; }
n/a
function email(list, path, options) { this._nativeType = String; this._underscoreMethods = ['gravatarUrl']; this.typeDescription = 'email address'; email.super_.call(this, list, path, options); }
n/a
function embedly(list, path, options) { this._underscoreMethods = ['reset']; this._fixedSize = 'full'; this.fromPath = options.from; this.embedlyOptions = options.options || {}; // check and api key has been set, or bail. if (!keystone.get('embedly api key')) { throw new Error('Invalid Configuration\n\n' + 'Embedly fields (' + list.key + '.' + path + ') require the "embedly api key" option to be set.\n\n' + 'See http://keystonejs.com/docs/configuration/#services-embedly for more information.\n'); } // ensure a fromPath has been defined if (!options.from) { throw new Error('Invalid Configuration\n\n' + 'Embedly fields (' + list.key + '.' + path + ') require a fromPath option to be set.\n' + 'See http://keystonejs.com/docs/database/#fieldtypes-embedly for more information.\n'); } // embedly fields cannot be set as initial fields if (options.initial) { throw new Error('Invalid Configuration\n\n' + 'Embedly fields (' + list.key + '.' + path + ') cannot be set as initial fields.\n'); } embedly.super_.call(this, list, path, options); }
n/a
function file(list, path, options) { this._underscoreMethods = ['format', 'upload', 'remove', 'reset']; this._fixedSize = 'full'; if (!options.storage) { throw new Error('Invalid Configuration\n\n' + 'File fields (' + list.key + '.' + path + ') require storage to be provided.'); } this.storage = options.storage; file.super_.call(this, list, path, options); }
n/a
function geopoint(list, path, options) { this._fixedSize = 'medium'; geopoint.super_.call(this, list, path, options); }
n/a
function html(list, path, options) { this._nativeType = String; this._defaultSize = 'full'; this.wysiwyg = options.wysiwyg || false; this.height = options.height || 180; this._properties = ['wysiwyg', 'height']; html.super_.call(this, list, path, options); }
n/a
function key(list, path, options) { this._nativeType = String; this._defaultSize = 'medium'; this.separator = options.separator || '-'; key.super_.call(this, list, path, options); }
n/a
function localfile(list, path, options) {
throw new Error('The LocalFile field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('move');
this._underscoreMethods = ['format', 'uploadFile'];
this._fixedSize = 'full';
this.autoCleanup = options.autoCleanup || false;
if (options.overwrite !== false) {
options.overwrite = true;
}
localfile.super_.call(this, list, path, options);
// validate destination dir
if (!options.dest) {
throw new Error('Invalid Configuration\n\n'
+ 'localfile fields (' + list.key + '.' + path + ') require the "dest" option to be set.');
}
// Allow hook into before and after
if (options.pre && options.pre.move) {
this.pre('move', options.pre.move);
}
if (options.post && options.post.move) {
this.post('move', options.post.move);
}
*/
}
n/a
function localfiles(list, path, options) {
throw new Error('The LocalFiles field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('move');
this._underscoreMethods = ['format', 'uploadFiles'];
this._fixedSize = 'full';
// 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'
+ 'localfiles fields (' + list.key + '.' + path + ') do not currently support being used as initial fields.\n');
}
if (options.overwrite !== false) {
options.overwrite = true;
}
localfiles.super_.call(this, list, path, options);
// validate destination dir
if (!options.dest) {
throw new Error('Invalid Configuration\n\n'
+ 'localfiles fields (' + list.key + '.' + path + ') require the "dest" option to be set.');
}
// Allow hook into before and after
if (options.pre && options.pre.move) {
this.pre('move', options.pre.move);
}
if (options.post && options.post.move) {
this.post('move', options.post.move);
}
*/
}
n/a
function location(list, path, options) { this._underscoreMethods = ['format', 'googleLookup', 'kmFrom', 'milesFrom']; this._fixedSize = 'full'; this._properties = ['enableMapsAPI']; this.enableMapsAPI = (options.enableImprove === true || (options.enableImprove !== false && keystone.get('google server api key '))) ? true : false; // Throw on invalid options in 4.0 (remove for 5.0) if ('geocodeGoogle' in options) { throw new Error('The geocodeGoogle option for Location fields has been renamed to enableImprove'); } if (!options.defaults) { options.defaults = {}; } if (options.required) { if (Array.isArray(options.required)) { // required can be specified as an array of paths this.requiredPaths = options.required; } else if (typeof options.required === 'string') { // or it can be specified as a comma-delimited list this.requiredPaths = options.required.replace(/,/g, ' ').split(/\s+/); } // options.required should always be simplified to a boolean options.required = true; } // default this.requiredPaths if (!this.requiredPaths) { this.requiredPaths = ['street1', 'suburb']; } location.super_.call(this, list, path, options); }
n/a
function markdown(list, path, options) { this._defaultSize = 'full'; this.toolbarOptions = options.toolbarOptions || {}; this.markedOptions = options.markedOptions || {}; this.height = options.height || 90; this.wysiwyg = ('wysiwyg' in options) ? options.wysiwyg : true; this._properties = ['wysiwyg', 'height', 'toolbarOptions']; markdown.super_.call(this, list, path, options); }
n/a
function money(list, path, options) { this.currency = options.currency; this._nativeType = Number; this._underscoreMethods = ['format']; this._properties = ['currency']; this._fixedSize = 'small'; this._formatString = (options.format === false) ? false : (options.format || '$0,0.00'); if (this._formatString && typeof this._formatString !== 'string') { throw new Error('FieldType.Money: options.format must be a string.'); } money.super_.call(this, list, path, options); }
n/a
function name(list, path, options) { this._fixedSize = 'full'; options.default = { first: '', last: '' }; name.super_.call(this, list, path, options); }
n/a
function number(list, path, options) { this._nativeType = Number; this._fixedSize = 'small'; this._underscoreMethods = ['format']; this.formatString = (options.format === false) ? false : (options.format || '0,0[.][000000000000]'); if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.Number: options.format must be a string.'); } number.super_.call(this, list, path, options); }
n/a
function numberarray(list, path, options) { this._nativeType = [Number]; this._underscoreMethods = ['format']; this._formatString = (options.format === false) ? false : (options.format || '0,0[.][000000000000]'); this._defaultSize = 'small'; if (this._formatString && typeof this._formatString !== 'string') { throw new Error('FieldType.NumberArray: options.format must be a string.'); } this.separator = options.separator || ' | '; numberarray.super_.call(this, list, path, options); }
n/a
function password(list, path, options) { this.options = options; this._nativeType = String; this._underscoreMethods = ['format', 'compare']; this._fixedSize = 'full'; // You can't sort on password fields options.nosort = true; this.workFactor = options.workFactor || 10; password.super_.call(this, list, path, options); for (var key in this.options.complexity) { if ({}.hasOwnProperty.call(this.options.complexity, key)) { if (key in regexChunk !== key in this.options.complexity) { throw new Error('FieldType.Password: options.complexity - option does not exist.'); } if (typeof this.options.complexity[key] !== 'boolean') { throw new Error('FieldType.Password: options.complexity - Value must be boolean.'); } } } if (this.options.max <= this.options.min) { throw new Error('FieldType.Password: options - min must be set at a lower value than max.'); } }
n/a
function relationship(list, path, options) { this.many = (options.many) ? true : false; this.filters = options.filters; this.createInline = (options.createInline) ? true : false; this._defaultSize = 'full'; this._nativeType = keystone.mongoose.Schema.Types.ObjectId; this._underscoreMethods = ['format', 'getExpandedData']; this._properties = ['isValid', 'many', 'filters', 'createInline']; relationship.super_.call(this, list, path, options); }
n/a
function s3file(list, path, options) {
throw new Error('The S3File field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('pre:upload');
this._underscoreMethods = ['format', 'uploadFile'];
this._fixedSize = 'full';
// 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'
+ 'S3File fields (' + list.key + '.' + path + ') do not currently support being used as initial fields.\n');
}
s3file.super_.call(this, list, path, options);
// validate s3 config (has to happen after super_.call)
if (!this.s3config) {
throw new Error('Invalid Configuration\n\n'
+ 'S3File fields (' + list.key + '.' + path + ') require the "s3 config" option to be set.\n\n'
+ 'See http://keystonejs.com/docs/configuration/#services-amazons3 for more information.\n');
}
// Could be more pre- hooks, just upload for now
if (options.pre && options.pre.upload) {
this.pre('upload', options.pre.upload);
}
*/
}
n/a
function select(list, path, options) { this.ui = options.ui || 'select'; this.numeric = options.numeric ? true : false; this._nativeType = (options.numeric) ? Number : String; this._underscoreMethods = ['format', 'pluck']; this._properties = ['ops', 'numeric']; if (typeof options.options === 'string') { options.options = options.options.split(','); } if (!Array.isArray(options.options)) { throw new Error('Select fields require an options array.'); } this.ops = options.options.map(function (i) { var op = typeof i === 'string' ? { value: i.trim(), label: utils.keyToLabel(i) } : i; if (!_.isObject(op)) { op = { label: '' + i, value: '' + i }; } if (options.numeric && !_.isNumber(op.value)) { op.value = Number(op.value); } return op; }); // undefined options.emptyOption defaults to true if (options.emptyOption === undefined) { options.emptyOption = true; } // ensure this.emptyOption is a boolean this.emptyOption = !!options.emptyOption; // cached maps for options, labels and values this.map = utils.optionsMap(this.ops); this.labels = utils.optionsMap(this.ops, 'label'); this.values = _.map(this.ops, 'value'); select.super_.call(this, list, path, options); }
n/a
function text(list, path, options) { this.options = options; this._nativeType = String; this._properties = ['monospace']; this._underscoreMethods = ['crop']; text.super_.call(this, list, path, options); }
n/a
function textarray(list, path, options) { this._nativeType = [String]; this._underscoreMethods = ['format']; this.separator = options.separator || ' | '; textarray.super_.call(this, list, path, options); }
n/a
function textarea(list, path, options) { this._nativeType = String; this._underscoreMethods = ['format', 'crop']; this.height = options.height || 90; this.multiline = true; this._properties = ['height', 'multiline']; textarea.super_.call(this, list, path, options); }
n/a
function url(list, path, options) { this._nativeType = String; this._underscoreMethods = ['format']; url.super_.call(this, list, path, options); }
n/a
addToSchema = function (schema) {
var azure = require('azure-storage');
var field = this;
var paths = this.paths = {
// fields
filename: this.path + '.filename',
path: this.path + '.path',
size: this.path + '.size',
filetype: this.path + '.filetype',
url: this.path + '.url',
etag: this.path + '.etag',
container: this.path + '.container',
// virtuals
exists: this.path + '.exists',
upload: this.path + '_upload',
action: this.path + '_action',
};
var schemaPaths = this._path.addTo({}, {
filename: String,
path: String,
size: Number,
filetype: String,
url: String,
etag: String,
container: String,
});
schema.add(schemaPaths);
var exists = function (item) {
return (item.get(paths.url) ? true : false);
};
// The .exists virtual indicates whether a file is stored
schema.virtual(paths.exists).get(function () {
return schemaMethods.exists.apply(this);
});
var reset = function (item) {
item.set(field.path, {
filename: '',
path: '',
size: 0,
filetype: '',
url: '',
});
};
var schemaMethods = {
exists: function () {
return exists(this);
},
/**
* Resets the value of the field
*
* @api public
*/
reset: function () {
try {
azure.createBlobService().deleteBlob(this.get(paths.container), this.get(paths.filename), function () {});
} catch (e) {} // eslint-disable-line no-empty
reset(this);
},
/**
* Deletes the file from AzureFile and resets the field
*
* @api public
*/
delete: function () {
try {
azure.createBlobService().blobService.deleteBlob(this.get(paths.container), this.get(paths.filename), function () {});
} catch (e) {} // eslint-disable-line no-empty
reset(this);
},
};
_.forEach(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();
}
n/a
format = function (item) { return item.get(this.paths.url); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
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.files && req.files[paths.upload] && req.files[paths.upload].size) { return field.uploadFile(item, req.files[paths.upload], true, callback); } return callback(); }; }
n/a
inputIsValid = function (data) { // eslint-disable-line no-unused-vars // TODO - how should file field input be validated? return true; }
n/a
isModified = function (item) { return item.isModified(this.paths.url); }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
updateItem = function (item, data, callback) { // TODO - direct updating of data (not via upload) process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
uploadFile = function (item, file, update, callback) { var azure = require('azure-storage'); var field = this; var filetype = file.mimetype || file.type; if (field.options.allowedTypes && !_.contains(field.options.allowedTypes, filetype)) { return callback(new Error('Unsupported File Type: ' + filetype)); } if (typeof update === 'function') { callback = update; update = false; } var doUpload = function () { var blobService = azure.createBlobService(); var container = field.options.containerFormatter(item, file.name); blobService.createContainerIfNotExists(container, { publicAccessLevel: 'blob' }, function (err) { if (err) return callback(err); blobService.createBlockBlobFromLocalFile(container, field.options.filenameFormatter(item, file.name), file.path, function (err , blob, res) { if (err) return callback(err); var fileData = { filename: blob.blob, size: file.size, filetype: filetype, etag: blob.etag, container: container, url: 'http://' + field.azurefileconfig.account + '.blob.core.windows.net/' + container + '/' + blob.blob, }; if (update) { item.set(field.path, fileData); } callback(null, fileData); }); }); }; this.callHook('pre:upload', item, file, function (err) { if (err) return callback(err); doUpload(); }); }
...
if (/^(delete|reset)$/.test(action)) {
field.apply(item, action);
}
}
if (req.files && req.files[paths.upload] && req.files[paths.upload].size) {
return field.uploadFile(item, req.files[paths.upload], true, callback);
}
return callback();
};
};
...
addFilterToQuery = function (filter) { var query = {}; if (!filter.value || filter.value === 'false') { query[this.path] = { $ne: true }; } else { query[this.path] = true; } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
inputIsValid = function (data, required) { if (required) { return (data[this.path] === true || data[this.path] === 'true') ? true : false; } else { return true; } }
n/a
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); if (typeof value === 'undefined') { return process.nextTick(callback); } if (!value || value === 'false') { if (item.get(this.path) !== false) { item.set(this.path, false); } } else if (!item.get(this.path)) { item.set(this.path, true); } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); var result = true; if (value !== undefined && value !== null && typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') { result = false; } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = (value && value !== 'false') || item.get(this.path) ? true : false; utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addToSchema = function (schema) {
var cloudinary = require('cloudinary');
var field = this;
var paths = this.paths = {
// cloudinary fields
public_id: this.path + '.public_id',
version: this.path + '.version',
signature: this.path + '.signature',
format: this.path + '.format',
resource_type: this.path + '.resource_type',
url: this.path + '.url',
width: this.path + '.width',
height: this.path + '.height',
secure_url: this.path + '.secure_url',
// virtuals
exists: this.path + '.exists',
folder: this.path + '.folder',
// form paths
select: this.path + '_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);
});
// 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 = (typeof options === 'object') ? options : {};
if (!('fetch_format' in options) && keystone.get('cloudinary webp') !== false) {
options.fetch_format = 'auto';
}
if (!('progressive' in options) && keystone.get('cloudinary progressive') !== false) {
options.progressive = true;
}
if (!('secure' in options) && keystone.get('cloudinary secure')) {
options.secure = true;
}
options.version = item.get(paths.version);
options.format = options.format || item.get(paths.format);
return cloudinary.url(item.get(paths.public_id), options);
};
var reset = function (item) {
item.set(field.path, getEmptyValue());
};
var addSize = function (options, width, height, other) {
if (width) options.width = width;
if (height) options.height = height;
if (typeof other === 'object') {
assign(options, other);
}
return options;
};
var schemaMethods = {
exists: function () {
return exists(this);
},
folder: function () {
return field.getFolder();
},
src: function (options) {
return src(this, options);
},
tag: function (options) {
return exists(this) ? cloudinary.image(this.get(field.path).public_id, 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', gravity: 'faces' }, width, height, options));
},
lfill: function (width, height, options) {
return src(this, addSize({ crop: 'lfill', gravity: 'faces' }, 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', gravity: 'faces' }, width, height, options));
},
thumbnail: function (width, height, options) {
return src(this, addSize({ crop: 'thumb', gravity: 'faces' }, 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 () {
var _this = this;
var promise = new Promise(function (resolve) {
cloudinary.uploader.destroy(_this.get(paths.public_id), function (result) { ...
n/a
fileExists = function (filename, callback) { var cloudinary = require('cloudinary'); cloudinary.api.resource(filename, function (result) { if (result.error && result.error.http_code === 404) { // File doesn't exist callback(null, false); } else if (result.error) { // Error callback(result.error, null); } else { // File exists callback(null, true); } }); }
n/a
format = function (item) { return item.get(this.paths.url); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getData = function (item) { var value = item.get(this.path); return typeof value === 'object' ? value : {}; }
...
process.exit(1);
}
}
break;
case 'module root':
// if relative path is used, resolve it based on the caller's path
if (!isAbsolutePath(value)) {
var caller = callerId.getData();
value = path.resolve(path.dirname(caller.filePath), value);
}
break;
case 'app':
this.app = value;
break;
case 'mongoose':
...
getFilename = function (file, callback) { var self = this; switch (self.options.whenExists) { case 'overwrite': self.options.generateFilename(file, 0, callback); break; case 'error': self.options.generateFilename(file, 0, function (err, filename) { if (err) return callback(err); self.fileExists(filename, function (err, result) { if (err) return callback(err); if (result === true) return callback(Error('File already exists')); callback(null, filename); }); }); break; case 'retry': self.retryFilename(0, file, callback); break; } }
...
return _react2.default.createElement(
'div',
null,
this.hasImage() ? _react2.default.createElement(
_FileChangeMessage2.default,
null,
this.getFilename()
) : null,
showChangeMessage && this.renderChangeMessage()
);
},
renderChangeMessage: function renderChangeMessage() {
if (this.state.userSelectedFile) {
return _react2.default.createElement(
...
getFolder = function () { var folder = null; if (keystone.get('cloudinary folders') || this.options.folder) { if (typeof this.options.folder === 'string') { folder = this.options.folder; } else { var folderList = keystone.get('cloudinary prefix') ? [keystone.get('cloudinary prefix')] : []; folderList.push(this.list.path); folderList.push(this.path); folder = folderList.join('/'); } } return folder; }
...
};
var schemaMethods = {
exists: function () {
return exists(this);
},
folder: function () {
return field.getFolder();
},
src: function (options) {
return src(this, options);
},
tag: function (options) {
return exists(this) ? cloudinary.image(this.get(field.path).public_id, options) : '';
},
...
getRequestHandler = function (item, req, paths, callback) { var cloudinary = require('cloudinary'); 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 = keystone.get('cloudinary prefix') || ''; var imageDelete; if (tp.length) { tp += '_'; } var uploadOptions = { tags: [tp + field.list.path + '_' + field.path, tp + field.list.path + '_' + field.path + '_' + item.id], }; if (keystone.get('cloudinary folders')) { uploadOptions.folder = item.get(paths.folder); } if (keystone.get('cloudinary prefix')) { uploadOptions.tags.push(keystone.get('cloudinary prefix')); } if (keystone.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; } } else if (field.options.filenameAsPublicID) { uploadOptions.public_id = req.files[paths.upload].originalname.substring(0, req.files[paths.upload].originalname.lastIndexOf ('.')); } if (field.options.autoCleanup && item.get(field.paths.exists)) { // capture image delete promise imageDelete = field.apply(item, 'delete'); } // callback to be called upon completion of the 'upload' method var uploadComplete = function (result) { if (result.error) { callback(result.error); } else { item.set(field.path, result); callback(); } }; // upload immediately if image is not being delete if (typeof imageDelete === 'undefined') { field.apply(item, 'upload', req.files[paths.upload].path, uploadOptions).then(uploadComplete); } else { // otherwise wait until image is deleted before uploading // this avoids problems when deleting/uploading images with the same public_id (issue #598) imageDelete.then(function (result) { if (result.error) { callback(result.error); } else { field.apply(item, 'upload', req.files[paths.upload].path, uploadOptions).then(uploadComplete); } }); } } else { callback(); } }; }
n/a
inputIsValid = function () { return true; }
n/a
isModified = function (item) { return item.isModified(this.paths.public_id); }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
retryFilename = function (attempt, file, callback) { var self = this; if (attempt > self.options.retryAttempts) { return callback(Error('Unique filename could not be generated; Maximum attempts exceeded')); } self.options.generateFilename(file, attempt, function (err, filename) { if (err) return callback(err); self.fileExists(filename, function (err, exists) { if (err) return callback(err); if (exists) return self.retryFilename(attempt + 1, file, callback); callback(null, filename); }); }); }
n/a
updateItem = function (item, data, files, callback) { // Process arguments if (typeof files === 'function') { callback = files; files = {}; } if (!files) { files = {}; } var cloudinary = require('cloudinary'); var field = this; // Prepare values var value = this.getValueFromData(data); var uploadedFile; // Providing the string "remove" removes the file and resets the field if (value === 'remove') { cloudinary.uploader.destroy(item.get(field.paths.public_id), function (result) { if (result.error) { callback(result.error); } else { item.set(field.path, getEmptyValue()); callback(); } }); return; } // Find an uploaded file in the files argument, either referenced in the // data argument or named with the field path / field_upload path + suffix // Base64 data and remote URLs are also accepted as images to upload if (typeof value === 'string' && value.substr(0, 7) === 'upload:') { uploadedFile = files[value.substr(7)]; } else if (typeof value === 'string' && /^(data:[a-z\/]+;base64)|(https?\:\/\/)/.test(value)) { uploadedFile = { path: value }; } else { uploadedFile = this.getValueFromData(files) || this.getValueFromData(files, '_upload'); } // Ensure a valid file was uploaded, else null out the value if (uploadedFile && !uploadedFile.path) { uploadedFile = undefined; } // If we have a file to upload, we do that and stop here if (uploadedFile) { var tagPrefix = keystone.get('cloudinary prefix') || ''; var uploadOptions = { tags: [], }; if (tagPrefix.length) { uploadOptions.tags.push(tagPrefix); tagPrefix += '_'; } uploadOptions.tags.push(tagPrefix + field.list.path + '_' + field.path); if (keystone.get('env') !== 'production') { uploadOptions.tags.push(tagPrefix + 'dev'); } var folder = this.getFolder(); if (folder) { uploadOptions.folder = folder; } this.getFilename(uploadedFile, function (err, filename) { if (err) return callback(err); // If an undefined filename is returned, Cloudinary will automatically generate a unique // filename. Therefore undefined is a valid filename value. if (filename !== undefined) { filename = sanitize(filename); uploadOptions.public_id = trimSupportedFileExtensions(filename); } // TODO: implement autoCleanup; should delete existing images before uploading cloudinary.uploader.upload(uploadedFile.path, function (result) { if (result.error) { return callback(result.error); } else { item.set(field.path, result); return callback(); } }, uploadOptions); }); return; } // Empty / null values reset the field if (value === null || value === '' || (typeof value === 'object' && !Object.keys(value).length)) { value = getEmptyValue(); } // If there is a valid value at this point, set it on the field if (typeof value === 'object') { item.set(this.path, value); } utils.defer(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); var result = validateInput(value); utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { // TODO: We need to also get the `files` argument, so we can check for // uploaded files. without it, this will return false negatives so we // can't actually validate required input at the moment. var result = true; // var value = this.getValueFromData(data); // var result = (value || item.get(this.path).public_id) ? true : false; utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addToSchema = function (schema) { var cloudinary = require('cloudinary'); var mongoose = keystone.mongoose; var field = this; this.paths = { // virtuals folder: this.path + '.folder', // form paths upload: this.path + '_upload', uploads: this.path + '_uploads', action: this.path + '_action', }; 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, }); // Generate cloudinary folder used to upload/select images var folder = function (item) { // eslint-disable-line no-unused-vars var folderValue = ''; if (keystone.get('cloudinary folders')) { if (field.options.folder) { folderValue = field.options.folder; } else { var folderList = keystone.get('cloudinary prefix') ? [keystone.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(field.paths.folder).get(function () { return folder(this); }); var src = function (img, options) { if (keystone.get('cloudinary secure')) { options = options || {}; options.secure = true; } options.format = options.format || img.format; return img.public_id ? cloudinary.url(img.public_id, options) : ''; }; var addSize = function (options, width, height, other) { if (width) options.width = width; if (height) options.height = height; if (typeof other === 'object') { assign(options, other); } return options; }; ImageSchema.method('src', function (options) { return src(this, options); }); ImageSchema.method('scale', function (width, height, options) { return src(this, addSize({ crop: 'scale' }, width, height, options)); }); ImageSchema.method('fill', function (width, height, options) { return src(this, addSize({ crop: 'fill', gravity: 'faces' }, width, height, options)); }); ImageSchema.method('lfill', function (width, height, options) { return src(this, addSize({ crop: 'lfill', gravity: 'faces' }, width, height, options)); }); ImageSchema.method('fit', function (width, height, options) { return src(this, addSize({ crop: 'fit' }, width, height, options)); }); ImageSchema.method('limit', function (width, height, options) { return src(this, addSize({ crop: 'limit' }, width, height, options)); }); ImageSchema.method('pad', function (width, height, options) { return src(this, addSize({ crop: 'pad' }, width, height, options)); }); ImageSchema.method('lpad', function (width, height, options) { return src(this, addSize({ crop: 'lpad' }, width, height, options)); }); ImageSchema.method('crop', function (width, height, options) { return src(this, addSize({ crop: 'crop', gravity: 'faces' }, width, height, options)); }); ImageSchema.method('thumbnail', function (width, height, options) { return src(this, addSize({ crop: 'thumb', gravity: 'faces' }, width, height, options)); }); schema.add(this._path.addTo({}, [ImageSchema])); this.removeImage = function (item, id, method, callback) { var images = item.get(field.path); if (typeof id !== 'number') { 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((typeof callback !== 'function') ? 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(); }
n/a
format = function (item) { return _.map(item.get(this.path), function (img) { return img.src(); }).join(', '); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getData = function (item) { var value = item.get(this.path); return Array.isArray(value) ? value : []; }
...
process.exit(1);
}
}
break;
case 'module root':
// if relative path is used, resolve it based on the caller's path
if (!isAbsolutePath(value)) {
var caller = callerId.getData();
value = path.resolve(path.dirname(caller.filePath), value);
}
break;
case 'app':
this.app = value;
break;
case 'mongoose':
...
getFolder = function () { var folder = null; if (keystone.get('cloudinary folders') || this.options.folder) { if (typeof this.options.folder === 'string') { folder = this.options.folder; } else { folder = this.list.path + '/' + this.path; } } return folder; }
...
};
var schemaMethods = {
exists: function () {
return exists(this);
},
folder: function () {
return field.getFolder();
},
src: function (options) {
return src(this, options);
},
tag: function (options) {
return exists(this) ? cloudinary.image(this.get(field.path).public_id, options) : '';
},
...
inputIsValid = function (data) { // eslint-disable-line no-unused-vars // TODO - how should image field input be validated? return true; }
n/a
updateItem = function (item, data, files, callback) { if (typeof files === 'function') { callback = files; files = {}; } else if (!files) { files = {}; } var cloudinary = require('cloudinary'); var field = this; var values = this.getValueFromData(data); // TODO: This logic needs to block uploading of files from the data argument, // see CloudinaryImage for a reference on how it should be implemented // Early exit path: reset value when falsy, or bail if no value was provided if (!values) { if (values !== undefined) { item.set(field.path, []); } return process.nextTick(callback); } // When the value exists, but isn't an array, turn it into one (this just // means a single field was submitted in the formdata) if (!Array.isArray(values)) { values = [values]; } // We cache options to avoid recalculating them on each iteration in the map below var cachedUploadOptions; function getUploadOptions () { if (cachedUploadOptions) { return cachedUploadOptions; } var tagPrefix = keystone.get('cloudinary prefix') || ''; var uploadOptions = { tags: [], }; if (tagPrefix.length) { uploadOptions.tags.push(tagPrefix); tagPrefix += '_'; } uploadOptions.tags.push(tagPrefix + field.list.path + '_' + field.path); if (keystone.get('env') !== 'production') { uploadOptions.tags.push(tagPrefix + 'dev'); } var folder = field.getFolder(); if (folder) { uploadOptions.folder = folder; } cachedUploadOptions = uploadOptions; return uploadOptions; } // Preprocess values to deserialise JSON, detect mappings to uploaded files // and flatten out arrays values = values.map(function (value) { // When the value is a string, it may be JSON serialised data. if (typeof value === 'string' && value.charAt(0) === '{' && value.charAt(value.length - 1) === '}' ) { try { return JSON.parse(value); } catch (e) { /* value isn't JSON */ } } if (typeof value === 'string') { // detect file upload (field value must be a reference to a field in the // uploaded files object provided by multer) if (value.substr(0, 7) === 'upload:') { var uploadFieldPath = value.substr(7); return files[uploadFieldPath]; } // detect a URL or Base64 Data else if (/^(data:[a-z\/]+;base64)|(https?\:\/\/)/.test(value)) { return { path: value }; } } return value; }); values = _.flatten(values); async.map(values, function (value, next) { if (typeof value === 'object' && 'public_id' in value) { // Cloudinary Image data provided if (value.public_id) { // Default the object with empty values var v = assign(getEmptyValue(), value); return next(null, v); } else { // public_id is falsy, remove the value return next(); } } else if (typeof value === 'object' && value.path) { // File provided - upload it var uploadOptions = getUploadOptions(); // NOTE: field.options.publicID has been deprecated (tbc) if (field.options.filenameAsPublicID && value.originalname && typeof value.originalname === 'string') { uploadOptions = assign({}, uploadOptions, { public_id: value.originalname.substring(0, value.originalname.lastIndexOf('.')), }); } // TODO: implement autoCleanup; should delete existing images before uploading cloudinary.uploader.upload(value.path, function (result) { if (result.error) { next(result.error); } else { next(null, result); } }, uploadOptions); } else { // Nothing to do // TODO: We should really also support deleting images from cloudinary, // see the CloudinaryImageType field for reference return next(); } }, function (err, result) { if (err) return callback(err); result = result.filter(truthy); item.set(field.path, result); return callback(); }); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'exactly' && !filter.value) { query[this.path] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); query[this.path] = filter.inverted ? { $not: value } : value; return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
validateInput = function (data, callback) { var max = this.options.max; var min = this.options.min; var value = this.getValueFromData(data); var result = value === undefined || value === null || typeof value === 'string'; if (max && typeof value === 'string') { result = value.length < max; } if (min && typeof value === 'string') { result = value.length > min; } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'exactly' && !filter.value) { query[this.path] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); query[this.path] = filter.inverted ? { $not: value } : value; return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
validateInput = function (data, callback) { var max = this.options.max; var min = this.options.min; var value = this.getValueFromData(data); var result = value === undefined || value === null || typeof value === 'string'; if (max && typeof value === 'string') { result = value.length < max; } if (min && typeof value === 'string') { result = value.length > min; } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'between') { if (filter.after && filter.before) { filter.after = moment(filter.after); filter.before = moment(filter.before); if (filter.after.isValid() && filter.before.isValid()) { query[this.path] = { $gte: filter.after.startOf('day').toDate(), $lte: filter.before.endOf('day').toDate(), }; } } } else if (filter.value) { var day = { moment: moment(filter.value), }; day.start = day.moment.startOf('day').toDate(); day.end = moment(filter.value).endOf('day').toDate(); if (day.moment.isValid()) { if (filter.mode === 'after') { query[this.path] = { $gt: day.end }; } else if (filter.mode === 'before') { query[this.path] = { $lt: day.start }; } else { query[this.path] = { $gte: day.start, $lte: day.end }; } } } if (filter.inverted) { query[this.path] = { $not: query[this.path] }; } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
format = function (item, format) { if (format || this.formatString) { return item.get(this.path) ? this.moment(item).format(format || this.formatString) : ''; } else { return item.get(this.path) || ''; } }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
inputIsValid = function (data, required, item) { if (!(this.path in data) && item && item.get(this.path)) return true; var newValue = moment(data[this.path], this.parseFormatString); if (required && (!newValue.isValid())) { return false; } else if (data[this.path] && newValue && !newValue.isValid()) { return false; } else { return true; } }
n/a
moment = function (item) { var m = moment(item.get(this.path)); if (this.isUTC) m.utc(); return m; }
...
},
moment: function moment(value) {
var m = (0, _moment3.default)(value);
if (this.props.isUTC) m.utc();
return m;
},
isValid: function isValid(value) {
return this.moment(value, this.inputFormat).isValid();
},
format: function format(value) {
return value ? this.moment(value).format(this.props.formatString) : '';
},
setToday: function setToday() {
this.valueChanged({
value: this.moment(new Date()).format(this.props.inputFormat)
...
parse = function (value, format, strict) { var m = this.isUTC ? moment.utc : moment; // TODO Check should maybe be if (typeof value === 'string') // use the parseFormatString. Ever relevant? if (typeof value === 'number' || value instanceof Date) { return m(value); } else { return m(value, format || this.parseFormatString, strict); } }
...
// throw on unsupported options
case 'email rules':
throw new Error('The option "' + key + '" is no longer supported. See https://github.com/keystonejs/
keystone/wiki/0.3.x-to-0.4.x-Changes');
// handle special settings
case 'cloudinary config':
var cloudinary = require('cloudinary');
if (typeof value === 'string') {
var parts = url.parse(value, true);
var auth = parts.auth ? parts.auth.split(':') : [];
value = {
cloud_name: parts.host,
api_key: auth[0],
api_secret: auth[1],
private_cdn: parts.pathname != null,
secure_distribution: parts.pathname && parts.pathname.substring(1),
...
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); if (value !== null && value !== '') { // If the value is not null, empty string or undefined, parse it var newValue = this.parse(value); // If it's valid and not the same as the last value, save it if (newValue.isValid() && (!item.get(this.path) || !newValue.isSame(item.get(this.path)))) { item.set(this.path, newValue.toDate()); } } else { // If it's null or empty string, clear it out item.set(this.path, null); } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); var result = true; if (value) { result = this.parse(value).isValid(); } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var dateTypeAddFilterToQuery = DateType.prototype.addFilterToQuery.bind(this); var query = dateTypeAddFilterToQuery(filter); if (query[this.path]) { query[this.path] = addPresenceToQuery(filter.presence || 'some', query[this.path]); } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
format = function (item, format, separator) { var value = item.get(this.path); if (format || this.formatString) { value = value.map(function (d) { return moment(d).format(format || this._formatString); }); } return value.join(separator || this.separator); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
inputIsValid = function (data, required, item) { var value = this.getValueFromData(data); var parseFormatString = this.parseFormatString; if (typeof value === 'string') { if (!moment(value, parseFormatString).isValid()) { return false; } value = [value]; } if (required) { if (value === undefined && item && item.get(this.path) && item.get(this.path).length) { return true; } if (value === undefined || !Array.isArray(value)) { return false; } if (Array.isArray(value) && !value.length) { return false; } } if (Array.isArray(value)) { // filter out empty fields value = value.filter(function (date) { return date.trim() !== ''; }); // if there are no values left, and requried is true, return false if (required && !value.length) { return false; } // if any date in the array is invalid, return false if (value.some(function (dateValue) { return !moment(dateValue, parseFormatString).isValid(); })) { return false; } } return (value === undefined || Array.isArray(value)); }
n/a
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); if (Array.isArray(value)) { // Only save valid dates value = value.filter(function (date) { return moment(date).isValid(); }); } if (value === null || value === undefined) { value = []; } if (typeof value === 'string') { if (moment(value).isValid()) { value = [value]; } } if (Array.isArray(value)) { item.set(this.path, value); } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); var result = true; if (value !== undefined && value !== null && value !== '') { if (!Array.isArray(value)) { value = [value]; } for (var i = 0; i < value.length; i++) { var currentValue; // If we pass it an epoch, parse it without the format string if (typeof value[i] === 'number') { currentValue = moment(value[i]); } else { currentValue = moment(value[i], this.parseFormatString); } // If moment does not think it's a valid date, invalidate if (!currentValue.isValid()) { result = false; break; } } } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = false; // If the field is undefined but has a value saved already, validate if (value === undefined) { if (item.get(this.path) && item.get(this.path).length) { result = true; } } if (typeof value === 'string' || typeof value === 'number') { if (moment(value).isValid()) { result = true; } // If it's an array of only dates and/or dateify-able data, validate } else if (Array.isArray(value)) { var invalidContent = false; for (var i = 0; i < value.length; i++) { var currentValue; // If we pass it an epoch, parse it without the format string if (typeof value[i] === 'number') { currentValue = moment(value[i]); } else { currentValue = moment(value[i], this.parseFormatString); } // If even a single item is not a valid date, invalidate if (!currentValue.isValid()) { invalidContent = true; break; } } if (invalidContent === false) { result = true; } } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'between') { if (filter.after && filter.before) { filter.after = moment(filter.after); filter.before = moment(filter.before); if (filter.after.isValid() && filter.before.isValid()) { query[this.path] = { $gte: filter.after.startOf('day').toDate(), $lte: filter.before.endOf('day').toDate(), }; } } } else if (filter.value) { var day = { moment: moment(filter.value), }; day.start = day.moment.startOf('day').toDate(); day.end = moment(filter.value).endOf('day').toDate(); if (day.moment.isValid()) { if (filter.mode === 'after') { query[this.path] = { $gt: day.end }; } else if (filter.mode === 'before') { query[this.path] = { $lt: day.start }; } else { query[this.path] = { $gte: day.start, $lte: day.end }; } } } if (filter.inverted) { query[this.path] = { $not: query[this.path] }; } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
format = function (item, format) { if (format || this.formatString) { return item.get(this.path) ? this.moment(item).format(format || this.formatString) : ''; } else { return item.get(this.path) || ''; } }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getInputFromData = function (data) { var dateValue = this.getValueFromData(data, '_date'); var timeValue = this.getValueFromData(data, '_time'); var tzOffsetValue = this.getValueFromData(data, '_tzOffset'); if (dateValue && timeValue) { var combined = dateValue + ' ' + timeValue; if (typeof tzOffsetValue !== 'undefined') { combined += ' ' + tzOffsetValue; } return combined; } return this.getValueFromData(data); }
...
}
return this.getValueFromData(data);
};
datetime.prototype.validateRequiredInput = function (item, data, callback) {
var value = this.getInputFromData(data);
var result = !!value;
if (value === undefined && item.get(this.path)) {
result = true;
}
utils.defer(callback, result);
};
...
inputIsValid = function (data, required, item) { if (!(this.path in data && !(this.paths.date in data && this.paths.time in data)) && item && item.get(this.path)) return true; var newValue = moment(this.getInputFromData(data), parseFormats); if (required && (!newValue || !newValue.isValid())) { return false; } else if (this.getInputFromData(data) && newValue && !newValue.isValid()) { return false; } else { return true; } }
n/a
moment = function (item) { var m = moment(item.get(this.path)); if (this.isUTC) m.utc(); return m; }
...
},
moment: function moment(value) {
var m = (0, _moment3.default)(value);
if (this.props.isUTC) m.utc();
return m;
},
isValid: function isValid(value) {
return this.moment(value, this.inputFormat).isValid();
},
format: function format(value) {
return value ? this.moment(value).format(this.props.formatString) : '';
},
setToday: function setToday() {
this.valueChanged({
value: this.moment(new Date()).format(this.props.inputFormat)
...
parse = function (value, format, strict) { var m = this.isUTC ? moment.utc : moment; // TODO Check should maybe be if (typeof value === 'string') // use the parseFormatString. Ever relevant? if (typeof value === 'number' || value instanceof Date) { return m(value); } else { return m(value, format || this.parseFormatString, strict); } }
...
// throw on unsupported options
case 'email rules':
throw new Error('The option "' + key + '" is no longer supported. See https://github.com/keystonejs/
keystone/wiki/0.3.x-to-0.4.x-Changes');
// handle special settings
case 'cloudinary config':
var cloudinary = require('cloudinary');
if (typeof value === 'string') {
var parts = url.parse(value, true);
var auth = parts.auth ? parts.auth.split(':') : [];
value = {
cloud_name: parts.host,
api_key: auth[0],
api_secret: auth[1],
private_cdn: parts.pathname != null,
secure_distribution: parts.pathname && parts.pathname.substring(1),
...
updateItem = function (item, data, callback) { // Get the values from the data var value = this.getInputFromData(data); if (value !== undefined) { if (value !== null && value !== '') { // If the value is not null, empty string or undefined, parse it var newValue = this.parse(value, this.parseFormatString, true); // If it's valid and not the same as the last value, save it if (!item.get(this.path) || !newValue.isSame(item.get(this.path))) { item.set(this.path, newValue.toDate()); } // If it's null or empty string, clear it out } else { item.set(this.path, null); } } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getInputFromData(data); // If the value is null, undefined or an empty string // bail early since updateItem sanitizes that just fine var result = true; if (value) { result = this.parse(value, this.parseFormatString, true).isValid(); } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getInputFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'exactly' && !filter.value) { query[this.path] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); query[this.path] = filter.inverted ? { $not: value } : value; return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
gravatarUrl = function (item, size, defaultImage, rating) { var value = item.get(this.path); if (typeof value !== 'string') { return ''; } return [ // base url protocol-less for both http/https '//www.gravatar.com/avatar/', // md5 hash the trimmed lowercase email crypto.createHash('md5').update(value.toLowerCase().trim()).digest('hex'), // size of images ranging from 1 to 2048 pixels, square '?s=' + (/^(?:[1-9][0-9]{0,2}|1[0-9]{3}|20[0-3][0-9]|204[0-8])$/.test(size) ? size : 80), // default image url encoded href or one of the built in options: 404, mm, identicon, monsterid, wavatar, retro, blank '&d=' + (defaultImage ? encodeURIComponent(defaultImage) : 'identicon'), // rating, g, pg, r or x '&r=' + (/^(?:g|pg|r|x)$/i.test(rating) ? rating.toLowerCase() : 'g'), ].join(''); }
n/a
inputIsValid = function (data, required, item) { var value = this.getValueFromData(data); if (value) { return utils.isEmail(value); } else { return (!required || (item && item.get(this.path))) ? true : false; } }
n/a
updateItem = function (item, data, callback) { var newValue = this.getValueFromData(data); if (typeof newValue === 'string') { newValue = newValue.toLowerCase(); } if (newValue !== undefined && newValue !== item.get(this.path)) { item.set(this.path, newValue); } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var input = this.getValueFromData(data); var result = true; if (input) { result = utils.isEmail(input); } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addToSchema = function (schema) { var field = this; this.paths = { exists: this.path + '.exists', type: this.path + '.type', title: this.path + '.title', url: this.path + '.url', width: this.path + '.width', height: this.path + '.height', version: this.path + '.version', description: this.path + '.description', html: this.path + '.html', authorName: this.path + '.authorName', authorUrl: this.path + '.authorUrl', providerName: this.path + '.providerName', providerUrl: this.path + '.providerUrl', thumbnailUrl: this.path + '.thumbnailUrl', thumbnailWidth: this.path + '.thumbnailWidth', thumbnailHeight: this.path + '.thumbnailHeight', }; schema.nested[this.path] = true; schema.add({ exists: Boolean, type: String, title: String, url: String, width: Number, height: Number, version: String, description: String, html: String, authorName: String, authorUrl: String, providerName: String, providerUrl: String, thumbnailUrl: String, thumbnailWidth: Number, thumbnailHeight: Number, }, this.path + '.'); // Bind the pre-save hook to hit the embedly api if the source path has changed schema.pre('save', function (next) { if (!this.isModified(field.fromPath)) { return next(); } var fromValue = this.get(field.fromPath); if (!fromValue) { field.reset(this); return next(); } var post = this; var api = new EmbedlyAPI({ key: keystone.get('embedly api key') }); var opts = _.defaults({ url: fromValue }, field.embedlyOptions); api.oembed(opts, function (err, objs) { if (err) { console.error('Embedly API Error:'); console.error(err, objs); field.reset(post); } else { var data = objs[0]; if (data && data.type !== 'error') { post.set(field.path, { exists: true, type: data.type, title: data.title, url: data.url, width: data.width, height: data.height, version: data.version, description: data.description, html: data.html, authorName: data.author_name, authorUrl: data.author_url, providerName: data.provider_name, providerUrl: data.provider_url, thumbnailUrl: data.thumbnail_url, thumbnailWidth: data.thumbnail_width, thumbnailHeight: data.thumbnail_height, }); } else { field.reset(post); } } return next(); }); }); this.bindUnderscoreMethods(); }
n/a
format = function (item) { return item.get(this.paths.html); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getData = function (item) { var value = item.get(this.path); return typeof value === 'object' ? value : {}; }
...
process.exit(1);
}
}
break;
case 'module root':
// if relative path is used, resolve it based on the caller's path
if (!isAbsolutePath(value)) {
var caller = callerId.getData();
value = path.resolve(path.dirname(caller.filePath), value);
}
break;
case 'app':
this.app = value;
break;
case 'mongoose':
...
inputIsValid = function () { return true; }
n/a
isModified = function (item) { // Assume that it has changed if the url is different return item.isModified(this.paths.url); }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
reset = function (item) { return item.set(item.set(this.path, { exists: false, type: null, title: null, url: null, width: null, height: null, version: null, description: null, html: null, authorName: null, authorUrl: null, providerName: null, providerUrl: null, thumbnailUrl: null, thumbnailWidth: null, thumbnailHeight: null, })); }
...
if (!this.isModified(field.fromPath)) {
return next();
}
var fromValue = this.get(field.fromPath);
if (!fromValue) {
field.reset(this);
return next();
}
var post = this;
var api = new EmbedlyAPI({ key: keystone.get('embedly api key') });
var opts = _.defaults({ url: fromValue }, field.embedlyOptions);
...
updateItem = function (item, data, callback) { // TODO: This could be more granular and check for actual changes to values, // see the Location field for an example item.set(item.set(this.path, { exists: data[this.paths.exists], type: data[this.paths.type], title: data[this.paths.title], url: data[this.paths.url], width: data[this.paths.width], height: data[this.paths.height], version: data[this.paths.version], description: data[this.paths.description], html: data[this.paths.html], authorName: data[this.paths.authorName], authorUrl: data[this.paths.authorUrl], providerName: data[this.paths.providerName], providerUrl: data[this.paths.providerUrl], thumbnailUrl: data[this.paths.thumbnailUrl], thumbnailWidth: data[this.paths.thumbnailWidth], thumbnailHeight: data[this.paths.thumbnailHeight], })); process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
addToSchema = function (schema) { var field = this; this.paths = {}; // add field paths from the storage schema Object.keys(this.storage.schema).forEach(function (path) { field.paths[path] = field.path + '.' + path; }); var schemaPaths = this._path.addTo({}, this.storage.schema); schema.add(schemaPaths); this.bindUnderscoreMethods(); }
n/a
format = function (item) { var value = item.get(this.path); if (value) return value.filename || ''; return ''; }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
isModified = function (item) { var modified = false; var paths = this.paths; Object.keys(this.storageSchema).forEach(function (path) { if (item.isModified(paths[path])) modified = true; }); return modified; }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
remove = function (item) { this.storage.removeFile(item.get(this.path)); this.reset(); }
...
function getId() {
return 'keystone-html-' + lastId++;
}
// Workaround for #2834 found here https://github.com/tinymce/tinymce/issues/794#issuecomment-203701329
function removeTinyMCEInstance(editor) {
var oldLength = _tinymce2.default.editors.length;
_tinymce2.default.remove(editor);
if (oldLength === _tinymce2.default.editors.length) {
_tinymce2.default.editors.remove(editor);
}
}
module.exports = _Field2.default.create({
...
reset = function (item) { var value = {}; Object.keys(this.storage.schema).forEach(function (path) { value[path] = null; }); item.set(this.path, value); }
...
if (!this.isModified(field.fromPath)) {
return next();
}
var fromValue = this.get(field.fromPath);
if (!fromValue) {
field.reset(this);
return next();
}
var post = this;
var api = new EmbedlyAPI({ key: keystone.get('embedly api key') });
var opts = _.defaults({ url: fromValue }, field.embedlyOptions);
...
updateItem = function (item, data, files, callback) { // Process arguments if (typeof files === 'function') { callback = files; files = {}; } if (!files) { files = {}; } // Prepare values var value = this.getValueFromData(data); var uploadedFile; // Providing the string "remove" removes the file and resets the field if (value === 'remove') { this.remove(item); utils.defer(callback); } // Find an uploaded file in the files argument, either referenced in the // data argument or named with the field path / field_upload path + suffix if (typeof value === 'string' && value.substr(0, 7) === 'upload:') { uploadedFile = files[value.substr(7)]; } else { uploadedFile = this.getValueFromData(files) || this.getValueFromData(files, '_upload'); } // Ensure a valid file was uploaded, else null out the value if (uploadedFile && !uploadedFile.path) { uploadedFile = undefined; } // If we have a file to upload, we do that and stop here if (uploadedFile) { return this.upload(item, uploadedFile, callback); } // Empty / null values reset the field if (value === null || value === '' || (typeof value === 'object' && !Object.keys(value).length)) { this.reset(item); value = undefined; } // If there is a valid value at this point, set it on the field if (typeof value === 'object') { item.set(this.path, value); } utils.defer(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
upload = function (item, file, callback) { var field = this; // TODO; Validate there is actuall a file to upload debug('[%s.%s] Uploading file for item %s:', this.list.key, this.path, item.id, file); this.storage.uploadFile(file, function (err, result) { if (err) return callback(err); debug('[%s.%s] Uploaded file for item %s with result:', field.list.key, field.path, item.id, result); item.set(field.path, result); callback(null, result); }); }
...
if (req.files && req.files.file) {
var options = {};
if (keystone.get('wysiwyg cloudinary images filenameAsPublicID')) {
options.public_id = req.files.file.originalname.substring(0, req.files.file.originalname.lastIndexOf('.'));
}
cloudinary.uploader.upload(req.files.file.path, function (result) {
var sendResult = function () {
if (result.error) {
res.send({ error: { message: result.error.message } });
} else {
res.send({ image: { url: (keystone.get('cloudinary secure') === true) ? result.secure_url : result.url } });
}
};
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); debug('[%s.%s] Validating input: ', this.list.key, this.path, value); var result = validateInput(value); debug('[%s.%s] Validation result: ', this.list.key, this.path, result); utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { // TODO: We need to also get the `files` argument, so we can check for // uploaded files. without it, this will return false negatives so we // can't actually validate required input at the moment. var result = true; // var value = this.getValueFromData(data); // debug('[%s.%s] Validating required input: ', this.list.key, this.path, value); // TODO: Need to actually check a dynamic path based on the adapter // TODO: This incorrectly allows empty values in the object to pass validation // var result = (value || item.get(this.paths.filename)) ? true : false; // debug('[%s.%s] Validation result: ', this.list.key, this.path, result); utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; // If latitude or longitude aren't specified, don't filter anything if (filter.lon && filter.lat) { query[this.path] = { $near: { $geometry: { type: 'Point', coordinates: [filter.lon, filter.lat], }, }, }; // MongoDB wants meters, but we accept kilometers via input so we * 1000 var distance = (filter.distance.value && filter.distance.value * 1000) || 500000; if (filter.distance.mode === 'min') { query[this.path].$near.$minDistance = distance; } else { query[this.path].$near.$maxDistance = distance; } } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
addToSchema = function (schema) { schema.path(this.path, _.defaults({ type: [Number], index: '2dsphere' }, this.options)); this.bindUnderscoreMethods(); }
n/a
format = function (item) { if (item.get(this.path)) { return item.get(this.path).reverse().join(', '); } return null; }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getData = function (item) { var points = item.get(this.path); return (points && points.length === 2) ? points : []; }
...
process.exit(1);
}
}
break;
case 'module root':
// if relative path is used, resolve it based on the caller's path
if (!isAbsolutePath(value)) {
var caller = callerId.getData();
value = path.resolve(path.dirname(caller.filePath), value);
}
break;
case 'app':
this.app = value;
break;
case 'mongoose':
...
inputIsValid = function (data, required, item) { // eslint-disable-line no-unused-vars var values = this.getValueFromData(data); // Input is valid if the field is not required, and not present if (values === undefined && !required) return true; if (Array.isArray(values)) { values = values.length === 2 ? values.join(',') : ''; } if (typeof values !== 'string') return false; if ((values === '' || values.charAt(0) === ',' || values.charAt(values.length - 1) === ',') && !required) return true; return REGEXP_LNGLAT.test(values); }
n/a
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); if (value === undefined) return process.nextTick(callback); if (typeof value === 'string') { // Value should be formatted lng,lat var values = REGEXP_LNGLAT.exec(value); if (values) { item.set(this.path, [values[1], values[2]]); } else { item.set(this.path, undefined); } } else if (Array.isArray(value)) { if (value.length === 2 && REGEXP_LNGLAT.test(_.compact(value).join(','))) { item.set(this.path, value); } else { item.set(this.path, undefined); } } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); var result = false; if (value === undefined || value === null || value === '') { result = true; } else { if (Array.isArray(value)) { value = value.length === 2 ? value.join(',') : ''; } if (typeof value === 'string') { result = REGEXP_LNGLAT.test(value); } } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = (value || (value === undefined && item.get(this.path) && item.get(this.path).length === 2)) ? true : false; utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'exactly' && !filter.value) { query[this.path] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); query[this.path] = filter.inverted ? { $not: value } : value; return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
validateInput = function (data, callback) { var max = this.options.max; var min = this.options.min; var value = this.getValueFromData(data); var result = value === undefined || value === null || typeof value === 'string'; if (max && typeof value === 'string') { result = value.length < max; } if (min && typeof value === 'string') { result = value.length > min; } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'exactly' && !filter.value) { query[this.path] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); query[this.path] = filter.inverted ? { $not: value } : value; return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
generateKey = function (str) { return utils.slug(String(str), this.separator); }
...
* Deprecated
*/
key.prototype.inputIsValid = function (data, required, item) {
var value = this.getValueFromData(data);
if (value === undefined && item && item.get(this.path)) {
return true;
}
value = this.generateKey(value);
return (value || !required) ? true : false;
};
/**
* Updates the value for this field in the item from a data object
*/
key.prototype.updateItem = function (item, data, callback) {
...
inputIsValid = function (data, required, item) { var value = this.getValueFromData(data); if (value === undefined && item && item.get(this.path)) { return true; } value = this.generateKey(value); return (value || !required) ? true : false; }
n/a
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); if (value === undefined) { return process.nextTick(callback); } value = this.generateKey(value); if (item.get(this.path) !== value) { item.set(this.path, value); } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var max = this.options.max; var min = this.options.min; var value = this.getValueFromData(data); var result = value === undefined || value === null || typeof value === 'string'; if (max && typeof value === 'string') { result = value.length < max; } if (min && typeof value === 'string') { result = value.length > min; } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addToSchema = function (schema) {
var field = this;
var paths = this.paths = {
// fields
filename: this.path + '.filename',
originalname: this.path + '.originalname',
path: this.path + '.path',
size: this.path + '.size',
filetype: this.path + '.filetype',
// virtuals
exists: this.path + '.exists',
href: this.path + '.href',
upload: this.path + '_upload',
action: this.path + '_action',
};
var schemaPaths = this._path.addTo({}, {
filename: String,
originalname: String,
path: String,
size: Number,
filetype: String,
});
schema.add(schemaPaths);
// exists checks for a matching file at run-time
var exists = function (item) {
var filepath = item.get(paths.path);
var filename = item.get(paths.filename);
if (!filepath || !filename) {
return false;
}
return fs.existsSync(path.join(filepath, filename));
};
// The .exists virtual indicates whether a file is stored
schema.virtual(paths.exists).get(function () {
return schemaMethods.exists.apply(this);
});
// The .href virtual returns the public path of the file
schema.virtual(paths.href).get(function () {
return field.href(this);
});
// reset clears the value of the field
var reset = function (item) {
item.set(field.path, {
filename: '',
path: '',
size: 0,
filetype: '',
});
};
var schemaMethods = {
exists: function () {
return exists(this);
},
/**
* Resets the value of the field
*
* @api public
*/
reset: function () {
reset(this);
},
/**
* Deletes the file from localfile and resets the field
*
* @api public
*/
delete: function () {
if (exists(this)) {
fs.unlinkSync(path.join(this.get(paths.path), this.get(paths.filename)));
}
reset(this);
},
};
_.forEach(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();
}
n/a
format = function (item) { if (!item.get(this.paths.filename)) return ''; if (this.hasFormatter()) { var file = item.get(this.path); file.href = this.href(item); return this.options.format.call(this, item, file); } return this.href(item); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
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.files && req.files[paths.upload] && req.files[paths.upload].size) { return field.uploadFile(item, req.files[paths.upload], true, callback); } return callback(); }; }
n/a
hasFormatter = function () { return typeof this.options.format === 'function'; }
...
* Formats the field value
*
* Delegates to the options.format function if it exists.
* @api public
*/
localfile.prototype.format = function (item) {
if (!item.get(this.paths.filename)) return '';
if (this.hasFormatter()) {
var file = item.get(this.path);
file.href = this.href(item);
return this.options.format.call(this, item, file);
}
return this.href(item);
};
...
href = function (item) { if (!item.get(this.paths.filename)) return ''; var prefix = this.options.prefix ? this.options.prefix : item.get(this.paths.path); return prefix + '/' + item.get(this.paths.filename); }
...
// The .exists virtual indicates whether a file is stored
schema.virtual(paths.exists).get(function () {
return schemaMethods.exists.apply(this);
});
// The .href virtual returns the public path of the file
schema.virtual(paths.href).get(function () {
return field.href(this);
});
// reset clears the value of the field
var reset = function (item) {
item.set(field.path, {
filename: '',
path: '',
...
inputIsValid = function (data) { // eslint-disable-line no-unused-vars // TODO - how should file field input be validated? return true; }
n/a
isModified = function (item) { return item.isModified(this.paths.path); }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
updateItem = function (item, data, callback) { // eslint-disable-line no-unused-vars // TODO - direct updating of data (not via upload) process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
uploadFile = function (item, file, update, callback) { var field = this; var prefix = field.options.datePrefix ? moment().format(field.options.datePrefix) + '-' : ''; var filename = prefix + file.name; var filetype = file.mimetype || file.type; if (field.options.allowedTypes && !_.includes(field.options.allowedTypes, filetype)) { return callback(new Error('Unsupported File Type: ' + filetype)); } if (typeof update === 'function') { callback = update; update = false; } var doMove = function (callback) { if (typeof field.options.filename === 'function') { filename = field.options.filename(item, file); } fs.move(file.path, path.join(field.options.dest, filename), { clobber: field.options.overwrite }, function (err) { if (err) return callback(err); var fileData = { filename: filename, originalname: file.originalname, path: field.options.dest, size: file.size, filetype: filetype, }; if (update) { item.set(field.path, fileData); } callback(null, fileData); }); }; field.callHook('pre:move', item, file, function (err) { if (err) return callback(err); doMove(function (err, fileData) { if (err) return callback(err); field.callHook('post:move', [item, file, fileData], function (err) { if (err) return callback(err); callback(null, fileData); }); }); }); }
...
if (/^(delete|reset)$/.test(action)) {
field.apply(item, action);
}
}
if (req.files && req.files[paths.upload] && req.files[paths.upload].size) {
return field.uploadFile(item, req.files[paths.upload], true, callback);
}
return callback();
};
};
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); utils.defer(callback, validateInput(value)); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = (value || item.get(this.path).path) ? true : false; utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addToSchema = function (schema) {
var field = this;
var mongoose = keystone.mongoose;
var paths = this.paths = {
// fields
filename: this.path + '.filename',
path: this.path + '.path',
originalname: this.path + '.originalname',
size: this.path + '.size',
filetype: this.path + '.filetype',
// virtuals
exists: this.path + '.exists',
upload: this.path + '_upload',
action: this.path + '_action',
order: this.path + '_order',
};
var schemaPaths = new mongoose.Schema({
filename: String,
originalname: String,
path: String,
size: Number,
filetype: String,
});
// The .href virtual returns the public path of the file
schemaPaths.virtual('href').get(function () {
return field.href(this);
});
schema.add(this._path.addTo({}, [schemaPaths]));
var exists = function (item, element_id) {
var values = item.get(field.path);
var value;
if (typeof values === 'undefined' || values.length === 0) {
return false;
}
// if current Field contains any file, it means it exists
if (typeof element_id === 'undefined') {
value = values[0];
} else {
// allow implicit type coercion to compare string IDs with MongoID objects
value = values.find(function (val) { return val._id == element_id; }); // eslint-disable-line eqeqeq
}
if (typeof value === 'undefined') {
return false;
}
var filepaths = value.path;
var filename = value.filename;
if (!filepaths || !filename) {
return false;
}
return fs.existsSync(path.join(filepaths, filename));
};
// The .exists virtual indicates whether a file is stored
schema.virtual(paths.exists).get(function () {
return schemaMethods.exists.apply(this);
});
var reset = function (item, element_id) {
if (typeof element_id === 'undefined') {
item.set(field.path, []);
} else {
var values = item.get(field.path);
// allow implicit type coercion to compare string IDs with MongoID objects
var value = values.find(function (val) { return val._id == element_id; }); // eslint-disable-line eqeqeq
if (typeof value !== 'undefined') {
values.splice(values.indexOf(value), 1);
item.set(field.path, values);
}
}
};
var schemaMethods = {
exists: function () {
return exists(this);
},
/**
* Resets the value of the field
*/
reset: function () {
reset(this);
},
/**
* Deletes the file from localfiles and resets the field
*/
delete: function (element_id) {
if (exists(this, element_id)) {
var values = this.get(field.path);
// allow implicit type coercion to compare string IDs with MongoID objects
var value = values.find(function (val) { return val._id == element_id; }); // eslint-disable-line eqeqeq
if (typeof value !== 'undefined') {
fs.unlinkSync(path.join(value.path, value.filename));
}
}
reset(this, element_id);
},
};
_.forEach(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();
}
n/a
format = function (item, i) { var files = item.get(this.path); if (typeof i === 'undefined') { return utils.plural(files.length, '* File'); } var file = files[i]; if (!file) return ''; if (this.hasFormatter()) { file.href = this.href(file); return this.options.format.call(this, item, file); } return file.filename; }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
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 files = item.get(field.path); var newOrder = req.body[paths.order].split(','); files.sort(function (a, b) { return (newOrder.indexOf(a._id.toString()) > newOrder.indexOf(b._id.toString())) ? 1 : -1; }); } // Removals 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]; var ids = action[1]; if (!(/^(delete|reset)$/.test(method)) || !ids) return; ids.split(',').forEach(function (id) { field.apply(item, method, id); }); }); } // Upload new files if (req.files) { var upFiles = req.files[paths.upload]; if (upFiles) { if (!Array.isArray(upFiles)) { upFiles = [upFiles]; } if (upFiles.length > 0) { upFiles = _.filter(upFiles, function (f) { return typeof f.name !== 'undefined' && f.name.length > 0; }); if (upFiles.length > 0) { return field.uploadFiles(item, upFiles, true, callback); } } } } return callback(); }; }
n/a
hasFormatter = function () { return typeof this.options.format === 'function'; }
...
* Formats the field value
*
* Delegates to the options.format function if it exists.
* @api public
*/
localfile.prototype.format = function (item) {
if (!item.get(this.paths.filename)) return '';
if (this.hasFormatter()) {
var file = item.get(this.path);
file.href = this.href(item);
return this.options.format.call(this, item, file);
}
return this.href(item);
};
...
href = function (file) { if (!file.filename) return ''; var prefix = this.options.prefix ? this.options.prefix : file.path; return prefix + '/' + file.filename; }
...
// The .exists virtual indicates whether a file is stored
schema.virtual(paths.exists).get(function () {
return schemaMethods.exists.apply(this);
});
// The .href virtual returns the public path of the file
schema.virtual(paths.href).get(function () {
return field.href(this);
});
// reset clears the value of the field
var reset = function (item) {
item.set(field.path, {
filename: '',
path: '',
...
inputIsValid = function (data) { // eslint-disable-line no-unused-vars // TODO - how should file field input be validated? return true; }
n/a
isModified = function (item) { return item.isModified(this.paths.path); }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
updateItem = function (item, data, callback) { // eslint-disable-line no-unused-vars // TODO - direct updating of data (not via upload) process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
uploadFiles = function (item, files, update, callback) { var field = this; if (typeof update === 'function') { callback = update; update = false; } async.map(files, function (file, processedFile) { var prefix = field.options.datePrefix ? moment().format(field.options.datePrefix) + '-' : ''; var filename = prefix + file.name; var filetype = file.mimetype || file.type; if (field.options.allowedTypes && !_.includes(field.options.allowedTypes, filetype)) { return processedFile(new Error('Unsupported File Type: ' + filetype)); } var doMove = function (doneMove) { if (typeof field.options.filename === 'function') { filename = field.options.filename(item, file); } fs.move(file.path, path.join(field.options.dest, filename), { clobber: field.options.overwrite }, function (err) { if (err) return doneMove(err); var fileData = { filename: filename, originalname: file.originalname, path: field.options.dest, size: file.size, filetype: filetype, }; if (update) { item.get(field.path).push(fileData); } doneMove(null, fileData); }); }; field.callHook('pre:move', item, file, function (err) { if (err) return processedFile(err); doMove(function (err, fileData) { if (err) return processedFile(err); field.callHook('post:move', item, file, fileData, function (err) { return processedFile(err, fileData); }); }); }); }, callback); }
...
if (upFiles) {
if (!Array.isArray(upFiles)) {
upFiles = [upFiles];
}
if (upFiles.length > 0) {
upFiles = _.filter(upFiles, function (f) { return typeof f.name !== 'undefined' && f.name.length >
0; });
if (upFiles.length > 0) {
return field.uploadFiles(item, upFiles, true, callback);
}
}
}
}
return callback();
};
...
addFilterToQuery = function (filter) { var query = {}; var field = this; ['street', 'city', 'state', 'code', 'country'].forEach(function (i) { if (!filter[i]) return; var value = utils.escapeRegExp(filter[i]); value = new RegExp(value, 'i'); query[field.paths[FILTER_PATH_MAP[i]]] = filter.inverted ? { $not: value } : value; }); return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
addToSchema = function (schema) { var field = this; var options = this.options; var paths = this.paths = { number: this.path + '.number', name: this.path + '.name', street1: this.path + '.street1', street2: this.path + '.street2', suburb: this.path + '.suburb', state: this.path + '.state', postcode: this.path + '.postcode', country: this.path + '.country', geo: this.path + '.geo', geo_lat: this.path + '.geo_lat', geo_lng: this.path + '.geo_lng', serialised: this.path + '.serialised', improve: this.path + '_improve', overwrite: this.path + '_improve_overwrite', }; var getFieldDef = function (type, key) { var def = { type: type }; if (options.defaults[key]) { def.default = options.defaults[key]; } return def; }; schema.nested[this.path] = true; schema.add({ number: getFieldDef(String, 'number'), name: getFieldDef(String, 'name'), street1: getFieldDef(String, 'street1'), street2: getFieldDef(String, 'street2'), street3: getFieldDef(String, 'street3'), suburb: getFieldDef(String, 'suburb'), state: getFieldDef(String, 'state'), postcode: getFieldDef(String, 'postcode'), country: getFieldDef(String, 'country'), geo: { type: [Number], index: '2dsphere' }, }, this.path + '.'); schema.virtual(paths.serialised).get(function () { return _.compact([ this.get(paths.number), this.get(paths.name), this.get(paths.street1), this.get(paths.street2), this.get(paths.suburb), this.get(paths.state), this.get(paths.postcode), this.get(paths.country), ]).join(', '); }); // pre-save hook to fix blank geo fields // see http://stackoverflow.com/questions/16388836/does-applying-a-2dsphere-index-on-a-mongoose-schema-force-the-location-field -to schema.pre('save', function (next) { var obj = field._path.get(this); var geo = (obj.geo || []).map(Number).filter(_.isFinite); obj.geo = (geo.length === 2) ? geo : undefined; next(); }); this.bindUnderscoreMethods(); }
n/a
format = function (item, values, delimiter) { if (!values) { return item.get(this.paths.serialised); } var paths = this.paths; values = values.split(' ').map(function (i) { return item.get(paths[i]); }); return _.compact(values).join(delimiter || ', '); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getInputFromData = function (data) { // Allow JSON structured data var input = this.getValueFromData(data); // If there is no structured data, look for the flat paths if (!input) { input = { number: data[this.paths.number], name: data[this.paths.name], street1: data[this.paths.street1], street2: data[this.paths.street2], suburb: data[this.paths.suburb], state: data[this.paths.state], postcode: data[this.paths.postcode], country: data[this.paths.country], geo: data[this.paths.geo], geo_lat: data[this.paths.geo], geo_lng: data[this.paths.geo], improve: data[this.paths_improve], overwrite: data[this.paths_improve_overwrite], }; } return input; }
...
}
return this.getValueFromData(data);
};
datetime.prototype.validateRequiredInput = function (item, data, callback) {
var value = this.getInputFromData(data);
var result = !!value;
if (value === undefined && item.get(this.path)) {
result = true;
}
utils.defer(callback, result);
};
...
googleLookup = function (item, region, update, callback) { if (typeof update === 'function') { callback = update; update = false; } var field = this; var stored = item.get(this.path); var address = item.get(this.paths.serialised); if (address.length === 0) { return callback({ status_code: 500, status_text: 'No address to geocode', status: 'NO_ADDRESS', }); } doGoogleGeocodeRequest(address, region || keystone.get('default region'), function (err, geocode) { if (err || geocode.status !== 'OK') { return callback(err || new Error(geocode.status + ': ' + geocode.error_message)); } // use the first result // if there were no results in the array, status would be ZERO_RESULTS var result = geocode.results[0]; // parse the address components into a location object var location = {}; _.forEach(result.address_components, function (val) { if (_.indexOf(val.types, 'street_number') >= 0) { location.street1 = [val.long_name]; } if (_.indexOf(val.types, 'route') >= 0) { location.street1 = location.street1 || []; location.street1.push(val.short_name); } // in some cases, you get suburb, city as locality - so only use the first if (_.indexOf(val.types, 'locality') >= 0 && !location.suburb) { location.suburb = val.long_name; } if (_.indexOf(val.types, 'administrative_area_level_1') >= 0) { location.state = val.short_name; } if (_.indexOf(val.types, 'country') >= 0) { location.country = val.long_name; } if (_.indexOf(val.types, 'postal_code') >= 0) { location.postcode = val.short_name; } }); if (Array.isArray(location.street1)) { location.street1 = location.street1.join(' '); } location.geo = [ result.geometry.location.lng, result.geometry.location.lat, ]; // console.log('------ Google Geocode Results ------'); // console.log(address); // console.log(result); // console.log(location); if (update === 'overwrite') { item.set(field.path, location); } else if (update) { _.forEach(location, function (value, key) { if (key === 'geo') { return; } if (!stored[key]) { item.set(field.paths[key], value); } }); if (!Array.isArray(stored.geo) || !stored.geo[0] || !stored.geo[1]) { item.set(field.paths.geo, location.geo); } } callback(null, location, result); }); }
...
var lng = utils.number(values[valuePaths.geo_lng]);
item.set(paths.geo, (lat && lng) ? [lng, lat] : undefined);
}
var doGoogleLookup = this.getValueFromData(data, '_improve');
if (doGoogleLookup) {
var googleUpdateMode = this.getValueFromData(data, '_improve_overwrite') ? 'overwrite' : true;
this.googleLookup(item, false, googleUpdateMode, function (err, location, result) {
// TODO: we are currently discarding the error; it should probably be
// sent back in the response, needs consideration
callback();
});
return;
}
...
inputIsValid = function (data, required, item) { if (!required) return true; var paths = this.paths; var nested = this._path.get(data); var values = nested || data; var valid = true; this.requiredPaths.forEach(function (path) { if (nested) { if (!(path in values) && item && item.get(paths[path])) { return; } if (!values[path]) { valid = false; } } else { if (!(paths[path] in values) && item && item.get(paths[path])) { return; } if (!values[paths[path]]) { valid = false; } } }); return valid; }
n/a
isModified = function (item) { return item.isModified(this.paths.number) || item.isModified(this.paths.name) || item.isModified(this.paths.street1) || item.isModified(this.paths.street2) || item.isModified(this.paths.suburb) || item.isModified(this.paths.state) || item.isModified(this.paths.postcode) || item.isModified(this.paths.country) || item.isModified(this.paths.geo); }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
kmFrom = function (item, point) { return calculateDistance(this.get(this.paths.geo), point) * RADIUS_KM; }
n/a
milesFrom = function (item, point) { return calculateDistance(this.get(this.paths.geo), point) * RADIUS_MILES; }
n/a
updateItem = function (item, data, callback) { var paths = this.paths; var fieldKeys = ['number', 'name', 'street1', 'street2', 'suburb', 'state', 'postcode', 'country']; var geoKeys = ['geo', 'geo_lat', 'geo_lng']; var valueKeys = fieldKeys.concat(geoKeys); var valuePaths = valueKeys; var values = this._path.get(data); if (!values) { // Handle flattened values valuePaths = valueKeys.map(function (i) { return paths[i]; }); values = _.pick(data, valuePaths); } // convert valuePaths to a map for easier usage valuePaths = _.zipObject(valueKeys, valuePaths); var setValue = function (key) { if (valuePaths[key] in values && values[valuePaths[key]] !== item.get(paths[key])) { item.set(paths[key], values[valuePaths[key]] || null); } }; _.forEach(fieldKeys, setValue); if (valuePaths.geo in values) { var oldGeo = item.get(paths.geo) || []; if (oldGeo.length > 1) { oldGeo[0] = item.get(paths.geo)[1]; oldGeo[1] = item.get(paths.geo)[0]; } var newGeo = values[valuePaths.geo]; if (!Array.isArray(newGeo) || newGeo.length !== 2) { newGeo = []; } if (newGeo[0] !== oldGeo[0] || newGeo[1] !== oldGeo[1]) { item.set(paths.geo, newGeo); } } else if (valuePaths.geo_lat in values && valuePaths.geo_lng in values) { var lat = utils.number(values[valuePaths.geo_lat]); var lng = utils.number(values[valuePaths.geo_lng]); item.set(paths.geo, (lat && lng) ? [lng, lat] : undefined); } var doGoogleLookup = this.getValueFromData(data, '_improve'); if (doGoogleLookup) { var googleUpdateMode = this.getValueFromData(data, '_improve_overwrite') ? 'overwrite' : true; this.googleLookup(item, false, googleUpdateMode, function (err, location, result) { // TODO: we are currently discarding the error; it should probably be // sent back in the response, needs consideration callback(); }); return; } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { // var input = this.getInputFromData(data); // TODO: We should strictly check for types in input here utils.defer(callback, true); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var result = true; var input = this.getInputFromData(data); var currentValue = item.get(this.path); this.requiredPaths.forEach(function (path) { // ignore missing values if they already exist in the item if (input[path] === undefined && currentValue[path]) return; // falsy values mean the input is invalid if (!input[path]) { result = false; } }); utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'exactly' && !filter.value) { query[this.paths.md] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); query[this.paths.md] = filter.inverted ? { $not: value } : value; return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
addToSchema = function (schema) { var paths = this.paths = { md: this.path + '.md', html: this.path + '.html', }; var markedOptions = this.markedOptions; var setMarkdown = function (value) { if (value === this.get(paths.md)) { return value; } if (typeof value === 'string') { this.set(paths.html, marked(value, markedOptions)); return value; } else { this.set(paths.html, undefined); return undefined; } }; schema.nested[this.path] = true; schema.add({ html: { type: String }, md: { type: String, set: setMarkdown }, }, this.path + '.'); this.bindUnderscoreMethods(); }
n/a
format = function (item) { return item.get(this.paths.html); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getData = function (item) { var value = item.get(this.path); return typeof value === 'object' ? value : {}; }
...
process.exit(1);
}
}
break;
case 'module root':
// if relative path is used, resolve it based on the caller's path
if (!isAbsolutePath(value)) {
var caller = callerId.getData();
value = path.resolve(path.dirname(caller.filePath), value);
}
break;
case 'app':
this.app = value;
break;
case 'mongoose':
...
inputIsValid = function (data, required, item) { if (!(this.path in data) && item && item.get(this.paths.md)) { return true; } return (!required || data[this.path]) ? true : false; }
n/a
isModified = function (item) { return item.isModified(this.paths.md); }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); if (value !== undefined) { item.set(this.paths.md, value); } else if (this.paths.md in data) { item.set(this.paths.md, data[this.paths.md]); } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var max = this.options.max; var min = this.options.min; var value = this.getValueFromData(data); var result = value === undefined || value === null || typeof value === 'string'; if (max && typeof value === 'string') { result = value.length < max; } if (min && typeof value === 'string') { result = value.length > min; } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'equals' && !filter.value) { query[this.path] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } if (filter.mode === 'between') { var min = utils.number(filter.value.min); var max = utils.number(filter.value.max); if (!isNaN(min) && !isNaN(max)) { if (filter.inverted) { var gte = {}; gte[this.path] = { $gt: max }; var lte = {}; lte[this.path] = { $lt: min }; query.$or = [gte, lte]; } else { query[this.path] = { $gte: min, $lte: max }; } } return query; } var value = utils.number(filter.value); if (!isNaN(value)) { if (filter.mode === 'gt') { query[this.path] = filter.inverted ? { $lt: value } : { $gt: value }; } else if (filter.mode === 'lt') { query[this.path] = filter.inverted ? { $gt: value } : { $lt: value }; } else { query[this.path] = filter.inverted ? { $ne: value } : value; } } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
format = function (item, format) { if (this.currency) { try { numeral.language(this.currency, require('numeral/languages/' + this.currency)); numeral.language(this.currency); } catch (err) { throw new Error('FieldType.Money: options.currency failed to load.'); } } if (format || this._formatString) { return (typeof item.get(this.path) === 'number') ? numeral(item.get(this.path)).format(format || this._formatString) : ''; } else { return item.get(this.path) || ''; } }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
inputIsValid = function (data, required, item) { var value = this.getValueFromData(data); if (value === undefined && item && (item.get(this.path) || item.get(this.path) === 0)) { return true; } if (value !== undefined && value !== '') { var newValue = utils.number(value); return (!isNaN(newValue)); } else { return (required) ? false : true; } }
n/a
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); if (value === undefined) { return process.nextTick(callback); } var newValue = utils.number(value); if (!isNaN(newValue)) { if (newValue !== item.get(this.path)) { item.set(this.path, newValue); } } else if (typeof item.get(this.path) === 'number') { item.set(this.path, null); } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); var result = value === undefined || typeof value === 'number' || value === null; if (typeof value === 'string') { if (value === '') { result = true; } else { value = utils.number(value); result = !isNaN(value); } } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!(value || typeof value === 'number'); if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'exactly' && !filter.value) { query[this.paths.first] = query[this.paths.last] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); if (filter.inverted) { query[this.paths.first] = query[this.paths.last] = { $not: value }; } else { var first = {}; first[this.paths.first] = value; var last = {}; last[this.paths.last] = value; query.$or = [first, last]; } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
addToSchema = function (schema) { var paths = this.paths = { first: this.path + '.first', last: this.path + '.last', full: this.path + '.full', }; schema.nested[this.path] = true; schema.add({ first: String, last: String, }, this.path + '.'); schema.virtual(paths.full).get(function () { return displayName(this.get(paths.first), this.get(paths.last)); }); schema.virtual(paths.full).set(function (value) { if (typeof value !== 'string') { this.set(paths.first, undefined); this.set(paths.last, undefined); return; } var split = value.split(' '); this.set(paths.first, split.shift()); this.set(paths.last, split.join(' ') || undefined); }); this.bindUnderscoreMethods(); }
n/a
format = function (item) { return item.get(this.paths.full); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getInputFromData = function (data) { // this.getValueFromData throws an error if we pass name: null if (data[this.path] === null) { return null; } var first = this.getValueFromData(data, '_first'); if (first === undefined) first = this.getValueFromData(data, '.first'); var last = this.getValueFromData(data, '_last'); if (last === undefined) last = this.getValueFromData(data, '.last'); if (first !== undefined || last !== undefined) { return { first: first, last: last, }; } return this.getValueFromData(data) || this.getValueFromData(data, '.full'); }
...
}
return this.getValueFromData(data);
};
datetime.prototype.validateRequiredInput = function (item, data, callback) {
var value = this.getInputFromData(data);
var result = !!value;
if (value === undefined && item.get(this.path)) {
result = true;
}
utils.defer(callback, result);
};
...
getSortString = function (options) { if (options.invert) { return '-' + this.paths.first + ' -' + this.paths.last; } return this.paths.first + ' ' + this.paths.last; }
...
field: field,
invert: invert,
path: field.path,
};
}).filter(truthy);
sort.string = sort.paths.map(function (i) {
if (i.field.getSortString) {
return i.field.getSortString(i);
}
return i.invert ? '-' + i.path : i.path;
}).join(' ');
return sort;
}
module.exports = expandSort;
...
inputIsValid = function (data, required, item) { // Input is valid if none was provided, but the item has data if (!(this.path in data || this.paths.first in data || this.paths.last in data || this.paths.full in data) && item && item.get( this.paths.full)) return true; // Input is valid if the field is not required if (!required) return true; // Otherwise check for valid strings in the provided data, // which may be nested or use flattened paths. if (_.isObject(data[this.path])) { return (data[this.path].full || data[this.path].first || data[this.path].last) ? true : false; } else { return (data[this.paths.full] || data[this.paths.first] || data[this.paths.last]) ? true : false; } }
n/a
isModified = function (item) { return item.isModified(this.paths.first) || item.isModified(this.paths.last); }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
updateItem = function (item, data, callback) { var paths = this.paths; var value = this.getInputFromData(data); if (typeof value === 'string' || value === null) { item.set(paths.full, value); } else if (typeof value === 'object') { if (typeof value.first === 'string' || value.first === null) { item.set(paths.first, value.first); } if (typeof value.last === 'string' || value.last === null) { item.set(paths.last, value.last); } } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getInputFromData(data); var result = value === undefined || value === null || typeof value === 'string' || (typeof value === 'object' && ( typeof value.first === 'string' || value.first === null || typeof value.last === 'string' || value.last === null) ); utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getInputFromData(data); var result; if (value === null) { result = false; } else { result = ( typeof value === 'string' && value.length || typeof value === 'object' && ( typeof value.first === 'string' && value.first.length || typeof value.last === 'string' && value.last.length) || (item.get(this.paths.full) || item.get(this.paths.first) || item.get(this.paths.last)) && ( value === undefined || (value.first === undefined && value.last === undefined)) ) ? true : false; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'equals' && !filter.value) { query[this.path] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } if (filter.mode === 'between') { var min = utils.number(filter.value.min); var max = utils.number(filter.value.max); if (!isNaN(min) && !isNaN(max)) { if (filter.inverted) { var gte = {}; gte[this.path] = { $gt: max }; var lte = {}; lte[this.path] = { $lt: min }; query.$or = [gte, lte]; } else { query[this.path] = { $gte: min, $lte: max }; } } return query; } var value = utils.number(filter.value); if (!isNaN(value)) { if (filter.mode === 'gt') { query[this.path] = filter.inverted ? { $lt: value } : { $gt: value }; } else if (filter.mode === 'lt') { query[this.path] = filter.inverted ? { $gt: value } : { $lt: value }; } else { query[this.path] = filter.inverted ? { $ne: value } : value; } } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
format = function (item, format) { var value = item.get(this.path); if (format || this.formatString) { return (typeof value === 'number') ? numeral(value).format(format || this.formatString) : ''; } else { return value || value === 0 ? String(value) : ''; } }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
inputIsValid = function (data, required, item) { var value = this.getValueFromData(data); if (value === undefined && item && (item.get(this.path) || item.get(this.path) === 0)) { return true; } if (value !== undefined && value !== '') { var newValue = utils.number(value); return (!isNaN(newValue)); } else { return (required) ? false : true; } }
n/a
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); if (value === undefined) { return process.nextTick(callback); } var newValue = utils.number(value); if (!isNaN(newValue)) { if (newValue !== item.get(this.path)) { item.set(this.path, newValue); } } else if (typeof item.get(this.path) === 'number') { item.set(this.path, null); } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); var result = value === undefined || typeof value === 'number' || value === null; if (typeof value === 'string') { if (value === '') { result = true; } else { value = utils.number(value); result = !isNaN(value); } } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!(value || typeof value === 'number'); if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; var presence = filter.presence || 'some'; // Filter empty/non-empty arrays (copied from textarray) if (filter.value === undefined || filter.value === null || filter.value === '') { // "At least one element contains nothing" // This isn't 100% accurate because this will only return arrays that // don't have elements, not ones that have empty elements, but it works // fine for 99% of the usecase query[this.path] = presence === 'some' ? { $size: 0, // "No elements contain nothing" } : { $not: { $size: 0, }, }; return query; } // Filter between two numbers if (filter.mode === 'between') { var min = utils.number(filter.value.min); var max = utils.number(filter.value.max); if (!isNaN(min) && !isNaN(max)) { query[this.path] = { $gte: min, $lte: max, }; query[this.path] = addPresenceToQuery(presence, query[this.path]); } return query; } var value = utils.number(filter.value); // Filter greater than, less than and equals if (!isNaN(value)) { if (filter.mode === 'gt') { query[this.path] = { $gt: value, }; } else if (filter.mode === 'lt') { query[this.path] = { $lt: value, }; } else { query[this.path] = { $eq: value, }; } query[this.path] = addPresenceToQuery(presence, query[this.path]); } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
format = function (item, format, separator) { var value = item.get(this.path); if (format || this._formatString) { value = value.map(function (n) { return numeral(n).format(format || this._formatString); }); } return value.join(separator || this.separator); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
inputIsValid = function (data, required, item) { var value = this.getValueFromData(data); if (required) { if (value === undefined && item && item.get(this.path) && item.get(this.path).length) { return true; } if (value === undefined || !Array.isArray(value) || (typeof value !== 'string') || (typeof value !== 'number')) { return false; } if (Array.isArray(value) && !value.length) { return false; } } if (typeof value === 'string') { if (!isValidNumber(value)) { return false; } } if (Array.isArray(value)) { for (var index = 0; index < value.length; index++) { if (!isValidNumber(value[index])) { return false; } } } return (value === undefined || Array.isArray(value) || (typeof value === 'string') || (typeof value === 'number')); }
n/a
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); if (value === undefined || value === null || value === '') { value = []; } if (!Array.isArray(value)) { value = [value]; } value = value.map(function (num) { if (typeof num !== 'number') { num = utils.number(num); } return num; }).filter(function (num) { return !Number.isNaN(num); }); item.set(this.path, value); process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); var result = true; // Let undefined, empty string and null pass if (value !== undefined && value !== '' && value !== null) { // Coerce a single value to an array if (!Array.isArray(value)) { value = [value]; } for (var i = 0; i < value.length; i++) { var thisValue = value[i]; // If it's a string, check if there's a number in the string if (typeof thisValue === 'string') { thisValue = utils.number(thisValue); } // If it's not a number or NaN invalidate if (typeof thisValue !== 'number' || Number.isNaN(thisValue)) { result = false; break; } } } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = false; // If the field is undefined but has a value saved already, validate if (value === undefined) { if (item.get(this.path) && item.get(this.path).length) { result = true; } } // If it's a string that's not empty, validate if (typeof value === 'string' && value !== '') { result = true; // If it's an array of only numbers and/or numberify-able data, validate } else if (Array.isArray(value)) { var invalidContent = false; for (var i = 0; i < value.length; i++) { var thisValue = value[i]; // If it's a string, check if there's a number in the string if (typeof thisValue === 'string') { thisValue = utils.number(thisValue); } // If even a single item is not a number or NaN, invalidate if (typeof thisValue !== 'number' || Number.isNaN(thisValue)) { invalidContent = true; break; } } if (invalidContent === false) { result = true; } } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; query[this.path] = (filter.exists) ? { $ne: null } : null; return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
addToSchema = function (schema) { var field = this; var needs_hashing = '__' + field.path + '_needs_hashing'; this.paths = { confirm: this.options.confirmPath || this.path + '_confirm', hash: this.options.hashPath || this.path + '_hash', }; schema.path(this.path, _.defaults({ type: String, set: function (newValue) { this[needs_hashing] = true; return newValue; }, }, this.options)); schema.virtual(this.paths.hash).set(function (newValue) { this.set(field.path, newValue); this[needs_hashing] = false; }); schema.pre('save', function (next) { if (!this.isModified(field.path) || !this[needs_hashing]) { return next(); } // reset the [needs_hashing] flag so that new values can't be hashed more than once // (inherited models double up on pre save handlers for password fields) this[needs_hashing] = false; if (!this.get(field.path)) { this.set(field.path, undefined); return next(); } var item = this; bcrypt.genSalt(field.workFactor, function (err, salt) { if (err) { return next(err); } bcrypt.hash(item.get(field.path), salt, function () {}, function (err, hash) { if (err) { return next(err); } // override the cleartext password with the hashed one item.set(field.path, hash); next(); }); }); }); this.bindUnderscoreMethods(); }
n/a
compare = function (item, candidate, callback) { if (typeof callback !== 'function') throw new Error('Password.compare() requires a callback function.'); var value = item.get(this.path); if (!value) return callback(null, false); bcrypt.compare(candidate, item.get(this.path), callback); }
...
// exclude non-javascript or coffee files in the updates folder
return (path.extname(i) !== '.js' && path.extname(i) !== '.coffee') ? false : path.basename(i,
x27;.js');
}).filter(function (i) {
// exclude falsy values and filenames that without a valid semver
return i && semver.valid(i.split('-')[0]);
}).sort(function (a, b) {
// exclude anything after a hyphen from the version number
return semver.compare(a.split('-')[0], b.split('-')[0]);
});
async.eachSeries(updates, applyUpdate, function (err) {
if (updateCount || deferCount || skipCount) {
var status = '';
if (updateCount) {
status += 'Successfully applied ' + utils.plural(updateCount, '* update');
...
format = function (item) { if (!item.get(this.path)) return ''; var len = Math.round(Math.random() * 4) + 6; var stars = ''; for (var i = 0; i < len; i++) stars += '*'; return stars; }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getData = function (item) { return item.get(this.path) ? true : false; }
...
process.exit(1);
}
}
break;
case 'module root':
// if relative path is used, resolve it based on the caller's path
if (!isAbsolutePath(value)) {
var caller = callerId.getData();
value = path.resolve(path.dirname(caller.filePath), value);
}
break;
case 'app':
this.app = value;
break;
case 'mongoose':
...
inputIsValid = function (data, required, item) { if (data[this.path] && this.paths.confirm in data) { return data[this.path] === data[this.paths.confirm] ? true : false; } if (data[this.path] || data[this.paths.hash] || (item && item.get(this.path))) return true; return required ? false : true; }
n/a
updateItem = function (item, data, callback) { var hashValue = this.getValueFromData(data, '_hash'); var passwordValue = this.getValueFromData(data); if (passwordValue !== undefined) { item.set(this.path, passwordValue); } else if (hashValue !== undefined) { item.set(this.paths.hash, hashValue); } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var detail = ''; var result = true; var min = this.options.min; var max = this.options.max || 72; var complexity = this.options.complexity; var confirmValue = this.getValueFromData(data, '_confirm'); var passwordValue = this.getValueFromData(data); if (confirmValue !== undefined && passwordValue !== confirmValue) { detail = 'passwords must match\n'; } if (min && typeof passwordValue === 'string' && passwordValue.length < min) { detail += 'password must be longer than ' + min + ' characters\n'; } if (max && typeof passwordValue === 'string' && passwordValue.length > max) { detail += 'password must not be longer than ' + max + ' characters\n'; } for (var prop in complexity) { if (complexity[prop] && typeof passwordValue === 'string') { var complexityCheck = (regexChunk[prop]).test(passwordValue); if (!complexityCheck) { detail += detailMsg[prop] + '\n'; } } } result = detail.length === 0; utils.defer(callback, result, detail); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var hashValue = this.getValueFromData(data, '_hash'); var passwordValue = this.getValueFromData(data); var result = hashValue || passwordValue ? true : false; if (!result && passwordValue === undefined && hashValue === undefined && item.get(this.path)) result = true; utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (!Array.isArray(filter.value)) { if (typeof filter.value === 'string' && filter.value) { filter.value = [filter.value]; } else { filter.value = []; } } if (filter.value.length) { query[this.path] = (filter.inverted) ? { $nin: filter.value } : { $in: filter.value }; } else { if (this.many) { query[this.path] = (filter.inverted) ? { $not: { $size: 0 } } : { $size: 0 }; } else { query[this.path] = (filter.inverted) ? { $ne: null } : null; } } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
addToSchema = function (schema) { var field = this; var def = { type: this._nativeType, ref: this.options.ref, index: (this.options.index ? true : false), required: (this.options.required ? true : false), unique: (this.options.unique ? true : false), }; this.paths = { refList: this.options.refListPath || this.path + 'RefList', }; schema.path(this.path, this.many ? [def] : def); schema.virtual(this.paths.refList).get(function () { return keystone.list(field.options.ref); }); this.bindUnderscoreMethods(); }
n/a
format = function (item) { var value = item.get(this.path); // force the formatted value to be a string - unexpected things happen with ObjectIds. return this.many ? value.join(', ') : (value || '') + ''; }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getData = function (item) { var value = item.get(this.path); if (this.many) { return Array.isArray(value) ? value : []; } else { return value; } }
...
process.exit(1);
}
}
break;
case 'module root':
// if relative path is used, resolve it based on the caller's path
if (!isAbsolutePath(value)) {
var caller = callerId.getData();
value = path.resolve(path.dirname(caller.filePath), value);
}
break;
case 'app':
this.app = value;
break;
case 'mongoose':
...
getExpandedData = function (item) { var value = item.get(this.path); if (this.many) { if (!value || !Array.isArray(value)) return []; return value.map(expandRelatedItemData.bind(this)).filter(truthy); } else { return expandRelatedItemData.call(this, value); } }
...
if (field.type !== 'relationship' || !options.expandRelationshipFields) {
// use the transformFieldValue function to get the data
data[path] = transformFieldValue(field, item, options);
return;
}
// relationship values should be expanded into separate name and
// id pairs using the field's getExpandedData method.
var expanded = field.getExpandedData(item);
if (field.many) {
// for many-type relationships, ensure the value is an array,
// and turn it into a list of 'name (id)' values
data[path] = (Array.isArray(expanded) ? expanded : []).map(function (i) {
return i.name ? i.name + ' (' + i.id + ')' : i.id;
}).join(', ');
} else if (typeof expanded === 'object') {
...
getProperties = function () { var refList = this.refList; return { refList: { singular: refList.singular, plural: refList.plural, path: refList.path, key: refList.key, }, }; }
n/a
inputIsValid = function (data, required, item) { if (!required) return true; if (!(this.path in data) && item && ((this.many && item.get(this.path).length) || item.get(this.path))) return true; if (typeof data[this.path] === 'string') { return (data[this.path].trim()) ? true : false; } else { return (data[this.path]) ? true : false; } }
n/a
updateItem = function (item, data, callback) { if (item.populated(this.path)) { throw new Error('fieldTypes.relationship.updateItem() Error - You cannot update populated relationships.'); } if (this.many) { var arr = item.get(this.path); var _old = arr.map(function (i) { return String(i); }); var _new = data[this.path]; if (!utils.isArray(_new)) { _new = String(_new || '').split(','); } _new = _.compact(_new); // Only update if the lists aren't the same if (!_.isEqual(_old, _new)) { item.set(this.path, _new); } } else { if (data[this.path]) { if (data[this.path] !== item.get(this.path)) { item.set(this.path, data[this.path]); } } else if (item.get(this.path)) { item.set(this.path, null); } } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); var result = false; if (value === undefined || value === null || value === '') { result = true; } else { if (this.many) { if (!Array.isArray(value) && typeof value === 'string' && value.length) { value = [value]; } if (Array.isArray(value)) { result = true; } } else { if (typeof value === 'string' && value.length) { result = true; } if (typeof value === 'object' && value.id) { result = true; } } } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = false; if (value === undefined) { if (this.many) { if (item.get(this.path).length) { result = true; } } else { if (item.get(this.path)) { result = true; } } } else if (this.many) { if (!Array.isArray(value) && typeof value === 'string' && value.length) { value = [value]; } if (Array.isArray(value) && value.length) { result = true; } } else { if (value) { result = true; } } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addToSchema = function (schema) {
var knox = require('knox');
var field = this;
var paths = this.paths = {
// fields
filename: this.path + '.filename',
originalname: this.path + '.originalname',
path: this.path + '.path',
size: this.path + '.size',
filetype: this.path + '.filetype',
url: this.path + '.url',
// virtuals
exists: this.path + '.exists',
upload: this.path + '_upload',
action: this.path + '_action',
};
var schemaPaths = this._path.addTo({}, {
filename: String,
originalname: String,
path: String,
size: Number,
filetype: String,
url: String,
});
schema.add(schemaPaths);
var exists = function (item) {
return (item.get(paths.url) ? true : false);
};
// The .exists virtual indicates whether a file is stored
schema.virtual(paths.exists).get(function () {
return schemaMethods.exists.apply(this);
});
var reset = function (item) {
item.set(field.path, {
filename: '',
originalname: '',
path: '',
size: 0,
filetype: '',
url: '',
});
};
var schemaMethods = {
exists: function () {
return exists(this);
},
/**
* Resets the value of the field
*
* @api public
*/
reset: function () {
reset(this);
},
/**
* Deletes the file from S3File and resets the field
*
* @api public
*/
delete: function () {
try {
var client = knox.createClient(field.s3config);
client.deleteFile(this.get(paths.path) + this.get(paths.filename), function (err, res) { return res ? res.resume() : false; }); //
eslint-disable-line handle-callback-err
} catch (e) {} // eslint-disable-line no-empty
reset(this);
},
};
_.forEach(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();
}
n/a
format = function (item) { if (this.hasFormatter()) { return this.options.format(item, item[this.path]); } return item.get(this.paths.url); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
generateHeaders = function (item, file, callback) { var field = this; var filetype = file.mimetype || file.type; var headers = { 'Content-Type': filetype, 'x-amz-acl': 'public-read', }; var customHeaders = {}; var headersOption = {}; var computedHeaders; var defaultHeaders; if (_.has(field.s3config, 'default headers')) { defaultHeaders = field.s3config['default headers']; if (_.isArray(defaultHeaders)) { _.forEach(defaultHeaders, function (header) { var _header = {}; if (validateHeader(header, callback)) { _header[header.name] = header.value; customHeaders = assign(customHeaders, _header); } }); } else if (_.isObject(defaultHeaders)) { customHeaders = assign(customHeaders, defaultHeaders); } else { return callback(new Error('Unsupported Header option: defaults headers must be either an Object or Array ' + JSON.stringify(defaultHeaders ))); } } if (field.options.headers) { headersOption = field.options.headers; if (_.isFunction(headersOption)) { computedHeaders = headersOption.call(field, item, file); if (_.isArray(computedHeaders)) { _.forEach(computedHeaders, function (header) { var _header = {}; if (validateHeader(header, callback)) { _header[header.name] = header.value; customHeaders = assign(customHeaders, _header); } }); } else if (_.isObject(computedHeaders)) { customHeaders = assign(customHeaders, computedHeaders); } else { return callback(new Error('Unsupported Header option: computed headers must be either an Object or Array ' + JSON.stringify( computedHeaders))); } } else if (_.isArray(headersOption)) { _.forEach(headersOption, function (header) { var _header = {}; if (validateHeader(header, callback)) { _header[header.name] = header.value; customHeaders = assign(customHeaders, _header); } }); } else if (_.isObject(headersOption)) { customHeaders = assign(customHeaders, headersOption); } } if (validateHeaders(customHeaders, callback)) { headers = assign(headers, customHeaders); } return headers; }
...
path = field.options.path(item, path);
}
if (typeof field.options.filename === 'function') {
filename = field.options.filename(item, filename, originalname);
}
headers = field.generateHeaders(item, file, callback);
knox.createClient(field.s3config).putFile(file.path, path + filename, headers, function (err, res) {
if (err) return callback(err);
if (res) {
if (res.statusCode !== 200) {
return callback(new Error('Amazon returned Http Code: ' + res.statusCode));
...
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.files && req.files[paths.upload] && req.files[paths.upload].size) { return field.uploadFile(item, req.files[paths.upload], true, callback); } return callback(); }; }
n/a
hasFormatter = function () { return typeof this.options.format === 'function'; }
...
* Formats the field value
*
* Delegates to the options.format function if it exists.
* @api public
*/
localfile.prototype.format = function (item) {
if (!item.get(this.paths.filename)) return '';
if (this.hasFormatter()) {
var file = item.get(this.path);
file.href = this.href(item);
return this.options.format.call(this, item, file);
}
return this.href(item);
};
...
inputIsValid = function (data) { // eslint-disable-line no-unused-vars // TODO - how should file field input be validated? return true; }
n/a
isModified = function (item) { return item.isModified(this.paths.url); }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
updateItem = function (item, data, callback) { // eslint-disable-line no-unused-vars // TODO - direct updating of data (not via upload) process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
uploadFile = function (item, file, update, callback) { var knox = require('knox'); var field = this; var path = field.options.s3path ? field.options.s3path + '/' : ''; var prefix = field.options.datePrefix ? moment().format(field.options.datePrefix) + '-' : ''; var filename = prefix + file.name; var originalname = file.originalname; var filetype = file.mimetype || file.type; var headers; if (typeof update === 'function') { callback = update; update = false; } if (field.options.allowedTypes && field.options.allowedTypes.indexOf(filetype) === -1) { return callback(new Error('Unsupported File Type: ' + filetype)); } var doUpload = function () { if (typeof field.options.path === 'function') { path = field.options.path(item, path); } if (typeof field.options.filename === 'function') { filename = field.options.filename(item, filename, originalname); } headers = field.generateHeaders(item, file, callback); knox.createClient(field.s3config).putFile(file.path, path + filename, headers, function (err, res) { if (err) return callback(err); if (res) { if (res.statusCode !== 200) { return callback(new Error('Amazon returned Http Code: ' + res.statusCode)); } else { res.resume(); } } var protocol = (field.s3config.protocol && field.s3config.protocol + ':') || ''; var url = res.req.url.replace(/^https?:/i, protocol).replace(/%25/g, '%'); var fileData = { filename: filename, originalname: originalname, path: path, size: file.size, filetype: filetype, url: url, }; if (update) { item.set(field.path, fileData); } callback(null, fileData); }); }; this.callHook('pre:upload', item, file, function (err) { if (err) return callback(err); doUpload(); }); }
...
if (/^(delete|reset)$/.test(action)) {
field.apply(item, action);
}
}
if (req.files && req.files[paths.upload] && req.files[paths.upload].size) {
return field.uploadFile(item, req.files[paths.upload], true, callback);
}
return callback();
};
};
...
addFilterToQuery = function (filter) { var query = {}; if (!Array.isArray(filter.value)) { if (filter.value) { filter.value = [filter.value]; } else { filter.value = []; } } if (filter.value.length > 1) { query[this.path] = (filter.inverted) ? { $nin: filter.value } : { $in: filter.value }; } else if (filter.value.length === 1) { query[this.path] = (filter.inverted) ? { $ne: filter.value[0] } : filter.value[0]; } else { query[this.path] = (filter.inverted) ? { $nin: ['', null] } : { $in: ['', null] }; } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
addToSchema = function (schema) { var field = this; this.paths = { data: this.options.dataPath || this.path + 'Data', label: this.options.labelPath || this.path + 'Label', options: this.options.optionsPath || this.path + 'Options', map: this.options.optionsMapPath || this.path + 'OptionsMap', }; schema.path(this.path, _.defaults({ type: this._nativeType, enum: this.values, set: function (val) { return (val === '' || val === null || val === false) ? undefined : val; }, }, this.options)); schema.virtual(this.paths.data).get(function () { return field.map[this.get(field.path)]; }); schema.virtual(this.paths.label).get(function () { return field.labels[this.get(field.path)]; }); schema.virtual(this.paths.options).get(function () { return field.ops; }); schema.virtual(this.paths.map).get(function () { return field.map; }); this.bindUnderscoreMethods(); }
n/a
cloneMap = function () { return utils.optionsMap(this.ops, true); }
n/a
cloneOps = function () { return _.map(this.ops, _.clone); }
n/a
format = function (item) { return this.labels[item.get(this.path)] || ''; }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
inputIsValid = function (data, required, item) { if (data[this.path]) { return (data[this.path] in this.map) ? true : false; } else { return (!required || (!(this.path in data) && item && item.get(this.path))) ? true : false; } }
n/a
pluck = function (item, property, _default) { var option = item.get(this.paths.data); return (option) ? option[property] : _default; }
n/a
validateInput = function (data, callback) { var value = this.getValueFromData(data); if (typeof value === 'string' && this.numeric) { value = utils.number(value); } var result = value === undefined || value === null || value === '' || (value in this.map) ? true : false; utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = false; if (value === undefined) { if (item.get(this.path)) { result = true; } } else if (value) { if (value !== '') { // This is already checkind in validateInput, but it doesn't hurt // to check again for security if (value in this.map) { result = true; } } } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'exactly' && !filter.value) { query[this.path] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); query[this.path] = filter.inverted ? { $not: value } : value; return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
crop = function (item, length, append, preserveWords) { return utils.cropString(item.get(this.path), length, append, preserveWords); }
n/a
validateInput = function (data, callback) { var max = this.options.max; var min = this.options.min; var value = this.getValueFromData(data); var result = value === undefined || value === null || typeof value === 'string'; if (max && typeof value === 'string') { result = value.length < max; } if (min && typeof value === 'string') { result = value.length > min; } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; var presence = filter.presence || 'some'; // Filter empty/non-empty arrays if (!filter.value) { // "At least one element contains nothing" // This isn't 100% accurate because this will only return arrays that // don't have elements, not ones that have empty elements, but it works // fine for 99% of the usecase query[this.path] = presence === 'some' ? { $size: 0, // "No elements contain nothing" } : { $not: { $size: 0, }, }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); if (presence === 'none') { query[this.path] = addPresenceToQuery(presence, value); } else { query[this.path] = addPresenceToQuery(presence, { $regex: value, }); } return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
format = function (item, separator) { return item.get(this.path).join(separator || this.separator); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
inputIsValid = function (data, required, item) { var value = this.getValueFromData(data); if (required) { if (value === undefined && item && item.get(this.path) && item.get(this.path).length) { return true; } if (value === undefined || !Array.isArray(value) || (typeof value !== 'string') || (typeof value !== 'number')) { return false; } if (Array.isArray(value) && !value.length) { return false; } } return (value === undefined || Array.isArray(value) || (typeof value === 'string') || (typeof value === 'number')); }
n/a
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); if (value === undefined || value === null || value === '') { value = []; } if (!Array.isArray(value)) { value = [value]; } value = value.map(function (str) { if (str && str.toString) { str = str.toString(); } return str; }).filter(function (str) { return (typeof str === 'string' && str); }); item.set(this.path, value); process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { var value = this.getValueFromData(data); var result = true; // If the value is null, undefined or an empty string // bail early since updateItem sanitizes that just fine if (value !== undefined && value !== null && value !== '') { // If the value is not an array, convert it to one // e.g. if textarr = 'somestring' (which is valid) if (!Array.isArray(value)) { value = [value]; } for (var i = 0; i < value.length; i++) { var thisValue = value[i]; // If the current value is not a string and is neither false nor // undefined, fail the validation if (typeof thisValue !== 'string') { result = false; break; } } } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = false; // If the value is undefined and we have something stored already, validate if (value === undefined) { if (item.get(this.path) && item.get(this.path).length) { result = true; } } // If it's a string that's not empty, validate if (typeof value === 'string') { if (value !== '') { result = true; } // If it's an array of only strings and/or strinigfy-able data, validate } else if (Array.isArray(value)) { var invalidContent = false; for (var i = 0; i < value.length; i++) { var thisValue = value[i]; // If even a single item is not a string or an empty string, invalidate if (typeof thisValue !== 'string' || thisValue === '') { invalidContent = true; break; } } if (invalidContent === false) { result = true; } } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'exactly' && !filter.value) { query[this.path] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); query[this.path] = filter.inverted ? { $not: value } : value; return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
crop = function (item, length, append, preserveWords) { return utils.cropString(item.get(this.path), length, append, preserveWords); }
n/a
format = function (item) { return utils.textToHTML(item.get(this.path)); }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
validateInput = function (data, callback) { var max = this.options.max; var min = this.options.min; var value = this.getValueFromData(data); var result = value === undefined || value === null || typeof value === 'string'; if (max && typeof value === 'string') { result = value.length < max; } if (min && typeof value === 'string') { result = value.length > min; } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addFilterToQuery = function (filter) { var query = {}; if (filter.mode === 'exactly' && !filter.value) { query[this.path] = filter.inverted ? { $nin: ['', null] } : { $in: ['', null] }; return query; } var value = utils.escapeRegExp(filter.value); if (filter.mode === 'beginsWith') { value = '^' + value; } else if (filter.mode === 'endsWith') { value = value + '$'; } else if (filter.mode === 'exactly') { value = '^' + value + '$'; } value = new RegExp(value, filter.caseSensitive ? '' : 'i'); query[this.path] = filter.inverted ? { $not: value } : value; return query; }
...
function addFiltersToQuery (filters) {
var fields = Object.keys(this.fields);
var query = {};
fields.forEach(function (path) {
var field = this.fields[path];
if (!field.addFilterToQuery || !filters[field.path]) return;
combineQueries(query, field.addFilterToQuery(filters[field.path]));
}, this);
debug('Adding filters to query, returned:', query);
return query;
}
module.exports = addFiltersToQuery;
...
format = function (item) { var url = item.get(this.path) || ''; if (this.options.format === false) { return url; } else if (typeof this.options.format === 'function') { return this.options.format(url); } else { return removeProtocolPrefix(url); } }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
validateInput = function (data, callback) { var max = this.options.max; var min = this.options.min; var value = this.getValueFromData(data); var result = value === undefined || value === null || typeof value === 'string'; if (max && typeof value === 'string') { result = value.length < max; } if (min && typeof value === 'string') { result = value.length > min; } utils.defer(callback, result); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = !!value; if (value === undefined && item.get(this.path)) { result = true; } utils.defer(callback, result); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
addToSchema = function (schema) { var ops = (this._nativeType) ? _.defaults({ type: this._nativeType }, this.options) : this.options; schema.path(this.path, ops); this.bindUnderscoreMethods(); }
n/a
bindUnderscoreMethods = function () { var field = this; (this._underscoreMethods || []).concat({ fn: 'updateItem', as: 'update' }).forEach(function (method) { if (typeof method === 'string') { method = { fn: method, as: method }; } if (typeof field[method.fn] !== 'function') { throw new Error('Invalid underscore method (' + method.fn + ') applied to ' + field.list.key + '.' + field.path + ' (' + field .type + ')'); } field.underscoreMethod(method.as, function () { var args = [this].concat(Array.prototype.slice.call(arguments)); return field[method.fn].apply(field, args); }); }); }
...
});
// 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
*/
azurefile.prototype.format = function (item) {
return item.get(this.paths.url);
...
format = function (item) { var value = item.get(this.path); if (value === undefined) return ''; return value; }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
getData = function (item) { return item.get(this.path); }
...
process.exit(1);
}
}
break;
case 'module root':
// if relative path is used, resolve it based on the caller's path
if (!isAbsolutePath(value)) {
var caller = callerId.getData();
value = path.resolve(path.dirname(caller.filePath), value);
}
break;
case 'app':
this.app = value;
break;
case 'mongoose':
...
getDefaultValue = function () { return typeof this.options.default !== 'undefined' ? this.options.default : ''; }
...
// Set the field values to their default values when first rendering the
// form. (If they have a default value, that is)
var values = {};
Object.keys(this.props.list.fields).forEach(function (key) {
var field = _this.props.list.fields[key];
var FieldComponent = _FieldTypes.Fields[field.type];
values[field.path] = FieldComponent.getDefaultValue(field);
});
return {
values: values,
alerts: {}
};
},
componentDidMount: function componentDidMount() {
...
getOptions = function () { if (!this.__options) { this.__options = {}; var optionKeys = DEFAULT_OPTION_KEYS; if (_.isArray(this._properties)) { optionKeys = optionKeys.concat(this._properties); } optionKeys.forEach(function (key) { if (this[key]) { this.__options[key] = this[key]; } else if (this.options[key]) { this.__options[key] = this.options[key]; } }, this); if (this.getProperties) { assign(this.__options, this.getProperties()); } this.__options.hasFilterMethod = this.addFilterToQuery ? true : false; this.__options.defaultValue = this.getDefaultValue(); } return this.__options; }
...
var forEach = require('lodash/forEach');
function createKeystoneHash () {
var hash = crypto.createHash('md5');
hash.update(this.version);
forEach(this.lists, function (list, key) {
hash.update(JSON.stringify(list.getOptions()));
});
return hash.digest('hex').slice(0, 6);
}
module.exports = createKeystoneHash;
...
getPreSaveWatcher = function () { var field = this; var applyValue; if (this.options.watch === true) { // watch == true means always apply the value method applyValue = function () { return true; }; } else { // if watch is a string, convert it to a list of paths to watch if (typeof this.options.watch === 'string') { this.options.watch = this.options.watch.split(' '); } if (typeof this.options.watch === 'function') { applyValue = this.options.watch; } else if (_.isArray(this.options.watch)) { applyValue = function (item) { var pass = false; field.options.watch.forEach(function (path) { if (item.isModified(path)) pass = true; }); return pass; }; } else if (_.isObject(this.options.watch)) { applyValue = function (item) { var pass = false; _.forEach(field.options.watch, function (value, path) { if (item.isModified(path) && item.get(path) === value) pass = true; }); return pass; }; } } if (!applyValue) { console.error('\nError: Invalid Configuration\n\n' + 'Invalid watch value (' + this.options.watch + ') provided for ' + this.list.key + '.' + this.path + ' (' + this.type + ')'); process.exit(1); } if (typeof this.options.value !== 'function') { console.error('\nError: Invalid Configuration\n\n' + 'Watch set with no value method provided for ' + this.list.key + '.' + this.path + ' (' + this.type + ')'); process.exit(1); } return function (next) { if (!applyValue(this)) { return next(); } di(field.options.value).call(this, function (err, val) { if (err) { console.error('\nError: ' + 'Watch set with value method for ' + field.list.key + '.' + field.path + ' (' + field.type + ') throws error:' + err); } else { this.set(field.path, val); } next(); }.bind(this)); }; }
n/a
getSize = function () { if (!this.__size) { var size = this._fixedSize || this.options.size || this.options.width; if (size !== 'small' && size !== 'medium' && size !== 'large' && size !== 'full') { size = this._defaultSize || 'full'; } this.__size = size; } return this.__size; }
n/a
getValueFromData = function (data, subpath) { return this._path.get(data, subpath); }
...
util.inherits(boolean, FieldType);
boolean.prototype.defaults = {
default: false,
};
boolean.prototype.validateInput = function (data, callback) {
var value = this.getValueFromData(data);
var result = true;
if (value !== undefined
&& value !== null
&& typeof value !== 'string'
&& typeof value !== 'number'
&& typeof value !== 'boolean') {
result = false;
...
inputIsValid = function (data, required, item) { if (!required) return true; var value = this.getValueFromData(data); if (value === undefined && item && item.get(this.path)) return true; if (typeof data[this.path] === 'string') { return (data[this.path].trim()) ? true : false; } else { return (data[this.path]) ? true : false; } }
n/a
isModified = function (item) { return item.isModified(this.path); }
...
var modified = false;
var incomplete = false;
var values = [];
autokey.from.forEach(function (ops) {
if (list.fields[ops.path]) {
values.push(list.fields[ops.path].format(this, ops.format));
if (list.fields[ops.path].isModified(this)) {
modified = true;
}
// if source field is neither selected nor modified we don't have a way to generate a complete autokey
else if (!this.isSelected(ops.path)) {
incomplete = true;
}
} else {
...
underscoreMethod = function (path, fn) { this.list.underscoreMethod(this.path + '.' + path, function () { return fn.apply(this, arguments); }); }
...
azure.createBlobService().blobService.deleteBlob(this.get(paths.container), this.get(paths.filename), function () {});
} catch (e) {} // eslint-disable-line no-empty
reset(this);
},
};
_.forEach(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));
};
...
updateItem = function (item, data, callback) { var value = this.getValueFromData(data); // This is a deliberate type coercion so that numbers from forms play nice if (value !== undefined && value != item.get(this.path)) { // eslint-disable-line eqeqeq item.set(this.path, value); } process.nextTick(callback); }
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
validateInput = function (data, callback) { utils.defer(callback, this.inputIsValid(data)); }
...
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
...
validateRequiredInput = function (item, data, callback) { utils.defer(callback, this.inputIsValid(data, true, item)); }
...
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
...
function file(list, path, options) { this._underscoreMethods = ['format', 'upload', 'remove', 'reset']; this._fixedSize = 'full'; if (!options.storage) { throw new Error('Invalid Configuration\n\n' + 'File fields (' + list.key + '.' + path + ') require storage to be provided.'); } this.storage = options.storage; file.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function geopoint(list, path, options) { this._fixedSize = 'medium'; geopoint.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function html(list, path, options) { this._nativeType = String; this._defaultSize = 'full'; this.wysiwyg = options.wysiwyg || false; this.height = options.height || 180; this._properties = ['wysiwyg', 'height']; html.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function key(list, path, options) { this._nativeType = String; this._defaultSize = 'medium'; this.separator = options.separator || '-'; key.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
Keystone = function () { grappling.mixin(this).allowHooks('pre:static', 'pre:bodyparser', 'pre:session', 'pre:logger', 'pre:admin', 'pre:routes', 'pre:render ', 'updates', 'signin', 'signout'); this.lists = {}; this.fieldTypes = {}; this.paths = {}; this._options = { 'name': 'Keystone', 'brand': 'Keystone', 'admin path': 'keystone', 'compress': true, 'headless': false, 'logger': ':method :url :status :response-time ms', 'auto update': false, 'model prefix': null, 'module root': moduleRoot, 'frame guard': 'sameorigin', 'cache admin bundles': true, }; this._redirects = {}; // expose express this.express = express; // init environment defaults this.set('env', process.env.NODE_ENV || 'development'); this.set('port', process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || '3000'); this.set('host', process.env.HOST || process.env.IP || process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0'); this.set('listen', process.env.LISTEN); this.set('ssl', process.env.SSL); this.set('ssl port', process.env.SSL_PORT || '3001'); this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP); this.set('ssl key', process.env.SSL_KEY); this.set('ssl cert', process.env.SSL_CERT); this.set('cookie secret', process.env.COOKIE_SECRET); this.set('cookie signin', (this.get('env') === 'development') ? true : false); this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY); this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY); this.set('mandrill username', process.env.MANDRILL_USERNAME); this.set('google api key', process.env.GOOGLE_BROWSER_KEY); this.set('google server api key', process.env.GOOGLE_SERVER_KEY); this.set('ga property', process.env.GA_PROPERTY); this.set('ga domain', process.env.GA_DOMAIN); this.set('chartbeat property', process.env.CHARTBEAT_PROPERTY); this.set('chartbeat domain', process.env.CHARTBEAT_DOMAIN); this.set('allowed ip ranges', process.env.ALLOWED_IP_RANGES); if (process.env.S3_BUCKET && process.env.S3_KEY && process.env.S3_SECRET) { this.set('s3 config', { bucket: process.env.S3_BUCKET, key: process.env.S3_KEY, secret: process.env.S3_SECRET, region: process .env.S3_REGION }); } if (process.env.AZURE_STORAGE_ACCOUNT && process.env.AZURE_STORAGE_ACCESS_KEY) { this.set('azurefile config', { account: process.env.AZURE_STORAGE_ACCOUNT, key: process.env.AZURE_STORAGE_ACCESS_KEY }); } if (process.env.CLOUDINARY_URL) { // process.env.CLOUDINARY_URL is processed by the cloudinary package when this is set this.set('cloudinary config', true); } // init mongoose this.set('mongoose', require('mongoose')); this.mongoose.Promise = require('es6-promise').Promise; // Attach middleware packages, bound to this instance this.middleware = { api: require('./lib/middleware/api')(this), cors: require('./lib/middleware/cors')(this), }; }
n/a
applyUpdates = function (callback) { var self = this; self.callHook('pre:updates', function (err) { if (err) return callback(err); require('./lib/updates').apply(function (err) { if (err) return callback(err); self.callHook('post:updates', callback); }); }); }
...
debug('mongo connection open');
mongoConnectionOpen = true;
var connected = function () {
if (keystone.get('auto update')) {
debug('applying auto update');
keystone.applyUpdates(callback);
} else {
callback();
}
};
if (keystone.sessionStorePromise) {
keystone.sessionStorePromise.then(connected);
...
function closeDatabaseConnection(callback) { this.mongoose.disconnect(function () { debug('mongo connection closed'); callback && callback(); }); return this; }
n/a
function createItems(data, ops, callback) { var keystone = this; var options = { verbose: false, strict: true, refs: null, }; var dashes = '------------------------------------------------'; if (!_.isObject(data)) { throw new Error('keystone.createItems() requires a data object as the first argument.'); } if (_.isObject(ops)) { _.extend(options, ops); } if (typeof ops === 'function') { callback = ops; } var lists = _.keys(data); var refs = options.refs || {}; var stats = {}; // logger function function writeLog (data) { console.log(keystone.get('name') + ': ' + data); } async.waterfall([ // create items function (next) { async.eachSeries(lists, function (key, doneList) { var list = keystone.list(key); var relationshipPaths = _.filter(list.fields, { type: 'relationship' }).map(function (i) { return i.path; }); if (!list) { if (options.strict) { return doneList({ type: 'invalid list', message: 'List key ' + key + ' is invalid.', }); } if (options.verbose) { writeLog('Skipping invalid list: ' + key); } return doneList(); } if (!refs[list.key]) { refs[list.key] = {}; } stats[list.key] = { singular: list.singular, plural: list.plural, created: 0, warnings: 0, }; var itemsProcessed = 0; var totalItems = data[key].length; if (options.verbose) { writeLog(dashes); writeLog('Processing list: ' + key); writeLog('Items to create: ' + totalItems); writeLog(dashes); } async.eachSeries(data[key], function (data, doneItem) { itemsProcessed++; // Evaluate function properties to allow generated values (excluding relationships) _.keys(data).forEach(function (i) { if (typeof data[i] === 'function' && relationshipPaths.indexOf(i) === -1) { data[i] = data[i](); if (options.verbose) { writeLog('Generated dynamic value for [' + i + ']: ' + data[i]); } } }); var doc = data.__doc = new list.model(); if (data.__ref) { refs[list.key][data.__ref] = doc; } async.each(list.fieldsArray, function (field, doneField) { // skip relationship fields on the first pass. if (field.type !== 'relationship') { // TODO: Validate items? field.updateItem(doc, data, doneField); } else { doneField(); } }, function (err) { if (!err) { if (options.verbose) { var documentName = list.getDocumentName(doc); writeLog('Creating item [' + itemsProcessed + ' of ' + totalItems + '] - ' + documentName); } doc.save(function (err) { if (err) { err.model = key; err.data = data; debug('error saving ', key); } else { stats[list.key].created++; } doneItem(err); }); } else { doneItem(err); } }); }, doneList); }, next); }, // link items function (next) { async.each(lists, function (key, doneList) { var list = keystone.list(key); var relationships = _.filter(list.fields, { type: 'relationship' }); if (!list || !relationships.length) { return doneList(); } var itemsProcessed = 0; var totalItems = data[key].length; if (options.verbose) { writeLog(dashes); writeLog('Processing relationships for: ' + key); writeLog('Items to process: ' + totalItems); writeLog(dashes); } async.each(data[key], function (srcData, doneItem) { var doc = srcData.__doc; var relationshipsUpdated = 0; itemsProcessed++; if (options.verbose) { var documentName = list.getDocumentName(doc); writeLog('Processing item [' + itemsProcessed + ' of ' + totalItems + '] - ' + documentName); } async.each(relationships, function (field, doneField) { var fieldValue = null; var refsLookup = null; if (!field.path) { writeLog('WARNING: Invalid relationship (undefined list path) [List: ' + key + ' ...
...
// auto-wrap create scripts for a friendlier shorthand syntax
if (_.isObject(update.create)) {
var items = update.create;
var ops = update.options || {};
var background_mode = update.__background__ ? ' (background mode) ' : '';
update = function (done) {
keystone.createItems(items, ops, function (err, stats) {
if (!err) {
var statsMsg = stats ? stats.message : '';
console.log('\n' + _dashes_,
'\n' + keystone.get('name') + ': Successfully applied update ' + file + background_mode +
x27;.',
'\n' + statsMsg,
'\n');
...
function createKeystoneHash() { var hash = crypto.createHash('md5'); hash.update(this.version); forEach(this.lists, function (list, key) { hash.update(JSON.stringify(list.getOptions())); }); return hash.digest('hex').slice(0, 6); }
...
src += '};\n';
});
return str(src);
}
module.exports = function createStaticRouter (keystone) {
var keystoneHash = keystone.createKeystoneHash();
var writeToDisk = keystone.get('cache admin bundles');
var router = express.Router();
/* Prepare browserify bundles */
var bundles = {
fields: browserify({
stream: buildFieldTypesStream(keystone.fieldTypes),
...
function createRouter() { return express.Router(); }
n/a
expandPath = function (pathValue) { pathValue = (typeof pathValue === 'string' && pathValue.substr(0, 1) !== path.sep && pathValue.substr(1, 2) !== ':\\') ? path.join(this.get('module root'), pathValue) : pathValue; return pathValue; }
...
if (typeof lessPaths === 'string') {
lessPaths = [lessPaths];
}
if (Array.isArray(lessPaths)) {
lessPaths.forEach(function (path) {
app.use(require('less-middleware')(keystone.expandPath(path), lessOptions
));
});
}
};
...
get = function (key, value) { if (arguments.length === 1) { return this._options[key]; } switch (key) { // throw on unsupported options case 'email rules': throw new Error('The option "' + key + '" is no longer supported. See https://github.com/keystonejs/keystone/wiki/0.3.x-to-0. 4.x-Changes'); // handle special settings case 'cloudinary config': var cloudinary = require('cloudinary'); if (typeof value === 'string') { var parts = url.parse(value, true); var auth = parts.auth ? parts.auth.split(':') : []; value = { cloud_name: parts.host, api_key: auth[0], api_secret: auth[1], private_cdn: parts.pathname != null, secure_distribution: parts.pathname && parts.pathname.substring(1), }; } cloudinary.config(value); value = cloudinary.config(); break; case 'auth': if (value === true && !this.get('session')) { this.set('session', true); } break; case 'nav': this.nav = this.initNav(value); break; case 'mongo': if (typeof value !== 'string') { if (Array.isArray(value) && (value.length === 2 || value.length === 3)) { console.log('\nWarning: using an array for the `mongo` option has been deprecated.\nPlease use a mongodb connection string, e.g. mongodb://localhost/db_name instead.\n\n' + 'Support for arrays as the `mongo` setting will be removed in a future version.'); value = (value.length === 2) ? 'mongodb://' + value[0] + '/' + value[1] : 'mongodb://' + value[0] + ':' + value[2] + '/' + value[1]; } else { console.error('\nInvalid Configuration:\nThe `mongo` option must be a mongodb connection string, e.g. mongodb://localhost/db_name\n'); process.exit(1); } } break; case 'module root': // if relative path is used, resolve it based on the caller's path if (!isAbsolutePath(value)) { var caller = callerId.getData(); value = path.resolve(path.dirname(caller.filePath), value); } break; case 'app': this.app = value; break; case 'mongoose': this.mongoose = value; break; case 'frame guard': var validFrameGuardOptions = ['deny', 'sameorigin']; if (value === true) { value = 'deny'; } if (typeof value === 'string') { value = value.toLowerCase(); if (validFrameGuardOptions.indexOf(value) < 0) { value = false; } } else if (typeof value !== 'boolean') { value = false; } break; } this._options[key] = value; return this; }
...
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT || '3001');
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development
') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
...
function getOrphanedLists() { if (!this.nav) { return []; } return _.filter(this.lists, function (list, key) { if (list.get('hidden')) return false; return (!this.nav.by.list[key]) ? list : false; }.bind(this)); }
n/a
getPath = function (key, defaultValue) { return this.expandPath(this.get(key) || defaultValue); }
...
exports.apply = function (callback) {
var Update = mongoose.model('App_Update');
var updateCount = 0;
var deferCount = 0;
var skipCount = 0;
var updatesPath = keystone.getPath('updates', 'updates');
// logError is used to log errors before the process exits since it is more synchronous than console.error. Using
// console.error gets into race condition issues with process.exit, which has higher priority.
var logError = function () {
for (var i = 0, len = arguments.length; i < len; ++i) {
process.stderr.write(arguments[i] + '\n');
}
...
import = function (dirname) { var initialPath = path.join(this.get('module root'), dirname); var doImport = function (fromPath) { var imported = {}; fs.readdirSync(fromPath).forEach(function (name) { var fsPath = path.join(fromPath, name); var info = fs.statSync(fsPath); // recur if (info.isDirectory()) { imported[name] = doImport(fsPath); } else { // only import files that we can `require` var ext = path.extname(name); var base = path.basename(name, ext); if (require.extensions[ext]) { imported[base] = require(fsPath); } } }); return imported; }; return doImport(initialPath); }
...
keystone.utils = utils;
/**
* returns all .js modules (recursively) in the path specified, relative
* to the module root (where the keystone project is being consumed from).
*
* ####Example:
* var models = keystone.import('models');
*/
Keystone.prototype.import = function (dirname) {
var initialPath = path.join(this.get('module root'), dirname);
var doImport = function (fromPath) {
...
function dispatchImporter(rel__dirname) { function importer (from) { debug('importing ', from); var imported = {}; var joinPath = function () { return '.' + path.sep + path.join.apply(path, arguments); }; var fsPath = joinPath(path.relative(process.cwd(), rel__dirname), from); fs.readdirSync(fsPath).forEach(function (name) { var info = fs.statSync(path.join(fsPath, name)); debug('recur'); if (info.isDirectory()) { imported[name] = importer(joinPath(from, name)); } else { // only import files that we can `require` var ext = path.extname(name); var base = path.basename(name, ext); if (require.extensions[ext]) { imported[base] = require(path.join(rel__dirname, from, name)); } else { debug('cannot require ', ext); } } }); return imported; } return importer; }
...
/**
* Returns a function that looks in a specified path relative to the current
* directory, and returns all .js modules in it (recursively).
*
* ####Example:
*
* var importRoutes = keystone.importer(__dirname);
*
* var routes = {
* site: importRoutes('./site'),
* api: importRoutes('./api')
* };
*
* @param {String} rel__dirname
...
function init(options) { this.options(options); return this; }
...
self.editor = editor;
editor.on('change', self.valueChanged);
editor.on('focus', self.focusChanged.bind(self, true));
editor.on('blur', self.focusChanged.bind(self, false));
};
this._currentValue = this.props.value;
_tinymce2.default.init(opts);
if ((0, _evalDependsOn2.default)(this.props.dependsOn, this.props.values)) {
this.setState({ wysiwygActive: true });
}
},
removeWysiwyg: function removeWysiwyg(state) {
removeTinyMCEInstance(_tinymce2.default.get(state.id));
this.setState({ wysiwygActive: false });
...
function initDatabaseConfig() { if (!this.get('mongo')) { var dbName = this.get('db name') || utils.slug(this.get('name')); var dbUrl = process.env.MONGO_URI || process.env.MONGODB_URI || process.env.MONGO_URL || process.env.MONGODB_URL || process.env.MONGOLAB_URI || process.env.MONGOLAB_URL || (process.env.OPENSHIFT_MONGODB_DB_URL || 'mongodb://localhost/') + dbName; this.set('mongo', dbUrl); } return this; }
...
keystone.app = express();
}
var app = keystone.app;
require('./initLetsEncrypt')(keystone, app);
require('./initSslRedirect')(keystone, app);
keystone.initDatabaseConfig();
keystone.initExpressSession(keystone.mongoose);
require('./initTrustProxy')(keystone, app);
require('./initViewEngine')(keystone, app);
require('./initViewLocals')(keystone, app);
require('./bindIPRestrictions')(keystone, app);
...
function initExpressApp(customApp) { if (this.app) return this; this.initDatabaseConfig(); this.initExpressSession(this.mongoose); if (customApp) { this.app = customApp; require('../../server/createApp')(this); } else { this.app = require('../../server/createApp')(this); } return this; }
...
process.exit();
} else {
console.log(e.stack || e);
process.exit(1);
}
});
this.initExpressApp();
var keystone = this;
var app = keystone.app;
this.openDatabaseConnection(function () {
fireEvent('onMount');
...
function initExpressSession(mongoose) { if (this.expressSession) return this; var sessionStorePromise; // Initialise and validate session options if (!this.get('cookie secret')) { console.error('\nKeystoneJS Configuration Error:\n\nPlease provide a `cookie secret` value for session encryption.\n'); process.exit(1); } var sessionOptions = this.get('session options'); if (typeof sessionOptions !== 'object') { sessionOptions = {}; } if (!sessionOptions.key) { sessionOptions.key = 'this.sid'; } if (!sessionOptions.resave) { sessionOptions.resave = false; } if (!sessionOptions.saveUninitialized) { sessionOptions.saveUninitialized = false; } if (!sessionOptions.secret) { sessionOptions.secret = this.get('cookie secret'); } sessionOptions.cookieParser = cookieParser(this.get('cookie secret')); var sessionStore = this.get('session store'); if (typeof sessionStore === 'function') { sessionOptions.store = sessionStore(session); } else if (sessionStore) { var sessionStoreOptions = this.get('session store options') || {}; // Perform any session store specific configuration or exit on an unsupported session store if (sessionStore === 'mongo') { sessionStore = 'connect-mongo'; } else if (sessionStore === 'redis') { sessionStore = 'connect-redis'; } switch (sessionStore) { case 'connect-mongo': debug('using connect-mongo session store'); if (process.version.substr(0, 4) === 'v0.1') { // try to require connect-mongo/es5; if this works, we can // assume a new version of connect-mongo, and the es5 // variant should be used try { require('connect-mongo/es5'); sessionStore = 'connect-mongo/es5'; } catch (e) { // if it throws, allow the error to be handled by the // normal try/catch process around initialisation } } _.defaults(sessionStoreOptions, { collection: 'app_sessions', mongooseConnection: mongoose.connection, }); break; case 'connect-mongostore': debug('using connect-mongostore session store'); _.defaults(sessionStoreOptions, { collection: 'app_sessions', }); if (!sessionStoreOptions.db) { console.error( '\nERROR: connect-mongostore requires `session store options` to be set.' + '\n' + '\nSee http://thisjs.com/docs/configuration#options-database for details.' + '\n'); process.exit(1); } break; case 'connect-redis': debug('using connect-redis session store'); break; default: console.error( '\nERROR: unsupported session store ' + sessionStore + '.' + '\n' + '\nSee http://thisjs.com/docs/configuration#options-database for details.' + '\n'); process.exit(1); break; } // Initialize the session store try { var SessionStore = require(sessionStore)(session); sessionStorePromise = new Promise( function (resolve, reject) { sessionOptions.store = new SessionStore(sessionStoreOptions, resolve); sessionOptions.store.on('connect', resolve); sessionOptions.store.on('connected', resolve); sessionOptions.store.on('disconnect', function () { console.error( '\nThere was an error connecting to the ' + sessionStore + ' session store.' + '\n'); process.exit(1); }); } ); } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { console.error( '\n' + e.toString() + '\nTo use ' + this.get('session store') + ' as a `session store` option, run:' + '\nnpm install ' + sessionStore + ' --save' + '\n'); process.exit(1); } else { throw e; } } } // expose initialised session and options this.set('session options', sessionOptions); this.expressSession = session(sessionOptions); this.sessionStorePromise = sessionStorePromise; return this; }
...
}
var app = keystone.app;
require('./initLetsEncrypt')(keystone, app);
require('./initSslRedirect')(keystone, app);
keystone.initDatabaseConfig();
keystone.initExpressSession(keystone.mongoose);
require('./initTrustProxy')(keystone, app);
require('./initViewEngine')(keystone, app);
require('./initViewLocals')(keystone, app);
require('./bindIPRestrictions')(keystone, app);
// Compress response bodies
...
function initNav(sections) { var keystone = this; var nav = { sections: [], by: { list: {}, section: {}, }, }; if (!sections) { sections = {}; nav.flat = true; _.forEach(this.lists, function (list) { if (list.get('hidden')) return; sections[list.path] = [list.path]; }); } _.forEach(sections, function (section, key) { if (typeof section === 'string') { section = [section]; } section = { lists: section, label: nav.flat ? keystone.list(section[0]).label : utils.keyToLabel(key), }; section.key = key; section.lists = _.map(section.lists, function (i) { if (typeof i === 'string') { var list = keystone.list(i); if (!list) { throw new Error('Invalid Keystone Option (nav): list ' + i + ' has not been defined.\n'); } if (list.get('hidden')) { throw new Error('Invalid Keystone Option (nav): list ' + i + ' is hidden.\n'); } nav.by.list[list.key] = section; return { key: list.key, label: list.label, path: list.path, }; } else if (_.isObject(i)) { if (!_.has(i, 'key')) { throw new Error('Invalid Keystone Option (nav): object ' + i + ' requires a "key" property.\n'); } i.label = i.label || utils.keyToLabel(key); i.path = i.path || utils.keyToPath(key); i.external = true; nav.by.list[i.key] = section; return i; } throw new Error('Invalid Keystone Option (nav): ' + i + ' is in an unrecognized format.\n'); }); if (section.lists.length) { nav.sections.push(section); nav.by.section[section.key] = section; } }); return nav; }
...
break;
case 'auth':
if (value === true && !this.get('session')) {
this.set('session', true);
}
break;
case 'nav':
this.nav = this.initNav(value);
break;
case 'mongo':
if (typeof value !== 'string') {
if (Array.isArray(value) && (value.length === 2 || value.length === 3)) {
console.log('\nWarning: using an array for the `mongo` option has been deprecated.\nPlease use a mongodb connection string
, e.g. mongodb://localhost/db_name instead.\n\n'
+ 'Support for arrays as the `mongo` setting will be removed in a future version.');
value = (value.length === 2) ? 'mongodb://' + value[0] + '/' + value[1] : 'mongodb://' + value
[0] + ':' + value[2] + '/' + value[1];
...
function list(key) { var result = this.lists[key] || this.lists[this.paths[key]]; if (!result) throw new ReferenceError('Unknown keystone list ' + JSON.stringify(key)); return result; }
...
async.waterfall([
// create items
function (next) {
async.eachSeries(lists, function (key, doneList) {
var list = keystone.list(key);
var relationshipPaths = _.filter(list.fields, { type: 'relationship' }).map(function (i) { return i.path; });
if (!list) {
if (options.strict) {
return doneList({
type: 'invalid list',
message: 'List key ' + key + ' is invalid.',
...
function openDatabaseConnection(callback) { var keystone = this; var mongoConnectionOpen = false; // support replica sets for mongoose if (keystone.get('mongo replica set')) { if (keystone.get('logger')) { console.log('\nWarning: using the `mongo replica set` option has been deprecated and will be removed in' + ' a future version.\nInstead set the `mongo` connection string with your host details, e.g.' + ' mongodb://username:password@host:port,host:port,host:port/database and set any replica set options' + ' in `mongo options`.\n\nRefer to https://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html' + ' for more details on the connection settings.'); } debug('setting up mongo replica set'); var replicaData = keystone.get('mongo replica set'); var replica = ''; var credentials = (replicaData.username && replicaData.password) ? replicaData.username + ':' + replicaData.password + '@' : ''; replicaData.db.servers.forEach(function (server) { replica += 'mongodb://' + credentials + server.host + ':' + server.port + '/' + replicaData.db.name + ','; }); var options = { auth: { authSource: replicaData.authSource }, replset: { rs_name: replicaData.db.replicaSetOptions.rs_name, readPreference: replicaData.db.replicaSetOptions.readPreference, }, }; debug('connecting to replica set'); keystone.mongoose.connect(replica, options); } else { debug('connecting to mongo'); keystone.initDatabaseConfig(); keystone.mongoose.connect(keystone.get('mongo'), keystone.get('mongo options')); } keystone.mongoose.connection.on('error', function (err) { // The DB connection has been established previously and this a ValidationError caused by restrictions Mongoose is enforcing on the field value // We can ignore these here; they'll also be picked up by the 'error' event listener on the model; see /lib/list/register.js if (mongoConnectionOpen && err && err.name === 'ValidationError') return; // Alternatively, the error is legitimate; output it console.error('------------------------------------------------'); console.error('Mongoose connection "error" event fired with:'); console.error(err); // There's been an error establishing the initial connection, ie. Keystone is attempting to start if (!mongoConnectionOpen) { throw new Error('KeystoneJS (' + keystone.get('name') + ') failed to start - Check that you are running `mongod` in a separate process.'); } // Otherwise rethrow the initial error throw err; }).once('open', function () { debug('mongo connection open'); mongoConnectionOpen = true; var connected = function () { if (keystone.get('auto update')) { debug('applying auto update'); keystone.applyUpdates(callback); } else { callback(); } }; if (keystone.sessionStorePromise) { keystone.sessionStorePromise.then(connected); } else { connected(); } }); return this; }
...
});
this.initExpressApp();
var keystone = this;
var app = keystone.app;
this.openDatabaseConnection(function () {
fireEvent('onMount');
var ssl = keystone.get('ssl');
var unixSocket = keystone.get('unix socket');
var startupMessages = ['KeystoneJS Started:'];
...
options = function (options) { if (!arguments.length) { return this._options; } if (typeof options === 'object') { var keys = Object.keys(options); var i = keys.length; var k; while (i--) { k = keys[i]; this.set(k, options[k]); } } return this._options; }
...
var initialFields;
// Inherit default options from parent list if it exists
if (options && options.inherits) {
if (options.inherits.options && options.inherits.options.inherits) {
throw new Error('Inherited Lists may not contain any inheritance');
}
defaultOptions = utils.options(defaultOptions, options.inherits.options);
if (options.inherits.options.track) {
options.track = false;
}
}
this.options = utils.options(defaultOptions, options);
...
function populateRelated(docs, relationships, callback) { if (Array.isArray(docs)) { async.each(docs, function (doc, done) { doc.populateRelated(relationships, done); }, callback); } else if (docs && docs.populateRelated) { docs.populateRelated(relationships, callback); } else { callback(); } return this; }
...
*
* @api public
*/
module.exports = function populateRelated (docs, relationships, callback) {
if (Array.isArray(docs)) {
async.each(docs, function (doc, done) {
doc.populateRelated(relationships, done);
}, callback);
} else if (docs && docs.populateRelated) {
docs.populateRelated(relationships, callback);
} else {
callback();
}
return this;
...
prefixModel = function (key) { var modelPrefix = this.get('model prefix'); if (modelPrefix) { key = modelPrefix + '_' + key; } return require('mongoose/lib/utils').toCollectionName(key); }
...
function List (key, options) {
if (!(this instanceof List)) return new List(key, options);
this.keystone = keystone;
var defaultOptions = {
schema: {
collection: keystone.prefixModel(key),
},
noedit: false,
nocreate: false,
nodelete: false,
autocreate: false,
sortable: false,
hidden: false,
...
function redirect() { if (arguments.length === 1 && utils.isObject(arguments[0])) { _.extend(this._redirects, arguments[0]); } else if (arguments.length === 2 && typeof arguments[0] === 'string' && typeof arguments[1] === 'string') { this._redirects[arguments[0]] = arguments[1]; } return this; }
...
module.exports = function bindErrorHandlers (keystone, app) {
if (Object.keys(keystone._redirects).length) {
app.use(function (req, res, next) {
if (keystone._redirects[req.path]) {
res.redirect(keystone._redirects[req.path]);
} else {
next();
}
});
}
};
...
routes = function () { throw new Error('keystone.routes(fn) has been removed, use keystone.set(\'routes\', fn)'); }
...
Keystone.prototype.redirect = require('./lib/core/redirect');
Keystone.prototype.start = require('./lib/core/start');
Keystone.prototype.wrapHTMLError = require('./lib/core/wrapHTMLError');
Keystone.prototype.createKeystoneHash = require('./lib/core/createKeystoneHash');
/* Deprecation / Change warnings for 0.4 */
Keystone.prototype.routes = function () {
throw new Error('keystone.routes(fn) has been removed, use keystone.set(\'
routes\', fn)');
};
/**
* The exports object is an instance of Keystone.
*/
var keystone = module.exports = new Keystone();
...
set = function (key, value) { if (arguments.length === 1) { return this._options[key]; } switch (key) { // throw on unsupported options case 'email rules': throw new Error('The option "' + key + '" is no longer supported. See https://github.com/keystonejs/keystone/wiki/0.3.x-to-0. 4.x-Changes'); // handle special settings case 'cloudinary config': var cloudinary = require('cloudinary'); if (typeof value === 'string') { var parts = url.parse(value, true); var auth = parts.auth ? parts.auth.split(':') : []; value = { cloud_name: parts.host, api_key: auth[0], api_secret: auth[1], private_cdn: parts.pathname != null, secure_distribution: parts.pathname && parts.pathname.substring(1), }; } cloudinary.config(value); value = cloudinary.config(); break; case 'auth': if (value === true && !this.get('session')) { this.set('session', true); } break; case 'nav': this.nav = this.initNav(value); break; case 'mongo': if (typeof value !== 'string') { if (Array.isArray(value) && (value.length === 2 || value.length === 3)) { console.log('\nWarning: using an array for the `mongo` option has been deprecated.\nPlease use a mongodb connection string, e.g. mongodb://localhost/db_name instead.\n\n' + 'Support for arrays as the `mongo` setting will be removed in a future version.'); value = (value.length === 2) ? 'mongodb://' + value[0] + '/' + value[1] : 'mongodb://' + value[0] + ':' + value[2] + '/' + value[1]; } else { console.error('\nInvalid Configuration:\nThe `mongo` option must be a mongodb connection string, e.g. mongodb://localhost/db_name\n'); process.exit(1); } } break; case 'module root': // if relative path is used, resolve it based on the caller's path if (!isAbsolutePath(value)) { var caller = callerId.getData(); value = path.resolve(path.dirname(caller.filePath), value); } break; case 'app': this.app = value; break; case 'mongoose': this.mongoose = value; break; case 'frame guard': var validFrameGuardOptions = ['deny', 'sameorigin']; if (value === true) { value = 'deny'; } if (typeof value === 'string') { value = value.toLowerCase(); if (validFrameGuardOptions.indexOf(value) < 0) { value = false; } } else if (typeof value !== 'boolean') { value = false; } break; } this._options[key] = value; return this; }
...
Alternatively, to include Keystone in an existing project or start from scratch (without Yeoman), specify `keystone: "^0.3.
9"` in the `dependencies` array of your `package.json` file, and run `npm install` from your terminal.
Then read through the [Documentation](http://keystonejs.com/docs) and the [Example Projects](http://keystonejs.com/examples) to
understand how to use it.
### Configuration
Config variables can be passed in an object to the `keystone.init` method, or can be set any time before `keystone.start` is called
using `keystone.set(key, value)`. This allows for a more flexible order of execution
(e.g. if you refer to Lists in your routes, you can set the routes after configuring your Lists, as in the example above).
See the [KeystoneJS configuration documentation](http://keystonejs.com/docs/configuration) for details and examples of the available
configuration options.
### Database field types
Keystone builds on the basic data types provided by mongo and allows you to easily add rich, functional fields to your application
's models.
...
function start(events) { if (typeof events === 'function') { events = { onStart: events }; } if (!events) events = {}; function fireEvent (name) { if (typeof events[name] === 'function') { events[name](); } } process.on('uncaughtException', function (e) { if (e.code === 'EADDRINUSE') { console.log(dashes + keystone.get('name') + ' failed to start: address already in use\n' + 'Please check you are not already running a server on the specified port.\n'); process.exit(); } else { console.log(e.stack || e); process.exit(1); } }); this.initExpressApp(); var keystone = this; var app = keystone.app; this.openDatabaseConnection(function () { fireEvent('onMount'); var ssl = keystone.get('ssl'); var unixSocket = keystone.get('unix socket'); var startupMessages = ['KeystoneJS Started:']; async.parallel([ // HTTP Server function (done) { if (ssl === 'only' || unixSocket) return done(); require('../../server/startHTTPServer')(keystone, app, function (err, msg) { fireEvent('onHttpServerCreated'); startupMessages.push(msg); done(err); }); }, // HTTPS Server function (done) { if (!ssl || unixSocket) return done(); require('../../server/startSecureServer')(keystone, app, function () { fireEvent('onHttpsServerCreated'); }, function (err, msg) { startupMessages.push(msg); done(err); }); }, // Unix Socket function (done) { if (!unixSocket) return done(); require('../../server/startSocketServer')(keystone, app, function (err, msg) { fireEvent('onSocketServerCreated'); startupMessages.push(msg); done(err); }); }, ], function serversStarted (err, messages) { if (keystone.get('logger')) { console.log(dashes + startupMessages.join('\n') + dashes); } fireEvent('onStart'); }); }); return this; }
n/a
function wrapHTMLError(title, err) { return '<html><head><meta charset=\'utf-8\'><title>Error</title>' + '<link rel=\'stylesheet\' href=\'/' + this.get('admin path') + '/styles/error.css\'>' + '</head><body><div class=\'error\'><h1 class=\'error-title\'>' + title + '</h1>' + '<div class="error-message">' + (err || '') + '</div></div></body></html>'; }
...
module.exports = function bindErrorHandlers (keystone, app) {
// Handle 404 (no route matched) errors
var default404Handler = function (req, res) {
if (req.headers.accept === 'application/json') {
return res.status(404).json({ error: 'not found' });
}
res.status(404).send(keystone.wrapHTMLError('Sorry, no page could be found at
this address (404)'));
};
app.use(function (req, res, next) {
var err404 = keystone.get('404');
if (err404) {
try {
if (typeof err404 === 'function') {
...
err = function (type, msg) { if (keystone.get('logger')) { var dashes = '\n------------------------------------------------\n'; console.log(dashes + 'KeystoneJS: ' + type + ':\n\n' + msg + dashes); } }
n/a
function List(key, options) { if (!(this instanceof List)) return new List(key, options); this.keystone = keystone; var defaultOptions = { schema: { collection: keystone.prefixModel(key), }, noedit: false, nocreate: false, nodelete: false, autocreate: false, sortable: false, hidden: false, track: false, inherits: false, perPage: 100, searchFields: '__name__', searchUsesTextIndex: false, defaultSort: '__default__', defaultColumns: '__name__', }; // initialFields values are initialised on demand by the getter var initialFields; // Inherit default options from parent list if it exists if (options && options.inherits) { if (options.inherits.options && options.inherits.options.inherits) { throw new Error('Inherited Lists may not contain any inheritance'); } defaultOptions = utils.options(defaultOptions, options.inherits.options); if (options.inherits.options.track) { options.track = false; } } this.options = utils.options(defaultOptions, options); // init properties this.key = key; this.path = this.get('path') || utils.keyToPath(key, true); this.schema = new keystone.mongoose.Schema({}, this.options.schema); this.schemaFields = []; this.uiElements = []; this.underscoreMethods = {}; this.fields = {}; this.fieldsArray = []; this.fieldTypes = {}; this.relationshipFields = []; this.relationships = {}; this.mappings = { name: null, createdBy: null, createdOn: null, modifiedBy: null, modifiedOn: null, }; var self = this; // init mappings _.forEach(this.options.map, function (val, key) { self.map(key, val); }); // define property getters Object.defineProperty(this, 'label', { get: function () { return this.get('label') || this.set('label', utils.plural(utils.keyToLabel(key))); } }); Object.defineProperty(this, 'singular', { get: function () { return this.get('singular') || this.set('singular', utils.singular(this.label)); } }); Object.defineProperty(this, 'plural', { get: function () { return this.get('plural') || this.set('plural', utils.plural(this.singular)); } }); Object.defineProperty(this, 'namePath', { get: function () { return this.mappings.name || '_id'; } }); Object.defineProperty(this, 'nameField', { get: function () { return this.fields[this.mappings.name]; } }); Object.defineProperty(this, 'nameIsVirtual', { get: function () { return this.model.schema.virtuals[this.mappings.name] ? true : false; } }); Object.defineProperty(this, 'nameFieldIsFormHeader', { get: function () { return (this.fields[this.mappings.name] && this.fields[this.mappings.name].type === 'text') ? !this.fields[this.mappings.name ].noedit : false; } }); Object.defineProperty(this, 'nameIsInitial', { get: function () { return (this.fields[this.mappings.name] && this.fields[this.mappings.name].options.initial === undefined); } }); Object.defineProperty(this, 'initialFields', { get: function () { return initialFields || (initialFields = _.filter(this.fields, function (i) { return i.initial; })); } }); if (this.get('inherits')) { var parentFields = this.get('inherits').schemaFields; this.add.apply(this, parentFields); } }
n/a
function add() { var add = function (obj, prefix) { prefix = prefix || ''; var keys = Object.keys(obj); for (var i = 0; i < keys.length; ++i) { var key = keys[i]; if (!obj[key]) { throw new Error( 'Invalid value for schema path `' + prefix + key + '` in `' + this.key + '`.\n' + 'Did you misspell the field type?\n' ); } if (utils.isObject(obj[key]) && (!obj[key].constructor || obj[key].constructor.name === 'Object') && (!obj[key].type || obj[key ].type.type)) { if (Object.keys(obj[key]).length) { // nested object, e.g. { last: { name: String }} // matches logic in mongoose/Schema:add this.schema.nested[prefix + key] = true; add(obj[key], prefix + key + '.'); } else { addField(prefix + key, obj[key]); // mixed type field } } else { addField(prefix + key, obj[key]); } } }.bind(this); var addField = function (path, options) { if (this.isReserved(path)) { throw new Error('Path ' + path + ' on list ' + this.key + ' is a reserved path'); } this.uiElements.push({ type: 'field', field: this.field(path, options), }); }.bind(this); var args = Array.prototype.slice.call(arguments); var self = this; _.forEach(args, function (def) { self.schemaFields.push(def); if (typeof def === 'string') { if (def === '>>>') { self.uiElements.push({ type: 'indent', }); } else if (def === '<<<') { self.uiElements.push({ type: 'outdent', }); } else { self.uiElements.push({ type: 'heading', heading: def, options: {}, }); } } else { if (def.heading && typeof def.heading === 'string') { self.uiElements.push({ type: 'heading', heading: def.heading, options: def, }); } else { add(def); } } }); return this; }
...
* @api public
*/
Page.prototype.add = function (fields) {
// TODO: nested paths
if (!utils.isObject(fields)) {
throw new Error('keystone.content.Page.add() Error: fields must be an object.
x27;);
}
var self = this;
_.forEach(fields, function (options, path) {
if (typeof options === 'function') {
...
function addFiltersToQuery(filters) { var fields = Object.keys(this.fields); var query = {}; fields.forEach(function (path) { var field = this.fields[path]; if (!field.addFilterToQuery || !filters[field.path]) return; combineQueries(query, field.addFilterToQuery(filters[field.path])); }, this); debug('Adding filters to query, returned:', query); return query; }
n/a
function addSearchToQuery(searchString) { searchString = String(searchString || '').trim(); var query = {}; var searchFilters = []; if (!searchString) return query; if (this.options.searchUsesTextIndex) { debug('Using text search index for value: "' + searchString + '"'); searchFilters.push({ $text: { $search: searchString, }, }); if (this.autokey) { var strictAutokeyFilter = {}; var autokeyRegExp = new RegExp('^' + utils.escapeRegExp(searchString)); strictAutokeyFilter[this.autokey.path] = autokeyRegExp; searchFilters.push(strictAutokeyFilter); } } else { debug('Using regular expression search for value: "' + searchString + '"'); var searchRegExp = new RegExp(utils.escapeRegExp(searchString), 'i'); searchFilters = this.searchFields.map(function (i) { if (i.field && i.field.type === 'name') { return getNameFilter(i.field, searchString); } else { return getStringFilter(i.path, searchRegExp); } }, this); if (this.autokey) { var autokeyFilter = {}; autokeyFilter[this.autokey.path] = searchRegExp; searchFilters.push(autokeyFilter); } } if (utils.isValidObjectId(searchString)) { searchFilters.push({ _id: searchString, }); } if (searchFilters.length > 1) { query.$or = searchFilters; } else if (searchFilters.length) { assign(query, searchFilters[0]); } debug('Built search query for value: "' + searchString + '"', query); return query; }
n/a
function apiForGet(options) { var idParam = options.id || 'id'; var List = this; return function (req, res) { var id = req.params[idParam]; var query = List.model.findById(id); if (typeof options.query === 'function') { var result = options.query(query, req); if (result === false) return; } else if (typeof options.query === 'object') { query.where(options.query); } query.exec(function (err, item) { if (err) return res.status(500).json({ err: 'database error', detail: err }); if (!item) return res.status(404).json({ err: 'not found', id: id }); if (options.transform) { item = options.transform(item, req, res); if (item === false) return; } return res.json({ data: item, }); }); }; }
...
/*
# List.apiForGet(options)
Returns JSON API middleware for a GET /:id endpoint
Supports the following options:
### `id` (string)
...
function automap(field) { if ((field.path in this.mappings) && !this.mappings[field.path]) { this.map(field.path, field.path); } return this; }
n/a
function buildSearchTextIndex() { var idxDef = {}; for (var i = 0; i < this.searchFields.length; i++) { var sf = this.searchFields[i]; if (!sf.path || !sf.field) continue; // TODO: Allow fields to define their own `getTextIndex` method, so that // each type can define the right options for their schema. This is unlikely // to behave as expected for fields that aren't simple strings or names // until that has been done. Should error if the field type doesn't support // text indexing, as the list has been misconfigured. // Does the field have a single path or does it use nested values (like 'name') if (sf.field.paths) { var nFields = sf.field.paths; var nKeys = Object.keys(nFields); for (var n = 0; n < nKeys.length; n++) { idxDef[nFields[nKeys[n]]] = 'text'; } } else if (sf.field.path) { idxDef[sf.field.path] = 'text'; } } // debug('text index for \'' + this.key + '\':', idxDef); return Object.keys(idxDef).length > 0 ? idxDef : false; }
...
* ensureIndexes() call or the connection error listener.
* Or at least that's what was happening for me (mongoose v3.8.40, mongodb v1.4.38)..
*/
function ensureTextIndex (callback) {
var list = this;
var collection = list.model.collection;
var textIndex = list.buildSearchTextIndex();
var fieldsHash = Math.abs(hashString(Object.keys(textIndex).sort().join(';')));
var indexNamePrefix = 'keystone_searchFields_textIndex_';
var newIndexName = indexNamePrefix + fieldsHash;
// We use this later to create a new index if needed
var createNewIndex = function () {
collection.createIndex(textIndex, { name: newIndexName }, function (result) {
...
function declaresTextIndex() { var indexes = this.schema.indexes(); for (var i = 0; i < indexes.length; i++) { var fields = indexes[i][0]; var fieldNames = Object.keys(fields); for (var h = 0; h < fieldNames.length; h++) { var val = fields[fieldNames[h]]; if (typeof val === 'string' && val.toLowerCase() === 'text') return true; } } return false; }
...
/* Apply list inheritance */
if (this.get('inherits')) {
this.model = this.get('inherits').model.discriminator(this.key, this.schema);
} else {
this.model = keystone.mongoose.model(this.key, this.schema);
}
/* Setup search text index */
if (this.options.searchUsesTextIndex && !this.declaresTextIndex()) {
// If the list is configured to use a text index for search and the list
// doesn't explicitly define one, create (or update) one of our own
this.ensureTextIndex(function () {
debug('this.ensureTextIndex() done for \'' + list.key + '\'');
});
}
/* Register the list and its field types on the Keystone instance */
...
function ensureTextIndex(callback) { var list = this; var collection = list.model.collection; var textIndex = list.buildSearchTextIndex(); var fieldsHash = Math.abs(hashString(Object.keys(textIndex).sort().join(';'))); var indexNamePrefix = 'keystone_searchFields_textIndex_'; var newIndexName = indexNamePrefix + fieldsHash; // We use this later to create a new index if needed var createNewIndex = function () { collection.createIndex(textIndex, { name: newIndexName }, function (result) { debug('collection.createIndex() result for \'' + list.key + '\:', result); return callback(); }); }; collection.getIndexes(function (err, indexes) { if (err) { if (err.code === 26) { // if the database doesn't exist, we'll get error code 26 "no database" here // that's fine, we just default the indexes object so the new text index // gets created when the database is connected. indexes = {}; } else { // otherwise, we've had an unexpected error, so we throw it throw err; } } var indexNames = Object.keys(indexes); // Search though the for (var i = 0; i < indexNames.length; i++) { var existingIndexName = indexNames[i]; var isText = false; // Check we're dealing with a text index for (var h = 0; h < indexes[existingIndexName].length; h++) { var column = indexes[existingIndexName][h]; if (column[1] === 'text') isText = isText || true; } // Skip non-text indexes if (!isText) continue; // Already exists with correct def if (existingIndexName === newIndexName) { debug('Existing text index \'' + existingIndexName + '\' already matches the searchFields for \'' + list.key + '\''); return; } // Exists but hash (def) doesn't match // Check for 'searchFields_text_index' for backwards compatibility if (existingIndexName.slice(0, indexNamePrefix.length) === indexNamePrefix || existingIndexName === 'searchFields_text_index') { debug('Existing text index \'' + existingIndexName + '\' doesn\'t match the searchFields for \'' + list.key + '\' and will be recreated as\'' + newIndexName + '\''); collection.dropIndex(existingIndexName, function (result) { debug('collection.dropIndex() result for \'' + list.key + '\:', result); createNewIndex(); }); return; } // It's a text index but not one of ours; nothing we can do console.error('' + 'list.ensureTextIndex() failed to update the existing text index \'' + existingIndexName + '\' for the \'' + list.key + '\' list.\n' + 'The existing index wasn\'t automatically created by ensureTextIndex() so will not be replaced.\n' + 'This may lead to unexpected behaviour when performing text searches on the this list.' ); return; } // No text indexes found at all; create ours now debug('No existing text index found in \'' + list.key + '\'; Creating ours now'); createNewIndex(); }); }
...
createNewIndex();
});
return;
}
// It's a text index but not one of ours; nothing we can do
console.error(''
+ 'list.ensureTextIndex() failed to update the existing text index \'
x27; + existingIndexName + '\' for the \'' + list.key + '\' list.\n'
+ 'The existing index wasn\'t automatically created by ensureTextIndex() so will not be replaced.\n'
+ 'This may lead to unexpected behaviour when performing text searches on the this list.'
);
return;
}
// No text indexes found at all; create ours now
...
function expandColumns(cols) { if (typeof cols === 'string') { cols = cols.split(','); } if (!Array.isArray(cols)) { throw new Error('List.expandColumns: cols must be an array.'); } var list = this; var expanded = []; var nameCol = false; var getCol = function (def) { if (def.path === '__name__') { def.path = list.namePath; } var field = list.fields[def.path]; var col = null; if (field) { col = { field: field, path: field.path, type: field.type, label: def.label || field.label, }; if (col.type === 'relationship') { col.refList = col.field.refList; if (col.refList) { col.refPath = def.subpath || col.refList.namePath; col.subField = col.refList.fields[col.refPath]; col.populate = { path: col.field.path, subpath: col.refPath }; } if (!def.label && def.subpath) { col.label = field.label + ': ' + (col.subField ? col.subField.label : utils.keyToLabel(def.subpath)); } } } else if (list.model.schema.paths[def.path] || list.model.schema.virtuals[def.path]) { // column refers to a path in the schema // TODO: this needs to handle sophisticated types, including arrays, nested Schemas, and mixed types col = { path: def.path, label: def.label || utils.keyToLabel(def.path), }; } if (col) { col.width = def.width; if (col.path === list.namePath) { col.isName = true; nameCol = col; } if (field && field.col) { _.extend(col, field.col); } } return col; }; for (var i = 0; i < cols.length; i++) { var def = {}; if (typeof cols[i] === 'string') { var parts = cols[i].trim().split('|'); def.width = parts[1] || false; parts = parts[0].split(':'); def.path = parts[0]; def.subpath = parts[1]; } if (!utils.isObject(def) || !def.path) { throw new Error('List.expandColumns: column definition must contain a path.'); } var col = getCol(def); if (col) { expanded.push(col); } } if (!nameCol) { nameCol = getCol({ path: list.namePath }); if (nameCol) { expanded.unshift(nameCol); } } return expanded; }
...
},
});
// Default Column Fields
Object.defineProperty(List.prototype, 'defaultColumns', {
get: function () {
if (!this._defaultColumns) {
this._defaultColumns = this.expandColumns(this.get('defaultColumns'));
}
return this._defaultColumns;
}, set: function (value) {
this.set('defaultColumns', value);
delete this._defaultColumns;
},
});
...
function expandPaths(paths) { return listToArray(paths).map(function (path) { if (path === '__name__') { path = this.mappings.name; } return { path: path, field: this.fields[path], }; }, this); }
...
// TODO: Protect dynamic properties from being accessed until the List
// has been registered (otherwise, incomplete schema could be cached)
// Search Fields
Object.defineProperty(List.prototype, 'searchFields', {
get: function () {
if (!this._searchFields) {
this._searchFields = this.expandPaths(this.get('searchFields'));
}
return this._searchFields;
}, set: function (value) {
this.set('searchFields', value);
delete this._searchFields;
},
});
...
function expandSort(input) { var fields = this.fields; var sort = { rawInput: input || this.defaultSort, isDefaultSort: false, }; sort.input = sort.rawInput; if (sort.input === '__default__') { sort.isDefaultSort = true; sort.input = this.sortable ? 'sortOrder' : this.namePath; } sort.paths = listToArray(sort.input).map(function (path) { var invert = false; if (path.charAt(0) === '-') { invert = true; path = path.substr(1); } var field = fields[path]; if (!field) { return; } return { field: field, invert: invert, path: field.path, }; }).filter(truthy); sort.string = sort.paths.map(function (i) { if (i.field.getSortString) { return i.field.getSortString(i); } return i.invert ? '-' + i.path : i.path; }).join(' '); return sort; }
n/a
function field(path, options) { var Field = this.keystone.Field; if (arguments.length === 1) { return this.fields[path]; } if (typeof options === 'function') { options = { type: options }; } if (this.get('noedit')) { options.noedit = true; } if (!options.note && this.get('notes')) { options.note = this.get('notes')[path]; } if (typeof options.type !== 'function') { throw new Error('Fields must be specified with a type function'); } if (!(options.type.prototype instanceof Field)) { // Convert native field types to their default Keystone counterpart if (options.type === String) { options.type = Field.Types.Text; } else if (options.type === Number) { options.type = Field.Types.Number; } else if (options.type === Boolean) { options.type = Field.Types.Boolean; } else if (options.type === Date) { options.type = Field.Types.Datetime; } else { throw new Error('Unrecognised field constructor: ' + options.type); } } // Note the presence of this field type for client-side script optimisation this.fieldTypes[options.type.name] = options.type.properName; // Wysiwyg HTML fields are handled as a special case so we can include TinyMCE as required if (options.type.name === 'html' && options.wysiwyg) { this.fieldTypes.wysiwyg = true; } var field = new options.type(this, path, options); this.fields[path] = field; this.fieldsArray.push(field); if (field.type === 'relationship') { this.relationshipFields.push(field); } return field; }
...
var addField = function (path, options) {
if (this.isReserved(path)) {
throw new Error('Path ' + path + ' on list ' + this.key + ' is a reserved path');
}
this.uiElements.push({
type: 'field',
field: this.field(path, options),
});
}.bind(this);
var args = Array.prototype.slice.call(arguments);
var self = this;
_.forEach(args, function (def) {
...
function set(key, value) { if (arguments.length === 1) { return this.options[key]; } this.options[key] = value; return value; }
...
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT || '3001');
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development
') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
...
function getAdminURL(item) { return '/' + this.keystone.get('admin path') + '/' + this.path + (item ? '/' + item.id : ''); }
...
/**
* Gets the Admin URL to view the list (or an item if provided)
*
* Example:
* var listURL = list.getAdminURL()
* var itemURL = list.getAdminURL(item)
*
* @param {Object} item
*/
function getAdminURL (item) {
return '/' + this.keystone.get('admin path') + '/' + this.path + (item ? '/' + item.id :
x27;');
}
...
function getCSVData(item, options) { if (!options) { options = {}; } options.fields; if (options.fields === undefined) { options.fields = Object.keys(this.options.fields); } var data = { id: String(item.id), }; if (this.autokey) { data[this.autokey.path] = item.get(this.autokey.path); } if (options.fields) { if (typeof options.fields === 'string') { options.fields = listToArray(options.fields); } if (!Array.isArray(options.fields)) { throw new Error('List.getCSV: options.fields must be undefined, a string, or an array.'); } options.fields.forEach(function (path) { var field = this.fields[path]; if (!field) { // if the path isn't actually a field, just add the value from // that path in the mongoose document. data[path] = item.get(path); return; } if (field.type !== 'relationship' || !options.expandRelationshipFields) { // use the transformFieldValue function to get the data data[path] = transformFieldValue(field, item, options); return; } // relationship values should be expanded into separate name and // id pairs using the field's getExpandedData method. var expanded = field.getExpandedData(item); if (field.many) { // for many-type relationships, ensure the value is an array, // and turn it into a list of 'name (id)' values data[path] = (Array.isArray(expanded) ? expanded : []).map(function (i) { return i.name ? i.name + ' (' + i.id + ')' : i.id; }).join(', '); } else if (typeof expanded === 'object') { // for single-type relationships, add two columns to the data data[path] = expanded.name; data[path + 'Id'] = expanded.id; } }, this); } if (typeof item.getCSVData === 'function') { var ext = item.getCSVData(data, options); if (typeof ext === 'object') { _.forOwn(ext, function (value, key) { if (value === undefined) { delete data[key]; } else { data[key] = value; } }); } } // Copy each value into the return structure, flattening arrays into lists and // flattening objects into a column per property (one level only) var rtn = {}; _.forOwn(data, function (value, prop) { if (Array.isArray(value)) { // Array values are serialised to JSON, this should be an edge-case catch // for data coming raw from the item; array-type fields will already have // been stringified by the field.format method. rtn[prop] = JSON.stringify(value); } else if (typeof value === 'object') { // For object values, we loop through each key and add it to its own column // in the csv. Complex values are serialised to JSON. _.forOwn(value, function (v, i) { var suffix = i.substr(0, 1).toUpperCase() + i.substr(1); rtn[prop + suffix] = (typeof v === 'object') ? JSON.stringify(v) : v; }); } else { rtn[prop] = value; } }); return rtn; }
...
// for single-type relationships, add two columns to the data
data[path] = expanded.name;
data[path + 'Id'] = expanded.id;
}
}, this);
}
if (typeof item.getCSVData === 'function') {
var ext = item.getCSVData(data, options);
if (typeof ext === 'object') {
_.forOwn(ext, function (value, key) {
if (value === undefined) {
delete data[key];
} else {
data[key] = value;
}
...
function getData(item, fields, expandRelationshipFields) { var data = { id: item.id, name: this.getDocumentName(item), }; if (this.autokey) { data[this.autokey.path] = item.get(this.autokey.path); } if (this.options.sortable) { data.sortOrder = item.sortOrder; } if (fields === undefined) { fields = Object.keys(this.fields); } if (fields) { if (!Array.isArray(fields)) { throw new Error('List.getData: fields must be undefined, a string, or an array.'); } data.fields = {}; fields.forEach(function (path) { var field = this.fields[path]; if (field) { if (field.type === 'relationship' && expandRelationshipFields) { data.fields[path] = field.getExpandedData(item); } else { data.fields[path] = field.getData(item); } } else { data.fields[path] = item.get(path); } }, this); } return data; }
...
process.exit(1);
}
}
break;
case 'module root':
// if relative path is used, resolve it based on the caller's path
if (!isAbsolutePath(value)) {
var caller = callerId.getData();
value = path.resolve(path.dirname(caller.filePath), value);
}
break;
case 'app':
this.app = value;
break;
case 'mongoose':
...
function getDocumentName(doc, escape) { // console.log('getting document name for ' + doc.id, 'nameField: ' + this.nameField, 'namePath: ' + this.namePath); // console.log('raw name value: ', doc.get(this.namePath)); // if (this.nameField) console.log('formatted name value: ', this.nameField.format(doc)); var name = String(this.nameField ? this.nameField.format(doc) : doc.get(this.namePath)); return (escape) ? utils.encodeHTMLEntities(name) : name; }
...
field.updateItem(doc, data, doneField);
} else {
doneField();
}
}, function (err) {
if (!err) {
if (options.verbose) {
var documentName = list.getDocumentName(doc);
writeLog('Creating item [' + itemsProcessed + ' of ' + totalItems + '] - ' + documentName);
}
doc.save(function (err) {
if (err) {
err.model = key;
err.data = data;
...
function getOptions() { var ops = { autocreate: this.options.autocreate, autokey: this.autokey, defaultColumns: this.options.defaultColumns, defaultSort: this.options.defaultSort, fields: {}, hidden: this.options.hidden, initialFields: _.map(this.initialFields, 'path'), key: this.key, label: this.label, nameField: this.nameField ? this.nameField.getOptions() : null, nameFieldIsFormHeader: this.nameFieldIsFormHeader, nameIsInitial: this.nameIsInitial, nameIsVirtual: this.nameIsVirtual, namePath: this.namePath, nocreate: this.options.nocreate, nodelete: this.options.nodelete, noedit: this.options.noedit, path: this.path, perPage: this.options.perPage, plural: this.plural, searchFields: this.options.searchFields, singular: this.singular, sortable: this.options.sortable, sortContext: this.options.sortContext, track: this.options.track, tracking: this.tracking, relationships: this.relationships, uiElements: [], }; _.forEach(this.uiElements, function (el) { switch (el.type) { // TODO: handle indentation case 'field': // add the field options by path ops.fields[el.field.path] = el.field.getOptions(); // don't output hidden fields if (el.field.hidden) { return; } // add the field to the elements array ops.uiElements.push({ type: 'field', field: el.field.path, }); break; case 'heading': ops.uiElements.push({ type: 'heading', content: el.heading, options: el.options, }); break; } }); return ops; }
...
var forEach = require('lodash/forEach');
function createKeystoneHash () {
var hash = crypto.createHash('md5');
hash.update(this.version);
forEach(this.lists, function (list, key) {
hash.update(JSON.stringify(list.getOptions()));
});
return hash.digest('hex').slice(0, 6);
}
module.exports = createKeystoneHash;
...
function getPages(options, maxPages) { var surround = Math.floor(maxPages / 2); var firstPage = maxPages ? Math.max(1, options.currentPage - surround) : 1; var padRight = Math.max(((options.currentPage - surround) - 1) * -1, 0); var lastPage = maxPages ? Math.min(options.totalPages, options.currentPage + surround + padRight) : options.totalPages; var padLeft = Math.max(((options.currentPage + surround) - lastPage), 0); options.pages = []; firstPage = Math.max(Math.min(firstPage, firstPage - padLeft), 1); for (var i = firstPage; i <= lastPage; i++) { options.pages.push(i); } if (firstPage !== 1) { options.pages.shift(); options.pages.unshift('...'); } if (lastPage !== Number(options.totalPages)) { options.pages.pop(); options.pages.push('...'); } }
...
totalPages: totalPages,
pages: [],
previous: (currentPage > 1) ? (currentPage - 1) : false,
next: (currentPage < totalPages) ? (currentPage + 1) : false,
first: skip + 1,
last: skip + results.length,
};
list.getPages(rtn, maxPages);
callback(err, rtn);
});
});
};
if (callback) {
...
function getSearchFilters(search, add) { var filters = {}; var list = this; search = String(search || '').trim(); if (search.length) { // Use the text index the behaviour is enabled by the list schema // Usually, when searchUsesTextIndex is true, list.register() will maintain an index using ensureTextIndex() // However, it's possible for searchUsesTextIndex to be true while there is an existing text index on the collection that *isn 't* described in the list schema // If this occurs, an error will be reported when list.register() is called and the existing index will not be replaced, meaning it will be used here if (this.options.searchUsesTextIndex) { filters.$text = { $search: search }; } else { var searchFilter; var searchParts = search.split(' '); var searchRx = new RegExp(utils.escapeRegExp(search), 'i'); var splitSearchRx = new RegExp((searchParts.length > 1) ? _.map(searchParts, utils.escapeRegExp).join('|') : search, 'i'); var searchFields = this.get('searchFields'); var searchFilters = []; var searchIdField = utils.isValidObjectId(search); if (typeof searchFields === 'string') { searchFields = searchFields.split(','); } searchFields.forEach(function (path) { path = path.trim(); if (path === '__name__') { path = list.mappings.name; } var field = list.fields[path]; if (field && field.type === 'name') { var first = {}; first[field.paths.first] = splitSearchRx; var last = {}; last[field.paths.last] = splitSearchRx; searchFilter = {}; searchFilter.$or = [first, last]; searchFilters.push(searchFilter); } else { searchFilter = {}; searchFilter[path] = searchRx; searchFilters.push(searchFilter); } }); if (list.autokey) { searchFilter = {}; searchFilter[list.autokey.path] = searchRx; searchFilters.push(searchFilter); } if (searchIdField) { searchFilter = {}; searchFilter._id = search; searchFilters.push(searchFilter); } if (searchFilters.length > 1) { filters.$or = searchFilters; } else if (searchFilters.length) { filters = searchFilters[0]; } } } if (add) { _.forEach(add, function (filter) { var cond; var path = filter.key; var value = filter.value; switch (filter.field.type) { case 'boolean': if (!value || value === 'false') { filters[path] = { $ne: true }; } else { filters[path] = true; } break; case 'localfile': case 'cloudinaryimage': case 'cloudinaryimages': case 's3file': case 'name': case 'password': // TODO break; case 'location': _.forEach(['street1', 'suburb', 'state', 'postcode', 'country'], function (pathKey, i) { var value = filter.value[i]; if (value) { filters[filter.field.paths[pathKey]] = new RegExp(utils.escapeRegExp(value), 'i'); } }); break; case 'relationship': if (value) { if (filter.field.many) { filters[path] = (filter.inverse) ? { $nin: [value] } : { $in: [value] }; } else { filters[path] = (filter.inverse) ? { $ne: value } : value; } } else { if (filter.field.many) { filters[path] = (filter.inverse) ? { $not: { $size: 0 } } : { $size: 0 }; } else { filters[path] = (filter.inverse) ? { $ne: null } : null; } } break; case 'select': if (filter.value) { filters[path] = (filter.inverse) ? { $ne: value } : value; } else { filters[path] = (filter.inverse) ? { $nin: ['', null] } : { $in: ['', null] }; } break; case 'number': case 'money': if (filter.operator === 'bt') { value = [ utils.number(value[0]), utils.number(value[1]), ]; if (!isNaN(value[0]) && !isNaN(value[1])) { filters[path] = { $gte: value[0], $lte: value[1], }; } else { filters[path] = null; } } else { value = utils.number(value); if (!isNaN(value)) { if (fil ...
...
* Also accepts a filters object from `processFilters()`, any of which may
* override the search string.
*
* NOTE: This function is deprecated in favor of List.prototype.addSearchToQuery
* and will be removed in a later version.
*
* Example:
* list.getSearchFilters('jed') // returns { name: /jed/i }
*
* @param {String} query
* @param {Object} additional filters
*/
function getSearchFilters (search, add) {
var filters = {};
var list = this;
...
function getUniqueValue(path, generator, limit, callback) { var model = this.model; var count = 0; var value; if (typeof limit === 'function') { callback = limit; limit = 10; } if (Array.isArray(generator)) { var fn = generator[0]; var args = generator.slice(1); generator = function () { return fn.apply(this, args); }; } var check = function () { if (count++ > 10) { return callback(undefined, undefined); } value = generator(); model.count().where(path, value).exec(function (err, matches) { if (err) return callback(err); if (matches) return check(); callback(undefined, value); }); }; check(); }
n/a
function isReserved(path) { return reservedPaths.indexOf(path) >= 0; }
...
} else {
addField(prefix + key, obj[key]);
}
}
}.bind(this);
var addField = function (path, options) {
if (this.isReserved(path)) {
throw new Error('Path ' + path + ' on list ' + this.key + ' is a reserved path');
}
this.uiElements.push({
type: 'field',
field: this.field(path, options),
});
}.bind(this);
...
function map(field, path) { if (path) { this.mappings[field] = path; } return this.mappings[field]; }
...
modifiedBy: null,
modifiedOn: null,
};
var self = this;
// init mappings
_.forEach(this.options.map, function (val, key) { self.map(key, val); });
// define property getters
Object.defineProperty(this, 'label', { get: function () {
return this.get('label') || this.set('label', utils.plural(utils.keyToLabel(key)));
} });
Object.defineProperty(this, 'singular', { get: function () {
return this.get('singular') || this.set('singular', utils.singular(this.label));
...
function paginate(options, callback) { var list = this; var model = this.model; options = options || {}; var query = model.find(options.filters, options.optionalExpression); var countQuery = model.find(options.filters); query._original_exec = query.exec; query._original_sort = query.sort; query._original_select = query.select; var currentPage = Number(options.page) || 1; var resultsPerPage = Number(options.perPage) || 50; var maxPages = Number(options.maxPages) || 10; var skip = (currentPage - 1) * resultsPerPage; list.pagination = { maxPages: maxPages }; // as of mongoose 3.7.x, we need to defer sorting and field selection // until after the count has been executed query.select = function () { options.select = arguments[0]; return query; }; query.sort = function () { options.sort = arguments[0]; return query; }; query.exec = function (callback) { countQuery.count(function (err, count) { if (err) callback(err); query.find().limit(resultsPerPage).skip(skip); // apply the select and sort options before calling exec if (options.select) { query._original_select(options.select); } if (options.sort) { query._original_sort(options.sort); } query._original_exec(function (err, results) { if (err) return callback(err); var totalPages = Math.ceil(count / resultsPerPage); var rtn = { total: count, results: results, currentPage: currentPage, totalPages: totalPages, pages: [], previous: (currentPage > 1) ? (currentPage - 1) : false, next: (currentPage < totalPages) ? (currentPage + 1) : false, first: skip + 1, last: skip + results.length, }; list.getPages(rtn, maxPages); callback(err, rtn); }); }); }; if (callback) { return query(callback); } else { return query; } }
...
/**
* Gets a special Query object that will paginate documents in the list
*
* Example:
* list.paginate({
* page: 1,
* perPage: 100,
* maxPages: 10
* }).exec(function(err, results) {
* // do something
* });
*
...
function processFilters(q) { var list = this; var filters = {}; queryfilterlib.QueryFilters.create(q).getFilters().forEach(function (filter) { filter.path = filter.key; // alias for b/c filter.field = list.fields[filter.key]; filters[filter.path] = filter; }); return filters; }
...
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
module.exports = function (req, res) {
var baby = require('babyparse');
var keystone = req.keystone;
var filters = req.list.processFilters(req.query.q);
var queryFilters = req.list.getSearchFilters(req.query.search, filters);
var relFields = [];
_.forEach(req.list.fields, function (field) {
if (field.type === 'relationship') {
relFields.push(field.path);
}
...
function register() {
var keystone = this.keystone;
var list = this;
/* Handle deprecated options */
if (this.schema.methods.toCSV) {
console.warn(this.key + ' Warning: List.schema.methods.toCSV support has been removed from KeystoneJS.\nPlease use getCSVData
instead (see the 0.3 -> 4.0 Upgrade Guide)\n');
}
/* Apply Plugins */
if (this.get('sortable')) {
schemaPlugins.sortable.apply(this);
}
if (this.get('autokey')) {
schemaPlugins.autokey.apply(this);
}
if (this.get('track')) {
schemaPlugins.track.apply(this);
}
if (this.get('history')) {
schemaPlugins.history.apply(this);
}
this.schema.virtual('list').get(function () {
return list;
});
/* Add common methods to the schema */
if (Object.keys(this.relationships).length) {
this.schema.methods.getRelated = schemaPlugins.methods.getRelated;
this.schema.methods.populateRelated = schemaPlugins.methods.populateRelated;
if (!this.schema.options.toObject) this.schema.options.toObject = {};
this.schema.options.toObject.transform = schemaPlugins.options.transform;
}
this.schema.virtual('_').get(function () {
if (!this.__methods) {
this.__methods = utils.bindMethods(list.underscoreMethods, this);
}
return this.__methods;
});
this.schema.method('getUpdateHandler', function (req, res, ops) {
return new UpdateHandler(list, this, req, res, ops);
});
/* Apply list inheritance */
if (this.get('inherits')) {
this.model = this.get('inherits').model.discriminator(this.key, this.schema);
} else {
this.model = keystone.mongoose.model(this.key, this.schema);
}
/* Setup search text index */
if (this.options.searchUsesTextIndex && !this.declaresTextIndex()) {
// If the list is configured to use a text index for search and the list
// doesn't explicitly define one, create (or update) one of our own
this.ensureTextIndex(function () {
debug('this.ensureTextIndex() done for \'' + list.key + '\'');
});
}
/* Register the list and its field types on the Keystone instance */
keystone.lists[this.key] = this;
keystone.paths[this.path] = this.key;
assign(keystone.fieldTypes, this.fieldTypes);
/* Add listeners for model events */
// see http://mongoosejs.com/docs/api.html#model_Model
this.model.on('index', function (err) {
if (err) console.error('Mongoose model \'index\' event fired on \'' + list.key + '\' with error:\n', err.message, err.stack);
});
this.model.on('index-single-start', function (index) {
debug('Mongoose model \'index-single-start\' event fired on \'' + list.key + '\' for index:\n', index);
});
this.model.on('index-single-done', function (err, index) {
if (err) console.error('Mongoose model \'index-single-done\' event fired on \'' + list.key + '\' for index:\n', index, '\nWith
error:\n', err.message, err.stack);
else debug('Mongoose model \'index-single-done\' event fired on \'' + list.key + '\' for index:\n', index);
});
this.model.on('error', function (err) {
if (err) console.error('Mongoose model \'error\' event fired on \'' + list.key + '\' with error:\n', err.message, err.stack);
});
return this;
}
...
/**
* Registers the page with Keystone.
*
* ####Example:
*
* var homePage = new keystone.content.Page('home');
* // ...
* homePage.register();
*
* // later...
* var homePage = keystone.content.page('home');
*
* @api public
*/
...
function relationship(def) { var keystone = this.keystone; if (arguments.length > 1) { for (var i = 0; i < arguments.length; i++) { this.relationship(arguments[i]); } return this; } if (typeof def === 'string') { def = { ref: def }; } if (!def.ref) { throw new Error('List Relationships must be specified with an object containing ref (' + this.key + ')'); } if (!def.refPath) { def.refPath = utils.downcase(this.key); } if (!def.path) { def.path = utils.keyToProperty(def.ref, true); } Object.defineProperty(def, 'refList', { get: function () { return keystone.list(def.ref); }, }); Object.defineProperty(def, 'isValid', { get: function () { return keystone.list(def.ref) ? true : false; }, }); this.relationships[def.path] = def; return this; }
...
/**
* Registers relationships to this list defined on others
*/
function relationship (def) {
var keystone = this.keystone;
if (arguments.length > 1) {
for (var i = 0; i < arguments.length; i++) {
this.relationship(arguments[i]);
}
return this;
}
if (typeof def === 'string') {
def = { ref: def };
}
if (!def.ref) {
...
function selectColumns(q, cols) { // Populate relationship columns var select = []; var populate = {}; var path; cols.forEach(function (col) { select.push(col.path); if (col.populate) { if (!populate[col.populate.path]) { populate[col.populate.path] = []; } populate[col.populate.path].push(col.populate.subpath); } }); q.select(select.join(' ')); for (path in populate) { if (populate.hasOwnProperty(path)) { q.populate(path, populate[path].join(' ')); } } }
n/a
function set(key, value) { if (arguments.length === 1) { return this.options[key]; } this.options[key] = value; return value; }
...
Alternatively, to include Keystone in an existing project or start from scratch (without Yeoman), specify `keystone: "^0.3.
9"` in the `dependencies` array of your `package.json` file, and run `npm install` from your terminal.
Then read through the [Documentation](http://keystonejs.com/docs) and the [Example Projects](http://keystonejs.com/examples) to
understand how to use it.
### Configuration
Config variables can be passed in an object to the `keystone.init` method, or can be set any time before `keystone.start` is called
using `keystone.set(key, value)`. This allows for a more flexible order of execution
(e.g. if you refer to Lists in your routes, you can set the routes after configuring your Lists, as in the example above).
See the [KeystoneJS configuration documentation](http://keystonejs.com/docs/configuration) for details and examples of the available
configuration options.
### Database field types
Keystone builds on the basic data types provided by mongo and allows you to easily add rich, functional fields to your application
's models.
...
function underscoreMethod(path, fn) { var target = this.underscoreMethods; path = path.split('.'); var last = path.pop(); path.forEach(function (part) { if (!target[part]) target[part] = {}; target = target[part]; }); target[last] = fn; return this; }
...
azure.createBlobService().blobService.deleteBlob(this.get(paths.container), this.get(paths.filename), function () {});
} catch (e) {} // eslint-disable-line no-empty
reset(this);
},
};
_.forEach(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));
};
...
function updateItem(item, data, options, callback) {
/* Process arguments and options */
if (typeof options === 'function') {
callback = options;
options = {};
}
if (!options) {
options = {};
}
// update fields with noedit: true set if fields have been explicitly
// provided, or if the ignoreNoEdit option is true
var ignoreNoEdit = !!(options.fields || options.ignoreNoEdit);
// fields defaults to all the fields in the list
var fields = options.fields || this.fieldsArray;
// fields can be a list or array of field paths or Field instances
fields = listToArray(fields).map(function (field) {
// TODO: Check that field is an instance of Field
return (typeof field === 'string') ? this.fields[field] : field;
}, this);
// check for invalid fields
if (fields.indexOf(undefined) >= 0) {
return callback({
error: 'invalid configuration',
detail: 'Invalid path specified in fields to update [' + options.fields + '] for list ' + this.key,
});
}
// Strip out noedit fields
if (!ignoreNoEdit) {
fields = fields.filter(function (i) {
return !i.noedit;
});
}
// you can optionally require fields that aren't required in the schema
// note that if fields are required in the schema, they will always be checked
//
// this option supports the backwards compatible { path: true } format, or a
// list or array of field paths to validate
var requiredFields = options.required;
var requiredFieldPaths = {};
if (typeof requiredFields === 'string') {
requiredFields = listToArray(requiredFields);
}
if (Array.isArray(requiredFields)) {
requiredFields.forEach(function (path) {
requiredFieldPaths[path] = true;
});
} else if (typeof requiredFields === 'object') {
requiredFieldPaths = requiredFields;
}
/* Field Validation */
// TODO: If a field is required but not specified in the provided fields array
// we should explicitly include it in the set of fields to validate
var validationErrors = {};
function doFieldValidation (field, done) {
// Note; we don't pass back validation errors to the callback, because we don't
// want to break the async loop before all the fields have been validated.
field.validateInput(data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'invalid', detail);
done();
} else {
if ((field.required || requiredFieldPaths[field.path])
&& (!field.dependsOn || evalDependsOn(field.dependsOn, data)))
{
field.validateRequiredInput(item, data, function (valid, detail) {
if (!valid) {
addValidationError(options, validationErrors, field, 'required', detail);
}
done();
});
} else {
done();
}
}
});
}
/* Field Updates */
var updateErrors = {};
function doFieldUpdate (field, done) {
var callback = function (err) {
// Note; we don't pass back errors to the callback, because we don't want
// to break the async loop before all the fields have been updated.
if (err) {
addFieldUpdateError(updateErrors, field, err);
}
done();
};
// all fields have (item, data) as the first two arguments
var updateArgs = [item, data];
// some fields support an optional third argument: files
if (field.updateItem.length > 3) {
updateArgs.push(options.files);
}
// callback is always the last argument
updateArgs.push(callback);
// call field.updateItem with the arguments
field.updateItem.apply(field, updateArgs);
}
/* Track plugin support */
// If the track plugin is enabled for the list, it looks for ._req_user to
// detect the user that performed the updated. Default it to the user
// specified in the options.
if (options.user) {
item._req_user = options.user;
}
/* Flow control */
async.series([
/* Process validation */
function (doneValidation) {
async.each(fields, doFieldValidation, function () {
if (Object.keys(validationErrors).length) {
return doneValidation({
error: 'validation errors',
detail: validationErrors,
});
}
doneValidation();
});
},
/* Apply updates to ...
...
title: errorMessage,
list: errors.length ? errors : undefined,
});
}
}
}
this.list.updateItem(this.item, data, options, function (err) {
if (err) {
if (options.logErrors) {
console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err);
}
if (options.flashErrors) {
flashErrors(err);
}
...
function localfile(list, path, options) {
throw new Error('The LocalFile field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('move');
this._underscoreMethods = ['format', 'uploadFile'];
this._fixedSize = 'full';
this.autoCleanup = options.autoCleanup || false;
if (options.overwrite !== false) {
options.overwrite = true;
}
localfile.super_.call(this, list, path, options);
// validate destination dir
if (!options.dest) {
throw new Error('Invalid Configuration\n\n'
+ 'localfile fields (' + list.key + '.' + path + ') require the "dest" option to be set.');
}
// Allow hook into before and after
if (options.pre && options.pre.move) {
this.pre('move', options.pre.move);
}
if (options.post && options.post.move) {
this.post('move', options.post.move);
}
*/
}
n/a
function localfiles(list, path, options) {
throw new Error('The LocalFiles field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('move');
this._underscoreMethods = ['format', 'uploadFiles'];
this._fixedSize = 'full';
// 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'
+ 'localfiles fields (' + list.key + '.' + path + ') do not currently support being used as initial fields.\n');
}
if (options.overwrite !== false) {
options.overwrite = true;
}
localfiles.super_.call(this, list, path, options);
// validate destination dir
if (!options.dest) {
throw new Error('Invalid Configuration\n\n'
+ 'localfiles fields (' + list.key + '.' + path + ') require the "dest" option to be set.');
}
// Allow hook into before and after
if (options.pre && options.pre.move) {
this.pre('move', options.pre.move);
}
if (options.post && options.post.move) {
this.post('move', options.post.move);
}
*/
}
n/a
function location(list, path, options) { this._underscoreMethods = ['format', 'googleLookup', 'kmFrom', 'milesFrom']; this._fixedSize = 'full'; this._properties = ['enableMapsAPI']; this.enableMapsAPI = (options.enableImprove === true || (options.enableImprove !== false && keystone.get('google server api key '))) ? true : false; // Throw on invalid options in 4.0 (remove for 5.0) if ('geocodeGoogle' in options) { throw new Error('The geocodeGoogle option for Location fields has been renamed to enableImprove'); } if (!options.defaults) { options.defaults = {}; } if (options.required) { if (Array.isArray(options.required)) { // required can be specified as an array of paths this.requiredPaths = options.required; } else if (typeof options.required === 'string') { // or it can be specified as a comma-delimited list this.requiredPaths = options.required.replace(/,/g, ' ').split(/\s+/); } // options.required should always be simplified to a boolean options.required = true; } // default this.requiredPaths if (!this.requiredPaths) { this.requiredPaths = ['street1', 'suburb']; } location.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function markdown(list, path, options) { this._defaultSize = 'full'; this.toolbarOptions = options.toolbarOptions || {}; this.markedOptions = options.markedOptions || {}; this.height = options.height || 90; this.wysiwyg = ('wysiwyg' in options) ? options.wysiwyg : true; this._properties = ['wysiwyg', 'height', 'toolbarOptions']; markdown.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function money(list, path, options) { this.currency = options.currency; this._nativeType = Number; this._underscoreMethods = ['format']; this._properties = ['currency']; this._fixedSize = 'small'; this._formatString = (options.format === false) ? false : (options.format || '$0,0.00'); if (this._formatString && typeof this._formatString !== 'string') { throw new Error('FieldType.Money: options.format must be a string.'); } money.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function name(list, path, options) { this._fixedSize = 'full'; options.default = { first: '', last: '' }; name.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function numberarray(list, path, options) { this._nativeType = [Number]; this._underscoreMethods = ['format']; this._formatString = (options.format === false) ? false : (options.format || '0,0[.][000000000000]'); this._defaultSize = 'small'; if (this._formatString && typeof this._formatString !== 'string') { throw new Error('FieldType.NumberArray: options.format must be a string.'); } this.separator = options.separator || ' | '; numberarray.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function number(list, path, options) { this._nativeType = Number; this._fixedSize = 'small'; this._underscoreMethods = ['format']; this.formatString = (options.format === false) ? false : (options.format || '0,0[.][000000000000]'); if (this.formatString && typeof this.formatString !== 'string') { throw new Error('FieldType.Number: options.format must be a string.'); } number.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function password(list, path, options) { this.options = options; this._nativeType = String; this._underscoreMethods = ['format', 'compare']; this._fixedSize = 'full'; // You can't sort on password fields options.nosort = true; this.workFactor = options.workFactor || 10; password.super_.call(this, list, path, options); for (var key in this.options.complexity) { if ({}.hasOwnProperty.call(this.options.complexity, key)) { if (key in regexChunk !== key in this.options.complexity) { throw new Error('FieldType.Password: options.complexity - option does not exist.'); } if (typeof this.options.complexity[key] !== 'boolean') { throw new Error('FieldType.Password: options.complexity - Value must be boolean.'); } } } if (this.options.max <= this.options.min) { throw new Error('FieldType.Password: options - min must be set at a lower value than max.'); } }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function relationship(list, path, options) { this.many = (options.many) ? true : false; this.filters = options.filters; this.createInline = (options.createInline) ? true : false; this._defaultSize = 'full'; this._nativeType = keystone.mongoose.Schema.Types.ObjectId; this._underscoreMethods = ['format', 'getExpandedData']; this._properties = ['isValid', 'many', 'filters', 'createInline']; relationship.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function s3file(list, path, options) {
throw new Error('The S3File field type has been removed. Please use File instead.'
+ '\n\nSee https://github.com/keystonejs/keystone/wiki/File-Fields-Upgrade-Guide\n');
/*
grappling.mixin(this).allowHooks('pre:upload');
this._underscoreMethods = ['format', 'uploadFile'];
this._fixedSize = 'full';
// 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'
+ 'S3File fields (' + list.key + '.' + path + ') do not currently support being used as initial fields.\n');
}
s3file.super_.call(this, list, path, options);
// validate s3 config (has to happen after super_.call)
if (!this.s3config) {
throw new Error('Invalid Configuration\n\n'
+ 'S3File fields (' + list.key + '.' + path + ') require the "s3 config" option to be set.\n\n'
+ 'See http://keystonejs.com/docs/configuration/#services-amazons3 for more information.\n');
}
// Could be more pre- hooks, just upload for now
if (options.pre && options.pre.upload) {
this.pre('upload', options.pre.upload);
}
*/
}
n/a
function select(list, path, options) { this.ui = options.ui || 'select'; this.numeric = options.numeric ? true : false; this._nativeType = (options.numeric) ? Number : String; this._underscoreMethods = ['format', 'pluck']; this._properties = ['ops', 'numeric']; if (typeof options.options === 'string') { options.options = options.options.split(','); } if (!Array.isArray(options.options)) { throw new Error('Select fields require an options array.'); } this.ops = options.options.map(function (i) { var op = typeof i === 'string' ? { value: i.trim(), label: utils.keyToLabel(i) } : i; if (!_.isObject(op)) { op = { label: '' + i, value: '' + i }; } if (options.numeric && !_.isNumber(op.value)) { op.value = Number(op.value); } return op; }); // undefined options.emptyOption defaults to true if (options.emptyOption === undefined) { options.emptyOption = true; } // ensure this.emptyOption is a boolean this.emptyOption = !!options.emptyOption; // cached maps for options, labels and values this.map = utils.optionsMap(this.ops); this.labels = utils.optionsMap(this.ops, 'label'); this.values = _.map(this.ops, 'value'); select.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function Storage(options) { // we're going to mutate options so get a clean copy of it options = assign({}, options); var AdapterType = options.adapter; delete options.adapter; if (typeof AdapterType !== 'function') { throw new Error('Invalid Storage Adapter\n' + 'The storage adapter specified is not a function. Did you ' + 'require the right package?\n'); } debug('Initialising Storage with adapter ' + AdapterType.name); // ensure the adapter compatibilityLevel of the adapter matches if (AdapterType.compatibilityLevel !== ADAPTER_COMPATIBILITY_LEVEL) { throw new Error('Incompatible Storage Adapter\n' + 'The storage adapter specified (' + AdapterType.name + ') ' + 'does not match the compatibility level required for this ' + 'version of Keystone.\n'); } // assign ensures the default schema constant isn't exposed as a property var schemaFields = assign({}, SCHEMA_FIELD_DEFAULTS, AdapterType.SCHEMA_FIELD_DEFAULTS, options.schema); delete options.schema; // Copy requested fields into local schema. var schema = this.schema = {}; for (var path in schemaFields) { if (!schemaFields[path]) continue; var type = AdapterType.SCHEMA_TYPES[path] || SCHEMA_TYPES[path]; if (!type) throw Error('Unknown type for requested schema field ' + path); schema[path] = type; } // ensure Storage schema features are supported by the Adapter if (schema.url && typeof AdapterType.prototype.getFileURL !== 'function') { throw Error('URL schema field is not supported by the ' + AdapterType.name + ' adapter'); } // create the adapter this.adapter = new AdapterType(options, schema); }
n/a
contentHashFilename = function (file, i, callback) { var hash = crypto.createHash('sha1'); var calledCallback = false; fs.createReadStream(file.path) .on('error', function (err) { // I'm worried that on('error') might fire after on('readable') // happens. if (calledCallback) return; calledCallback = true; callback(err); }) .pipe(hash); hash.on('readable', function () { var data = hash.read(); if (data == null) return; if (calledCallback) return; calledCallback = true; // Data is a node Buffer. callback(null, filenameFromBuffer(data, file.extension)); }); }
n/a
originalFilename = function (file) { return file.originalname; }
n/a
randomFilename = function (file, i, callback) { crypto.randomBytes(16, function (err, data) { if (err) return callback(err); return callback(null, filenameFromBuffer(data, file.extension)); }); }
n/a
function FSAdapter(options, schema) { if (!schema.filename) throw Error('Cannot use FSAdapter without storing filename'); this.options = assign({}, DEFAULT_OPTIONS, options.fs); debug('Initialising FS Adapter with options', this.options); this.options.generateFilename = ensureCallback(this.options.generateFilename); ensurePath(this.options.path); }
n/a
fileExists = function (filename, callback) { var path = this.pathForFile(filename); debug('Checking for file at path %s', filename); // Returns (err, bool) to the callback based on whether or not the file // already exists. Used if whenExists: 'error' or 'retry' in the options fs.stat(path, function (err, stats) { if (err && err.code === 'ENOENT') { // File does not exist callback(null, false); } else if (err) { // Other error getting file info callback(err); } else if (stats.isFile()) { // File does exist callback(null, true); } else { // Object at path is not a file callback(Error('Invalid save destination - dest is not a file')); } }); }
n/a
getFileURL = function (file) { var publicPath = this.options.publicPath; if (!publicPath) return null; // No URL. return url.resolve(publicPath, file.filename); }
n/a
getFilename = function (file, callback) { var self = this; switch (self.options.whenExists) { case 'overwrite': self.options.generateFilename(file, 0, callback); break; case 'error': self.options.generateFilename(file, 0, function (err, filename) { if (err) return callback(err); self.fileExists(filename, function (err, result) { if (err) return callback(err); if (result === true) return callback(Error('File already exists')); callback(null, filename); }); }); break; case 'retry': self.retryFilename(0, file, callback); break; } }
...
return _react2.default.createElement(
'div',
null,
this.hasImage() ? _react2.default.createElement(
_FileChangeMessage2.default,
null,
this.getFilename()
) : null,
showChangeMessage && this.renderChangeMessage()
);
},
renderChangeMessage: function renderChangeMessage() {
if (this.state.userSelectedFile) {
return _react2.default.createElement(
...
pathForFile = function (filename) { return path.resolve(this.options.path, sanitize(filename)); }
n/a
removeFile = function (file, callback) { debug('Removing file', file); fs.unlink(this.pathForFile(file.filename), function (err) { if (err && err.code === 'ENOENT') { // The file doesn't exist. console.warn('Attempted to remove a non-existant file'); return callback(); } callback(err); }); }
...
};
/**
* Deletes the stored file and resets the field value
*/
// TODO: Should we accept a callback here? Seems like a good idea.
file.prototype.remove = function (item) {
this.storage.removeFile(item.get(this.path));
this.reset();
};
/**
* Formats the field value
*/
file.prototype.format = function (item) {
...
retryFilename = function (attempt, file, callback) { var self = this; if (attempt > self.options.retryAttempts) { return callback(Error('Unique filename could not be generated; Maximum attempts exceeded')); } self.options.generateFilename(file, attempt, function (err, filename) { if (err) return callback(err); self.fileExists(filename, function (err, exists) { if (err) return callback(err); if (exists) return self.retryFilename(attempt + 1, file, callback); callback(null, filename); }); }); }
n/a
uploadFile = function (file, callback) { debug('Uploading file', file); var options = this.options; this.getFilename(file, function (err, filename) { if (err) return callback(err); filename = sanitize(filename); debug('Uploading file with filename: %s', filename); var uploadPath = path.resolve(options.path, filename); fs.move(file.path, uploadPath, function (err) { if (err) return callback(err); // TODO: Chmod the file. var data = { filename: filename, size: file.size, mimetype: file.mimetype, path: options.path, originalname: file.originalname, }; debug('Uploaded file, returning data', data); callback(null, data); }); }); }
...
if (/^(delete|reset)$/.test(action)) {
field.apply(item, action);
}
}
if (req.files && req.files[paths.upload] && req.files[paths.upload].size) {
return field.uploadFile(item, req.files[paths.upload], true, callback);
}
return callback();
};
};
...
removeFile = function (value, callback) { this.adapter.removeFile(value, callback); }
...
};
/**
* Deletes the stored file and resets the field value
*/
// TODO: Should we accept a callback here? Seems like a good idea.
file.prototype.remove = function (item) {
this.storage.removeFile(item.get(this.path));
this.reset();
};
/**
* Formats the field value
*/
file.prototype.format = function (item) {
...
uploadFile = function (file, callback) { var self = this; // Ensure a file object has been provided if (!file) return callback(Error('Cannot upload file - No file object provided')); // The path to the file is required if (!file.path) return callback(Error('Cannot upload file - No source path')); normalizeFile(file, this.schema, function (err, file) { if (err) return callback(err); self.adapter.uploadFile(file, function (err, result) { if (err) return callback(err); if (self.schema.url && self.adapter.getFileURL) { result.url = self.adapter.getFileURL(result); } callback(null, result); }); }); }
...
if (/^(delete|reset)$/.test(action)) {
field.apply(item, action);
}
}
if (req.files && req.files[paths.upload] && req.files[paths.upload].size) {
return field.uploadFile(item, req.files[paths.upload], true, callback);
}
return callback();
};
};
...
function textarray(list, path, options) { this._nativeType = [String]; this._underscoreMethods = ['format']; this.separator = options.separator || ' | '; textarray.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function text(list, path, options) { this.options = options; this._nativeType = String; this._properties = ['monospace']; this._underscoreMethods = ['crop']; text.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function textarea(list, path, options) { this._nativeType = String; this._underscoreMethods = ['format', 'crop']; this.height = options.height || 90; this.multiline = true; this._properties = ['height', 'multiline']; textarea.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function url(list, path, options) { this._nativeType = String; this._underscoreMethods = ['format']; url.super_.call(this, list, path, options); }
n/a
function Field(list, path, options) { // Set field properties and options this.list = list; this._path = new Path(path); this.path = path; this.type = this.constructor.name; this.options = _.defaults({}, options, this.defaults); this.label = options.label || utils.keyToLabel(this.path); this.typeDescription = options.typeDescription || this.typeDescription || this.type; this.list.automap(this); // Warn on required fields that aren't initial if (this.options.required && this.options.initial === undefined && this.options.default === undefined && !this.options.value && !this.list.get('nocreate') && this.path !== this.list.mappings.name ) { console.error('\nError: Invalid Configuration\n\n' + 'Field (' + list.key + '.' + path + ') is required but not initial, and has no default or generated value.\n' + 'Please provide a default, remove the required setting, or set initial: false to override this error.\n'); process.exit(1); } // if dependsOn and required, set required to a function for validation if (this.options.dependsOn && this.options.required === true) { var opts = this.options; this.options.required = function () { // `this` refers to the validating document debug('validate dependsOn required', evalDependsOn(opts.dependsOn, this.toObject())); return evalDependsOn(opts.dependsOn, this.toObject()); }; } // Add the field to the schema this.addToSchema(this.list.schema); // Add pre-save handler to the list if this field watches others if (this.options.watch) { this.list.schema.pre('save', this.getPreSaveWatcher()); } // Convert notes from markdown to html var note = null; Object.defineProperty(this, 'note', { get: function () { return (note === null) ? (note = (this.options.note) ? marked(this.options.note) : '') : note; }, }); }
n/a
function View(req, res) { if (!req || req.constructor.name !== 'IncomingMessage') { throw new Error('Keystone.View Error: Express request object is required.'); } if (!res || res.constructor.name !== 'ServerResponse') { throw new Error('Keystone.View Error: Express response object is required.'); } this.req = req; this.res = res; this.initQueue = []; // executed first in series this.actionQueue = []; // executed second in parallel, if optional conditions are met this.queryQueue = []; // executed third in parallel this.renderQueue = []; // executed fourth in parallel }
n/a
on = function (on) {
var req = this.req;
var callback = arguments[1];
if (typeof on === 'function') {
/* If the first argument is a function that returns truthy then add the second
* argument to the action queue
*
* Example:
*
* view.on(function() {
* var thing = true;
* return thing;
* },
* function(next) {
* console.log('thing is true!');
* next();
* }
* );
*/
if (on()) {
this.actionQueue.push(callback);
}
} else if (utils.isObject(on)) {
/* Do certain actions depending on information in the response object.
*
* Example:
*
* view.on({ 'user.name.first': 'Admin' }, function(next) {
* console.log('Hello Admin!');
* next();
* });
*/
var check = function (value, path) {
var ctx = req;
var parts = path.split('.');
for (var i = 0; i < parts.length - 1; i++) {
if (!ctx[parts[i]]) {
return false;
}
ctx = ctx[parts[i]];
}
path = _.last(parts);
return (value === true && path in ctx) ? true : (ctx[path] === value);
};
if (_.every(on, check)) {
this.actionQueue.push(callback);
}
} else if (on === 'get' || on === 'post' || on === 'put' || on === 'delete') {
/* Handle HTTP verbs
*
* Example:
* view.on('get', function(next) {
* console.log('GOT!');
* next();
* });
*/
if (req.method !== on.toUpperCase()) {
return this;
}
if (arguments.length === 3) {
/* on a POST and PUT requests search the req.body for a matching value
* on every other request search the query.
*
* Example:
* view.on('post', { action: 'theAction' }, function(next) {
* // respond to the action
* next();
* });
*
* Example:
* view.on('get', { page: 2 }, function(next) {
* // do something specifically on ?page=2
* next();
* });
*/
callback = arguments[2];
var values = {};
if (utils.isString(arguments[1])) {
values[arguments[1]] = true;
} else {
values = arguments[1];
}
var ctx = (on === 'post' || on === 'put') ? req.body : req.query;
if (!_.every(values || {}, function (value, path) {
return (value === true && path in ctx) ? true : (ctx[path] === value);
})) {
return this;
}
}
this.actionQueue.push(callback);
} else if (on === 'init') {
/* Init events are always fired in series, before any other actions
*
* Example:
* view.on('init', function (next) {
* // do something before any actions or queries have run
* });
*/
this.initQueue.push(callback);
} else if (on === 'render') {
/* Render events are always fired last in parallel, after any other actions
*
* Example:
* view.on('render', function (next) {
* // do something after init, action and query middleware has run
* });
*/
this.renderQueue.push(callback);
}
// TODO: Should throw if we didn't recognise the first argument!
return this;
}
...
// Initialize the session store
try {
var SessionStore = require(sessionStore)(session);
sessionStorePromise = new Promise(
function (resolve, reject) {
sessionOptions.store = new SessionStore(sessionStoreOptions, resolve);
sessionOptions.store.on('connect', resolve);
sessionOptions.store.on('connected', resolve);
sessionOptions.store.on('disconnect', function () {
console.error(
'\nThere was an error connecting to the ' + sessionStore + ' session store.'
+ '\n');
process.exit(1);
});
...
query = function (key, query, options) {
var locals = this.res.locals;
var parts = key.split('.');
var chain = new QueryCallbacks(options);
key = parts.pop();
for (var i = 0; i < parts.length; i++) {
if (!locals[parts[i]]) {
locals[parts[i]] = {};
}
locals = locals[parts[i]];
}
this.queryQueue.push(function (next) {
query.exec(function (err, results) {
locals[key] = results;
var callbacks = chain.callbacks;
if (err) {
if ('err' in callbacks) {
/* Will pass errors into the err callback
*
* Example:
* view.query('books', keystone.list('Book'))
* .err(function (err, next) {
* console.log('ERROR: ', err);
* next();
* });
*/
return callbacks.err(err, next);
}
} else {
if ((!results || (utils.isArray(results) && !results.length)) && 'none' in callbacks) {
/* If there are no results view.query().none will be called
*
* Example:
* view.query('books', keystone.list('Book').model.find())
* .none(function (next) {
* console.log('no results');
* next();
* });
*/
return callbacks.none(next);
} else if ('then' in callbacks) {
if (utils.isFunction(callbacks.then)) {
return callbacks.then(err, results, next);
} else {
return keystone.populateRelated(results, callbacks.then, next);
}
}
}
return next(err);
});
});
return chain;
}
...
module.exports = function apiForGet (options) {
var idParam = options.id || 'id';
var List = this;
return function (req, res) {
var id = req.params[idParam];
var query = List.model.findById(id);
if (typeof options.query === 'function') {
var result = options.query(query, req);
if (result === false) return;
} else if (typeof options.query === 'object') {
query.where(options.query);
}
query.exec(function (err, item) {
if (err) return res.status(500).json({ err: 'database error', detail: err });
if (!item) return res.status(404).json({ err: 'not found', id: id });
...
render = function (renderFn, locals, callback) { var req = this.req; var res = this.res; if (typeof renderFn === 'string') { var viewPath = renderFn; renderFn = function () { if (typeof locals === 'function') { locals = locals(); } this.res.render(viewPath, locals, callback); }.bind(this); } if (typeof renderFn !== 'function') { throw new Error('Keystone.View.render() renderFn must be a templatePath (string) or a function.'); } // Add actions, queries & renderQueue to the end of the initQueue this.initQueue.push.apply(this.initQueue, this.actionQueue); this.initQueue.push.apply(this.initQueue, this.queryQueue); var preRenderQueue = []; // Add Keystone's global pre('render') queue keystone.getMiddleware('pre:render').forEach(function (fn) { preRenderQueue.push(function (next) { fn(req, res, next); }); }); this.initQueue.push(preRenderQueue); this.initQueue.push(this.renderQueue); async.eachSeries(this.initQueue, function (i, next) { if (Array.isArray(i)) { // process nested arrays in parallel async.parallel(i, next); } else if (typeof i === 'function') { // process single methods in series i(next); } else { throw new Error('Keystone.View.render() events must be functions.'); } }, function (err) { renderFn(err, req, res); }); }
...
try {
if (typeof err404 === 'function') {
return err404(req, res, next);
} else if (typeof err404 === 'string') {
if (req.headers.accept === 'application/json') {
return res.status(404).json({ error: 'not found' });
}
return res.status(404).render(err404);
} else {
if (keystone.get('logger')) {
console.log(dashes + 'Error handling 404 (not found): Invalid type (' + (typeof err404) + ') for 404 setting
.' + dashes);
}
return default404Handler(req, res, next);
}
} catch (e) {
...
autocomplete = function (req, res) { var cloudinary = require('cloudinary'); var max = req.query.max || 10; var prefix = req.query.prefix || ''; var next = req.query.next || null; cloudinary.api.resources(function (result) { if (result.error) { res.json({ error: { message: result.error.message } }); } else { res.json({ next: result.next_cursor, items: result.resources, }); } }, { type: 'upload', prefix: prefix, max_results: max, next_cursor: next, }); }
n/a
get = function (req, res) { var cloudinary = require('cloudinary'); cloudinary.api.resource(req.query.id, function (result) { if (result.error) { res.json({ error: { message: result.error.message } }); } else { res.json({ item: result }); } }); }
...
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT || '3001');
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development
') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
...
upload = function (req, res) { var cloudinary = require('cloudinary'); var keystone = req.keystone; if (req.files && req.files.file) { var options = {}; if (keystone.get('wysiwyg cloudinary images filenameAsPublicID')) { options.public_id = req.files.file.originalname.substring(0, req.files.file.originalname.lastIndexOf('.')); } cloudinary.uploader.upload(req.files.file.path, function (result) { var sendResult = function () { if (result.error) { res.send({ error: { message: result.error.message } }); } else { res.send({ image: { url: (keystone.get('cloudinary secure') === true) ? result.secure_url : result.url } }); } }; // TinyMCE upload plugin uses the iframe transport technique // so the response type must be text/html res.format({ html: sendResult, json: sendResult, }); }, options); } else { res.json({ error: { message: 'No image selected' } }); } }
...
if (req.files && req.files.file) {
var options = {};
if (keystone.get('wysiwyg cloudinary images filenameAsPublicID')) {
options.public_id = req.files.file.originalname.substring(0, req.files.file.originalname.lastIndexOf('.'));
}
cloudinary.uploader.upload(req.files.file.path, function (result) {
var sendResult = function () {
if (result.error) {
res.send({ error: { message: result.error.message } });
} else {
res.send({ image: { url: (keystone.get('cloudinary secure') === true) ? result.secure_url : result.url } });
}
};
...
function blend(color1, color2, percent) { const decimalFraction = percent / 100; const hex1 = validateHex(color1); const hex2 = validateHex(color2); // 1. const f = parseInt(hex1, 16); const t = parseInt(hex2, 16); const R1 = f >> 16; const G1 = f >> 8 & 0x00FF; const B1 = f & 0x0000FF; const R2 = t >> 16; const G2 = t >> 8 & 0x00FF; const B2 = t & 0x0000FF; // 2. return '#' + (0x1000000 + (Math.round((R2 - R1) * decimalFraction) + R1) * 0x10000 + (Math.round((G2 - G1) * decimalFraction) + G1) * 0x100 + (Math.round((B2 - B1) * decimalFraction) + B1)).toString(16).slice(1); }
n/a
function darken(color, percent) { return shade(color, percent * -1); }
n/a
function fade(color, opacity = 100) { const decimalFraction = opacity / 100; const hex = validateHex(color); // 1. const r = parseInt(hex.substring(0, 2), 16); const g = parseInt(hex.substring(2, 4), 16); const b = parseInt(hex.substring(4, 6), 16); // 2. const result = 'rgba(' + r + ',' + g + ',' + b + ',' + decimalFraction + ')'; return result; }
n/a
function shade(color, percent) { const decimalFraction = percent / 100; const hex = validateHex(color); // 1. let f = parseInt(hex, 16); let t = decimalFraction < 0 ? 0 : 255; let p = decimalFraction < 0 ? decimalFraction * -1 : decimalFraction; const R = f >> 16; const G = f >> 8 & 0x00FF; const B = f & 0x0000FF; // 2. return '#' + (0x1000000 + (Math.round((t - R) * p) + R) * 0x10000 + (Math.round((t - G) * p) + G) * 0x100 + (Math.round((t - B) * p) + B)).toString(16).slice(1); }
n/a
createSecret = function () { return crypto.pseudoRandomBytes(exports.SECRET_LENGTH).toString('base64'); }
...
}
exports.createSecret = function () {
return crypto.pseudoRandomBytes(exports.SECRET_LENGTH).toString('base64');
};
exports.getSecret = function (req) {
return req.session[exports.SECRET_KEY] || (req.session[exports.SECRET_KEY] = exports.createSecret
());
};
exports.createToken = function (req) {
return tokenize(utils.randomString(exports.SECRET_LENGTH), exports.getSecret(req));
};
exports.getToken = function (req, res) {
...
createToken = function (req) { return tokenize(utils.randomString(exports.SECRET_LENGTH), exports.getSecret(req)); }
...
};
exports.createToken = function (req) {
return tokenize(utils.randomString(exports.SECRET_LENGTH), exports.getSecret(req));
};
exports.getToken = function (req, res) {
res.locals[exports.LOCAL_VALUE] = res.locals[exports.LOCAL_VALUE] || exports.createToken(req);
res.cookie(exports.XSRF_COOKIE_KEY, res.locals[exports.LOCAL_VALUE]);
return res.locals[exports.LOCAL_VALUE];
};
exports.requestToken = function (req) {
if (req.body && req.body[exports.TOKEN_KEY]) {
return req.body[exports.TOKEN_KEY];
...
getSecret = function (req) { return req.session[exports.SECRET_KEY] || (req.session[exports.SECRET_KEY] = exports.createSecret()); }
...
};
exports.getSecret = function (req) {
return req.session[exports.SECRET_KEY] || (req.session[exports.SECRET_KEY] = exports.createSecret());
};
exports.createToken = function (req) {
return tokenize(utils.randomString(exports.SECRET_LENGTH), exports.getSecret(req));
};
exports.getToken = function (req, res) {
res.locals[exports.LOCAL_VALUE] = res.locals[exports.LOCAL_VALUE] || exports.createToken(req);
res.cookie(exports.XSRF_COOKIE_KEY, res.locals[exports.LOCAL_VALUE]);
return res.locals[exports.LOCAL_VALUE];
};
...
getToken = function (req, res) { res.locals[exports.LOCAL_VALUE] = res.locals[exports.LOCAL_VALUE] || exports.createToken(req); res.cookie(exports.XSRF_COOKIE_KEY, res.locals[exports.LOCAL_VALUE]); return res.locals[exports.LOCAL_VALUE]; }
...
)
);
};
exports.middleware = {
init: function (req, res, next) {
res.locals[exports.LOCAL_KEY] = exports.LOCAL_VALUE;
exports.getToken(req, res);
next();
},
validate: function (req, res, next) {
// Allow environment variable to disable check
if (DISABLE_CSRF) return next();
// Bail on safe methods
if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') {
...
requestToken = function (req) { if (req.body && req.body[exports.TOKEN_KEY]) { return req.body[exports.TOKEN_KEY]; } else if (req.query && req.query[exports.TOKEN_KEY]) { return req.query[exports.TOKEN_KEY]; } else if (req.headers && req.headers[exports.XSRF_HEADER_KEY]) { return req.headers[exports.XSRF_HEADER_KEY]; } else if (req.headers && req.headers[exports.CSRF_HEADER_KEY]) { return req.headers[exports.CSRF_HEADER_KEY]; } else if (req.cookies && req.cookies[exports.XSRF_COOKIE_KEY]) { return req.cookies[exports.XSRF_COOKIE_KEY]; } return ''; }
...
return '';
};
exports.validate = function (req, token) {
// Allow environment variable to disable check
if (DISABLE_CSRF) return true;
if (arguments.length === 1) {
token = exports.requestToken(req);
}
if (typeof token !== 'string') {
return false;
}
return scmp(
token,
tokenize(
...
validate = function (req, token) { // Allow environment variable to disable check if (DISABLE_CSRF) return true; if (arguments.length === 1) { token = exports.requestToken(req); } if (typeof token !== 'string') { return false; } return scmp( token, tokenize( token.slice(0, exports.SECRET_LENGTH), req.session[exports.SECRET_KEY] ) ); }
...
// Allow environment variable to disable check
if (DISABLE_CSRF) return next();
// Bail on safe methods
if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') {
return next();
}
// Validate token
if (exports.validate(req)) {
next();
} else {
res.statusCode = 403;
next(new Error('CSRF token mismatch'));
}
},
};
...
function borderBottomRadius(radius) { return { borderBottomLeftRadius: radius, borderBottomRightRadius: radius, }; }
n/a
function borderLeftRadius(radius) { return { borderBottomLeftRadius: radius, borderTopLeftRadius: radius, }; }
n/a
function borderRightRadius(radius) { return { borderBottomRightRadius: radius, borderTopRightRadius: radius, }; }
n/a
function borderTopRadius(radius) { return { borderTopLeftRadius: radius, borderTopRightRadius: radius, }; }
n/a
function gradientHorizontal(top, bottom, base) { return linearGradient('to right', top, bottom, base); }
n/a
function gradientVertical(top, bottom, base) { return linearGradient('to bottom', top, bottom, base); }
n/a
function createApplication() { var app = function(req, res, next) { app.handle(req, res, next); }; mixin(app, EventEmitter.prototype, false); mixin(app, proto, false); app.request = { __proto__: req, app: app }; app.response = { __proto__: res, app: app }; app.init(); return app; }
n/a
function Route(path) { this.path = path; this.stack = []; debug('new %s', path); // route handlers for various http methods this.methods = {}; }
n/a
Router = function (options) { var opts = options || {}; function router(req, res, next) { router.handle(req, res, next); } // mixin Router class functions router.__proto__ = proto; router.params = {}; router._params = []; router.caseSensitive = opts.caseSensitive; router.mergeParams = opts.mergeParams; router.strict = opts.strict; router.stack = []; return router; }
...
});
// Configure application routes
var appRouter = keystone.get('routes');
if (typeof appRouter === 'function') {
if (appRouter.length === 3) {
// new:
// var myRouter = new express.Router();
// myRouter.get('/', (req, res) => res.send('hello world'));
// keystone.set('routes', myRouter);
app.use(appRouter);
} else {
// old:
// var initRoutes = function (app) {
// app.get('/', (req, res) => res.send('hello world'));
...
function query(options) { var opts = Object.create(options || null); var queryparse = qs.parse; if (typeof options === 'function') { queryparse = options; opts = undefined; } if (opts !== undefined && opts.allowPrototypes === undefined) { // back-compat for qs module opts.allowPrototypes = true; } return function query(req, res, next){ if (!req.query) { var val = parseUrl(req).query; req.query = queryparse(val, opts); } next(); }; }
...
module.exports = function apiForGet (options) {
var idParam = options.id || 'id';
var List = this;
return function (req, res) {
var id = req.params[idParam];
var query = List.model.findById(id);
if (typeof options.query === 'function') {
var result = options.query(query, req);
if (result === false) return;
} else if (typeof options.query === 'object') {
query.where(options.query);
}
query.exec(function (err, item) {
if (err) return res.status(500).json({ err: 'database error', detail: err });
if (!item) return res.status(404).json({ err: 'not found', id: id });
...
function serveStatic(root, options) { if (!root) { throw new TypeError('root path required') } if (typeof root !== 'string') { throw new TypeError('root path must be a string') } // copy options object var opts = Object.create(options || null) // fall-though var fallthrough = opts.fallthrough !== false // default redirect var redirect = opts.redirect !== false // headers listener var setHeaders = opts.setHeaders if (setHeaders && typeof setHeaders !== 'function') { throw new TypeError('option setHeaders must be function') } // setup options for send opts.maxage = opts.maxage || opts.maxAge || 0 opts.root = resolve(root) // construct directory listener var onDirectory = redirect ? createRedirectDirectoryListener() : createNotFoundDirectoryListener() return function serveStatic (req, res, next) { if (req.method !== 'GET' && req.method !== 'HEAD') { if (fallthrough) { return next() } // method not allowed res.statusCode = 405 res.setHeader('Allow', 'GET, HEAD') res.setHeader('Content-Length', '0') res.end() return } var forwardError = !fallthrough var originalUrl = parseUrl.original(req) var path = parseUrl(req).pathname // make sure redirect occurs at mount if (path === '/' && originalUrl.pathname.substr(-1) !== '/') { path = '' } // create send stream var stream = send(req, path, opts) // add directory handler stream.on('directory', onDirectory) // add headers listener if (setHeaders) { stream.on('headers', setHeaders) } // add file listener for fallthrough if (fallthrough) { stream.on('file', function onFile () { // once file is determined, always forward error forwardError = true }) } // forward errors stream.on('error', function error (err) { if (forwardError || !(err.statusCode < 500)) { next(err) return } next() }) // pipe stream.pipe(res) } }
...
if (typeof staticPaths === 'string') {
staticPaths = [staticPaths];
}
if (Array.isArray(staticPaths)) {
staticPaths.forEach(function (value) {
app.use(express.static(keystone.expandPath(value), staticOptions));
});
}
};
...
function Route(path) { this.path = path; this.stack = []; debug('new %s', path); // route handlers for various http methods this.methods = {}; }
n/a
function _handles_method(method) { if (this.methods._all) { return true; } var name = method.toLowerCase(); if (name === 'head' && !this.methods['head']) { name = 'get'; } return Boolean(this.methods[name]); }
n/a
function _options() { var methods = Object.keys(this.methods); // append automatic head if (this.methods.get && !this.methods.head) { methods.push('head'); } for (var i = 0; i < methods.length; i++) { // make upper case methods[i] = methods[i].toUpperCase(); } return methods; }
n/a
acl = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
function all() { var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.all() requires callback functions but got a ' + type; throw new TypeError(msg); } var layer = Layer('/', {}, handle); layer.method = undefined; this.methods._all = true; this.stack.push(layer); } return this; }
...
* * `res.apiResponse(data)`
* * `res.apiError(key, err, msg, code)`
* * `res.apiNotFound(err, msg)`
* * `res.apiNotAllowed(err, msg)`
*
* ####Example:
*
* app.all('/api*', keystone.middleware.api);
*
* @param {app.request} req
* @param {app.response} res
* @param {function} next
* @api public
*/
...
bind = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
...
constructor() {
super();
bindFunctions.call(this, ['handleClick', 'handleOther']);
}
*/
module.exports = function bindFunctions (functions) {
functions.forEach(f => (this[f] = this[f].bind(this)));
};
...
checkout = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
connect = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
...
replset: {
rs_name: replicaData.db.replicaSetOptions.rs_name,
readPreference: replicaData.db.replicaSetOptions.readPreference,
},
};
debug('connecting to replica set');
keystone.mongoose.connect(replica, options);
} else {
debug('connecting to mongo');
keystone.initDatabaseConfig();
keystone.mongoose.connect(keystone.get('mongo'), keystone.get('mongo options'));
}
...
copy = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
delete = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
function dispatch(req, res, done) { var idx = 0; var stack = this.stack; if (stack.length === 0) { return done(); } var method = req.method.toLowerCase(); if (method === 'head' && !this.methods['head']) { method = 'get'; } req.route = this; next(); function next(err) { if (err && err === 'route') { return done(); } var layer = stack[idx++]; if (!layer) { return done(err); } if (layer.method && layer.method !== method) { return next(err); } if (err) { layer.handle_error(err, req, res, next); } else { layer.handle_request(req, res, next); } } }
n/a
get = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
...
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT || '3001');
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development
') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
...
head = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
link = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
lock = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
m-search = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
merge = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
mkactivity = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
mkcalendar = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
mkcol = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
move = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
...
var doMove = function (callback) {
if (typeof field.options.filename === 'function') {
filename = field.options.filename(item, file);
}
fs.move(file.path, path.join(field.options.dest, filename), { clobber: field.options
.overwrite }, function (err) {
if (err) return callback(err);
var fileData = {
filename: filename,
originalname: file.originalname,
path: field.options.dest,
...
notify = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
options = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
...
var initialFields;
// Inherit default options from parent list if it exists
if (options && options.inherits) {
if (options.inherits.options && options.inherits.options.inherits) {
throw new Error('Inherited Lists may not contain any inheritance');
}
defaultOptions = utils.options(defaultOptions, options.inherits.options);
if (options.inherits.options.track) {
options.track = false;
}
}
this.options = utils.options(defaultOptions, options);
...
patch = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
post = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
...
// Init API request helpers
router.use('/api', require('../middleware/apiError'));
router.use('/api', require('../middleware/logError'));
// #1: Session API
// TODO: this should respect keystone auth options
router.get('/api/session', require('../api/session/get'));
router.post('/api/session/signin', require('../api/session/signin'
;));
router.post('/api/session/signout', require('../api/session/signout'));
// #2: Session Routes
// Bind auth middleware (generic or custom) to * routes, allowing
// access to the generic signin page if generic auth is used
if (keystone.get('auth') === true) {
// TODO: poor separation of concerns; settings should be defaulted elsewhere
...
propfind = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
proppatch = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
purge = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
put = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
rebind = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
report = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
search = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
subscribe = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
trace = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
...
},
], function (err) {
if (err) {
console.error(err);
if ('stack' in err) {
console.trace(err.stack);
}
return callback && callback(err);
}
var msg = '\nSuccessfully created:\n';
_.forEach(stats, function (list) {
msg += '\n* ' + utils.plural(list.created, '* ' + list.singular, '* ' + list.plural);
...
unbind = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
unlink = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
...
var fs = require('fs');
module.exports = function (keystone, app, callback) {
var unixSocket = keystone.get('unix socket');
var message = keystone.get('name') + ' is ready on ' + unixSocket;
fs.unlink(unixSocket, function () {
// we expect err if the file is new so don't capture the argument
keystone.httpServer = app.listen(unixSocket, function (err) {
callback(err, message);
});
fs.chmod(unixSocket, 0x777);
});
...
unlock = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
unsubscribe = function (){ var handles = flatten(slice.call(arguments)); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires callback functions but got a ' + type; throw new Error(msg); } debug('%s %s', method, this.path); var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } return this; }
n/a
Router = function (options) { var opts = options || {}; function router(req, res, next) { router.handle(req, res, next); } // mixin Router class functions router.__proto__ = proto; router.params = {}; router._params = []; router.caseSensitive = opts.caseSensitive; router.mergeParams = opts.mergeParams; router.strict = opts.strict; router.stack = []; return router; }
...
});
// Configure application routes
var appRouter = keystone.get('routes');
if (typeof appRouter === 'function') {
if (appRouter.length === 3) {
// new:
// var myRouter = new express.Router();
// myRouter.get('/', (req, res) => res.send('hello world'));
// keystone.set('routes', myRouter);
app.use(appRouter);
} else {
// old:
// var initRoutes = function (app) {
// app.get('/', (req, res) => res.send('hello world'));
...
acl = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
all = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
...
* * `res.apiResponse(data)`
* * `res.apiError(key, err, msg, code)`
* * `res.apiNotFound(err, msg)`
* * `res.apiNotAllowed(err, msg)`
*
* ####Example:
*
* app.all('/api*', keystone.middleware.api);
*
* @param {app.request} req
* @param {app.response} res
* @param {function} next
* @api public
*/
...
bind = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
...
constructor() {
super();
bindFunctions.call(this, ['handleClick', 'handleOther']);
}
*/
module.exports = function bindFunctions (functions) {
functions.forEach(f => (this[f] = this[f].bind(this)));
};
...
checkout = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
connect = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
...
replset: {
rs_name: replicaData.db.replicaSetOptions.rs_name,
readPreference: replicaData.db.replicaSetOptions.readPreference,
},
};
debug('connecting to replica set');
keystone.mongoose.connect(replica, options);
} else {
debug('connecting to mongo');
keystone.initDatabaseConfig();
keystone.mongoose.connect(keystone.get('mongo'), keystone.get('mongo options'));
}
...
copy = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
delete = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
get = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
...
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT || '3001');
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development
') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
...
function handle(req, res, out) { var self = this; debug('dispatching %s %s', req.method, req.url); var search = 1 + req.url.indexOf('?'); var pathlength = search ? search - 1 : req.url.length; var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://'); var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : ''; var idx = 0; var removed = ''; var slashAdded = false; var paramcalled = {}; // store options for OPTIONS request // only used if OPTIONS request var options = []; // middleware and routes var stack = self.stack; // manage inter-router variables var parentParams = req.params; var parentUrl = req.baseUrl || ''; var done = restore(out, req, 'baseUrl', 'next', 'params'); // setup next layer req.next = next; // for options requests, respond with a default if nothing else responds if (req.method === 'OPTIONS') { done = wrap(done, function(old, err) { if (err || options.length === 0) return old(err); sendOptionsResponse(res, options, old); }); } // setup basic req values req.baseUrl = parentUrl; req.originalUrl = req.originalUrl || req.url; next(); function next(err) { var layerError = err === 'route' ? null : err; // remove added slash if (slashAdded) { req.url = req.url.substr(1); slashAdded = false; } // restore altered req.url if (removed.length !== 0) { req.baseUrl = parentUrl; req.url = protohost + removed + req.url.substr(protohost.length); removed = ''; } // no more matching layers if (idx >= stack.length) { setImmediate(done, layerError); return; } // get pathname of request var path = getPathname(req); if (path == null) { return done(layerError); } // find next matching layer var layer; var match; var route; while (match !== true && idx < stack.length) { layer = stack[idx++]; match = matchLayer(layer, path); route = layer.route; if (typeof match !== 'boolean') { // hold on to layerError layerError = layerError || match; } if (match !== true) { continue; } if (!route) { // process non-route handlers normally continue; } if (layerError) { // routes do not match with a pending error match = false; continue; } var method = req.method; var has_method = route._handles_method(method); // build up automatic options response if (!has_method && method === 'OPTIONS') { appendMethods(options, route._options()); } // don't even bother matching route if (!has_method && method !== 'HEAD') { match = false; continue; } } // no match if (match !== true) { return done(layerError); } // store route for dispatch on change if (route) { req.route = route; } // Capture one-time layer values req.params = self.mergeParams ? mergeParams(layer.params, parentParams) : layer.params; var layerPath = layer.path; // this should be done for the layer self.process_params(layer, paramcalled, req, res, function (err) { if (err) { return next(layerError || err); } if (route) { return layer.handle_request(req, res, next); } trim_prefix(layer, layerError, layerPath, path); }); } function trim_prefix(layer, layerError, layerPath, path) { var c = path[layerPath.length]; if (c && '/' !== c && '.' !== c) return next(layerError); // Trim off the part of the url that matches the route // middleware (.use stuff) needs to have the path stripped if (layerPath.length !== 0) { debug('trim prefix (%s) from url %s', layerPath, req.url); removed = layerPath; req.url = protohost + req.url.substr(protohost.length + removed.length); // Ensure leading slash if (!fqdn && req.url[0] !== '/') { req.url = ' ...
n/a
head = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
link = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
lock = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
m-search = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
merge = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
mkactivity = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
mkcalendar = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
mkcol = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
move = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
...
var doMove = function (callback) {
if (typeof field.options.filename === 'function') {
filename = field.options.filename(item, file);
}
fs.move(file.path, path.join(field.options.dest, filename), { clobber: field.options
.overwrite }, function (err) {
if (err) return callback(err);
var fileData = {
filename: filename,
originalname: file.originalname,
path: field.options.dest,
...
notify = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
options = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
...
var initialFields;
// Inherit default options from parent list if it exists
if (options && options.inherits) {
if (options.inherits.options && options.inherits.options.inherits) {
throw new Error('Inherited Lists may not contain any inheritance');
}
defaultOptions = utils.options(defaultOptions, options.inherits.options);
if (options.inherits.options.track) {
options.track = false;
}
}
this.options = utils.options(defaultOptions, options);
...
function param(name, fn) { // param logic if (typeof name === 'function') { deprecate('router.param(fn): Refactor to use path params'); this._params.push(name); return; } // apply param functions var params = this._params; var len = params.length; var ret; if (name[0] === ':') { deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.substr(1)) + ', fn) instead '); name = name.substr(1); } for (var i = 0; i < len; ++i) { if (ret = params[i](name, fn)) { fn = ret; } } // ensure we end up with a // middleware function if ('function' !== typeof fn) { throw new Error('invalid param() call for ' + name + ', got ' + fn); } (this.params[name] = this.params[name] || []).push(fn); return this; }
n/a
patch = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
post = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
...
// Init API request helpers
router.use('/api', require('../middleware/apiError'));
router.use('/api', require('../middleware/logError'));
// #1: Session API
// TODO: this should respect keystone auth options
router.get('/api/session', require('../api/session/get'));
router.post('/api/session/signin', require('../api/session/signin'
;));
router.post('/api/session/signout', require('../api/session/signout'));
// #2: Session Routes
// Bind auth middleware (generic or custom) to * routes, allowing
// access to the generic signin page if generic auth is used
if (keystone.get('auth') === true) {
// TODO: poor separation of concerns; settings should be defaulted elsewhere
...
function process_params(layer, called, req, res, done) { var params = this.params; // captured parameters from the layer, keys and values var keys = layer.keys; // fast track if (!keys || keys.length === 0) { return done(); } var i = 0; var name; var paramIndex = 0; var key; var paramVal; var paramCallbacks; var paramCalled; // process params in order // param callbacks can be async function param(err) { if (err) { return done(err); } if (i >= keys.length ) { return done(); } paramIndex = 0; key = keys[i++]; if (!key) { return done(); } name = key.name; paramVal = req.params[name]; paramCallbacks = params[name]; paramCalled = called[name]; if (paramVal === undefined || !paramCallbacks) { return param(); } // param previously called with same value or error occurred if (paramCalled && (paramCalled.match === paramVal || (paramCalled.error && paramCalled.error !== 'route'))) { // restore value req.params[name] = paramCalled.value; // next param return param(paramCalled.error); } called[name] = paramCalled = { error: null, match: paramVal, value: paramVal }; paramCallback(); } // single param callbacks function paramCallback(err) { var fn = paramCallbacks[paramIndex++]; // store updated value paramCalled.value = req.params[key.name]; if (err) { // store error paramCalled.error = err; param(err); return; } if (!fn) return param(); try { fn(req, res, paramCallback, paramVal, key.name); } catch (e) { paramCallback(e); } } param(); }
n/a
propfind = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
proppatch = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
purge = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
put = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
rebind = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
report = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function route(path) { var route = new Route(path); var layer = new Layer(path, { sensitive: this.caseSensitive, strict: this.strict, end: true }, route.dispatch.bind(route)); layer.route = route; this.stack.push(layer); return route; }
n/a
search = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
subscribe = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
trace = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
...
},
], function (err) {
if (err) {
console.error(err);
if ('stack' in err) {
console.trace(err.stack);
}
return callback && callback(err);
}
var msg = '\nSuccessfully created:\n';
_.forEach(stats, function (list) {
msg += '\n* ' + utils.plural(list.created, '* ' + list.singular, '* ' + list.plural);
...
unbind = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
unlink = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
...
var fs = require('fs');
module.exports = function (keystone, app, callback) {
var unixSocket = keystone.get('unix socket');
var message = keystone.get('name') + ' is ready on ' + unixSocket;
fs.unlink(unixSocket, function () {
// we expect err if the file is new so don't capture the argument
keystone.httpServer = app.listen(unixSocket, function (err) {
callback(err, message);
});
fs.chmod(unixSocket, 0x777);
});
...
unlock = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
unsubscribe = function (path){ var route = this.route(path) route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function use(fn) { var offset = 0; var path = '/'; // default path to '/' // disambiguate router.use([fn]) if (typeof fn !== 'function') { var arg = fn; while (Array.isArray(arg) && arg.length !== 0) { arg = arg[0]; } // first arg is the path if (typeof arg !== 'function') { offset = 1; path = fn; } } var callbacks = flatten(slice.call(arguments, offset)); if (callbacks.length === 0) { throw new TypeError('Router.use() requires middleware functions'); } for (var i = 0; i < callbacks.length; i++) { var fn = callbacks[i]; if (typeof fn !== 'function') { throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn)); } // add the middleware debug('use %s %s', path, fn.name || '<anonymous>'); var layer = new Layer(path, { sensitive: this.caseSensitive, strict: false, end: false }, fn); layer.route = undefined; this.stack.push(layer); } return this; }
...
module.exports = function bindIPRestrictions (keystone, app) {
// Set up body options and cookie parser
var bodyParserParams = {};
if (keystone.get('file limit')) {
bodyParserParams.limit = keystone.get('file limit');
}
app.use(bodyParser.json(bodyParserParams));
bodyParserParams.extended = true;
app.use(bodyParser.urlencoded(bodyParserParams));
app.use(multer({
includeEmptyFields: true,
}));
};
...
acl = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function all(path) { this.lazyrouter(); var route = this._router.route(path); var args = slice.call(arguments, 1); for (var i = 0; i < methods.length; i++) { route[methods[i]].apply(route, args); } return this; }
...
* * `res.apiResponse(data)`
* * `res.apiError(key, err, msg, code)`
* * `res.apiNotFound(err, msg)`
* * `res.apiNotAllowed(err, msg)`
*
* ####Example:
*
* app.all('/api*', keystone.middleware.api);
*
* @param {app.request} req
* @param {app.response} res
* @param {function} next
* @api public
*/
...
bind = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
...
constructor() {
super();
bindFunctions.call(this, ['handleClick', 'handleOther']);
}
*/
module.exports = function bindFunctions (functions) {
functions.forEach(f => (this[f] = this[f].bind(this)));
};
...
checkout = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
connect = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
...
replset: {
rs_name: replicaData.db.replicaSetOptions.rs_name,
readPreference: replicaData.db.replicaSetOptions.readPreference,
},
};
debug('connecting to replica set');
keystone.mongoose.connect(replica, options);
} else {
debug('connecting to mongo');
keystone.initDatabaseConfig();
keystone.mongoose.connect(keystone.get('mongo'), keystone.get('mongo options'));
}
...
copy = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function defaultConfiguration() { var env = process.env.NODE_ENV || 'development'; // default settings this.enable('x-powered-by'); this.set('etag', 'weak'); this.set('env', env); this.set('query parser', 'extended'); this.set('subdomain offset', 2); this.set('trust proxy', false); // trust proxy inherit back-compat Object.defineProperty(this.settings, trustProxyDefaultSymbol, { configurable: true, value: true }); debug('booting in %s mode', env); this.on('mount', function onmount(parent) { // inherit trust proxy if (this.settings[trustProxyDefaultSymbol] === true && typeof parent.settings['trust proxy fn'] === 'function') { delete this.settings['trust proxy']; delete this.settings['trust proxy fn']; } // inherit protos this.request.__proto__ = parent.request; this.response.__proto__ = parent.response; this.engines.__proto__ = parent.engines; this.settings.__proto__ = parent.settings; }); // setup locals this.locals = Object.create(null); // top-most app is mounted at / this.mountpath = '/'; // default locals this.locals.settings = this.settings; // default configuration this.set('view', View); this.set('views', resolve('views')); this.set('jsonp callback name', 'callback'); if (env === 'production') { this.enable('view cache'); } Object.defineProperty(this, 'router', { get: function() { throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.'); } }); }
n/a
del = function (arg0) { "use strict" log.call(deprecate, message, site) return fn.apply(this, arguments) }
n/a
delete = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function disable(setting) { return this.set(setting, false); }
...
module.exports = function initTrustProxy (keystone, app) {
// Process 'X-Forwarded-For' request header
if (keystone.get('trust proxy') === true) {
app.enable('trust proxy');
} else {
app.disable('trust proxy');
}
};
...
function disabled(setting) { return !this.set(setting); }
n/a
function enable(setting) { return this.set(setting, true); }
...
module.exports = function initTrustProxy (keystone, app) {
// Process 'X-Forwarded-For' request header
if (keystone.get('trust proxy') === true) {
app.enable('trust proxy');
} else {
app.disable('trust proxy');
}
};
...
function enabled(setting) { return Boolean(this.set(setting)); }
n/a
function engine(ext, fn) { if (typeof fn !== 'function') { throw new Error('callback function required'); } // get file extension var extension = ext[0] !== '.' ? '.' + ext : ext; // store engine this.engines[extension] = fn; return this; }
...
var path = require('path');
module.exports = function initViewEngine (keystone, app) {
// Allow usage of custom view engines
if (keystone.get('custom engine')) {
app.engine(keystone.get('view engine'), keystone.get('custom engine
x27;));
}
// Set location of view templates and view engine
app.set('views', keystone.getPath('views') || path.sep + 'views');
app.set('view engine', keystone.get('view engine'));
var customView = keystone.get('view');
...
get = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
...
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT || '3001');
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development
') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
...
function handle(req, res, callback) { var router = this._router; // final handler var done = callback || finalhandler(req, res, { env: this.get('env'), onerror: logerror.bind(this) }); // no routes if (!router) { debug('no routes defined on app'); done(); return; } router.handle(req, res, done); }
n/a
head = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function init() { this.cache = {}; this.engines = {}; this.settings = {}; this.defaultConfiguration(); }
...
self.editor = editor;
editor.on('change', self.valueChanged);
editor.on('focus', self.focusChanged.bind(self, true));
editor.on('blur', self.focusChanged.bind(self, false));
};
this._currentValue = this.props.value;
_tinymce2.default.init(opts);
if ((0, _evalDependsOn2.default)(this.props.dependsOn, this.props.values)) {
this.setState({ wysiwygActive: true });
}
},
removeWysiwyg: function removeWysiwyg(state) {
removeTinyMCEInstance(_tinymce2.default.get(state.id));
this.setState({ wysiwygActive: false });
...
function lazyrouter() { if (!this._router) { this._router = new Router({ caseSensitive: this.enabled('case sensitive routing'), strict: this.enabled('strict routing') }); this._router.use(query(this.get('query parser fn'))); this._router.use(middleware.init(this)); } }
n/a
link = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function listen() { var server = http.createServer(this); return server.listen.apply(server, arguments); }
...
var host = keystone.get('host');
var port = keystone.get('port');
var forceSsl = (keystone.get('ssl') === 'force');
keystone.httpServer = http
.createServer(app)
.listen(port, host, function ready (err) {
if (err) { return callback(err); }
var message = keystone.get('name') + ' is ready on '
+ 'http://' + host + ':' + port
+ (forceSsl ? ' (SSL redirect)' : '');
callback(null, message);
});
...
lock = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
m-search = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
merge = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
mkactivity = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
mkcalendar = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
mkcol = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
move = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
...
var doMove = function (callback) {
if (typeof field.options.filename === 'function') {
filename = field.options.filename(item, file);
}
fs.move(file.path, path.join(field.options.dest, filename), { clobber: field.options
.overwrite }, function (err) {
if (err) return callback(err);
var fileData = {
filename: filename,
originalname: file.originalname,
path: field.options.dest,
...
notify = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
options = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
...
var initialFields;
// Inherit default options from parent list if it exists
if (options && options.inherits) {
if (options.inherits.options && options.inherits.options.inherits) {
throw new Error('Inherited Lists may not contain any inheritance');
}
defaultOptions = utils.options(defaultOptions, options.inherits.options);
if (options.inherits.options.track) {
options.track = false;
}
}
this.options = utils.options(defaultOptions, options);
...
function param(name, fn) { this.lazyrouter(); if (Array.isArray(name)) { for (var i = 0; i < name.length; i++) { this.param(name[i], fn); } return this; } this._router.param(name, fn); return this; }
n/a
patch = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function path() { return this.parent ? this.parent.path() + this.mountpath : ''; }
...
util.inherits(geopoint, FieldType);
/**
* Registers the field on the List's Mongoose Schema.
* Adds a 2dsphere indexed lat/lng pair
*/
geopoint.prototype.addToSchema = function (schema) {
schema.path(this.path, _.defaults({ type: [Number], index: '2dsphere' }, this
.options));
this.bindUnderscoreMethods();
};
/**
* Gets the field's data from an Item, as used by the React components
*/
geopoint.prototype.getData = function (item) {
...
post = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
...
// Init API request helpers
router.use('/api', require('../middleware/apiError'));
router.use('/api', require('../middleware/logError'));
// #1: Session API
// TODO: this should respect keystone auth options
router.get('/api/session', require('../api/session/get'));
router.post('/api/session/signin', require('../api/session/signin'
;));
router.post('/api/session/signout', require('../api/session/signout'));
// #2: Session Routes
// Bind auth middleware (generic or custom) to * routes, allowing
// access to the generic signin page if generic auth is used
if (keystone.get('auth') === true) {
// TODO: poor separation of concerns; settings should be defaulted elsewhere
...
propfind = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
proppatch = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
purge = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
put = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
rebind = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function render(name, options, callback) { var cache = this.cache; var done = callback; var engines = this.engines; var opts = options; var renderOptions = {}; var view; // support callback function as second arg if (typeof options === 'function') { done = options; opts = {}; } // merge app.locals merge(renderOptions, this.locals); // merge options._locals if (opts._locals) { merge(renderOptions, opts._locals); } // merge options merge(renderOptions, opts); // set .cache unless explicitly provided if (renderOptions.cache == null) { renderOptions.cache = this.enabled('view cache'); } // primed cache if (renderOptions.cache) { view = cache[name]; } // view if (!view) { var View = this.get('view'); view = new View(name, { defaultEngine: this.get('view engine'), root: this.get('views'), engines: engines }); if (!view.path) { var dirs = Array.isArray(view.root) && view.root.length > 1 ? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"' : 'directory "' + view.root + '"' var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs); err.view = view; return done(err); } // prime the cache if (renderOptions.cache) { cache[name] = view; } } // render tryRender(view, renderOptions, done); }
...
try {
if (typeof err404 === 'function') {
return err404(req, res, next);
} else if (typeof err404 === 'string') {
if (req.headers.accept === 'application/json') {
return res.status(404).json({ error: 'not found' });
}
return res.status(404).render(err404);
} else {
if (keystone.get('logger')) {
console.log(dashes + 'Error handling 404 (not found): Invalid type (' + (typeof err404) + ') for 404 setting
.' + dashes);
}
return default404Handler(req, res, next);
}
} catch (e) {
...
report = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function route(path) { this.lazyrouter(); return this._router.route(path); }
n/a
search = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function set(setting, val) { if (arguments.length === 1) { // app.get(setting) return this.settings[setting]; } debug('set "%s" to %o', setting, val); // set value this.settings[setting] = val; // trigger matched settings switch (setting) { case 'etag': this.set('etag fn', compileETag(val)); break; case 'query parser': this.set('query parser fn', compileQueryParser(val)); break; case 'trust proxy': this.set('trust proxy fn', compileTrust(val)); // trust proxy inherit back-compat Object.defineProperty(this.settings, trustProxyDefaultSymbol, { configurable: true, value: false }); break; } return this; }
...
Alternatively, to include Keystone in an existing project or start from scratch (without Yeoman), specify `keystone: "^0.3.
9"` in the `dependencies` array of your `package.json` file, and run `npm install` from your terminal.
Then read through the [Documentation](http://keystonejs.com/docs) and the [Example Projects](http://keystonejs.com/examples) to
understand how to use it.
### Configuration
Config variables can be passed in an object to the `keystone.init` method, or can be set any time before `keystone.start` is called
using `keystone.set(key, value)`. This allows for a more flexible order of execution
(e.g. if you refer to Lists in your routes, you can set the routes after configuring your Lists, as in the example above).
See the [KeystoneJS configuration documentation](http://keystonejs.com/docs/configuration) for details and examples of the available
configuration options.
### Database field types
Keystone builds on the basic data types provided by mongo and allows you to easily add rich, functional fields to your application
's models.
...
subscribe = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
trace = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
...
},
], function (err) {
if (err) {
console.error(err);
if ('stack' in err) {
console.trace(err.stack);
}
return callback && callback(err);
}
var msg = '\nSuccessfully created:\n';
_.forEach(stats, function (list) {
msg += '\n* ' + utils.plural(list.created, '* ' + list.singular, '* ' + list.plural);
...
unbind = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
unlink = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
...
var fs = require('fs');
module.exports = function (keystone, app, callback) {
var unixSocket = keystone.get('unix socket');
var message = keystone.get('name') + ' is ready on ' + unixSocket;
fs.unlink(unixSocket, function () {
// we expect err if the file is new so don't capture the argument
keystone.httpServer = app.listen(unixSocket, function (err) {
callback(err, message);
});
fs.chmod(unixSocket, 0x777);
});
...
unlock = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
unsubscribe = function (path){ if (method === 'get' && arguments.length === 1) { // app.get(setting) return this.set(path); } this.lazyrouter(); var route = this._router.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }
n/a
function use(fn) { var offset = 0; var path = '/'; // default path to '/' // disambiguate app.use([fn]) if (typeof fn !== 'function') { var arg = fn; while (Array.isArray(arg) && arg.length !== 0) { arg = arg[0]; } // first arg is the path if (typeof arg !== 'function') { offset = 1; path = fn; } } var fns = flatten(slice.call(arguments, offset)); if (fns.length === 0) { throw new TypeError('app.use() requires middleware functions'); } // setup router this.lazyrouter(); var router = this._router; fns.forEach(function (fn) { // non-express app if (!fn || !fn.handle || !fn.set) { return router.use(path, fn); } debug('.use app under %s', path); fn.mountpath = path; fn.parent = this; // restore .app property on req and res router.use(path, function mounted_app(req, res, next) { var orig = req.app; fn.handle(req, res, function (err) { req.__proto__ = orig.request; res.__proto__ = orig.response; next(err); }); }); // mounted an app fn.emit('mount', this); }, this); return this; }
...
module.exports = function bindIPRestrictions (keystone, app) {
// Set up body options and cookie parser
var bodyParserParams = {};
if (keystone.get('file limit')) {
bodyParserParams.limit = keystone.get('file limit');
}
app.use(bodyParser.json(bodyParserParams));
bodyParserParams.extended = true;
app.use(bodyParser.urlencoded(bodyParserParams));
app.use(multer({
includeEmptyFields: true,
}));
};
...
accepts = function (){ var accept = accepts(this); return accept.types.apply(accept, arguments); }
n/a
acceptsCharset = function () { "use strict" log.call(deprecate, message, site) return fn.apply(this, arguments) }
n/a
acceptsCharsets = function (){ var accept = accepts(this); return accept.charsets.apply(accept, arguments); }
n/a
acceptsEncoding = function () { "use strict" log.call(deprecate, message, site) return fn.apply(this, arguments) }
n/a
acceptsEncodings = function (){ var accept = accepts(this); return accept.encodings.apply(accept, arguments); }
n/a
acceptsLanguage = function () { "use strict" log.call(deprecate, message, site) return fn.apply(this, arguments) }
n/a
acceptsLanguages = function (){ var accept = accepts(this); return accept.languages.apply(accept, arguments); }
n/a
function header(name) { if (!name) { throw new TypeError('name argument is required to req.get'); } if (typeof name !== 'string') { throw new TypeError('name must be a string to req.get'); } var lc = name.toLowerCase(); switch (lc) { case 'referer': case 'referrer': return this.headers.referrer || this.headers.referer; default: return this.headers[lc]; } }
...
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT || '3001');
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development
') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
...
function header(name) { if (!name) { throw new TypeError('name argument is required to req.get'); } if (typeof name !== 'string') { throw new TypeError('name must be a string to req.get'); } var lc = name.toLowerCase(); switch (lc) { case 'referer': case 'referrer': return this.headers.referrer || this.headers.referer; default: return this.headers[lc]; } }
...
// passed as middeware to the express app.
module.exports = function (keystone) {
return function cors (req, res, next) {
var origin = keystone.get('cors allow origin');
if (origin) {
res.header('Access-Control-Allow-Origin', origin === true ? '*'
; : origin);
}
if (keystone.get('cors allow methods') !== false) {
res.header('Access-Control-Allow-Methods', keystone.get('cors allow methods') || 'GET,PUT,POST,DELETE
,OPTIONS');
}
if (keystone.get('cors allow headers') !== false) {
res.header('Access-Control-Allow-Headers', keystone.get('cors allow headers') || 'Content-Type, Authorization
');
...
function is(types) { var arr = types; // support flattened arguments if (!Array.isArray(types)) { arr = new Array(arguments.length); for (var i = 0; i < arr.length; i++) { arr[i] = arguments[i]; } } return typeis(this, arr); }
...
instance.setFullscreen(true);
});
}
editor.append(editorHeader);
// Wrap the textarea
if (container.is('textarea')) {
container.before(editor);
textarea = container;
textarea.addClass('md-input');
editor.append(textarea);
} else {
var rawContent = typeof toMarkdown == 'function' ? toMarkdown(container.html()) : container.html(),
currentContent = $.trim(rawContent);
...
function param(name, defaultValue) { var params = this.params || {}; var body = this.body || {}; var query = this.query || {}; var args = arguments.length === 1 ? 'name' : 'name, default'; deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead'); if (null != params[name] && params.hasOwnProperty(name)) return params[name]; if (null != body[name]) return body[name]; if (null != query[name]) return query[name]; return defaultValue; }
n/a
function range(size, options) { var range = this.get('Range'); if (!range) return; return parseRange(size, range, options); }
n/a
function append(field, val) { var prev = this.get(field); var value = val; if (prev) { // concat the new and prev vals value = Array.isArray(prev) ? prev.concat(val) : Array.isArray(val) ? [prev].concat(val) : [prev, val]; } return this.set(field, value); }
...
buttonContainer.attr('data-toggle', 'button');
}
buttonIconContainer = $('<span/>');
buttonIconContainer.addClass(buttonIcon);
buttonIconContainer.prependTo(buttonContainer);
// Attach the button object
btnGroupContainer.append(buttonContainer);
// Register handler and callback
handler.push(buttonHandler);
callback.push(button.callback);
}
// Attach the button group into container dom
...
function attachment(filename) { if (filename) { this.type(extname(filename)); } this.set('Content-Disposition', contentDisposition(filename)); return this; }
...
}
query.exec(function (err, results) {
if (err) return res.status(500).json(err);
var sendCSV = function (data) {
res.attachment(req.list.path + '-' + moment().format('YYYYMMDD-HHMMSS
') + '.csv');
res.setHeader('Content-Type', 'application/octet-stream');
var content = baby.unparse(data, {
delimiter: keystone.get('csv field delimiter') || ',',
});
res.end(content, 'utf-8');
...
function clearCookie(name, options) { var opts = merge({ expires: new Date(1), path: '/' }, options); return this.cookie(name, '', opts); }
n/a
function contentType(type) { var ct = type.indexOf('/') === -1 ? mime.lookup(type) : type; return this.set('Content-Type', ct); }
n/a
cookie = function (name, value, options) { var opts = merge({}, options); var secret = this.req.secret; var signed = opts.signed; if (signed && !secret) { throw new Error('cookieParser("secret") required for signed cookies'); } var val = typeof value === 'object' ? 'j:' + JSON.stringify(value) : String(value); if (signed) { val = 's:' + sign(val, secret); } if ('maxAge' in opts) { opts.expires = new Date(Date.now() + opts.maxAge); opts.maxAge /= 1000; } if (opts.path == null) { opts.path = '/'; } this.append('Set-Cookie', cookie.serialize(name, String(val), opts)); return this; }
...
exports.createToken = function (req) {
return tokenize(utils.randomString(exports.SECRET_LENGTH), exports.getSecret(req));
};
exports.getToken = function (req, res) {
res.locals[exports.LOCAL_VALUE] = res.locals[exports.LOCAL_VALUE] || exports.createToken(req);
res.cookie(exports.XSRF_COOKIE_KEY, res.locals[exports.LOCAL_VALUE]);
return res.locals[exports.LOCAL_VALUE];
};
exports.requestToken = function (req) {
if (req.body && req.body[exports.TOKEN_KEY]) {
return req.body[exports.TOKEN_KEY];
} else if (req.query && req.query[exports.TOKEN_KEY]) {
...
function download(path, filename, callback) { var done = callback; var name = filename; // support function as second arg if (typeof filename === 'function') { done = filename; name = null; } // set Content-Disposition when file is sent var headers = { 'Content-Disposition': contentDisposition(name || path) }; // Resolve the full path for sendFile var fullPath = resolve(path); return this.sendFile(fullPath, { headers: headers }, done); }
n/a
format = function (obj){ var req = this.req; var next = req.next; var fn = obj.default; if (fn) delete obj.default; var keys = Object.keys(obj); var key = keys.length > 0 ? req.accepts(keys) : false; this.vary("Accept"); if (key) { this.set('Content-Type', normalizeType(key).value); obj[key](req, this, next); } else if (fn) { fn(); } else { var err = new Error('Not Acceptable'); err.status = err.statusCode = 406; err.types = normalizeTypes(keys).map(function(o){ return o.value }); next(err); } return this; }
...
var value = item.get(field.path);
if (transform.length === 1) {
return value[transform[0]];
} else {
return _.pick(value, transform);
}
}
return field.format(item);
}
/**
* Gets the data from an Item ready to be serialised to CSV for download
*/
function getCSVData (item, options) {
...
get = function (field){ return this.getHeader(field); }
...
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT || '3001');
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development
') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
...
function header(field, val) { if (arguments.length === 2) { var value = Array.isArray(val) ? val.map(String) : String(val); // add charset to content-type if (field.toLowerCase() === 'content-type' && !charsetRegExp.test(value)) { var charset = mime.charsets.lookup(value.split(';')[0]); if (charset) value += '; charset=' + charset.toLowerCase(); } this.setHeader(field, value); } else { for (var key in field) { this.set(key, field[key]); } } return this; }
...
// passed as middeware to the express app.
module.exports = function (keystone) {
return function cors (req, res, next) {
var origin = keystone.get('cors allow origin');
if (origin) {
res.header('Access-Control-Allow-Origin', origin === true ? '*'
; : origin);
}
if (keystone.get('cors allow methods') !== false) {
res.header('Access-Control-Allow-Methods', keystone.get('cors allow methods') || 'GET,PUT,POST,DELETE
,OPTIONS');
}
if (keystone.get('cors allow headers') !== false) {
res.header('Access-Control-Allow-Headers', keystone.get('cors allow headers') || 'Content-Type, Authorization
');
...
function json(obj) { var val = obj; // allow status / body if (arguments.length === 2) { // res.json(body, status) backwards compat if (typeof arguments[1] === 'number') { deprecate('res.json(obj, status): Use res.status(status).json(obj) instead'); this.statusCode = arguments[1]; } else { deprecate('res.json(status, obj): Use res.status(status).json(obj) instead'); this.statusCode = arguments[0]; val = arguments[1]; } } // settings var app = this.app; var replacer = app.get('json replacer'); var spaces = app.get('json spaces'); var body = stringify(val, replacer, spaces); // content-type if (!this.get('Content-Type')) { this.set('Content-Type', 'application/json'); } return this.send(body); }
...
module.exports = function bindIPRestrictions (keystone, app) {
// Set up body options and cookie parser
var bodyParserParams = {};
if (keystone.get('file limit')) {
bodyParserParams.limit = keystone.get('file limit');
}
app.use(bodyParser.json(bodyParserParams));
bodyParserParams.extended = true;
app.use(bodyParser.urlencoded(bodyParserParams));
app.use(multer({
includeEmptyFields: true,
}));
};
...
function jsonp(obj) { var val = obj; // allow status / body if (arguments.length === 2) { // res.json(body, status) backwards compat if (typeof arguments[1] === 'number') { deprecate('res.jsonp(obj, status): Use res.status(status).json(obj) instead'); this.statusCode = arguments[1]; } else { deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead'); this.statusCode = arguments[0]; val = arguments[1]; } } // settings var app = this.app; var replacer = app.get('json replacer'); var spaces = app.get('json spaces'); var body = stringify(val, replacer, spaces); var callback = this.req.query[app.get('jsonp callback name')]; // content-type if (!this.get('Content-Type')) { this.set('X-Content-Type-Options', 'nosniff'); this.set('Content-Type', 'application/json'); } // fixup callback if (Array.isArray(callback)) { callback = callback[0]; } // jsonp if (typeof callback === 'string' && callback.length !== 0) { this.charset = 'utf-8'; this.set('X-Content-Type-Options', 'nosniff'); this.set('Content-Type', 'text/javascript'); // restrict callback charset callback = callback.replace(/[^\[\]\w$.]/g, ''); // replace chars not allowed in JavaScript that are in JSON body = body .replace(/\u2028/g, '\\u2028') .replace(/\u2029/g, '\\u2029'); // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse" // the typeof check is just to reduce client error noise body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');'; } return this.send(body); }
...
// passed as middeware to the express app.
module.exports = function (keystone) {
return function initAPI (req, res, next) {
res.apiResponse = function (data) {
if (req.query.callback) {
res.jsonp(data);
} else {
res.json(data);
}
};
res.apiError = function (key, err, msg, code) {
msg = msg || 'Error';
...
links = function (links){ var link = this.get('Link') || ''; if (link) link += ', '; return this.set('Link', link + Object.keys(links).map(function(rel){ return '<' + links[rel] + '>; rel="' + rel + '"'; }).join(', ')); }
n/a
function location(url) { var loc = url; // "back" is an alias for the referrer if (url === 'back') { loc = this.req.get('Referrer') || '/'; } // set location return this.set('Location', encodeUrl(loc)); }
n/a
function redirect(url) { var address = url; var body; var status = 302; // allow status / url if (arguments.length === 2) { if (typeof arguments[0] === 'number') { status = arguments[0]; address = arguments[1]; } else { deprecate('res.redirect(url, status): Use res.redirect(status, url) instead'); status = arguments[1]; } } // Set location header address = this.location(address).get('Location'); // Support text/{plain,html} by default this.format({ text: function(){ body = statusCodes[status] + '. Redirecting to ' + address; }, html: function(){ var u = escapeHtml(address); body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'; }, default: function(){ body = ''; } }); // Respond this.statusCode = status; this.set('Content-Length', Buffer.byteLength(body)); if (this.req.method === 'HEAD') { this.end(); } else { this.end(body); } }
...
module.exports = function bindErrorHandlers (keystone, app) {
if (Object.keys(keystone._redirects).length) {
app.use(function (req, res, next) {
if (keystone._redirects[req.path]) {
res.redirect(keystone._redirects[req.path]);
} else {
next();
}
});
}
};
...
function render(view, options, callback) { var app = this.req.app; var done = callback; var opts = options || {}; var req = this.req; var self = this; // support callback function as second arg if (typeof options === 'function') { done = options; opts = {}; } // merge res.locals opts._locals = self.locals; // default callback to respond done = done || function (err, str) { if (err) return req.next(err); self.send(str); }; // render app.render(view, opts, done); }
...
try {
if (typeof err404 === 'function') {
return err404(req, res, next);
} else if (typeof err404 === 'string') {
if (req.headers.accept === 'application/json') {
return res.status(404).json({ error: 'not found' });
}
return res.status(404).render(err404);
} else {
if (keystone.get('logger')) {
console.log(dashes + 'Error handling 404 (not found): Invalid type (' + (typeof err404) + ') for 404 setting
.' + dashes);
}
return default404Handler(req, res, next);
}
} catch (e) {
...
function send(body) { var chunk = body; var encoding; var len; var req = this.req; var type; // settings var app = this.app; // allow status / body if (arguments.length === 2) { // res.send(body, status) backwards compat if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') { deprecate('res.send(body, status): Use res.status(status).send(body) instead'); this.statusCode = arguments[1]; } else { deprecate('res.send(status, body): Use res.status(status).send(body) instead'); this.statusCode = arguments[0]; chunk = arguments[1]; } } // disambiguate res.send(status) and res.send(status, num) if (typeof chunk === 'number' && arguments.length === 1) { // res.send(status) will set status message as text string if (!this.get('Content-Type')) { this.type('txt'); } deprecate('res.send(status): Use res.sendStatus(status) instead'); this.statusCode = chunk; chunk = statusCodes[chunk]; } switch (typeof chunk) { // string defaulting to html case 'string': if (!this.get('Content-Type')) { this.type('html'); } break; case 'boolean': case 'number': case 'object': if (chunk === null) { chunk = ''; } else if (Buffer.isBuffer(chunk)) { if (!this.get('Content-Type')) { this.type('bin'); } } else { return this.json(chunk); } break; } // write strings in utf-8 if (typeof chunk === 'string') { encoding = 'utf8'; type = this.get('Content-Type'); // reflect this in content-type if (typeof type === 'string') { this.set('Content-Type', setCharset(type, 'utf-8')); } } // populate Content-Length if (chunk !== undefined) { if (!Buffer.isBuffer(chunk)) { // convert chunk to Buffer; saves later double conversions chunk = new Buffer(chunk, encoding); encoding = undefined; } len = chunk.length; this.set('Content-Length', len); } // populate ETag var etag; var generateETag = len !== undefined && app.get('etag fn'); if (typeof generateETag === 'function' && !this.get('ETag')) { if ((etag = generateETag(chunk, encoding))) { this.set('ETag', etag); } } // freshness if (req.fresh) this.statusCode = 304; // strip irrelevant headers if (204 === this.statusCode || 304 === this.statusCode) { this.removeHeader('Content-Type'); this.removeHeader('Content-Length'); this.removeHeader('Transfer-Encoding'); chunk = ''; } if (req.method === 'HEAD') { // skip body for HEAD this.end(); } else { // respond this.end(chunk, encoding); } return this; }
...
var email = new KeystoneEmail(templateName, options);
// Make email.send backwards compatible with old argument signature
var send = email.send;
email.send = function () {
var args = [arguments[0]];
if (typeof arguments[1] === 'function') {
// map .send(options, callback) => .send(locals, options, callback)
// TOOD: Deprecate this call signature
args.push(arguments[0]);
args.push(arguments[1]);
} else {
// map .send(locals options, callback) => .send(locals, options, callback)
args.push(arguments[1]);
args.push(arguments[2]);
...
function sendFile(path, options, callback) { var done = callback; var req = this.req; var res = this; var next = req.next; var opts = options || {}; if (!path) { throw new TypeError('path argument is required to res.sendFile'); } // support function as second arg if (typeof options === 'function') { done = options; opts = {}; } if (!opts.root && !isAbsolute(path)) { throw new TypeError('path must be absolute or specify root to res.sendFile'); } // create file stream var pathname = encodeURI(path); var file = send(req, pathname, opts); // transfer sendfile(res, file, opts, function (err) { if (done) return done(err); if (err && err.code === 'EISDIR') return next(); // next() all but write errors if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') { next(err); } }); }
n/a
function sendStatus(statusCode) { var body = statusCodes[statusCode] || String(statusCode); this.statusCode = statusCode; this.type('txt'); return this.send(body); }
n/a
sendfile = function (arg0, arg1, arg2) { "use strict" log.call(deprecate, message, site) return fn.apply(this, arguments) }
n/a
function header(field, val) { if (arguments.length === 2) { var value = Array.isArray(val) ? val.map(String) : String(val); // add charset to content-type if (field.toLowerCase() === 'content-type' && !charsetRegExp.test(value)) { var charset = mime.charsets.lookup(value.split(';')[0]); if (charset) value += '; charset=' + charset.toLowerCase(); } this.setHeader(field, value); } else { for (var key in field) { this.set(key, field[key]); } } return this; }
...
Alternatively, to include Keystone in an existing project or start from scratch (without Yeoman), specify `keystone: "^0.3.
9"` in the `dependencies` array of your `package.json` file, and run `npm install` from your terminal.
Then read through the [Documentation](http://keystonejs.com/docs) and the [Example Projects](http://keystonejs.com/examples) to
understand how to use it.
### Configuration
Config variables can be passed in an object to the `keystone.init` method, or can be set any time before `keystone.start` is called
using `keystone.set(key, value)`. This allows for a more flexible order of execution
(e.g. if you refer to Lists in your routes, you can set the routes after configuring your Lists, as in the example above).
See the [KeystoneJS configuration documentation](http://keystonejs.com/docs/configuration) for details and examples of the available
configuration options.
### Database field types
Keystone builds on the basic data types provided by mongo and allows you to easily add rich, functional fields to your application
's models.
...
function status(code) { this.statusCode = code; return this; }
...
var utils = require('keystone-utils');
module.exports = function bindErrorHandlers (keystone, app) {
// Handle 404 (no route matched) errors
var default404Handler = function (req, res) {
if (req.headers.accept === 'application/json') {
return res.status(404).json({ error: 'not found' });
}
res.status(404).send(keystone.wrapHTMLError('Sorry, no page could be found at this address (404)'));
};
app.use(function (req, res, next) {
var err404 = keystone.get('404');
if (err404) {
...
function contentType(type) { var ct = type.indexOf('/') === -1 ? mime.lookup(type) : type; return this.set('Content-Type', ct); }
...
else {
throw new Error('keystone.content.page.add() Error: Unrecognised field constructor: ' + options.type);
}
}
self.fields[path] = new options.type(path, options);
});
return this;
};
...
vary = function (field){ // checks for back-compat if (!field || (Array.isArray(field) && !field.length)) { deprecate('res.vary(): Provide a field name'); return this; } vary(this, field); return this; }
n/a
function html(path, options) { html.super_.call(path, options); }
...
// Wrap the textarea
if (container.is('textarea')) {
container.before(editor);
textarea = container;
textarea.addClass('md-input');
editor.append(textarea);
} else {
var rawContent = typeof toMarkdown == 'function' ? toMarkdown(container.html
span>()) : container.html(),
currentContent = $.trim(rawContent);
// This is some arbitrary content that could be edited
textarea = $('<textarea/>', {
'class': 'md-input',
'val': currentContent
});
...
super_ = function (path, options) { // eslint-disable-line no-unused-vars // TODO }
n/a
function initAPI(req, res, next) { res.apiResponse = function (data) { if (req.query.callback) { res.jsonp(data); } else { res.json(data); } }; res.apiError = function (key, err, msg, code) { msg = msg || 'Error'; key = key || 'unknown error'; msg += ' (' + key + ')'; if (keystone.get('logger')) { console.log(msg + (err ? ':' : '')); if (err) { console.log(err); } } res.status(code || 500); res.apiResponse({ error: key || 'error', detail: err }); }; res.apiNotFound = function (err, msg) { res.apiError('data not found', err, msg || 'not found', 404); }; res.apiNotAllowed = function (err, msg) { res.apiError('access not allowed', err, msg || 'not allowed', 403); }; next(); }
n/a
function cors(req, res, next) { var origin = keystone.get('cors allow origin'); if (origin) { res.header('Access-Control-Allow-Origin', origin === true ? '*' : origin); } if (keystone.get('cors allow methods') !== false) { res.header('Access-Control-Allow-Methods', keystone.get('cors allow methods') || 'GET,PUT,POST,DELETE,OPTIONS'); } if (keystone.get('cors allow headers') !== false) { res.header('Access-Control-Allow-Headers', keystone.get('cors allow headers') || 'Content-Type, Authorization'); } next(); }
n/a
options = function (options) { if (!arguments.length) { return this._options; } if (typeof options === 'object') { var keys = Object.keys(options); var i = keys.length; var k; while (i--) { k = keys[i]; this.set(k, options[k]); } } return this._options; }
...
var initialFields;
// Inherit default options from parent list if it exists
if (options && options.inherits) {
if (options.inherits.options && options.inherits.options.inherits) {
throw new Error('Inherited Lists may not contain any inheritance');
}
defaultOptions = utils.options(defaultOptions, options.inherits.options);
if (options.inherits.options.track) {
options.track = false;
}
}
this.options = utils.options(defaultOptions, options);
...
expandPath = function (pathValue) { pathValue = (typeof pathValue === 'string' && pathValue.substr(0, 1) !== path.sep && pathValue.substr(1, 2) !== ':\\') ? path.join(this.get('module root'), pathValue) : pathValue; return pathValue; }
...
if (typeof lessPaths === 'string') {
lessPaths = [lessPaths];
}
if (Array.isArray(lessPaths)) {
lessPaths.forEach(function (path) {
app.use(require('less-middleware')(keystone.expandPath(path), lessOptions
));
});
}
};
...
get = function (key, value) { if (arguments.length === 1) { return this._options[key]; } switch (key) { // throw on unsupported options case 'email rules': throw new Error('The option "' + key + '" is no longer supported. See https://github.com/keystonejs/keystone/wiki/0.3.x-to-0. 4.x-Changes'); // handle special settings case 'cloudinary config': var cloudinary = require('cloudinary'); if (typeof value === 'string') { var parts = url.parse(value, true); var auth = parts.auth ? parts.auth.split(':') : []; value = { cloud_name: parts.host, api_key: auth[0], api_secret: auth[1], private_cdn: parts.pathname != null, secure_distribution: parts.pathname && parts.pathname.substring(1), }; } cloudinary.config(value); value = cloudinary.config(); break; case 'auth': if (value === true && !this.get('session')) { this.set('session', true); } break; case 'nav': this.nav = this.initNav(value); break; case 'mongo': if (typeof value !== 'string') { if (Array.isArray(value) && (value.length === 2 || value.length === 3)) { console.log('\nWarning: using an array for the `mongo` option has been deprecated.\nPlease use a mongodb connection string, e.g. mongodb://localhost/db_name instead.\n\n' + 'Support for arrays as the `mongo` setting will be removed in a future version.'); value = (value.length === 2) ? 'mongodb://' + value[0] + '/' + value[1] : 'mongodb://' + value[0] + ':' + value[2] + '/' + value[1]; } else { console.error('\nInvalid Configuration:\nThe `mongo` option must be a mongodb connection string, e.g. mongodb://localhost/db_name\n'); process.exit(1); } } break; case 'module root': // if relative path is used, resolve it based on the caller's path if (!isAbsolutePath(value)) { var caller = callerId.getData(); value = path.resolve(path.dirname(caller.filePath), value); } break; case 'app': this.app = value; break; case 'mongoose': this.mongoose = value; break; case 'frame guard': var validFrameGuardOptions = ['deny', 'sameorigin']; if (value === true) { value = 'deny'; } if (typeof value === 'string') { value = value.toLowerCase(); if (validFrameGuardOptions.indexOf(value) < 0) { value = false; } } else if (typeof value !== 'boolean') { value = false; } break; } this._options[key] = value; return this; }
...
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT || '3001');
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development
') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
...
getPath = function (key, defaultValue) { return this.expandPath(this.get(key) || defaultValue); }
...
exports.apply = function (callback) {
var Update = mongoose.model('App_Update');
var updateCount = 0;
var deferCount = 0;
var skipCount = 0;
var updatesPath = keystone.getPath('updates', 'updates');
// logError is used to log errors before the process exits since it is more synchronous than console.error. Using
// console.error gets into race condition issues with process.exit, which has higher priority.
var logError = function () {
for (var i = 0, len = arguments.length; i < len; ++i) {
process.stderr.write(arguments[i] + '\n');
}
...
set = function (key, value) { if (arguments.length === 1) { return this._options[key]; } switch (key) { // throw on unsupported options case 'email rules': throw new Error('The option "' + key + '" is no longer supported. See https://github.com/keystonejs/keystone/wiki/0.3.x-to-0. 4.x-Changes'); // handle special settings case 'cloudinary config': var cloudinary = require('cloudinary'); if (typeof value === 'string') { var parts = url.parse(value, true); var auth = parts.auth ? parts.auth.split(':') : []; value = { cloud_name: parts.host, api_key: auth[0], api_secret: auth[1], private_cdn: parts.pathname != null, secure_distribution: parts.pathname && parts.pathname.substring(1), }; } cloudinary.config(value); value = cloudinary.config(); break; case 'auth': if (value === true && !this.get('session')) { this.set('session', true); } break; case 'nav': this.nav = this.initNav(value); break; case 'mongo': if (typeof value !== 'string') { if (Array.isArray(value) && (value.length === 2 || value.length === 3)) { console.log('\nWarning: using an array for the `mongo` option has been deprecated.\nPlease use a mongodb connection string, e.g. mongodb://localhost/db_name instead.\n\n' + 'Support for arrays as the `mongo` setting will be removed in a future version.'); value = (value.length === 2) ? 'mongodb://' + value[0] + '/' + value[1] : 'mongodb://' + value[0] + ':' + value[2] + '/' + value[1]; } else { console.error('\nInvalid Configuration:\nThe `mongo` option must be a mongodb connection string, e.g. mongodb://localhost/db_name\n'); process.exit(1); } } break; case 'module root': // if relative path is used, resolve it based on the caller's path if (!isAbsolutePath(value)) { var caller = callerId.getData(); value = path.resolve(path.dirname(caller.filePath), value); } break; case 'app': this.app = value; break; case 'mongoose': this.mongoose = value; break; case 'frame guard': var validFrameGuardOptions = ['deny', 'sameorigin']; if (value === true) { value = 'deny'; } if (typeof value === 'string') { value = value.toLowerCase(); if (validFrameGuardOptions.indexOf(value) < 0) { value = false; } } else if (typeof value !== 'boolean') { value = false; } break; } this._options[key] = value; return this; }
...
Alternatively, to include Keystone in an existing project or start from scratch (without Yeoman), specify `keystone: "^0.3.
9"` in the `dependencies` array of your `package.json` file, and run `npm install` from your terminal.
Then read through the [Documentation](http://keystonejs.com/docs) and the [Example Projects](http://keystonejs.com/examples) to
understand how to use it.
### Configuration
Config variables can be passed in an object to the `keystone.init` method, or can be set any time before `keystone.start` is called
using `keystone.set(key, value)`. This allows for a more flexible order of execution
(e.g. if you refer to Lists in your routes, you can set the routes after configuring your Lists, as in the example above).
See the [KeystoneJS configuration documentation](http://keystonejs.com/docs/configuration) for details and examples of the available
configuration options.
### Database field types
Keystone builds on the basic data types provided by mongo and allows you to easily add rich, functional fields to your application
's models.
...
function Page(key, options) { if (!(this instanceof Page)) { return new Page(key, options); } this.options = assign({}, options); this.key = key; this.fields = {}; }
...
* ####Example:
*
* var homePage = new keystone.content.Page('home');
* // ...
* homePage.register();
*
* // later...
* var homePage = keystone.content.page('home');
*
* @api public
*/
Page.prototype.register = function () {
return keystone.content.page(this.key, this);
};
...
add = function (fields) { // TODO: nested paths if (!utils.isObject(fields)) { throw new Error('keystone.content.Page.add() Error: fields must be an object.'); } var self = this; _.forEach(fields, function (options, path) { if (typeof options === 'function') { options = { type: options }; } if (typeof options.type !== 'function') { throw new Error('keystone.content.page.add() Error: Page fields must be specified with a type function'); } if (options.type.prototype.__proto__ !== Type.prototype) { // eslint-disable-line no-proto // Convert native field types to their default Keystone counterpart if (options.type === String) { options.type = keystone.content.Types.Text; } // TODO: More types // else if (options.type == Number) // options.type = Field.Types.Number; // else if (options.type == Boolean) // options.type = Field.Types.Boolean; // else if (options.type == Date) // options.type = Field.Types.Datetime; else { throw new Error('keystone.content.page.add() Error: Unrecognised field constructor: ' + options.type); } } self.fields[path] = new options.type(path, options); }); return this; }
...
* @api public
*/
Page.prototype.add = function (fields) {
// TODO: nested paths
if (!utils.isObject(fields)) {
throw new Error('keystone.content.Page.add() Error: fields must be an object.
x27;);
}
var self = this;
_.forEach(fields, function (options, path) {
if (typeof options === 'function') {
...
clean = function (data) { if (typeof data !== 'object') { data = {}; } // TODO: implement schema return data; }
n/a
get = function (key) { if (!key) { throw new Error('keystone.content.Paget.get() Error: must be provided with a key to get a value.'); } if (!this.options.hasOwnProperty(key)) { return null; } return this.options[key]; }
...
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT || '3001');
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development
') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
...
populate = function (data) { if (typeof data !== 'object') { data = {}; } // TODO: implement schema return data; }
...
}
populate[col.populate.path].push(col.populate.subpath);
}
});
q.select(select.join(' '));
for (path in populate) {
if (populate.hasOwnProperty(path)) {
q.populate(path, populate[path].join(' '));
}
}
}
module.exports = selectColumns;
...
register = function () { return keystone.content.page(this.key, this); }
...
/**
* Registers the page with Keystone.
*
* ####Example:
*
* var homePage = new keystone.content.Page('home');
* // ...
* homePage.register();
*
* // later...
* var homePage = keystone.content.page('home');
*
* @api public
*/
...
set = function (key, value) { if (!key) { throw new Error('keystone.content.Page.set() Error: must be provided with a key to set a value.'); } value = value || null; this.options[key] = value; return value; }
...
Alternatively, to include Keystone in an existing project or start from scratch (without Yeoman), specify `keystone: "^0.3.
9"` in the `dependencies` array of your `package.json` file, and run `npm install` from your terminal.
Then read through the [Documentation](http://keystonejs.com/docs) and the [Example Projects](http://keystonejs.com/examples) to
understand how to use it.
### Configuration
Config variables can be passed in an object to the `keystone.init` method, or can be set any time before `keystone.start` is called
using `keystone.set(key, value)`. This allows for a more flexible order of execution
(e.g. if you refer to Lists in your routes, you can set the routes after configuring your Lists, as in the example above).
See the [KeystoneJS configuration documentation](http://keystonejs.com/docs/configuration) for details and examples of the available
configuration options.
### Database field types
Keystone builds on the basic data types provided by mongo and allows you to easily add rich, functional fields to your application
's models.
...
validate = function (data) { if (typeof data !== 'object') { data = {}; } // TODO: implement schema return data; }
...
// Allow environment variable to disable check
if (DISABLE_CSRF) return next();
// Bail on safe methods
if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') {
return next();
}
// Validate token
if (exports.validate(req)) {
next();
} else {
res.statusCode = 403;
next(new Error('CSRF token mismatch'));
}
},
};
...
upload = function (req, res) { var knox = require('knox'); var keystone = req.keystone; var Types = keystone.Field.Types; if (!keystone.security.csrf.validate(req, req.body.authenticity_token)) { return res.status(403).send({ error: { message: 'invalid csrf' } }); } if (req.files && req.files.file) { var s3Config = keystone.get('s3 config'); var file = req.files.file; var path = s3Config.s3path ? s3Config.s3path + '/' : ''; var headers = Types.S3File.prototype.generateHeaders.call({ s3config: s3Config, options: {} }, null, file); var s3Client = knox.createClient(s3Config); s3Client.putFile(file.path, path + file.name, headers, function (err, s3Response) { var sendResult = function () { if (err) { return res.send({ error: { message: err.message } }); } if (s3Response) { if (s3Response.statusCode !== 200) { return res.send({ error: { message: 'Amazon returned Http Code: ' + s3Response.statusCode } }); } else { var region = 's3'; if (s3Config.region && s3Config.region !== 'us-east-1') { region = 's3-' + s3Config.region; } return res.send({ image: { url: 'https://' + region + '.amazonaws.com/' + s3Config.bucket + '/' + file.name } }); } } }; res.format({ html: sendResult, json: sendResult, }); }); } else { res.json({ error: { message: 'No image selected' } }); } }
...
if (req.files && req.files.file) {
var options = {};
if (keystone.get('wysiwyg cloudinary images filenameAsPublicID')) {
options.public_id = req.files.file.originalname.substring(0, req.files.file.originalname.lastIndexOf('.'));
}
cloudinary.uploader.upload(req.files.file.path, function (result) {
var sendResult = function () {
if (result.error) {
res.send({ error: { message: result.error.message } });
} else {
res.send({ image: { url: (keystone.get('cloudinary secure') === true) ? result.secure_url : result.url } });
}
};
...
function autokey() { var autokey = this.autokey = _.clone(this.get('autokey')); var def = {}; var list = this; if (!autokey.from) { var fromMsg = 'Invalid List Option (autokey) for ' + list.key + ' (from is required)\n'; throw new Error(fromMsg); } if (!autokey.path) { var pathMsg = 'Invalid List Option (autokey) for ' + list.key + ' (path is required)\n'; throw new Error(pathMsg); } if (typeof autokey.from === 'string') { autokey.from = autokey.from.split(' '); } autokey.from = autokey.from.map(function (i) { i = i.split(':'); return { path: i[0], format: i[1] }; }); def[autokey.path] = { type: String, index: true, }; if (autokey.unique) { def[autokey.path].index = { unique: true }; } this.schema.add(def); var getUniqueKey = function (doc, src, callback) { var q = list.model.find().where(autokey.path, src); if (_.isObject(autokey.unique)) { _.forEach(autokey.unique, function (k, v) { if (typeof v === 'string' && v.charAt(0) === ':') { q.where(k, doc.get(v.substr(1))); } else { q.where(k, v); } }); } q.exec(function (err, results) { if (err) { callback(err); // deliberate use of implicit type coercion with == because doc.id may need to become a String } else if (results.length && (results.length > 1 || results[0].id != doc.id)) { // eslint-disable-line eqeqeq var inc = src.match(/^(.+)\-(\d+)$/); if (inc && inc.length === 3) { src = inc[1]; inc = '-' + ((inc[2] * 1) + 1); } else { inc = '-1'; } return getUniqueKey(doc, src + inc, callback); } else { doc.set(autokey.path, src); return callback(); } }); }; this.schema.pre('save', function (next) { var modified = false; var incomplete = false; var values = []; autokey.from.forEach(function (ops) { if (list.fields[ops.path]) { values.push(list.fields[ops.path].format(this, ops.format)); if (list.fields[ops.path].isModified(this)) { modified = true; } // if source field is neither selected nor modified we don't have a way to generate a complete autokey else if (!this.isSelected(ops.path)) { incomplete = true; } } else { values.push(this.get(ops.path)); // virtual paths are always assumed to have changed, except 'id' if (ops.path !== 'id' && list.schema.pathType(ops.path) === 'virtual' || this.isModified(ops.path)) { modified = true; } } }, this); // if source fields are not completely selected or set, skip generation unless told to ignore the condition if (incomplete && !autokey.ingoreIncompleteSource) { return next(); } // if has a value and is unmodified or fixed, don't update it if ((!modified || autokey.fixed) && (this.get(autokey.path) || !this.isSelected(autokey.path))) { return next(); } var newKey = utils.slug(values.join(' '), null, { locale: autokey.locale }) || this.id; if (autokey.unique) { return getUniqueKey(this, newKey, next); } else { this.set(autokey.path, newKey); return next(); } }); }
n/a
function history() { var list = this; // If model already exists for a '_revisions' in an inherited model, log a warning but skip creating the new model (inherited _revisions model will be used). var collectionName = getHistoryModelName(list); if (list.get('inherits') && collectionName.indexOf(historyModelSuffix, collectionName.length - historyModelSuffix.length) !== -1 && keystone.mongoose.models[collectionName]) { console.log('List/model already exists for ' + collectionName + '.\nWon\'t re-create, keystone continuing.'); return; } var userModel = keystone.get('user model'); var HistoryModel = list.HistoryModel = getHistoryModel(this, userModel); list.schema.add({ __rev: Number, }); list.schema.pre('save', function (next) { this.__rev = (typeof this.__rev === 'number') ? this.__rev + 1 : 1; var data = this.toObject(); delete data._id; delete data.__v; delete data.__rev; var doc = { i: this.id, t: Date.now(), o: this.isNew ? 'c' : 'u', c: [], d: data, }; for (var path in list.fields) { if (this.isModified(path)) { doc.c.push(path); } } if (list.autokey) { if (this.isModified(list.autokey.path)) { doc.c.push(list.autokey.path); } } if (userModel && this._req_user) { doc.u = this._req_user; } new HistoryModel(doc).save(next); }); list.schema.pre('remove', function (next) { var data = this.toObject(); data.__v = undefined; var doc = { t: Date.now(), o: 'd', d: data, }; if (userModel && this._req_user) { doc.u = this._req_user; } new HistoryModel(doc).save(next); }); }
n/a
function sortable() { var list = this; this.add({ sortOrder: { type: Number, index: true, hidden: true }, }); this.schema.pre('save', function (next) { if (typeof this.sortOrder === 'number') { return next(); } var item = this; var addLast = function (done) { list.model.findOne().sort('-sortOrder').exec(function (err, max) { // eslint-disable-line no-unused-vars, handle-callback-err item.sortOrder = (max && max.sortOrder) ? max.sortOrder + 1 : 1; done(); }); }; if (list.get('sortable') === 'unshift') { list.model.where('sortOrder') .setOptions({ multi: true }) .update( { $inc: { sortOrder: 1 } }, function (err) { if (err) { console.log('err', err); return addLast(next); } item.sortOrder = 1; next(); } ); } else { addLast(next); } }); this.schema.statics.reorderItems = function reorderItems (id, prevOrder, newOrder, cb) { prevOrder = parseFloat(prevOrder); newOrder = parseFloat(newOrder); var whichWay = (newOrder > prevOrder) ? -1 : 1; var gte = (newOrder > prevOrder) ? prevOrder + 1 : newOrder; var lte = (newOrder > prevOrder) ? newOrder : prevOrder - 1; return list.model .where('sortOrder') .gte(gte) .lte(lte) .setOptions({ multi: true }) .update({ $inc: { sortOrder: whichWay } }, function (err) { if (err) { console.log('err', err); } list.model.findOneAndUpdate({ _id: id }, { sortOrder: newOrder }).exec(cb); }); }; }
n/a
function track() { var list = this; var options = list.get('track'); var userModel = keystone.get('user model'); if (!options) { // if the track setting is falsy, bail return; } var defaultOptions = { createdAt: false, createdBy: false, updatedAt: false, updatedBy: false, }; var fields = {}; // ensure track is a boolean or an object if (!_.isBoolean(options) && !_.isObject(options)) { throw new Error('Invalid List "track" option for ' + list.key + '\n' + '"track" must be a boolean or an object.\n\n' + 'See http://keystonejs.com/docs/database/#lists-options for more information.'); } if (_.isBoolean(options)) { // shorthand: { track: true } sets all tracked fields to true options = { createdAt: true, createdBy: true, updatedAt: true, updatedBy: true, }; } // if all track fields are set to false, then there's nothing to track if (!options.createdAt && !options.createdBy && !options.updatedAt && !options.updatedBy) { return; } // merge user options with default options options = _.extend({}, defaultOptions, options); // validate option fields _.forEach(options, function (value, key) { var fieldName; // make sure the key isn't already defined as a field if (_.has(list.fields, key)) { throw new Error('Invalid List "track" option for ' + list.key + '\n' + '"' + key + '" is already defined in the Schema.'); } // make sure it's a valid track option field if (_.has(defaultOptions, key)) { // make sure the option field value is either a boolean or a string if (!_.isBoolean(value) && typeof value !== 'string') { throw new Error('Invalid List "track" option for ' + list.key + '\n' + '"' + key + '" must be a boolean or a string.\n\n' + 'See http://keystonejs.com/docs/database/#lists-options for more information.'); } if (value) { // determine fieldName = value === true ? key : value; options[key] = fieldName; list.map(key, fieldName); switch (key) { case 'createdAt': case 'updatedAt': fields[fieldName] = { type: Date, noedit: true, collapse: true, index: true }; break; case 'createdBy': case 'updatedBy': fields[fieldName] = { type: Types.Relationship, ref: userModel, noedit: true, collapse: true, index: true }; break; } } } else { throw new Error('Invalid List "track" option for ' + list.key + '\n' + 'valid field options are "createdAt", "createdBy", "updatedAt", an "updatedBy".\n\n' + 'See http://keystonejs.com/docs/database/#lists-options for more information.'); } }); // add track fields to schema list.add('Meta', fields); list.tracking = options; // add the pre-save schema plugin list.schema.pre('save', function (next) { var now = new Date(); // set createdAt/createdBy on new docs if (this.isNew) { if (options.createdAt && !this.get(options.createdAt)) { this.set(options.createdAt, now); } if (options.createdBy && this._req_user && !this.get(options.createdBy)) { this.set(options.createdBy, this._req_user._id); } } // set updatedAt/updatedBy when doc is modified if (this.isNew || this.isModified()) { if (options.updatedAt) { this.set(options.updatedAt, now); } if (options.updatedBy && this._req_user) { this.set(options.updatedBy, this._req_user._id); } } next(); }); }
n/a
keystoneAuth = function (req, res, next) { if (!req.user || !req.user.canAccessKeystone) { if (req.headers.accept === 'application/json') { return req.user ? res.status(403).json({ error: 'not authorised' }) : res.status(401).json({ error: 'not signed in' }); } var regex = new RegExp('^\/' + keystone.get('admin path') + '\/?$', 'i'); var from = regex.test(req.originalUrl) ? '' : '?from=' + req.originalUrl; return res.redirect(keystone.get('signin url') + from); } next(); }
n/a
persist = function (req, res, next) { var User = keystone.list(keystone.get('user model')); if (!req.session) { console.error('\nKeystoneJS Runtime Error:\n\napp must have session middleware installed. Try adding "express-session" to your express instance.\n'); process.exit(1); } if (keystone.get('cookie signin') && !req.session.userId && req.signedCookies['keystone.uid'] && req.signedCookies['keystone.uid '].indexOf(':') > 0) { exports.signin(req.signedCookies['keystone.uid'], req, res, function () { next(); }, function (err) { var cookieOpts = _.defaults({}, keystone.get('cookie signin options'), { signed: true, httpOnly: true, }); // Force the cookie to expire! cookieOpts.maxAge = 0; res.clearCookie('keystone.uid', cookieOpts); req.user = null; next(); }); } else if (req.session.userId) { User.model.findById(req.session.userId).exec(function (err, user) { if (err) return next(err); req.user = user; next(); }); } else { next(); } }
n/a
signin = function (lookup, req, res, onSuccess, onFail) { keystone.callHook({}, 'pre:signin', function (err) { if (err) { return onFail(err); } doSignin(lookup, req, res, onSuccess, onFail); }); }
n/a
function signinWithUser(user, req, res, onSuccess) { if (arguments.length < 4) { throw new Error('keystone.session.signinWithUser requires user, req and res objects, and an onSuccess callback.'); } if (typeof user !== 'object') { throw new Error('keystone.session.signinWithUser requires user to be an object.'); } if (typeof req !== 'object') { throw new Error('keystone.session.signinWithUser requires req to be an object.'); } if (typeof res !== 'object') { throw new Error('keystone.session.signinWithUser requires res to be an object.'); } if (typeof onSuccess !== 'function') { throw new Error('keystone.session.signinWithUser requires onSuccess to be a function.'); } req.session.regenerate(function () { req.user = user; req.session.userId = user.id; // if the user has a password set, store a persistence cookie to resume sessions if (keystone.get('cookie signin') && user.password) { var userToken = user.id + ':' + hash(user.password); var cookieOpts = _.defaults({}, keystone.get('cookie signin options'), { signed: true, httpOnly: true, maxAge: 10 * 24 * 60 * 60, }); res.cookie('keystone.uid', userToken, cookieOpts); } onSuccess(user); }); }
n/a
signout = function (req, res, next) { keystone.callHook(req.user, 'pre:signout', function (err) { if (err) { console.log("An error occurred in signout 'pre' middleware", err); } var cookieOpts = _.defaults({}, keystone.get('cookie signin options'), { signed: true, httpOnly: true, }); // Force the cookie to expire! cookieOpts.maxAge = 0; res.clearCookie('keystone.uid', cookieOpts); req.user = null; req.session.regenerate(function (err) { if (err) { return next(err); } keystone.callHook({}, 'post:signout', function (err) { if (err) { console.log("An error occurred in signout 'post' middleware", err); } next(); }); }); }); }
...
var session = require('../../../lib/session');
module.exports = function SignoutRoute (req, res) {
var keystone = req.keystone;
session.signout(req, res, function () {
// After logging out, the user will be redirected to /signin?signedout
// It shows a bar on top of the sign in panel saying "You have been signed out".
if (typeof keystone.get('signout redirect') === 'string') {
return res.redirect(keystone.get('signout redirect'));
} else if (typeof keystone.get('signout redirect') === 'function') {
return keystone.get('signout redirect')(req, res);
} else {
...
function text(path, options) { text.super_.call(path, options); }
...
btnClass = button.btnClass ? button.btnClass : 'md-editor__btn',
tabIndex = button.tabIndex ? button.tabIndex : '-1',
hotkey = typeof button.hotkey !== 'undefined' ? button.hotkey : '',
hotkeyCaption = typeof jQuery.hotkeys !== 'undefined' && hotkey !== '' ? ' (' +
hotkey + ')' : '';
// Construct the button object
buttonContainer = $('<button></button>');
buttonContainer.text(' ' + this.__localize(btnText)).addClass('md
-editor__btn').addClass(btnClass);
if (btnClass.match(/md-editor__btn\--(primary|success|info|warning|danger|link)/)) {
buttonContainer.removeClass('md-editor__btn');
}
buttonContainer.attr({
'type': 'button',
'title': this.__localize(button.title) + hotkeyCaption,
'tabindex': tabIndex,
...
super_ = function (path, options) { // eslint-disable-line no-unused-vars // TODO }
n/a
function UpdateHandler(list, item, req, options) { this.list = list; this.item = item; this.req = req; if (arguments.length === 5) { // old signature; set options to the last argument options = arguments[4]; } this.options = options || {}; }
n/a
process = function (data, options, callback) { // options is not required if (typeof options === 'function') { callback = options; options = {}; } var item = this.item; var list = this.list; var req = this.req; options = assign({}, this.options, options); // pass the user from the request by default if (!options.user) { options.user = req && req.user; } // pass the uploaded files object from the request by default if (!options.files) { options.files = req && req.files; } function flashErrors (err) { var errorMessage = options.errorMessage || 'There was a problem saving your changes'; // Detect Keystone validation errors if (err.error === 'validation errors') { if (options.flashErrors === true || options.flashErrors === 'validation') { req.flash('error', { type: 'ValidationError', title: errorMessage, // TODO: Apply custom invalidMessages / requiredMessages list: _.map(err.detail, 'error'), }); } } // Detect Mongoose validation errors else if (err.name === 'ValidationError') { if (options.flashErrors === true || options.flashErrors === 'validation') { req.flash('error', { type: 'ValidationError', title: errorMessage, list: _.map(err.errors, 'message'), }); } } // Detect Keystone field update errors else if (err.error === 'field errors') { if (options.flashErrors === true || options.flashErrors === 'update') { req.flash('error', { type: 'UpdateError', title: errorMessage, list: _.map(err.detail, 'error'), }); } } // Fallback else { if (options.flashErrors === true || options.flashErrors === 'update') { var errors = []; if (typeof err.error === 'string') { errors.push(err.error); } if (typeof err.detail === 'string') { errors.push(err.detail); } req.flash('error', { type: 'UpdateError', title: errorMessage, list: errors.length ? errors : undefined, }); } } } this.list.updateItem(this.item, data, options, function (err) { if (err) { if (options.logErrors) { console.log('Error saving changes to ' + list.singular + ' ' + item.id + ':', err); } if (options.flashErrors) { flashErrors(err); } } callback(err); }); }
n/a
apply = function (callback) { var Update = mongoose.model('App_Update'); var updateCount = 0; var deferCount = 0; var skipCount = 0; var updatesPath = keystone.getPath('updates', 'updates'); // logError is used to log errors before the process exits since it is more synchronous than console.error. Using // console.error gets into race condition issues with process.exit, which has higher priority. var logError = function () { for (var i = 0, len = arguments.length; i < len; ++i) { process.stderr.write(arguments[i] + '\n'); } }; var applyUpdate = function (file, done) { Update.findOne({ key: file }, function (err, updateRecord) { if (err) { console.error('Error searching database for update ' + file + ':'); console.dir(err); done(err); } else if (!updateRecord) { var update = require(path.join(updatesPath, file)); // skip updates that export a falsy value if (!update) { skipCount++; return done(); } // auto-wrap create scripts for a friendlier shorthand syntax if (_.isObject(update.create)) { var items = update.create; var ops = update.options || {}; var background_mode = update.__background__ ? ' (background mode) ' : ''; update = function (done) { keystone.createItems(items, ops, function (err, stats) { if (!err) { var statsMsg = stats ? stats.message : ''; console.log('\n' + _dashes_, '\n' + keystone.get('name') + ': Successfully applied update ' + file + background_mode + '.', '\n' + statsMsg, '\n'); done(null); } else { logError('\n' + _dashes_, '\n' + keystone.get('name') + ': Update ' + file + background_mode + ' failed with errors:', '\n' + err, '\n'); // give the logging some time to finish process.nextTick(function () { done(err); }); } }); }; } // ensure type if (typeof update !== 'function') { console.log('\nError in update file ./updates/' + file + '.js\nUpdate files must export a function\n'); process.exit(); } // if an update is deferred, don't process it if (update.__defer__) { deferCount++; return done(); } // if there are deferred updates, don't process any subsequent ones if (deferCount) { skipCount++; return done(); } console.log(_dashes_ + '\nApplying update ' + file + '...'); if (update.__background__) { updateCount++; update(function (err) { if (!err) { if (update.__commit__ !== false) { new Update({ key: file }).save(); } } else { done(err); } }); done(); } else { update(function (err) { if (!err) { updateCount++; if (update.__commit__ === false) { done(); } else { new Update({ key: file }).save(done); } } else { done(err); } }); } } else { done(); } }); }; if (!fs.existsSync(updatesPath)) { console.log('\nKeystoneJS Update Error:\n\n' + 'An updates folder must exist in your project root to use automatic updates.\n' + 'If you want to use a custom path for your updates, set the `updates` option.\n' + 'If you don\'t want to use updates, set the `auto update` option to `false`.\n' + 'See http://keystonejs.com/docs/configuration/#updates for more information.\n'); process.exit(); } var updates = fs.readdirSync(updatesPath) .map(function (i) { // exclude non-javascript or coffee files in the updates folder return (path.extname(i) !== '.js' && path.extname(i) !== '.coffee') ? false : path.basename(i, '.js'); }).filter(function (i) { // exclude falsy values and filenames that without a valid semver return i && semver.valid(i.split('-')[0]); }).sort(function (a, b) { // exclude anything after a hyphen from the version number return semver.compare(a.split('-')[0], b.split('-')[0]); }); async.eachSeries(updates, applyUpdate, function (err) { if (updateCount || deferCou ...
...
* Applies Application updates
*/
Keystone.prototype.applyUpdates = function (callback) {
var self = this;
self.callHook('pre:updates', function (err) {
if (err) return callback(err);
require('./lib/updates').apply(function (err) {
if (err) return callback(err);
self.callHook('post:updates', callback);
});
});
};
...
function bindMethods(obj, scope) { var bound = {}; for (var prop in obj) { if (typeof obj[prop] === 'function') { bound[prop] = obj[prop].bind(scope); } else if (isObject(obj[prop])) { bound[prop] = bindMethods(obj[prop], scope); } } return bound; }
...
this.schema.methods.getRelated = schemaPlugins.methods.getRelated;
this.schema.methods.populateRelated = schemaPlugins.methods.populateRelated;
if (!this.schema.options.toObject) this.schema.options.toObject = {};
this.schema.options.toObject.transform = schemaPlugins.options.transform;
}
this.schema.virtual('_').get(function () {
if (!this.__methods) {
this.__methods = utils.bindMethods(list.underscoreMethods, this);
}
return this.__methods;
});
this.schema.method('getUpdateHandler', function (req, res, ops) {
return new UpdateHandler(list, this, req, res, ops);
});
/* Apply list inheritance */
...
function calculateDistance(point1, point2) { var dLng = (point2[0] - point1[0]) * Math.PI / 180; var dLat = (point2[1] - point1[1]) * Math.PI / 180; var lat1 = (point1[1]) * Math.PI / 180; var lat2 = (point2[1]) * Math.PI / 180; var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLng / 2) * Math.sin(dLng / 2) * Math.cos(lat1) * Math.cos(lat2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return c; }
n/a
function camelcase(str, lc) { return inflect.camelize(str, !(lc)); }
n/a
function cropHTMLString(str, length, append, preserveWords) { return textToHTML(cropString(htmlToText(str), length, append, preserveWords)); }
n/a
function cropString(str, length, append, preserveWords) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; if (typeof append === 'boolean') { preserveWords = append; append = null; } str = String(str); if (str.length <= length) return str; var cropTo = length; if (preserveWords) { var r = str.substr(cropTo); var word = r.match(/^\w+/); if (word && word.length) { cropTo += word[0].length; } } var rtn = str.substr(0, cropTo); return (rtn.length < str.length && append) ? rtn + append : rtn; }
...
return query;
};
/**
* Crops the string to the specifed length.
*/
text.prototype.crop = function (item, length, append, preserveWords) {
return utils.cropString(item.get(this.path), length, append, preserveWords);
};
/* Export Field Type */
module.exports = text;
...
function decodeHTMLEntities(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; return str.replace(/&([^;&\s]+);/g, function (match, ent) { return String.fromCharCode(ent[0] !== '#' ? htmlEntities[ent] : ent[1] === 'x' ? parseInt(ent.substr(2), 16) : parseInt(ent.substr (1), 10)); }); }
n/a
function defer(fn) { var args = Array.prototype.slice.call(arguments, 1); process.nextTick(function () { fn.apply(null, args); }); }
...
if (value !== undefined
&& value !== null
&& typeof value !== 'string'
&& typeof value !== 'number'
&& typeof value !== 'boolean') {
result = false;
}
utils.defer(callback, result);
};
boolean.prototype.validateRequiredInput = function (item, data, callback) {
var value = this.getValueFromData(data);
var result = (value && value !== 'false') || item.get(this.path) ? true : false;
utils.defer(callback, result);
};
...
function downcase(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; return (str.substr(0, 1).toLowerCase() + str.substr(1)); }
...
if (typeof def === 'string') {
def = { ref: def };
}
if (!def.ref) {
throw new Error('List Relationships must be specified with an object containing ref (' + this.key + ')');
}
if (!def.refPath) {
def.refPath = utils.downcase(this.key);
}
if (!def.path) {
def.path = utils.keyToProperty(def.ref, true);
}
Object.defineProperty(def, 'refList', {
get: function () {
return keystone.list(def.ref);
...
function encodeHTMLEntities(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; return str.replace(htmlEntitiesRegExp, function (match) { return '&' + htmlEntitiesMap[match] + ';'; }); }
...
* @param {Boolean} escape - causes HTML entities to be encoded
*/
function getDocumentName (doc, escape) {
// console.log('getting document name for ' + doc.id, 'nameField: ' + this.nameField, 'namePath: '
; + this.namePath);
// console.log('raw name value: ', doc.get(this.namePath));
// if (this.nameField) console.log('formatted name value: ', this.nameField.format(doc));
var name = String(this.nameField ? this.nameField.format(doc) : doc.get(this.namePath));
return (escape) ? utils.encodeHTMLEntities(name) : name;
}
module.exports = getDocumentName;
...
function escapeRegExp(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); }
...
$text: {
$search: searchString,
},
});
if (this.autokey) {
var strictAutokeyFilter = {};
var autokeyRegExp = new RegExp('^' + utils.escapeRegExp(searchString));
strictAutokeyFilter[this.autokey.path] = autokeyRegExp;
searchFilters.push(strictAutokeyFilter);
}
} else {
debug('Using regular expression search for value: "' + searchString + '"');
var searchRegExp = new RegExp(utils.escapeRegExp(searchString), 'i');
searchFilters = this.searchFields.map(function (i) {
...
function escapeString(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; return str.replace(/[\\'"]/g, '\\$&'); }
n/a
function htmlStringify(obj, fromRecur) { var tag = (fromRecur) ? 'span' : 'div'; var nextLevel = (fromRecur || 0) + 1; // strings if (typeof obj == 'string') { return '<' + tag + ' style="color: #0e4889; cursor: default;">"' + obj + '"</' + tag + '>'; } // booleans, null and undefined else if (typeof obj == 'boolean' || obj === null || obj === undefined) { return '<' + tag + '><em style="color: #06624b; cursor: default;">' + obj + '</em></' + tag + '>'; } // numbers else if (typeof obj == 'number') { return '<' + tag + ' style="color: #ca000a; cursor: default;">' + obj + '</' + tag + '>'; } // dates else if (Object.prototype.toString.call(obj) == '[object Date]') { return '<' + tag + ' style="color: #009f7b; cursor: default;">' + obj + '</' + tag + '>'; } // arrays else if (Array.isArray(obj)) { var rtn = '<' + tag + ' style="color: #666; cursor: default;">Array: ['; if (!obj.length) { return rtn + ']</' + tag + '>'; } rtn += '</' + tag + '><div style="padding-left: 20px;">'; for (var i = 0; i < obj.length; i++) { rtn += '<span></span>' + htmlStringify(obj[i], nextLevel); // give the DOM structure has as many elements as an object, for collapse behaviour if (i < obj.length - 1) { rtn += ', <br>'; } } return rtn + '</div><' + tag + ' style="color: #666">]</' + tag + '>'; } // objects else if (obj && typeof obj == 'object') { var rtn = '', len = Object.keys(obj).length; if (fromRecur && !len) { return '<' + tag + ' style="color: #999; cursor: default;">Object: {}</' + tag + '>'; } if (fromRecur) { rtn += '<' + tag + ' style="color: #0b89b6">Object: {</' + tag + '><div class="_stringify_recur _stringify_recur_level_' + fromRecur + '" style="padding-left: 20px;">'; } for (var key in obj) { if (typeof obj[key] != 'function') { rtn += '<div><span style="padding-right: 5px; cursor: default;">' +key + ':</span>' + htmlStringify(obj[key], nextLevel) + '</ div>'; } } if (fromRecur) { rtn += '</div><' + tag + ' style="color: #0b89b6; cursor: default;">}</' + tag + '>'; } return rtn; } return ''; }
n/a
function htmlToText(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; // remove all source-code line-breaks first str = str.replace(/\n/g, ''); // turn non-breaking spaces into normal spaces str = str.replace(/ /g, ' '); // <br> tags become single line-breaks str = str.replace(/<br>/gi, '\n'); // <p>, <li>, <td> and <th> tags become double line-breaks str = str.replace(/<(?:p|li|td|th)[^>]*>/gi, '\n'); // strip all other tags (including closing tags) str = str.replace(/<[^>]*>/g, ''); // compress white space str = str.replace(/(\s)\s+/g, '$1'); // remove leading or trailing spaces str = str.replace(/^\s+|\s+$/g, ''); return decodeHTMLEntities(str); }
n/a
function isArray(arg) { return Array.isArray(arg); }
...
var lessPaths = keystone.get('less');
var lessOptions = keystone.get('less options') || {};
if (typeof lessPaths === 'string') {
lessPaths = [lessPaths];
}
if (Array.isArray(lessPaths)) {
lessPaths.forEach(function (path) {
app.use(require('less-middleware')(keystone.expandPath(path), lessOptions));
});
}
};
...
function isDataURL(str) { if (typeof str !== 'string') return false; var isDataURLRegex = /^\s*data:([a-z]+\/[a-z0-9\-\+]+(;[a-z\-]+\=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\ _\~\:\@\/\?\%\s]*\s*$/i; return !!str.match(isDataURLRegex); }
n/a
function isDate(arg) { return Object.prototype.toString.call(arg) === '[object Date]'; }
n/a
function isEmail(str) { return /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.['a-z0-9!#$%&*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0 -9-]*[a-z0-9])?$/i.test(str); }
...
/**
* Asynchronously confirms that the provided email is valid
*/
email.prototype.validateInput = function (data, callback) {
var input = this.getValueFromData(data);
var result = true;
if (input) {
result = utils.isEmail(input);
}
utils.defer(callback, result);
};
/**
* Asynchronously confirms that required input is present
*/
...
function isFunction(arg) { return typeof arg === 'function'; }
...
* - `field.paths.action` in `req.body` (`clear` or `delete`)
* - `field.paths.upload` in `req.files` (uploads the file to s3file)
*/
azurefile.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 () {};
...
function isNumber(arg) { return typeof arg === 'number'; }
...
throw new Error('Select fields require an options array.');
}
this.ops = options.options.map(function (i) {
var op = typeof i === 'string' ? { value: i.trim(), label: utils.keyToLabel(i) } : i;
if (!_.isObject(op)) {
op = { label: '' + i, value: '' + i };
}
if (options.numeric && !_.isNumber(op.value)) {
op.value = Number(op.value);
}
return op;
});
// undefined options.emptyOption defaults to true
if (options.emptyOption === undefined) {
options.emptyOption = true;
...
function isObject(arg) { return Object.prototype.toString.call(arg) === '[object Object]'; }
...
* @api public
*/
var Email = function (options) {
if (typeof options === 'string') {
options = { templateName: options };
}
if (!utils.isObject(options)) {
throw new Error('The keystone.Email class requires a templateName or options argument to be provided');
}
/**
* Keystone < 0.4 Compatibility
*
* NOTE: Add warnings and enable them in 4.1 or 4.2 release. These patterns
...
function isString(arg) { return typeof arg === 'string'; }
...
if (!_.has(header, HEADER_VALUE_KEY)) {
return callback(new Error('Unsupported Header option: missing required key "' + HEADER_VALUE_KEY + '"
in ' + JSON.stringify(header)));
}
filteredKeys = _.filter(_.keys(header), function (key) { return _.indexOf(validKeys, key) > -1; });
_.forEach(filteredKeys, function (key) {
if (!_.isString(header[key])) {
return callback(new Error('Unsupported Header option: value for ' + key + ' header must be a String ' + header
[key].toString()));
}
});
return true;
};
...
function isValidObjectId(arg) { var len = arg.length; if (len === 12 || len === 24) { return /^[0-9a-fA-F]+$/.test(arg); } else { return false; } }
...
if (this.autokey) {
var autokeyFilter = {};
autokeyFilter[this.autokey.path] = searchRegExp;
searchFilters.push(autokeyFilter);
}
}
if (utils.isValidObjectId(searchString)) {
searchFilters.push({
_id: searchString,
});
}
if (searchFilters.length > 1) {
query.$or = searchFilters;
...
function keyToLabel(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; str = str.replace(/([a-z])([A-Z])/g, '$1 $2'); str = str.replace(/([0-9])([a-zA-Z])/g, '$1 $2'); str = str.replace(/([a-zA-Z])([0-9])/g, '$1 $2'); var parts = str.split(/\s|\.|_|-|:|;|([A-z\u00C0-\u00ff]+)/); for (var i = 0; i < parts.length; i++) { if (parts[i] && !/^[A-Z0-9]+$/.test(parts[i])) { parts[i] = upcase(parts[i]); } } return compact(parts).join(' '); }
...
var self = this;
// init mappings
_.forEach(this.options.map, function (val, key) { self.map(key, val); });
// define property getters
Object.defineProperty(this, 'label', { get: function () {
return this.get('label') || this.set('label', utils.plural(utils.keyToLabel
(key)));
} });
Object.defineProperty(this, 'singular', { get: function () {
return this.get('singular') || this.set('singular', utils.singular(this.label));
} });
Object.defineProperty(this, 'plural', { get: function () {
return this.get('plural') || this.set('plural', utils.plural(this.singular));
} });
...
function keyToPath(str, plural) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; var parts = slug(keyToLabel(str)).split('-'); if (parts.length && plural) { parts[parts.length - 1] = inflect.pluralize(parts[parts.length - 1]); } return parts.join('-'); }
...
}
}
this.options = utils.options(defaultOptions, options);
// init properties
this.key = key;
this.path = this.get('path') || utils.keyToPath(key, true);
this.schema = new keystone.mongoose.Schema({}, this.options.schema);
this.schemaFields = [];
this.uiElements = [];
this.underscoreMethods = {};
this.fields = {};
this.fieldsArray = [];
this.fieldTypes = {};
...
function keyToProperty(str, plural) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; var parts = slug(keyToLabel(str)).split('-'); if (parts.length && plural) { parts[parts.length - 1] = inflect.pluralize(parts[parts.length - 1]); } for (var i = 1; i < parts.length; i++) { parts[i] = upcase(parts[i]); } return parts.join(''); }
...
if (!def.ref) {
throw new Error('List Relationships must be specified with an object containing ref (' + this.key + ')');
}
if (!def.refPath) {
def.refPath = utils.downcase(this.key);
}
if (!def.path) {
def.path = utils.keyToProperty(def.ref, true);
}
Object.defineProperty(def, 'refList', {
get: function () {
return keystone.list(def.ref);
},
});
Object.defineProperty(def, 'isValid', {
...
function kmBetween(point1, point2) { return calculateDistance(point1, point2) * RADIUS_KM; }
n/a
function milesBetween(point1, point2) { return calculateDistance(point1, point2) * RADIUS_MILES; }
n/a
noop = function () {}
n/a
function number(str) { return parseFloat(String(str).replace(/[^\-0-9\.]/g, '')); }
...
}
break;
case 'number':
case 'money':
if (filter.operator === 'bt') {
value = [
utils.number(value[0]),
utils.number(value[1]),
];
if (!isNaN(value[0]) && !isNaN(value[1])) {
filters[path] = {
$gte: value[0],
$lte: value[1],
};
...
function options(defaults, ops) { defaults = defaults || {}; ops = ops || {}; Object.keys(ops).forEach(function (key) { defaults[key] = ops[key]; }); return defaults; }
...
var initialFields;
// Inherit default options from parent list if it exists
if (options && options.inherits) {
if (options.inherits.options && options.inherits.options.inherits) {
throw new Error('Inherited Lists may not contain any inheritance');
}
defaultOptions = utils.options(defaultOptions, options.inherits.options);
if (options.inherits.options.track) {
options.track = false;
}
}
this.options = utils.options(defaultOptions, options);
...
function optionsMap(arr, property, clone) { if (arguments.length === 2 && typeof property === 'boolean') { clone = property; property = undefined; } var obj = {}; for (var i = 0; i < arr.length; i++) { var prop = (property) ? arr[i][property] : arr[i]; if (clone) { prop = deepClone(prop); } obj[arr[i].value] = prop; } return obj; }
...
// undefined options.emptyOption defaults to true
if (options.emptyOption === undefined) {
options.emptyOption = true;
}
// ensure this.emptyOption is a boolean
this.emptyOption = !!options.emptyOption;
// cached maps for options, labels and values
this.map = utils.optionsMap(this.ops);
this.labels = utils.optionsMap(this.ops, 'label');
this.values = _.map(this.ops, 'value');
select.super_.call(this, list, path, options);
}
select.properName = 'Select';
util.inherits(select, FieldType);
...
function plural(count, sn, pl) { if (arguments.length === 1) { return inflect.pluralize(count); } if (typeof sn !== 'string') sn = ''; if (!pl) { pl = inflect.pluralize(sn); } if (typeof count === 'string') { count = Number(count); } else if (typeof count !== 'number') { count = Object.keys(count).length; } return (count === 1 ? sn : pl).replace('*', count); }
...
var self = this;
// init mappings
_.forEach(this.options.map, function (val, key) { self.map(key, val); });
// define property getters
Object.defineProperty(this, 'label', { get: function () {
return this.get('label') || this.set('label', utils.plural(utils
.keyToLabel(key)));
} });
Object.defineProperty(this, 'singular', { get: function () {
return this.get('singular') || this.set('singular', utils.singular(this.label));
} });
Object.defineProperty(this, 'plural', { get: function () {
return this.get('plural') || this.set('plural', utils.plural(this.singular));
} });
...
function randomKey(len, chars) { var str = ''; if (typeof len === 'object') { // cheap array check var min = number(len[0]); var max = number(len[1]); len = Math.round(Math.random() * (max - min)) + min; } else { len = number(len); } chars = chars || randomKey.default; for (var i = 0; i < len; i++) { str += chars.charAt(Math.floor(Math.random() * chars.length)); } return str; }
...
};
exports.getSecret = function (req) {
return req.session[exports.SECRET_KEY] || (req.session[exports.SECRET_KEY] = exports.createSecret());
};
exports.createToken = function (req) {
return tokenize(utils.randomString(exports.SECRET_LENGTH), exports.getSecret(req));
};
exports.getToken = function (req, res) {
res.locals[exports.LOCAL_VALUE] = res.locals[exports.LOCAL_VALUE] || exports.createToken(req);
res.cookie(exports.XSRF_COOKIE_KEY, res.locals[exports.LOCAL_VALUE]);
return res.locals[exports.LOCAL_VALUE];
};
...
function singular(str) { return inflect.singularize(str); }
...
_.forEach(this.options.map, function (val, key) { self.map(key, val); });
// define property getters
Object.defineProperty(this, 'label', { get: function () {
return this.get('label') || this.set('label', utils.plural(utils.keyToLabel(key)));
} });
Object.defineProperty(this, 'singular', { get: function () {
return this.get('singular') || this.set('singular', utils.singular(this.label));
} });
Object.defineProperty(this, 'plural', { get: function () {
return this.get('plural') || this.set('plural', utils.plural(this.singular));
} });
Object.defineProperty(this, 'namePath', { get: function () {
return this.mappings.name || '_id';
} });
...
function slug(str, sep, options) { if (!limax) { return _slug(str, { separator: sep, custom: { '’': '', }, }).toLowerCase(); } options = options || {}; sep = sep || '-'; return limax(str, { tone: false, lang: options.locale, separator: sep }); }
...
var utils = require('keystone-utils');
module.exports = function initDatabaseConfig () {
if (!this.get('mongo')) {
var dbName = this.get('db name')
|| utils.slug(this.get('name'));
var dbUrl = process.env.MONGO_URI
|| process.env.MONGODB_URI
|| process.env.MONGO_URL
|| process.env.MONGODB_URL
|| process.env.MONGOLAB_URI
|| process.env.MONGOLAB_URL
|| (process.env.OPENSHIFT_MONGODB_DB_URL
...
function stringify(obj) { return JSON.stringify(obj).replace(/[\u000A\u000D\u2028\u2029]/g, function (c) { return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4); }); }
...
if (keystone.get('env') === 'development') {
if (err instanceof Error) {
if (err.type) {
msg += '<h2>' + err.type + '</h2>';
}
msg += utils.textToHTML(err.message);
} else if (typeof err === 'object') {
msg += '<code>' + JSON.stringify(err) + '</code>
;';
} else if (err) {
msg += err;
}
}
return res.status(500).send(keystone.wrapHTMLError('Sorry, an error occurred loading the page (500)', msg));
};
...
function stripDiacritics(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; var rtn = []; for (var i = 0; i < str.length; i++) { var c = str.charAt(i); rtn.push(diacritics[c] || c); } return rtn.join(''); }
n/a
function textToHTML(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; return encodeHTMLEntities(str).replace(/\n/g, '<br>'); }
...
}
var msg = '';
if (keystone.get('env') === 'development') {
if (err instanceof Error) {
if (err.type) {
msg += '<h2>' + err.type + '</h2>';
}
msg += utils.textToHTML(err.message);
} else if (typeof err === 'object') {
msg += '<code>' + JSON.stringify(err) + '</code>';
} else if (err) {
msg += err;
}
}
return res.status(500).send(keystone.wrapHTMLError('Sorry, an error occurred loading the page (500)', msg));
...
function titlecase(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; str = str.replace(/([a-z])([A-Z])/g, '$1 $2'); var parts = str.split(/\s|_|\-/); for (var i = 0; i < parts.length; i++) { if (parts[i] && !/^[A-Z0-9]+$/.test(parts[i])) { parts[i] = upcase(parts[i]); } } return compact(parts).join(' '); }
n/a
function transliterate(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; var rtn = []; // applying зг-zgh rule str = str.replace(transliteration.regexp.Zgh, 'Zgh'); str = str.replace(transliteration.regexp.zgh, 'zgh'); // replace characters with equivalent maps for (var i = 0; i < str.length; i++) { var character = str[i]; var latinCharacter = transliteration.characterMap[character]; rtn.push((latinCharacter || latinCharacter === '') ? latinCharacter : character); } return rtn.join(''); }
n/a
function upcase(str) { if (str && str.toString) str = str.toString(); if (!isString(str) || !str.length) return ''; return (str.substr(0, 1).toUpperCase() + str.substr(1)); }
...
exports.titlecase = function (str) {
if (str && str.toString) str = str.toString();
if (typeof str !== 'string' || !str.length) return '';
str = str.replace(/([a-z])([A-Z])/g, '$1 $2');
var parts = str.split(/\s|_|\-/);
for (var i = 0; i < parts.length; i++) {
if (parts[i] && !/^[A-Z0-9]+$/.test(parts[i])) {
parts[i] = exports.upcase(parts[i]);
}
}
return (0, _lodash.compact)(parts).join(' ');
};
/**
* Converts a string to camel case
...