mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-06 14:05:46 +01:00
store pinned topics in new zset
keep pinned topics on top on different sort types
This commit is contained in:
@@ -11,19 +11,30 @@ var privileges = require('../privileges');
|
||||
module.exports = function (Categories) {
|
||||
|
||||
Categories.purge = function (cid, uid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
batch.processSortedSet('cid:' + cid + ':tids', function (tids, next) {
|
||||
async.eachLimit(tids, 10, function (tid, next) {
|
||||
topics.purgePostsAndTopic(tid, uid, next);
|
||||
}, next);
|
||||
}, {alwaysStartAt: 0}, function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}, {alwaysStartAt: 0}, next);
|
||||
},
|
||||
function (next) {
|
||||
Categories.getPinnedTids('cid:' + cid + ':tids:pinned', 0, -1, next);
|
||||
},
|
||||
function (pinnedTids, next) {
|
||||
async.eachLimit(pinnedTids, 10, function (tid, next) {
|
||||
topics.purgePostsAndTopic(tid, uid, next);
|
||||
}, next);
|
||||
},
|
||||
function (next) {
|
||||
purgeCategory(cid, next);
|
||||
},
|
||||
function (next) {
|
||||
plugins.fireHook('action:category.delete', cid);
|
||||
next();
|
||||
}
|
||||
async.series([
|
||||
async.apply(purgeCategory, cid),
|
||||
async.apply(plugins.fireHook, 'action:category.delete', cid)
|
||||
], callback);
|
||||
});
|
||||
};
|
||||
|
||||
function purgeCategory(cid, callback) {
|
||||
@@ -37,6 +48,7 @@ module.exports = function (Categories) {
|
||||
function (next) {
|
||||
db.deleteAll([
|
||||
'cid:' + cid + ':tids',
|
||||
'cid:' + cid + ':tids:pinned',
|
||||
'cid:' + cid + ':tids:posts',
|
||||
'cid:' + cid + ':pids',
|
||||
'cid:' + cid + ':read_by_uid',
|
||||
@@ -50,7 +62,9 @@ module.exports = function (Categories) {
|
||||
groups.destroy('cid:' + cid + ':privileges:' + privilege, next);
|
||||
}, next);
|
||||
}
|
||||
], callback);
|
||||
], function (err) {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
function removeFromParent(cid, callback) {
|
||||
|
||||
@@ -14,7 +14,7 @@ module.exports = function (Categories) {
|
||||
plugins.fireHook('filter:category.topics.prepare', data, next);
|
||||
},
|
||||
function (data, next) {
|
||||
Categories.getTopicIds(data.set, data.reverse, data.start, data.stop, next);
|
||||
Categories.getTopicIds(data.cid, data.set, data.reverse, data.start, data.stop, next);
|
||||
},
|
||||
function (tids, next) {
|
||||
topics.getTopicsByTids(tids, data.uid, next);
|
||||
@@ -36,6 +36,58 @@ module.exports = function (Categories) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
Categories.getTopicIds = function (cid, set, reverse, start, stop, callback) {
|
||||
var pinnedTids;
|
||||
var pinnedCount;
|
||||
var totalPinnedCount;
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Categories.getPinnedTids(cid, 0, -1, next);
|
||||
},
|
||||
function (_pinnedTids, next) {
|
||||
totalPinnedCount = _pinnedTids.length;
|
||||
|
||||
pinnedTids = _pinnedTids.slice(start, stop === -1 ? undefined : stop + 1);
|
||||
|
||||
pinnedCount = pinnedTids.length;
|
||||
|
||||
var topicsPerPage = stop - start + 1;
|
||||
|
||||
var normalTidsToGet = Math.max(0, topicsPerPage - pinnedCount);
|
||||
|
||||
if (!normalTidsToGet && stop !== -1) {
|
||||
return next(null, []);
|
||||
}
|
||||
if (start > 0 && totalPinnedCount) {
|
||||
start -= totalPinnedCount - pinnedCount;
|
||||
}
|
||||
stop = stop === -1 ? stop : start + normalTidsToGet - 1;
|
||||
|
||||
if (Array.isArray(set)) {
|
||||
db[reverse ? 'getSortedSetRevIntersect' : 'getSortedSetIntersect']({sets: set, start: start, stop: stop}, next);
|
||||
} else {
|
||||
db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, stop, next);
|
||||
}
|
||||
},
|
||||
function (normalTids, next) {
|
||||
normalTids = normalTids.filter(function (tid) {
|
||||
return pinnedTids.indexOf(tid) === -1;
|
||||
});
|
||||
|
||||
next(null, pinnedTids.concat(normalTids));
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
Categories.getAllTopicIds = function (cid, start, stop, callback) {
|
||||
db.getSortedSetRange(['cid:' + cid + ':tids:pinned', 'cid:' + cid + ':tids'], start, stop, callback);
|
||||
};
|
||||
|
||||
Categories.getPinnedTids = function (cid, start, stop, callback) {
|
||||
db.getSortedSetRevRange('cid:' + cid + ':tids:pinned', start, stop, callback);
|
||||
};
|
||||
|
||||
Categories.modifyTopicsByPrivilege = function (topics, privileges) {
|
||||
if (!Array.isArray(topics) || !topics.length || privileges.isAdminOrMod) {
|
||||
return;
|
||||
@@ -52,22 +104,9 @@ module.exports = function (Categories) {
|
||||
});
|
||||
};
|
||||
|
||||
Categories.getTopicIds = function (set, reverse, start, stop, callback) {
|
||||
if (Array.isArray(set)) {
|
||||
db[reverse ? 'getSortedSetRevIntersect' : 'getSortedSetIntersect']({sets: set, start: start, stop: stop}, callback);
|
||||
} else {
|
||||
db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, stop, callback);
|
||||
}
|
||||
};
|
||||
|
||||
Categories.getTopicIndex = function (tid, callback) {
|
||||
topics.getTopicField(tid, 'cid', function (err, cid) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
db.sortedSetRevRank('cid:' + cid + ':tids', tid, callback);
|
||||
});
|
||||
console.warn('[Categories.getTopicIndex] deprecated');
|
||||
callback(null, 1);
|
||||
};
|
||||
|
||||
Categories.onNewPostMade = function (cid, pinned, postData, callback) {
|
||||
|
||||
@@ -62,7 +62,7 @@ module.exports = function (SocketTopics) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
categories.getTopicIds('cid:' + data.currentCid + ':tids', true, 0, -1, next);
|
||||
categories.getAllTopicIds(data.currentCid, 0, -1, next);
|
||||
},
|
||||
function (tids, next) {
|
||||
async.eachLimit(tids, 50, function (tid, next) {
|
||||
|
||||
@@ -53,23 +53,8 @@ var social = require('./social');
|
||||
};
|
||||
|
||||
Topics.getTidPage = function (tid, uid, callback) {
|
||||
if(!tid) {
|
||||
return callback(new Error('[[error:invalid-tid]]'));
|
||||
}
|
||||
|
||||
async.parallel({
|
||||
index: function (next) {
|
||||
categories.getTopicIndex(tid, next);
|
||||
},
|
||||
settings: function (next) {
|
||||
user.getSettings(uid, next);
|
||||
}
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, Math.ceil((results.index + 1) / results.settings.topicsPerPage));
|
||||
});
|
||||
console.warn('[Topics.getTidPage] deprecated!');
|
||||
callback(null, 1);
|
||||
};
|
||||
|
||||
Topics.getTopicsFromSet = function (set, uid, start, stop, callback) {
|
||||
|
||||
@@ -177,6 +177,7 @@ module.exports = function (Topics) {
|
||||
function (next) {
|
||||
db.sortedSetsRemove([
|
||||
'cid:' + topicData.cid + ':tids',
|
||||
'cid:' + topicData.cid + ':tids:pinned',
|
||||
'cid:' + topicData.cid + ':tids:posts',
|
||||
'cid:' + topicData.cid + ':uid:' + topicData.uid + ':tids',
|
||||
'uid:' + topicData.uid + ':topics'
|
||||
|
||||
@@ -7,6 +7,7 @@ var db = require('../database');
|
||||
var plugins = require('../plugins');
|
||||
var privileges = require('../privileges');
|
||||
var user = require('../user');
|
||||
var categories = require('../categories');
|
||||
|
||||
module.exports = function (Topics) {
|
||||
var terms = {
|
||||
@@ -24,7 +25,11 @@ module.exports = function (Topics) {
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRevRange(cid ? 'cid:' + cid + ':tids' : 'topics:recent', 0, 199, next);
|
||||
if (cid) {
|
||||
categories.getTopicIds(cid, 'cid:' + cid + ':tids', true, 0, 199, next);
|
||||
} else {
|
||||
db.getSortedSetRevRange('topics:recent', 0, 199, next);
|
||||
}
|
||||
},
|
||||
function (tids, next) {
|
||||
filterTids(tids, uid, filter, next);
|
||||
|
||||
@@ -72,7 +72,7 @@ module.exports = function (Topics) {
|
||||
Topics.getTopicField(tid, 'cid', next);
|
||||
},
|
||||
function (cid, next) {
|
||||
categories.getTopicIds('cid:' + cid + ':tids', true, 0, 9, next);
|
||||
categories.getTopicIds(cid, 'cid:' + cid + ':tids', true, 0, 9, next);
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ var async = require('async');
|
||||
|
||||
var db = require('../database');
|
||||
var categories = require('../categories');
|
||||
var meta = require('../meta');
|
||||
var plugins = require('../plugins');
|
||||
var privileges = require('../privileges');
|
||||
|
||||
@@ -167,7 +166,7 @@ module.exports = function (Topics) {
|
||||
if (!exists) {
|
||||
return callback(new Error('[[error:no-topic]]'));
|
||||
}
|
||||
Topics.getTopicFields(tid, ['cid', 'lastposttime'], next);
|
||||
Topics.getTopicFields(tid, ['cid', 'lastposttime', 'postcount'], next);
|
||||
},
|
||||
function (_topicData, next) {
|
||||
topicData = _topicData;
|
||||
@@ -177,9 +176,24 @@ module.exports = function (Topics) {
|
||||
if (!isAdminOrMod) {
|
||||
return next(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
async.parallel([
|
||||
async.apply(Topics.setTopicField, tid, 'pinned', pin ? 1 : 0),
|
||||
async.apply(db.sortedSetAdd, 'cid:' + topicData.cid + ':tids', pin ? Math.pow(2, 53) : topicData.lastposttime, tid)
|
||||
function (next) {
|
||||
if (pin) {
|
||||
async.parallel([
|
||||
async.apply(db.sortedSetAdd, 'cid:' + topicData.cid + ':tids:pinned', Date.now(), tid),
|
||||
async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids', tid),
|
||||
async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids:posts', tid),
|
||||
], next);
|
||||
} else {
|
||||
async.parallel([
|
||||
async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids:pinned', tid),
|
||||
async.apply(db.sortedSetAdd, 'cid:' + topicData.cid + ':tids', topicData.lastposttime, tid),
|
||||
async.apply(db.sortedSetAdd, 'cid:' + topicData.cid + ':tids:posts', topicData.postcount, tid),
|
||||
], next);
|
||||
}
|
||||
}
|
||||
], next);
|
||||
},
|
||||
function (results, next) {
|
||||
@@ -213,14 +227,17 @@ module.exports = function (Topics) {
|
||||
topic = topicData;
|
||||
db.sortedSetsRemove([
|
||||
'cid:' + topicData.cid + ':tids',
|
||||
'cid:' + topicData.cid + ':tids:pinned',
|
||||
'cid:' + topicData.cid + ':tids:posts'
|
||||
], tid, next);
|
||||
},
|
||||
function (next) {
|
||||
var timestamp = parseInt(topic.pinned, 10) ? Math.pow(2, 53) : topic.lastposttime;
|
||||
if (parseInt(topic.pinned, 10)) {
|
||||
db.sortedSetAdd('cid:' + cid + ':tids:pinned', Date.now(), tid, next);
|
||||
} else {
|
||||
async.parallel([
|
||||
function (next) {
|
||||
db.sortedSetAdd('cid:' + cid + ':tids', timestamp, tid, next);
|
||||
db.sortedSetAdd('cid:' + cid + ':tids', topic.lastposttime, tid, next);
|
||||
},
|
||||
function (next) {
|
||||
topic.postcount = topic.postcount || 0;
|
||||
@@ -228,6 +245,7 @@ module.exports = function (Topics) {
|
||||
}
|
||||
], next);
|
||||
}
|
||||
}
|
||||
], function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
/* globals define, console, require */
|
||||
/* globals console, require */
|
||||
|
||||
var db = require('./database'),
|
||||
async = require('async'),
|
||||
@@ -1016,7 +1016,49 @@ Upgrade.upgrade = function (callback) {
|
||||
winston.info('[2016/11/22] Update global and user language keys - skipped!');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function (next) {
|
||||
thisSchemaDate = Date.UTC(2016, 10, 25);
|
||||
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
winston.info('[2016/11/25] Creating sorted sets for pinned topcis');
|
||||
|
||||
var topics = require('./topics');
|
||||
var batch = require('./batch');
|
||||
batch.processSortedSet('topics:tid', function (ids, next) {
|
||||
topics.getTopicsFields(ids, ['tid', 'cid', 'pinned', 'lastposttime'], function (err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
data = data.filter(function (topicData) {
|
||||
return parseInt(topicData.pinned, 10) === 1;
|
||||
});
|
||||
|
||||
async.eachSeries(data, function (topicData, next) {
|
||||
console.log('processing tid: ' + topicData.tid);
|
||||
|
||||
async.parallel([
|
||||
async.apply(db.sortedSetAdd, 'cid:' + topicData.cid + ':tids:pinned', Date.now(), topicData.tid),
|
||||
async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids', topicData.tid),
|
||||
async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids:posts', topicData.tid)
|
||||
], next);
|
||||
}, next);
|
||||
});
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
winston.info('[2016/11/25] Creating sorted sets for pinned topics - done');
|
||||
Upgrade.update(thisSchemaDate, next);
|
||||
});
|
||||
} else {
|
||||
winston.info('[2016/11/25] Creating sorted sets for pinned topics - skipped!');
|
||||
next();
|
||||
}
|
||||
},
|
||||
// Add new schema updates here
|
||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 24!!!
|
||||
], function (err) {
|
||||
|
||||
@@ -366,6 +366,28 @@ describe('Categories', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should purge category', function (done) {
|
||||
Categories.create({
|
||||
name: 'purge me',
|
||||
description: 'update description'
|
||||
}, function (err, category) {
|
||||
assert.ifError(err);
|
||||
Topics.post({
|
||||
uid: posterUid,
|
||||
cid: category.cid,
|
||||
title: 'Test Topic Title',
|
||||
content: 'The content of test topic'
|
||||
}, function (err) {
|
||||
assert.ifError(err);
|
||||
socketCategories.purge({uid: adminUid}, category.cid, function (err) {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user