Merge branch 'master' of github.com:NuSkooler/enigma-bbs into 216-waiting-for-caller
This commit is contained in:
@@ -421,7 +421,8 @@ const DB_INIT_TABLE = {
|
||||
hash_tag_id INTEGER NOT NULL,
|
||||
file_id INTEGER NOT NULL,
|
||||
|
||||
UNIQUE(hash_tag_id, file_id)
|
||||
UNIQUE(hash_tag_id, file_id),
|
||||
FOREIGN KEY(file_id) REFERENCES file(file_id) ON DELETE CASCADE
|
||||
);`
|
||||
);
|
||||
|
||||
@@ -431,7 +432,10 @@ const DB_INIT_TABLE = {
|
||||
user_id INTEGER NOT NULL,
|
||||
rating INTEGER NOT NULL,
|
||||
|
||||
UNIQUE(file_id, user_id)
|
||||
UNIQUE(file_id, user_id),
|
||||
FOREIGN KEY(file_id) REFERENCES file(file_id) ON DELETE CASCADE
|
||||
-- Note that we cannot CASCADE if user_id is removed from user.db
|
||||
-- See processing in oputil's removeUser()
|
||||
);`
|
||||
);
|
||||
|
||||
@@ -447,7 +451,8 @@ const DB_INIT_TABLE = {
|
||||
hash_id VARCHAR NOT NULL,
|
||||
file_id INTEGER NOT NULL,
|
||||
|
||||
UNIQUE(hash_id, file_id)
|
||||
UNIQUE(hash_id, file_id),
|
||||
FOREIGN KEY(file_id) REFERENCES file(file_id) ON DELETE CASCADE
|
||||
);`
|
||||
);
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ exports.getModule = class FileAreaList extends MenuModule {
|
||||
},
|
||||
function display(callback) {
|
||||
return self.displayBrowsePage(false, err => {
|
||||
if(err && 'NORESULTS' === err.reasonCode) {
|
||||
if(err) {
|
||||
self.gotoMenu(self.menuConfig.config.noResultsMenu || 'fileBaseListEntriesNoResults');
|
||||
}
|
||||
return callback(err);
|
||||
@@ -740,7 +740,7 @@ exports.getModule = class FileAreaList extends MenuModule {
|
||||
}
|
||||
|
||||
FileEntry.findFiles(filterCriteria, (err, fileIds) => {
|
||||
this.fileList = fileIds;
|
||||
this.fileList = fileIds || [];
|
||||
return cb(err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -243,6 +243,15 @@ module.exports = class FileEntry {
|
||||
);
|
||||
}
|
||||
|
||||
static removeUserRatings(userId, cb) {
|
||||
return fileDb.run(
|
||||
`DELETE FROM file_user_rating
|
||||
WHERE user_id = ?;`,
|
||||
[ userId ],
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
||||
static persistMetaValue(fileId, name, value, transOrDb, cb) {
|
||||
if(!_.isFunction(cb) && _.isFunction(transOrDb)) {
|
||||
cb = transOrDb;
|
||||
@@ -457,6 +466,16 @@ module.exports = class FileEntry {
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Find file(s) by |filter|
|
||||
//
|
||||
// - sort: sort results by any well known name, file_id, or user_rating
|
||||
// - terms: one or more search terms to search within filenames as well
|
||||
// as short and long descriptions. We attempt to use the FTS ability when
|
||||
// possible, but want to allow users to search for wildcard matches in
|
||||
// which some cases we'll use multiple LIKE queries.
|
||||
// See _normalizeFileSearchTerms()
|
||||
//
|
||||
static findFiles(filter, cb) {
|
||||
filter = filter || {};
|
||||
|
||||
@@ -500,8 +519,8 @@ module.exports = class FileEntry {
|
||||
if('user_rating' === filter.sort) {
|
||||
sql =
|
||||
`SELECT DISTINCT f.file_id,
|
||||
(SELECT IFNULL(AVG(rating), 0) rating
|
||||
FROM file_user_rating
|
||||
(SELECT IFNULL(AVG(rating), 0) rating
|
||||
FROM file_user_rating
|
||||
WHERE file_id = f.file_id)
|
||||
AS avg_rating
|
||||
FROM file f`;
|
||||
@@ -540,16 +559,16 @@ module.exports = class FileEntry {
|
||||
mp.value = mp.value.replace(/\*/g, '%').replace(/\?/g, '_');
|
||||
appendWhereClause(
|
||||
`f.file_id IN (
|
||||
SELECT file_id
|
||||
FROM file_meta
|
||||
SELECT file_id
|
||||
FROM file_meta
|
||||
WHERE meta_name = "${mp.name}" AND meta_value LIKE "${mp.value}"
|
||||
)`
|
||||
);
|
||||
} else {
|
||||
appendWhereClause(
|
||||
`f.file_id IN (
|
||||
SELECT file_id
|
||||
FROM file_meta
|
||||
SELECT file_id
|
||||
FROM file_meta
|
||||
WHERE meta_name = "${mp.name}" AND meta_value = "${mp.value}"
|
||||
)`
|
||||
);
|
||||
@@ -562,14 +581,29 @@ module.exports = class FileEntry {
|
||||
}
|
||||
|
||||
if(filter.terms && filter.terms.length > 0) {
|
||||
// note the ':' in MATCH expr., see https://www.sqlite.org/cvstrac/wiki?p=FullTextIndex
|
||||
appendWhereClause(
|
||||
`f.file_id IN (
|
||||
SELECT rowid
|
||||
FROM file_fts
|
||||
WHERE file_fts MATCH ":${sanitizeString(filter.terms)}"
|
||||
)`
|
||||
);
|
||||
const [terms, queryType] = FileEntry._normalizeFileSearchTerms(filter.terms);
|
||||
|
||||
if ('fts_match' === queryType) {
|
||||
// note the ':' in MATCH expr., see https://www.sqlite.org/cvstrac/wiki?p=FullTextIndex
|
||||
appendWhereClause(
|
||||
`f.file_id IN (
|
||||
SELECT rowid
|
||||
FROM file_fts
|
||||
WHERE file_fts MATCH ":${terms}"
|
||||
)`
|
||||
);
|
||||
} else {
|
||||
appendWhereClause(
|
||||
`(f.file_name LIKE "${terms}" OR
|
||||
f.desc LIKE "${terms}" OR
|
||||
f.desc_long LIKE "${terms}")`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// handle e.g. 1998 -> "1998"
|
||||
if (_.isNumber(filter.tags)) {
|
||||
filter.tags = filter.tags.toString();
|
||||
}
|
||||
|
||||
if(filter.tags && filter.tags.length > 0) {
|
||||
@@ -693,4 +727,46 @@ module.exports = class FileEntry {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static _normalizeFileSearchTerms(terms) {
|
||||
// ensure we have reasonable input to start with
|
||||
terms = sanitizeString(terms.toString());
|
||||
|
||||
// No wildcards?
|
||||
const hasSingleCharWC = terms.indexOf('?') > -1;
|
||||
if (terms.indexOf('*') === -1 && !hasSingleCharWC) {
|
||||
return [ terms, 'fts_match' ];
|
||||
}
|
||||
|
||||
const prepareLike = () => {
|
||||
// Convert * and ? to SQL LIKE style
|
||||
terms = terms.replace(/\*/g, '%').replace(/\?/g, '_');
|
||||
return terms;
|
||||
};
|
||||
|
||||
// Any ? wildcards?
|
||||
if (hasSingleCharWC) {
|
||||
return [ prepareLike(terms), 'like' ];
|
||||
}
|
||||
|
||||
const split = terms.replace(/\s+/g, ' ').split(' ');
|
||||
const useLike = split.some(term => {
|
||||
if (term.indexOf('?') > -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const wcPos = term.indexOf('*');
|
||||
if (wcPos > -1 && wcPos !== term.length - 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (useLike) {
|
||||
return [ prepareLike(terms), 'like' ];
|
||||
}
|
||||
|
||||
return [ terms, 'fts_match' ];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,7 +57,7 @@ Actions:
|
||||
|
||||
lock USERNAME Set a user's status to "locked"
|
||||
|
||||
group USERNAME [+|-]GROUP Adds (+) or removes (-) user from a group
|
||||
group USERNAME [+|~]GROUP Adds (+) or removes (~) user from a group
|
||||
|
||||
list [FILTER] List users with optional FILTER.
|
||||
|
||||
|
||||
@@ -172,6 +172,7 @@ function removeUser(user) {
|
||||
message : [ 'user_message_area_last_read' ],
|
||||
system : [ 'user_event_log', ],
|
||||
user : [ 'user_group_member', 'user' ],
|
||||
file : [ 'file_user_rating']
|
||||
};
|
||||
|
||||
async.eachSeries(Object.keys(DeleteFrom), (dbName, nextDbName) => {
|
||||
@@ -275,7 +276,7 @@ function modUserGroups(user) {
|
||||
let groupName = argv._[argv._.length - 1].toString().replace(/["']/g, ''); // remove any quotes - necessary to allow "-foo"
|
||||
let action = groupName[0]; // + or -
|
||||
|
||||
if('-' === action || '+' === action) {
|
||||
if('-' === action || '+' === action || '~' === action) {
|
||||
groupName = groupName.substr(1);
|
||||
}
|
||||
|
||||
@@ -286,7 +287,7 @@ function modUserGroups(user) {
|
||||
}
|
||||
|
||||
//
|
||||
// Groups are currently arbritary, so do a slight validation
|
||||
// Groups are currently arbitrary, so do a slight validation
|
||||
//
|
||||
if(!/[A-Za-z0-9]+/.test(groupName)) {
|
||||
process.exitCode = ExitCodes.BAD_ARGS;
|
||||
@@ -303,7 +304,7 @@ function modUserGroups(user) {
|
||||
}
|
||||
|
||||
const UserGroup = require('../../core/user_group.js');
|
||||
if('-' === action) {
|
||||
if('-' === action || '~' === action) {
|
||||
UserGroup.removeUserFromGroup(user.userId, groupName, done);
|
||||
} else {
|
||||
UserGroup.addUserToGroup(user.userId, groupName, done);
|
||||
|
||||
Reference in New Issue
Block a user