|
|
@ -3,6 +3,7 @@
|
|
|
|
'use strict';
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
|
|
var async = require('async');
|
|
|
|
var async = require('async');
|
|
|
|
|
|
|
|
var winston = require('winston');
|
|
|
|
var db = require('../database');
|
|
|
|
var db = require('../database');
|
|
|
|
var user = require('../user');
|
|
|
|
var user = require('../user');
|
|
|
|
var analytics = require('../analytics');
|
|
|
|
var analytics = require('../analytics');
|
|
|
@ -171,7 +172,7 @@ module.exports = function(Posts) {
|
|
|
|
}, next);
|
|
|
|
}, next);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
posts: function(next) {
|
|
|
|
posts: function(next) {
|
|
|
|
Posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags']}, next);
|
|
|
|
Posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags', 'flag:assignee', 'flag:state', 'flag:notes', 'flag:history']}, next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, next);
|
|
|
|
}, next);
|
|
|
|
},
|
|
|
|
},
|
|
|
@ -190,6 +191,8 @@ module.exports = function(Posts) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
results.posts.forEach(function(post, index) {
|
|
|
|
results.posts.forEach(function(post, index) {
|
|
|
|
|
|
|
|
var history;
|
|
|
|
|
|
|
|
|
|
|
|
if (post) {
|
|
|
|
if (post) {
|
|
|
|
post.flagReasons = reasons[index];
|
|
|
|
post.flagReasons = reasons[index];
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -197,6 +200,42 @@ module.exports = function(Posts) {
|
|
|
|
|
|
|
|
|
|
|
|
next(null, results.posts);
|
|
|
|
next(null, results.posts);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
async.apply(Posts.expandFlagHistory),
|
|
|
|
|
|
|
|
function(posts, next) {
|
|
|
|
|
|
|
|
// Parse out flag data into its own object inside each post hash
|
|
|
|
|
|
|
|
posts = posts.map(function(postObj) {
|
|
|
|
|
|
|
|
for(var prop in postObj) {
|
|
|
|
|
|
|
|
postObj.flagData = postObj.flagData || {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (postObj.hasOwnProperty(prop) && prop.startsWith('flag:')) {
|
|
|
|
|
|
|
|
postObj.flagData[prop.slice(5)] = postObj[prop];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (prop === 'flag:state') {
|
|
|
|
|
|
|
|
switch(postObj[prop]) {
|
|
|
|
|
|
|
|
case 'open':
|
|
|
|
|
|
|
|
postObj.flagData.labelClass = 'info';
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'wip':
|
|
|
|
|
|
|
|
postObj.flagData.labelClass = 'warning';
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'resolved':
|
|
|
|
|
|
|
|
postObj.flagData.labelClass = 'success';
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'rejected':
|
|
|
|
|
|
|
|
postObj.flagData.labelClass = 'danger';
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
delete postObj[prop];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return postObj;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setImmediate(next.bind(null, null, posts));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
], callback);
|
|
|
|
], callback);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -226,4 +265,130 @@ module.exports = function(Posts) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
], callback);
|
|
|
|
], callback);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Posts.updateFlagData = function(uid, pid, flagObj, callback) {
|
|
|
|
|
|
|
|
// Retrieve existing flag data to compare for history-saving purposes
|
|
|
|
|
|
|
|
var changes = [];
|
|
|
|
|
|
|
|
var changeset = {};
|
|
|
|
|
|
|
|
var prop;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Posts.getPostData(pid, function(err, postData) {
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
|
|
return callback(err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Track new additions
|
|
|
|
|
|
|
|
for(prop in flagObj) {
|
|
|
|
|
|
|
|
if (flagObj.hasOwnProperty(prop) && !postData.hasOwnProperty('flag:' + prop)) {
|
|
|
|
|
|
|
|
changes.push(prop);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Generate changeset for object modification
|
|
|
|
|
|
|
|
if (flagObj.hasOwnProperty(prop)) {
|
|
|
|
|
|
|
|
changeset['flag:' + prop] = flagObj[prop];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Track changed items
|
|
|
|
|
|
|
|
for(prop in postData) {
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
|
|
postData.hasOwnProperty(prop) && prop.startsWith('flag:') &&
|
|
|
|
|
|
|
|
flagObj.hasOwnProperty(prop.slice(5)) &&
|
|
|
|
|
|
|
|
postData[prop] !== flagObj[prop.slice(5)]
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
changes.push(prop.slice(5));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Append changes to history string
|
|
|
|
|
|
|
|
if (changes.length) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
var history = JSON.parse(postData['flag:history'] || '[]');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
changes.forEach(function(property) {
|
|
|
|
|
|
|
|
switch(property) {
|
|
|
|
|
|
|
|
case 'assignee': // intentional fall-through
|
|
|
|
|
|
|
|
case 'state':
|
|
|
|
|
|
|
|
history.unshift({
|
|
|
|
|
|
|
|
uid: uid,
|
|
|
|
|
|
|
|
type: property,
|
|
|
|
|
|
|
|
value: flagObj[property],
|
|
|
|
|
|
|
|
timestamp: Date.now()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'notes':
|
|
|
|
|
|
|
|
history.unshift({
|
|
|
|
|
|
|
|
uid: uid,
|
|
|
|
|
|
|
|
type: property,
|
|
|
|
|
|
|
|
timestamp: Date.now()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
changeset['flag:history'] = JSON.stringify(history);
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
|
|
winston.warn('[posts/updateFlagData] Unable to deserialise post flag history, likely malformed data');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Save flag data into post hash
|
|
|
|
|
|
|
|
Posts.setPostFields(pid, changeset, callback);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Posts.expandFlagHistory = function(posts, callback) {
|
|
|
|
|
|
|
|
// Expand flag history
|
|
|
|
|
|
|
|
async.map(posts, function(post, next) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
var history = JSON.parse(post['flag:history'] || '[]');
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
|
|
winston.warn('[posts/getFlags] Unable to deserialise post flag history, likely malformed data');
|
|
|
|
|
|
|
|
callback(e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async.map(history, function(event, next) {
|
|
|
|
|
|
|
|
event.timestampISO = new Date(event.timestamp).toISOString();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async.parallel([
|
|
|
|
|
|
|
|
function(next) {
|
|
|
|
|
|
|
|
user.getUserFields(event.uid, ['username', 'picture'], function(err, userData) {
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
|
|
return next(err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
event.user = userData;
|
|
|
|
|
|
|
|
next();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
function(next) {
|
|
|
|
|
|
|
|
if (event.type === 'assignee') {
|
|
|
|
|
|
|
|
user.getUserField(parseInt(event.value, 10), 'username', function(err, username) {
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
|
|
return next(err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
event.label = username || 'Unknown user';
|
|
|
|
|
|
|
|
next(null);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} else if (event.type === 'state') {
|
|
|
|
|
|
|
|
event.label = '[[topic:flag_manage_state_' + event.value + ']]';
|
|
|
|
|
|
|
|
setImmediate(next);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
setImmediate(next);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
], function(err) {
|
|
|
|
|
|
|
|
next(err, event);
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}, function(err, history) {
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
|
|
return next(err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
post['flag:history'] = history;
|
|
|
|
|
|
|
|
next(null, post);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}, callback);
|
|
|
|
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|