diff --git a/install/package.json b/install/package.json index 6d6d10f009..7ae1282656 100644 --- a/install/package.json +++ b/install/package.json @@ -73,9 +73,9 @@ "nodebb-plugin-spam-be-gone": "0.5.3", "nodebb-rewards-essentials": "0.0.11", "nodebb-theme-lavender": "5.0.4", - "nodebb-theme-persona": "8.0.3", + "nodebb-theme-persona": "8.0.4", "nodebb-theme-slick": "1.1.5", - "nodebb-theme-vanilla": "9.0.1", + "nodebb-theme-vanilla": "9.0.3", "nodebb-widget-essentials": "4.0.2", "nodemailer": "4.4.1", "passport": "^0.4.0", diff --git a/public/src/client/topic/diffs.js b/public/src/client/topic/diffs.js index 63dd2b7b28..90892fcfd1 100644 --- a/public/src/client/topic/diffs.js +++ b/public/src/client/topic/diffs.js @@ -24,6 +24,7 @@ define('forum/topic/diffs', ['benchpress', 'translator'], function (Benchpress, var modal = bootbox.dialog({ title: '[[topic:diffs.title]]', message: html, + size: 'large', }); if (!timestamps.length) { diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index b44d571919..9c4d698b01 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -219,7 +219,7 @@ define('forum/topic/posts', [ Posts._infiniteScrollTimeout = setTimeout(function () { delete Posts._infiniteScrollTimeout; }, 1000); - var replies = components.get('post').not('[data-index=0]').not('.new'); + var replies = components.get('topic').find(components.get('post').not('[data-index=0]').not('.new')); var afterEl = direction > 0 ? replies.last() : replies.first(); var after = parseInt(afterEl.attr('data-index'), 10) || 0; diff --git a/src/posts/diffs.js b/src/posts/diffs.js index 1472e913e4..bb1e7ec4d2 100644 --- a/src/posts/diffs.js +++ b/src/posts/diffs.js @@ -12,21 +12,44 @@ module.exports = function (Posts) { Posts.diffs = {}; Posts.diffs.exists = function (pid, callback) { - db.sortedSetCard('post:' + pid + ':diffs', function (err, numDiffs) { - return callback(err, numDiffs > 0); + db.listLength('post:' + pid + ':diffs', function (err, numDiffs) { + return callback(err, !!numDiffs); }); }; + Posts.diffs.get = function (pid, since, callback) { + async.waterfall([ + async.apply(db.getListRange.bind(db), 'post:' + pid + ':diffs', 0, -1), + function (timestamps, next) { + // Pass those made after `since`, and create keys + const keys = timestamps.filter(function (timestamp) { + return (parseInt(timestamp, 10) || 0) > since; + }).map(function (timestamp) { + return 'diff:' + pid + '.' + timestamp; + }); + + db.getObjects(keys, next); + }, + ], callback); + }; + Posts.diffs.list = function (pid, callback) { - db.getSortedSetRangeWithScores('post:' + pid + ':diffs', 0, -1, function (err, diffs) { - callback(err, diffs ? diffs.map(function (diffObj) { - return diffObj.score; - }).reverse() : null); - }); + db.getListRange('post:' + pid + ':diffs', 0, -1, callback); }; Posts.diffs.save = function (pid, oldContent, newContent, callback) { - db.sortedSetAdd('post:' + pid + ':diffs', Date.now(), diff.createPatch('', newContent, oldContent), callback); + const now = Date.now(); + const patch = diff.createPatch('', newContent, oldContent); + async.parallel([ + async.apply(db.listPrepend.bind(db), 'post:' + pid + ':diffs', now), + async.apply(db.setObject.bind(db), 'diff:' + pid + '.' + now, { + pid: pid, + patch: patch, + }), + ], function (err) { + // No return arguments passed back + callback(err); + }); }; Posts.diffs.load = function (pid, since, uid, callback) { @@ -41,7 +64,7 @@ module.exports = function (Posts) { post: async.apply(Posts.getPostSummaryByPids, [pid], uid, { parse: false, }), - diffs: async.apply(db.getSortedSetRangeByScore.bind(db), 'post:' + pid + ':diffs', 0, -1, since, Date.now()), + diffs: async.apply(Posts.diffs.get, pid, since), }, function (err, data) { if (err) { return callback(err); @@ -51,8 +74,8 @@ module.exports = function (Posts) { data.post.content = validator.unescape(data.post.content); // Replace content with re-constructed content from that point in time - data.post.content = data.diffs.reverse().reduce(function (content, diffString) { - return diff.applyPatch(content, diffString, { + data.post.content = data.diffs.reduce(function (content, currentDiff) { + return diff.applyPatch(content, currentDiff.patch, { fuzzFactor: 1, }); }, data.post.content); diff --git a/src/upgrades/1.8.1/diffs_zset_to_listhash.js b/src/upgrades/1.8.1/diffs_zset_to_listhash.js new file mode 100644 index 0000000000..b7a2bba296 --- /dev/null +++ b/src/upgrades/1.8.1/diffs_zset_to_listhash.js @@ -0,0 +1,57 @@ +'use strict'; + +var db = require('../../database'); +const batch = require('../../batch'); + +var async = require('async'); + +module.exports = { + name: 'Reformatting post diffs to be stored in lists and hash instead of single zset', + timestamp: Date.UTC(2017, 2, 15), + method: function (callback) { + var progress = this.progress; + + batch.processSortedSet('posts:pid', function (pids, next) { + async.each(pids, function (pid, next) { + db.getSortedSetRangeWithScores('post:' + pid + ':diffs', 0, -1, function (err, diffs) { + if (err) { + return next(err); + } + + if (!diffs || !diffs.length) { + progress.incr(); + return next(); + } + + // For each diff, push to list + async.each(diffs, function (diff, next) { + async.series([ + async.apply(db.delete.bind(db), 'post:' + pid + ':diffs'), + async.apply(db.listPrepend.bind(db), 'post:' + pid + ':diffs', diff.score), + async.apply(db.setObject.bind(db), 'diff:' + pid + '.' + diff.score, { + pid: pid, + patch: diff.value, + }), + ], next); + }, function (err) { + if (err) { + return next(err); + } + + progress.incr(); + return next(); + }); + }); + }, function (err) { + if (err) { + // Probably type error, ok to incr and continue + progress.incr(); + } + + return next(); + }); + }, { + progress: progress, + }, callback); + }, +};