185 lines
4.4 KiB
JavaScript
185 lines
4.4 KiB
JavaScript
'use strict';
|
|
|
|
var getSlug = require('speakingurl'),
|
|
shortId = require('shortid'),
|
|
async = require('async');
|
|
|
|
module.exports = function (schema, options) {
|
|
var watcher = [],
|
|
slugs = [],
|
|
opts = {
|
|
separator: "-",
|
|
lang: "en",
|
|
truncate: 120
|
|
};
|
|
|
|
//merge de options
|
|
for (var attrname in options) {
|
|
opts[attrname] = options[attrname];
|
|
}
|
|
|
|
|
|
schema.eachPath(function (pathname, schemaType) {
|
|
if (schemaType.instance == "String" && schemaType.options && schemaType.options.slug) {
|
|
|
|
var slug = {
|
|
"name": pathname
|
|
};
|
|
|
|
if (typeof(schemaType.options.slug) === "string") {
|
|
slug.values = [schemaType.options.slug]
|
|
} else if (schemaType.options.slug instanceof Array) {
|
|
slug.values = schemaType.options.slug
|
|
} else {
|
|
//TODO launch error
|
|
}
|
|
|
|
if (schemaType.options.unique || schemaType.options.unique_slug) {
|
|
slug.unique = true;
|
|
}
|
|
|
|
if (schemaType.options.slug_padding_size === undefined) {
|
|
slug.isShortIdMode = true;
|
|
} else {
|
|
slug.isShortIdMode = false;
|
|
slug.padding = schemaType.options.slug_padding_size;
|
|
}
|
|
|
|
watcher = watcher.concat(slug.values.filter(function (item) {
|
|
return watcher.indexOf(item) < 0;
|
|
}));
|
|
slugs.push(slug);
|
|
|
|
}
|
|
});
|
|
|
|
|
|
/**
|
|
* Executed before save value
|
|
*/
|
|
schema.pre('save', function (next) {
|
|
var doc = this,
|
|
reSlug = false;
|
|
|
|
watcher.forEach(function (item) {
|
|
if (doc.isModified(item)) {
|
|
reSlug = true;
|
|
}
|
|
});
|
|
|
|
if (!reSlug) {
|
|
return next();
|
|
}
|
|
|
|
async.each(slugs, function (item, callback) {
|
|
var values = [];
|
|
|
|
item.values.forEach(function (item) {
|
|
values.push(doc[item]);
|
|
});
|
|
|
|
if (!(item.unique || item.unique_slug)) {
|
|
doc[item.name] = makeSlug(values, opts);
|
|
callback();
|
|
} else {
|
|
if (item.isShortIdMode) {
|
|
makeUniqueShortIdSlug(doc, item.name, values, opts, function (err, slug) {
|
|
doc[item.name] = slug;
|
|
callback();
|
|
})
|
|
} else {
|
|
makeUniqueCounterSlug(doc, item.name, values, opts, item.padding , function (err, slug) {
|
|
doc[item.name] = slug;
|
|
callback();
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
}, function (err, res) {
|
|
next();
|
|
});
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
function makeSlug(values, options) {
|
|
|
|
var slug = getSlug(
|
|
values.join(" "),
|
|
options
|
|
);
|
|
|
|
return slug;
|
|
}
|
|
|
|
|
|
function makeUniqueCounterSlug(doc, field, values, options, padding, next) {
|
|
|
|
var slug = makeSlug(values, options),
|
|
count = 0,
|
|
match = null,
|
|
test = new RegExp(options.separator + '(\\d+)$'),
|
|
query = {},
|
|
search = new RegExp(slug + "(" + options.separator + '(\\d+))?$'),
|
|
sort = {};
|
|
|
|
sort[field] = -1;
|
|
|
|
if (doc._id) {
|
|
query["_id"] = {
|
|
$ne: doc._id
|
|
}
|
|
}
|
|
|
|
query[field] = search;
|
|
|
|
// field = search and doc != doc
|
|
doc.model(doc.constructor.modelName).findOne(query).sort(sort).exec(function (err, result) {
|
|
if (result) {
|
|
if (match = result[field].match(test)) {
|
|
count = match[1];
|
|
}
|
|
count++;
|
|
slug += options.separator + pad(count, padding);
|
|
}
|
|
|
|
next(null, slug);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
function pad(num, size) {
|
|
var s = num+"";
|
|
while (s.length < size) s = "0" + s;
|
|
return s;
|
|
}
|
|
|
|
/***
|
|
* Generates a unique slug. If the slug is already used, the generated slug has an appended random string, eg: my-slug-NJw9XvZ5l
|
|
*
|
|
* @param doc
|
|
* @param field
|
|
* @param values
|
|
* @param options
|
|
* @param next
|
|
*/
|
|
function makeUniqueShortIdSlug(doc, field, values, options, next) {
|
|
|
|
var slug = makeSlug(values, options),
|
|
query = {};
|
|
|
|
query[field] = slug;
|
|
|
|
doc.model(doc.constructor.modelName).findOne(query).exec(function (err, result) {
|
|
if (result) {
|
|
slug += options.separator + shortId.generate();
|
|
}
|
|
|
|
next(null, slug);
|
|
});
|
|
} |