<commit_before>var keystone = require('../'),
	Types = require('./fieldTypes'),
	_ = require('underscore'),
	async = require('async'),
	utils = require('keystone-utils');

var methods = module.exports.methods = {};
var options = module.exports.options = {};

exports.sortable = function() {

	var list = this;

	this.schema.add({
		sortOrder: { type: Number, index: true }
	});

	this.schema.pre('save', function(next) {

		if (this.sortOrder) {
			return next();
		}

		var item = this;

		list.model.findOne().sort('-sortOrder').exec(function(err, max) {
			item.sortOrder = (max && max.sortOrder) ? max.sortOrder + 1 : 1;
			next();
		});

	});

};

exports.autokey = function() {

	var autokey = this.autokey = this.get('autokey'),
		list = this,
		def = {};

	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 ('string' === typeof autokey.from) {
		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)) {
			_.each(autokey.unique, function(k, v) {
				if (_.isString(v) && 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);
			} else if (results.length && (results.length > 1 || results[0].id != doc.id)) {
				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,
			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;
				}
			} 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 (!modified && this.get(autokey.path)) {
			return next();
		}

		var newKey = utils.slug(values.join(' ') || this.id);

		if (autokey.unique) {<commit_msg>lib/schemaPlugins autokey: Added 'fixed' attribute to control if the value should be changed on update<commit_after>var keystone = require('../'),
	Types = require('./fieldTypes'),
	_ = require('underscore'),
	async = require('async'),
	utils = require('keystone-utils');

var methods = module.exports.methods = {};
var options = module.exports.options = {};

exports.sortable = function() {

	var list = this;

	this.schema.add({
		sortOrder: { type: Number, index: true }
	});

	this.schema.pre('save', function(next) {

		if (this.sortOrder) {
			return next();
		}

		var item = this;

		list.model.findOne().sort('-sortOrder').exec(function(err, max) {
			item.sortOrder = (max && max.sortOrder) ? max.sortOrder + 1 : 1;
			next();
		});

	});

};

exports.autokey = function() {

	var autokey = this.autokey = this.get('autokey'),
		list = this,
		def = {};

	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 ('string' === typeof autokey.from) {
		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)) {
			_.each(autokey.unique, function(k, v) {
				if (_.isString(v) && 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);
			} else if (results.length && (results.length > 1 || results[0].id != doc.id)) {
				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,
			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;
				}
			} 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 has a value and is unmodified or fixed, don't update it
		if ((!modified || autokey.fixed) && this.get(autokey.path)) {
			return next();
		}

		var newKey = utils.slug(values.join(' ') || this.id);

		if (autokey.unique) {
{'fraction_non_alphanumeric': 0.19856618872957651, 'fraction_numerical': 0.004668222740913638, 'mean_word_length': 2.9993333333333334, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7207966', 'n_tokens_mistral': 2523, 'n_tokens_neox': 2169, 'n_words': 465}