299 lines
9.9 KiB
JavaScript
299 lines
9.9 KiB
JavaScript
/* jslint node: true */
|
|
'use strict';
|
|
|
|
// ENiGMA½
|
|
const MenuModule = require('./menu_module.js').MenuModule;
|
|
const ViewController = require('./view_controller.js').ViewController;
|
|
const Errors = require('./enig_error.js').Errors;
|
|
const FileEntry = require('./file_entry.js');
|
|
const FileBaseFilters = require('./file_base_filter.js');
|
|
const { getAvailableFileAreaTags } = require('./file_base_area.js');
|
|
const {
|
|
getSortedAvailMessageConferences,
|
|
getSortedAvailMessageAreasByConfTag,
|
|
updateMessageAreaLastReadId,
|
|
getMessageIdNewerThanTimestampByArea,
|
|
} = require('./message_area.js');
|
|
const UserProps = require('./user_property.js');
|
|
|
|
// deps
|
|
const async = require('async');
|
|
const moment = require('moment');
|
|
const _ = require('lodash');
|
|
|
|
exports.moduleInfo = {
|
|
name: 'Set New Scan Date',
|
|
desc: 'Sets new scan date for applicable scans',
|
|
author: 'NuSkooler',
|
|
};
|
|
|
|
const MciViewIds = {
|
|
main: {
|
|
scanDate: 1,
|
|
targetSelection: 2,
|
|
},
|
|
};
|
|
|
|
// :TODO: for messages, we could insert "conf - all areas" into targets, and allow such
|
|
|
|
exports.getModule = class SetNewScanDate extends MenuModule {
|
|
constructor(options) {
|
|
super(options);
|
|
|
|
const config = this.menuConfig.config;
|
|
|
|
this.target = config.target || 'message';
|
|
this.scanDateFormat = config.scanDateFormat || 'YYYYMMDD';
|
|
|
|
this.menuMethods = {
|
|
scanDateSubmit: (formData, extraArgs, cb) => {
|
|
let scanDate = _.get(formData, 'value.scanDate');
|
|
if (!scanDate) {
|
|
return cb(Errors.MissingParam('"scanDate" missing from form data'));
|
|
}
|
|
|
|
scanDate = moment(scanDate, this.scanDateFormat);
|
|
if (!scanDate.isValid()) {
|
|
return cb(
|
|
Errors.Invalid(
|
|
`"${_.get(formData, 'value.scanDate')}" is not a valid date`
|
|
)
|
|
);
|
|
}
|
|
|
|
const targetSelection = _.get(formData, 'value.targetSelection'); // may be undefined if N/A
|
|
|
|
this[`setNewScanDateFor${_.capitalize(this.target)}Base`](
|
|
targetSelection,
|
|
scanDate,
|
|
() => {
|
|
return this.prevMenu(cb);
|
|
}
|
|
);
|
|
},
|
|
};
|
|
}
|
|
|
|
setNewScanDateForMessageBase(targetSelection, scanDate, cb) {
|
|
const target = this.targetSelections[targetSelection];
|
|
if (!target) {
|
|
return cb(
|
|
Errors.UnexpectedState('Unable to get target in which to set new scan')
|
|
);
|
|
}
|
|
|
|
// selected area, or all of 'em
|
|
let updateAreaTags;
|
|
if ('' === target.area.areaTag) {
|
|
updateAreaTags = this.targetSelections
|
|
.map(targetSelection => targetSelection.area.areaTag)
|
|
.filter(areaTag => areaTag); // remove the blank 'all' entry
|
|
} else {
|
|
updateAreaTags = [target.area.areaTag];
|
|
}
|
|
|
|
async.each(
|
|
updateAreaTags,
|
|
(areaTag, nextAreaTag) => {
|
|
getMessageIdNewerThanTimestampByArea(
|
|
areaTag,
|
|
scanDate,
|
|
(err, messageId) => {
|
|
if (err) {
|
|
return nextAreaTag(err);
|
|
}
|
|
|
|
if (!messageId) {
|
|
return nextAreaTag(null); // nothing to do
|
|
}
|
|
|
|
messageId = Math.max(messageId - 1, 0);
|
|
|
|
return updateMessageAreaLastReadId(
|
|
this.client.user.userId,
|
|
areaTag,
|
|
messageId,
|
|
true, // allowOlder
|
|
nextAreaTag
|
|
);
|
|
}
|
|
);
|
|
},
|
|
err => {
|
|
return cb(err);
|
|
}
|
|
);
|
|
}
|
|
|
|
setNewScanDateForFileBase(targetSelection, scanDate, cb) {
|
|
//
|
|
// ENiGMA doesn't currently have the concept of per-area
|
|
// scan pointers for users, so we use all areas avail
|
|
// to the user.
|
|
//
|
|
const filterCriteria = {
|
|
areaTag: getAvailableFileAreaTags(this.client),
|
|
newerThanTimestamp: scanDate,
|
|
limit: 1,
|
|
orderBy: 'upload_timestamp',
|
|
order: 'ascending',
|
|
};
|
|
|
|
FileEntry.findFiles(filterCriteria, (err, fileIds) => {
|
|
if (err) {
|
|
return cb(err);
|
|
}
|
|
|
|
if (!fileIds || 0 === fileIds.length) {
|
|
// nothing to do
|
|
return cb(null);
|
|
}
|
|
|
|
const pointerFileId = Math.max(fileIds[0] - 1, 0);
|
|
|
|
return FileBaseFilters.setFileBaseLastViewedFileIdForUser(
|
|
this.client.user,
|
|
pointerFileId,
|
|
true, // allowOlder
|
|
cb
|
|
);
|
|
});
|
|
}
|
|
|
|
loadAvailMessageBaseSelections(cb) {
|
|
//
|
|
// Create an array of objects with conf/area information per entry,
|
|
// sorted naturally or via the 'sort' member in config
|
|
//
|
|
const selections = [];
|
|
getSortedAvailMessageConferences(this.client).forEach(conf => {
|
|
getSortedAvailMessageAreasByConfTag(conf.confTag, {
|
|
client: this.client,
|
|
}).forEach(area => {
|
|
selections.push({
|
|
conf: {
|
|
confTag: conf.confTag,
|
|
text: conf.conf.name, // standard
|
|
name: conf.conf.name,
|
|
desc: conf.conf.desc,
|
|
},
|
|
area: {
|
|
areaTag: area.areaTag,
|
|
text: area.area.name, // standard
|
|
name: area.area.name,
|
|
desc: area.area.desc,
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
selections.unshift({
|
|
conf: {
|
|
confTag: '',
|
|
text: 'All conferences',
|
|
name: 'All conferences',
|
|
desc: 'All conferences',
|
|
},
|
|
area: {
|
|
areaTag: '',
|
|
text: 'All areas',
|
|
name: 'All areas',
|
|
desc: 'All areas',
|
|
},
|
|
});
|
|
|
|
// Find current conf/area & move it directly under "All"
|
|
const currConfTag = this.client.user.properties[UserProps.MessageConfTag];
|
|
const currAreaTag = this.client.user.properties[UserProps.MessageAreaTag];
|
|
if (currConfTag && currAreaTag) {
|
|
const confAreaIndex = selections.findIndex(confArea => {
|
|
return (
|
|
confArea.conf.confTag === currConfTag &&
|
|
confArea.area.areaTag === currAreaTag
|
|
);
|
|
});
|
|
|
|
if (confAreaIndex > -1) {
|
|
selections.splice(1, 0, selections.splice(confAreaIndex, 1)[0]);
|
|
}
|
|
}
|
|
|
|
this.targetSelections = selections;
|
|
|
|
return cb(null);
|
|
}
|
|
|
|
mciReady(mciData, cb) {
|
|
super.mciReady(mciData, err => {
|
|
if (err) {
|
|
return cb(err);
|
|
}
|
|
|
|
const self = this;
|
|
const vc = self.addViewController(
|
|
'main',
|
|
new ViewController({ client: this.client })
|
|
);
|
|
|
|
async.series(
|
|
[
|
|
function validateConfig(callback) {
|
|
if (!['message', 'file'].includes(self.target)) {
|
|
return callback(
|
|
Errors.Invalid(
|
|
`Invalid "target" in config: ${self.target}`
|
|
)
|
|
);
|
|
}
|
|
// :TOD0: validate scanDateFormat
|
|
return callback(null);
|
|
},
|
|
function loadFromConfig(callback) {
|
|
return vc.loadFromMenuConfig(
|
|
{ callingMenu: self, mciMap: mciData.menu },
|
|
callback
|
|
);
|
|
},
|
|
function loadAvailSelections(callback) {
|
|
switch (self.target) {
|
|
case 'message':
|
|
return self.loadAvailMessageBaseSelections(callback);
|
|
|
|
default:
|
|
return callback(null);
|
|
}
|
|
},
|
|
function populateForm(callback) {
|
|
const today = moment();
|
|
|
|
const scanDateView = vc.getView(MciViewIds.main.scanDate);
|
|
|
|
// :TODO: MaskTextEditView needs some love: If setText() with input that matches the mask, we should ignore the non-mask chars! Hack in place for now
|
|
const scanDateFormat = self.scanDateFormat.replace(
|
|
/[/\-. ]/g,
|
|
''
|
|
);
|
|
scanDateView.setText(today.format(scanDateFormat));
|
|
|
|
if ('message' === self.target) {
|
|
const targetSelectionView = vc.getView(
|
|
MciViewIds.main.targetSelection
|
|
);
|
|
|
|
targetSelectionView.setItems(self.targetSelections);
|
|
targetSelectionView.setFocusItemIndex(0);
|
|
}
|
|
|
|
self.viewControllers.main.resetInitialFocus();
|
|
//vc.switchFocus(MciViewIds.main.scanDate);
|
|
return callback(null);
|
|
},
|
|
],
|
|
err => {
|
|
return cb(err);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
};
|