Commit graph

2163 commits

Author SHA1 Message Date
Dan Stillman
f483c80f36 Fix item tree column refresh before the table has mounted
Resetting the columns of an item tree that hadn't rendered its table
yet threw inside a forceUpdate() callback before resolving the promise
ItemTree.notify() was waiting on, so the notification never reached
later observers. Regression from 5ca1fbb167, which dropped the
unmounted-tree check from ItemTree.notify().

https://forums.zotero.org/discussion/134185/
2026-10-12 16:12:41 -04:00
Dan Stillman
a69b55ea96 Mark the first request when using Replace Online Library
Add a restoreToServer=1 query parameter so we can identify restores
server-side if necessary
2026-10-09 14:46:19 -04:00
Dan Stillman
b551fb6705 Add Unrelate Items to item context menu
Shown when two or more selected items are related to each other.
Removes relations among the selected items only.

https://forums.zotero.org/discussion/comment/519130/#Comment_519130
2026-10-09 13:28:48 -04:00
Dan Stillman
c280964d46 Name the parent folder in data directory access-denied error 2026-10-08 12:53:38 -04:00
Dan Stillman
338ce732bd Show correct access-denied error when data directory can't be created
The OS.File.makeDir() shim swallowed IOUtils errors, so a failure to
create the data directory showed as a generic "database cannot be
opened" error. For the default location, just explain and quit instead
of offering to choose another directory.

https://forums.zotero.org/discussion/134124/zotero-not-working-on-a-mac
2026-10-08 12:50:58 -04:00
Dan Stillman
9cbba8c4d2 Local API: Decode + as a space in form-encoded requests
Some checks failed
CI / Detect changes (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Utilities Tests (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
File uploads that encoded spaces as + were stored with literal +
characters in the filename.

https://forums.zotero.org/discussion/134038/
2026-10-06 11:33:29 -04:00
Dan Stillman
09c3c8cd3d Clear MDPI's Akamai bot challenge during Find Full Text
MDPI serves a JS proof-of-work interstitial in place of the article
page, so Find Full Text found nothing. Run the challenge in a hidden
browser when a page's meta refresh points to a registered challenge
host, then retry the page.

Also match meta refresh URL= case-insensitively, since MDPI's is
uppercase, and bound meta refreshes by the redirect limit.

https://forums.zotero.org/discussion/132837/
https://forums.zotero.org/discussion/134079/
2026-10-06 10:36:20 -04:00
Dan Stillman
9071805bdc Shorten the default HTTP retry window
Some checks are pending
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
CI / Detect changes (push) Waiting to run
An hour of retries makes sense for syncing, which runs automatically and
can just spin during server maintenance rather than showing errors that
send people to the forums, but it was also inherited by foreground
requests, where it stalled operations the user was waiting on. Sync and
file syncing now ask for the long window explicitly.
2026-10-05 15:31:24 -04:00
Dan Stillman
cdf10bf62b Don't stall the Find Full Text queue on a failing download
A file URL returning a server error was retried for up to an hour inside
the download, and a 429 or Retry-After was waited out there too. Since
the queue processes one item at a time, that blocked every other
selected item. Throttling now goes to Find Full Text's own per-domain
handling, as it did before 10.0, which also needed to read Retry-After
from a fetch Response and parse HTTP-date values.

https://forums.zotero.org/discussion/133703/
2026-10-05 15:25:25 -04:00
Dan Stillman
a6e814417f Stop retrying indefinitely on a repeated Retry-After
Retry-After was honored unconditionally with no attempt limit, so a server
returning 429 or 503 with the header on every request retried forever.
Retry-After waits and backoff intervals now share the errorDelayMax
budget, with each Retry-After counted as at least a second so that a
value of 0 can't loop forever.
2026-10-05 15:23:57 -04:00
Dan Stillman
9b769d3987 Don't retry failing custom Find Full Text resolvers
A custom resolver returning a 429/5xx inherited Zotero.HTTP's default
retry policy, which could cause the whole queue to stall for up to an
hour -- or indefinitely for a Retry-After -- on a dead resolver, despite
the 5-second timeout on the request.

https://forums.zotero.org/discussion/133642/
2026-10-05 15:08:34 -04:00
Dan Stillman
364181f4e7 Send cookies and Referer from HTTP.download()
Since the switch to fetch() in 0fe31b0f04 (Zotero 10.0.0), download
requests didn't use cookies, and the Referer header was silently dropped
as a forbidden header. Sites that check either -- e.g., IEEE Xplore,
which returns a 502 -- failed during Find Full Text.

https://forums.zotero.org/discussion/133703/
2026-10-05 14:18:34 -04:00
Thenewmanator15
ba2d6ecee4
Fix plugin loading on Firefox 153.3 (untrusted URI) (#6058)
As of Firefox 153.3.0esr, Services.scriptloader refuses jar:file: and file:
URIs unless allowUnsafeURL is passed (Mozilla bug 1974213), so no plugin
loads: bootstrap.js fails with "Trying to load untrusted URI", then
"Plugin ... is missing bootstrap method 'startup'".

Pass allowUnsafeURL when loading a plugin's bootstrap.js and prefs.js,
and preference pane scripts, and temporarily enable
security.allow_unsafe_subscript_loads, which covers loads from plugin
code outside the bootstrap scope.

Also add a test that installs a fixture plugin that loads a script from its XPI
and sets a default pref.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-10-05 10:51:07 -04:00
Dan Stillman
15f6a81181 Refresh tag selector when a tag's type changes between views
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
Switching to a view that has the same tags but with different types
(manual vs. automatic) kept the previous list, so with "Show Automatic"
off a tag could show or hide incorrectly.
2026-10-02 12:38:58 -04:00
Dan Stillman
d8382c5c11 Fix manual tags missing from tag selector with automatic tags hidden
If a view contained manual and automatic tags with the same name, only
one was kept, and it would disappear with "Show Automatic" off.

https://forums.zotero.org/discussion/133869/
2026-10-02 12:38:53 -04:00
Dan Stillman
d7751b93d8 Don't match an item by a trashed child item in a search
A condition that matched a child item in the trash rolled up to its
parent when the search had a top-level (or other ancestor) result level,
or when "Include parent and child items of matching items" was checked.

https://forums.zotero.org/discussion/133836/
2026-09-29 11:34:37 -04:00
Dan Stillman
ebdc1287ac Await saving of recognized parent item before moving attachment under it
A standalone PDF recognized by ISBN wasn't moved under the new parent
item, because the move was attempted before the item had been saved
and had an ID.

https://forums.zotero.org/discussion/133957/
2026-09-29 11:33:12 -04:00
Abe Jellinek
ed8ca1bfce Item merging: Don't merge web link attachments with different URLs
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
https://forums.zotero.org/discussion/133795
2026-09-23 13:53:08 -04:00
Dan Stillman
22a26bb483 Local API: Fix object writes with API-client-supplied keys
Requests that included a key got a 400 with `'primaryData' not
loaded for item`
2026-09-23 13:21:52 -04:00
Dan Stillman
8e7639d73d Show virtual rows (e.g., Trash) matching the collection filter
https://forums.zotero.org/discussion/133833/cant-show-trash-via-collections-search
2026-09-21 23:26:03 -04:00
Dan Stillman
b6f0f6c0ba Show login manager error when a credential write fails outside the keystore
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
The login manager can fail to store a value even when the OS keystore
works -- e.g., if key4.db is read-only, which Firefox 153 triggers
because NSS now creates a new AES key on the first write. Show the
existing corrupted-logins instructions instead of the keystore alert or
the unencrypted-storage offer, neither of which can help.

Also update the corrupted-logins dialog to add an "Open Profile
Directory" button
2026-09-17 10:37:04 -04:00
Dan Stillman
acc4fd5ad2 Pass a temp copy of dragged attachment files on Windows
Allowing only 'copy' for file attachment drags kept File Explorer from
moving files out of 'storage' but locked the cursor at '+' even for
moves within Zotero. Instead, provide the file via a flavor data
provider that copies it to the temp directory when a target asks for it,
so the drag can allow 'copyMove' again and Explorer's move only touches
the copy. A copy's directory is removed once Explorer has moved the file
out of it, at the next drag, or with the temp directory at shutdown.
2026-09-16 22:39:03 -04:00
Dan Stillman
bdb656ad5c Fix dragging file attachments to and from parent items on Windows
Since 56eb77b704, drags of file attachments allow only 'copy' so that
File Explorer doesn't move the file out of storage, but the trees set
dropEffect to 'move' in onDragOver() for drops within Zotero, and OLE
refuses a drop whose dropEffect isn't among the drag's allowed effects.
Have setDropEffect() fall back to an allowed effect and have onDrop()
act on the effect the tree chose, kept in
Zotero.DragDrop.currentDropEffect, rather than on the drop event's
dropEffect.

https://forums.zotero.org/discussion/133765/
2026-09-16 22:38:23 -04:00
Dan Stillman
1d4b8bc89a Fix WebDAV auth for passwords containing '%' followed by two hex digits
fd812070b6 made _parseURI() decode the URL credentials so that
download() could build its own Basic Auth header from the decoded
values, but request() passed the decoded credentials to xmlhttp.open(),
which percent-decodes them again, so a password like "example%41pass"
was sent as "exampleApass". Re-encode the credentials before passing
them to open().

Fixes #6048
2026-09-16 10:04:14 -04:00
Dan Stillman
ab34e9031a Handle overlong filenames in getClosestDirectory()
Some checks are pending
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
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 / Utilities Tests (push) Waiting to run
relinkAttachment() calls getClosestDirectory() before showing the file
picker, and a too-long filename caused the OS.File.stat() in
getClosestDirectory() to throw, which prevented the file picker from
appearing after clicking Locate.

https://forums.zotero.org/discussion/133685/
2026-09-10 09:53:46 -04:00
Dan Stillman
e03920890a Start HiddenBrowser loads from the parent process
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
Firefox ESR 140.15 rejects loads from a content process for URLs that
process couldn't load on its own, including blob: URLs created by chrome
code. Full-text indexing loads HTML attachments through such a URL, so
indexing crashed Zotero with "Illegal load attempt of blob: URL from
web". Loads started by the parent are exempt, so start the load there
and use the child actor only to disable content retargeting.

https://forums.zotero.org/discussion/133661/
2026-09-08 17:08:55 -04:00
Dan Stillman
bdaecbb5cc Detect database corruption reported by a transaction's commit
We only ever checked for corruption errors from transactions in
queryAsync(), so a corruption error raised by the COMMIT that mozStorage
runs itself bypassed the check. A 10.0 schema upgrade -- which heavily
exercises the database -- that failed due to corruption showed "Database
upgrade error" and a single Sqlite.sys.mjs frame instead of the prompt
offering to restore from a backup.

Two users reported this, but it's not clear what triggered it --
corruption usually occurs during a statement, which we did catch. There
may have been some statement transaction whose corruption error was
caught and ignored rather than being left to abort the transaction,
causing SQLite to then block the commit. That's what the test does, and
it fails without the fix.

https://forums.zotero.org/discussion/133611/
2026-09-08 16:01:23 -04:00
Dan Stillman
fc17dcd24a Tell the user to set up syncing again if credentials can't be decrypted
Some checks failed
CI / Build, Upload (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Utilities Tests (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
If the keystore's secret is replaced -- say, after a keyring reset --
the stored value won't be able to be decrypted again, so tell them to
try logging in again instead of trying to fix their keyring.

https://forums.zotero.org/discussion/133603/
2026-09-04 18:12:31 -04:00
Dan Stillman
92dfac8c64 Don't make an unloaded tab reopenable with Undo Close Tab
unload() closes and re-adds the tab, and the close recorded an undo-close
history entry, so Undo Close Tab could open a duplicate of a tab that was
still open.
2026-09-04 15:30:33 -04:00
Dan Stillman
e88b7ee10f Don't clear the hidden flag when refreshing retractions data
_addEntry() wrote the row with REPLACE INTO, listing only itemID and
data, so flag reverted to its default of 0. It's called for every match
on every update, not just new ones, so a retraction the user had hidden
came back as soon as the list changed and Zotero restarted.
2026-09-04 15:07:15 -04:00
Dan Stillman
53bcd30a5f Don't report a window's observers as leaked while it's still closing
A window is marked as closed before its unload handlers run, so the tab
events fired from ZoteroPane.destroy() warned about every observer that
the closing window hadn't torn down yet. The leak check now also requires
the window to be detached from its docShell, and it covers functions. It
caught an observer that outlived its window -- the sync-reminder API key
observer, which is now unregistered on teardown.
2026-09-04 14:40:24 -04:00
Dan Stillman
d0a32a3b4f Poll for expected tag selector state in flaky tests
Selecting a collection doesn't wait for the tag selector to re-render,
so waitForTagSelector() could be resolved by the initial render instead
of the awaited change, and the assertions could run against a stale tag
list.
2026-09-04 14:28:11 -04:00
Dan Stillman
4d94810def Don't send a conditional request for an invalidated retractions cache
The cached retractions list is saved with the current data version from
the code, which we can bump to make clients discard their cached data
and refetch it. We've never actually done that, but if we did, it
would've hit a bug: init() would've started the refetch but still sent
the cached ETag, so if the list itself hadn't changed, the server
would've returned 304 and nothing would have been updated. The stale
version then would have survived, so the refetch would have been retried
on every startup.
2026-09-04 13:27:12 -04:00
Jakob Nylin
e3da52bebc
Match duplicate books by ISBN-10/13 equivalence (#6028)
---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-09-03 12:42:04 -04:00
Dan Stillman
e56fad828d Throw from removed selection methods in pre-release builds
The shims from 5697ee0af7 keep old plugins working for users, but
beta/dev/source builds should still throw to get the attention of
developers and (hopefully breakage-tolerant) beta users.
2026-09-02 22:34:19 -04:00
Dan Stillman
5697ee0af7 Shim removed selection methods when a single row is selected
Plugins written for earlier versions call methods like
CollectionTree#getSelectedSearch(), which now throw. We did this
intentionally to make old code more obviously broken, even with single
selection, so developers would fix their code, but we're not yet
blocking plugins that illegitimately declared compatibility with a
future version, so some haven't been updated and are breaking Zotero.
With a single row selected, the pre-10 functions can technically
continue to work, so for now, restore them and just warn and name the
plugin instead of throwing. They still need to throw if multiple rows
are selected, since plugins that haven't been updated can't safely act
on a selection that might span collections or libraries.

https://forums.zotero.org/discussion/133565/
2026-09-02 22:16:13 -04:00
Dan Stillman
08ed64f17f Fix "false" in Added By/Modified By columns for trashed collections
The row values were computed with `row.isItem && …`, so collection and
search rows in the trash got the boolean `false`, which the table then
rendered as the text "false".

https://forums.zotero.org/discussion/133560/
2026-09-02 12:18:12 -04:00
Dan Stillman
661843b03f Fix sync error due to invalid attachment filename
A stored-file path of 'storage:/' -- left behind by the 128 schema step,
which skipped paths with no basename -- triggered an
NS_ERROR_FILE_UNRECOGNIZED_PATH that aborted the whole
checkForUpdatedFiles() loop, so no files synced in the library. Skip an
attachment that throws instead of failing the whole library.

Also apply the setter's directory-path rule in getFilePath[Async]() to
avoid errors elsewhere.

https://forums.zotero.org/discussion/133523/synchronize-issue
2026-09-01 22:52:54 -04:00
Dan Stillman
a0ce7b3256
Show recently used collections in the Add to Collection menu (#6036)
Collections are now tracked as they're selected, added to, or dropped
on, and the five most recent usable targets are listed above the
full collection hierarchy by full path.
2026-09-01 21:59:20 -04:00
Dan Stillman
4a8a88dd41 Fix error using a CSL 0.8 style
transformToDocument() needs a load group, which it takes from the source
document or from the window that created the XSLTProcessor. Neither of
those has existed since 0f2690eb75 in Zotero 8 stopped taking
XSLTProcessor from the hidden window, so the CSL 0.8 → 1.0 upgrade threw
NS_ERROR_FAILURE. In Word, this showed as "Zotero encountered an error
while updating your document."

https://forums.zotero.org/discussion/133543/
2026-09-01 15:45:48 -04:00
Dan Stillman
984a72a3d9 Fix the startup error shown when the database can't be written to
Regression from b27c4cb023 in 10.0, which added pragma commands that
write to the database. If those failed because the database was
read-only, we would show a raw SQLite error instead of the message about
permissions.

https://forums.zotero.org/discussion/133521/error-for-zotero-10-for-mac
2026-08-31 14:23:53 -04:00
Dan Stillman
e679473db1 Remove citeproc-rs
citeproc-rs is no longer maintained, and people who had enabled the
hidden pref were hitting errors.

This also drops the free() call on CSL engines, which only existed to
free the citeproc-rs wasm driver.

https://forums.zotero.org/discussion/133515/
2026-08-31 12:38:05 -04:00
Dan Stillman
30425e024d Revoke the server API key if it can't be stored locally
The login session creates a key on the server before we try to store it,
so each attempt that failed to save the key left an active key behind.
2026-08-31 12:21:52 -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
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
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