mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
refactor(api): post diffs to use write API
This commit is contained in:
@@ -108,6 +108,10 @@ paths:
|
|||||||
$ref: 'write/posts/pid/vote.yaml'
|
$ref: 'write/posts/pid/vote.yaml'
|
||||||
/posts/{pid}/bookmark:
|
/posts/{pid}/bookmark:
|
||||||
$ref: 'write/posts/pid/bookmark.yaml'
|
$ref: 'write/posts/pid/bookmark.yaml'
|
||||||
|
/posts/{pid}/diffs:
|
||||||
|
$ref: 'write/posts/pid/diffs.yaml'
|
||||||
|
/posts/{pid}/diffs/{since}:
|
||||||
|
$ref: 'write/posts/pid/diffs/since.yaml'
|
||||||
/admin/settings/{setting}:
|
/admin/settings/{setting}:
|
||||||
$ref: 'write/admin/settings/setting.yaml'
|
$ref: 'write/admin/settings/setting.yaml'
|
||||||
/files/:
|
/files/:
|
||||||
|
|||||||
41
public/openapi/write/posts/pid/diffs.yaml
Normal file
41
public/openapi/write/posts/pid/diffs.yaml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- posts
|
||||||
|
summary: get post edit history
|
||||||
|
description: This operation retrieves a post's edit history
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: pid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: a valid post id
|
||||||
|
example: 2
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Post history successfully retrieved.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
$ref: ../../../components/schemas/Status.yaml#/Status
|
||||||
|
response:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
timestamps:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
revisions:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
timestamp:
|
||||||
|
type: number
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
editable:
|
||||||
|
type: boolean
|
||||||
65
public/openapi/write/posts/pid/diffs/since.yaml
Normal file
65
public/openapi/write/posts/pid/diffs/since.yaml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- posts
|
||||||
|
summary: get single post edit history
|
||||||
|
description: This operation retrieves a post's edit history
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: pid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: a valid post id
|
||||||
|
example: 2
|
||||||
|
- in: path
|
||||||
|
name: since
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
required: true
|
||||||
|
description: a valid UNIX timestamp
|
||||||
|
example: 0
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Post history successfully retrieved.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
$ref: ../../../../components/schemas/Status.yaml#/Status
|
||||||
|
response:
|
||||||
|
$ref: ../../../../components/schemas/PostObject.yaml#/PostObject
|
||||||
|
put:
|
||||||
|
tags:
|
||||||
|
- posts
|
||||||
|
summary: revert a post
|
||||||
|
description: This operation reverts a post to an earlier version. The revert process will append a new history item to the post's edit history.
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: pid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: a valid post id
|
||||||
|
example: 2
|
||||||
|
- in: path
|
||||||
|
name: since
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
required: true
|
||||||
|
description: a valid UNIX timestamp
|
||||||
|
example: 0
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Post successfully reverted
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
$ref: ../../../../components/schemas/Status.yaml#/Status
|
||||||
|
response:
|
||||||
|
type: object
|
||||||
|
properties: {}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
define('forum/topic/diffs', ['forum/topic/images'], function () {
|
define('forum/topic/diffs', ['api', 'forum/topic/images'], function (api) {
|
||||||
var Diffs = {};
|
var Diffs = {};
|
||||||
|
|
||||||
Diffs.open = function (pid) {
|
Diffs.open = function (pid) {
|
||||||
@@ -10,11 +10,7 @@ define('forum/topic/diffs', ['forum/topic/images'], function () {
|
|||||||
|
|
||||||
var localeStringOpts = { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
var localeStringOpts = { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||||
|
|
||||||
socket.emit('posts.getDiffs', { pid: pid }, function (err, data) {
|
api.get(`/posts/${pid}/diffs`, {}).then((data) => {
|
||||||
if (err) {
|
|
||||||
return app.alertError(err.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.parseAndTranslate('partials/modals/post_history', {
|
app.parseAndTranslate('partials/modals/post_history', {
|
||||||
diffs: data.revisions.map(function (revision) {
|
diffs: data.revisions.map(function (revision) {
|
||||||
var timestamp = parseInt(revision.timestamp, 10);
|
var timestamp = parseInt(revision.timestamp, 10);
|
||||||
@@ -56,7 +52,7 @@ define('forum/topic/diffs', ['forum/topic/images'], function () {
|
|||||||
revertEl.prop('disabled', true);
|
revertEl.prop('disabled', true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}).catch(app.alertError);
|
||||||
};
|
};
|
||||||
|
|
||||||
Diffs.load = function (pid, since, postContainer) {
|
Diffs.load = function (pid, since, postContainer) {
|
||||||
@@ -64,11 +60,7 @@ define('forum/topic/diffs', ['forum/topic/images'], function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.emit('posts.showPostAt', { pid: pid, since: since }, function (err, data) {
|
api.get(`/posts/${pid}/diffs/${since}`, {}).then((data) => {
|
||||||
if (err) {
|
|
||||||
return app.alertError(err.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.deleted = !!parseInt(data.deleted, 10);
|
data.deleted = !!parseInt(data.deleted, 10);
|
||||||
|
|
||||||
app.parseAndTranslate('partials/posts_list', 'posts', {
|
app.parseAndTranslate('partials/posts_list', 'posts', {
|
||||||
@@ -76,7 +68,7 @@ define('forum/topic/diffs', ['forum/topic/images'], function () {
|
|||||||
}, function (html) {
|
}, function (html) {
|
||||||
postContainer.empty().append(html);
|
postContainer.empty().append(html);
|
||||||
});
|
});
|
||||||
});
|
}).catch(app.alertError);
|
||||||
};
|
};
|
||||||
|
|
||||||
Diffs.restore = function (pid, since, modal) {
|
Diffs.restore = function (pid, since, modal) {
|
||||||
@@ -84,14 +76,10 @@ define('forum/topic/diffs', ['forum/topic/images'], function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.emit('posts.restoreDiff', { pid: pid, since: since }, function (err) {
|
api.put(`/posts/${pid}/diffs/${since}`, {}).then(() => {
|
||||||
if (err) {
|
|
||||||
return app.alertError(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
modal.modal('hide');
|
modal.modal('hide');
|
||||||
app.alertSuccess('[[topic:diffs.post-restored]]');
|
app.alertSuccess('[[topic:diffs.post-restored]]');
|
||||||
});
|
}).catch(app.alertError);
|
||||||
};
|
};
|
||||||
|
|
||||||
return Diffs;
|
return Diffs;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const validator = require('validator');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
const utils = require('../utils');
|
const utils = require('../utils');
|
||||||
|
const user = require('../user');
|
||||||
const posts = require('../posts');
|
const posts = require('../posts');
|
||||||
const topics = require('../topics');
|
const topics = require('../topics');
|
||||||
const groups = require('../groups');
|
const groups = require('../groups');
|
||||||
@@ -213,3 +214,61 @@ postsAPI.bookmark = async function (caller, data) {
|
|||||||
postsAPI.unbookmark = async function (caller, data) {
|
postsAPI.unbookmark = async function (caller, data) {
|
||||||
return await apiHelpers.postCommand(caller, 'unbookmark', 'bookmarked', '', data);
|
return await apiHelpers.postCommand(caller, 'unbookmark', 'bookmarked', '', data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function diffsPrivilegeCheck(pid, uid) {
|
||||||
|
const [deleted, privilegesData] = await Promise.all([
|
||||||
|
posts.getPostField(pid, 'deleted'),
|
||||||
|
privileges.posts.get([pid], uid),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const allowed = privilegesData[0]['posts:history'] && (deleted ? privilegesData[0]['posts:view_deleted'] : true);
|
||||||
|
if (!allowed) {
|
||||||
|
throw new Error('[[error:no-privileges]]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
postsAPI.getDiffs = async (caller, data) => {
|
||||||
|
await diffsPrivilegeCheck(data.pid, caller.uid);
|
||||||
|
const timestamps = await posts.diffs.list(data.pid);
|
||||||
|
const post = await posts.getPostFields(data.pid, ['timestamp', 'uid']);
|
||||||
|
|
||||||
|
const diffs = await posts.diffs.get(data.pid);
|
||||||
|
const uids = diffs.map(diff => diff.uid || null);
|
||||||
|
uids.push(post.uid);
|
||||||
|
let usernames = await user.getUsersFields(uids, ['username']);
|
||||||
|
usernames = usernames.map(userObj => (userObj.uid ? userObj.username : null));
|
||||||
|
|
||||||
|
let canEdit = true;
|
||||||
|
try {
|
||||||
|
await user.isPrivilegedOrSelf(caller.uid, post.uid);
|
||||||
|
} catch (e) {
|
||||||
|
canEdit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamps.push(post.timestamp);
|
||||||
|
|
||||||
|
return {
|
||||||
|
timestamps: timestamps,
|
||||||
|
revisions: timestamps.map((timestamp, idx) => ({
|
||||||
|
timestamp: timestamp,
|
||||||
|
username: usernames[idx],
|
||||||
|
})),
|
||||||
|
editable: canEdit,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
postsAPI.loadDiff = async (caller, data) => {
|
||||||
|
await diffsPrivilegeCheck(data.pid, caller.uid);
|
||||||
|
return await posts.diffs.load(data.pid, data.since, caller.uid);
|
||||||
|
};
|
||||||
|
|
||||||
|
postsAPI.restoreDiff = async (caller, data) => {
|
||||||
|
const cid = await posts.getCidByPid(data.pid);
|
||||||
|
const canEdit = await privileges.categories.can('edit', cid, caller.uid);
|
||||||
|
if (!canEdit) {
|
||||||
|
throw new Error('[[error:no-privileges]]');
|
||||||
|
}
|
||||||
|
|
||||||
|
const edit = await posts.diffs.restore(data.pid, data.since, caller.uid, apiHelpers.buildReqObject(caller));
|
||||||
|
websockets.in('topic_' + edit.topic.tid).emit('event:post_edited', edit);
|
||||||
|
};
|
||||||
|
|||||||
@@ -73,3 +73,16 @@ Posts.unbookmark = async (req, res) => {
|
|||||||
await api.posts.unbookmark(req, data);
|
await api.posts.unbookmark(req, data);
|
||||||
helpers.formatApiResponse(200, res);
|
helpers.formatApiResponse(200, res);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Posts.getDiffs = async (req, res) => {
|
||||||
|
helpers.formatApiResponse(200, res, await api.posts.getDiffs(req, { ...req.params }));
|
||||||
|
};
|
||||||
|
|
||||||
|
Posts.loadDiff = async (req, res) => {
|
||||||
|
helpers.formatApiResponse(200, res, await api.posts.loadDiff(req, { ...req.params }));
|
||||||
|
};
|
||||||
|
|
||||||
|
Posts.restoreDiff = async (req, res) => {
|
||||||
|
helpers.formatApiResponse(200, res, await api.posts.restoreDiff(req, { ...req.params }));
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,5 +24,9 @@ module.exports = function () {
|
|||||||
setupApiRoute(router, 'put', '/:pid/bookmark', [...middlewares, middleware.assert.post], controllers.write.posts.bookmark);
|
setupApiRoute(router, 'put', '/:pid/bookmark', [...middlewares, middleware.assert.post], controllers.write.posts.bookmark);
|
||||||
setupApiRoute(router, 'delete', '/:pid/bookmark', [...middlewares, middleware.assert.post], controllers.write.posts.unbookmark);
|
setupApiRoute(router, 'delete', '/:pid/bookmark', [...middlewares, middleware.assert.post], controllers.write.posts.unbookmark);
|
||||||
|
|
||||||
|
setupApiRoute(router, 'get', '/:pid/diffs', [middleware.authenticateOrGuest, middleware.assert.post], controllers.write.posts.getDiffs);
|
||||||
|
setupApiRoute(router, 'get', '/:pid/diffs/:since', [middleware.authenticateOrGuest, middleware.assert.post], controllers.write.posts.loadDiff);
|
||||||
|
setupApiRoute(router, 'put', '/:pid/diffs/:since', [...middlewares, middleware.assert.post], controllers.write.posts.restoreDiff);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,67 +1,21 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const posts = require('../../posts');
|
const api = require('../../api');
|
||||||
const user = require('../../user');
|
|
||||||
const privileges = require('../../privileges');
|
|
||||||
const apiHelpers = require('../../api/helpers');
|
|
||||||
const websockets = require('..');
|
const websockets = require('..');
|
||||||
|
|
||||||
module.exports = function (SocketPosts) {
|
module.exports = function (SocketPosts) {
|
||||||
SocketPosts.getDiffs = async function (socket, data) {
|
SocketPosts.getDiffs = async function (socket, data) {
|
||||||
await privilegeCheck(data.pid, socket.uid);
|
websockets.warnDeprecated(socket, 'GET /api/v3/posts/:pid/diffs');
|
||||||
const timestamps = await posts.diffs.list(data.pid);
|
return await api.posts.getDiffs(socket, data);
|
||||||
const post = await posts.getPostFields(data.pid, ['timestamp', 'uid']);
|
|
||||||
|
|
||||||
const diffs = await posts.diffs.get(data.pid);
|
|
||||||
const uids = diffs.map(diff => diff.uid || null);
|
|
||||||
uids.push(post.uid);
|
|
||||||
let usernames = await user.getUsersFields(uids, ['username']);
|
|
||||||
usernames = usernames.map(userObj => (userObj.uid ? userObj.username : null));
|
|
||||||
|
|
||||||
let canEdit = true;
|
|
||||||
try {
|
|
||||||
await user.isPrivilegedOrSelf(socket.uid, post.uid);
|
|
||||||
} catch (e) {
|
|
||||||
canEdit = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamps.push(post.timestamp);
|
|
||||||
|
|
||||||
return {
|
|
||||||
timestamps: timestamps,
|
|
||||||
revisions: timestamps.map((timestamp, idx) => ({
|
|
||||||
timestamp: timestamp,
|
|
||||||
username: usernames[idx],
|
|
||||||
})),
|
|
||||||
editable: canEdit,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketPosts.showPostAt = async function (socket, data) {
|
SocketPosts.showPostAt = async function (socket, data) {
|
||||||
await privilegeCheck(data.pid, socket.uid);
|
websockets.warnDeprecated(socket, 'GET /api/v3/posts/:pid/diffs/:since');
|
||||||
return await posts.diffs.load(data.pid, data.since, socket.uid);
|
return await api.posts.loadDiff(socket, data);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function privilegeCheck(pid, uid) {
|
|
||||||
const [deleted, privilegesData] = await Promise.all([
|
|
||||||
posts.getPostField(pid, 'deleted'),
|
|
||||||
privileges.posts.get([pid], uid),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const allowed = privilegesData[0]['posts:history'] && (deleted ? privilegesData[0]['posts:view_deleted'] : true);
|
|
||||||
if (!allowed) {
|
|
||||||
throw new Error('[[error:no-privileges]]');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketPosts.restoreDiff = async function (socket, data) {
|
SocketPosts.restoreDiff = async function (socket, data) {
|
||||||
const cid = await posts.getCidByPid(data.pid);
|
websockets.warnDeprecated(socket, 'PUT /api/v3/posts/:pid/diffs/:since');
|
||||||
const canEdit = await privileges.categories.can('edit', cid, socket.uid);
|
return await api.posts.restoreDiff(socket, data);
|
||||||
if (!canEdit) {
|
|
||||||
throw new Error('[[error:no-privileges]]');
|
|
||||||
}
|
|
||||||
|
|
||||||
const edit = await posts.diffs.restore(data.pid, data.since, socket.uid, apiHelpers.buildReqObject(socket));
|
|
||||||
websockets.in('topic_' + edit.topic.tid).emit('event:post_edited', edit);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user