diff --git a/lib/models/post.js b/lib/models/post.js index cf83a33e5c..d53e875d87 100644 --- a/lib/models/post.js +++ b/lib/models/post.js @@ -133,63 +133,76 @@ module.exports = function(ctx) { }); Post.method('setCategories', function(cats) { - cats = removeEmptyTag(cats); + // Remove empty categories, preserving hierarchies + cats = cats.filter(function(cat) { + return Array.isArray(cat) ? true : cat != null && cat !== ''; + }).map(function(cat) { + return Array.isArray(cat) ? removeEmptyTag(cat) : cat + ''; + }); var PostCategory = ctx.model('PostCategory'); var Category = ctx.model('Category'); var id = this._id; - var arr = []; + var allIds = []; var existed = PostCategory.find({post_id: id}, {lean: true}).map(pickID); - - // Don't use "Promise.map". It doesn't run in series. - // MUST USE "Promise.each". - return Promise.each(cats, function(cat, i) { - // Find the category by name - var data = Category.findOne({ - name: cat, - parent: i ? arr[i - 1] : {$exists: false} - }, {lean: true}); - - if (data) { - arr.push(data._id); - return data; - } - - // Insert the category if not exist - var obj = {name: cat}; - if (i) obj.parent = arr[i - 1]; - - return Category.insert(obj).catch(function(err) { - // Try to find the category again. Throw the error if not found + var hasHierarchy = cats.filter(Array.isArray).length > 0; + + // Add a hierarchy of categories + var addHierarchy = function(catHierarchy) { + var parentIds = []; + if (!Array.isArray(catHierarchy)) catHierarchy = [catHierarchy]; + // Don't use "Promise.map". It doesn't run in series. + // MUST USE "Promise.each". + return Promise.each(catHierarchy, function(cat, i) { + // Find the category by name var data = Category.findOne({ name: cat, - parent: i ? arr[i - 1] : {$exists: false} + parent: i ? parentIds[i - 1] : {$exists: false} }, {lean: true}); - if (data) return data; - throw err; - }).then(function(data) { - arr.push(data._id); - return data; + if (data) { + allIds.push(data._id); + parentIds.push(data._id); + return data; + } + + // Insert the category if not exist + var obj = {name: cat}; + if (i) obj.parent = parentIds[i - 1]; + + return Category.insert(obj).catch(function(err) { + // Try to find the category again. Throw the error if not found + var data = Category.findOne({ + name: cat, + parent: i ? parentIds[i - 1] : {$exists: false} + }, {lean: true}); + + if (data) return data; + throw err; + }).then(function(data) { + allIds.push(data._id); + parentIds.push(data._id); + return data; + }); }); - }).map(function() { - // Get the index from the second argument - // and get the category id from arr. - var cat = arr[arguments[1]]; + }; + return (hasHierarchy ? Promise.each(cats, addHierarchy) : Promise.resolve(addHierarchy(cats)) + ).then(function() { + return allIds; + }).map(function(catId) { // Find the reference - var ref = PostCategory.findOne({post_id: id, category_id: cat}, {lean: true}); + var ref = PostCategory.findOne({post_id: id, category_id: catId}, {lean: true}); if (ref) return ref; // Insert the reference if not exist return PostCategory.insert({ post_id: id, - category_id: cat + category_id: catId }); - }).then(function(cats) { + }).then(function(postCats) { // Remove old categories - var deleted = _.difference(existed, cats.map(pickID)); - return deleted; + return _.difference(existed, postCats.map(pickID)); }).map(function(cat) { return PostCategory.removeById(cat); }); diff --git a/test/scripts/helpers/list_categories.js b/test/scripts/helpers/list_categories.js index 976a0831f9..353b362a45 100644 --- a/test/scripts/helpers/list_categories.js +++ b/test/scripts/helpers/list_categories.js @@ -19,12 +19,14 @@ describe('list_categories', () => { {source: 'foo', slug: 'foo'}, {source: 'bar', slug: 'bar'}, {source: 'baz', slug: 'baz'}, - {source: 'boo', slug: 'boo'} + {source: 'boo', slug: 'boo'}, + {source: 'bat', slug: 'bat'} ])).then(posts => Promise.each([ ['baz'], ['baz', 'bar'], ['foo'], - ['baz'] + ['baz'], + ['bat', ['baz', 'bar']] ], (cats, i) => posts[i].setCategories(cats))).then(() => { hexo.locals.invalidate(); ctx.site = hexo.locals.toObject(); @@ -37,10 +39,13 @@ describe('list_categories', () => { result.should.eql([ '