Commit graph

16753 commits

Author SHA1 Message Date
Dan Stillman
08880f7a0e Update citeproc.js to include Juris-M/citeproc-js#286
Ignore unknown style-level attributes instead of failing to load the
style, so that additive style-level attributes can be adopted in future
CSL versions without breaking deployed processors [1].

[1] https://discourse.citationstyles.org/t/faster-csl-specification-updates/2050
2026-07-30 09:43:01 -04:00
Tom Najdek
c39f16891f Extend the template engine and add template validation (#5989)
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
- Fix multiple potential scenarios causing a template engine crash
 - Add support for specifying string literals in the template engine
 - Validate `if/else/elseif` order and matching clause closures
 - Validate to ensure every `{{` is properly closed with a matching `}}`
 - When a template is invalid, display a warning, do not offer batch-renaming tools, do not update synced setting
 - When a template is invalid, prompt the user to fix or reset the template when

Closes #5965
2026-07-29 13:30:12 -04:00
Tom Najdek
efcfaec5d4 Move templating engine to a standalone, lazy-loaded module (#5989) 2026-07-29 13:30:03 -04:00
Dan Stillman
dc6d55a137 Remove collectionTreeRow from the plugin menu context
Menu plugins reading it acted on one arbitrary row of the selection.
Reading it now throws and names collectionTreeRows, which the context
has already provided since multi-collection selection landed.

The context now copies property descriptors rather than values, since
copying values would evaluate the throwing collectionTreeRow getter for
every menu.
2026-07-29 13:22:58 -04:00
Dan Stillman
783da0e66e Don't offer Remove from Collection if saved search is selected
Collections and saved searches can be selected together, and the
search's items don't need to be in any of the collections, which would
result in search-only items being moved to the trash.
2026-07-29 13:12:47 -04:00
Dan Stillman
1d97f6448e Replace ItemTree#collectionTreeRow with a validated view mode
The view-wide branches (trash, duplicates, feeds, Recently Read) read
the first selected row, which multi-collection selection preserved with
a getter rather than updating. setCollectionTreeRows() now derives the
kind of view the selection adds up to, throwing if the rows disagree,
and those branches test it.
2026-07-29 13:12:47 -04:00
Dan Stillman
5d1a2cba32 Remove the singular collection tree selection getters
Multi-collection selection left .collectionTreeRow and similar in place
to reduce breakage, but that would just leave plugins and other callers
potentially broken when multiple rows were selected. All singular
getters now throw and say what to use instead. getSelectedLibraryIDs()
was added to replace getSelectedLibraryID().
2026-07-29 13:12:47 -04:00
Dan Stillman
e45871fa9d Don't treat a selected saved search as a collection
Collections and saved searches can be selected together, so a
collection-item change called getDescendents() on rows that don't have
it and compared search IDs against collection IDs.
2026-07-29 12:55:41 -04:00
Dan Stillman
c92c75eb36 Mark read attachments in every selected library
Recently Read rows can span libraries, but read attachments were marked
as matches for the first row's library only, leaving the rest as grayed
context rows.
2026-07-29 12:55:40 -04:00
Dan Stillman
3da869b2e6 Keep library headers in place when the items list is re-sorted
Header and spacer rows stayed in the row list during a sort and were
ordered as if they were items, so a column sort could move them out of
position.
2026-07-29 11:33:15 -04:00
Dan Stillman
85a33e1586 Don't treat library header and spacer rows as items
Code walking the items list assumed every row was an object, so the
headers and spacers shown whenever more than one row is selected got
picked up as items: getSortedItems() passed them to export and report
generation, restoring from the trash called item methods on them, and
the item pane counted them in "N items in this view".
2026-07-29 11:33:14 -04:00
Dan Stillman
3bf74a3e46 Local API: Actually exempt file uploads from Zotero-Server-ID
The requireServerIDOnWrite flag was added to the base endpoint class but
never set on the upload receiver, so uploads sent without the header
were still rejected with 428.
2026-07-29 10:18:45 -04:00
Dan Stillman
ae98136ec8 Save uploaded objects to the sync cache on an unchanged response
The cache-existence check didn't await getCacheObject(), so the
promise was always truthy and no cache object was ever saved for
objects the server reported as unchanged. Without a cached version,
later remote changes to those objects produced avoidable conflicts.
2026-07-29 00:06:42 -04:00
Dan Stillman
2fb8d69a93 Auto-resolve lastRead conflicts without a cached version
lastRead conflicts were auto-resolved only when a cached version of
the object was available, so differing read times could still trigger
the conflict resolution window.
2026-07-28 23:56:29 -04:00
Abe Jellinek
681c48f1c2 Local API: Don't validate Zotero-Server-ID for file uploads
Some checks are pending
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
https://github.com/urschrei/pyzotero/issues/344#issuecomment-5108140508
2026-07-28 12:34:40 -06:00
Dan Stillman
caeffa9f41 Citation dialog: Hide the citation preview until the citation has items
Show the preview area and its toggle button only once there is
something to preview, instead of showing a "Preview" placeholder in an
empty dialog.
2026-07-28 12:29:59 -04:00
Dan Stillman
b66519cb4c Citation dialog: Hide the list layout divider when the list is empty
With nothing in the list, the layout's divider sat directly above the
bottom area's divider, showing a doubled line below the input field.
2026-07-28 12:18:21 -04:00
Dan Stillman
db5e7edb49 Citation dialog: Show number of items the + button will add in a tooltip
The citation dialog's + column now has a tooltip that reflects how
many items a click will add based on the current selection, e.g.,
"Add 3 Items to Citation".
2026-07-28 12:18:13 -04:00
Dan Stillman
7925b4e9ac Citation dialog: Add all selected items on + click in Library mode
Clicking the + button on a row that is part of a multi-item selection
now adds all selected items to the citation, matching the existing
list-mode behavior. Clicking + on an unselected row still adds only
that item.
2026-07-28 12:16:41 -04:00
Dan Stillman
93d66e3999 Citation dlg: Don't enter locator-typing mode after adding multiple items
With focus moving back to the input after a multi-item add, a typed
number was applied as a page locator to every just-added item. It now
starts a search instead. Single-item adds still enable the typed-locator
shortcut.
2026-07-28 12:16:11 -04:00
Dan Stillman
938c414e0c Citation dialog: Fix post-add scroll crash that left focus in the items list
_scrollItemTreeToRow() parsed the row index from the wrong rowID
segment, so it threw on every call, and adding items from the items list
never returned focus to the input as intended.
2026-07-28 12:16:01 -04:00
Dan Stillman
8526a022ec Move local API authorization prompt button labels to general strings 2026-07-28 10:04:02 -04:00
Tom Najdek
75b78b410b
Ensure emoji detection works as intended with Unicode 17 implementation (#6004) 2026-07-28 10:02:35 -04:00
Abe Jellinek
a37a9e782a Add Zotero-Server-ID, require for writes, validate when provided
Every local API response includes a stable per-database server ID so
that clients can partition cached data, especially local object
versions, by Zotero instance. Write requests must include the ID, and
when it's provided on any request it must match the current server.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-07-28 09:57:44 -04:00
Abe Jellinek
77f2432103 Local API: Support write requests
Add support for item, collection, and search writes (POST/PUT/PATCH/
DELETE), tag deletion, full-text writes, and the three-phase file
upload flow. Writes require a local API key granted by the user via a
confirmation dialog (POST /api/local/authorize) and follow the web
API's concurrency model, with If-Unmodified-Since-Version and
per-object version preconditions checked against local versions.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-07-28 09:57:44 -04:00
Abe Jellinek
9dd17a212e Track data object versions locally
Add a clientVersion column for items, collections, searches, and
libraries, incremented once per library per transaction on every
object save or deletion. The local API reports these versions instead
of synced versions -- in object JSON, format=versions, since=
filtering, and Last-Modified-Version -- since synced versions don't
reflect local changes and are 0 for unsynced objects. Group metadata
responses keep reporting the synced group version, which has no local
counterpart.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-07-28 09:57:43 -04:00
Dan Stillman
12114f6a86 Fix binary file corruption in multipart/form-data parsing
_decodeMultipartData() trimmed each part body, mangling file content that
begins or ends with a whitespace byte (e.g., a PDF ending in a newline).
2026-07-28 09:57:43 -04:00
Dan Stillman
ce20a5f228 Ship legacy Safari App Extension alongside the web extension on Mac
Developer ID-signed Safari web extensions load only in Safari 18.4 and
later -- older Safari blocks them at the code-signing layer -- so macOS
11 and 12 users, whose Safari versions top out at 16.6 and 17.6, lost
the connector with the web extension conversion.

Embed the prebuilt legacy App Extension ($SAFARI_APP_EXTENSION, set by
the deploy scripts) at Contents/PlugIns/ZoteroSafariExtension.appex with
the historical bundle identifier, and move the web extension to
Contents/PlugIns/ZoteroSafariWebExtension.appex with a new
.SafariWebExtension identifier. The web extension's
SFSafariAppExtensionBundleIdentifiersToReplace key causes Safari
versions that can load it to hide the App Extension and migrate its
enabled state, while older Safari shows only the App Extension.

Also remove the removed-files entries for the App Extension's
resources, which would otherwise delete them on update.

https://forums.zotero.org/discussion/132925/
2026-07-28 07:24:27 -04:00
Dan Stillman
91d7e73d26 Strip com.apple.FinderInfo xattrs from files in Mac disk images
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
hdiutil makehybrid gives every file on the image non-empty Finder info
(it sets an icon location), which appears as a com.apple.FinderInfo
extended attribute. codesign --verify --strict rejects FinderInfo as
detritus, and it can cause Safari to fail to load the web extension.
(Reported by a user with Safari 17.6 on macOS 12. No other reports from
the beta, so maybe not universal.) The Safari App Extension apparently
wasn't affected, since it shipped with the same attributes for years.

Convert the hybrid image to a read-write image, mount it, strip the
attributes from its files, and compress from that. The volume header is
untouched, so the open-folder flag that makes Finder open the volume
window on mount is preserved.

Only fresh installs from the DMG were affected. The updater writes fresh
files without the attributes, so copies updated in place were clean.
(Notably, this is the reverse of the post-update extension breakage for
which the standard advice has always been to delete Zotero.app and
redownload. Following that advice is exactly what resulted in a broken
copy here.)

https://forums.zotero.org/discussion/132925/
2026-07-27 15:31:18 -04:00
Dan Stillman
566115dc7c Use a fixed menu padding on Windows and Linux
Some checks failed
CI / Test (shard 1) (push) Has been cancelled
CI / Test (shard 2) (push) Has been cancelled
CI / Test (shard 3) (push) Has been cancelled
CI / Test (shard 4) (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
The toolkit's default menuitem padding is em-based, so it balloons
with the UI font size in menulist dropdowns (e.g., the item type
menu), which inherit that font, forcing long lists to scroll. macOS
uses a fixed value on all menus and is unaffected. Match that on
Windows and Linux, slightly roomier, so every menu stays compact
regardless of font size.

https://forums.zotero.org/discussion/132797/request-for-compact-view-in-dropdown-menus
2026-07-24 13:30:36 -04:00
Dan Stillman
512a2444d8 Add timeout to HiddenBrowser::getPageData()
The queries wait for the document to be ready, so a page that never
finished loading would hang the caller -- e.g., full-text indexing --
forever.
2026-07-24 13:11:50 -04:00
Dan Stillman
9eea7d9a0c Disable JavaScript when indexing HTML files
Page scripts otherwise run during indexing and can hang it. JavaScript
was disabled here in 2a7f31813e for the same reason, but that was lost
in the fx-compat rewrite (13adfd131c).

https://forums.zotero.org/discussion/132901/
2026-07-24 13:11:50 -04:00
Dan Stillman
3a54a5ecb1 Fix allowJavaScript option in HiddenBrowser
allowJavaScript: false didn't disable scripts for a system-principal
document, such as a blob: URL created from chrome code, which runs
scripts regardless of the allowJavascript flag. Sandbox the document
instead, forcing a null principal and blocking script execution.
2026-07-24 13:11:50 -04:00
Dan Stillman
0b32e93cb1 Batch retraction refresh notifications instead of one per item
The retraction checker fired a separate 'refresh' notification for
every matched item on each check, including already-known retractions
that hadn't changed. At startup this produced a burst of events that
forced observers like the item tree to rebuild repeatedly. Batch each
operation's changes into a single notification, and notify only for
items whose retraction state actually changed.
2026-07-24 10:56:36 -04:00
Dan Stillman
a81af0c268 Don't show Extra migration progress window unnecessarily
The candidate scan counted items whose Extra content parsed as fields
but couldn't actually be migrated (e.g., fields already set on the
item), so the window could appear on every global schema update without
changing anything.

Also use a dedicated headline instead of "Upgrading database…", and show
the window only if the migration is still running after 500 ms.
2026-07-23 23:21:01 -04:00
Martynas Bagdonas
ea4b301f73 Avoid SDT test fixture updates on version bumps
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
2026-07-23 20:24:21 +03:00
Dan Stillman
4f825ad50c Don't throw getting the filename of a stored file with a corrupt path
A stored file's path is normally 'storage:<filename>', which is parsed
as a string, but some ancient libraries apparently have relative paths
('../.../foo.pdf') that fell through to PathUtils.filename() and threw
NS_ERROR_FILE_UNRECOGNIZED_PATH, breaking syncing.

Resolve stored-file leaves as strings regardless of prefix and reserve
PathUtils.filename() for linked files, whose paths are genuine absolute
paths.

https://forums.zotero.org/discussion/132861/zotero-sync-issue-id-1987012678
2026-07-23 11:12:12 -04:00
Dan Stillman
6fb30346e6 Bundle Safari web extension instead of Safari App Extension
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
The connector is now a Safari web extension, so the bundled appex is a
static wrapper stub whose placeholder resources are replaced at build
time with a connector build via $SAFARI_EXT_RESOURCES. The stub only
needs to be rebuilt when the native wrapper project changes, not for
connector releases.
2026-07-22 15:11:23 -04:00
Dan Stillman
a4b39b91f3 Exclude trashed items from tag autocomplete suggestions
https://forums.zotero.org/discussion/132875/some-tag-suggestions-appearing-capitalised
2026-07-22 11:46:52 -04:00
Dan Stillman
146520aed5 Reload quick search when changing the search mode
Changing the quick-search mode dropdown didn't re-run the current
search, since the filter was applied without a mode and the unchanged
search text made setSearch() report no change.

Regression from 5ca1fbb167

https://forums.zotero.org/discussion/132873/beta-quick-search-isnt-reloaded-when-changing-search-mode
2026-07-22 11:14:23 -04:00
Dan Stillman
cddb19bf36 Only reject and strip actual directory paths in stored-file filenames
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
ee68452b treated any backslash as a directory separator, in both the
attachmentPath setter and the userdata 128 migration. But backslashes
are technically valid on Linux/macOS and appear in real filenames (due
to LaTeX in titles, etc.), so that would wrongly throw on or truncate
them. Only a forward slash (never valid in a filename) or a Windows
absolute path (drive-letter or UNC prefix) reliably indicates a leaked
directory path, so treat just those as paths and leave bare backslashes
alone. We should make sure we're running getValidFileName() everywhere
to avoid saving those.
2026-07-21 16:02:40 -04:00
Dan Stillman
e81e72dccf Fix negated Title/Creator/Year and Any Field advanced searches
These conditions expanded into an OR-group across their underlying
fields, so a "does not contain"/"is not" operator matched almost every
item: any item missing one of the fields satisfied that field's negated
condition. Use an AND-group for negative operators, so the value must be
absent from every field.

https://forums.zotero.org/discussion/132835/
2026-07-21 14:15:45 -04:00
Dan Stillman
40974e493d Fix Cmd/Ctrl-A not selecting all libraries when feeds are visible
Selecting all libraries pulled in feed rows, which belong to a different
visibility group and so can't be shown together, collapsing the
selection back to a single row. Constrain the all-libraries scope to the
current selection's visibility group. Also preventDefault() the shortcut
so it no longer falls through to the cmd_selectAll key command, which
threw NS_ERROR_FAILURE.

https://forums.zotero.org/discussion/132843/beta-cannot-select-all-libraries
2026-07-21 14:04:00 -04:00
Dan Stillman
621c0fbb91 Fix storageLocal test broken by ee68452b0
The download-rename test used a backslash as its sample invalid
character, which now throws in the attachmentFilename setter. Use a
colon instead, which still tests download-time filtering.
2026-07-21 11:08:42 -04:00
Dan Stillman
ee68452b05 Disallow slashes in stored-file attachment paths
Someone ended up (via a plugin, presumably) with stored-file attachments
with a full path after 'storage:', which broke file syncing. Throw when
setting a stored-file path containing a slash, and strip paths from
existing filenames in a schema update step. No particular reason to
think that the file with that basename will exist in the storage dir,
but at least it will be looking for the right file and not be totally
broken.

Separately, the dataserver will clean up filenames with full paths and
block going forward.

https://forums.zotero.org/discussion/132822/reference-sychronization-error
2026-07-21 10:40:59 -04:00
Dan Stillman
5877952954 Add accented stop-words to abbreviations word list
Accented stop-words weren't matched by the existing unaccented
entries (e.g., "fur"), so automatic journal abbreviation kept and
capitalized them: "Jahrbuch für Heimatkunde" became "Jahrb. Für
Heimatkunde" instead of "Jahrb. Heimatkunde".

https://groups.google.com/g/zotero-dev/c/uP18QEKe2JU/m/AsoGWxd9AwAJ
2026-07-20 22:52:51 -04:00
Tom Najdek
ccb592ba68
Advanced Search: Use dedicated WCAG-compliant colors for a warning (#5990)
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
2026-07-20 23:37:23 +02:00
Abe Jellinek
ddcdea3795 Read Aloud: Reword highlight granularity to "Highlight current" 2026-07-20 13:43:39 -04:00
Abe Jellinek
14c6167a4c Update reader submodule 2026-07-20 10:50:56 -04:00
Abe Jellinek
5a785c4e0f Read Aloud: Support highlight granularity customization & timestamps 2026-07-20 10:50:56 -04:00