From 5a0b291a026cf2cd2bf06e7443fbe94cf21c188b Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Wed, 28 Sep 2016 21:54:25 -0600 Subject: [PATCH] * Some WIP on file area releated stuff - various partially implemented pieces coming together * Some changes to database.js: Triggers for FTS were not being created properly * Misc fixes & improvements --- core/acs.js | 6 ++ core/bbs.js | 22 +++- core/config.js | 27 +++++ core/database.js | 44 +++++--- core/door.js | 1 + core/enig_error.js | 2 + core/file_area.js | 31 ++++++ core/file_entry.js | 171 +++++++++++++++++++++++++++++++ core/string_format.js | 8 ++ core/string_util.js | 19 ++++ core/transfer_file.js | 132 ++++++++++++++++++++++++ misc/startup_banner.asc | 9 ++ mods/file_area_list.js | 222 ++++++++++++++++++++++++++++++++++++++++ mods/msg_list.js | 2 +- 14 files changed, 675 insertions(+), 21 deletions(-) create mode 100644 core/file_area.js create mode 100644 core/file_entry.js create mode 100644 core/transfer_file.js create mode 100644 misc/startup_banner.asc create mode 100644 mods/file_area_list.js diff --git a/core/acs.js b/core/acs.js index df89be9b..3d2fd678 100644 --- a/core/acs.js +++ b/core/acs.js @@ -33,6 +33,10 @@ class ACS { return this.check(area.acs, 'read', ACS.Defaults.MessageAreaRead); } + hasFileAreaRead(area) { + return this.check(area.acs, 'read', ACS.Defaults.FileAreaRead); + } + getConditionalValue(condArray, memberName) { assert(_.isArray(condArray)); assert(_.isString(memberName)); @@ -59,6 +63,8 @@ class ACS { ACS.Defaults = { MessageAreaRead : 'GM[users]', MessageConfRead : 'GM[users]', + + FileAreaRead : 'GM[users]', }; module.exports = ACS; \ No newline at end of file diff --git a/core/bbs.js b/core/bbs.js index 36243227..d1ea390f 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -16,6 +16,8 @@ const async = require('async'); const util = require('util'); const _ = require('lodash'); const mkdirs = require('fs-extra').mkdirs; +const fs = require('fs'); +const paths = require('path'); // our main entry point exports.bbsMain = bbsMain; @@ -71,14 +73,23 @@ function bbsMain() { if(err) { console.error('Error initializing: ' + util.inspect(err)); } - callback(err); + return callback(err); }); }, function listenConnections(callback) { - startListening(callback); + return startListening(callback); } ], function complete(err) { + // note this is escaped: + fs.readFile(paths.join(__dirname, '../misc/startup_banner.asc'), 'utf8', (err, banner) => { + console.info('ENiGMA½ Copyright (c) 2014-2016 Bryan Ashby'); + if(!err) { + console.info(banner); + } + console.info('System started!'); + }); + if(err) { console.error('Error initializing: ' + util.inspect(err)); } @@ -87,7 +98,9 @@ function bbsMain() { } function shutdownSystem() { - logger.log.info('Process interrupted, shutting down...'); + const msg = 'Process interrupted. Shutting down...'; + console.info(msg); + logger.log.info(msg); async.series( [ @@ -114,7 +127,8 @@ function shutdownSystem() { } ], () => { - process.exit(); + console.info('Goodbye!'); + return process.exit(); } ); } diff --git a/core/config.js b/core/config.js index c53f37f0..31c77275 100644 --- a/core/config.js +++ b/core/config.js @@ -222,6 +222,33 @@ function getDefaultConfig() { } }, + fileTransferProtocols : { + zmodem8kSz : { + name : 'ZModem 8k', + type : 'external', + external : { + sendCmd : 'sz', // Avail on Debian/Ubuntu based systems as the package "lrzsz" + sendArgs : [ + '--zmodem', '-y', '--try-8k', '--binary', '--restricted', '{filePath}' + ], + escapeTelnet : true, // set to true to escape Telnet codes such as IAC + } + }, + + zmodem8kSexyz : { + name : 'ZModem 8k', + type : 'external', + external : { + // :TODO: Look into shipping sexyz binaries or at least hosting them somewhere for common systems + sendCmd : 'sexyz', + sendArgs : [ + '-telnet', 'sz', '{filePath}' + ], + escapeTelnet : true, // set to true to escape Telnet codes such as IAC + } + } + + }, messageAreaDefaults : { // diff --git a/core/database.js b/core/database.js index e5e74571..f4bfc752 100644 --- a/core/database.js +++ b/core/database.js @@ -175,17 +175,23 @@ const DB_INIT_TABLE = { dbs.message.run( `CREATE TRIGGER IF NOT EXISTS message_before_update BEFORE UPDATE ON message BEGIN DELETE FROM message_fts WHERE docid=old.rowid; - END; + END;` + ); - CREATE TRIGGER IF NOT EXISTS message_before_delete BEFORE DELETE ON message BEGIN + dbs.message.run( + `CREATE TRIGGER IF NOT EXISTS message_before_delete BEFORE DELETE ON message BEGIN DELETE FROM message_fts WHERE docid=old.rowid; - END; + END;` + ); - CREATE TRIGGER IF NOT EXISTS message_after_update AFTER UPDATE ON message BEGIN + dbs.message.run( + `CREATE TRIGGER IF NOT EXISTS message_after_update AFTER UPDATE ON message BEGIN INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message); - END; + END;` + ); - CREATE TRIGGER IF NOT EXISTS message_after_insert AFTER INSERT ON message BEGIN + dbs.message.run( + `CREATE TRIGGER IF NOT EXISTS message_after_insert AFTER INSERT ON message BEGIN INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message); END;` ); @@ -250,7 +256,7 @@ const DB_INIT_TABLE = { file_sha1 VARCHAR NOT NULL, file_name, /* FTS @ file_fts */ desc, /* FTS @ file_fts */ - desc_long, /* FTS @ file_fts */ + desc_long, /* FTS @ file_fts */ upload_by_username VARCHAR NOT NULL, upload_timestamp DATETIME NOT NULL );` @@ -273,18 +279,24 @@ const DB_INIT_TABLE = { dbs.file.run( `CREATE TRIGGER IF NOT EXISTS file_before_update BEFORE UPDATE ON file BEGIN DELETE FROM file_fts WHERE docid=old.rowid; - END; - - CREATE TRIGGER IF NOT EXISTS file_before_delete BEFORE DELETE ON file BEGIN + END;` + ); + + dbs.file.run( + `CREATE TRIGGER IF NOT EXISTS file_before_delete BEFORE DELETE ON file BEGIN DELETE FROM file_fts WHERE docid=old.rowid; - END; + END;` + ); - CREATE TRIGGER IF NOT EXISTS file_after_update AFTER UPDATE ON file BEGIN - INSERT INTO file_fts(docid, file_name, desc, long_desc) VALUES(new.rowid, new.file_name, new.desc, new.long_desc); - END; + dbs.file.run( + `CREATE TRIGGER IF NOT EXISTS file_after_update AFTER UPDATE ON file BEGIN + INSERT INTO file_fts(docid, file_name, desc, desc_long) VALUES(new.rowid, new.file_name, new.desc, new.desc_long); + END;` + ); - CREATE TRIGGER IF NOT EXISTS file_after_insert AFTER INSERT ON file BEGIN - INSERT INTO file_fts(docid, file_name, desc, desc_long) VALUES(new.rowid, new.file_name, new.desc, new.long_desc); + dbs.file.run( + `CREATE TRIGGER IF NOT EXISTS file_after_insert AFTER INSERT ON file BEGIN + INSERT INTO file_fts(docid, file_name, desc, desc_long) VALUES(new.rowid, new.file_name, new.desc, new.desc_long); END;` ); diff --git a/core/door.js b/core/door.js index 302de522..5670db1e 100644 --- a/core/door.js +++ b/core/door.js @@ -100,6 +100,7 @@ Door.prototype.run = function() { } // Expand arg strings, e.g. {dropFile} -> DOOR32.SYS + // :TODO: Use .map() here let args = _.clone(self.exeInfo.args); // we need a copy so the original is not modified for(let i = 0; i < args.length; ++i) { diff --git a/core/enig_error.js b/core/enig_error.js index adf5eef6..4695608b 100644 --- a/core/enig_error.js +++ b/core/enig_error.js @@ -19,6 +19,7 @@ class EnigError extends Error { } } +// :TODO: Just use EnigError for all class EnigMenuError extends EnigError { } exports.EnigError = EnigError; @@ -27,4 +28,5 @@ exports.EnigMenuError = EnigMenuError; exports.Errors = { General : (reason, reasonCode) => new EnigError('An error occurred', -33000, reason, reasonCode), MenuStack : (reason, reasonCode) => new EnigMenuError('Menu stack error', -33001, reason, reasonCode), + DoesNotExist : (reason, reasonCode) => new EnigError('Object does not exist', -33002, reason, reasonCode), }; diff --git a/core/file_area.js b/core/file_area.js new file mode 100644 index 00000000..8026c2ff --- /dev/null +++ b/core/file_area.js @@ -0,0 +1,31 @@ +/* jslint node: true */ +'use strict'; + +// ENiGMA½ +const Config = require('./config.js').config; +const Log = require('./logger.js').log; + +// deps +const _ = require('lodash'); + +exports.getAvailableFileAreas = getAvailableFileAreas; + +exports.getFileAreaByTag = getFileAreaByTag; + +function getAvailableFileAreas(client, options) { + options = options || { includeSystemInternal : false }; + + // perform ACS check per conf & omit system_internal if desired + return _.omit(Config.fileAreas.areas, (area, areaTag) => { + /* if(!options.includeSystemInternal && 'system_internal' === confTag) { + return true; + }*/ + + return !client.acs.hasFileAreaRead(area); + }); +} + + +function getFileAreaByTag(areaTag) { + return Config.fileAreas.areas[areaTag]; +} \ No newline at end of file diff --git a/core/file_entry.js b/core/file_entry.js new file mode 100644 index 00000000..fbb97c78 --- /dev/null +++ b/core/file_entry.js @@ -0,0 +1,171 @@ +/* jslint node: true */ +'use strict'; + +const fileDb = require('./database.js').dbs.file; +const Errors = require('./enig_error.js').Errors; + +// deps +const async = require('async'); +const _ = require('lodash'); + +const FILE_TABLE_MEMBERS = [ + 'file_id', 'area_tag', 'file_sha1', 'file_name', + 'desc', 'desc_long', 'upload_by_username', 'upload_timestamp' +]; + +module.exports = class FileEntry { + constructor(options) { + options = options || {}; + + this.fileId = options.fileId || 0; + this.areaTag = options.areaTag || ''; + this.meta = {}; + this.hashTags = new Set(); + } + + load(fileId, cb) { + const self = this; + + async.series( + [ + function loadBasicEntry(callback) { + fileDb.get( + `SELECT ${FILE_TABLE_MEMBERS.join(', ')} + FROM file + WHERE file_id=? + LIMIT 1;`, + [ fileId ], + (err, file) => { + if(err) { + return callback(err); + } + + if(!file) { + return callback(Errors.DoesNotExist('No file is available by that ID')); + } + + // assign props from |file| + FILE_TABLE_MEMBERS.forEach(prop => { + self[_.camelCase(prop)] = file[prop]; + }); + + return callback(null); + } + ); + }, + function loadMeta(callback) { + return self.loadMeta(callback); + }, + function loadHashTags(callback) { + return self.loadHashTags(callback); + } + ], + err => { + return cb(err); + } + ); + } + + loadMeta(cb) { + fileDb.each( + `SELECT meta_name, meta_value + FROM file_meta + WHERE file_id=?;`, + [ this.fileId ], + (err, meta) => { + if(meta) { + this.meta[meta.meta_name] = meta.meta_value; + } + }, + err => { + return cb(err); + } + ); + } + + loadHashTags(cb) { + fileDb.each( + `SELECT ht.hash_tag_id, ht.hash_tag + FROM hash_tag ht + WHERE ht.hash_tag_id IN ( + SELECT hash_tag_id + FROM file_hash_tag + WHERE file_id=? + );`, + [ this.fileId ], + (err, hashTag) => { + if(hashTag) { + this.hashTags.add(hashTag.hash_tag); + } + }, + err => { + return cb(err); + } + ); + } + + static findFiles(criteria, cb) { + // :TODO: build search here - return [ fileid1, fileid2, ... ] + // free form + // areaTag + // tags + // order by + // sort + + let sql = + `SELECT file_id + FROM file`; + + let sqlWhere = ''; + + function appendWhereClause(clause) { + if(sqlWhere) { + sqlWhere += ' AND '; + } else { + sqlWhere += ' WHERE '; + } + sqlWhere += clause; + } + + if(criteria.areaTag) { + appendWhereClause(`area_tag="${criteria.areaTag}"`); + } + + if(criteria.search) { + appendWhereClause( + `file_id IN ( + SELECT rowid + FROM file_fts + WHERE file_fts MATCH "${criteria.search.replace(/"/g,'""')}" + )` + ); + } + + if(Array.isArray(criteria.hashTags)) { + appendWhereClause( + `file_id IN ( + SELECT file_id + FROM file_hash_tag + WHERE hash_tag_id IN ( + SELECT hash_tag_id + FROM hash_tag + WHERE hash_tag IN (${criteria.hashTags.join(',')}) + ) + )` + ); + } + + // :TODO: criteria.orderBy + // :TODO: criteria.sort + + sql += sqlWhere + ';'; + const matchingFileIds = []; + fileDb.each(sql, (err, fileId) => { + if(fileId) { + matchingFileIds.push(fileId.file_id); + } + }, err => { + return cb(err, matchingFileIds); + }); + } +}; diff --git a/core/string_format.js b/core/string_format.js index 9abac186..dd1ece78 100644 --- a/core/string_format.js +++ b/core/string_format.js @@ -6,6 +6,8 @@ const pad = require('./string_util.js').pad; const stylizeString = require('./string_util.js').stylizeString; const renderStringLength = require('./string_util.js').renderStringLength; const renderSubstr = require('./string_util.js').renderSubstr; +const formatByteSize = require('./string_util.js').formatByteSize; +const formatByteSizeAbbr = require('./string_util.js').formatByteSizeAbbr; // deps const _ = require('lodash'); @@ -265,6 +267,12 @@ const transformers = { styleSmallI : (s) => stylizeString(s, 'small i'), styleMixed : (s) => stylizeString(s, 'mixed'), styleL33t : (s) => stylizeString(s, 'l33t'), + + // toMegs(), toKilobytes(), ... + // toList(), toCommaList(), + sizeWithAbbr : (n) => formatByteSize(n, true, 2), + sizeWithoutAbbr : (n) => formatByteSize(n, false, 2), + sizeAbbr : (n) => formatByteSizeAbbr(n), }; function transformValue(transformerName, value) { diff --git a/core/string_util.js b/core/string_util.js index ab543a4e..0ba8dec9 100644 --- a/core/string_util.js +++ b/core/string_util.js @@ -13,6 +13,8 @@ exports.debugEscapedString = debugEscapedString; exports.stringFromNullTermBuffer = stringFromNullTermBuffer; exports.renderSubstr = renderSubstr; exports.renderStringLength = renderStringLength; +exports.formatByteSizeAbbr = formatByteSizeAbbr; +exports.formatByteSize = formatByteSize; exports.cleanControlCodes = cleanControlCodes; // :TODO: create Unicode verison of this @@ -286,6 +288,23 @@ function renderStringLength(s) { return len; } +const SIZE_ABBRS = [ 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ]; // :) + +function formatByteSizeAbbr(byteSize) { + return SIZE_ABBRS[Math.floor(Math.log(byteSize) / Math.log(1024))]; +} + +function formatByteSize(byteSize, withAbbr, decimals) { + withAbbr = withAbbr || false; + decimals = decimals || 3; + const i = Math.floor(Math.log(byteSize) / Math.log(1024)); + let result = parseFloat((byteSize / Math.pow(1024, i)).toFixed(decimals)); + if(withAbbr) { + result += ` ${SIZE_ABBRS[i]}`; + } + return result; +} + // :TODO: See notes in word_wrap.js about need to consolidate the various ANSI related RegExp's //const REGEXP_ANSI_CONTROL_CODES = /(\x1b\x5b)([\?=;0-9]*?)([0-9A-ORZcf-npsu=><])/g; diff --git a/core/transfer_file.js b/core/transfer_file.js new file mode 100644 index 00000000..4faf5aa3 --- /dev/null +++ b/core/transfer_file.js @@ -0,0 +1,132 @@ +/* jslint node: true */ +'use strict'; + +// enigma-bbs +const MenuModule = require('./menu_module.js').MenuModule; +const Config = require('./config.js').config; +const stringFormat = require('./string_format.js'); + +// deps +const async = require('async'); +const _ = require('lodash'); +const pty = require('ptyw.js'); + +/* + Resources + + ZModem + * http://gallium.inria.fr/~doligez/zmodem/zmodem.txt + * https://github.com/protomouse/synchronet/blob/master/src/sbbs3/zmodem.c +*/ + +exports.moduleInfo = { + name : 'Transfer file', + desc : 'Sends or receives a file(s)', + author : 'NuSkooler', +}; + +exports.getModule = class TransferFileModule extends MenuModule { + constructor(options) { + super(options); + + this.config = this.menuConfig.config || {}; + this.config.protocol = this.config.protocol || 'zmodem8kSz'; + this.config.direction = this.config.direction || 'send'; + + this.protocolConfig = Config.fileTransferProtocols[this.config.protocol]; + + // :TODO: bring in extraArgs for path(s) to send when sending; Allow to hard code in config (e.g. for info pack/static downloads) + } + + restorePipeAfterExternalProc(pipe) { + if(!this.pipeRestored) { + this.pipeRestored = true; + + this.client.term.output.unpipe(pipe); + this.client.term.output.resume(); + } + } + + sendFiles(cb) { + async.eachSeries(this.sendQueue, (filePath, next) => { + // :TODO: built in protocols + // :TODO: use protocol passed in + this.executeExternalProtocolHandler(filePath, err => { + return next(err); + }); + }, err => { + return cb(err); + }); + } + + executeExternalProtocolHandler(filePath, cb) { + const external = this.protocolConfig.external; + const cmd = external[`${this.config.direction}Cmd`]; + const args = external[`${this.config.direction}Args`].map(arg => { + return stringFormat(arg, { + filePath : filePath, + }); + }); + + /*this.client.term.rawWrite(new Buffer( + [ + 255, 253, 0, // IAC DO TRANSMIT_BINARY + 255, 251, 0, // IAC WILL TRANSMIT_BINARY + ] + ));*/ + + const externalProc = pty.spawn(cmd, args, { + cols : this.client.term.termWidth, + rows : this.client.term.termHeight, + // :TODO: cwd + // :TODO: anything else?? + //env : self.exeInfo.env, + }); + + this.client.term.output.pipe(externalProc); + + /*this.client.term.output.on('data', data => { + // let tmp = data.toString('binary').replace(/\xff\xff/g, '\xff'); + // proc.write(new Buffer(tmp, 'binary')); + proc.write(data); + }); + */ + externalProc.on('data', data => { + // needed for things like sz/rz + if(external.escapeTelnet) { + const tmp = data.toString('binary').replace(/\xff/g, '\xff\xff'); + this.client.term.rawWrite(new Buffer(tmp, 'binary')); + } else { + this.client.term.rawWrite(data); + } + }); + + externalProc.once('close', () => { + return this.restorePipeAfterExternalProc(externalProc); + }); + + externalProc.once('exit', exitCode => { + this.restorePipeAfterExternalProc(externalProc); + externalProc.removeAllListeners(); + + return cb(null); + }); + } + + initSequence() { + const self = this; + + async.series( + [ + function validateConfig(callback) { + // :TODO: + return callback(null); + }, + function transferFiles(callback) { + self.sendQueue = [ '/home/nuskooler/Downloads/fdoor100.zip' ]; // :TODO: testing of course + return self.sendFiles(callback); + } + ] + ); + } +}; diff --git a/misc/startup_banner.asc b/misc/startup_banner.asc new file mode 100644 index 00000000..b758e066 --- /dev/null +++ b/misc/startup_banner.asc @@ -0,0 +1,9 @@ +_____________________ _____ ____________________ __________\_ / +\__ ____/\_ ____ \ /____/ / _____ __ \ / ______/ // /___jp! +// __|___// | \// |// | \// | | \// \ /___ /_____ +/____ _____| __________ ___|__| ____| \ / _____ \ +---- \______\ -- |______\ ------ /______/ ---- |______\ - |______\ /__/ // ___/ + /__ _\ + <*> ENiGMA½ // HTTPS://GITHUB.COM/NUSKOOLER/ENIGMA-BBS <*> /__/ + +------------------------------------------------------------------------------- diff --git a/mods/file_area_list.js b/mods/file_area_list.js new file mode 100644 index 00000000..ea6b1507 --- /dev/null +++ b/mods/file_area_list.js @@ -0,0 +1,222 @@ +/* jslint node: true */ +'use strict'; + +// ENiGMA½ +const MenuModule = require('../core/menu_module.js').MenuModule; +const ViewController = require('../core/view_controller.js').ViewController; +const ansi = require('../core/ansi_term.js'); +const theme = require('../core/theme.js'); +const FileEntry = require('../core/file_entry.js'); +const stringFormat = require('../core/string_format.js'); +const FileArea = require('../core/file_area.js'); + +// deps +const async = require('async'); +const _ = require('lodash'); +const moment = require('moment'); + +/* + Misc TODO + * Allow rating to be user defined colors & characters/etc. + * + + + Well known file entry meta values: + * upload_by_username + * upload_by_user_id + * file_md5 + * file_sha256 + * file_crc32 + * est_release_year + * dl_count + * byte_size + * user_rating + * +*/ + +exports.moduleInfo = { + name : 'File Area List', + desc : 'Lists contents of file an file area', + author : 'NuSkooler', +}; + +const FormIds = { + browse : 0, + details : 1, +}; + +const MciViewIds = { + browse : { + desc : 1, + navMenu : 2, + // 10+: customs + }, +}; + +exports.getModule = class FileAreaList extends MenuModule { + + constructor(options) { + super(options); + + const config = this.menuConfig.config; + + if(options.extraArgs) { + this.filterCriteria = options.extraArgs.filterCriteria; + } + + this.filterCriteria = this.filterCriteria || { + // :TODO: set area tag - all in current area by default + }; + } + + enter() { + super.enter(); + } + + leave() { + super.leave(); + } + + initSequence() { + const self = this; + + async.series( + [ + function beforeArt(callback) { + return self.beforeArt(callback); + }, + function display(callback) { + return self.displayBrowsePage(false, callback); + } + ], + () => { + self.finishedLoading(); + } + ); + } + + displayBrowsePage(clearScreen, cb) { + const self = this; + const config = this.menuConfig.config; + + async.waterfall( + [ + function clearAndDisplayArt(callback) { + if (clearScreen) { + self.client.term.rawWrite(ansi.resetScreen()); + } + + theme.displayThemedAsset( + //config.art.browse, + 'FBRWSE', + self.client, + { font : self.menuConfig.font, trailingLF : false }, + (err, artData) => { + return callback(err, artData); + } + ); + }, + function prepeareViewController(artData, callback) { + if(_.isUndefined(self.viewControllers.browse)) { + const vc = self.addViewController( + 'browse', + new ViewController( { client : self.client, formId : FormIds.browse } ) + ); + + const loadOpts = { + callingMenu : self, + mciMap : artData.mciMap, + formId : FormIds.browse, + }; + + return vc.loadFromMenuConfig(loadOpts, callback); + } + + self.viewControllers.view.setFocus(true); + self.viewControllers.view.getView(MciViewIds.view.BBSList).redraw(); + + return callback(null); + }, + function fetchEntryData(callback) { + return self.loadFileIds(callback); + }, + function loadCurrentFileInfo(callback) { + self.currentFileEntry = new FileEntry(); + + self.currentFileEntry.load( self.fileList[ self.fileListPosition ], err => { + return callback(err); + }); + }, + function populateViews(callback) { + if(_.isString(self.currentFileEntry.desc)) { + const descView = self.viewControllers.browse.getView(MciViewIds.browse.desc); + if(descView) { + descView.setText(self.currentFileEntry.desc); + //descView.redraw(); + } + } + + const currEntry = self.currentFileEntry; + const uploadTimestampFormat = config.browseUploadTimestampFormat || config.uploadTimestampFormat || 'YYYY-MMM-DD'; + const area = FileArea.getFileAreaByTag(currEntry.areaTag); + const hashTagsSep = config.hashTagsSep || ', '; + const entryInfo = { + fileId : currEntry.fileId, + areaTag : currEntry.areaTag, + areaName : area.name || 'N/A', + areaDesc : area.desc || 'N/A', + fileSha1 : currEntry.fileSha1, + fileName : currEntry.fileName, + desc : currEntry.desc, + descLong : currEntry.descLong, + uploadByUsername : currEntry.uploadByUsername, + uploadTimestamp : moment(currEntry.uploadTimestamp).format(uploadTimestampFormat), + hashTags : Array.from(currEntry.hashTags).join(hashTagsSep), + }; + + const META_NUMBERS = [ 'byte_size', 'dl_count' ]; + _.forEach(self.currentFileEntry.meta, (value, name) => { + if(META_NUMBERS.indexOf(name) > -1) { + value = parseInt(value); + } + entryInfo[_.camelCase(name)] = value; + }); + + + + // entryInfo.fileSize = 1241234; // :TODO: REMOVE ME! + + // 10+ are custom textviews + let textView; + let customMciId = 10; + + while( (textView = self.viewControllers.browse.getView(customMciId)) ) { + const key = `browseInfoFormat${customMciId}`; + const format = config[key]; + + if(format) { + textView.setText(stringFormat(format, entryInfo)); + } + + ++customMciId; + } + } + ], + err => { + if(cb) { + return cb(err); + } + } + ); + } + + loadFileIds(cb) { + this.fileListPosition = 0; + + FileEntry.findFiles(this.filterCriteria, (err, fileIds) => { + this.fileList = fileIds; + return cb(err); + }); + } + +}; diff --git a/mods/msg_list.js b/mods/msg_list.js index 83b4fe90..dc82d437 100644 --- a/mods/msg_list.js +++ b/mods/msg_list.js @@ -127,7 +127,7 @@ MessageListModule.prototype.enter = function() { if(this.messageAreaTag) { this.tempMessageConfAndAreaSwitch(this.messageAreaTag); } else { - this.messageAreaTag = this.messageAreaTag = this.client.user.properties.message_area_tag; + this.messageAreaTag = this.client.user.properties.message_area_tag; } };