Added filtering uploads by albumid

This works when listing all uploads as well, but Album column will only
be shown when albumid key is used in the filters.
I plan to someday add Manage Albums menu, which will use "View uploads"
buttons, just like the ones in Manage Users.
This commit is contained in:
Bobby Wibowo 2020-05-03 03:30:50 +07:00
parent 1980d536db
commit d201b03f59
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
5 changed files with 132 additions and 89 deletions

View File

@ -808,8 +808,7 @@ self.list = async (req, res) => {
// Look for any glob operators // Look for any glob operators
const match = pattern.match(/(?<!\\)(\*|\?)/g) const match = pattern.match(/(?<!\\)(\*|\?)/g)
if (match && match.length) { if (match && match.length)
logger.debug(pattern, match)
return { return {
count: match.length, count: match.length,
// Replace glob operators with their SQL equivalents // Replace glob operators with their SQL equivalents
@ -817,17 +816,19 @@ self.list = async (req, res) => {
.replace(/(?<!\\)\*/g, '%') .replace(/(?<!\\)\*/g, '%')
.replace(/(?<!\\)\?/g, '_') .replace(/(?<!\\)\?/g, '_')
} }
} else { else
return { return {
count: 0, count: 0,
// Assume partial match // Assume partial match
escaped: `%${escaped}%` escaped: `%${escaped}%`
} }
}
} }
if (filters) { if (filters) {
let keywords = [] let keywords = [
'albumid'
]
// Only allow filtering by 'ip' and 'user' keys when listing all uploads // Only allow filtering by 'ip' and 'user' keys when listing all uploads
if (all) if (all)
keywords = keywords.concat([ keywords = keywords.concat([
@ -1003,13 +1004,19 @@ self.list = async (req, res) => {
// Parse sort keys // Parse sort keys
if (filterObj.queries.sort) { if (filterObj.queries.sort) {
let allowed = [ let allowed = [
'albumid',
'expirydate', 'expirydate',
'id', 'id',
'name', 'name',
'size', 'size',
'timestamp' 'timestamp'
] ]
// Only allow sorting by 'albumid' when not listing album's uploads
if (req.params.id === undefined)
allowed = allowed.concat([
'albumid'
])
// Only allow sorting by 'ip' and 'userid' columns when listing all uploads // Only allow sorting by 'ip' and 'userid' columns when listing all uploads
if (all) if (all)
allowed = allowed.concat([ allowed = allowed.concat([
@ -1046,86 +1053,102 @@ self.list = async (req, res) => {
} }
function filter () { function filter () {
if (req.params.id !== undefined) { // If listing all uploads
this.where('albumid', req.params.id) if (all)
} else { this.where(function () {
// Sheesh, these look so overbearing... // Filter uploads matching any of the supplied 'user' keys and/or NULL flag
if (!all) // Prioritze exclude keys when both types found
// If not listing all uploads, list user's uploads this.orWhere(function () {
this.where('userid', user.id) if (filterObj.excludeUploaders.length)
else this.orWhereNotIn('userid', filterObj.excludeUploaders.map(v => v.id))
this.where(function () { else if (filterObj.uploaders.length)
// Filter uploads matching any of the supplied 'user' keys and/or NULL flag this.orWhereIn('userid', filterObj.uploaders.map(v => v.id))
// Prioritze exclude keys when both types found // Such overbearing logic for NULL values, smh...
this.orWhere(function () { if ((filterObj.excludeUploaders.length && filterObj.flags.userNull !== false) ||
if (filterObj.excludeUploaders.length) (filterObj.uploaders.length && filterObj.flags.userNull) ||
this.orWhereNotIn('userid', filterObj.excludeUploaders.map(v => v.id)) (!filterObj.excludeUploaders.length && !filterObj.uploaders.length && filterObj.flags.userNull))
else if (filterObj.uploaders.length) this.orWhereNull('userid')
this.orWhereIn('userid', filterObj.uploaders.map(v => v.id)) else if (filterObj.flags.userNull === false)
// Such overbearing logic for NULL values, smh... this.orWhereNotNull('userid')
if ((filterObj.excludeUploaders.length && filterObj.flags.userNull !== false) ||
(filterObj.uploaders.length && filterObj.flags.userNull) ||
(!filterObj.excludeUploaders.length && !filterObj.uploaders.length && filterObj.flags.userNull))
this.orWhereNull('userid')
else if (filterObj.flags.userNull === false)
this.orWhereNotNull('userid')
})
// Filter uploads matching any of the supplied 'ip' keys and/or NULL flag
// Same prioritization logic as above
this.orWhere(function () {
if (filterObj.queries.exclude.ip)
this.orWhereNotIn('ip', filterObj.queries.exclude.ip)
else if (filterObj.queries.ip)
this.orWhereIn('ip', filterObj.queries.ip)
// ...
if ((filterObj.queries.exclude.ip && filterObj.flags.ipNull !== false) ||
(filterObj.queries.ip && filterObj.flags.ipNull) ||
(!filterObj.queries.exclude.ip && !filterObj.queries.ip && filterObj.flags.ipNull))
this.orWhereNull('ip')
else if (filterObj.flags.ipNull === false)
this.orWhereNotNull('ip')
})
}) })
// Then, refine using the supplied 'date' ranges // Filter uploads matching any of the supplied 'ip' keys and/or NULL flag
// Same prioritization logic as above
this.orWhere(function () {
if (filterObj.queries.exclude.ip)
this.orWhereNotIn('ip', filterObj.queries.exclude.ip)
else if (filterObj.queries.ip)
this.orWhereIn('ip', filterObj.queries.ip)
// ...
if ((filterObj.queries.exclude.ip && filterObj.flags.ipNull !== false) ||
(filterObj.queries.ip && filterObj.flags.ipNull) ||
(!filterObj.queries.exclude.ip && !filterObj.queries.ip && filterObj.flags.ipNull))
this.orWhereNull('ip')
else if (filterObj.flags.ipNull === false)
this.orWhereNotNull('ip')
})
})
else
// If not listing all uploads, list user's uploads
this.where('userid', user.id)
// Then, refine using any of the supplied 'albumid' keys and/or NULL flag
// Same prioritization logic as 'userid' and 'ip' above
if (req.params.id === undefined)
this.andWhere(function () { this.andWhere(function () {
if (!filterObj.queries.date) return if (filterObj.queries.exclude.albumid)
if (typeof filterObj.queries.date.from === 'number') this.orWhereNotIn('albumid', filterObj.queries.exclude.albumid)
if (typeof filterObj.queries.date.to === 'number') else if (filterObj.queries.albumid)
this.andWhereBetween('timestamp', [filterObj.queries.date.from, filterObj.queries.date.to]) this.orWhereIn('albumid', filterObj.queries.albumid)
else // ...
this.andWhere('timestamp', '>=', filterObj.queries.date.from) if ((filterObj.queries.exclude.albumid && filterObj.flags.albumidNull !== false) ||
(filterObj.queries.albumid && filterObj.flags.albumidNull) ||
(!filterObj.queries.exclude.albumid && !filterObj.queries.albumid && filterObj.flags.albumidNull))
this.orWhereNull('albumid')
else if (filterObj.flags.ipNull === false)
this.orWhereNotNull('albumid')
})
else if (!all)
// If not listing all uploads, list uploads from user's album
this.andWhere('albumid', req.params.id)
// Then, refine using the supplied 'date' ranges
this.andWhere(function () {
if (!filterObj.queries.date) return
if (typeof filterObj.queries.date.from === 'number')
if (typeof filterObj.queries.date.to === 'number')
this.andWhereBetween('timestamp', [filterObj.queries.date.from, filterObj.queries.date.to])
else else
this.andWhere('timestamp', '<=', filterObj.queries.date.to) this.andWhere('timestamp', '>=', filterObj.queries.date.from)
}) else
this.andWhere('timestamp', '<=', filterObj.queries.date.to)
})
// Then, refine using the supplied 'expiry' ranges // Then, refine using the supplied 'expiry' ranges
this.andWhere(function () { this.andWhere(function () {
if (!filterObj.queries.expiry) return if (!filterObj.queries.expiry) return
if (typeof filterObj.queries.expiry.from === 'number') if (typeof filterObj.queries.expiry.from === 'number')
if (typeof filterObj.queries.expiry.to === 'number') if (typeof filterObj.queries.expiry.to === 'number')
this.andWhereBetween('expirydate', [filterObj.queries.expiry.from, filterObj.queries.expiry.to]) this.andWhereBetween('expirydate', [filterObj.queries.expiry.from, filterObj.queries.expiry.to])
else
this.andWhere('expirydate', '>=', filterObj.queries.date.from)
else else
this.andWhere('expirydate', '<=', filterObj.queries.date.to) this.andWhere('expirydate', '>=', filterObj.queries.date.from)
}) else
this.andWhere('expirydate', '<=', filterObj.queries.date.to)
})
// Then, refine using the supplied keywords against their file names // Then, refine using the supplied keywords against their file names
this.andWhere(function () { this.andWhere(function () {
if (!filterObj.queries.text) return if (!filterObj.queries.text) return
for (const pattern of filterObj.queries.text) for (const pattern of filterObj.queries.text)
this.orWhere('name', 'like', pattern) this.orWhere('name', 'like', pattern)
}) })
// Finally, refine using the supplied exclusions against their file names // Finally, refine using the supplied exclusions against their file names
this.andWhere(function () { this.andWhere(function () {
if (!filterObj.queries.exclude.text) return if (!filterObj.queries.exclude.text) return
for (const pattern of filterObj.queries.exclude.text) for (const pattern of filterObj.queries.exclude.text)
this.orWhere('name', 'not like', pattern) this.orWhere('name', 'not like', pattern)
}) })
}
} }
try { try {
@ -1144,9 +1167,13 @@ self.list = async (req, res) => {
const columns = ['id', 'name', 'userid', 'size', 'timestamp'] const columns = ['id', 'name', 'userid', 'size', 'timestamp']
if (temporaryUploads) if (temporaryUploads)
columns.push('expirydate') columns.push('expirydate')
if (!all || filterObj.queries.albumid || filterObj.queries.exclude.albumid ||
filterObj.flags.albumidNull !== undefined)
columns.push('albumid')
// Only select IPs if we are listing all uploads // Only select IPs if we are listing all uploads
columns.push(all ? 'ip' : 'albumid') if (all)
columns.push('ip')
// Build raw query for order by (sorting) operation // Build raw query for order by (sorting) operation
let orderByRaw let orderByRaw
@ -1177,9 +1204,9 @@ self.list = async (req, res) => {
file.thumb = `thumbs/${file.name.slice(0, -file.extname.length)}.png` file.thumb = `thumbs/${file.name.slice(0, -file.extname.length)}.png`
} }
// If we are not listing all uploads, query album names // If we queried albumid, query album names
let albums = {} let albums = {}
if (!all) { if (columns.includes('albumid')) {
const albumids = files const albumids = files
.map(file => file.albumid) .map(file => file.albumid)
.filter((v, i, a) => { .filter((v, i, a) => {
@ -1188,7 +1215,6 @@ self.list = async (req, res) => {
albums = await db.table('albums') albums = await db.table('albums')
.whereIn('id', albumids) .whereIn('id', albumids)
.where('enabled', 1) .where('enabled', 1)
.where('userid', user.id)
.select('id', 'name') .select('id', 'name')
.then(rows => { .then(rows => {
// Build Object indexed by their IDs // Build Object indexed by their IDs
@ -1214,7 +1240,7 @@ self.list = async (req, res) => {
// If there are no uploads attached to a registered user, send response // If there are no uploads attached to a registered user, send response
if (userids.length === 0) if (userids.length === 0)
return res.json({ success: true, files, count, basedomain }) return res.json({ success: true, files, count, albums, basedomain })
// Query usernames of user IDs from currently selected files // Query usernames of user IDs from currently selected files
usersTable = await db.table('users') usersTable = await db.table('users')
@ -1226,7 +1252,7 @@ self.list = async (req, res) => {
for (const user of usersTable) for (const user of usersTable)
users[user.id] = user.username users[user.id] = user.username
return res.json({ success: true, files, count, users, basedomain }) return res.json({ success: true, files, count, users, albums, basedomain })
} catch (error) { } catch (error) {
// If moderator, capture SQLITE_ERROR and use its error message for the response's description // If moderator, capture SQLITE_ERROR and use its error message for the response's description
let errorString let errorString

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -718,6 +718,7 @@ page.getUploads = (params = {}) => {
page.checkboxes[page.currentView] = table.querySelectorAll('.checkbox[data-action="select"]') page.checkboxes[page.currentView] = table.querySelectorAll('.checkbox[data-action="select"]')
} }
} else { } else {
const allAlbums = params.all && params.filters && params.filters.includes('albumid:')
page.dom.innerHTML = ` page.dom.innerHTML = `
${pagination} ${pagination}
${extraControls} ${extraControls}
@ -729,6 +730,7 @@ page.getUploads = (params = {}) => {
<th><input id="selectAll" class="checkbox" type="checkbox" title="Select all" data-action="select-all"></th> <th><input id="selectAll" class="checkbox" type="checkbox" title="Select all" data-action="select-all"></th>
<th>File name</th> <th>File name</th>
${params.album === undefined ? `<th>${params.all ? 'User' : 'Album'}</th>` : ''} ${params.album === undefined ? `<th>${params.all ? 'User' : 'Album'}</th>` : ''}
${allAlbums ? '<th>Album</th>' : ''}
<th>Size</th> <th>Size</th>
${params.all ? '<th>IP</th>' : ''} ${params.all ? '<th>IP</th>' : ''}
<th>Date</th> <th>Date</th>
@ -755,6 +757,7 @@ page.getUploads = (params = {}) => {
<td class="controls"><input type="checkbox" class="checkbox" title="Select" data-index="${i}" data-action="select"${upload.selected ? ' checked' : ''}></td> <td class="controls"><input type="checkbox" class="checkbox" title="Select" data-index="${i}" data-action="select"${upload.selected ? ' checked' : ''}></td>
<th><a href="${upload.file}" target="_blank" title="${upload.file}">${upload.name}</a></th> <th><a href="${upload.file}" target="_blank" title="${upload.file}">${upload.name}</a></th>
${params.album === undefined ? `<th>${upload.appendix}</th>` : ''} ${params.album === undefined ? `<th>${upload.appendix}</th>` : ''}
${allAlbums ? `<th>${files[i].albumid ? (albums[files[i].albumid] || '') : ''}</th>` : ''}
<td>${upload.prettyBytes}</td> <td>${upload.prettyBytes}</td>
${params.all ? `<td>${upload.ip || ''}</td>` : ''} ${params.all ? `<td>${upload.ip || ''}</td>` : ''}
<td>${upload.prettyDate}</td> <td>${upload.prettyDate}</td>
@ -1021,7 +1024,17 @@ page.uploadFiltersHelp = element => {
To exclude certain users/ips while still listing every other uploads, add negation sign (<code>-</code>) before the keys. To exclude certain users/ips while still listing every other uploads, add negation sign (<code>-</code>) before the keys.
Negation sign can also be used to exclude the special cases mentioned above (i.e. <code>-user:-</code> or <code>-ip:-</code>). Negation sign can also be used to exclude the special cases mentioned above (i.e. <code>-user:-</code> or <code>-ip:-</code>).
` : ''}
If you know the ID of a user's album, you can list its uploads with <b>albumid</b> key.
Negation sign works for this key as well.
` : `
There is only 1 filter key, namely <b>albumid</b>.
This key can be specified more than once.
Special case such as uploads with no albums, use <code>albumid:-</code>.
To exclude certain albums while still listing every other uploads, add negation sign (<code>-</code>) before the keys.
Negation sign can also be used to exclude the special case mentioned above (i.e. <code>-albumid:-</code>).
`}
There are 2 range keys: <b>date</b> (upload date) and <b>expiry</b> (expiry date). There are 2 range keys: <b>date</b> (upload date) and <b>expiry</b> (expiry date).
Their format is: <code>YYYY/MM/DD HH:MM:SS-YYYY/MM/DD HH:MM:SS</code> ("from" date and "to" date respectively). Their format is: <code>YYYY/MM/DD HH:MM:SS-YYYY/MM/DD HH:MM:SS</code> ("from" date and "to" date respectively).
You may specify only one of the dates. You may specify only one of the dates.
@ -1059,7 +1072,11 @@ page.uploadFiltersHelp = element => {
<code>-user:demo -user:"John Doe"</code> <code>-user:demo -user:"John Doe"</code>
- Uploads from IP "127.0.0.1" AND which file names match "*.rar" OR "*.zip": - Uploads from IP "127.0.0.1" AND which file names match "*.rar" OR "*.zip":
<code>ip:127.0.0.1 *.rar *.zip</code> <code>ip:127.0.0.1 *.rar *.zip</code>
` : ''}- Uploads uploaded since "1 June 2019 00:00:00": ` : ''}- Uploads without albums:
<code>albumid:-</code>
- ALL uploads, but NOT the ones from album with ID 69:
<code>-albumid:69</code>
- Uploads uploaded since "1 June 2019 00:00:00":
<code>date:2019/06</code> <code>date:2019/06</code>
- Uploads uploaded between "7 April 2020 12:00:00" and "7 April 2020 23:59:59": - Uploads uploaded between "7 April 2020 12:00:00" and "7 April 2020 23:59:59":
<code>date:2020/04/07 12-2020/04/07 23:59:59</code> <code>date:2020/04/07 12-2020/04/07 23:59:59</code>

View File

@ -1,5 +1,5 @@
{ {
"1": "1588448172", "1": "1588451242",
"2": "1581416390", "2": "1581416390",
"3": "1581416390", "3": "1581416390",
"4": "1581416390", "4": "1581416390",