feat: topic delete/restore/purge/(un)pin/(un)lock

v1.18.x
Julian Lam 5 years ago
parent 675a62dadd
commit da25ce4d09

@ -534,6 +534,135 @@ paths:
$ref: '#/components/schemas/Status'
response:
$ref: components/schemas/PostsObject.yaml#/PostsObject
delete:
tags:
- topics
summary: Delete a topic
description: This operation purges a topic and all of its posts (careful, there is no confirmation!)
responses:
'200':
description: Topic successfully purged
content:
application/json:
schema:
type: object
properties:
status:
$ref: '#/components/schemas/Status'
response:
type: object
properties: {}
/topics/{tid}/state:
delete:
tags:
- topics
summary: Delete a topic
description: This operation deletes an existing topic.
responses:
'200':
description: Topic successfully deleted
content:
application/json:
schema:
type: object
properties:
status:
$ref: '#/components/schemas/Status'
response:
type: object
properties: {}
put:
tags:
- topics
summary: Restore a topic
description: This operation restores a topic.
responses:
'200':
description: Topic successfully restored
content:
application/json:
schema:
type: object
properties:
status:
$ref: '#/components/schemas/Status'
response:
type: object
properties: {}
/topics/{tid}/lock:
delete:
tags:
- topics
summary: Lock a topic
description: This operation locks an existing topic.
responses:
'200':
description: Topic successfully locked
content:
application/json:
schema:
type: object
properties:
status:
$ref: '#/components/schemas/Status'
response:
type: object
properties: {}
put:
tags:
- topics
summary: Unlock a topic
description: This operation unlocks a topic.
responses:
'200':
description: Topic successfully unlocked
content:
application/json:
schema:
type: object
properties:
status:
$ref: '#/components/schemas/Status'
response:
type: object
properties: {}
/topics/{tid}/state:
delete:
tags:
- topics
summary: Pin a topic
description: This operation pins an existing topic.
responses:
'200':
description: Topic successfully pinned
content:
application/json:
schema:
type: object
properties:
status:
$ref: '#/components/schemas/Status'
response:
type: object
properties: {}
put:
tags:
- topics
summary: Unpin a topic
description: This operation unpins a topic.
responses:
'200':
description: Topic successfully unpinned
content:
application/json:
schema:
type: object
properties:
status:
$ref: '#/components/schemas/Status'
response:
type: object
properties: {}
components:
schemas:
Status:

@ -5,7 +5,8 @@ define('forum/topic/threadTools', [
'components',
'translator',
'handleBack',
], function (components, translator, handleBack) {
'api',
], function (components, translator, handleBack, api) {
var ThreadTools = {};
ThreadTools.init = function (tid, topicContainer) {
@ -27,22 +28,22 @@ define('forum/topic/threadTools', [
});
topicContainer.on('click', '[component="topic/lock"]', function () {
socket.emit('topics.lock', { tids: [tid], cid: ajaxify.data.cid });
api.put(`/topics/${tid}/lock`);
return false;
});
topicContainer.on('click', '[component="topic/unlock"]', function () {
socket.emit('topics.unlock', { tids: [tid], cid: ajaxify.data.cid });
api.del(`/topics/${tid}/lock`);
return false;
});
topicContainer.on('click', '[component="topic/pin"]', function () {
socket.emit('topics.pin', { tids: [tid], cid: ajaxify.data.cid });
api.put(`/topics/${tid}/pin`);
return false;
});
topicContainer.on('click', '[component="topic/unpin"]', function () {
socket.emit('topics.unpin', { tids: [tid], cid: ajaxify.data.cid });
api.del(`/topics/${tid}/pin`);
return false;
});
@ -176,11 +177,9 @@ define('forum/topic/threadTools', [
return;
}
socket.emit('topics.' + command, { tids: [tid], cid: ajaxify.data.cid }, function (err) {
if (err) {
app.alertError(err.message);
}
});
const method = command === 'restore' ? 'put' : 'del';
const suffix = command !== 'purge' ? '/state' : '';
api[method](`/topics/${tid}${suffix}`, undefined, undefined, err => app.alertError(err.status.message));
});
});
}

@ -4,6 +4,8 @@ const topics = require('../../topics');
const posts = require('../../posts');
const user = require('../../user');
const meta = require('../../meta');
const events = require('../../events');
const privileges = require('../../privileges');
const helpers = require('../helpers');
const socketHelpers = require('../../socket.io/helpers');
@ -71,3 +73,86 @@ Topics.reply = async (req, res) => {
user.updateOnlineUsers(req.user.uid);
socketHelpers.notifyNew(req.user.uid, 'newPost', result);
};
Topics.delete = async (req, res) => {
await doTopicAction('delete', 'event:topic_deleted', req, {
tids: [req.params.tid],
});
helpers.formatApiResponse(200, res);
};
Topics.restore = async (req, res) => {
await doTopicAction('restore', 'event:topic_restored', req, {
tids: [req.params.tid],
});
helpers.formatApiResponse(200, res);
};
Topics.purge = async (req, res) => {
await doTopicAction('purge', 'event:topic_purged', req, {
tids: [req.params.tid],
});
helpers.formatApiResponse(200, res);
};
Topics.pin = async (req, res) => {
await doTopicAction('pin', 'event:topic_pinned', req, {
tids: [req.params.tid],
});
helpers.formatApiResponse(200, res);
};
Topics.unpin = async (req, res) => {
await doTopicAction('unpin', 'event:topic_unpinned', req, {
tids: [req.params.tid],
});
helpers.formatApiResponse(200, res);
};
Topics.lock = async (req, res) => {
await doTopicAction('lock', 'event:topic_locked', req, {
tids: [req.params.tid],
});
helpers.formatApiResponse(200, res);
};
Topics.unlock = async (req, res) => {
await doTopicAction('unlock', 'event:topic_unlocked', req, {
tids: [req.params.tid],
});
helpers.formatApiResponse(200, res);
};
async function doTopicAction(action, event, socket, { tids }) {
if (!Array.isArray(tids)) {
throw new Error('[[error:invalid-tid]]');
}
if (typeof topics.tools[action] !== 'function') {
return;
}
const uids = await user.getUidsFromSet('users:online', 0, -1);
await Promise.all(tids.map(async function (tid) {
const title = await topics.getTopicField(tid, 'title');
const data = await topics.tools[action](tid, socket.uid);
const notifyUids = await privileges.categories.filterUids('topics:read', data.cid, uids);
socketHelpers.emitToTopicAndCategory(event, data, notifyUids);
await logTopicAction(action, socket, tid, title);
}));
}
async function logTopicAction(action, req, tid, title) {
var actionsToLog = ['delete', 'restore', 'purge'];
if (!actionsToLog.includes(action)) {
return;
}
await events.log({
type: 'topic-' + action,
uid: req.uid,
ip: req.ip,
tid: tid,
title: String(title),
});
}

@ -12,50 +12,17 @@ module.exports = function () {
setupApiRoute(router, '/', middleware, [...middlewares, middleware.checkRequired.bind(null, ['cid', 'title', 'content'])], 'post', controllers.write.topics.create);
setupApiRoute(router, '/:tid', middleware, [...middlewares, middleware.checkRequired.bind(null, ['content']), middleware.assertTopic], 'post', controllers.write.topics.reply);
// setupApiRoute(router, '/:cid', middleware, [...middlewares, middleware.isAdmin], 'put', controllers.write.categories.update);
// setupApiRoute(router, '/:cid', middleware, [...middlewares, middleware.isAdmin], 'delete', controllers.write.categories.delete);
setupApiRoute(router, '/:tid', middleware, [...middlewares, middleware.assertTopic], 'delete', controllers.write.topics.purge);
// app.route('/:tid')
// .delete(apiMiddleware.requireUser, apiMiddleware.validateTid, function(req, res) {
// Topics.purgePostsAndTopic(req.params.tid, req.params._uid, function(err) {
// errorHandler.handle(err, res);
// });
// })
// .put(apiMiddleware.requireUser, function(req, res) {
// if (!utils.checkRequired(['pid', 'content'], req, res)) {
// return false;
// }
setupApiRoute(router, '/:tid/state', middleware, [...middlewares, middleware.assertTopic], 'put', controllers.write.topics.restore);
setupApiRoute(router, '/:tid/state', middleware, [...middlewares, middleware.assertTopic], 'delete', controllers.write.topics.delete);
// var payload = {
// uid: req.user.uid,
// pid: req.body.pid,
// content: req.body.content,
// options: {}
// };
// console.log(payload);
setupApiRoute(router, '/:tid/pin', middleware, [...middlewares, middleware.assertTopic], 'put', controllers.write.topics.pin);
setupApiRoute(router, '/:tid/pin', middleware, [...middlewares, middleware.assertTopic], 'delete', controllers.write.topics.unpin);
// // Maybe a "set if available" utils method may come in handy
// if (req.body.handle) { payload.handle = req.body.handle; }
// if (req.body.title) { payload.title = req.body.title; }
// if (req.body.topic_thumb) { payload.options.topic_thumb = req.body.topic_thumb; }
// if (req.body.tags) { payload.options.tags = req.body.tags; }
setupApiRoute(router, '/:tid/lock', middleware, [...middlewares, middleware.assertTopic], 'put', controllers.write.topics.lock);
setupApiRoute(router, '/:tid/lock', middleware, [...middlewares, middleware.assertTopic], 'delete', controllers.write.topics.unlock);
// Posts.edit(payload, function(err, returnData) {
// errorHandler.handle(err, res, returnData);
// });
// });
// app.route('/:tid/state')
// .put(apiMiddleware.requireUser, apiMiddleware.validateTid, function (req, res) {
// Topics.restore(req.params.tid, req.params._uid, function (err) {
// errorHandler.handle(err, res);
// });
// })
// .delete(apiMiddleware.requireUser, apiMiddleware.validateTid, function (req, res) {
// Topics.delete(req.params.tid, req.params._uid, function (err) {
// errorHandler.handle(err, res);
// });
// });
// app.route('/:tid/follow')
// .put(apiMiddleware.requireUser, apiMiddleware.validateTid, function(req, res) {
@ -85,17 +52,5 @@ module.exports = function () {
// });
// });
// app.route('/:tid/pin')
// .put(apiMiddleware.requireUser, apiMiddleware.validateTid, function(req, res) {
// Topics.tools.pin(req.params.tid, req.user.uid, function(err) {
// errorHandler.handle(err, res);
// });
// })
// .delete(apiMiddleware.requireUser, apiMiddleware.validateTid, function(req, res) {
// Topics.tools.unpin(req.params.tid, req.user.uid, function(err) {
// errorHandler.handle(err, res);
// });
// });
return router;
};

@ -6,6 +6,7 @@ const events = require('../../events');
const privileges = require('../../privileges');
const plugins = require('../../plugins');
const socketHelpers = require('../helpers');
const sockets = require('..');
module.exports = function (SocketTopics) {
SocketTopics.loadTopicTools = async function (socket, data) {
@ -34,30 +35,37 @@ module.exports = function (SocketTopics) {
};
SocketTopics.delete = async function (socket, data) {
sockets.warnDeprecated(socket, 'DELETE /api/v1/topics/state');
await SocketTopics.doTopicAction('delete', 'event:topic_deleted', socket, data);
};
SocketTopics.restore = async function (socket, data) {
sockets.warnDeprecated(socket, 'PUT /api/v1/topics/state');
await SocketTopics.doTopicAction('restore', 'event:topic_restored', socket, data);
};
SocketTopics.purge = async function (socket, data) {
sockets.warnDeprecated(socket, 'DELETE /api/v1/topics');
await SocketTopics.doTopicAction('purge', 'event:topic_purged', socket, data);
};
SocketTopics.lock = async function (socket, data) {
sockets.warnDeprecated(socket, 'PUT /api/v1/topics/lock');
await SocketTopics.doTopicAction('lock', 'event:topic_locked', socket, data);
};
SocketTopics.unlock = async function (socket, data) {
sockets.warnDeprecated(socket, 'DELETE /api/v1/topics/lock');
await SocketTopics.doTopicAction('unlock', 'event:topic_unlocked', socket, data);
};
SocketTopics.pin = async function (socket, data) {
sockets.warnDeprecated(socket, 'PUT /api/v1/topics/pin');
await SocketTopics.doTopicAction('pin', 'event:topic_pinned', socket, data);
};
SocketTopics.unpin = async function (socket, data) {
sockets.warnDeprecated(socket, 'DELETE /api/v1/topics/pin');
await SocketTopics.doTopicAction('unpin', 'event:topic_unpinned', socket, data);
};

Loading…
Cancel
Save