refactor(api): post diffs to use write API

v1.18.x
Julian Lam 4 years ago
parent c2e2370655
commit e118e59ce0

@ -108,6 +108,10 @@ paths:
$ref: 'write/posts/pid/vote.yaml'
/posts/{pid}/bookmark:
$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}:
$ref: 'write/admin/settings/setting.yaml'
/files/:

@ -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

@ -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';
define('forum/topic/diffs', ['forum/topic/images'], function () {
define('forum/topic/diffs', ['api', 'forum/topic/images'], function (api) {
var Diffs = {};
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' };
socket.emit('posts.getDiffs', { pid: pid }, function (err, data) {
if (err) {
return app.alertError(err.message);
}
api.get(`/posts/${pid}/diffs`, {}).then((data) => {
app.parseAndTranslate('partials/modals/post_history', {
diffs: data.revisions.map(function (revision) {
var timestamp = parseInt(revision.timestamp, 10);
@ -56,7 +52,7 @@ define('forum/topic/diffs', ['forum/topic/images'], function () {
revertEl.prop('disabled', true);
});
});
});
}).catch(app.alertError);
};
Diffs.load = function (pid, since, postContainer) {
@ -64,11 +60,7 @@ define('forum/topic/diffs', ['forum/topic/images'], function () {
return;
}
socket.emit('posts.showPostAt', { pid: pid, since: since }, function (err, data) {
if (err) {
return app.alertError(err.message);
}
api.get(`/posts/${pid}/diffs/${since}`, {}).then((data) => {
data.deleted = !!parseInt(data.deleted, 10);
app.parseAndTranslate('partials/posts_list', 'posts', {
@ -76,7 +68,7 @@ define('forum/topic/diffs', ['forum/topic/images'], function () {
}, function (html) {
postContainer.empty().append(html);
});
});
}).catch(app.alertError);
};
Diffs.restore = function (pid, since, modal) {
@ -84,14 +76,10 @@ define('forum/topic/diffs', ['forum/topic/images'], function () {
return;
}
socket.emit('posts.restoreDiff', { pid: pid, since: since }, function (err) {
if (err) {
return app.alertError(err);
}
api.put(`/posts/${pid}/diffs/${since}`, {}).then(() => {
modal.modal('hide');
app.alertSuccess('[[topic:diffs.post-restored]]');
});
}).catch(app.alertError);
};
return Diffs;

@ -4,6 +4,7 @@ const validator = require('validator');
const _ = require('lodash');
const utils = require('../utils');
const user = require('../user');
const posts = require('../posts');
const topics = require('../topics');
const groups = require('../groups');
@ -213,3 +214,61 @@ postsAPI.bookmark = async function (caller, data) {
postsAPI.unbookmark = async function (caller, 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);
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, '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;
};

@ -1,67 +1,21 @@
'use strict';
const posts = require('../../posts');
const user = require('../../user');
const privileges = require('../../privileges');
const apiHelpers = require('../../api/helpers');
const api = require('../../api');
const websockets = require('..');
module.exports = function (SocketPosts) {
SocketPosts.getDiffs = async function (socket, data) {
await privilegeCheck(data.pid, socket.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(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,
};
websockets.warnDeprecated(socket, 'GET /api/v3/posts/:pid/diffs');
return await api.posts.getDiffs(socket, data);
};
SocketPosts.showPostAt = async function (socket, data) {
await privilegeCheck(data.pid, socket.uid);
return await posts.diffs.load(data.pid, data.since, socket.uid);
websockets.warnDeprecated(socket, 'GET /api/v3/posts/:pid/diffs/:since');
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) {
const cid = await posts.getCidByPid(data.pid);
const canEdit = await privileges.categories.can('edit', cid, socket.uid);
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);
websockets.warnDeprecated(socket, 'PUT /api/v3/posts/:pid/diffs/:since');
return await api.posts.restoreDiff(socket, data);
};
};

Loading…
Cancel
Save