UNPKG

keystone

Version:

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

118 lines (98 loc) 3.15 kB
var _ = require('lodash'); var utils = require('keystone-utils'); module.exports = 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(); } }); };