- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我使用 Apostrophe CMS 创建了一个项目并不断开发新的页面和文章。我们发布的内容(文章和页面)将对公众(非登录用户)和登录用户(管理员)可用。
我已经了解 Apostrophe CMS 中架构的基础知识,并且一直在尝试调试以下行为,这个问题的主题:文章的作者不会向未登录的用户显示。
我不确定这是错误还是默认行为,但我确实需要一些帮助,因为我已经花了几个小时研究如何更改/修复此问题。
< non-logged-in article thumb >
请注意,如果我未登录,则不会显示作者。这是呈现作者拇指的 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>
req.user
设置在 passport
包中,如下图所示:node_modules/passport/lib/strategies/session.js
req.user
是未定义的。node_modules/apostrophe/lib/modules/apostrophe-schemas/index.js
node_modules/apostrophe/lib/modules/apostrophe-schemas/index.js
joinByOne
和其他类型的连接。我要加入的是:lib/modules/apostrophe-blog/index.js
addFields: [
{
name: '_author',
label: 'Author',
type: 'joinByOne',
withType: 'apostrophe-user',
idField: 'userId',
},
其中userId是创建文章时登录的用户id。它作为 _author 加入。
我已经尝试过评论“如果”条件,但没有任何效果,所以没有成功(我可以正常看到列出的文章,但没有作者)。
我们真的需要向所有人展示作者。我该怎么做?提前致谢。
最佳答案
因此,在@boutell 的帮助下并研究文档后,我解决了这个问题。
本教程不会太长,而且非常简单。
profiles
piece type - 每个用户 1 个个人资料。描述
字段(如“关于作者”部分)apostrophe-blog
实体都有 1 个 _author
(个人资料 ID)user
后设置一个 Hook 以创建和关联其 profile
。apostrophe-blog
之前设置一个 Hook ,以关联登录用户的 profile
。迁移
,为每个已经存在的用户
生成配置文件
。migration
以交换 apostrophe-blog
中的字段和值:
apostrophe-blog
中有一个 userId
字段,所以 migration
从 userId
到 userProfileId
,其值不言自明。 如果您没有此字段或还没有任何 apostrophe-blog
,则不需要此迁移。我使用的撇号模块版本如下:
"apostrophe": "^2.37.2",
"apostrophe-blog": "^2.1.1",
profiles
片段类型。/app.js
modules: {
'apostrophe-blog': {},
'apostrophe-tags': {},
'profiles': {}, // <-- add this
/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);
}
}
}
注意:上面的文件包含一个迁移,假设您在 apostrophe-blog
实体中已经有 userId
字段。同样,如果您没有此字段或还没有任何 apostrophe-blogs,则不需要此迁移。此外,它假定您有用户的缩略图。
代码有详细的文档记录,因此您知道在需要时更改哪里。
apostrophe-blog
,删除 userId 字段(如果有)(在迁移前删除是安全的)。/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) {
// ...
从项目的根文件夹:
Node app.js apostrophe-migrations:migrate
html
文件进行必要的更改以显示作者姓名和缩略图(如果适用)。瞧!您现在应该能够看到作者。
注意:本教程不会就您是否需要让您的应用程序 100% 的时间运行进行任何说明。您最终需要将其关闭/重新启动以添加迁移。首先在开发环境中运行测试。
关于javascript - 文章的作者不会在 Apostrophe CMS 中显示给未登录的用户,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47837933/
我找到了 this excellent question and answer它以 x/y(加上 center x/y 和 degrees/radians)开始并计算旋转- 到 x'/y'。这个计算很
全部: 我已经创建了一个 Windows 窗体和一个按钮。在另一个线程中,我试图更改按钮的文本,但它崩溃了;但是如果我尝试更改按钮的颜色,它肯定会成功。我认为如果您更改任何 Windows 窗体控件属
本网站的另一个问题已证实,C 中没有缩写的字面后缀,并且可以执行以下操作: short Number = (short)1; 但是转换它和不这样做有什么区别: short Number = 1; 您使
我有下表: ID (int) EMAIL (varchar(50)) CAMPAIGNID (int) isSubscribe (bit) isActionByUser (bit) 此表存储了用户对事
也就是说,无需触发Javascript事件即可改变的属性,如何保留我手动选中或取消选中的复选框的状态,然后复制到另一个地方? 运行下面的代码片段并选中或取消选中其中的一些,然后点击“复制”: $('#
我在网上找到的所有关于递增指针导致段错误的示例都涉及指针的取消引用 - 如果我只想递增它(例如在 for 循环的末尾)并且我不在乎它是否最终进入无效内存,因为我不会再使用它。例如,在这个程序中,每次迭
我有一个 Spring MVC REST 服务,它使用 XStream 将消息与 XML 相互转换。 有什么方法可以将请求和响应中的 xml(即正文)打印到普通的 log4j 记录器? 在 Contr
做我的任务有一个很大的挑战,那就是做相互依赖的任务我在这张照片中说的。假设我们有两个任务 A 和 B,执行子任务 A1、A2 和 B1、B2,假设任务 B 依赖于 A。 要理想地执行任务 B,您应该执
通过阅读该网站上的几个答案,我了解到 CoInitialize(Ex) should be called by the creator of a thread 。然后,在该线程中运行的任何代码都可以使
这个问题已经困扰我一段时间了。我以前从未真正使用过 ListViews,也没有使用过 FirebaseListAdapters。我想做的就是通过显示 id 和用户位置来启动列表的基础,但由于某种原因,
我很难解释这两个(看似简单)句子的含义: “受检异常由编译器在编译时检查” 这是什么意思?编译器检查是否捕获了所有已检查的异常(在代码中抛出)? “未经检查的异常在运行时检查,而不是编译时” 这句话中
我有一个包含排除子字符串的文本文件,我想迭代该文件以检查并返回不带排除子字符串的输入项。 这里我使用 python 2.4,因此下面的代码可以实现此目的,因为 with open 和 any 不起作用
Spring 的缓存框架能否了解请求上下文的身份验证状态,或者更容易推出自己的缓存解决方案? 最佳答案 尽管我发现这个用例 super 奇怪,但您可以为几乎任何与 SpEL 配合使用的内容设置缓存条件
我有以下函数模板: template HeldAs* duplicate(MostDerived *original, HeldAs *held) { // error checking omi
如果我的应用程序具有设备管理员/设备所有者权限(未获得 root 权限),我如何才能从我的应用程序中终止(或阻止启动)另一个应用程序? 最佳答案 设备所有者可以阻止应用程序: DevicePolicy
非常简单的问题,但我似乎无法让它正常工作。 我有一个组件,其中有一些 XSLT(用于导航)。它通过 XSLT TBB 使用 XSLT Mediator 发布。 发布后
我正在将一个对象拖动到一个可拖放的对象内,该对象也是可拖动的。放置对象后,它会嵌套在可放置对象内。同样,如果我将对象拖到可放置的外部,它就不再嵌套。 但是,如果我经常拖入和拖出可放置对象,则可拖动对象
我正在尝试为按钮和弹出窗口等多个指令实现“取消选择”功能。也就是说,我希望当用户单击不属于指令模板一部分的元素时触发我的函数。目前,我正在使用以下 JQuery 代码: $('body').click
我从 this question 得到了下面的代码,该脚本用于在 Google tasks 上更改 iframe[src="about:blank"] 内的 CSS使用 Chrome 扩展 Tempe
我有一些 @Mock 对象,但没有指定在该对象上调用方法的返回值。该方法返回 int (不是 Integer)。我很惊讶地发现 Mockito 没有抛出 NPE 并返回 0。这是预期的行为吗? 例如:
我是一名优秀的程序员,十分优秀!