Apostrophe CMS

时间:2017-12-15 18:25:38

标签: javascript node.js debugging content-management-system apostrophe-cms

我使用Apostrophe CMS创建了一个项目,并不断开发新的页面和文章。 我们发布的内容(文章和页面)将向公众(非登录用户)以及登录用户(管理员)提供。

我已经了解Apostrophe CMS中架构的基础知识,并且一直在尝试调试以下行为,这个问题的主题是:文章作者未向未登录用户显示。< / p>

我不确定这是一个错误还是默认行为,但我真的需要一些帮助,因为我已经花了几个小时来研究如何更改/解决这个问题。

到目前为止我调试过的内容

  1. 这是为登录用户显示文章拇指的方式:
  2. &LT; logged-in article thumb&GT;

    1. 这就是 -logged-in用户的显示方式:
    2. &LT; non-logged-in article thumb&GT;

      请注意,如果我没有登录,作者是如何不显示的。 这是呈现作者拇指的HTML的一部分:

      <div class="wrap-content">
      
        {% if piece._author.thumbnail.items.length %}
          <img src="{{apos.attachments.url(apos.images.first(piece._author.thumbnail))}}" class="image-user" alt="">
        {% endif %}
      
        <h2 class="title">{{piece.title}}</h2>
        <p class="description">{{ piece.description }} </p>
      
      1. 我发现req.user已在passport包中设置,如下图所示:
      2. node_modules/passport/lib/strategies/session.js

        &LT; passport-set-req-user&GT;

        1. 我来到了一个&#34;如果&#34;检查用户是否已登录的条件。在下图中,我未登录,因此req.user未定义。
        2. node_modules/apostrophe/lib/modules/apostrophe-schemas/index.js

          &LT; apostrophe-cms-logged-off&GT;

          1. 登录后,用户设置为:
          2. node_modules/apostrophe/lib/modules/apostrophe-schemas/index.js

            &LT; apostrophe-cms-logged-in&GT;

            1. 登录后,撇号模式执行joinByOne和其他类型的连接。我必须加入的是:
            2. lib/modules/apostrophe-blog/index.js

              addFields: [
                {
                  name: '_author',
                  label: 'Author',
                  type: 'joinByOne',
                  withType: 'apostrophe-user',
                  idField: 'userId',
                },
              

              其中userId是创建文章时登录的用户ID。它作为_author加入。

              1. 在我上面显示的html文件中,如果有一个&#34; piece._author&#34;它显示了作者,否则它没有。不是因为我们从未加入过,所以从来没有作者,因此如果我没有登录则不会显示作者。
              2. 我已经尝试评论&#34; if&#34;条件,没有任何影响,所以没有成功(我可以看到正常列出的文章,但没有作者)。

                我们真的需要向所有人展示作者。我该怎么做?提前谢谢你。

2 个答案:

答案 0 :(得分:1)

所以在@boutell的帮助和研究文档中我解决了问题。

本教程不太长,而且非常简单。

概述

  1. 为每个用户创建了一个新的profiles件类型 - 1个配置文件。
  2. 每个个人资料都有一个description字段(如“关于作者”部分)
  3. 每个apostrophe-blog实体都有1 _author(个人资料ID)
  4. 在插入user后设置一个钩子,以创建并关联其profile
  5. 在插入apostrophe-blog之前设置一个挂钩,以关联登录用户的profile
  6. 为每个已存在的migration创建profile以生成user
  7. 创建migration以交换apostrophe-blog中的字段和值:
    • 当发现问题时,我userId已经有一个apostrophe-blog字段,因此migrationuserId更改为userProfileId,这个值是不言自明的。 如果您还没有此字段,或者还没有apostrophe-blog,则无需迁移。
  8. 我使用的撇号模块的版本如下:

    "apostrophe": "^2.37.2",
    "apostrophe-blog": "^2.1.1",
    

    解决方案

    1. 创建了新的profiles件类型。
    2. <强> /app.js

          modules: {
              'apostrophe-blog': {},
              'apostrophe-tags': {},
              'profiles': {}, // <-- add this
      
      1. 创建以下文件夹和文件:
      2. <强> /lib/modules/profiles/index.js

            /**
             * Module for adding "profile" entity. Every user has its profile. Before article (apostrophe-blog)
             * entities are inserted, they have the logged in user's profile id userprofile._id attached to
             * the article, as userProfileId field. This way, author of articles are always shown, even to
             * non-logged in users. In this file two migrations are included - add profile to existing
             * users and change from old "userId" that were in articles to new "userProfileId".
             * 
             * Run migration with node app.js apostrophe-migrations:migrate.
             * 
             * @author Alexandre Duarte (github.com/duartealexf)
             */
        
            const async = require('async');
        
            module.exports = {
        
                /**
                 * Default stuff required by ApostropheCMS.
                 */
                extend: 'apostrophe-pieces',
                name: 'profile',
                label: 'Profile',
                pluralLabel: 'Profiles',
                searchable: false,
        
                afterConstruct: function(self, callback) {
        
                    /**
                     * Ensure collection is set and add migrations to DB.
                     */
                    return async.series([
                        self.ensureCollection,
                        self.addUserProfileMigration,
                        self.addBlogPageAuthorMigration,
                    ], callback);
                },
        
                beforeConstruct: function(self, options) {
        
                    options.addFields = [
                        /**
                         * User of profile.
                         */
                        {
                            name: '_user',
                            label: 'User',
                            type: 'joinByOne',
                            withType: 'apostrophe-user',
                            idField: 'userId',
                        },
        
                        /**
                         * Optional profile description.
                         */
                        {
                            type: 'string',
                            textarea: true,
                            name: 'description',
                            label: 'Description',
                        },
        
                        /**
                         * Whether profile is published.
                         * Does not affect whether author is shown.
                         */
                        {
                            type: 'boolean',
                            name: 'published',
                            label: 'Published',
                            def: true,
                        },
        
                        /**
                         * Profile thumbnail.
                         */
                        {
                            name: 'thumbnail',
                            type: 'singleton',
                            widgetType: 'apostrophe-images',
                            label: 'Picture',
                            options: {
                                limit: 1,
                                aspectRatio: [100,100]
                            }
                        }
                    ].concat(options.addFields || []);
                },
        
                construct: function(self, options) {
        
                    /**
                     * Ensure collection variable is set.
                     * 
                     * @param {Function} callback 
                     */
                    self.ensureCollection = function(callback) {
                        return self.apos.db.collection('aposDocs', function(err, collection) {
                            self.db = collection;
                            return callback(err);
                        });
                    };
        
                    /**
                     * Hook after inserting user. Actually watches on any doc insert,
                     * so we need the 'if' statement below.
                     *
                     * @param {any} req Request.
                     * @param {any} doc Doc being inserted.
                     * @param {any} options Options from hook.
                     * @param {any} callback
                     */
                    self.docAfterInsert = function(req, doc, options, callback) {
        
                        /**
                         * No doc id, no change.
                         */
                        if (!doc._id) {
                            return setImmediate(callback);
                        }
        
                        /**
                         * If it is an user, we add the profile.
                         */
                        if (doc.type === 'apostrophe-user') {
                            return self.addUserProfile(req, doc, options, callback);
                        }
                        return setImmediate(callback);
                    }
        
                    /**
                     * Hook before inserting article.
                     * 
                     * @param {any} req Request.
                     * @param {any} doc Doc being inserted.
                     * @param {any} options Options from hook.
                     * @param {any} callback
                     */
                    self.docBeforeInsert = function(req, doc, options, callback) {
        
                        /**
                         * No doc id, no change.
                         */
                        if (!doc._id) {
                            return setImmediate(callback);
                        }
        
                        /**
                         * If it is a apostrophe-blog, we associate the profile
                         */
                        if (doc.type === 'apostrophe-blog') {
                            return self.addProfileToArticle(req, doc, options, callback);
                        }
                        return setImmediate(callback);
                    }
        
                    /**
                     * Method for creating user profile.
                     * 
                     * @param {any} req Request.
                     * @param {any} user User having profile added.
                     * @param {any} options Options from hook.
                     * @param {any} callback
                     */
                    self.addUserProfile = function(req, user, options, callback) {
        
                        /**
                         * Our profile entity.
                         */
                        const profile = {
                            description: '',
                            published: true,
                            userId: user._id,
                            title: user.title,
                            slug: user.slug.replace(/^(user\-)?/, 'profile-'),
                            thumbnail: user.thumbnail
                        }
        
                        /**
                         * Insert async.
                         */
                        return async.series({
                            save: function(callback) {
                                return self.insert(req, profile, {}, callback);
                            }
                        });
                    }
        
                    /**
                     * Method to add userProfileId to article.
                     * 
                     * @param {any} req Request.
                     * @param {any} article Article having profile associated.
                     * @param {any} options Options from hook.
                     * @param {any} callback
                     */
                    self.addProfileToArticle = async function(req, article, options, callback) {
        
                        /**
                         * Currently logged in user.
                         */
                        const user = req.user;
        
                        /**
                         * Extra check.
                         */
                        if (!user) {
                            return setImmediate(callback);
                        }
        
                        /**
                         * This promise should resolve to the
                         * currently logged in user's profile id.
                         */
                        const profileId = await new Promise(resolve => {
        
                            // Get profile of logged in user.
                            self.db.find({ type: self.name, userId: user._id }, async function(err, cursor) {
                                if (err) {
                                    resolve();
                                }
        
                                const profile = await cursor.next();
        
                                resolve(profile ? profile._id : undefined);
                            });
                        });
        
                        /**
                         * No profile, no association.
                         */
                        if (!profileId) {
                            return setImmediate(callback);
                        }
        
                        /**
                         * Attach the userProfileId and callback (ApostropheCMS will save the entity).
                         */
                        article.userProfileId = profileId;
        
                        return setImmediate(callback);
                    }
        
                    /**
                     * Method to add migration that adds profile to already existing users.
                     * 
                     * @param {Function} callback 
                     */
                    self.addUserProfileMigration = function(callback) {
        
                        /**
                         * Add migration to DB. The callback function will be called
                         * when running ApostropheCMS's CLI 'migration' command.
                         */
                        self.apos.migrations.add(self.__meta.name + '.addUserProfile', function(callback) {
        
                            /**
                             * The users that need migrating.
                             */
                            let usersToMigrate = [];
        
                            /**
                             * Run 'docs' and 'migrate' functions async.
                             */
                            return async.series([ docs, migrate ], callback);
        
                            /**
                             * Get the users that need migrating.
                             */
                            function docs(callback) {
        
                                /**
                                 * Get all profiles.
                                 */
                                return self.db.find({ type: self.name }, async function(err, profiles) {
                                    if (err) {
                                        return callback(err);
                                    }
        
                                    let userIds = [], profile;
        
                                    /**
                                     * Fill array of userIds from already existing profiles.
                                     */
                                    while (profile = await profiles.next()) {
                                        userIds.push(profile.userId);
                                    }
        
                                    /**
                                     * Get all users not in userIds (users that have no profile).
                                     * These are the usersToMigrate.
                                     */
                                    self.db.find({ type: 'apostrophe-user', _id: { $nin: userIds } }, async function(err, users) {
                                        if (err) {
                                            return callback(err);
                                        }
        
                                        while (user = await users.next()) {
                                            usersToMigrate.push(user);
                                        }
        
                                        return callback(null);
                                    });
                                })
                            }
        
                            /**
                             * Run migration.
                             * 
                             * @param {Function} callback 
                             */
                            async function migrate(callback) {
        
                                /**
                                 * Iterate on usersToMigrate and create a profile for each user.
                                 */
                                for (let i = 0; i < usersToMigrate.length; i++) {
                                    const user = usersToMigrate[i];
        
                                    /**
                                     * Our profile entity.
                                     */
                                    const profile = {
                                        _id: self.apos.utils.generateId(),
                                        description: '',
                                        published: true,
                                        userId: user._id,
                                        title: user.title,
                                        type: self.name,
                                        createdAt: user.updatedAt,
                                        slug: user.slug.replace(/^(user\-)?/, 'profile-'),
                                        docPermissions: [],
                                        thumbnail: user.thumbnail,
                                    }
        
                                    await new Promise(resolve => self.db.insert(profile, resolve));
                                }
        
                                return setImmediate(callback);
                            }
                        }, {
                            safe: true
                        });
        
                        return setImmediate(callback);
        
                    }
        
        
                    /**
                     * Migration to swap from userId to userProfileId to
                     * already existing apostrophe-blog entities.
                     */
                    self.addBlogPageAuthorMigration = function(callback) {
        
                        /**
                         * Add migration to DB. The callback function will be called
                         * when running ApostropheCMS's CLI 'migration' command.
                         */
                        self.apos.migrations.add(self.__meta.name + '.addBlogPageAuthor', function(callback) {
        
                            /**
                             * Mapping of profile id by user id.
                             */
                            const profileMapByUserId = new Map();
        
                            /**
                             * Posts (apostrophe-blog entities) that need migrating.
                             */
                            const postsToMigrate = [];
        
                            /**
                             * Run 'posts', 'profiles' and 'migrate' functions async.
                             */
                            return async.series([ posts, profiles, migrate ], callback);
        
                            /**
                             * Get the posts that need migrating.
                             * 
                             * @param {Function} callback
                             */
                            function posts(callback) {
        
                                /**
                                 * Get all posts having an userId set (not yet migrated ones).
                                 */
                                return self.db.find({ type: 'apostrophe-blog', userId: { $exists: true }}, async function(err, blogPosts) {
                                    if (err) {
                                        return callback(err);
                                    }
        
                                    let post;
        
                                    /**
                                     * Add found posts to postsToMigrate.
                                     */
                                    while (post = await blogPosts.next()) {
                                        postsToMigrate.push(post);
                                    }
        
                                    return callback(null);
                                });
                            }
        
                            /**
                            * Create the profiles mapping by user id.
                            * 
                            * @param {Function} callback
                            */
                            function profiles(callback) {
        
                                /**
                                * As this function is running async, we need to set immediate
                                * callback to not migrate if there are no posts to migrate.
                                */
                                if (!postsToMigrate.length) {
                                    setImmediate(callback);
                                }
        
                                /**
                                * Get all profiles.
                                */
                                return self.db.find({ type: self.name }, async function(err, profiles) {
                                    if (err) {
                                        return callback(err);
                                    }
        
                                    let profile;
        
                                    /**
                                    * Build mapping.
                                    */
                                    while (profile = await profiles.next()) {
                                        profileMapByUserId.set(profile.userId, profile);
                                    }
        
                                    return callback(null);
                                });
                            }
        
                            /**
                            * Run migration.
                            * 
                            * @param {Function} callback 
                            */
                            async function migrate(callback) {
        
                                let userId, profile, post;
        
                                for (let i = 0; i < postsToMigrate.length; i++) {
        
                                    /**
                                    * Get userId of post.
                                    */
                                    post = postsToMigrate[i];
                                    userId = post.userId;
        
                                    if (!userId) {
                                        continue;
                                    }
        
                                    /**
                                    * Get profile of user.
                                    */
                                    profile = profileMapByUserId.get(userId);
        
                                    if (!profile) {
                                        continue;
                                    }
        
                                    /**
                                    * Swap from userId to userProfileId.
                                    */
                                    delete post.userId;
                                    post.userProfileId = profile._id;
        
                                    /**
                                    * Replace the post to the new one having userProfileId.
                                    */
                                    await new Promise(resolve => self.db.replaceOne({ _id: post._id }, post, resolve));
                                }
        
                                return callback(null);
                            }
                        }, {
                            safe: true
                        });
        
                        return setImmediate(callback);
        
                    }
                }
            }
        

        注意:上面的文件包含一项迁移,假设您已在userId个实体中拥有apostrophe-blog字段。同样,如果您没有此字段或者还没有任何撇号博客,则不需要进行此迁移。此外,它假设您有用户的缩略图。

        代码记录良好,因此您可以根据需要知道在哪里进行更改。

        1. 将_author字段添加到apostrophe-blog,删除userId字段(如果有)(在迁移之前可以安全删除)。
        2. <强> /lib/modules/apostrophe-blog/index.js

              module.exports = {
          
                addFields: [
                  {                           // <-- add this
                    name: '_author',          // <-- add this
                    label: 'Author',          // <-- add this
                    type: 'joinByOne',        // <-- add this
                    withType: 'profile',      // <-- add this
                    idField: 'userProfileId'  // <-- add this
                  }                           // <-- add this
                  // ...
                ],
                beforeConstruct: function(self, options) {
                  // ...
          
          1. 启动/重新启动ApostropheCMS流程以添加迁移。
          2. 在CLI中运行迁移(不记得是否需要运行ApostropheCMS才能执行此操作)。
          3. 从项目的根文件夹:

            node app.js apostrophe-migrations:migrate

            1. html个文件进行必要的更改,以显示作者的姓名和缩略图(如果适用)。
            2. <强>瞧!您现在应该能够看到作者。

              注意:本教程不会记录您是否需要让您的应用程序100%运行。您最终需要将其关闭/重新启动以添加迁移。首先在开发环境中运行测试。

答案 1 :(得分:0)

您没有指定,但由于apostrophe-blog默认情况下没有_author加入,我假设您添加了一个,并且它与apostrophe-user一起加入。

在Apostrophe 0.5中,“用户”(可以在网站上做的事情)和“人”(在网站上谈论的人,有关员工的公共信息目录等)曾经是同一类型。在2.x中,我们停止这样做,因为拥有一个实际上不是用户的“作者”或者永远不会向公众展示其信息的用户来说太常见了。

所以在2.x apostrophe-users中永远不会设置published标志,而非管理员根本找不到其他用户;公众根本找不到用户。

我们建议您改为创建profiles件类型,并加入其中。这使你可以保持“谁可以做网站的东西”和“公众知道谁写东西的东西”之间的分离。

您可以使用beforeSave的{​​{1}}处理程序自动创建配置文件。