mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 19:46:01 +01:00
Add edit, delete, and topics:delete permissions for users acting on their own posts
This commit is contained in:
@@ -27,7 +27,9 @@ define('forum/topic/posts', [
|
||||
data.privileges = ajaxify.data.privileges;
|
||||
data.posts.forEach(function(post) {
|
||||
post.selfPost = !!app.user.uid && parseInt(post.uid, 10) === parseInt(app.user.uid, 10);
|
||||
post.display_moderator_tools = post.selfPost || ajaxify.data.privileges.isAdminOrMod;
|
||||
post.display_edit_tools = (ajaxify.data.privileges.editOwnPosts && post.selfPost) || ajaxify.data.privileges.isAdminOrMod;
|
||||
post.display_delete_tools = (ajaxify.data.privileges.editOwnPosts && post.selfPost) || ajaxify.data.privileges.isAdminOrMod;
|
||||
post.display_moderator_tools = post.display_edit_tools || post.display_delete_tools;
|
||||
post.display_move_tools = ajaxify.data.privileges.isAdminOrMod;
|
||||
post.display_post_menu = ajaxify.data.privileges.isAdminOrMod || post.selfPost || ((app.user.uid || ajaxify.data.postSharing.length) && !post.deleted);
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ module.exports = function(Categories) {
|
||||
function(data, next) {
|
||||
category = data.category;
|
||||
|
||||
var defaultPrivileges = ['find', 'read', 'topics:read', 'topics:create', 'topics:reply', 'upload:post:image'];
|
||||
var defaultPrivileges = ['find', 'read', 'topics:read', 'topics:create', 'topics:reply', 'edit', 'delete', 'upload:post:image'];
|
||||
|
||||
async.series([
|
||||
async.apply(db.setObject, 'category:' + category.cid, category),
|
||||
|
||||
@@ -8,6 +8,9 @@ privileges.userPrivilegeList = [
|
||||
'topics:read',
|
||||
'topics:create',
|
||||
'topics:reply',
|
||||
'edit',
|
||||
'delete',
|
||||
'topics:delete',
|
||||
'upload:post:image',
|
||||
'upload:post:file',
|
||||
'purge',
|
||||
@@ -20,6 +23,9 @@ privileges.groupPrivilegeList = [
|
||||
'groups:topics:read',
|
||||
'groups:topics:create',
|
||||
'groups:topics:reply',
|
||||
'groups:edit',
|
||||
'groups:delete',
|
||||
'groups:topics:delete',
|
||||
'groups:upload:post:image',
|
||||
'groups:upload:post:file',
|
||||
'groups:purge',
|
||||
|
||||
@@ -23,6 +23,9 @@ module.exports = function(privileges) {
|
||||
{name: 'Access Topics'},
|
||||
{name: 'Create Topics'},
|
||||
{name: 'Reply to Topics'},
|
||||
{name: 'Edit Posts'},
|
||||
{name: 'Delete Posts'},
|
||||
{name: 'Delete Topics'},
|
||||
{name: 'Upload Images'},
|
||||
{name: 'Upload Files'},
|
||||
{name: 'Purge'},
|
||||
@@ -362,6 +365,15 @@ module.exports = function(privileges) {
|
||||
'topics:reply': function(next) {
|
||||
groups.isMember(uid, 'cid:' + cid + ':privileges:topics:reply', next);
|
||||
},
|
||||
'edit': function(next) {
|
||||
groups.isMember(uid, 'cid:' + cid + ':privileges:edit', next);
|
||||
},
|
||||
'delete': function(next) {
|
||||
groups.isMember(uid, 'cid:' + cid + ':privileges:delete', next);
|
||||
},
|
||||
'topics:delete': function(next) {
|
||||
groups.isMember(uid, 'cid:' + cid + ':privileges:topics:delete', next);
|
||||
},
|
||||
mods: function(next) {
|
||||
user.isModerator(uid, cid, next);
|
||||
}
|
||||
@@ -380,6 +392,15 @@ module.exports = function(privileges) {
|
||||
'groups:topics:reply': function(next) {
|
||||
groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:topics:reply', next);
|
||||
},
|
||||
'groups:edit': function(next) {
|
||||
groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:edit', next);
|
||||
},
|
||||
'groups:delete': function(next) {
|
||||
groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:delete', next);
|
||||
},
|
||||
'groups:topics:delete': function(next) {
|
||||
groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:topics:delete', next);
|
||||
},
|
||||
'groups:topics:read': function(next) {
|
||||
groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:topics:read', next);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ module.exports = function(privileges) {
|
||||
isOwner: async.apply(posts.isOwner, pids, uid),
|
||||
'topics:read': async.apply(helpers.isUserAllowedTo, 'topics:read', uid, cids),
|
||||
read: async.apply(helpers.isUserAllowedTo, 'read', uid, cids),
|
||||
edit: async.apply(helpers.isUserAllowedTo, 'edit', uid, cids),
|
||||
}, next);
|
||||
}
|
||||
], function(err, results) {
|
||||
@@ -41,7 +42,7 @@ module.exports = function(privileges) {
|
||||
|
||||
for (var i=0; i<pids.length; ++i) {
|
||||
var isAdminOrMod = results.isAdmin || results.isModerator[i];
|
||||
var editable = isAdminOrMod || results.isOwner[i];
|
||||
var editable = isAdminOrMod || (results.isOwner[i] && results.edit[i]);
|
||||
|
||||
privileges.push({
|
||||
editable: editable,
|
||||
@@ -169,7 +170,8 @@ module.exports = function(privileges) {
|
||||
async.parallel({
|
||||
isAdminOrMod: async.apply(isAdminOrMod, pid, uid),
|
||||
isLocked: async.apply(topics.isLocked, postData.tid),
|
||||
isOwner: async.apply(posts.isOwner, pid, uid)
|
||||
isOwner: async.apply(posts.isOwner, pid, uid),
|
||||
'delete': async.apply(privileges.posts.can, 'delete', pid, uid)
|
||||
}, next);
|
||||
}
|
||||
], function(err, results) {
|
||||
@@ -182,6 +184,9 @@ module.exports = function(privileges) {
|
||||
if (results.isLocked) {
|
||||
return callback(new Error('[[error:topic-locked]]'));
|
||||
}
|
||||
if (!results['delete']) {
|
||||
return callback(null, false);
|
||||
}
|
||||
var postDeleteDuration = parseInt(meta.config.postDeleteDuration, 10);
|
||||
if (postDeleteDuration && (Date.now() - parseInt(postData.timestamp, 10) > postDeleteDuration * 1000)) {
|
||||
return callback(new Error('[[error:post-delete-duration-expired, ' + meta.config.postDeleteDuration + ']]'));
|
||||
@@ -234,10 +239,13 @@ module.exports = function(privileges) {
|
||||
return callback(null, {isLocked: true});
|
||||
}
|
||||
|
||||
posts.isOwner(pid, uid, next);
|
||||
async.parallel({
|
||||
owner: async.apply(posts.isOwner, pid, uid),
|
||||
edit: async.apply(privileges.posts.can, 'edit', pid, uid)
|
||||
}, next);
|
||||
},
|
||||
function(isOwner, next) {
|
||||
next(null, {editable: isOwner});
|
||||
function(result, next) {
|
||||
next(null, {editable: result.owner && result.edit});
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ module.exports = function(privileges) {
|
||||
async.parallel({
|
||||
'topics:reply': async.apply(helpers.isUserAllowedTo, 'topics:reply', uid, [topic.cid]),
|
||||
'topics:read': async.apply(helpers.isUserAllowedTo, 'topics:read', uid, [topic.cid]),
|
||||
'topics:delete': async.apply(helpers.isUserAllowedTo, 'topics:delete', uid, [topic.cid]),
|
||||
edit: async.apply(helpers.isUserAllowedTo, 'edit', uid, [topic.cid]),
|
||||
'delete': async.apply(helpers.isUserAllowedTo, 'delete', uid, [topic.cid]),
|
||||
read: async.apply(helpers.isUserAllowedTo, 'read', uid, [topic.cid]),
|
||||
isOwner: function(next) {
|
||||
next(null, !!parseInt(uid, 10) && parseInt(uid, 10) === parseInt(topic.uid, 10));
|
||||
@@ -40,7 +43,7 @@ module.exports = function(privileges) {
|
||||
var locked = parseInt(topic.locked, 10) === 1;
|
||||
var isAdminOrMod = results.isAdministrator || results.isModerator;
|
||||
var editable = isAdminOrMod;
|
||||
var deletable = isAdminOrMod || results.isOwner;
|
||||
var deletable = isAdminOrMod || (results.isOwner && results['topics:delete'][0]);
|
||||
|
||||
plugins.fireHook('filter:privileges.topics.get', {
|
||||
'topics:reply': (results['topics:reply'][0] && !locked) || isAdminOrMod,
|
||||
@@ -53,7 +56,9 @@ module.exports = function(privileges) {
|
||||
isAdminOrMod: isAdminOrMod,
|
||||
disabled: disabled,
|
||||
tid: tid,
|
||||
uid: uid
|
||||
uid: uid,
|
||||
editOwnPosts: results.edit[0],
|
||||
deleteOwnPosts: results['delete'][0]
|
||||
}, callback);
|
||||
});
|
||||
};
|
||||
@@ -176,6 +181,29 @@ module.exports = function(privileges) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
privileges.topics.canDelete = function(tid, uid, callback) {
|
||||
topics.getTopicField(tid, 'cid', function(err, cid) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
helpers.some([
|
||||
async.apply(user.isModerator, uid, cid),
|
||||
async.apply(user.isAdministrator, uid),
|
||||
function(next) {
|
||||
async.parallel({
|
||||
owner: async.apply(topics.isOwner, tid, uid),
|
||||
'topics:delete': async.apply(helpers.isUserAllowedTo, 'topics:delete', uid, [cid])
|
||||
}, function(err, result) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
next(null, result.owner && result['topics:delete'][0]);
|
||||
});
|
||||
}
|
||||
], callback);
|
||||
});
|
||||
};
|
||||
|
||||
privileges.topics.canEdit = function(tid, uid, callback) {
|
||||
privileges.topics.isOwnerOrAdminOrMod(tid, uid, callback);
|
||||
};
|
||||
|
||||
@@ -28,6 +28,12 @@ module.exports = function(SocketPosts) {
|
||||
isAdminOrMod: function(next) {
|
||||
privileges.categories.isAdminOrMod(data.cid, socket.uid, next);
|
||||
},
|
||||
canEdit: function(next) {
|
||||
privileges.posts.canEdit(data.pid, socket.uid, next);
|
||||
},
|
||||
canDelete: function(next) {
|
||||
privileges.posts.canDelete(data.pid, socket.uid, next);
|
||||
},
|
||||
favourited: function(next) {
|
||||
favourites.getFavouritesByPostIDs([data.pid], socket.uid, next);
|
||||
},
|
||||
@@ -45,7 +51,9 @@ module.exports = function(SocketPosts) {
|
||||
results.posts.deleted = parseInt(results.posts.deleted, 10) === 1;
|
||||
results.posts.favourited = results.favourited[0];
|
||||
results.posts.selfPost = socket.uid && socket.uid === parseInt(results.posts.uid, 10);
|
||||
results.posts.display_moderator_tools = results.isAdminOrMod || results.posts.selfPost;
|
||||
results.posts.display_edit_tools = results.canEdit;
|
||||
results.posts.display_delete_tools = results.canDelete;
|
||||
results.posts.display_moderator_tools = results.posts.display_edit_tools || results.posts.display_delete_tools;
|
||||
results.posts.display_move_tools = results.isAdminOrMod;
|
||||
callback(null, results);
|
||||
});
|
||||
|
||||
@@ -292,6 +292,8 @@ module.exports = function(Topics) {
|
||||
|
||||
postData.favourited = false;
|
||||
postData.votes = 0;
|
||||
postData.display_edit_tools = true;
|
||||
postData.display_delete_tools = true;
|
||||
postData.display_moderator_tools = true;
|
||||
postData.display_move_tools = true;
|
||||
postData.selfPost = false;
|
||||
|
||||
@@ -141,7 +141,9 @@ module.exports = function(Topics) {
|
||||
var loggedIn = !!parseInt(topicPrivileges.uid, 10);
|
||||
topicData.posts.forEach(function(post) {
|
||||
if (post) {
|
||||
post.display_moderator_tools = topicPrivileges.isAdminOrMod || post.selfPost;
|
||||
post.display_edit_tools = topicPrivileges.isAdminOrMod || (post.selfPost && topicPrivileges['edit']);
|
||||
post.display_delete_tools = topicPrivileges.isAdminOrMod || (post.selfPost && topicPrivileges['delete']);
|
||||
post.display_moderator_tools = post.display_edit_tools || post.display_delete_tools;
|
||||
post.display_move_tools = topicPrivileges.isAdminOrMod && post.index !== 0;
|
||||
post.display_post_menu = topicPrivileges.isAdminOrMod || post.selfPost || ((loggedIn || topicData.postSharing.length) && !post.deleted);
|
||||
post.ip = topicPrivileges.isAdminOrMod ? post.ip : undefined;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async'),
|
||||
var async = require('async');
|
||||
|
||||
db = require('../database'),
|
||||
categories = require('../categories'),
|
||||
plugins = require('../plugins'),
|
||||
privileges = require('../privileges');
|
||||
var db = require('../database');
|
||||
var categories = require('../categories');
|
||||
var meta = require('../meta');
|
||||
var plugins = require('../plugins');
|
||||
var privileges = require('../privileges');
|
||||
|
||||
|
||||
module.exports = function(Topics) {
|
||||
@@ -32,10 +33,10 @@ module.exports = function(Topics) {
|
||||
if (!exists) {
|
||||
return next(new Error('[[error:no-topic]]'));
|
||||
}
|
||||
privileges.topics.isOwnerOrAdminOrMod(tid, uid, next);
|
||||
privileges.topics.canDelete(tid, uid, next);
|
||||
},
|
||||
function (isOwnerOrAdminOrMod, next) {
|
||||
if (!isOwnerOrAdminOrMod) {
|
||||
function (canDelete, next) {
|
||||
if (!canDelete) {
|
||||
return next(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
Topics.getTopicFields(tid, ['tid', 'cid', 'uid', 'deleted', 'title', 'mainPid'], next);
|
||||
|
||||
107
src/upgrade.js
107
src/upgrade.js
@@ -10,7 +10,7 @@ var db = require('./database'),
|
||||
schemaDate, thisSchemaDate,
|
||||
|
||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema
|
||||
latestSchema = Date.UTC(2016, 7, 5);
|
||||
latestSchema = Date.UTC(2016, 8, 6);
|
||||
|
||||
Upgrade.check = function(callback) {
|
||||
db.get('schemaDate', function(err, value) {
|
||||
@@ -682,6 +682,111 @@ Upgrade.upgrade = function(callback) {
|
||||
winston.info('[2016/08/05] Removing best posts with negative scores skipped!');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = Date.UTC(2016, 8, 6);
|
||||
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2016/08/06] Granting edit/delete/delete topic on existing categories');
|
||||
|
||||
var groupsAPI = require('./groups');
|
||||
var privilegesAPI = require('./privileges');
|
||||
|
||||
db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) {
|
||||
async.eachSeries(cids, function(cid, next) {
|
||||
privilegesAPI.categories.list(cid, function(err, data) {
|
||||
var groups = data.groups;
|
||||
var users = data.users;
|
||||
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
async.eachSeries(groups, function(group, next) {
|
||||
if (group.privileges['groups:topics:reply']) {
|
||||
return async.parallel([
|
||||
async.apply(groupsAPI.join, 'cid:' + cid + ':privileges:groups:edit', group.name),
|
||||
async.apply(groupsAPI.join, 'cid:' + cid + ':privileges:groups:delete', group.name)
|
||||
], function(err) {
|
||||
if (!err) {
|
||||
winston.info('cid:' + cid + ':privileges:groups:edit, cid:' + cid + ':privileges:groups:delete granted to gid: ' + group.name);
|
||||
}
|
||||
|
||||
return next(err);
|
||||
});
|
||||
}
|
||||
|
||||
next(null);
|
||||
}, next);
|
||||
},
|
||||
function(next) {
|
||||
async.eachSeries(groups, function(group, next) {
|
||||
if (group.privileges['groups:topics:create']) {
|
||||
return groupsAPI.join('cid:' + cid + ':privileges:groups:topics:delete', group.name, function(err) {
|
||||
if (!err) {
|
||||
winston.info('cid:' + cid + ':privileges:groups:topics:delete granted to gid: ' + group.name);
|
||||
}
|
||||
|
||||
return next(err);
|
||||
});
|
||||
}
|
||||
|
||||
next(null);
|
||||
}, next);
|
||||
},
|
||||
function(next) {
|
||||
async.eachSeries(users, function(user, next) {
|
||||
if (user.privileges['topics:reply']) {
|
||||
return async.parallel([
|
||||
async.apply(groupsAPI.join, 'cid:' + cid + ':privileges:edit', user.uid),
|
||||
async.apply(groupsAPI.join, 'cid:' + cid + ':privileges:delete', user.uid)
|
||||
], function(err) {
|
||||
if (!err) {
|
||||
winston.info('cid:' + cid + ':privileges:edit, cid:' + cid + ':privileges:delete granted to uid: ' + user.uid);
|
||||
}
|
||||
|
||||
return next(err);
|
||||
});
|
||||
}
|
||||
|
||||
next(null);
|
||||
}, next);
|
||||
},
|
||||
function(next) {
|
||||
async.eachSeries(users, function(user, next) {
|
||||
if (user.privileges['topics:create']) {
|
||||
return groupsAPI.join('cid:' + cid + ':privileges:topics:delete', user.uid, function(err) {
|
||||
if (!err) {
|
||||
winston.info('cid:' + cid + ':privileges:topics:delete granted to uid: ' + user.uid);
|
||||
}
|
||||
|
||||
return next(err);
|
||||
});
|
||||
}
|
||||
|
||||
next(null);
|
||||
}, next);
|
||||
}
|
||||
], function(err) {
|
||||
if (!err) {
|
||||
winston.info('-- cid ' + cid + ' upgraded');
|
||||
}
|
||||
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
winston.info('[2016/08/06] Granting edit/delete/delete topic on existing categories - done');
|
||||
Upgrade.update(thisSchemaDate, next);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
winston.info('[2016/08/06] Granting edit/delete/delete topic on existing categories - skipped!');
|
||||
next();
|
||||
}
|
||||
}
|
||||
// Add new schema updates here
|
||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 24!!!
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
<li role="presentation"><a href="#" data-priv="groups:topics:read" class="<!-- IF groups.privileges.groups:topics:read -->active<!-- ENDIF groups.privileges.groups:topics:read -->">Access Topics</a></li>
|
||||
<li role="presentation"><a href="#" data-priv="groups:topics:create" class="<!-- IF groups.privileges.groups:topics:create -->active<!-- ENDIF groups.privileges.groups:topics:create -->">Create Topics</a></li>
|
||||
<li role="presentation"><a href="#" data-priv="groups:topics:reply" class="<!-- IF groups.privileges.groups:topics:reply -->active<!-- ENDIF groups.privileges.groups:topics:reply -->">Reply to Topics</a></li>
|
||||
<li role="presentation"><a href="#" data-priv="groups:edit" class="<!-- IF groups.privileges.groups:edit -->active<!-- ENDIF groups.privileges.groups:edit -->">Edit Posts</a></li>
|
||||
<li role="presentation"><a href="#" data-priv="groups:delete" class="<!-- IF groups.privileges.groups:delete -->active<!-- ENDIF groups.privileges.groups:delete -->">Delete Posts</a></li>
|
||||
<li role="presentation"><a href="#" data-priv="groups:topics:delete" class="<!-- IF groups.privileges.groups:topics:delete -->active<!-- ENDIF groups.privileges.groups:topics:delete -->">Delete Topics</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{groups.displayName}
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
<li role="presentation"><a href="#" data-priv="topics:read" class="<!-- IF users.privileges.topics:read -->active<!-- ENDIF users.privileges.topics:read -->">Access Topics</a></li>
|
||||
<li role="presentation"><a href="#" data-priv="topics:create" class="<!-- IF users.privileges.topics:create -->active<!-- ENDIF users.privileges.topics:create -->">Create Topics</a></li>
|
||||
<li role="presentation"><a href="#" data-priv="topics:reply" class="<!-- IF users.privileges.topics:reply -->active<!-- ENDIF users.privileges.topics:reply -->">Reply to Topics</a></li>
|
||||
<li role="presentation"><a href="#" data-priv="edit" class="<!-- IF users.privileges.edit -->active<!-- ENDIF users.privileges.edit -->">Edit Posts</a></li>
|
||||
<li role="presentation"><a href="#" data-priv="delete" class="<!-- IF users.privileges.delete -->active<!-- ENDIF users.privileges.delete -->">Delete Posts</a></li>
|
||||
<li role="presentation"><a href="#" data-priv="topics:delete" class="<!-- IF users.privileges.topics:delete -->active<!-- ENDIF users.privileges.topics:delete -->">Delete Topics</a></li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation"><a href="#" data-priv="mods" class="<!-- IF users.privileges.mods -->active<!-- ENDIF users.privileges.mods -->">Moderator</a></li>
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user