* New ACS class avail @ client.acs
* Experimental / WIP work on issue #79 * Issue # 77: Changes to new_scan.js, temp conf/area swapping, etc. * message area: getMessageConfByAreaTag(), getMessageConfTagByAreaTag(), tempChangeMessageConfAndArea(), getNewMessageCountInAreaForUser()
This commit is contained in:
parent
2baf028e34
commit
8bd61f2d01
|
@ -0,0 +1,53 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ENiGMA½
|
||||||
|
const checkAcs = require('./acs_parser.js').parse;
|
||||||
|
|
||||||
|
// deps
|
||||||
|
const assert = require('assert');
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
class ACS {
|
||||||
|
constructor(client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
check(acs, scope, defaultAcs) {
|
||||||
|
acs = acs ? acs[scope] : defaultAcs;
|
||||||
|
acs = acs || defaultAcs;
|
||||||
|
return checkAcs(acs, { client : this.client } );
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMessageConfRead(conf) {
|
||||||
|
return this.check(conf.acs, 'read', ACS.Defaults.MessageConfRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMessageAreaRead(area) {
|
||||||
|
return this.check(area.acs, 'read', ACS.Defaults.MessageAreaRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
getConditionalValue(condArray, memberName) {
|
||||||
|
assert(_.isArray(condArray));
|
||||||
|
assert(_.isString(memberName));
|
||||||
|
|
||||||
|
const matchCond = condArray.find( cond => {
|
||||||
|
if(_.has(cond, 'acs')) {
|
||||||
|
return checkAcs(cond.acs, { client : this.client } );
|
||||||
|
} else {
|
||||||
|
return true; // no acs check req.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(matchCond) {
|
||||||
|
return matchCond[memberName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ACS.Defaults = {
|
||||||
|
MessageAreaRead : 'GM[users]',
|
||||||
|
MessageConfRead : 'GM[users]',
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = ACS;
|
|
@ -40,6 +40,7 @@ var moduleUtil = require('./module_util.js');
|
||||||
var menuUtil = require('./menu_util.js');
|
var menuUtil = require('./menu_util.js');
|
||||||
var Config = require('./config.js').config;
|
var Config = require('./config.js').config;
|
||||||
var MenuStack = require('./menu_stack.js');
|
var MenuStack = require('./menu_stack.js');
|
||||||
|
const ACS = require('./acs.js');
|
||||||
|
|
||||||
var stream = require('stream');
|
var stream = require('stream');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
@ -54,7 +55,7 @@ exports.Client = Client;
|
||||||
// * http://www.ansi-bbs.org/ansi-bbs-core-server.html
|
// * http://www.ansi-bbs.org/ansi-bbs-core-server.html
|
||||||
//
|
//
|
||||||
|
|
||||||
// :TODO: put this in a common area!!!!
|
// :TODO: put this in a common area!!!!...actually, just replace it with more modern code - see ansi_term.js
|
||||||
function getIntArgArray(array) {
|
function getIntArgArray(array) {
|
||||||
var i = array.length;
|
var i = array.length;
|
||||||
while(i--) {
|
while(i--) {
|
||||||
|
@ -68,19 +69,19 @@ var RE_DEV_ATTR_RESPONSE_ANYWHERE = /(?:\u001b\[)[\=\?]([0-9a-zA-Z\;]+)(c)/;
|
||||||
var RE_META_KEYCODE_ANYWHERE = /(?:\u001b)([a-zA-Z0-9])/;
|
var RE_META_KEYCODE_ANYWHERE = /(?:\u001b)([a-zA-Z0-9])/;
|
||||||
var RE_META_KEYCODE = new RegExp('^' + RE_META_KEYCODE_ANYWHERE.source + '$');
|
var RE_META_KEYCODE = new RegExp('^' + RE_META_KEYCODE_ANYWHERE.source + '$');
|
||||||
var RE_FUNCTION_KEYCODE_ANYWHERE = new RegExp('(?:\u001b+)(O|N|\\[|\\[\\[)(?:' + [
|
var RE_FUNCTION_KEYCODE_ANYWHERE = new RegExp('(?:\u001b+)(O|N|\\[|\\[\\[)(?:' + [
|
||||||
'(\\d+)(?:;(\\d+))?([~^$])',
|
'(\\d+)(?:;(\\d+))?([~^$])',
|
||||||
'(?:M([@ #!a`])(.)(.))', // mouse stuff
|
'(?:M([@ #!a`])(.)(.))', // mouse stuff
|
||||||
'(?:1;)?(\\d+)?([a-zA-Z@])'
|
'(?:1;)?(\\d+)?([a-zA-Z@])'
|
||||||
].join('|') + ')');
|
].join('|') + ')');
|
||||||
|
|
||||||
var RE_FUNCTION_KEYCODE = new RegExp('^' + RE_FUNCTION_KEYCODE_ANYWHERE.source);
|
var RE_FUNCTION_KEYCODE = new RegExp('^' + RE_FUNCTION_KEYCODE_ANYWHERE.source);
|
||||||
var RE_ESC_CODE_ANYWHERE = new RegExp( [
|
var RE_ESC_CODE_ANYWHERE = new RegExp( [
|
||||||
RE_FUNCTION_KEYCODE_ANYWHERE.source,
|
RE_FUNCTION_KEYCODE_ANYWHERE.source,
|
||||||
RE_META_KEYCODE_ANYWHERE.source,
|
RE_META_KEYCODE_ANYWHERE.source,
|
||||||
RE_DSR_RESPONSE_ANYWHERE.source,
|
RE_DSR_RESPONSE_ANYWHERE.source,
|
||||||
RE_DEV_ATTR_RESPONSE_ANYWHERE.source,
|
RE_DEV_ATTR_RESPONSE_ANYWHERE.source,
|
||||||
/\u001b./.source
|
/\u001b./.source
|
||||||
].join('|'));
|
].join('|'));
|
||||||
|
|
||||||
|
|
||||||
function Client(input, output) {
|
function Client(input, output) {
|
||||||
|
@ -95,6 +96,7 @@ function Client(input, output) {
|
||||||
this.currentTheme = { info : { name : 'N/A', description : 'None' } };
|
this.currentTheme = { info : { name : 'N/A', description : 'None' } };
|
||||||
this.lastKeyPressMs = Date.now();
|
this.lastKeyPressMs = Date.now();
|
||||||
this.menuStack = new MenuStack(this);
|
this.menuStack = new MenuStack(this);
|
||||||
|
this.acs = new ACS(this);
|
||||||
|
|
||||||
Object.defineProperty(this, 'node', {
|
Object.defineProperty(this, 'node', {
|
||||||
get : function() {
|
get : function() {
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
// ENiGMA½
|
// ENiGMA½
|
||||||
var loadMenu = require('./menu_util.js').loadMenu;
|
var loadMenu = require('./menu_util.js').loadMenu;
|
||||||
var acsUtil = require('./acs_util.js');
|
|
||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
@ -49,7 +48,7 @@ MenuStack.prototype.next = function(cb) {
|
||||||
var next;
|
var next;
|
||||||
|
|
||||||
if(_.isArray(menuConfig.next)) {
|
if(_.isArray(menuConfig.next)) {
|
||||||
next = acsUtil.getConditionalValue(this.client, menuConfig.next, 'next');
|
next = this.client.acs.getConditionalValue(menuConfig.next, 'next');
|
||||||
if(!next) {
|
if(!next) {
|
||||||
cb(new Error('No matching condition for \'next\'!'));
|
cb(new Error('No matching condition for \'next\'!'));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -6,7 +6,6 @@ const msgDb = require('./database.js').dbs.message;
|
||||||
const Config = require('./config.js').config;
|
const Config = require('./config.js').config;
|
||||||
const Message = require('./message.js');
|
const Message = require('./message.js');
|
||||||
const Log = require('./logger.js').log;
|
const Log = require('./logger.js').log;
|
||||||
const checkAcs = require('./acs_util.js').checkAcs;
|
|
||||||
const msgNetRecord = require('./msg_network.js').recordMessage;
|
const msgNetRecord = require('./msg_network.js').recordMessage;
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
|
@ -24,22 +23,15 @@ exports.getMessageConferenceByTag = getMessageConferenceByTag;
|
||||||
exports.getMessageAreaByTag = getMessageAreaByTag;
|
exports.getMessageAreaByTag = getMessageAreaByTag;
|
||||||
exports.changeMessageConference = changeMessageConference;
|
exports.changeMessageConference = changeMessageConference;
|
||||||
exports.changeMessageArea = changeMessageArea;
|
exports.changeMessageArea = changeMessageArea;
|
||||||
|
exports.tempChangeMessageConfAndArea = tempChangeMessageConfAndArea;
|
||||||
exports.getMessageListForArea = getMessageListForArea;
|
exports.getMessageListForArea = getMessageListForArea;
|
||||||
|
exports.getNewMessageCountInAreaForUser = getNewMessageCountInAreaForUser;
|
||||||
exports.getNewMessagesInAreaForUser = getNewMessagesInAreaForUser;
|
exports.getNewMessagesInAreaForUser = getNewMessagesInAreaForUser;
|
||||||
exports.getMessageAreaLastReadId = getMessageAreaLastReadId;
|
exports.getMessageAreaLastReadId = getMessageAreaLastReadId;
|
||||||
exports.updateMessageAreaLastReadId = updateMessageAreaLastReadId;
|
exports.updateMessageAreaLastReadId = updateMessageAreaLastReadId;
|
||||||
exports.persistMessage = persistMessage;
|
exports.persistMessage = persistMessage;
|
||||||
exports.trimMessageAreasScheduledEvent = trimMessageAreasScheduledEvent;
|
exports.trimMessageAreasScheduledEvent = trimMessageAreasScheduledEvent;
|
||||||
|
|
||||||
const CONF_AREA_RW_ACS_DEFAULT = 'GM[users]';
|
|
||||||
const AREA_MANAGE_ACS_DEFAULT = 'GM[sysops]';
|
|
||||||
|
|
||||||
const AREA_ACS_DEFAULT = {
|
|
||||||
read : CONF_AREA_RW_ACS_DEFAULT,
|
|
||||||
write : CONF_AREA_RW_ACS_DEFAULT,
|
|
||||||
manage : AREA_MANAGE_ACS_DEFAULT,
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Method for sorting Message areas and conferences
|
// Method for sorting Message areas and conferences
|
||||||
// If the sort key is present and is a number, sort in numerical order;
|
// If the sort key is present and is a number, sort in numerical order;
|
||||||
|
@ -67,13 +59,12 @@ function getAvailableMessageConferences(client, options) {
|
||||||
options = options || { includeSystemInternal : false };
|
options = options || { includeSystemInternal : false };
|
||||||
|
|
||||||
// perform ACS check per conf & omit system_internal if desired
|
// perform ACS check per conf & omit system_internal if desired
|
||||||
return _.omit(Config.messageConferences, (v, k) => {
|
return _.omit(Config.messageConferences, (conf, confTag) => {
|
||||||
if(!options.includeSystemInternal && 'system_internal' === k) {
|
if(!options.includeSystemInternal && 'system_internal' === confTag) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const readAcs = v.acs || CONF_AREA_RW_ACS_DEFAULT;
|
return !client.acs.hasMessageConfRead(conf);
|
||||||
return !checkAcs(client, readAcs);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,9 +95,8 @@ function getAvailableMessageAreasByConfTag(confTag, options) {
|
||||||
return areas;
|
return areas;
|
||||||
} else {
|
} else {
|
||||||
// perform ACS check per area
|
// perform ACS check per area
|
||||||
return _.omit(areas, v => {
|
return _.omit(areas, area => {
|
||||||
const readAcs = _.has(v, 'acs.read') ? v.acs.read : CONF_AREA_RW_ACS_DEFAULT;
|
return !options.client.acs.hasMessageAreaRead(area);
|
||||||
return !checkAcs(options.client, readAcs);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,16 +130,15 @@ function getDefaultMessageConferenceTag(client, disableAcsCheck) {
|
||||||
//
|
//
|
||||||
let defaultConf = _.findKey(Config.messageConferences, o => o.default);
|
let defaultConf = _.findKey(Config.messageConferences, o => o.default);
|
||||||
if(defaultConf) {
|
if(defaultConf) {
|
||||||
const acs = Config.messageConferences[defaultConf].acs || CONF_AREA_RW_ACS_DEFAULT;
|
const conf = Config.messageConferences[defaultConf];
|
||||||
if(true === disableAcsCheck || checkAcs(client, acs)) {
|
if(true === disableAcsCheck || client.acs.hasMessageConfRead(conf)) {
|
||||||
return defaultConf;
|
return defaultConf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// just use anything we can
|
// just use anything we can
|
||||||
defaultConf = _.findKey(Config.messageConferences, (o, k) => {
|
defaultConf = _.findKey(Config.messageConferences, (conf, confTag) => {
|
||||||
const acs = o.acs || CONF_AREA_RW_ACS_DEFAULT;
|
return 'system_internal' !== confTag && (true === disableAcsCheck || client.acs.hasMessageConfRead(conf));
|
||||||
return 'system_internal' !== k && (true === disableAcsCheck || checkAcs(client, acs));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return defaultConf;
|
return defaultConf;
|
||||||
|
@ -169,15 +158,14 @@ function getDefaultMessageAreaTagByConfTag(client, confTag, disableAcsCheck) {
|
||||||
const areaPool = Config.messageConferences[confTag].areas;
|
const areaPool = Config.messageConferences[confTag].areas;
|
||||||
let defaultArea = _.findKey(areaPool, o => o.default);
|
let defaultArea = _.findKey(areaPool, o => o.default);
|
||||||
if(defaultArea) {
|
if(defaultArea) {
|
||||||
const readAcs = _.has(areaPool, [ defaultArea, 'acs', 'read' ]) ? areaPool[defaultArea].acs.read : AREA_ACS_DEFAULT.read;
|
const area = areaPool[defaultArea];
|
||||||
if(true === disableAcsCheck || checkAcs(client, readAcs)) {
|
if(true === disableAcsCheck || client.acs.hasMessageAreaRead(area)) {
|
||||||
return defaultArea;
|
return defaultArea;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultArea = _.findKey(areaPool, (o, k) => {
|
defaultArea = _.findKey(areaPool, (area) => {
|
||||||
const readAcs = _.has(areaPool, [ defaultArea, 'acs', 'read' ]) ? areaPool[defaultArea].acs.read : AREA_ACS_DEFAULT.read;
|
return (true === disableAcsCheck || client.acs.hasMessageAreaRead(area));
|
||||||
return (true === disableAcsCheck || checkAcs(client, readAcs));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return defaultArea;
|
return defaultArea;
|
||||||
|
@ -188,6 +176,25 @@ function getMessageConferenceByTag(confTag) {
|
||||||
return Config.messageConferences[confTag];
|
return Config.messageConferences[confTag];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMessageConfByAreaTag(areaTag) {
|
||||||
|
const confs = Config.messageConferences;
|
||||||
|
let conf;
|
||||||
|
_.forEach(confs, (v) => {
|
||||||
|
if(_.has(v, [ 'areas', areaTag ])) {
|
||||||
|
conf = v;
|
||||||
|
return false; // stop iteration
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMessageConfTagByAreaTag(areaTag) {
|
||||||
|
const confs = Config.messageConferences;
|
||||||
|
return Object.keys(confs).find( (confTag) => {
|
||||||
|
return _.has(confs, [ confTag, 'areas', areaTag]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getMessageAreaByTag(areaTag, optionalConfTag) {
|
function getMessageAreaByTag(areaTag, optionalConfTag) {
|
||||||
const confs = Config.messageConferences;
|
const confs = Config.messageConferences;
|
||||||
|
|
||||||
|
@ -234,17 +241,10 @@ function changeMessageConference(client, confTag, cb) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function validateAccess(conf, areaInfo, callback) {
|
function validateAccess(conf, areaInfo, callback) {
|
||||||
const confAcs = conf.acs || CONF_AREA_RW_ACS_DEFAULT;
|
if(!client.acs.hasMessageConfRead(conf) || !client.acs.hasMessageAreaRead(areaInfo.area)) {
|
||||||
|
return callback(new Error('Access denied to message area and/or conference'));
|
||||||
if(!checkAcs(client, confAcs)) {
|
|
||||||
callback(new Error('User does not have access to this conference'));
|
|
||||||
} else {
|
} else {
|
||||||
const areaAcs = _.has(areaInfo, 'area.acs.read') ? areaInfo.area.acs.read : CONF_AREA_RW_ACS_DEFAULT;
|
return callback(null, conf, areaInfo);
|
||||||
if(!checkAcs(client, areaAcs)) {
|
|
||||||
callback(new Error('User does not have access to default area in this conference'));
|
|
||||||
} else {
|
|
||||||
callback(null, conf, areaInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function changeConferenceAndArea(conf, areaInfo, callback) {
|
function changeConferenceAndArea(conf, areaInfo, callback) {
|
||||||
|
@ -268,34 +268,34 @@ function changeMessageConference(client, confTag, cb) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeMessageArea(client, areaTag, cb) {
|
function changeMessageAreaWithOptions(client, areaTag, options, cb) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
async.waterfall(
|
async.waterfall(
|
||||||
[
|
[
|
||||||
function getArea(callback) {
|
function getArea(callback) {
|
||||||
const area = getMessageAreaByTag(areaTag);
|
const area = getMessageAreaByTag(areaTag);
|
||||||
|
return callback(area ? null : new Error('Invalid message areaTag'), area);
|
||||||
if(area) {
|
|
||||||
callback(null, area);
|
|
||||||
} else {
|
|
||||||
callback(new Error('Invalid message area tag'));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
function validateAccess(area, callback) {
|
function validateAccess(area, callback) {
|
||||||
//
|
//
|
||||||
// Need at least *read* to access the area
|
// Need at least *read* to access the area
|
||||||
//
|
//
|
||||||
const readAcs = _.has(area, 'acs.read') ? area.acs.read : CONF_AREA_RW_ACS_DEFAULT;
|
if(!client.acs.hasMessageAreaRead(area)) {
|
||||||
if(!checkAcs(client, readAcs)) {
|
return callback(new Error('Access denied to message area'));
|
||||||
callback(new Error('User does not have access to this area'));
|
|
||||||
} else {
|
} else {
|
||||||
callback(null, area);
|
return callback(null, area);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function changeArea(area, callback) {
|
function changeArea(area, callback) {
|
||||||
client.user.persistProperty('message_area_tag', areaTag, function persisted(err) {
|
if(true === options.persist) {
|
||||||
callback(err, area);
|
client.user.persistProperty('message_area_tag', areaTag, function persisted(err) {
|
||||||
});
|
return callback(err, area);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
client.user.properties['message_area_tag'] = areaTag;
|
||||||
|
return callback(null, area);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
function complete(err, area) {
|
function complete(err, area) {
|
||||||
|
@ -310,6 +310,36 @@ function changeMessageArea(client, areaTag, cb) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Temporairly -- e.g. non-persisted -- change to an area and it's
|
||||||
|
// associated underlying conference. ACS is checked for both.
|
||||||
|
//
|
||||||
|
// This is useful for example when doing a new scan
|
||||||
|
//
|
||||||
|
function tempChangeMessageConfAndArea(client, areaTag) {
|
||||||
|
const area = getMessageAreaByTag(areaTag);
|
||||||
|
const confTag = getMessageConfTagByAreaTag(areaTag);
|
||||||
|
|
||||||
|
if(!area || !confTag) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conf = getMessageConferenceByTag(confTag);
|
||||||
|
|
||||||
|
if(!client.acs.hasMessageConfRead(conf) || !client.acs.hasMessageAreaRead(area)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.user.properties.message_conf_tag = confTag;
|
||||||
|
client.user.properties.message_area_tag = areaTag;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeMessageArea(client, areaTag, cb) {
|
||||||
|
changeMessageAreaWithOptions(client, areaTag, { persist : true }, cb);
|
||||||
|
}
|
||||||
|
|
||||||
function getMessageFromRow(row) {
|
function getMessageFromRow(row) {
|
||||||
return {
|
return {
|
||||||
messageId : row.message_id,
|
messageId : row.message_id,
|
||||||
|
@ -323,6 +353,62 @@ function getMessageFromRow(row) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNewMessageDataInAreaForUserSql(userId, areaTag, lastMessageId, what) {
|
||||||
|
//
|
||||||
|
// Helper for building SQL to fetch either a full message list or simply
|
||||||
|
// a count of new messages based on |what|.
|
||||||
|
//
|
||||||
|
// * If |areaTag| is Message.WellKnownAreaTags.Private,
|
||||||
|
// only messages addressed to |userId| should be returned/counted.
|
||||||
|
//
|
||||||
|
// * Only messages > |lastMessageId| should be returned/counted
|
||||||
|
//
|
||||||
|
const selectWhat = ('count' === what) ?
|
||||||
|
'COUNT() AS count' :
|
||||||
|
'message_id, message_uuid, reply_to_message_id, to_user_name, from_user_name, subject, modified_timestamp, view_count';
|
||||||
|
|
||||||
|
let sql =
|
||||||
|
`SELECT ${selectWhat}
|
||||||
|
FROM message
|
||||||
|
WHERE area_tag = "${areaTag}" AND message_id > ${lastMessageId}`;
|
||||||
|
|
||||||
|
if(Message.WellKnownAreaTags.Private === areaTag) {
|
||||||
|
sql +=
|
||||||
|
` AND message_id in (
|
||||||
|
SELECT message_id
|
||||||
|
FROM message_meta
|
||||||
|
WHERE meta_category = "System" AND meta_name = "${Message.SystemMetaNames.LocalToUserID}" AND meta_value = ${userId}
|
||||||
|
)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if('count' === what) {
|
||||||
|
sql += ';';
|
||||||
|
} else {
|
||||||
|
sql += ' ORDER BY message_id;';
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNewMessageCountInAreaForUser(userId, areaTag, cb) {
|
||||||
|
async.waterfall(
|
||||||
|
[
|
||||||
|
function getLastMessageId(callback) {
|
||||||
|
getMessageAreaLastReadId(userId, areaTag, function fetched(err, lastMessageId) {
|
||||||
|
callback(null, lastMessageId || 0); // note: willingly ignoring any errors here!
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function getCount(lastMessageId, callback) {
|
||||||
|
const sql = getNewMessageDataInAreaForUserSql(userId, areaTag, lastMessageId, 'count');
|
||||||
|
msgDb.get(sql, (err, row) => {
|
||||||
|
return callback(err, row ? row.count : 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function getNewMessagesInAreaForUser(userId, areaTag, cb) {
|
function getNewMessagesInAreaForUser(userId, areaTag, cb) {
|
||||||
//
|
//
|
||||||
// If |areaTag| is Message.WellKnownAreaTags.Private,
|
// If |areaTag| is Message.WellKnownAreaTags.Private,
|
||||||
|
@ -340,6 +426,7 @@ function getNewMessagesInAreaForUser(userId, areaTag, cb) {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function getMessages(lastMessageId, callback) {
|
function getMessages(lastMessageId, callback) {
|
||||||
|
/*
|
||||||
let sql =
|
let sql =
|
||||||
`SELECT message_id, message_uuid, reply_to_message_id, to_user_name, from_user_name, subject, modified_timestamp, view_count
|
`SELECT message_id, message_uuid, reply_to_message_id, to_user_name, from_user_name, subject, modified_timestamp, view_count
|
||||||
FROM message
|
FROM message
|
||||||
|
@ -353,6 +440,8 @@ function getNewMessagesInAreaForUser(userId, areaTag, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sql += ' ORDER BY message_id;';
|
sql += ' ORDER BY message_id;';
|
||||||
|
*/
|
||||||
|
const sql = getNewMessageDataInAreaForUserSql(userId, areaTag, lastMessageId, 'messages');
|
||||||
|
|
||||||
msgDb.each(sql, function msgRow(err, row) {
|
msgDb.each(sql, function msgRow(err, row) {
|
||||||
if(!err) {
|
if(!err) {
|
||||||
|
|
|
@ -138,63 +138,35 @@ function NewScanModule(options) {
|
||||||
// Advance to next area if possible
|
// Advance to next area if possible
|
||||||
if(sortedAreas.length >= self.currentScanAux.area + 1) {
|
if(sortedAreas.length >= self.currentScanAux.area + 1) {
|
||||||
self.currentScanAux.area += 1;
|
self.currentScanAux.area += 1;
|
||||||
callback(null);
|
return callback(null);
|
||||||
} else {
|
} else {
|
||||||
self.updateScanStatus(self.scanCompleteMsg);
|
self.updateScanStatus(self.scanCompleteMsg);
|
||||||
callback(new Error('No more areas'));
|
return callback(new Error('No more areas')); // this will stop our scan
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function updateStatusScanStarted(callback) {
|
function updateStatusScanStarted(callback) {
|
||||||
self.updateScanStatus(self.scanStartFmt.format(getFormatObj()));
|
self.updateScanStatus(self.scanStartFmt.format(getFormatObj()));
|
||||||
callback(null);
|
return callback(null);
|
||||||
},
|
},
|
||||||
function newScanAreaAndGetMessages(callback) {
|
function getNewMessagesCountInArea(callback) {
|
||||||
msgArea.getNewMessagesInAreaForUser(
|
msgArea.getNewMessageCountInAreaForUser(
|
||||||
self.client.user.userId, currentArea.areaTag, function msgs(err, msgList) {
|
self.client.user.userId, currentArea.areaTag, (err, newMessageCount) => {
|
||||||
if(!err) {
|
callback(err, newMessageCount);
|
||||||
if(0 === msgList.length) {
|
|
||||||
self.updateScanStatus(self.scanFinishNoneFmt.format(getFormatObj()));
|
|
||||||
} else {
|
|
||||||
const formatObj = Object.assign(getFormatObj(), { count : msgList.length } );
|
|
||||||
self.updateScanStatus(self.scanFinishNewFmt.format(formatObj));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callback(err, msgList);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
function displayMessageList(msgList) {
|
function displayMessageList(newMessageCount) {
|
||||||
if(msgList && msgList.length > 0) {
|
if(newMessageCount <= 0) {
|
||||||
const nextModuleOpts = {
|
return self.newScanMessageArea(conf, cb); // next area, if any
|
||||||
extraArgs: {
|
|
||||||
messageAreaTag : currentArea.areaTag,
|
|
||||||
messageList : msgList,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// provide a serializer so we don't dump *huge* bits of information to the log
|
|
||||||
// due to the size of |messageList|
|
|
||||||
// https://github.com/trentm/node-bunyan/issues/189
|
|
||||||
//
|
|
||||||
nextModuleOpts.extraArgs.toJSON = function() {
|
|
||||||
let logMsgList;
|
|
||||||
if(this.messageList.length <= 4) {
|
|
||||||
logMsgList = this.messageList;
|
|
||||||
} else {
|
|
||||||
logMsgList = this.messageList.slice(0, 2).concat(this.messageList.slice(-2));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
messageAreaTag : this.messageAreaTag,
|
|
||||||
partialMessageList : logMsgList,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
self.gotoMenu(config.newScanMessageList || 'newScanMessageList', nextModuleOpts);
|
|
||||||
} else {
|
|
||||||
self.newScanMessageArea(conf, cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nextModuleOpts = {
|
||||||
|
extraArgs: {
|
||||||
|
messageAreaTag : currentArea.areaTag,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return self.gotoMenu(config.newScanMessageList || 'newScanMessageList', nextModuleOpts);
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
cb // no more areas
|
cb // no more areas
|
||||||
|
|
|
@ -82,6 +82,23 @@ function MessageListModule(options) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Provide a serializer so we don't dump *huge* bits of information to the log
|
||||||
|
// due to the size of |messageList|. See https://github.com/trentm/node-bunyan/issues/189
|
||||||
|
//
|
||||||
|
modOpts.extraArgs.toJSON = function() {
|
||||||
|
const logMsgList = (this.messageList.length <= 4) ?
|
||||||
|
this.messageList :
|
||||||
|
this.messageList.slice(0, 2).concat(this.messageList.slice(-2));
|
||||||
|
|
||||||
|
return {
|
||||||
|
messageAreaTag : this.messageAreaTag,
|
||||||
|
apprevMessageList : logMsgList,
|
||||||
|
messageCount : this.messageList.length,
|
||||||
|
messageIndex : formData.value.message,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
self.gotoMenu(config.menuViewPost || 'messageAreaViewPost', modOpts);
|
self.gotoMenu(config.menuViewPost || 'messageAreaViewPost', modOpts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,11 +121,28 @@ MessageListModule.prototype.enter = function() {
|
||||||
// Config can specify |messageAreaTag| else it comes from
|
// Config can specify |messageAreaTag| else it comes from
|
||||||
// the user's current area
|
// the user's current area
|
||||||
//
|
//
|
||||||
if(!this.messageAreaTag) {
|
if(this.messageAreaTag) {
|
||||||
this.messageAreaTag = this.client.user.properties.message_area_tag;
|
this.prevMessageConfAndArea = {
|
||||||
|
confTag : this.client.user.properties.message_conf_tag,
|
||||||
|
areaTag : this.client.user.properties.message_area_tag,
|
||||||
|
};
|
||||||
|
if(!messageArea.tempChangeMessageConfAndArea(this.client, this.messageAreaTag)) {
|
||||||
|
// :TODO: Really, checks should have been done & failed before this, but log here!
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.messageAreaTag = this.messageAreaTag = this.client.user.properties.message_area_tag;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MessageListModule.prototype.leave = function() {
|
||||||
|
if(this.prevMessageConfAndArea) {
|
||||||
|
this.client.user.properties.message_conf_tag = this.prevMessageConfAndArea.confTag;
|
||||||
|
this.client.user.properties.message_area_tag = this.prevMessageConfAndArea.areaTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageListModule.super_.prototype.leave.call(this);
|
||||||
|
};
|
||||||
|
|
||||||
MessageListModule.prototype.mciReady = function(mciData, cb) {
|
MessageListModule.prototype.mciReady = function(mciData, cb) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const vc = self.viewControllers.allViews = new ViewController( { client : self.client } );
|
const vc = self.viewControllers.allViews = new ViewController( { client : self.client } );
|
||||||
|
|
Loading…
Reference in New Issue