123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- 'use strict';
- const { Schema } = require('warehouse');
- const moment = require('moment');
- const { extname, join, sep } = require('path');
- const Promise = require('bluebird');
- const Moment = require('./types/moment');
- const { full_url_for } = require('hexo-util');
- function pickID(data) {
- return data._id;
- }
- function removeEmptyTag(tags) {
- return tags.filter(tag => tag != null && tag !== '').map(tag => `${tag}`);
- }
- module.exports = ctx => {
- const Post = new Schema({
- id: String,
- title: {type: String, default: ''},
- date: {
- type: Moment,
- default: moment,
- language: ctx.config.languages,
- timezone: ctx.config.timezone
- },
- updated: {
- type: Moment,
- language: ctx.config.languages,
- timezone: ctx.config.timezone
- },
- comments: {type: Boolean, default: true},
- layout: {type: String, default: 'post'},
- _content: {type: String, default: ''},
- source: {type: String, required: true},
- slug: {type: String, required: true},
- photos: [String],
- link: {type: String, default: ''},
- raw: {type: String, default: ''},
- published: {type: Boolean, default: true},
- content: {type: String},
- excerpt: {type: String},
- more: {type: String}
- });
- Post.virtual('path').get(function() {
- const path = ctx.execFilterSync('post_permalink', this, {context: ctx});
- return typeof path === 'string' ? path : '';
- });
- Post.virtual('permalink').get(function() {
- return full_url_for.call(ctx, this.path);
- });
- Post.virtual('full_source').get(function() {
- return join(ctx.source_dir, this.source || '');
- });
- Post.virtual('asset_dir').get(function() {
- const src = this.full_source;
- return src.substring(0, src.length - extname(src).length) + sep;
- });
- Post.virtual('tags').get(function() {
- const PostTag = ctx.model('PostTag');
- const Tag = ctx.model('Tag');
- const ids = PostTag.find({post_id: this._id}, {lean: true}).map(item => item.tag_id);
- return Tag.find({_id: {$in: ids}});
- });
- Post.method('setTags', function(tags) {
- tags = removeEmptyTag(tags);
- const PostTag = ctx.model('PostTag');
- const Tag = ctx.model('Tag');
- const id = this._id;
- const existed = PostTag.find({post_id: id}, {lean: true}).map(pickID);
- return Promise.map(tags, tag => {
- // Find the tag by name
- const data = Tag.findOne({name: tag}, {lean: true});
- if (data) return data;
- // Insert the tag if not exist
- return Tag.insert({name: tag}).catch(err => {
- // Try to find the tag again. Throw the error if not found
- const data = Tag.findOne({name: tag}, {lean: true});
- if (data) return data;
- throw err;
- });
- }).map(tag => {
- // Find the reference
- const ref = PostTag.findOne({post_id: id, tag_id: tag._id}, {lean: true});
- if (ref) return ref;
- // Insert the reference if not exist
- return PostTag.insert({
- post_id: id,
- tag_id: tag._id
- });
- }).then(tags => {
- // Remove old tags
- const deleted = existed.filter(item => !tags.map(pickID).includes(item));
- return deleted;
- }).map(tag => PostTag.removeById(tag));
- });
- Post.virtual('categories').get(function() {
- const PostCategory = ctx.model('PostCategory');
- const Category = ctx.model('Category');
- const ids = PostCategory.find({post_id: this._id}, {lean: true}).map(item => item.category_id);
- return Category.find({_id: {$in: ids}});
- });
- Post.method('setCategories', function(cats) {
- // Remove empty categories, preserving hierarchies
- cats = cats.filter(cat => {
- return Array.isArray(cat) || (cat != null && cat !== '');
- }).map(cat => {
- return Array.isArray(cat) ? removeEmptyTag(cat) : `${cat}`;
- });
- const PostCategory = ctx.model('PostCategory');
- const Category = ctx.model('Category');
- const id = this._id;
- const allIds = [];
- const existed = PostCategory.find({post_id: id}, {lean: true}).map(pickID);
- const hasHierarchy = cats.filter(Array.isArray).length > 0;
- // Add a hierarchy of categories
- const addHierarchy = catHierarchy => {
- const 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, (cat, i) => {
- // Find the category by name
- const data = Category.findOne({
- name: cat,
- parent: i ? parentIds[i - 1] : {$exists: false}
- }, {lean: true});
- if (data) {
- allIds.push(data._id);
- parentIds.push(data._id);
- return data;
- }
- // Insert the category if not exist
- const obj = {name: cat};
- if (i) obj.parent = parentIds[i - 1];
- return Category.insert(obj).catch(err => {
- // Try to find the category again. Throw the error if not found
- const data = Category.findOne({
- name: cat,
- parent: i ? parentIds[i - 1] : {$exists: false}
- }, {lean: true});
- if (data) return data;
- throw err;
- }).then(data => {
- allIds.push(data._id);
- parentIds.push(data._id);
- return data;
- });
- });
- };
- return (hasHierarchy ? Promise.each(cats, addHierarchy) : Promise.resolve(addHierarchy(cats))
- ).then(() => allIds).map(catId => {
- // Find the reference
- const 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: catId
- });
- }).then(postCats => // Remove old categories
- existed.filter(item => !postCats.map(pickID).includes(item))).map(cat => PostCategory.removeById(cat));
- });
- // Remove PostTag references
- Post.pre('remove', data => {
- const PostTag = ctx.model('PostTag');
- return PostTag.remove({post_id: data._id});
- });
- // Remove PostCategory references
- Post.pre('remove', data => {
- const PostCategory = ctx.model('PostCategory');
- return PostCategory.remove({post_id: data._id});
- });
- // Remove assets
- Post.pre('remove', data => {
- const PostAsset = ctx.model('PostAsset');
- return PostAsset.remove({post: data._id});
- });
- return Post;
- };
|