enigma-bbs/core/set_newscan_date.js

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);
}
);
});
}
};