Add attachment storage type search filter

This commit is contained in:
nexdep 2026-04-06 15:28:11 -04:00
parent 4398a2bcee
commit 9781b26bcb
6 changed files with 128 additions and 2 deletions

View file

@ -363,6 +363,7 @@
case 'lastRead':
case 'itemType':
case 'fileTypeID':
case 'attachmentStorageType':
case 'publicationTitle':
case 'tag':
case 'note':
@ -490,6 +491,16 @@
this.createValueMenu(rows);
break;
}
case 'attachmentStorageType':
{
let rows = ['stored', 'linked'].map(type => ({
name: Zotero.getString('attachment-storage-type-' + type),
value: type
}));
this.createValueMenu(rows);
break;
}
default:
{
if (operatorsList.value == 'isInTheLast') {
@ -522,7 +533,8 @@
// Drop-down menu
if (this.selectedCondition == 'collection'
|| this.selectedCondition == 'itemType'
|| this.selectedCondition == 'fileTypeID') {
|| this.selectedCondition == 'fileTypeID'
|| this.selectedCondition == 'attachmentStorageType') {
this.querySelector('#valuefield').hidden = true;
this.querySelector('#valuemenu').hidden = false;
this.querySelector('#value-date-age').hidden = true;

View file

@ -1424,6 +1424,37 @@ Zotero.Search.prototype._buildQuery = async function () {
}
skipOperators = true;
break;
case 'attachmentStorageType': {
let linkModes;
switch (condition.value) {
case 'stored':
linkModes = [
Zotero.Attachments.LINK_MODE_IMPORTED_FILE,
Zotero.Attachments.LINK_MODE_IMPORTED_URL
];
break;
case 'linked':
linkModes = [
Zotero.Attachments.LINK_MODE_LINKED_FILE,
Zotero.Attachments.LINK_MODE_LINKED_URL
];
break;
default:
throw ("Invalid attachmentStorageType '" + condition.value
+ "' specified in search.js");
}
condSQL += 'linkMode ';
if (condition.operator == 'isNot') {
condSQL += 'NOT ';
}
condSQL += `IN (${linkModes.map(() => '?').join(', ')})`;
condSQLParams.push(...linkModes);
skipOperators = true;
break;
}
case 'tag':
condSQL += "tagID IN (SELECT tagID FROM tags WHERE ";

View file

@ -355,6 +355,16 @@ Zotero.SearchConditions = new function () {
table: 'itemAttachments',
field: 'fileTypeID'
},
{
name: 'attachmentStorageType',
operators: {
is: true,
isNot: true
},
table: 'itemAttachments',
field: 'linkMode'
},
{
name: 'tagID',

View file

@ -815,6 +815,7 @@ search-conditions-dateModified = Date Modified
search-conditions-fulltextContent = Attachment Content
search-conditions-programmingLanguage = Programming Language
search-conditions-fileTypeID = Attachment File Type
search-conditions-attachmentStorageType = Attachment Storage Type
search-conditions-lastRead = Attachment Last Read
search-conditions-annotationText = Annotation Text
search-conditions-annotationComment = Annotation Comment
@ -851,6 +852,9 @@ file-type-presentation = Presentation
file-type-document = Document
file-type-ebook = Ebook
attachment-storage-type-stored = Stored
attachment-storage-type-linked = Linked
post-upgrade-message = You’ve been upgraded to <span data-l10n-name="post-upgrade-appver">{ -app-name } { $version }</span>! Learn about <a data-l10n-name="new-features-link">what’s new</a>.
post-upgrade-remind-me-later =
.label = { general-remind-me-later }
@ -897,4 +901,3 @@ banner-close-button =
plugins-blocked-plugin =
.message = This plugin has been disabled by { -app-name }.

View file

@ -1073,6 +1073,9 @@
"search-conditions-fileTypeID": {
"string": "Attachment File Type"
},
"search-conditions-attachmentStorageType": {
"string": "Attachment Storage Type"
},
"search-conditions-lastRead": {
"string": "Attachment Last Read"
},
@ -1127,6 +1130,12 @@
"file-type-ebook": {
"string": "Ebook"
},
"attachment-storage-type-stored": {
"string": "Stored"
},
"attachment-storage-type-linked": {
"string": "Linked"
},
"post-upgrade-message": {
"string": "You’ve been upgraded to <span data-l10n-name=\"post-upgrade-appver\">{ app-name } { version }</span>! Learn about <a data-l10n-name=\"new-features-link\">what’s new</a>."
},

View file

@ -127,6 +127,9 @@ describe("Zotero.Search", function () {
var fooItem;
var foobarItem;
var bazItem;
var importedURLItem;
var linkedFileItem;
var linkedURLItem;
var fooItemGroup;
var foobarItemGroup;
var bazItemGroup;
@ -141,6 +144,25 @@ describe("Zotero.Search", function () {
foobarItem = await importFileAttachment("search/foobar.html");
bazItem = await importFileAttachment("search/baz.pdf");
userLibraryID = fooItem.libraryID;
let testPDF = getTestDataDirectory();
testPDF.append('test.pdf');
importedURLItem = await Zotero.Attachments.importSnapshotFromFile({
file: testPDF,
libraryID: userLibraryID,
title: 'imported-url-pdf',
url: 'http://example.com/imported-url.pdf',
contentType: 'application/pdf',
singleFile: true
});
linkedFileItem = await Zotero.Attachments.linkFromFile({
file: OS.Path.join(getTestDataDirectory().path, 'test.pdf'),
title: 'linked-file-pdf'
});
linkedURLItem = await Zotero.Attachments.linkFromURL({
url: 'http://example.com/linked-url',
title: 'linked-url',
contentType: 'text/html'
});
let group = await getGroup();
fooItemGroup = await importFileAttachment("search/foo.html", { libraryID: group.libraryID });
@ -152,6 +174,9 @@ describe("Zotero.Search", function () {
yield fooItem.eraseTx();
yield foobarItem.eraseTx();
yield bazItem.eraseTx();
yield importedURLItem.eraseTx();
yield linkedFileItem.eraseTx();
yield linkedURLItem.eraseTx();
yield fooItemGroup.eraseTx();
yield foobarItemGroup.eraseTx();
yield bazItemGroup.eraseTx();
@ -264,6 +289,42 @@ describe("Zotero.Search", function () {
assert.sameMembers(matches, [fooItem.id, foobarItem.id]);
});
});
describe("attachmentStorageType", function () {
it("should search by stored attachment storage type", async function () {
let s = new Zotero.Search();
s.libraryID = userLibraryID;
s.addCondition('attachmentStorageType', 'is', 'stored');
let matches = await s.search();
assert.sameMembers(matches, [
fooItem.id,
foobarItem.id,
bazItem.id,
importedURLItem.id
]);
});
it("should search by linked attachment storage type", async function () {
let s = new Zotero.Search();
s.libraryID = userLibraryID;
s.addCondition('attachmentStorageType', 'is', 'linked');
let matches = await s.search();
assert.sameMembers(matches, [linkedFileItem.id, linkedURLItem.id]);
});
it("should support isNot", async function () {
let s = new Zotero.Search();
s.libraryID = userLibraryID;
s.addCondition('attachmentStorageType', 'isNot', 'linked');
let matches = await s.search();
assert.sameMembers(matches, [
fooItem.id,
foobarItem.id,
bazItem.id,
importedURLItem.id
]);
});
});
describe("fulltextContent", function () {
it("should find text in HTML files", async function () {