Commit graph

11345 commits

Author SHA1 Message Date
Dan Stillman
c19177a469 Reset the account pane if the login session can't be stored
If storing the API key failed, the pane kept showing the spinner and
"Waiting for login…" until the user clicked Cancel, even though the
login attempt was already over.

https://forums.zotero.org/discussion/133418/
2026-08-31 12:21:29 -04:00
Dan Stillman
ace9fa0227 Report the real cause of an OS keystore failure
Every failure shows "User canceled OS unlock entry" no matter what went
wrong. Test the store for the actual state, and give callers a message
describing what couldn't be accessed.
2026-08-31 12:21:29 -04:00
Dan Stillman
57c8c81872 Don't show credential dialogs during automatic syncs
Both the keystore fallback prompt and the migration alert are reachable
from the credential read paths that a sync uses, so don't show if it's
an automatic sync.
2026-08-31 11:55:58 -04:00
Dan Stillman
892898040b Fall back to saving credentials without OS keystore encryption
Some Linux systems have no Secret Service running, and if users can't
change that (e.g., a managed system), storing an API key fails and login
never completes. Offer to store credentials unencrypted instead, and try
to encrypt them on a later read if the keystore becomes usable.

https://forums.zotero.org/discussion/133418/
2026-08-31 11:44:43 -04:00
Dan Stillman
d276ec19c5 Fix the window lookup for OS keystore alerts
'zotero:main' isn't a registered window type, so the lookup always
returned null and the alerts were never shown.

https://forums.zotero.org/discussion/133418/
2026-08-30 14:52:16 -04:00
Dan Stillman
bccaf46ff2 Advanced search: Reopen the conditions menu to the selection on macOS
Firefox 153 renders a menulist's popup as a native macOS menu, which
can't be opened to a submenu, so revealSelectedCondition() has done
nothing on macOS since the upgrade. Opt this popup out of native
rendering.
2026-08-28 14:16:11 -04:00
Dan Stillman
fa94212496 Advanced search: Use the shared find-as-you-type for the conditions menu
The conditions menu had its own find-as-you-type, which
Utilities.Internal.addMenuFindAsYouType() now provides for any menu.
Matching walks the menu rather than a cached list of every condition, so
typing can no longer select a condition the menu doesn't offer --
Collection and Saved Search are removed when the search spans multiple
libraries.

An attachment or annotation condition is shown with a short label inside
its submenu, so pass the full name to match on, which is what the
menulist shows once the condition is selected.
2026-08-28 14:16:10 -04:00
Dan Stillman
c8b841df8a Advanced search: Show subcollections in submenus
The Collection value menu was a flat list of every collection in the
library, with subcollections set apart by an indent (or hyphens before
606d8f19ba). Build it with Utilities.Internal.createMenuForTarget()
instead, using the new 'filter' feature to limit to collections. The new
FAYT helper preserves matching on subcollections.

If a subcollection is selected, open the path down to the subcollection
when opening the menu. (As of fx153, macOS renders popups as native
menus, which can't be opened to a submenu, so revert to non-native menus
there.) A collection in a submenu can't be a menulist's selected item,
so the condition holds the value and sets the menulist's label and icon
itself, with the collection's path as a tooltip.
2026-08-28 14:13:26 -04:00
Dan Stillman
41353f01cf Add find-as-you-type to createMenuForTarget()
A collection in a submenu can't otherwise be reached from the keyboard.
2026-08-28 14:02:41 -04:00
Dan Stillman
dffcbb553f Add a find-as-you-type helper for menus
A menulist's built-in find-as-you-type searches only the direct children
of its menupopup, so an item in a submenu can't be reached from the
keyboard. addMenuFindAsYouType() matches on every item in the menu
instead.

customElements.js opens a focused menulist when a space is pressed,
which would end a search partway through a name, so defer that while a
search is in progress.
2026-08-28 14:02:41 -04:00
Dan Stillman
eb33fa5835 Move the checkmark to the activated target in createMenuForTarget()
The target menu isn't rebuilt between openings, so the checkmark stayed
on the target the menu was built with. In the New Collection dialog,
whose menu belongs to a menulist, that would leave two checkmarks if you
clicked a different collection and then reopened the menu.
2026-08-28 13:43:37 -04:00
Dan Stillman
07745b634b Add a filter option to createMenuForTarget()
A caller can leave out targets it doesn't want, with an excluded target
replaced by its subcollections.
2026-08-28 11:40:26 -04:00
Dan Stillman
c098f5e84d Show the icon on the checked item in a collection menu on macOS
On macOS, the current target in the New Collection dialog and the Add To
menus showed a checkmark in place of its folder icon -- type="checkbox"
makes nsMenuItemX::SetupIcon() skip the item. The `checked` attribute
alone marks it without suppressing the icon. Windows and Linux draw the
check in place of the icon either way.
2026-08-27 21:02:03 -04:00
Dan Stillman
c2052cfff1 Don't add items in imported collections to the import collection
Items are added to the collection created for an import as they're
saved, before the imported collection hierarchy exists, so an item only
in a subcollection ended up in the top collection as well as its own.
Remove those once the hierarchy has been created.

When everything imported belongs to a single top-level collection, that
collection is used for the import rather than being nested inside a
collection named after the file.

https://forums.zotero.org/discussion/133174/
2026-08-27 14:15:19 -04:00
Dan Stillman
61174e2158 Export selected collections as collections
Exporting a collection included only its subcollections, so an item
directly in the collection came through with no collection at all, and
an item in both the collection and a subcollection came through in only
the subcollection.

Exporting a selection of multiple collections exported a flat list of
items with no collections at all.

We now include the selected collection(s), with one exception: if a
saved search is also selected, we export a flat item list, since a
search can't be exported as a collection.
2026-08-27 14:15:19 -04:00
Dan Stillman
e29d8f151e Try to fix Safari extension breakage after app updates
Forcing a Gatekeeper assessment seems to fix the extension when it's
broken. General theory: the system does an assessment while the app is
doing an in-place update, calculates a signature mismatch between the
parent app and the appex (or within one of the bundles?), and caches
that forever, so forcing a reassessment fixes it.
2026-08-27 11:45:03 -04:00
Dan Stillman
8e4ec83abd Rename _checkClientVersion() to _handleClientVersionChange()
The function runs upgrade tasks and records the current version.
2026-08-27 11:38:47 -04:00
Dan Stillman
3b93d33b35 Fix WebDAV downloads with non-ASCII characters in the password
HTTP.download() built the Basic auth header with btoa(), which throws
on code points above 255, so every file download failed immediately
with a TypeError. Requests that go through XMLHttpRequest were
unaffected, since Necko builds the header itself, UTF-8 encoded.

https://forums.zotero.org/discussion/133454/synchronization-error-after-upgrading-to-10-0-1
2026-08-27 10:46:00 -04:00
Dan Stillman
0c9ba2d05c Fix corruption of non-ASCII characters in encrypted credentials
Mozilla's OSKeyStore.encrypt() encodes the string as UTF-8 before
encrypting, but its decrypt() returns the decrypted bytes as a binary
string without decoding them, so a WebDAV password containing non-ASCII
characters came back mojibake and authentication failed.

https://forums.zotero.org/discussion/133465/problem-login-into-webdav-server-with-10-0-1
2026-08-27 10:45:45 -04:00
Dan Stillman
b6efbe880e Pass save options through in Zotero.Collection::removeItems()
addItems() passes its options to Zotero.Item::save(), but removeItems()
dropped everything but skipEditCheck, so callers couldn't batch the
resulting notifications.
2026-08-26 15:24:57 -04:00
Dan Stillman
ff93139cce Localize the search syntax's operators and other words
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
Operators can be typed as the Advanced Search shows them, which every
locale already translates, and new keyword messages cover the join
words, "no"/"has", the units of a relative date, and the range forms.

Each range form is given as an example with its two ends filled in, so
that a locale can say it its own way and each form keeps its own words
(e.g., no "between 1970 to 2000").
2026-08-26 15:19:44 -04:00
Dan Stillman
4a250cd20f Support date and count ranges in the search syntax
"year is between 1970 and 2000", "year:1970-2000", "1970..2000", and
"1970 to 2000" all match values within the range, inclusive of both
ends. Ends can be a year, a month ("added between 2024-02 and
2024-06"), a day ("date:2020-03-01..2020-03-15"), or a count ("number
of tags between 2 and 5").
2026-08-26 15:17:55 -04:00
Mynacol
d153397151
Use versioned libc.so instead of /bin/ln on Linux (#6030)
* Fix js-ctypes-based symlinking on Linux by using `libc.so.6` instead of `libc.so` in `OS.File.unixSymlink()` and `Zotero.File.createSymlink()`
* Use that instead of `/bin/ln`, which doesn't exist on NixOS
* Replace `/bin/ln` with `Zotero.File.createSymlink()` in symlinked-database test

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-08-26 13:02:48 -04:00
Dan Stillman
5d20c692e8 Don't use FSEvents when storage isn't on a local volume
FSEvents is backed by a per-volume journal that only local volumes
have. On a network mount the stream is created and started
successfully but never delivers events, so the watcher would report
that nothing had changed for as long as it was used. Check the volume
with statfs() and fall back to scanning.
2026-08-26 10:36:54 -04:00
Dan Stillman
a1ea7037c2 Fix locally missing attachments never being downloaded
Since f21e1b2d32, a full local file scan no longer runs periodically and
on every manual sync, so locally missed attachments stayed marked for
upload and were skipped as unavailable instead of being downloaded.
"Reset File Sync History" marked every attachment for upload, including
files that had never been downloaded, so the forced download check added
in 404fc41b88 found nothing to download.

Missing files are now marked for download when the upload queue is
filled, and in at-sync-time mode they're downloaded in the same sync.
The reset marks them for download directly, and downloads are no longer
skipped just because there were no remote storage changes.

https://forums.zotero.org/discussion/133414/
2026-08-26 10:36:54 -04:00
Dan Stillman
6d8198bbfa Don't offer a nonexistent file when dragging an attachment
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
getFilePath() and getFile() return a path whether or not the file
exists, so dragging an attachment that hadn't been downloaded handed
the drop target a path that didn't exist. Missing files also went
unreported by the drag data provider.
2026-08-25 15:25:33 -04:00
Dan Stillman
56eb77b704 Fix dragging attachments to File Explorer
Firefox 140.14 in Zotero 10.0 made the drag transferable's data
principal null for chrome-initiated drags, so the file-promise stream
that File Explorer used couldn't be created and the drop failed with
"Unspecified error". On Windows the promise was just a file:// URL for
the attachment itself, so hand over the file directly instead, and
force a copy so that File Explorer doesn't move it out of storage.

https://forums.zotero.org/discussion/133399/
https://bugzilla.mozilla.org/show_bug.cgi?id=2054665
2026-08-25 15:25:33 -04:00
Dan Stillman
e69ee69218 Fix Software Update window stuck on "Applying update…"
If an update had already finished downloading when the download page was
shown, the page waited for an update-staged notification even when
staging wasn't possible -- e.g., a default Windows installation in
Program Files, which isn't writable -- so it never advanced past
"Applying update…". It now checks whether staging is actually in
progress.

Not yet tested in an updatable build
2026-08-25 11:09:20 -04:00
Adomas Venčkauskas
baa47e3bb9 Remove bibliography when last citation is deleted. Closes #3474 2026-08-25 15:55:11 +03:00
Dan Stillman
753dbf557a Fix stuck "Loading items" at startup
Some checks are pending
CI / Build, Upload (push) Waiting to run
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
The collections tree selects a row as soon as it's initialized, which
raced with the items tree's initialization. If the collections tree won,
onCollectionSelected() threw on the missing items view and no items were
ever loaded.

https://forums.zotero.org/discussion/133379/
https://forums.zotero.org/discussion/133380/
2026-08-24 10:39:10 -04:00
Dan Stillman
786cdea884 Remove dead transaction rollback path
Some checks failed
CI / Detect changes (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Test () (push) Has been cancelled
CI / Test (macOS NFS) (push) Has been cancelled
CI / Test (Windows arm64) (push) Has been cancelled
CI / Test (Windows x64) (push) Has been cancelled
rollbackAllTransactions() called transactionInProgress() and
rollbackTransaction(), neither of which has existed since nested
transaction support was removed in 14d435b8d8, so it would have thrown
had either of its two callers still been reachable. Both are in code
long since replaced: Zotero.Sync.Server, which uses the synchronous
Zotero.DB.columnQuery(), and an error handler in Zotero.Sync.Storage
that nothing calls.

Drop it along with _transactionNestingLevel, _transactionRollback, and
_shutdown, which nothing reads.
2026-08-22 15:09:37 -04:00
Dan Stillman
f2a42bec15 Retry note saves that time out waiting for the database
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
Any error while saving a note prompted the user to restart Zotero, even
a transaction timeout caused by a long-running operation elsewhere.
Nothing has been written when the wait times out, so retry, unless newer
note content has been handed to the editor in the meantime.

https://forums.zotero.org/discussion/133298/
2026-08-21 16:31:18 -04:00
Dan Stillman
070ae8b615 Don't hold the database to optimize the full-text index
Editing a note flagged it stale, and the background drain indexed it and
then ran an FTS5 'optimize' -- a single statement that rewrites the
content index and can hold the shared database for over a minute --
because the queue was empty again. Note saves waiting on the connection
hit the transaction timeout and told the user to restart Zotero.

Merge the index in bounded steps instead, so no statement runs long
enough to keep other queries waiting, and only after enough items have
been indexed to be worth it.

https://forums.zotero.org/discussion/133298/
2026-08-21 16:31:18 -04:00
Dan Stillman
e8055dfdf2 Open the database only once when callers arrive concurrently
_getConnectionAsync() checked for an existing connection and then awaited
several filesystem operations before assigning one, so callers arriving in
that window each opened their own. Only the last was kept, and the rest
stayed open and unreachable, holding a mozStorage thread apiece until
shutdown.
2026-08-21 16:28:36 -04:00
Dan Stillman
9e45191b22 Stop observing idle after the database is closed permanently
A permanently closed connection kept its idle observer, so it went on
being notified and attempting backups for the life of the process. This
affects plugin databases, which are closed permanently when the plugin
shuts down.
2026-08-21 16:28:35 -04:00
Dan Stillman
7243648ed7 Register the DB backup idle observer only once per connection
The observer was added on every open with no matching removal, so each
reopen left behind another registration that received its own idle
notification. On macOS, where the periodic backup closes and reopens the
connection, the registrations accumulated and multiplied the work done
on each idle.

Addresses #6027
2026-08-21 16:28:35 -04:00
Dan Stillman
85e1cbc75c Don't back up an unchanged database
currentDBTime and lastBackupTime became Date objects when the backup was
made asynchronous in 2014, so comparing them with == has tested object
identity and returned false ever since. The interval check that follows
measures from the backup file's mtime, which a copy inherits from the
database file, so an unchanged database was backed up on every idle.

Fixes #6027
2026-08-21 16:28:35 -04:00
Dan Stillman
ad98e84d24 Fix Any Field searches at a non-item result level
Any Field expands to a generic 'field' condition, which the cross-level
code treated as matching only on top-level items. At an attachment
result level it therefore matched attachments whose parent item had the
value, instead of attachments with the value themselves. Since the
condition stands in for every field, it's now treated as matching at any
level those fields live at.
2026-08-21 12:56:05 -04:00
Dan Stillman
77a3a8815e Add a query syntax to the quick search
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
Zotero.SearchQuery turns a query like `by:smith after:2020 tag:"to read"
crispr` or `creator is smith and (tag is foo or bar)` into a
Zotero.Search, matching whatever text is left over using the current
search mode. Anything that doesn't look like a clause is free text, so a
DOI or a title with a colon in it is matched literally.

The search box syntax-highlights the parts of recognized conditions and
offers autocomplete for condition names, for the values of conditions
that have a fixed set of them (like item type), and for tags and
creators from the selected libraries.
2026-08-20 16:09:20 -04:00
Dan Stillman
766cb079a0 Only defer the quick search while a quotation mark is unclosed
Searching was deferred whenever the query contained a double quotation
mark, with an items-pane message to press Enter. Quoted phrases are no
longer expensive now that Everything mode uses full-text search, so
defer only while an unclosed quote means the phrase is still being
typed, and search as soon as it's closed. Enter still forces a search
mid-quote.
2026-08-20 16:09:20 -04:00
Dan Stillman
0ee3e4052e Don't prompt to save an unchanged saved search
https://forums.zotero.org/discussion/133302/zotero-10-questions-about-search
2026-08-20 15:38:13 -04:00
Dan Stillman
0746c665a9 Don't match dates without a sortable year in "is before" searches
Values without a parsable year are stored with a 0000-00-00 sort key,
and the guard meant to exclude them from comparisons checked the full
multipart value, which is always greater than '0000-00-00'.
2026-08-20 13:19:23 -04:00
Dan Stillman
73f4273e2d Make Original Date a date field
Update the global schema to 45, resolve a field's date type through its
base-field mapping in ItemFields.isDate() (to cover priorityDate), and
convert stored values of date-type fields to multipart dates on schema
upgrade.
2026-08-20 12:57:46 -04:00
Dan Stillman
04796dff22 Use date operators for all date-type item fields in searches
Only Date and Accessed were routed to the datefield condition, so other
date fields (e.g., Filing Date) offered only text operators and
couldn't be compared as dates. Text operators are still accepted on
date fields so existing saved searches keep loading and running.
2026-08-20 12:57:46 -04:00
Dan Stillman
6ce33bcd81 Use forward slashes in ZIP entry names on Windows
The ZIP format requires forward slashes, and nsIZipWriter stores
whatever it's given, so entries for files in subdirectories were
written with backslashes.
2026-08-20 12:34:41 -04:00
Dan Stillman
f5a2985a34 Handle unparseable paths in getClosestDirectory()
On Windows, stat rejects a POSIX-style path with
NS_ERROR_FILE_UNRECOGNIZED_PATH rather than a not-found error.
2026-08-20 12:34:41 -04:00
Dan Stillman
dac1ad489f Update utilities submodule
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
And add test for citing date strings wrapped in double quotes

https://forums.zotero.org/discussion/133277/
2026-08-19 15:28:34 -04:00
Dan Stillman
e1759daa00 Show in-window message in style preview instead of progress popup
Since 30b5bf4bb9, the popup opens as a dependent window of the main
window, so it appeared behind the Style Preview window and, on Windows,
brought the main window to the front.

https://forums.zotero.org/discussion/133296/
2026-08-19 10:54:56 -04:00
Dan Stillman
ad1bec62f2 Use offline database backups on network filesystems on Linux
The online backup API writes the backup file through SQLite's default
VFS, whose locking can hang on network filesystems -- its lock upgrades
conflict with the SMB byte-range lock mapping on CIFS mounts and retry
forever -- so fall back to an offline backup, which copies the file
with the connection closed. Extends getFileSystemInfo() to Linux using
statfs f_type magic numbers.
2026-08-19 09:37:38 -04:00
Martynas Bagdonas
db1216d3e2 Fix annotations not appearing when imported while reader is opening
Fixes https://forums.zotero.org/discussion/133270/bug-embedded-pdf-annotations-and-highlights-fail-to-render-in-zotero-reader
2026-08-19 10:06:31 +03:00