Merge remote-tracking branch 'origin/develop' into bootstrap5

This commit is contained in:
Julian Lam
2022-09-21 12:12:27 -04:00
25 changed files with 231 additions and 95 deletions

View File

@@ -1,6 +1,7 @@
'use strict';
const winston = require('winston');
const _ = require('lodash');
const db = require('../database');
@@ -11,11 +12,14 @@ const plugins = require('../plugins');
const batch = require('../batch');
module.exports = function (Categories) {
Categories.getRecentReplies = async function (cid, uid, count) {
if (!parseInt(count, 10)) {
return [];
Categories.getRecentReplies = async function (cid, uid, start, stop) {
// backwards compatibility, treat start as count
if (stop === undefined && start > 0) {
winston.warn('[Categories.getRecentReplies] 3 params deprecated please use Categories.getRecentReplies(cid, uid, start, stop)');
stop = start - 1;
start = 0;
}
let pids = await db.getSortedSetRevRange(`cid:${cid}:pids`, 0, count - 1);
let pids = await db.getSortedSetRevRange(`cid:${cid}:pids`, start, stop);
pids = await privileges.posts.filter('topics:read', pids, uid);
return await posts.getPostSummaryByPids(pids, uid, { stripTags: true });
};

View File

@@ -266,6 +266,10 @@ program
].join('\n')}`);
})
.action((scripts, options) => {
if (program.opts().dev) {
process.env.NODE_ENV = 'development';
global.env = 'development';
}
require('./upgrade').upgrade(scripts.length ? scripts : true, options);
});

View File

@@ -120,7 +120,7 @@ pkgInstall.installAll = () => {
command = `cnpm install ${prod ? ' --production' : ''}`;
break;
default:
command += prod ? ' --production' : '';
command += prod ? ' --omit=dev' : '';
break;
}
}

View File

@@ -165,7 +165,11 @@ module.exports = function (middleware) {
return controllers.helpers.notAllowed(req, res);
}
const uid = await user.getUidByUserslug(req.params.userslug);
if (!['uid', 'userslug'].some(param => req.params.hasOwnProperty(param))) {
return controllers.helpers.notAllowed(req, res);
}
const uid = req.params.uid || await user.getUidByUserslug(req.params.userslug);
let allowed = await privileges.users.canEdit(req.uid, uid);
if (allowed) {
return next();

View File

@@ -7,7 +7,7 @@ const db = require('../database');
const meta = require('../meta');
const plugins = require('../plugins');
const translator = require('../translator');
const topics = require('../topics');
module.exports = function (Posts) {
const Diffs = {};
@@ -38,16 +38,24 @@ module.exports = function (Posts) {
};
Diffs.save = async function (data) {
const { pid, uid, oldContent, newContent, edited } = data;
const { pid, uid, oldContent, newContent, edited, topic } = data;
const editTimestamp = edited || Date.now();
const patch = diff.createPatch('', newContent, oldContent);
const diffData = {
uid: uid,
pid: pid,
};
if (oldContent !== newContent) {
diffData.patch = diff.createPatch('', newContent, oldContent);
}
if (topic.renamed) {
diffData.title = topic.oldTitle;
}
if (topic.tagsupdated && Array.isArray(topic.oldTags)) {
diffData.tags = topic.oldTags.map(tag => tag && tag.value).filter(Boolean).join(',');
}
await Promise.all([
db.listPrepend(`post:${pid}:diffs`, editTimestamp),
db.setObject(`diff:${pid}.${editTimestamp}`, {
uid: uid,
pid: pid,
patch: patch,
}),
db.setObject(`diff:${pid}.${editTimestamp}`, diffData),
]);
};
@@ -71,6 +79,8 @@ module.exports = function (Posts) {
content: post.content,
req: req,
timestamp: since,
title: post.topic.title,
tags: post.topic.tags.map(tag => tag.value),
});
};
@@ -130,6 +140,16 @@ module.exports = function (Posts) {
// Replace content with re-constructed content from that point in time
post[0].content = diffs.reduce(applyPatch, validator.unescape(post[0].content));
const titleDiffs = diffs.filter(d => d.hasOwnProperty('title') && d.title);
if (titleDiffs.length && post[0].topic) {
post[0].topic.title = validator.unescape(String(titleDiffs[titleDiffs.length - 1].title));
}
const tagDiffs = diffs.filter(d => d.hasOwnProperty('tags') && d.tags);
if (tagDiffs.length && post[0].topic) {
const tags = tagDiffs[tagDiffs.length - 1].tags.split(',').map(tag => ({ value: tag }));
post[0].topic.tags = await topics.getTagData(tags);
}
return post[0];
}
@@ -144,9 +164,12 @@ module.exports = function (Posts) {
}
function applyPatch(content, aDiff) {
const result = diff.applyPatch(content, aDiff.patch, {
fuzzFactor: 1,
});
return typeof result === 'string' ? result : content;
if (aDiff && aDiff.patch) {
const result = diff.applyPatch(content, aDiff.patch, {
fuzzFactor: 1,
});
return typeof result === 'string' ? result : content;
}
return content;
}
};

View File

@@ -29,7 +29,9 @@ module.exports = function (Posts) {
throw new Error('[[error:no-post]]');
}
const topicData = await topics.getTopicFields(postData.tid, ['cid', 'mainPid', 'title', 'timestamp', 'scheduled', 'slug']);
const topicData = await topics.getTopicFields(postData.tid, [
'cid', 'mainPid', 'title', 'timestamp', 'scheduled', 'slug', 'tags',
]);
await scheduledTopicCheck(data, topicData);
@@ -53,7 +55,10 @@ module.exports = function (Posts) {
]);
await Posts.setPostFields(data.pid, result.post);
const contentChanged = data.content !== oldContent;
const contentChanged = data.content !== oldContent ||
topic.renamed ||
topic.tagsupdated;
if (meta.config.enablePostHistory === 1 && contentChanged) {
await Posts.diffs.save({
pid: data.pid,
@@ -61,6 +66,7 @@ module.exports = function (Posts) {
oldContent: oldContent,
newContent: data.content,
edited: editPostData.edited,
topic,
});
}
await Posts.uploads.sync(data.pid);
@@ -109,6 +115,7 @@ module.exports = function (Posts) {
title: validator.escape(String(topicData.title)),
isMainPost: false,
renamed: false,
tagsupdated: false,
};
}
@@ -124,15 +131,16 @@ module.exports = function (Posts) {
newTopicData.slug = `${tid}/${slugify(title) || 'topic'}`;
}
data.tags = data.tags || [];
const tagsupdated = Array.isArray(data.tags) &&
!_.isEqual(data.tags, topicData.tags.map(tag => tag.value));
if (data.tags.length) {
if (tagsupdated) {
const canTag = await privileges.categories.can('topics:tag', topicData.cid, data.uid);
if (!canTag) {
throw new Error('[[error:no-privileges]]');
}
await topics.validateTags(data.tags, topicData.cid, data.uid, tid);
}
await topics.validateTags(data.tags, topicData.cid, data.uid, tid);
const results = await plugins.hooks.fire('filter:topic.edit', {
req: data.req,
@@ -140,7 +148,9 @@ module.exports = function (Posts) {
data: data,
});
await db.setObject(`topic:${tid}`, results.topic);
await topics.updateTopicTags(tid, data.tags);
if (tagsupdated) {
await topics.updateTopicTags(tid, data.tags);
}
const tags = await topics.getTopicTagsObjects(tid);
if (rescheduling(data, topicData)) {
@@ -149,7 +159,7 @@ module.exports = function (Posts) {
newTopicData.tags = data.tags;
newTopicData.oldTitle = topicData.title;
const renamed = translator.escape(validator.escape(String(title))) !== topicData.title;
const renamed = title && translator.escape(validator.escape(String(title))) !== topicData.title;
plugins.hooks.fire('action:topic.edit', { topic: newTopicData, uid: data.uid });
return {
tid: tid,
@@ -160,8 +170,10 @@ module.exports = function (Posts) {
slug: newTopicData.slug || topicData.slug,
isMainPost: true,
renamed: renamed,
rescheduled: rescheduling(data, topicData),
tagsupdated: tagsupdated,
tags: tags,
oldTags: topicData.tags,
rescheduled: rescheduling(data, topicData),
};
}

View File

@@ -76,9 +76,15 @@ module.exports = function (Posts) {
}
async function getTopicAndCategories(tids) {
const topicsData = await topics.getTopicsFields(tids, ['uid', 'tid', 'title', 'cid', 'slug', 'deleted', 'scheduled', 'postcount', 'mainPid', 'teaserPid']);
const topicsData = await topics.getTopicsFields(tids, [
'uid', 'tid', 'title', 'cid', 'tags', 'slug',
'deleted', 'scheduled', 'postcount', 'mainPid', 'teaserPid',
]);
const cids = _.uniq(topicsData.map(topic => topic && topic.cid));
const categoriesData = await categories.getCategoriesFields(cids, ['cid', 'name', 'icon', 'slug', 'parentCid', 'bgColor', 'color', 'backgroundImage', 'imageClass']);
const categoriesData = await categories.getCategoriesFields(cids, [
'cid', 'name', 'icon', 'slug', 'parentCid',
'bgColor', 'color', 'backgroundImage', 'imageClass',
]);
return { topics: topicsData, categories: categoriesData };
}

View File

@@ -307,7 +307,11 @@ async function generateForRecentPosts(req, res, next) {
if (meta.config['feeds:disableRSS']) {
return next();
}
const postData = await posts.getRecentPosts(req.uid, 0, 19, 'month');
const page = parseInt(req.query.page, 10) || 1;
const postsPerPage = 20;
const start = Math.max(0, (page - 1) * postsPerPage);
const stop = start + postsPerPage - 1;
const postData = await posts.getRecentPosts(req.uid, start, stop, 'month');
const feed = generateForPostsFeed({
title: 'Recent Posts',
description: 'A list of recent posts',
@@ -323,11 +327,14 @@ async function generateForCategoryRecentPosts(req, res) {
return controllers404.handle404(req, res);
}
const cid = req.params.category_id;
const page = parseInt(req.query.page, 10) || 1;
const topicsPerPage = 20;
const start = Math.max(0, (page - 1) * topicsPerPage);
const stop = start + topicsPerPage - 1;
const [userPrivileges, category, postData] = await Promise.all([
privileges.categories.get(cid, req.uid),
categories.getCategoryData(cid),
categories.getRecentReplies(cid, req.uid || req.query.uid || 0, 20),
categories.getRecentReplies(cid, req.uid || req.query.uid || 0, start, stop),
]);
if (!category) {

View File

@@ -10,7 +10,7 @@ const SocketCategories = module.exports;
require('./categories/search')(SocketCategories);
SocketCategories.getRecentReplies = async function (socket, cid) {
return await categories.getRecentReplies(cid, socket.uid, 4);
return await categories.getRecentReplies(cid, socket.uid, 0, 4);
};
SocketCategories.get = async function (socket) {