Merge pull request #5467 from NodeBB/upgrades-refactor
Refactor upgrade scripts to use individual files in src/upgrades/ as source of schema changesv1.18.x
commit
b0fa9f85e4
@ -0,0 +1,41 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'Chat room hashes',
|
||||
timestamp: Date.UTC(2015, 11, 23),
|
||||
method: function (callback) {
|
||||
db.getObjectField('global', 'nextChatRoomId', function (err, nextChatRoomId) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var currentChatRoomId = 1;
|
||||
async.whilst(function () {
|
||||
return currentChatRoomId <= nextChatRoomId;
|
||||
}, function (next) {
|
||||
db.getSortedSetRange('chat:room:' + currentChatRoomId + ':uids', 0, 0, function (err, uids) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!Array.isArray(uids) || !uids.length || !uids[0]) {
|
||||
currentChatRoomId += 1;
|
||||
return next();
|
||||
}
|
||||
|
||||
db.setObject('chat:room:' + currentChatRoomId, { owner: uids[0], roomId: currentChatRoomId }, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
currentChatRoomId += 1;
|
||||
next();
|
||||
});
|
||||
});
|
||||
}, callback);
|
||||
});
|
||||
},
|
||||
};
|
@ -0,0 +1,87 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Upgrading chats',
|
||||
timestamp: Date.UTC(2015, 11, 15),
|
||||
method: function (callback) {
|
||||
db.getObjectFields('global', ['nextMid', 'nextChatRoomId'], function (err, globalData) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var rooms = {};
|
||||
var roomId = globalData.nextChatRoomId || 1;
|
||||
var currentMid = 1;
|
||||
|
||||
async.whilst(function () {
|
||||
return currentMid <= globalData.nextMid;
|
||||
}, function (next) {
|
||||
db.getObject('message:' + currentMid, function (err, message) {
|
||||
var msgTime;
|
||||
|
||||
function addMessageToUids(roomId, callback) {
|
||||
async.parallel([
|
||||
function (next) {
|
||||
db.sortedSetAdd('uid:' + message.fromuid + ':chat:room:' + roomId + ':mids', msgTime, currentMid, next);
|
||||
},
|
||||
function (next) {
|
||||
db.sortedSetAdd('uid:' + message.touid + ':chat:room:' + roomId + ':mids', msgTime, currentMid, next);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
if (err || !message) {
|
||||
winston.verbose('skipping chat message ', currentMid);
|
||||
currentMid += 1;
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var pairID = [parseInt(message.fromuid, 10), parseInt(message.touid, 10)].sort().join(':');
|
||||
msgTime = parseInt(message.timestamp, 10);
|
||||
|
||||
if (rooms[pairID]) {
|
||||
winston.verbose('adding message ' + currentMid + ' to existing roomID ' + roomId);
|
||||
addMessageToUids(rooms[pairID], function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
currentMid += 1;
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
winston.verbose('adding message ' + currentMid + ' to new roomID ' + roomId);
|
||||
async.parallel([
|
||||
function (next) {
|
||||
db.sortedSetAdd('uid:' + message.fromuid + ':chat:rooms', msgTime, roomId, next);
|
||||
},
|
||||
function (next) {
|
||||
db.sortedSetAdd('uid:' + message.touid + ':chat:rooms', msgTime, roomId, next);
|
||||
},
|
||||
function (next) {
|
||||
db.sortedSetAdd('chat:room:' + roomId + ':uids', [msgTime, msgTime + 1], [message.fromuid, message.touid], next);
|
||||
},
|
||||
function (next) {
|
||||
addMessageToUids(roomId, next);
|
||||
},
|
||||
], function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
rooms[pairID] = roomId;
|
||||
roomId += 1;
|
||||
currentMid += 1;
|
||||
db.setObjectField('global', 'nextChatRoomId', roomId, next);
|
||||
});
|
||||
}
|
||||
});
|
||||
}, callback);
|
||||
});
|
||||
},
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'Creating Global moderators group',
|
||||
timestamp: Date.UTC(2016, 0, 23),
|
||||
method: function (callback) {
|
||||
var groups = require('../../groups');
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
groups.exists('Global Moderators', next);
|
||||
},
|
||||
function (exists, next) {
|
||||
if (exists) {
|
||||
return next(null, null);
|
||||
}
|
||||
groups.create({
|
||||
name: 'Global Moderators',
|
||||
userTitle: 'Global Moderator',
|
||||
description: 'Forum wide moderators',
|
||||
hidden: 0,
|
||||
private: 1,
|
||||
disableJoinRequests: 1,
|
||||
}, next);
|
||||
},
|
||||
function (groupData, next) {
|
||||
groups.show('Global Moderators', next);
|
||||
},
|
||||
], callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'Social: Post Sharing',
|
||||
timestamp: Date.UTC(2016, 1, 25),
|
||||
method: function (callback) {
|
||||
var social = require('../../social');
|
||||
async.parallel([
|
||||
function (next) {
|
||||
social.setActivePostSharingNetworks(['facebook', 'google', 'twitter'], next);
|
||||
},
|
||||
function (next) {
|
||||
db.deleteObjectField('config', 'disableSocialButtons', next);
|
||||
},
|
||||
], callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,18 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'Adding theme to active plugins sorted set',
|
||||
timestamp: Date.UTC(2015, 11, 23),
|
||||
method: function (callback) {
|
||||
async.waterfall([
|
||||
async.apply(db.getObjectField, 'config', 'theme:id'),
|
||||
async.apply(db.sortedSetAdd, 'plugins:active', 0),
|
||||
], callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,31 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Creating user best post sorted sets',
|
||||
timestamp: Date.UTC(2016, 0, 14),
|
||||
method: function (callback) {
|
||||
var batch = require('../../batch');
|
||||
|
||||
batch.processSortedSet('posts:pid', function (ids, next) {
|
||||
async.eachSeries(ids, function (id, next) {
|
||||
db.getObjectFields('post:' + id, ['pid', 'uid', 'votes'], function (err, postData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!postData || !parseInt(postData.votes, 10) || !parseInt(postData.uid, 10)) {
|
||||
return next();
|
||||
}
|
||||
winston.verbose('processing pid: ' + postData.pid + ' uid: ' + postData.uid + ' votes: ' + postData.votes);
|
||||
db.sortedSetAdd('uid:' + postData.uid + ':posts:votes', postData.votes, postData.pid, next);
|
||||
});
|
||||
}, next);
|
||||
}, {}, callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,31 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Creating users:notvalidated',
|
||||
timestamp: Date.UTC(2016, 0, 20),
|
||||
method: function (callback) {
|
||||
var batch = require('../../batch');
|
||||
var now = Date.now();
|
||||
batch.processSortedSet('users:joindate', function (ids, next) {
|
||||
async.eachSeries(ids, function (id, next) {
|
||||
db.getObjectFields('user:' + id, ['uid', 'email:confirmed'], function (err, userData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!userData || !parseInt(userData.uid, 10) || parseInt(userData['email:confirmed'], 10) === 1) {
|
||||
return next();
|
||||
}
|
||||
winston.verbose('processing uid: ' + userData.uid + ' email:confirmed: ' + userData['email:confirmed']);
|
||||
db.sortedSetAdd('users:notvalidated', now, userData.uid, next);
|
||||
});
|
||||
}, next);
|
||||
}, callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,73 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Giving topics:read privs to any group that was previously allowed to Find & Access Category',
|
||||
timestamp: Date.UTC(2016, 4, 28),
|
||||
method: function (callback) {
|
||||
var groupsAPI = require('../../groups');
|
||||
var privilegesAPI = require('../../privileges');
|
||||
|
||||
db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
async.eachSeries(cids, function (cid, next) {
|
||||
privilegesAPI.categories.list(cid, function (err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var groups = data.groups;
|
||||
var users = data.users;
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.eachSeries(groups, function (group, next) {
|
||||
if (group.privileges['groups:read']) {
|
||||
return groupsAPI.join('cid:' + cid + ':privileges:groups:topics:read', group.name, function (err) {
|
||||
if (!err) {
|
||||
winston.verbose('cid:' + cid + ':privileges:groups:topics:read granted to gid: ' + group.name);
|
||||
}
|
||||
|
||||
return next(err);
|
||||
});
|
||||
}
|
||||
|
||||
next(null);
|
||||
}, next);
|
||||
},
|
||||
function (next) {
|
||||
async.eachSeries(users, function (user, next) {
|
||||
if (user.privileges.read) {
|
||||
return groupsAPI.join('cid:' + cid + ':privileges:topics:read', user.uid, function (err) {
|
||||
if (!err) {
|
||||
winston.verbose('cid:' + cid + ':privileges:topics:read granted to uid: ' + user.uid);
|
||||
}
|
||||
|
||||
return next(err);
|
||||
});
|
||||
}
|
||||
|
||||
next(null);
|
||||
}, next);
|
||||
},
|
||||
], function (err) {
|
||||
if (!err) {
|
||||
winston.verbose('-- cid ' + cid + ' upgraded');
|
||||
}
|
||||
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
}, callback);
|
||||
});
|
||||
},
|
||||
};
|
@ -0,0 +1,43 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Dismiss flags from deleted topics',
|
||||
timestamp: Date.UTC(2016, 3, 29),
|
||||
method: function (callback) {
|
||||
var posts = require('../../posts');
|
||||
var topics = require('../../topics');
|
||||
|
||||
var pids;
|
||||
var tids;
|
||||
|
||||
async.waterfall([
|
||||
async.apply(db.getSortedSetRange, 'posts:flagged', 0, -1),
|
||||
function (_pids, next) {
|
||||
pids = _pids;
|
||||
posts.getPostsFields(pids, ['tid'], next);
|
||||
},
|
||||
function (_tids, next) {
|
||||
tids = _tids.map(function (a) {
|
||||
return a.tid;
|
||||
});
|
||||
|
||||
topics.getTopicsFields(tids, ['deleted'], next);
|
||||
},
|
||||
function (state, next) {
|
||||
var toDismiss = state.map(function (a, idx) {
|
||||
return parseInt(a.deleted, 10) === 1 ? pids[idx] : null;
|
||||
}).filter(Boolean);
|
||||
|
||||
winston.verbose('[2016/04/29] ' + toDismiss.length + ' dismissable flags found');
|
||||
async.each(toDismiss, posts.dismissFlag, next);
|
||||
},
|
||||
], callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Group title from settings to user profile',
|
||||
timestamp: Date.UTC(2016, 3, 14),
|
||||
method: function (callback) {
|
||||
var user = require('../../user');
|
||||
var batch = require('../../batch');
|
||||
var count = 0;
|
||||
batch.processSortedSet('users:joindate', function (uids, next) {
|
||||
winston.verbose('upgraded ' + count + ' users');
|
||||
user.getMultipleUserSettings(uids, function (err, settings) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
count += uids.length;
|
||||
settings = settings.filter(function (setting) {
|
||||
return setting && setting.groupTitle;
|
||||
});
|
||||
|
||||
async.each(settings, function (setting, next) {
|
||||
db.setObjectField('user:' + setting.uid, 'groupTitle', setting.groupTitle, next);
|
||||
}, next);
|
||||
});
|
||||
}, {}, callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,50 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Store upvotes/downvotes separately',
|
||||
timestamp: Date.UTC(2016, 5, 13),
|
||||
method: function (callback) {
|
||||
var batch = require('../../batch');
|
||||
var posts = require('../../posts');
|
||||
var count = 0;
|
||||
batch.processSortedSet('posts:pid', function (pids, next) {
|
||||
winston.verbose('upgraded ' + count + ' posts');
|
||||
count += pids.length;
|
||||
async.each(pids, function (pid, next) {
|
||||
async.parallel({
|
||||
upvotes: function (next) {
|
||||
db.setCount('pid:' + pid + ':upvote', next);
|
||||
},
|
||||
downvotes: function (next) {
|
||||
db.setCount('pid:' + pid + ':downvote', next);
|
||||
},
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
var data = {};
|
||||
|
||||
if (parseInt(results.upvotes, 10) > 0) {
|
||||
data.upvotes = results.upvotes;
|
||||
}
|
||||
if (parseInt(results.downvotes, 10) > 0) {
|
||||
data.downvotes = results.downvotes;
|
||||
}
|
||||
|
||||
if (Object.keys(data).length) {
|
||||
posts.setPostFields(pid, data, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}, next);
|
||||
}, next);
|
||||
}, {}, callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,50 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Users post count per tid',
|
||||
timestamp: Date.UTC(2016, 3, 19),
|
||||
method: function (callback) {
|
||||
var batch = require('../../batch');
|
||||
var topics = require('../../topics');
|
||||
var count = 0;
|
||||
batch.processSortedSet('topics:tid', function (tids, next) {
|
||||
winston.verbose('upgraded ' + count + ' topics');
|
||||
count += tids.length;
|
||||
async.each(tids, function (tid, next) {
|
||||
db.delete('tid:' + tid + ':posters', function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
topics.getPids(tid, function (err, pids) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (!pids.length) {
|
||||
return next();
|
||||
}
|
||||
|
||||
async.eachSeries(pids, function (pid, next) {
|
||||
db.getObjectField('post:' + pid, 'uid', function (err, uid) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!parseInt(uid, 10)) {
|
||||
return next();
|
||||
}
|
||||
db.sortedSetIncrBy('tid:' + tid + ':posters', 1, uid, next);
|
||||
});
|
||||
}, next);
|
||||
});
|
||||
});
|
||||
}, next);
|
||||
}, {}, callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Removing best posts with negative scores',
|
||||
timestamp: Date.UTC(2016, 7, 5),
|
||||
method: function (callback) {
|
||||
var batch = require('../../batch');
|
||||
batch.processSortedSet('users:joindate', function (ids, next) {
|
||||
async.each(ids, function (id, next) {
|
||||
winston.verbose('processing uid ' + id);
|
||||
db.sortedSetsRemoveRangeByScore(['uid:' + id + ':posts:votes'], '-inf', 0, next);
|
||||
}, next);
|
||||
}, {}, callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'Giving upload privileges',
|
||||
timestamp: Date.UTC(2016, 6, 12),
|
||||
method: function (callback) {
|
||||
var privilegesAPI = require('../../privileges');
|
||||
var meta = require('../../meta');
|
||||
|
||||
db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
async.eachSeries(cids, function (cid, next) {
|
||||
privilegesAPI.categories.list(cid, function (err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
async.eachSeries(data.groups, function (group, next) {
|
||||
if (group.name === 'guests' && parseInt(meta.config.allowGuestUploads, 10) !== 1) {
|
||||
return next();
|
||||
}
|
||||
if (group.privileges['groups:read']) {
|
||||
privilegesAPI.categories.give(['upload:post:image'], cid, group.name, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}, next);
|
||||
});
|
||||
}, callback);
|
||||
});
|
||||
},
|
||||
};
|
@ -0,0 +1,33 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'Category recent tids',
|
||||
timestamp: Date.UTC(2016, 8, 22),
|
||||
method: function (callback) {
|
||||
db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
async.eachSeries(cids, function (cid, next) {
|
||||
db.getSortedSetRevRange('cid:' + cid + ':pids', 0, 0, function (err, pid) {
|
||||
if (err || !pid) {
|
||||
return next(err);
|
||||
}
|
||||
db.getObjectFields('post:' + pid, ['tid', 'timestamp'], function (err, postData) {
|
||||
if (err || !postData || !postData.tid) {
|
||||
return next(err);
|
||||
}
|
||||
db.sortedSetAdd('cid:' + cid + ':recent_tids', postData.timestamp, postData.tid, next);
|
||||
});
|
||||
});
|
||||
}, callback);
|
||||
});
|
||||
},
|
||||
};
|
@ -0,0 +1,109 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Granting edit/delete/delete topic on existing categories',
|
||||
timestamp: Date.UTC(2016, 7, 7),
|
||||
method: function (callback) {
|
||||
var groupsAPI = require('../../groups');
|
||||
var privilegesAPI = require('../../privileges');
|
||||
|
||||
db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
async.eachSeries(cids, function (cid, next) {
|
||||
privilegesAPI.categories.list(cid, function (err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
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:posts:edit', group.name),
|
||||
async.apply(groupsAPI.join, 'cid:' + cid + ':privileges:groups:posts:delete', group.name),
|
||||
], function (err) {
|
||||
if (!err) {
|
||||
winston.verbose('cid:' + cid + ':privileges:groups:posts:edit, cid:' + cid + ':privileges:groups:posts: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.verbose('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:posts:edit', user.uid),
|
||||
async.apply(groupsAPI.join, 'cid:' + cid + ':privileges:posts:delete', user.uid),
|
||||
], function (err) {
|
||||
if (!err) {
|
||||
winston.verbose('cid:' + cid + ':privileges:posts:edit, cid:' + cid + ':privileges:posts: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.verbose('cid:' + cid + ':privileges:topics:delete granted to uid: ' + user.uid);
|
||||
}
|
||||
|
||||
return next(err);
|
||||
});
|
||||
}
|
||||
|
||||
next(null);
|
||||
}, next);
|
||||
},
|
||||
], function (err) {
|
||||
if (!err) {
|
||||
winston.verbose('-- cid ' + cid + ' upgraded');
|
||||
}
|
||||
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
}, callback);
|
||||
});
|
||||
},
|
||||
};
|
@ -0,0 +1,52 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'Favourites to Bookmarks',
|
||||
timestamp: Date.UTC(2016, 9, 8),
|
||||
method: function (callback) {
|
||||
function upgradePosts(next) {
|
||||
var batch = require('../../batch');
|
||||
|
||||
batch.processSortedSet('posts:pid', function (ids, next) {
|
||||
async.each(ids, function (id, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.rename('pid:' + id + ':users_favourited', 'pid:' + id + ':users_bookmarked', next);
|
||||
},
|
||||
function (next) {
|
||||
db.getObjectField('post:' + id, 'reputation', next);
|
||||
},
|
||||
function (reputation, next) {
|
||||
if (parseInt(reputation, 10)) {
|
||||
db.setObjectField('post:' + id, 'bookmarks', reputation, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
function (next) {
|
||||
db.deleteObjectField('post:' + id, 'reputation', next);
|
||||
},
|
||||
], next);
|
||||
}, next);
|
||||
}, {}, next);
|
||||
}
|
||||
|
||||
function upgradeUsers(next) {
|
||||
var batch = require('../../batch');
|
||||
|
||||
batch.processSortedSet('users:joindate', function (ids, next) {
|
||||
async.each(ids, function (id, next) {
|
||||
db.rename('uid:' + id + ':favourites', 'uid:' + id + ':bookmarks', next);
|
||||
}, next);
|
||||
}, {}, next);
|
||||
}
|
||||
|
||||
async.series([upgradePosts, upgradeUsers], callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,35 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Sorted sets for post replies',
|
||||
timestamp: Date.UTC(2016, 9, 14),
|
||||
method: function (callback) {
|
||||
var posts = require('../../posts');
|
||||
var batch = require('../../batch');
|
||||
batch.processSortedSet('posts:pid', function (ids, next) {
|
||||
posts.getPostsFields(ids, ['pid', 'toPid', 'timestamp'], function (err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
async.eachSeries(data, function (postData, next) {
|
||||
if (!parseInt(postData.toPid, 10)) {
|
||||
return next(null);
|
||||
}
|
||||
winston.verbose('processing pid: ' + postData.pid + ' toPid: ' + postData.toPid);
|
||||
async.parallel([
|
||||
async.apply(db.sortedSetAdd, 'pid:' + postData.toPid + ':replies', postData.timestamp, postData.pid),
|
||||
async.apply(db.incrObjectField, 'post:' + postData.toPid, 'replies'),
|
||||
], next);
|
||||
}, next);
|
||||
});
|
||||
}, callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,59 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'Update global and user language keys',
|
||||
timestamp: Date.UTC(2016, 10, 22),
|
||||
method: function (callback) {
|
||||
var user = require('../../user');
|
||||
var meta = require('../../meta');
|
||||
var batch = require('../../batch');
|
||||
var newLanguage;
|
||||
async.parallel([
|
||||
function (next) {
|
||||
meta.configs.get('defaultLang', function (err, defaultLang) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (!defaultLang) {
|
||||
return setImmediate(next);
|
||||
}
|
||||
|
||||
newLanguage = defaultLang.replace('_', '-').replace('@', '-x-');
|
||||
if (newLanguage !== defaultLang) {
|
||||
meta.configs.set('defaultLang', newLanguage, next);
|
||||
} else {
|
||||
setImmediate(next);
|
||||
}
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
batch.processSortedSet('users:joindate', function (ids, next) {
|
||||
async.each(ids, function (uid, next) {
|
||||
async.waterfall([
|
||||
async.apply(db.getObjectField, 'user:' + uid + ':settings', 'userLang'),
|
||||
function (language, next) {
|
||||
if (!language) {
|
||||
return setImmediate(next);
|
||||
}
|
||||
|
||||
newLanguage = language.replace('_', '-').replace('@', '-x-');
|
||||
if (newLanguage !== language) {
|
||||
user.setSetting(uid, 'userLang', newLanguage, next);
|
||||
} else {
|
||||
setImmediate(next);
|
||||
}
|
||||
},
|
||||
], next);
|
||||
}, next);
|
||||
}, next);
|
||||
},
|
||||
], callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'Sorted set for pinned topics',
|
||||
timestamp: Date.UTC(2016, 10, 25),
|
||||
method: function (callback) {
|
||||
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) {
|
||||
winston.verbose('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);
|
||||
});
|
||||
}, callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'User_friendly_upgrade_script_name',
|
||||
timestamp: Date.UTC(2017, 0, 1),
|
||||
method: function (callback) {
|
||||
async.waterfall([
|
||||
function (cb) {
|
||||
db.getObject('config', cb);
|
||||
},
|
||||
function (config, cb) {
|
||||
if (!config) {
|
||||
return cb();
|
||||
}
|
||||
|
||||
var keys = ['brand:favicon', 'brand:touchicon', 'og:image', 'brand:logo:url', 'defaultAvatar', 'profile:defaultCovers'];
|
||||
|
||||
keys.forEach(function (key) {
|
||||
var oldValue = config[key];
|
||||
|
||||
if (!oldValue || typeof oldValue !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
config[key] = oldValue.replace(/(?:\/assets)?\/(images|uploads)\//g, '/assets/$1/');
|
||||
});
|
||||
|
||||
db.setObject('config', config, cb);
|
||||
},
|
||||
], callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,67 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'Update global and user sound settings',
|
||||
timestamp: Date.UTC(2017, 1, 25),
|
||||
method: function (callback) {
|
||||
var meta = require('../../meta');
|
||||
var batch = require('../../batch');
|
||||
|
||||
var map = {
|
||||
'notification.mp3': 'Default | Deedle-dum',
|
||||
'waterdrop-high.mp3': 'Default | Water drop (high)',
|
||||
'waterdrop-low.mp3': 'Default | Water drop (low)',
|
||||
};
|
||||
|
||||
async.parallel([
|
||||
function (cb) {
|
||||
var keys = ['chat-incoming', 'chat-outgoing', 'notification'];
|
||||
|
||||
db.getObject('settings:sounds', function (err, settings) {
|
||||
if (err || !settings) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
keys.forEach(function (key) {
|
||||
if (settings[key] && settings[key].indexOf(' | ') === -1) {
|
||||
settings[key] = map[settings[key]] || '';
|
||||
}
|
||||
});
|
||||
|
||||
meta.configs.setMultiple(settings, cb);
|
||||
});
|
||||
},
|
||||
function (cb) {
|
||||
var keys = ['notificationSound', 'incomingChatSound', 'outgoingChatSound'];
|
||||
|
||||
batch.processSortedSet('users:joindate', function (ids, next) {
|
||||
async.each(ids, function (uid, next) {
|
||||
db.getObject('user:' + uid + ':settings', function (err, settings) {
|
||||
if (err || !settings) {
|
||||
return next(err);
|
||||
}
|
||||
var newSettings = {};
|
||||
keys.forEach(function (key) {
|
||||
if (settings[key] && settings[key].indexOf(' | ') === -1) {
|
||||
newSettings[key] = map[settings[key]] || '';
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(newSettings).length) {
|
||||
db.setObject('user:' + uid + ':settings', newSettings, next);
|
||||
} else {
|
||||
setImmediate(next);
|
||||
}
|
||||
});
|
||||
}, next);
|
||||
}, cb);
|
||||
},
|
||||
], callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,89 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'Migrating flags to new schema',
|
||||
timestamp: Date.UTC(2016, 11, 7),
|
||||
method: function (callback) {
|
||||
var batch = require('../../batch');
|
||||
var posts = require('../../posts');
|
||||
var flags = require('../../flags');
|
||||
|
||||
batch.processSortedSet('posts:pid', function (ids, next) {
|
||||
posts.getPostsByPids(ids, 1, function (err, posts) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
posts = posts.filter(function (post) {
|
||||
return post.hasOwnProperty('flags');
|
||||
});
|
||||
|
||||
async.each(posts, function (post, next) {
|
||||
async.parallel({
|
||||
uids: async.apply(db.getSortedSetRangeWithScores, 'pid:' + post.pid + ':flag:uids', 0, -1),
|
||||
reasons: async.apply(db.getSortedSetRange, 'pid:' + post.pid + ':flag:uid:reason', 0, -1),
|
||||
}, function (err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
// Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db)
|
||||
if (!data.uids.length || !data.reasons.length) {
|
||||
return setImmediate(next);
|
||||
}
|
||||
|
||||
// Just take the first entry
|
||||
var datetime = data.uids[0].score;
|
||||
var reason = data.reasons[0].split(':')[1];
|
||||
var flagObj;
|
||||
|
||||
async.waterfall([
|
||||
async.apply(flags.create, 'post', post.pid, data.uids[0].value, reason, datetime),
|
||||
function (_flagObj, next) {
|
||||
flagObj = _flagObj;
|
||||
if (post['flag:state'] || post['flag:assignee']) {
|
||||
flags.update(flagObj.flagId, 1, {
|
||||
state: post['flag:state'],
|
||||
assignee: post['flag:assignee'],
|
||||
datetime: datetime,
|
||||
}, next);
|
||||
} else {
|
||||
setImmediate(next);
|
||||
}
|
||||
},
|
||||
function (next) {
|
||||
if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) {
|
||||
try {
|
||||
var history = JSON.parse(post['flag:history']);
|
||||
history = history.filter(function (event) {
|
||||
return event.type === 'notes';
|
||||
})[0];
|
||||
|
||||
flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp, next);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
} else {
|
||||
setImmediate(next);
|
||||
}
|
||||
},
|
||||
], function (err) {
|
||||
if (err && err.message === '[[error:already-flagged]]') {
|
||||
// Already flagged, no need to parse, but not an error
|
||||
next();
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}, next);
|
||||
});
|
||||
}, callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
|
||||
module.exports = {
|
||||
name: 'New sorted set posts:votes',
|
||||
timestamp: Date.UTC(2017, 1, 27),
|
||||
method: function (callback) {
|
||||
require('../../batch').processSortedSet('posts:pid', function (pids, next) {
|
||||
async.each(pids, function (pid, next) {
|
||||
db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) {
|
||||
if (err || !postData) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10);
|
||||
db.sortedSetAdd('posts:votes', votes, pid, next);
|
||||
});
|
||||
}, next);
|
||||
}, {}, callback);
|
||||
},
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
/* jslint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../../database');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = {
|
||||
name: 'User_friendly_upgrade_script_name',
|
||||
timestamp: Date.UTC(2017, 0, 1),
|
||||
method: function (callback) {
|
||||
// Do stuff here...
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue