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
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/
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.
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/
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.
This runs during Find Full Text and connector saves, where the default
30-second timeout and hour of 5xx retries are far longer than a user
wants to wait.
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/
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/
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>
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.
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/
As of Firefox 140.17/153.4 (Bug 2068406), setting nsIFilePicker's
displayDirectory to a directory that doesn't exist or isn't readable
throws instead of being ignored. This broke callers that pass a saved
path that may be stale (e.g., choosing a PDF/EPUB handler after the old
app's folder was removed, or a missing Scaffold translators directory).
The action column's width was given as "32px", which VirtualizedTable
now turns into an invalid CSS width, collapsing the column and hiding
the buttons needed to resolve unmapped and ambiguous citations. Also
restore the accept-match icon on ambiguous-citation candidates.
https://forums.zotero.org/discussion/133740/
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/
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/
A userdata upgrade that took more than 5 minutes (e.g., dropping a large
legacy word index) was rolled back by Sqlite.sys.mjs, while its remaining
statements autocommitted and marked the database as upgraded, leaving
steps 126 and 127 missing.
Disable the transaction timeout and replace steps 122-129 (added in
Zotero 7, 9, and 10) with step 130, which checks for each change before
making it. To speed up the upgrade, drop the legacy word index without
overwriting the freed pages, since the full text already exists on disk
and we're just rewriting it in fulltext.sqlite.
https://forums.zotero.org/discussion/133859/zotero-connector-in-chrome-not-workinghttps://forums.zotero.org/discussion/133965/citation-saving-does-not-work-with-zotero-connector-firefox
Firefox has AppKit draw the menulist's capsule and chevron, but it sizes
the box and positions the label itself, using a fixed dropdown border
and the label's CSS margins. On macOS 27 (and probably 26), that left the
label closer to the edges and the chevron than in a native NSPopUpButton.
It also made the box 26.5px tall. Firefox assumes regular pop-up buttons
are 22px tall and only draws them unscaled in boxes up to 2px taller, so
it drew the control at 22px and scaled the image up, enlarging the
capsule and chevron.
Add padding to match native label insets, and trim the label's vertical
margins to keep the box at 24px, so Firefox draws the control unscaled.
The result measures the same as a native NSPopUpButton.
Build the launcher with the macOS 26.5 SDK. AppKit chooses control metrics
based on the main executable's SDK, so with the old 15.5 launcher, native
buttons and menulists used pre-Tahoe metrics with built-in margins while
Mozilla's XUL (built with 26.5) stopped compensating for them on macOS 26+
(bug 1992898), leaving labels cramped and controls indented.
Also add custom libmozglue.dylib (#6056) and set source info in mozconfig,
which official builds require and which Mozilla only detects automatically
from Mercurial checkouts.
Mozilla's helper apps (GPU, content, etc.) use the hardened runtime and
Mozilla's Team ID, so in unsigned builds they couldn't load our custom
libmozglue.dylib and failed to launch. Re-sign them ad hoc.
Firefox stopped inflating native controls on Tahoe (bug 1992898), but
still uses the old widget border sizes, so labels nearly touch the edges
of the new capsule-shaped buttons and run into menulist arrows.
curl is already required, so drop wget as a build requirement.
build_autoupdate.sh now checks the HTTP status code for its ETag cache
instead of relying on wget not creating the file on a 304.
If NOTARIZATION_PROFILE is set, notarize_mac_app and notarization_info
authenticate with that stored notarytool profile (e.g., an App Store
Connect API key) instead of an Apple ID and app-specific password,
unlocking the keychain first if needed.
153.3.0esr updated the Chromium sandbox, changing the TargetConfig
interface that xul.dll calls into the launcher's sandbox broker
through. The stubs were still built from 153.0esr, so the 153.3.0esr
xul.dll called the wrong methods and crashed on startup with
"config->SetProcessMitigations(initialMitigations) failed".
The release script never set SAFARI_APP_EXTENSION, so 10.0 through
10.0.3 shipped with only the web extension, which doesn't run on Big Sur
or Monterey. Only beta builds have been including both.
https://forums.zotero.org/discussion/133871/
This showed up as devtools failing to start in a -d build on some machines, but any Subprocess.call() could fail because of it.
---------
Co-authored-by: Dan Stillman <dstillman@zotero.org>
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
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.
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/
The drop indicator span was inserted before the cells, so the first
cell stopped matching :first-child and picked up inline-start padding.
Insert it after the cells instead.
Regression from 5ca1fbb167, which replaced the .first-column class with
:first-child.
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
Gecko 140.15 made nsIExternalProtocolService.loadURI()'s triggering
principal mandatory, so Zotero.launchURL() threw NS_ERROR_ILLEGAL_VALUE
for any scheme handled by an external app.
https://forums.zotero.org/discussion/133709/
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/
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/
SpiderMonkey stacks contain only frames, so the startup error messages
that showed just the stack didn't say what had failed. 9b3d7a32e3 added
the message to one of the three, where a ternary-precedence bug then
dropped the surrounding text instead. Format all three the same way.
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/
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/
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.
_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.
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.
Listeners added to the item and collection trees' event bindings
survived unregister() and held ZoteroPane and the views for as long
as the tree objects did. makeClassEventDispatcher() now provides
clearEventListeners(), which the trees call on unregister, so callers
don't need to remove their own listeners. The collection tree focus
listener, a plain DOM listener, is removed in destroy().
Hiding the tag selector destroyed the TagSelectorContainer but left
its media-query listener registered, keeping the destroyed instance
alive for the lifetime of the window and firing setState() on the
unmounted component when the display density changed. The fontSize
pref observer id was also overwritten by the uiDensity registration,
so it was never unregistered.
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.
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.
Retraction Watch has renamed, merged, and added reason terms since these
were added, leaving only 65 of the 102 entries matching a term still in
use. 90% of retracted items had at least one reason that didn't show a
description, and 21% showed none at all. Rebuilt from the current
Appendix B list, which covers every reason in the data.
Windows returns this for ERROR_FILE_CORRUPT/ERROR_DISK_CORRUPT. It's not
in xpc.msg, so the exception has an empty name and isn't in
Components.results, and it was falling through to the generic file sync
error instead of the file access error with the path. Show a message
about the reported corruption in place of the usual permissions advice.
https://forums.zotero.org/discussion/133592/
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.
Status messages were left aligned for the multi-paragraph intro text,
but in a narrow window, that made single lines that wrapped sit
off-center, so center those instead.
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/
A plugin that monkey-patches a method in the item list's load path --
say, Zotero.CollectionTreeRow.prototype.getItems() -- can throw and
leave the pane showing only "Error loading items list", with nothing in
the error report to identify it. Update the message to name the culprit.
https://forums.zotero.org/discussion/133565/
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/
Zotero.Session.save() runs from a quit-application-granted observer
registered before the database checks, but session.json is only read
after them, so a startup error -- an incompatible database from a newer
version, say -- meant that quitting overwrote the file with an empty
state and all open tabs were lost.
https://forums.zotero.org/discussion/133542/
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
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.
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/
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/
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/
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.
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.
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/
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.
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.
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.
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.
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.
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.
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/
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.
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.
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
addItems() passes its options to Zotero.Item::save(), but removeItems()
dropped everything but skipEditCheck, so callers couldn't batch the
resulting notifications.
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").
"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").
* 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>
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.
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/
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.
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
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
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.
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/
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/
_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.
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.
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
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
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.
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.
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.
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'.
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.
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.
The GitHub runner image installs the x64 build of Git for Windows, so
its bash and everything run from it execute under x64 emulation on
Windows on ARM, where uname -m reports x86_64 and the staged build got
the wrong architecture. uname -s still reports the machine, as
MINGW64_NT-10.0-26200-ARM64, so use that instead. If the image ever
installs a native arm64 Git for Windows, uname -m will be correct and
this can go.
The ZIP format requires forward slashes, and nsIZipWriter stores
whatever it's given, so entries for files in subdirectories were
written with backslashes.
zotero.exe is the Mozilla launcher process, which spawns the browser
process and exits, so the test runner checked for the success file and
cleaned up the profile while the app was still starting, and passing
runs were reported as failures that ended at arbitrary points.
Neither is reliably available on Windows, so copy the source tree with
Python when rsync is missing and delete the CSL locale support files
with a directory walk.
Boots the Windows x64 and arm64 builds, which use custom-built Firefox
components rather than stock Mozilla binaries, and runs the DB and file
tests. A Gecko bump commit updates the custom-component hashes in
config.sh along with the version, so this tests each new set of custom
components, which previously required manual Windows testing.
Run the smb/nfs test shards and a new macOS NFS job only when the
changes touch the Gecko version or the network-filesystem database
handling, plus on manual and monthly scheduled runs, via a 'changes'
job feeding the test matrix. The macOS job uses the native NFS server
with a loopback mount and runs the real code path that crashed on
network data directories on macOS.
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/
CIFS mounts can't create real symlinks without special mount options,
so skip the symlinked-database and broken-symlink tests when a created
symlink doesn't exist or isn't visible as one.
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.
Adds 'smb' and 'nfs' entries to the test matrix that run the DB and
file test suites with the test data directory on a loopback CIFS or
NFS mount, covering the network-filesystem database handling from
bba85a3939. The test harness places the data directory under TMPDIR,
so connection initialization itself runs against the share, which is
where network-filesystem failures (startup crashes and hangs) occur.
The step timeout turns a startup hang into a failure.
On macOS, SQLite chooses locking methods based on the filesystem
containing the database, and network filesystems (e.g., SMB, NFS),
read-only volumes, and filesystems without byte-range locking get
methods without shared-memory support, which WAL requires. Opening a
database with an adjacent WAL file on those crashes -- Mozilla's VFS
wrapper hides the missing shared-memory methods from SQLite's WAL
support check -- so the first Zotero 10 run converted the database to
WAL and every launch after that segfaulted during connection
initialization.
On macOS, mirror SQLite's method selection and use a rollback journal
when shared memory isn't available, converting an existing WAL database
before opening it: an empty WAL by reverting the header format versions
in place, and a non-empty WAL by replaying it into a temporary copy on
local disk that replaces the database file only after passing an
integrity check. A WAL file next to an already-converted database
(e.g., from an interrupted conversion) goes through the same
conversion. Also use openNotExclusive during integrity checks and
corruption recovery, which otherwise fail on SMB shares with an I/O
error from the exclusive open lock.
This corrects 22055d92b7, which passed openNotExclusive on all
platforms for an open failure described as affecting macOS and Linux,
and expected locking_mode=EXCLUSIVE to keep the WAL index in heap
memory with no -shm file. Neither claim held up: mozStorage opens the
WAL while initializing the connection, before any pragma can run, so
the index uses shared memory unless the exclusive VFS is in use, and
the exclusive open works on Linux CIFS mounts -- where non-exclusive
access instead made SQLite's lock-upgrade sequence trip over the SMB
byte-range lock mapping, leaving startup hung and the database never
created. So pass openNotExclusive only on macOS. On Linux this restores
unix-excl, which performs all locking under a single held lock and
keeps the WAL index in heap memory; Windows has no distinct exclusive
VFS and is unaffected.
https://forums.zotero.org/discussion/133258/
rpl 2.1.0 guesses each file's character encoding instead of assuming
UTF-8, and aborts on some locale files, leaving the script's later
steps unrun. Pin the encoding, and use -exec + so a failure in one of
these passes actually stops the script.
Also drop the ⏎ replacement, which hasn't matched anything since 2013
and would now insert real newlines rather than literal \n, truncating
.properties entries.
The traversal waits on focus/blur events, which Gecko fires only while
the window is active, so a window that can't be activated -- another
app focused during a local run, or intermittent deactivation in CI --
timed out after 10 seconds instead of reporting that it couldn't run.
Firefox 153 removed the wizard's extra1 and extra2 buttons, so onLoad()
threw while caching their labels and never got to starting the update
check or adding the page handlers. Create the buttons ourselves instead.
i686 builds are gone as of Zotero 11, so a full build failed trying to
package a linux-i686 MAR from a staging directory that no longer exists.
Skip the architecture entirely, and don't download it for older FROM
versions when building incrementals.
Tests asserted exact search results for words like "one" and "two", so
a leftover item from an earlier test whose random title contained one
of the words as a substring could match and fail the assertion. Use
distinctive random strings instead.
EDTF dates -- ranges ("2021/2026"), uncertain/approximate dates
("2004-06~"), and BCE dates ("-0429") -- and common equivalent notations
("1995-1996", "2021-22", "~1995", "ca. 1995", "429 BCE") are now passed
to citeproc-js as CSL date ranges, circa flags, and negative years.
Previously, such dates were mangled or dropped entirely unless entered as
CSL variables in Extra.
CSL date variables in Extra get the same parsing.
Other date handling in the client doesn't understand EDTF yet: the y/m/d
indicator in the date field doesn't reflect EDTF parsing, date searches
only match a range by its start date, and BCE dates still can't be
sorted.
Addresses #637
The debounced count update from fe35127f50 went through itemSelected()
and fired on any notifier change, so a pending timer could bump the
itemSelected spy after a test reset it, failing "shouldn't select a
modified item" in CI. Emit rowCountChange only when the row count
actually changed, and re-render the item pane message directly.
The build-and-deploy scripts run scripts and config from the local
checkout but build source files from the tip of the remote branch, so
a stale or wrong-branch checkout could silently build with the wrong
Gecko version or omni patches.
md5Async() read and hashed the entire file synchronously on the main
thread. Read the file via an input stream pump instead, with file I/O
off the main thread and hashing done a segment at a time.
The test relied on the automatic selection of a newly created item,
which can lose the race with a view switch (e.g., from another library
selected by a previous test), leaving the info pane empty.
Notifier events that added or removed rows (e.g., items downloaded
during a sync) didn't update the no-selection message in the item pane,
so the count went stale until the selection changed. The row provider
now emits a rowCountChange event, and the pane re-renders the count,
debounced, when nothing is selected.
Zotero.Users.setName() updated the in-memory cache even if the
transaction was later rolled back, so a retry (e.g., of a failed sync
download batch) would skip rewriting the users row.
Saving each downloaded object in its own transaction made large
download syncs extremely slow, since every object required a separate
disk commit. Objects are now saved in batches sized by the existing
notifier batch ramp, and a failed batch is reprocessed individually so
that an error still rolls back only that object's save. The cached
parent item is reloaded on rollback, since it would otherwise keep
collections added via newParentItemCollections in memory and the retry
would skip writing them.
An error thrown from a commit callback rejected executeTransaction()
even though the transaction had been committed, so callers would treat
saved data as rolled back, and rollback callbacks (e.g., the notifier
reset) ran against committed data. Commit-callback errors are now
logged instead, and any error after a successful commit skips rollback
callbacks and is marked with 'committed' on the error object.
If a transaction was rolled back, callbacks queued via
addCurrentCallback('commit') stayed queued and ran after the next
successful commit, potentially applying in-memory cache updates for
rolled-back data.
Runs a function within a transaction that's rolled back at the end,
with rollback callbacks guaranteed to have run by the time it returns.
Replaces try/catch blocks in existing tests that threw and caught a
marker string from executeTransaction() to force a rollback.
the all-creators mode (fieldmode 2) concatenated
firstname || ' ' || lastname, so a two-field creator without a first
name produced ' last', which advanced search fields inserted with the
space.
purgeOrphanedContent() ran on every startup, unlike the other
data-object purges, which are gated on the purge.* prefs.
startQueueDrain() also ran an FTS5 'optimize' even when the queues were
empty.
Gate the former on purge.items and the latter on having indexed
something.
The menu labeled Annotation Type listed "Highlight annotation" and "Image
Annotation", from the strings the reader announces annotations with. Use
the short names, which existed for two of the six.
mozinference (which is currently CPU-only) is better for many tasks, but
plugins may want WebGPU for some features (e.g., chatbots), so just
follow Firefox, which currently enables it by default for Windows and
Apple Silicon macOS. Adds 5.8 MB compressed to the Windows installer.
Binding is meaningful for a condition that matches at every level -- a
tag bound to an attachment means the tag is on the attachment -- but a
group carrying one lost the binding as soon as the search was
serialized, so "items with an attachment tagged foo" couldn't be built.
The row label builder skipped values that weren't strings, so a numeric
cell -- a note count, or a pre-1000 year (which is parsed with
parseInt()) -- announced only its column
name.
Also switch to formatColumnName() while we're here, so row labels
resolve the column name the same way the header does.
The guard tested the newly clicked column's settings entry while the
delete targeted the previously sorted column's, so the old direction
survived when the new column had no entry, and clicking threw when the
old column had none.
Skip crashhelper (all platforms -- the crash reporter is disabled) and
desktop-launcher and nmhproxy.exe (Windows).
Also skip dxcompiler.dll (Windows, 22 MB), which is loaded only by
WebGPU, now also disabled via pref. Nothing renders with WebGPU, and
local ML -- including any future plugin API -- should go through
Firefox's inference libraries (mozinference/onnxruntime), which are
compiled CPU-only.
XULButtonElement only opens a type=menu button's menu when the button
is the event's original target, which worked before because xul.css
gave .toolbarbutton-menu-dropmarker 'pointer-events: none'. That rule
was removed along with the dropmarker element, so clicks on our
recreated dropmarker did nothing.
The menu icon element is now an <html:img> that renders the 'image'
attribute or 'content: var(--menuitem-icon)', and 'list-style-image' is
ignored, so iconic menuitems showed broken-image placeholders on
Windows and Linux. list-style-image is kept for native macOS menus,
which still read it.
Firefox 153 removed the chrome://browser/skin/window-controls/ SVGs and
now draws the caption buttons with Segoe Fluent Icons glyphs (falling
back to Segoe MDL2 Assets on Windows 10), which broke the Windows build.
Port the new browser.css button styling and stop keeping the removed
directory in build.sh.
An exact-match condition compared with SQLite's case-sensitive '=' and
skipped the normalized shadow columns, so 'publication is "review of
finance"' missed "Review of Finance" while every other kind of search
matched it.
Better fix than d5aacafe3f -- we don't need source-repo info in our
builds at all, so just disable MOZ_INCLUDE_SOURCE_INFO rather than
synthesizing it.
Firefox's official-build source-repo.h generator only auto-detects
Mercurial, so building from our git checkout left the file empty and
packaging failed with "no preprocessor directives found". Derive
MOZ_SOURCE_REPO and MOZ_SOURCE_CHANGESET from git in the mozconfig so
configure records them for any invocation, including a manual
./mach package.
app/win/build added the cross-compilation target to whatever toolchain
was default before switching to RUST_VERSION, so after a Rust version
bump the new toolchain lacked the aarch64/i686 target and the arm and
win32 builds failed at configure. Set the default first.
Bug 1974578 added oncommand to GlobalEventHandlers for the Invoker
Commands API, so the assignment in _initImportedNodesPostInsert() -- an
inert expando write until now -- started setting the WebIDL event
handler, and [LegacyTreatNonObjectAsNull] turns a string into null. That
wiped the handlers compiled when the pane fragment was imported, leaving
every inline oncommand handler in the preferences panes dead.
importNode() compiles the attribute handlers itself, so the loop can
just be removed.
Bug 1980856 removed both wantdropmarker support and the rule that gave
.toolbarbutton-menu-dropmarker its image, once Firefox's last consumer
stopped using it, so the New Item, Add Attachment, and New Note buttons
lost their chevrons, along with the ones in advanced search, the feed
settings dialog, and Scaffold.
Draw it with a background, since a <dropmarker> doesn't render
list-style-image, and use chevron-6 to match the quick search dropmarker
and the menu marker on our own buttons. The rule also moves out of the
toolbarbutton block, since a plain <button> can host one too.
Firefox now creates a clear button for <input type="search"> in chrome
documents regardless of layout.forms.input-type-search.enabled (bug
1655503), which doubled up with the one we draw ourselves.
There's no mac wizard.css -- the shared one from the global list is all
macOS needs, as on Linux -- so this only produced a missing-file warning
whenever a <wizard> was created.
Without -f, curl saves the error page and the download appears to succeed,
so a missing build fails later with a confusing "Unrecognized archive
format" from tar.
The sidebar customizations all broke: the category buttons are
moz-page-nav-buttons now rather than button[name="..."], and the Settings
and Support links moved out of <sidebar-footer> into a secondary nav. The
sidebar also has a heading now that duplicates the main pane's "Manage
Your Plugins", so hide the whole column instead of the pieces.
-moz-mac-help-button draws the question mark itself, and the Help label is
now painted over it at the bottom of the settings panes. Firefox hides the
label the same way for the Page Info help button (bug 2009625).
Babel strips the trailing newline from defaults/preferences/zotero.js, so
the platform-specific prefs appended after it shared a line with the last
Zotero pref.
SelectableProfile.sys.mjs now statically imports BackupService, which
statically imports chrome://browser/content/backup/backup-constants.mjs,
so startup failed once SelectableProfileService was instantiated as a
command-line handler.
nsMenuBarX now looks for a menuitem with the id menu_settings on Ventura
and later, falling back to menu_preferences only on older versions, so
declare both the way Firefox does.
Bug 2037682 removed nsIPK11TokenDB in favor of getting the internal key
token directly from @mozilla.org/security/internalkeytoken;1, and bug
2043434 replaced initPassword()/needsUserInit with changePassword().
Bug 2017957 made Cu.Sandbox freeze built-ins by default for
system-principal sandboxes, so the translation framework's assignments to
Promise silently failed and loading the framework threw on
Zotero.Promise.method.
Bug 2008041 made disabled, checked, hidden, collapsed, and selected
boolean attributes, so their value is empty and [disabled="true"] no
longer matches. Nothing sets any of them to "false", so matching on
presence alone is equivalent.
Bug 2008041's change to boolean attributes also covers hidden and
collapsed, whose UA selectors became [hidden] and [collapsed], so
setAttribute('hidden', false) now hides the element. Switch the setters
that can be passed a falsy value to toggleAttribute(), read them with
hasAttribute(), and match the [collapsed=true] selectors in our own
stylesheets to the new presence-only form.
Bug 2008041 made them html-style boolean attributes, so presence alone
means true and setAttribute('disabled', false) now disables the element.
Switch the sites that can be passed a falsy value to toggleAttribute(),
read them with hasAttribute(), and drop a disabled="false" from markup.
Sites that only ever pass true are left alone.
Bug 2009806 moved XUL checkbox click handling into C++ and dropped the
CheckboxStateChange event, so listen for command instead. The event now
only fires on user interaction, so the tests have to click the checkboxes
rather than assign to .checked.
Bug 2011326 moved remote type prediction into C++ and removed
E10SUtils.predictOriginAttributes() and getRemoteTypeForURI(). The
resulting TypeError meant nothing ever loaded in the hidden browser.
Translators are evaluated with the system principal, which worked until
now only because nsContentSecurityUtils::IsEvalAllowed() exempted any
profile with "JS hacks" present -- which for us meant
xpinstall.signatures.required being false. Bug 2038660 dropped that
exemption, so ask for eval explicitly.
findLogins() now throws, and removeLogin()/modifyLogin()/removeAllLogins()
are Async-suffixed and return promises. Without this, Zotero couldn't read
the stored API key and showed the corrupted-logins-database error on
startup.
There's no synchronous read left, so Zotero.Sync.Runner.enabled -- which is
consulted from places that can't await -- now reads a value that
hasCredentials() caches.
Bug 2043845 put the FORCE_ALLOWED_DTD content policy type behind a pref
that's off for everything but Thunderbird, so NS_NewChannel() refused the
load with NS_ERROR_CONTENT_BLOCKED and the XML parser silently skipped the
entity. Custom elements whose markup uses DTD entities then failed to
parse with "not well-formed XML".
Bug 2038660 applies "script-src chrome: resource: moz-src:" to every
chrome: document, which blocks inline <script>s and inline event
handlers, so the main window loaded but ran none of its scripts.
We should move our inline scripts and event handlers into separate files
so that we can drop the pref.
Mozilla removed the widget and its stylesheet in bug 1968916 without a
drop-in replacement, so bring both into Zotero. The two rules that lived
in Firefox's UA sheets (text-shadow and -moz-window-dragging) move to
_search.scss, and the aria-autocomplete removal that fetch_xulrunner
used to patch in is now part of the widget itself.
aboutaddons.js and aboutaddonsCommon.js were split into
aboutaddons-utils.mjs plus per-component modules under components/, and
the addon details template moved out of aboutaddons.html into
addon-details.mjs.
The unsigned-addon warning is now suppressed through Firefox's own
isUnsignedWarningMessageDisabled(), which is otherwise gated on
MOZILLA_OFFICIAL.
AboutTranslations and the PictureInPicture actors moved out of the
JSWINDOWACTORS literal into conditional assignments after it.
Anchor every pattern to the start of the line while we're here, since
'PictureInPicture: {' matched the MozTogglePictureInPicture event name in
the new file and silently deleted everything through the next ' },'.
The gap after the icon came from whitespace baked into the legacy
cross.png, so it disappeared when the icon switched to the
edge-to-edge cross.svg and the title abutted the icon.
While trying to [fix](https://github.com/NixOS/nixpkgs/pull/519431) another issue in the Zotero package for nixpkgs, I discovered a new bug. When setting a non-`source` update channel, the build fails on MacOS with x86 architectures.
The build fails with:
```
source not found twice in ChannelPrefs
source
stringWithCString:encoding:
```
Which definitely means it is caused by the `strings` command [here](https://github.com/zotero/zotero/blob/main/app/mac/set-channel-prefs-channel#L29).
When manually executing the strings command on the file, I can repeat getting only one `source` result on x86 mac, while getting two on arm64 or linux. After digging around, I tried with the flag `-arch all`, and both `source` values are found on x86 mac. Full command: `strings -n 3 -arch all $binary`.
Closes#5974.
Additional fixes for broken item tree behaviour when multiple items are
selected, and changing focus with ctrl/cmd-arrow keys.
Aligned Collection Tree/Virtualized Tree collapse/expand behaviour when
multiple containers are selected, one of them is focused, and arrow key
left-right is pressed, to the behaviour in Item Tree - now all of them
are collapsed/expanded.
---------
Co-authored-by: Dan Stillman <dstillman@zotero.org>
The PMC lookup in #5906 was added before the OA lookup, but the OA
lookup often returns a PMC URL of its own (including, at least now, for
the DOI given in the PR), with a direct file URL rather than a page to
scrape, so try it first and fall back to the PMCID-derived URL only when
it turns up nothing.
Since #5906, the 'oa' resolver list can start with a plain URL object
for items with a PMCID, but _getOpenAccessPDFURLs() and
saveOpenAccessAttachment() called resolvers[0](), which threw for those
items -- skipping the lookup silently in the first case and failing the
save in the second.
readAsText() already reads the charset from the blob's type when no
encoding is passed, so pulling it out first had no effect. The regex
was also missing a quantifier and captured a single character, which
readAsText() discarded as an unrecognized label.
Show File triggered a file 'open' notification, so revealing a file
moved its item to the top of Recently Read. Use a separate 'reveal'
event, which file syncing still observes to queue a modification check.
https://forums.zotero.org/discussion/133006/
- 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
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.
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.
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.
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().
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.
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.
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".
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.
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.
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.
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".
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.
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.
_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.
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>
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>
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>
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/
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/
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
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.
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.
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.
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
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.
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.
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/
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
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.
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
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
If queued items can't be indexed and removed for any reason, the
startup and background drains would otherwise run forever. Stop after
three passes without the queues shrinking; the items are picked up
again on the next trigger.
If a queued attachment was missing its cache file and re-extraction
produced no text, nothing was recorded in the index state, so the
content-index queue selected the same items forever and the startup
drain never finished.
https://forums.zotero.org/discussion/132808/
Say "Attachments indexed"/"Partially indexed" instead of
"Indexed"/"Partial", reverse "File or full-text content not
available", and list notes last so the attachment rows are contiguous.
The progress popup was opened with alwaysontop, which is system-wide,
so a long-running popup (e.g., full-text indexing) stayed on top of
other apps' windows after switching away from Zotero. Open it as a
dependent window instead, which keeps it above only the Zotero window.
A term mixing CJK with non-ASCII words (e.g., Cyrillic plus Japanese)
was routed to the CJK index with only its CJK characters, matching
every document that contained those. Treat any non-CJK letter or digit
as making a term mixed-script, which falls back to a literal scan of
the cached text.
Replace the trigram FTS5 index for attachment content with a unicode61
word index, so terms match whole words with the final token as a prefix
("archive" matches "archives", but "ion" doesn't match "condition"), as
in the pre-FTS5 word index. A multi-word phrase gets adjacent-token
candidates from the index and is then verified against the cached text
of just those items, since FTS5 ignores what separates adjacent tokens;
the verification treats whitespace and hyphen runs as equivalent
(they're frequently extraction layout or styling) but requires other
punctuation to match literally. Notes keep the trigram index and CJK
matching is unchanged; the index database version is bumped so the
index is rebuilt.
Follow-up to #5979
JNA 5.x ships per-architecture macOS native libraries instead of a
single com/sun/jna/darwin/ directory, so the notarization re-signing
step failed with "filename not matched".
This step can be removed entirely once the plugin ships a jna.jar
without the macOS natives, which are never loaded.
SQLite replays a leftover -wal file (e.g., from a force-quit) into
whatever file next occupies the database path, so copying a backup
over zotero.sqlite produced a corrupted-database error, and the
automatic restore recreated the same mismatch and failed every time.
When corruption is detected, check if the database file is valid without
its journal files, and if so, save a verified copy and restart, swapping
the copy in at the next startup before the database is reopened. (The
copy is made before shutdown because SQLite automatically checkpoints
the WAL into the database file when the last connection closes, which
would write the stale WAL data into the file.) Otherwise, move journal
files along with the .damaged file to clear them from the main path
before restoring from the automatic backup or creating a new database.
Since a mismatched WAL can cause subtle data damage without errors, also
run a full integrity check at startup after an unclean shutdown. Skip
the explicit close-time WAL checkpoint once corruption has been flagged,
and truncate the WAL during idle maintenance to limit stale WAL data.
showZoteroPaneProgressMeter() now returns a token capturing the
previous message, meter mode and position, and owner, and
restoreZoteroPaneProgressMeter() restores that state if no other
operation has changed the display since, with nested tokens restoring
in reverse order.
The CSL locales repository replaced locales-sr-RS.xml with script
variants, so citing with a stored or default 'sr-RS' locale loaded no
terms and failed with an et-al error. Resolve unavailable locales to
the closest available CSL locale (sr-Cyrl-RS for sr-RS) when rendering
and in locale selectors.
https://forums.zotero.org/discussion/comment/515598/#Comment_515598
Backup copies preserve the database file's mtime, and the backup
interval is measured from the backup file's mtime, so a forced
pre-update backup of a long-idle database could be rotated out a day
early. Regular backups still keep the database mtime, which the
unchanged-database check relies on.
The backup before repairing integrity-check errors from the Advanced
pane has thrown a TypeError since backUpDatabase() started requiring an
options object in 8.0, aborting the repair. Also force the backup so a
recent automatic backup can't cause it to be skipped.
If a schema update committed the userdata upgrade but kept failing in a
later step, each restart forced another rotation backup of the
now-upgraded database, deleting the pre-upgrade backup after numBackups
restarts. Record the pending target versions in the database when
making a forced backup and skip the backup while they're unchanged, so
the pre-update backup survives the normal rotation period.
Replace the manually maintained minorUpdateFrom flag, which was easy to
forget to update when adding a slow upgrade step and couldn't account
for database size or hardware. The message is now shown for any upgrade
still running after 500 ms.
Versioned backups (zotero.sqlite.<version>.bak) were kept until the
next userdata upgrade, potentially bloating the data directory by
gigabytes, while minor upgrades made no backup at all. Now any userdata
upgrade, integrity check, or global schema update forces a rotation
backup. Since versioned backups ignored backup.numBackups, setting that
to 0 now fully disables backups, including before upgrades.
Show indexing progress as a bar (items in the search index out of those
that will be) that resolves to "up to date", alongside
Indexed/Partial/Notes counts and a "File or full-text content not
available" count for attachments with no local file or full-text
content. While the pane is open, trigger indexing directly so it
advances as the user watches.
Remove the Rebuild Index and Clear Index buttons. Rebuild Index marked
all content unsynced and re-uploaded it, triggering a server reindex and
re-download on other devices, and the per-item reindex paths now cover
what needed it -- raising a length limit re-extracts only the affected
items, and unindexed items are indexed automatically. Clear Index wiped
the local index and extracted-text cache files, a destructive action
that shouldn't be exposed.
Note content is indexed into fulltext.sqlite, making note searches
accent- and case-insensitive and matching the note's plain text rather
than its HTML markup. To avoid re-indexing on every auto-save, a save
flags the note for background indexing, and searches match a flagged
note from its normalized text in memory until it's indexed.
Closes#378
The condition `required` flag was removed in #5962, but the column was
kept so older clients could still read the database. The full-text
search changes bump the userdata compatibility version, locking out
those clients, so the column can now be dropped.
Index attachment content into a contentless trigram FTS5 table in a
separate, attached fulltext.sqlite, normalized so matching is accent-
and case-insensitive. For content containing CJK characters, a companion
'ascii'-tokenized table holds bigrams so 1-2 character CJK queries, which
the trigram tokenizer can't match, still work. The extracted text still
lives in the .zotero-ft-cache files, so the index is fully derived and
rebuildable.
Use the FTS index for the fulltextContent condition, falling back to the
cached-text scan for queries too short to index, and point quick
search's content matching at the FTS index in place of the now-removed
word index. (One side effect: quick search now matches attachment
content by substring rather than by word.)
Already-extracted content is migrated into the index at startup, slowing
down on active usage. A background queue then extracts not-yet-indexed
attachments gradually when Zotero is idle. Attachments with no local
file or full-text content are recorded as missing. Content downloaded
via sync is processed into the index immediately when the sync finishes,
rather than waiting for idle like it did before, so it's searchable
immediately in on-demand file-download mode.
The index DB is tied to the main DB via the local user key and rebuilt
if they don't match (e.g., after a delete-and-resync). We compact it by
running FTS5's 'optimize' command once the indexing queue drains, and we
vacuum the attached database when necessary to reclaim disk space.
Closes#2038, #2044
Addresses #1595
Code that attaches its own database to the main connection needs to hook
into that connection's lifecycle. Add onIdle() to run maintenance (e.g.,
vacuuming) during the main database's idle maintenance, and
addCorruptionHandler() to handle a corruption error for the attached
database (after confirming that the main database is actually corrupt,
so a corrupt attached database doesn't trigger main-database recovery).
Load a bundled SQLite extension (e.g., FTS5) by name. mozStorage
disables generic extension loading but allows specific bundled ones.
Extensions are registered per connection, so a loaded extension is
remembered and re-loaded automatically after a reconnect, before
onConnect() callbacks run.
Search now ignores accents, so "seance" matches "séance" and vice versa.
Text is normalized with Unicode NFKD compatibility decomposition (which
also handles typographic ligatures, superscripts, full-width forms,
etc.) plus a small map for letters NFKD leaves alone (ø, œ, æ, ß, ...)
and the fraction slash, via Z.Utilities.Internal.normalizeForSearch().
The HTML tags we support in item fields are stripped, so markup isn't
matched (#81). Typographic quotes (#29, #1876) and dashes are folded to
ASCII.
Each searchable column gets a normalized shadow column --
itemDataValues.valueNormalized, tags.nameNormalized,
creators.firstNameNormalized/lastNameNormalized, and
itemAnnotations.textNormalized/commentNormalized -- populated at write
time and matched via COALESCE(normalized, raw) LIKE. NULL is stored when
normalizing only changes case, so plain-ASCII values are only stored
once. This covers the contains/doesNotContain/beginsWith operators in
both quick search and Advanced Search.
The new columns are local-only derived data and aren't synced. Older
clients will ignore them, so this doesn't break DB compatibility.
Existing rows are backfilled after the startup sync by
Zotero.Schema.populateNormalizedSearchColumns(), which should only take
a few seconds on most databases.
Closes#29, #81, #1300, #1876
Rename the sync-delivered full-text content processor's methods and
state to "sync content" (registerSyncContentProcessor,
processSyncedContent, indexSyncedContent, getSyncedContentCacheFile,
etc.), so it reads distinctly from the local index-building queues added
for content search. No behavior change.
The document-worker bump raised SDT_PROCESSOR_VERSIONS.pdf from 2 to 3,
so the test's "valid" pack fixture (embedding version 2) was rejected as
stale-processor during generation, failing "should generate the pack when
missing."
viewAttachment() fired the 'open' notification before launchFile() saved
a sniffed content type, so AttachmentReadObserver's concurrent lastRead
save could reload the item and discard the pending change, leaving the
attachment with a blank or incorrect content type.
The #focus() Tab/Shift-Tab tests wait on focus/blur events, which fire
only while the window is active. It intermittently loses activation in
CI's Xvfb, so call win.focus() in beforeEach when the window isn't
active.
Temporary diagnostic for the intermittent shift-tab focus-test timeout.
A central afterEach in runtests.js tracks when
Services.focus.activeWindow stops being the main window; the shift-tab
test logs the last-active and first-inactive test in its timeout
message, to identify what leaves the pane inactive in CI.
This reverts commit 03a610e696.
activate() foregrounds a window via the window manager, which CI's Xvfb
doesn't have, so activateZoteroPane() in beforeEach just hangs there
instead of fixing anything. Restore the temporary open-window logging to
try to catch what leaves the pane window inactive.
An image pasted into a note is stored as a hidden attachment, so a
matching search condition (e.g., "# of Tags" is 0) would pull the
image's parent item into the results with no visible match.
Notes and attachments are counted on regular items, and annotations on an
attachment or across a regular item's attachments; other rows are excluded
rather than always matching with a count of 0. Trashed children aren't
counted.
The front-most tab's item appeared under Selected Items instead of Open
Documents, and the library tab's actual selection wasn't shown. Selected
Items now always reflects the item tree selection, and an item that's both
selected and open stays under Open Documents (sorted after the front-most
tab) so that suggestions specific to open documents (e.g., the current page
number, planned in #6001) can apply to it. Notes open in tabs now appear
under Open Documents when adding a note.
Opening the Advanced Search from the quick search cleared the search
text only on the first selected row, while the quick search had applied
it to all of them, so with a multi-collection selection the other
collections stayed filtered by the now-invisible text -- both while the
advanced search was open and after closing it.
With the saved-search editor open, Cmd/Ctrl-Shift-F replaced it with the
temporary Advanced Search pane and the search field's close button
closed it, in both cases discarding the editor's working copy with no
way to recover the edits. Move the collection-switch save prompt into a
shared helper and show it before replacing or closing the editor.
Choosing Save on a collection switch now continues the switch directly
instead of relying on the save's modify notification to re-sync the view.
onConditionSelected() populates the Author value menu asynchronously,
and until it finished, the row's default textbox was still the visible
value control, so serializing the row (running or saving the search)
replaced the loaded author with an empty value. Serialize the stored
value while the menu is pending, and invalidate the population if a
newer condition selection takes over meanwhile.
removeRow() remembered the removed row's index within its own group,
but when the removal emptied the group and pruned it, the index was
applied to the ancestor's children, focusing an unrelated row. Track the
pruned group's slot in its parent instead.
The pane-level Enter handler ran the default action (save in the saved
editor, submit in the temporary pane) regardless of the event target, so
Enter with focus on the Cancel button saved and closed the editor, and
Enter on Clear ran the search. Click the focused button instead, which
the platform otherwise only does for Space.
The "in the same annotation" binding menu was hidden as soon as a group
no longer had two conditions at the bound level, but the group's stored
binding was still saved and applied. After switching one of two bound
annotation conditions to another type, the group looked like a plain
"Match all of the following" group but still required a single
annotation to satisfy all of its conditions, and no control was left to
see or clear the binding.
Show the menu whenever the group is bound, drop the binding only once no
condition at its level remains, and refresh the menus before
collectGroup() in updateSearch() so the first save after such an edit
doesn't serialize stale state.
setAdvancedSearch() invalidated the row unconditionally, unlike
setSearch()/setTags(), so merely opening or closing the Advanced Search
pane (which applies a null filter) re-queried and rebuilt the items
list. A passed search is still always applied, since its conditions may
have been edited in place, and setFilter() still waits for a refresh
already in flight so that callers see the settled view.
_rollUpAnyToLevel() only followed an annotation's parent when the
attachment itself had a parent, so in a search for top-level items, a
tag on a standalone attachment's annotation matched nothing.
The condition seeded by _loadConditions() used `mode: undefined`, but
toJSON() only omits the "/mode" suffix when the mode is exactly false
(what parseCondition() returns), so a saved search migrated from the
obsolete childNote condition serialized -- and synced -- the condition
as "resultLevel/undefined".
_buildQuery() already uses the first top-level joinMode for the
full-text post-filter; with multiple top-level joinMode conditions (only
creatable via the API), combineConditions() used the last.
Extends existing handling for tag selection and quicksearch to handle
advanced search as well.
Fixes#5996 (not just for zotero://select but for all selectItems()
callers)
Instead of just prefixing the labels with "-", indent the whole row,
including the icon. This looks better and fixes FAYT on subcollection
names.
https://forums.zotero.org/discussion/132561/
The #focus() Tab/Shift-Tab tests dispatch synthetic keydowns and wait on
a focusin event to sync with the collections search field's async
(setTimeout) focus. Gecko only fires focus/blur events while the window
is active, so when the pane window wasn't active -- it opens in the
background on macOS/Windows and can lose activation on Linux/CI -- the
focusin never fired and the test hung until Mocha's timeout. Activate
the window in beforeEach, as the item pane and tags box focus tests
already do.
Also removes the temporary open-window logging added in e1f19f34f5.
Cache patches and compressed files by content hash so that they're reused
across FROM versions and releases. Cache entries are touched when used and
expire after 60 days.
The cache key includes a tag covering the xz/mbsdiff versions and the
compression options, so entries are invalidated when the toolchain changes.
Run the per-file mbsdiff/xz work in make_incremental_update.sh and
make_full_update.sh through a parallel job pool, largest files first, and
assemble the manifests serially afterward, producing byte-identical MARs.
Set UPDATE_PACKAGING_JOBS to override the number of parallel jobs.
Tests that shrink the window to test visibility-based rendering restore the
height on their last line without waiting for it to apply, so the next test
could start rendering while the window was still 100px tall. The attachments
box's async render is skipped for panes outside the viewport and only retried
on scroll, so the pane never rendered and assertions on it failed or timed
out.
waitForPreviewBoxRender() without an item id returns immediately if the box
still holds the previous item's completed render, so on a slow machine the
row assertions could run before the new item's async render started.
EPUB recognition translates the EPUB's RDF metadata, and
Zotero.Translators.save() requires translators to be loaded. The full test
run relied on an earlier test file having initialized translators.
Tests restore the stub at the end of the test body, so a failure left
Zotero.Translate.Search.prototype.translate wrapped and retries failed with
"Attempted to wrap translate which is already wrapped".
The reporter only showed the final failure, so if a retry failed differently
(e.g., from a stub left wrapped by the first failure), the original error was
lost.
Zotero.Cite.getLocatorString() requires Zotero.Styles.init() to have run.
The full test run relied on an earlier test file (bibliographyTest) having
initialized styles.
Pushes to branches in this repo already run CI, so run pull_request jobs only
for PRs from forks, which don't trigger push runs here. The push run's checks
still appear on the PR, since checks are attached to the head commit. (This
skips merge-commit testing for same-repo PRs -- the push run tests the branch
as-is.)
Each shard job builds and runs a quarter of the test files via runtests.sh -p.
The deployment ZIP upload and utilities tests move to separate jobs. With the
default fail-fast matrix behavior, a failure in one shard cancels the others.
The sorted file list is split into contiguous chunks of roughly equal total
file size, using size as a stand-in for run time, so new test files are
included automatically and slow test files are spread across shards rather
than landing in the same shard by chance. Contiguous chunks preserve the
alphabetical run order of a full run, so files keep the same preceding files
as in a full run except at chunk starts, and a shard can be reproduced locally
by passing its first and last files to -s and -e.
After a full build, dir_build saves a manifest of build/ files to
staging/.build-manifest. On subsequent runs, if all changed files are
ones that build.sh copies into omni.ja unmodified (chrome/, components/,
resource/, and test/ when tests are staged), zip just those files into
the staged omni.ja instead of rebuilding, taking rebuilds from ~15
seconds to ~0.3 seconds on an M1 Mac. Files are prescreened by size and
mtime so that only changed files need to be hashed. Zotero .ftl files
are also updated at their localization/<locale>/ paths, and test files
are also copied to the staged tests/ directory.
Any other change triggers an automatic full rebuild: files transformed
by build.sh (defaults/, chrome.manifest, version, translators/, styles/,
mozilla .ftl files, CSL locales), removed files, changes to build inputs
in app/ (detected via a size/mtime fingerprint, with xulrunner runtimes
covered by the hash-* files written by fetch_xulrunner), or requesting
tests or devtools that the staged build doesn't include.
Other changes:
- dir_build no longer takes -q and always skips omni.ja compression and
optimization, which only matter for distribution builds made via
build.sh. Use -f (dir_build or build_and_run) to force a full rebuild.
- build_and_run now always rebuilds. -r is deprecated, and -n skips the
rebuild and just launches the app.
- build_and_run no longer passes -purgecaches. Startup caches are
invalidated automatically when the BuildID changes, which now happens
whenever omni.ja is modified (including via add_omni_file), so
relaunching an unchanged build can use the startup cache.
- build_and_run and runtests.sh invoke js-build directly instead of via
'npm run', which saves ~270ms of npm overhead per build.
- The Word integration dylib is now ad-hoc-signed by dir_build, and only
on full rebuilds, since incremental updates don't invalidate the
existing signature. This also covers test builds, which were never
signed before.
- dir_build removes broken symlinks left in build/ when source files are
deleted, which previously broke rsync in prepare_build.
The reader now releases its rendered pages while its document is
hidden, which requires document.visibilityState to be truthful: it's
driven by docShellIsActive, which also controls event-loop throttling
for background tabs. The flag was maintained by scattered one-off
writes and went stale on the transitions they didn't cover: a tab
opened in the background ran at foreground priority (unthrottled rAF
and timers) with a 'visible' document until the next tab switch, and a
hidden tab kept an active docShell forever after its Read Aloud
playback was paused or stopped — in both cases keeping the reader from
ever releasing memory.
Replace the writes with a single _updateDocShellActivity() that
re-derives the state — active only while the tab is selected or
playing Read Aloud — called from the three places its inputs change:
tab creation, tab selection, and Read Aloud status changes.
If the preview fails, log the error and show "Preview unavailable"
instead of breaking the item-add flow with an unhandled rejection.
If io.sort() fails during accept, log and continue unsorted -- the
insert runs the same processor operation, so a real failure triggers the
document-update error dialog instead of a stuck progress window.
Skipping the sort doesn't affect the inserted citation, which the
processor sorts itself. It only determines the stored item order, and
with it the initial bubble order if the citation is edited later -- the
reopened dialog re-sorts once cited data has loaded.
Deleting a search in the collections pane moves it to the trash rather
than erasing it, so the check that closes the saved-search editor when
the edited search is deleted didn't catch it. The save-changes prompt
then appeared on the next selection change, and canceling couldn't
restore the removed row, so the prompt reappeared on every selection
until choosing Don't Save. Treat a trashed search like a deleted one
and close the editor without prompting.
Previously a search could be saved only at a library root. Now the Save
Search button is enabled when collections and/or saved searches within
a single editable library are selected, and the selection is added to
the saved search as collection/savedSearch conditions -- an 'any' group
of them when more than one row is selected. If the search's own join
mode is 'any', its existing conditions move into an 'any' group of
their own so the scope conditions apply to every result instead of
joining the OR. A 'recursive' condition is included per the
recursiveCollections pref.
https://forums.zotero.org/discussion/132528/beta-cannot-create-a-saved-search-from-a-collection
With "Hide Non-Matching Annotations" enabled, an attachment displayed as
empty and non-expandable if no annotations matched the search, so
searching by any non-annotation condition made it impossible to expand
attachments to browse their annotations. Now only hide the non-matching
annotations when the attachment actually has a matching one.
https://forums.zotero.org/discussion/132519/beta-advanced-search-cannot-expand-annotations-of-search-results
Previous command only worked with OpenSSL's labeled output where the hash is the second field; the update also handles LibreSSL (macOS), which prints just the bare hash.
When the item pane is dragged wide, the items pane is squeezed and the
quick search wrapper kept its intrinsic width and overflowed, pushing the
trailing Advanced Search button out under the item pane. Let the wrapper
shrink so the button stays within the pane.
Fixes#5982
The 'items in this view' count failed to update when the result set
changed without a selection change (e.g., quick search, tag filter, or
sort with nothing selected). The listener that refreshes the item pane
on refresh was registered in 8277277948 to fire only once, on initial
load, so later refreshes never recomputed the count.
This is all a regression from the items tree refactor (cbbff600a6),
which stopped running itemSelected() on every refresh.
- Fix a bug where focusing on a read-only field with a common value across items would clear the field and display a "Multiple" placeholder instead
- Fix read-only date fields always showing "Multiple" in batch edit mode, even when values are genuinely different
- Read-only fields are now focusable in batch edit mode
A field that exists at more than one level (e.g., Title, on both
top-level items and attachments) targeting a descendant result level
only matched the closest ancestor, so searching annotations by Title
found nothing, since it checked the parent attachment's title rather
than the top-level item's. Map down from each ancestor level and union
them, testing the predicate once so its bound parameters aren't
duplicated.
Addresses #5978
Saved searches were folded into the "Collection" condition's value menu
in 2016 (9c52ebdf8b), for reasons I can't totally remember. Give Saved
Search its own condition again.
Add annotationType, annotationColor, and annotationAuthor conditions,
each tagged `level: 'annotation'` so the cross-level search logic maps
and negates them correctly. The value fields are drop-down menus: the
annotation types, the reader's color palette, and the library's
existing annotation authors.
Ported from #5839. The PR kept a negated annotation condition (e.g.,
"Annotation Color" "is not" "yellow") from matching every non-annotation
item by checking whether the condition name contained "annotation". This
does the same using the condition's level, which the cross-level logic
already handles, so the existing annotationText and annotationComment
conditions are covered too.
Fixes#5837
The flag forced a condition to be ANDed even in "any" mode, but
condition groups now express that directly. It was never exposed in the
search UI and nothing seems to have been using it.
addCondition()/updateCondition() now throw if passed a truthy
`required`. Not dropping the column now to preserve DB compatibility.
Opening Advanced Search from a non-empty quick search reproduces the
current quick search mode as editable conditions, one per word (or quoted
phrase) joined with "all":
- Title/Creator/Year: a single Title, Creator, Year condition per word,
with the result level set to item (the mode matches only top-level items)
- All Fields & Tags: a single Any Field condition per word
- Everything: Any Field + Full Text Content as an "any" group per word
(relies on grouped full-text composing correctly in SQL)
The Title/Creator/Year mode needs a condition to map to, so add a "Title,
Creator, Year" search condition that expands to the same field set as the
quick search mode (title, publication title, short title, court, year,
citation key, creator), mirroring how Any Field matches All Fields & Tags.
Like Any Field, it expands at query-build time, so the saved search stores
a single condition and its sub-fields don't need their own entries in the
condition menu.
An Any Field condition expands into field/tag/note/creator conditions for
the term. Splice the expansion in right after the condition so it stays at
the same nesting depth, rather than appending it to the end of the
processing queue, which would emit it at the top level instead of within
its group. (Top-level Any Field is unaffected.)
Add annotation text and comments to the conditions the Any Field
condition expands to, matching the fields covered by the All Fields &
Tags quick search mode, as the comment already says is intended. Key
detection and quoted-phrase splitting still differ, since those depend
on the quick search string parsing that Any Field doesn't do.
- Reword the header as one sentence with a result-level menu ("Find
[attachments] matching [all] of the following:")
- Provide a per-group menu to bind the group's descendant conditions to
the same attachment, note, or annotation (e.g., one annotation that is
both red and contains a given word, not two different ones)
- Show a hint that offers to group ungrouped sibling conditions (e.g.,
two annotation conditions at the top level, to bind them to one
annotation)
- Show a warning when conditions can't combine at the chosen result
level (e.g., an annotation condition with a note result level)
- Remove the two legacy checkboxes:
- "Show top-level items" becomes result level = top-level item and is
migrated on save
- "Include parent and child items", which has no result-level
equivalent, keeps working, stays editable, and round-trips on
searches that already have it, but it isn't offered on new searches
and is removed on save if unchecked
Give a search a result level -- top-level item, attachment, note, or
annotation -- and map every condition to that level, so one search can
mix conditions that match at different levels of the item hierarchy
(e.g., a top-level item with a given author and a red annotation on one
of its PDFs). Each condition carries the level(s) it matches at: a match
is mapped up to an ancestor or down to a descendant, level-agnostic
conditions (tags) roll up to the result level, and fields that exist on
both items and attachments (title, url, accessDate) match natively at
either. The result level is stored as a `resultLevel` marker condition
alongside the join mode.
This also removes the temporary annotation-parent hacks, which the
general cross-level mapping replaces.
A fulltextContent condition was evaluated as a global post-filter keyed
on the search's top-level join mode -- correct for a top-level
condition, but not for one inside a group, which must combine with its
siblings under the group's own join mode. The new condition grouping UI
allows fulltextContent to be placed within groups, and we need to do so
to prefill the advanced-search pane from an "Everything" quicksearch.
Materialize a grouped fulltextContent into an itemID set and emit it as an
ordinary itemID IN/NOT IN predicate, so combineConditions composes it
under the group's join mode. Top-level fulltextContent keeps the existing
post-filter unchanged.
This also removes the quicksearch full-text post-filter special case. A
quick search puts its full-text in per-word "any" groups, so the
_hasQuicksearch flag was needed to make the post-filter union those
matches rather than intersect them under the top-level "all" join. Now
that the grouped full-text is composed in SQL it never reaches the
post-filter, so the flag is gone.
With condition groups, the position of conditions and the pairing of
groupStart/groupEnd markers are meaningful, but conditions were diffed
as an unordered member set (compared by value, with additions appended).
A sync-conflict merge could reorder conditions or add/drop group
markers, corrupting the group structure.
Diff them as a single ordered unit instead, like creators. This changes
how concurrent edits to a search are reconciled: rather than merging the
two sides' conditions member by member, a conflicting edit now resolves
wholesale -- the remote condition list replaces the local one (searches
auto-merge to the remote version); a one-sided change still applies that
side's full list. Discarding one side of a rare simultaneous edit is
acceptable and avoids silently corrupting a grouped search's structure.
This was the only user of SearchConditions.equals(), so remove it.
Render the search as a tree of groups: a root group plus nested
search-condition-group elements, each with its own join-mode menu and a
remove control. Each condition row gets a "( )" button that wraps it in
a new group in place, so further conditions can be added to combine with
it under a separate join mode. Switch the builder to rebuild-from-tree --
the DOM is the source of truth and the search's flat conditions (with
groupStart/joinMode/groupEnd markers) are regenerated on each edit, so
the old conditionID-as-index tracking is gone.
Replace the flat anySQL/quicksearch-block assembly in _buildQuery with a
recursive tree of AND/OR groups, built and reduced by a new
Zotero.Search.combineConditions() helper. groupStart/groupEnd markers
delimit nested groups and a joinMode marker sets each group's mode, so a
saved search can combine conditions with arbitrary nesting and per-group
join modes. The per-condition SQL generation is unchanged.
For example, a search built as
search.addCondition('joinMode', 'all');
search.addCondition('title', 'contains', 'foo');
search.addCondition('groupStart', 'true', '');
search.addCondition('joinMode', 'any');
search.addCondition('tag', 'is', 'x');
search.addCondition('tag', 'is', 'y');
search.addCondition('groupEnd', 'true', '');
means "title contains 'foo' AND (tag is 'x' OR tag is 'y')". The 'true'
operator on the group markers is an unused placeholder -- they carry no
value, but a condition's operator can't be empty.
The quick search (matching multiple words) and the Any Field condition
previously had their own special handling in the query builder; they now
use the same grouping as everything else, so that code is gone. Behavior
for existing non-grouped searches is unchanged; new tests cover nested
groups and combineConditions() directly.
A plain select-all would mix uncombinable rows, so scope it to the
current selection's type: a library selection expands to all library
roots; a collection selection to every collection sharing a parent with
a selected one (so multi-level/multi-parent selections expand within
each branches); and a Recently Read selection to Recently Read in every
library. Other rows -- saved searches, Unfiled, Trash, etc. -- have no
useful expansion, so the tree's key handler leaves the selection
untouched instead of clearing it.
Version Read Aloud cache keys with the server-provided cacheVersion so
a version bump misses stale entries and re-fetches correct audio, prune
obsolete entries once per session, and skip caching responses sent with
Cache-Control: no-store.
Regressed by f4adb452ee, which dropped the items-tree body's top padding
to 0 for sticky section headers; the scroll container's overflow then
clipped the top of the first row's focus ring, which drew outside the
row box.
The pinned sticky section header was opaque to paint but had
pointer-events: none, so clicks, drag-starts, and drops fell through to
the item occluded underneath it.
Make the opaque content capture pointer events instead. Clicks no-op
(header rows aren't selectable), drags don't start (header rows aren't
draggable), and drops on the header are rejected so they're a no-op
rather than acting on the list underneath.
Also drop the index > 0 exception when skipping non-selectable rows in
_onSelection(), so clicking a non-selectable row at the top (i.e., the
first library's header) is a true no-op instead of scrolling to the top.
Also skip non-selectable rows in handleActivate(), since the header can
now be double-clicked; without this it would try to open the library as
an item.
Fixes#5960
Arrow-key navigation scrolled the newly selected row flush with the top
of the view, leaving it hidden behind the pinned sticky section header.
Reserve a row's worth of space at the top so the row lands below the
pinned header.
Fixes#5959
#5658 added includeDeleted to the Advanced Search outside the trash, so
trashed items kept matching: trashing a result removed its row, but
re-running the search brought it back. Exclude deleted items by default,
the same as a quick search, and only include them when viewing the trash,
where the scope returns only deleted items.
Fixes#5956
When viewing the trash, trashed collections and saved searches were appended
to the items list unconditionally, so every advanced search (and quick search)
in the trash matched all of them. Now they're filtered by name during a quick
search and excluded entirely when an advanced search or tag filter is active,
since they can't match item-level conditions or tags.
Fixes#5957
The refactor dropped the 8px (COLUMN_PADDING / 2) offset that makes up
for the inline-start padding the first cell omits, so the header label
sat 8px too far left of the item titles.
Pin the header inside the scrolling body as a zero-height position:
sticky element so its width tracks the body's content box and it lines
up with the rows without any JS geometry. Reserve a scrollbar gutter so
the macOS overlay scrollbar doesn't float over the content (the opaque
header must paint above the rows to occlude them, and so above the
scrollbar, so it can't be put under it). Drop the body's top padding for
the item tree so rows clip exactly where the header pins, and keep the
gap below the column header as a margin outside the scroll area.
Fixes#5958
Route each item by its own library -- items already in the target
library are added directly (or skipped, for a library root), while
items from other libraries are copied in -- instead of attempting an
invalid cross-library insert. Disallow a move of such a selection
rather than silently copying.
Fixes#5961
The collections passed through Notes.open into the note window were never
read -- the note editor has no collections setter and EditorInstance.collection
is never assigned on this path -- so they had no effect. New notes are still
added to the selected collection(s) directly in newNote().
When multiple collection-list rows are selected, group the combined
items by library under sticky headers (e.g., "My Library", "Group X (2
collections selected)"), or show a single summary header for a multi-row
selection within one library, with blank spacer rows separating
libraries.
When stickySectionHeaders is enabled, the header of the section at the
top of the view is pinned in an overlay that the rows scroll under,
pushed up as the next section's header arrives. Consumers identify
header rows via isSectionHeader. Row striping restarts at each section
header so every section's first row is the same shade.
stickySectionHeaders defaults to false, so existing tables are
unaffected.
When the items list contains items from more than one library, group
them by library -- in collections-list order, independent of the active
sort -- with a section heading above each library's items.
Grouping is triggered automatically by an items list spanning more than
one library, not the kind of selection behind it, so any future source
of multi-library items would be separated the same way. Today the
cross-library collection selection is the only such source.
Allow selecting multiple collections, saved searches, or library roots in the
collection tree -- within a library or across libraries -- and show the union
of their items. The selection is threaded through the pane as an array
(getCollectionTreeRows(), changeCollectionTreeRows(), etc.); the item pane, tag
selector, reports, and export operate on all selected rows.
Adding items (new items and notes, drag-and-drop, the attachment dialog,
import, Add by Identifier) targets every selected collection. Only rows that can
share an items view may be combined: collections, saved searches, and library
roots mix freely, and multiple Recently Read rows can be combined across
libraries, but other special views (Trash, Duplicates, etc.) and rows from
different visibility groups can't be shown together, so a selection mixing them
keeps only the focused row. In-window advanced search runs across all selected
collections.
Advanced search value autocomplete is now scoped to the searched library, fixing
a long-standing TODO where suggestions were drawn from all libraries regardless
of the search scope; for a cross-library selection it spans the selected
libraries.
For a cross-library selection, the tag selector shows the union of tags
(colored tags only when a single library is in scope, since colors are
per-library), and deleting a tag spans all selected libraries while
rename/color/split are disabled. If advanced search is open, the
collection and saved-search conditions are omitted, since each is scoped
to a single library.
On a fresh profile, the default sort column had no explicit sortDirection,
so the first header click set the default direction instead of reversing
the sort, and no sort indicator was shown.
In other words, don't show the children that make the parent "recently
read" as context rows.
Children that don't match the Recently Read condition (such as notes,
as well as other child attachments that weren't read since the cutoff)
are still shown as context rows.
And add tests for the new behavior, and clean up an unused local var.
- Include the Advanced Search button in the quick-search tab sequence
- Tab: quick-search textbox -> Advanced Search button -> item tree
- Shift-Tab: Advanced Search button -> quick-search textbox
- Shift-Tab: item tree -> Advanced Search button (when the deck is
closed)
- Shift-Tab: Advanced Search collapse button -> New Item button
- Fix Shift-Tab from the item tree doing nothing when the Advanced
Search deck is open; now moves focus to the deck's last focusable
element
- add tooltiptext to expand/collapse and close btns
PubMed saves can include a PMCID even when DOI-based full-text lookup
fails to produce a PDF. In that case the connector asks the desktop
attachment resolver flow for fallback sources, but getFileResolvers()
only considered DOI and URL data, so Zotero never tried the matching
PMC article URL.
Treat PMCID as a valid signal for file lookup eligibility and add a
PubMed Central OA resolver before DOI-based OA lookup. Cover the new
fallback path in attachment resolver tests and the connector
hasAttachmentResolvers endpoint.
1. 862573eab added references to fileRenaming.ftl strings but didn't
add a <link> to it, so document.l10n.formatMessages() returned
nothing. It fell back to Zotero.getString(), which threw an error
due to a missing string in en-US. We didn't catch that, so the entire
search process aborted every time.
2. message.attributes is an optional array of { name, value } objects,
not an object itself, so we were never successfully adding FTL .title
and .label attributes to the parsed strings.
This fixes the 862573eab regression, and makes search match FTL
attributes as it was always intended to.
Fixes#5947
(Confirmed on macOS, but probably Linux too)
Pass openNotExclusive to avoid acquiring an exclusive lock at the OS level
at open time, which fails with an I/O error on network shares. We still set
locking_mode=EXCLUSIVE, so the connection holds an exclusive SQLite lock for
its lifetime and keeps the WAL index in heap memory (no -shm file).
Regression in Zotero 7 (Fx115)
Fixes#4860
The temporary advanced search saved silently under an auto-generated
"Untitled" name. Prompt for a name instead (defaulting to that name),
and label its button "Save Search…" to signal the dialog. The saved-search
editor's button, which saves changes directly, is now just "Save".
https://github.com/zotero/zotero/pull/5658#issuecomment-4696398750
Deleting the saved search being edited triggered onCollectionSelected's
save-changes prompt, which doesn't make sense for a search that's gone.
Detect the deleted search and close the editor without prompting.
The menu was focused programmatically (in response to the filter-button
click), so :focus-visible wasn't set and no focus ring showed. Pass
focusVisible to force the ring.
The remove button on the sole remaining condition is now enabled once a
value is entered, and clicking it replaces the condition with the default
empty one shown when the pane is first opened, instead of staying
disabled.
macOS-normalize-controls zeros margins on inputs and checkboxes but not
menulists or buttons, so their native platform margins threw off the
spacing. Zero them and restore spacing via the containers' gaps.
https://github.com/zotero/zotero/pull/5658#issuecomment-4696398750
The inner <deck> stacks both panes in one grid cell, so the area was
always sized to the taller (saved-search) pane and never shrank back.
The non-selected pane is now removed from layout entirely.
https://github.com/zotero/zotero/pull/5658#issuecomment-4696398750
When conditions are removed, shift conditionIDs so that
conditionIDs always go in increments of 1 (0, 1, 2 ...).
It prevents conditionIDs from conflicting with each other
when conditions are rearranged.
Fixes: #3434
After the first bubble is added, the focused input gets a placeholder
indicating that typing a number will add it as a page to the just-added
bubble. The placeholder is truncated if it's too close to the edge in
multi-item citations.
Also add a tip to the item details popup explaining that locators can
be typed into the main input field, with a link to the documentation.
The tip stops appearing once a typed locator has been used.
---------
Co-authored-by: Dan Stillman <dstillman@zotero.org>
- allow to accept the dialog via cmd/ctrl+Enter from
inside of a panel
- enable click-through on item details panel, so
one can click on the "Accept" button without having
to close the popup first
These two measures allow one to add a bubble, modify
the citation via item details popup, and accept it
with one less step (without having to close the popup first)
Zotero never sets a primary password, so if one is set on the NSS key
database, it was either corrupted or copied in from a Firefox profile,
and stored logins can never be decrypted, since there's no
primary-password prompt. This previously made it impossible to save
credentials without manually deleting cert9.db, key4.db, and
logins.json from the profile directory.
If reading or saving credentials fails and a primary password is set,
clear stored logins and reset the key database so that credentials can
be saved again. Saving the API key or WebDAV password is retried
automatically, so logging in completes without a manual fix, and if
previously stored credentials are lost to a reset, show a one-time
alert prompting the user to log in again.
The once-per-minute rate limit from e40fff6a7d (5.0.91) was inverted,
and the last-error time was never initialized, so the alert hasn't
appeared since 2020.
Wraps the values stored in nsILoginManager with OSKeyStore, which derives
its master key from Keychain on macOS, DPAPI on Windows, and libsecret on
Linux. A copy of the profile alone is no longer enough to extract these
credentials.
Existing plaintext entries are mirrored once per session to a new
"(encrypted)" realm but preserved in the original realm so a user can
still downgrade to a release that doesn't know about encryption. Active
credential changes (sign in, sign out, password change) write to the
encrypted realm only and remove the legacy entry. A future version can
clear any remaining legacy entries on startup.
Patches MOZ_APP_BASENAME in the bundled runtime so the keychain master
key is labeled "Zotero Encrypted Storage" rather than "Firefox Encrypted
Storage", with a check_line guard so a future Mozilla change to the
OSKeyStore label format fails the build instead of silently rebranding
the entry. Also fixes check_line to take an explicit file argument.
The local-file-change watcher added in f21e1b2d32 accumulated changed
item keys globally but was drained separately by each library's storage
engine, with the drained keys filtered to that library. The first
library to file-sync (normally My Library) consumed all pending events,
and keys belonging to other libraries were silently discarded, so files
modified on disk in group libraries were never marked for upload. The
initial and periodic full-scan fallbacks on Windows and Linux were
likewise global, so only the first library ever received them, and
changes made in other libraries while Zotero was closed were never
detected at all.
The sync runner now drains the watcher once per sync session and
immediately runs the modification check on the changed items across all
libraries, recording any changes in the database, and the per-library
storage engines skip the check entirely unless the watcher reports that
the library needs a full scan:
- On all platforms, a library that has never been scanned gets one full
scan, which also gives libraries one recovery scan for changes dropped
by affected releases.
- On Windows/Linux, where the watchers only capture events while Zotero
is running, each library gets a full scan on its first file sync of
the session, on every manual sync, and daily during background syncs
(instead of the previous 3-hour interval, which dated from when scans
were the primary detection mechanism).
- On macOS, libraries scanned since the last FSEvents journal
discontinuity are tracked in a pref, since the journal -- and
therefore the validity of previous scans -- survives restarts.
Also:
- Check FSEvents event flags and fall back to full scans when events
were dropped or coalesced (MustScanSubDirs/UserDropped/KernelDropped/
EventIdsWrapped), and skip HistoryDone sentinel events
- Disable the watcher for the session and fall back to legacy scanning
on backend errors, including when the inotify watch limit is reached,
instead of continuing with silently incomplete coverage
- Prune scan records for deleted libraries, since SQLite can reuse a
deleted library's libraryID
https://forums.zotero.org/discussion/132120/
On macOS 27 and later, the installer gets access to the Word startup
folder via a folder-selection dialog rather than an OS permission
prompt, so support an adjusted banner message and add strings for the
folder dialog.
createDirectoryForItem() wipes and recreates the item's storage
directory before moving in a downloaded file. If removeDir() failed
(e.g., a locked file on Windows), the error bubbled up to zfs.js and
became the generic sync error. Route it through checkFileAccessError()
instead, so the user gets the actionable locked-file message and a
Show Parent Directory button.
https://forums.zotero.org/discussion/132097/
In Add/Edit Citation mode, display a preview of the citation
in the bottom section. The section can be hidden/displayed
via the toggle in the right corner.
Remove io.preview from editor instance, so that citation
dialog knows not to show the preview even if the preference
is set.
A minor refactor to have resizeWindow() resolve
when the animation is fully over, and clear minHeight
on window in list mode before resizing, restoring
it when resizing animation is done, same as in library mode.
It allows us to fully expand the window in list mode before
showing the preview.
Fixes: zotero#5910
The item tree's DOM id carries a view-specific suffix (e.g.
"item-tree-main-default", "item-tree-main-recentlyRead"), but three
call sites compared against a single hardcoded "item-tree-main":
- Collection highlighting on Ctrl/Option (zoteroPane.js) -- match on
the "item-tree-main" prefix to cover all views. This restores
highlighting in Recently Read, where it silently failed.
- Focusing the items list after Add Item by Identifier (lookup.js) --
use the current view's tree id instead of a literal that resolved to
null and threw.
- Shift-Tab from the item tree to the toolbar (zoteroPane.js) -- key the
actionsMap on the current view's tree id.
Add a test confirming focus lands on the items list after a lookup.
https://forums.zotero.org/discussion/130968/collection-of-selected-papers-is-not-highlighted-in-recently-read-panel
The earlier fix (ef7896ab5f) migrated 9.0.x's "<id>-default" prefs
forward to the refactor's new "<id>" key, but that left 9.0.x and 10
beta keying differently in treePrefs.json, so switching between versions
always reset 9.0.x to defaults.
Put the `-default` suffix back so Z9 and Z10 read and write the same
key, and migrate any unsuffixed `<id>` data written by an earlier beta
back to `<id>-default` on first load. The migration runs once per
process and can be removed in a future release once beta users have all
updated.
https://forums.zotero.org/discussion/131653/zotero-10-beta-5-reinitializes-the-columns-in-main-pane
example:
"Atmospheric Chemistry and Physics" is currently incorrectly abbreviated as "Atmospheric Chem. Phys." should be abbreviated "Atmos. Chem. Phys."
Zotero.Tags.init() keyed the id cache by the raw tag name from the
database, but getID() and Items._loadTags() both normalize names via
cleanData() (trim + NFC). A tag stored in a non-normalized form (e.g.,
non-NFC or with surrounding whitespace) could therefore never be matched
by getID(), which returned false. When such a tag was removed from an
item, that false was bound as the tagID parameter in the DELETE FROM
itemTags query, throwing "Invalid boolean parameter 1 'false'" and
aborting the save. Since this fired while applying changes during sync,
it persistently blocked sync.
To fix, normalize names in init() to match getID()/_loadTags().
https://forums.zotero.org/discussion/131901/error-report-seems-to-be-preventing-sync
vacuum() and the APFS-cloning offline backup path both close and
reopen the SQLite connection, which drops all ATTACHed databases --
including the in-memory "indexing" alias used for the fulltextWords
scratch table set up in Fulltext.init(). Once the connection is
reopened, indexing queries fail with "no such table:
indexing.fulltextWords", and indexItems()'s ignoreErrors path
routes the error to logError(), so indexing silently stops working
for the rest of the session.
Add an onConnect() hook on Zotero.DBConnection for per-connection
state that doesn't persist across reopens, and use it from
Fulltext.init() to re-attach the indexing DB on each reconnect.
Regression sources:
- 67288047f3 ("Use APFS cloning for file copies on macOS") flips
online idle backups to offline-with-clone on APFS, so every idle
backup interval (24h default) closes and reopens the connection.
Affects Mac users on APFS.
- b27c4cb023 ("Enable SQLite WAL mode and add periodic VACUUM
INTO") adds the vacuum path, which closes and reopens on the
first idle period in a session that passes the freelist/time
gates. Affects all users with sufficient DB churn, roughly once
per 14 days.
https://forums.zotero.org/discussion/131576/debug-id-d848621212-indexing-of-pdfs-fail-zotero-9-0-3https://forums.zotero.org/discussion/131718/possible-bug-regression-report-search-unusable-on-macos-zotero-9-0-3-with-large-library
For a dependent CSL style with no default-locale of its own, citeproc-js
parses the parent's XML and silently uses the parent's default-locale
over any user-selected locale, making the locale dropdown a no-op. Add
a Zotero.Style.effectiveLocale getter that falls back to the parent's
locale, and use it in updateLocaleList and the locale-selector custom
element to disable the dropdown in that case.
https://forums.zotero.org/discussion/comment/512891/#Comment_512891
The general HTTP layer's automatic 429/Retry-After retry only retries
the one failed request, but the sync layer wants to pause its entire
batch of concurrent requests via concurrentCaller.pause(). Add a
noRetryOnThrottle option to Zotero.HTTP.request() that throws on 429 or
503 with Retry-After so the caller can apply its own throttling, and
have syncAPIClient set it. Extend sync's catch block to also honor
Retry-After on 503 (previously only invoked via _check429).
When a user's account is deleted and then undeleted on the server, the
dataserver creates a fresh shardLibraries row at a low version, while the
client still has a much higher local library version. Subsequent syncs
then throw "_libraryVersion cannot decrease" and the user is stuck.
Tag the cannot-decrease errors from the library version setters with a
named error, catch it in Engine.start(), reset libraryVersion and
storageVersion to -1, and restart. The retry hits the existing
libraryVersion == -1 branch and runs _fullSync(), which re-uploads the
local library against the recreated server library.
Previously only 503 + Retry-After was retried automatically; 429 was
handled only inside the sync API client. Extend _retryOnServerError()
to also retry on 429, honoring Retry-After on both 429 and 503 and
falling back to the existing exponential backoff otherwise.
In list mode, sort libraries by the count of their items
cited in the current document, falling back to alphabetic sorting
when counts are equal, with "My Library" always getting
priority over other groups.
Fixes: #5924
The test opened an advanced search window but never closed it, which
likely caused the intermittent failures in the ZoteroPane focus() test
"should shift-tab across the zotero pane".
Ignore persisted compatibility flag from previous app version in non-stable releases to fix disabled for wrongly using old flag
Ignore max version compatibility for plugin update check on non-stable releases
Relevant: https://forums.zotero.org/discussion/131096/
---------
Co-authored-by: Dan Stillman <dstillman@zotero.org>
Before the refactor, main library column prefs were keyed under
"<id>-default", because the visibilityGroup getter returns 'default'
(truthy) for the main library. The refactor changed the suffix logic to
only append non-default groups, leaving existing prefs orphaned at
"<id>-default" while the new code reads/writes "<id>".
Fall back to the legacy key on load when the new key is missing or an
empty object (which can be written out by an unmodified post-refactor
build that flushed prefs on a visibility-group switch, like switching to
feeds), and drop the legacy key on the next write.
https://forums.zotero.org/discussion/131202/lost-columns-displayed-choice-in-the-items-tree-after-update-to-10-beta-4
Covers the regression fixed by a7d001fc1d. The prior test for
_setHighlightedRowsCallback() called the callback directly, bypassing
the focus check and the keydown handler.
After the item tree refactor, the items tree id became "item-tree-main"
instead of "item-tree-main-default". Update the remaining call sites,
restoring collection highlighting on Ctrl/Option, focusing the item tree
after Add Item by Identifier, and Shift-Tab focus movement from the item
tree to the toolbar (which only still worked due to the native tab
order).
HTTP.download() was rewritten in 0fe31b0f04 to use fetch() and build the
Basic auth header itself via btoa(username + ':' + password), but the
username and password come from nsIURI.username/password, which are
percent-encoded. As a result, a username like "user@example.com" was
sent as "user%40example.com", causing a 401 on every WebDAV download for
any user with @, :, space, etc. in their username. Other request types
were unaffected because they go through xmlhttp.open(method, url, true,
username, password), which decodes internally.
Decode username and password in _parseURI() so the values returned can
be used directly for Basic auth (and as a side effect, fix the other
display/use sites that were getting the percent-encoded form).
https://forums.zotero.org/discussion/131174/zotero-10-betas-1-2-3-cant-download-from-webdav-http-401
Use one registry to avoid competing of language detect between plugins,
as resources declared as optional is identical to resource missing and thus fails the check and returns null for the string (https://searchfox.org/mozilla-esr140/source/intl/l10n/rust/l10nregistry-rs/src/registry/asynchronous.rs#140)
Register the plugin FTL for all languages Zotero supports with proper fallback logic so that even resources are declared as required, the check doesn't fail when plugin doesn't provide the resource.
Fix Zotero.File.getResourceAsync to use NetUtil channel to handle jar: url with `@`.
In a newly created profile, we check whether another profile is using
the default data directory, and if so, we create a new data directory
named after the profile (e.g., "Zotero Work"). But on Windows, paths in
prefs.js are stored with escaped backslaches (C:\\Users\\foo\\Zotero),
so searching for the JS string with literal single backslashes
(C:\Users\foo\Zotero) always failed, we would conclude that the default
dir was unused, and we would reuse the existing database.
To fix, escape backslashes in dataDir before the substring check.
Mac/Linux paths have no backslashes so this is a no-op there.
The new-install branch in DataDirectory.init() read prefs.js from the
default Firefox profile to detect a pre-2017 dataDir setting, and the
read was unwrapped, making prefs.js access errors fatal at startup [1].
We could add a try/catch, but after nine years, it's probably safe to
just remove the migration. If anyone is returning from >9 years in the
wilderness and they still want old unsynced Zotero data, they can
manually move their data to the default location.
[1] https://forums.zotero.org/discussion/131176/installation-error-accessing-mozillas-pref-js-see-msg-pls
Columns now have properties: `enabledIn`, `disabledIn` and `defaultIn`,
corresponding to column picker availability and default visibility. The
properties now filter based on attached collection view type instead of
visibilityGroup.
Visibility groups are for views where we want distinct column sets to
persist, like the feeds view.
Collection type properties are used to specify which columns are
available for a given type, regardless of whether it's in a different
visibility group or not.
Split ItemTree megaclass into:
- ItemTree - concerned with drawing the virtualized table container and
column interaction
- ItemTreeRowProvider - provides rows and issues notifications for
render updates
- ItemTreeRow and subclasses - contains row-specific data and rendering
logic
- CollectionViewItemTree and its accompanying classes - a version of
ItemTree that renders items attached to a given Collection or
CollectionView (CollectionTreeRow).
Various improvements in logic and rendering, separation of concerns.
When a custom data directory is on a network share or in a cloud storage
folder, SQLite can fail to open with NS_ERROR_STORAGE_IOERR and the user
previously had to manually edit prefs.js to recover. Show a "Use Default
Location" button in the startup error dialog that resets the data
directory pref and restarts.
Addresses #4860
Repo moved to zotero/document-worker on GitHub
After pulling, run:
git submodule sync
git submodule update --init document-worker
If an old `pdf-worker/` directory is left behind, it can be removed manually.
Add a spy on Zotero.HTTP.download and update call-count assertions to
split between request() and download() calls. Previously download()
delegated to request(), so a single requestStub tracked everything.
- Replace XHR-based download with fetch() + response.body streaming,
writing chunks to disk via IOUtils instead of buffering the entire
response in memory
- Separate out the retry and URL-parsing logic so it can be reused
between request() and download()
- Split the ZFS download code into a request() with `followRedirects:
false` to get the metadata headers and a separate download() to
download the file
Fixes#5476, Downloading of large files is broken
- Restructure the pane layout: account info (username, email,
login/logout) at the top, followed by a separate "Sync" section with
an intro description and "Learn more" link, data sync options, and
file syncing
- Migrate hardcoded "Zotero storage" strings to FTL and fix
capitalization
- Rename the "Zotero" storage dropdown to "Zotero Storage".
Don't throw when updateDisplayTitle() retry fails after loading
annotation data for a corrupt item. Also add an integrity check to
delete annotation items without itemAnnotations rows.
Fixes https://forums.zotero.org/discussion/130872
These tested racing an offline backup against an in-progress online
backup, which wouldn't happen in practice (schema backups run at startup
before the idle observer, and someone is very unlikely to perform a DB
integrity check immediately after returning from an idle that triggered
a backup). The tests relied on fragile timing and a shared tmp file,
causing failures on Linux after the WAL checkpoint change. The tests
were already skipped on macOS after switching to APFS clones.
- Switch journal mode from DELETE to WAL for better write performance.
With EXCLUSIVE locking mode, SQLite uses heap memory for the WAL
index, avoiding an -shm file. Set synchronous=NORMAL (matching what
Mozilla uses for Places). Checkpoint WAL on database close so the
.sqlite file has all data (for copies or backups).
- Add periodic database compaction on idle (after DB backup) using
VACUUM INTO and do an atomic file swap back to zotero.sqlite if no
writes occurred during the operation. Check if vacuuming is needed
based on time interval (default 14 days) and freelist ratio (default
10% threshold).
- Disable auto_vacuum, which causes fragmentation and is unnecessary
with periodic VACUUM
- Remove the VACUUM call from the integrity check, which was always just
an awkward hack to let people trigger a VACUUM without having an
explicit button
Closes#652
Fetch pinned-incrementals-{platform} from the deploy server and merge
with the normal last-N incrementals list. This ensures incremental MARs
are always built from key versions (e.g., last release of each major
version) regardless of how many newer versions have been released.
Split build_and_deploy into build_for_deploy (builds, uploads to S3,
rsyncs manifests) and a deploy script in client-downloads (updates
updates JSON, updates incrementals, runs deploy command).
Release scripts support -b flag for build-only (deploy later manually).
Beta/dev/test scripts build and deploy automatically via SSH.
Move incrementals files from S3 to deploy server. Update DEPLOY_PATH to
root of client-downloads so manifests and deploy script paths are
derived from it.
The `_getColumns()` column cache key didn't account for library type, so
switching between personal and group libraries reused cached columns
from the previous library type. This caused `groupLibrariesOnly` columns
(Added By, Modified By) to be missing in group libraries or incorrectly
shown in personal libraries.
Additionally, `VirtualizedTable.componentDidUpdate()` only recreated its
internal `Columns` object when the id prop changed, not when the columns
prop changed, so even with correct column rebuilding in `ItemTree`, the
table wouldn't pick up the new column list.
https://forums.zotero.org/discussion/130521/added-by-column-missing
Add Zotero.File.copyFile(), which uses clonefile() on APFS with a
fallback to IOUtils.copy(), and use it for all significant file copies.
APFS is detected and cached for each parent folder via
Zotero.File.isAPFS(), which uses statfs().
On APFS, all database backups now use the offline (close/clone/reopen)
path instead of the SQLite online backup API. Cloning is nearly
instant, and backup files share disk blocks via copy-on-write, saving
potentially gigabytes of space.
Closes#5330
Move file-renaming-* and rename-files-preview-* strings from zotero.ftl
to a dedicated fileRenaming.ftl. Rename rename-files-preview-* keys to
file-renaming-preview-window-* for consistency.
- prevent the focus handler from resetting the cached
items in search handler
- wait for search to finish after changing dialog type
in "should not display empty note child rows" test
Pass filterChildItems to itemTree from citationDialog
to filter out unwanted child rows before adding them
in toggleOpenState.
In annotations mode, do not add child notes and
attachments without annotations
In add-note mode, only keep child notes that have content
- updated annotations dialog type icons for dark and light mode
- use checkmark instead of right arrow for "accept" button
- fix incorrect sizing of "accept" and "cancel" icons
- Allow different min-width for list vs library layouts.
In library mode, we need at least 1000px to have a functional
itemTree, especially when sidepane is present. But for list
mode, 1000px is very wide, so min-width should be 800px.
Increased min-height of itemTree from 200px to 400px
- When the dialog closes, store the width/height of
the current dialog's layout. Then, when the dialog opens
next time in that layout, set window's width/height based on last saved
params. That way, one can open a citation dialog in list
mode at 800px, then open annotations dialog (always library mode)
at 1000px, and then have the citation dialog in list mode open
again at its intended 800px.
- If one switches from library to list mode, add a smooth
window resizing animation to shrink diagonally from current width
to 800px. If one switches from list to library mode,
window smoothly resizes to 1000px min-width. This is
not applied to linux where win resizing behavior is a bit jumpy.
If the server returns a write report with 403 for admin-only settings,
mark successful settings as synced and reset rejected settings to
their remote values by re-downloading from the server.
Needed for #5862
- fix breakage when initially setting annotation dialog
mode to library
- on click of a non-annotation item in suggested items,
select the top-level library if the item could not be selected
in the initial state of the itemTree
- increase min-width of the dialog to 1000px to ensure
there's always space for the sidebar.
Fetch emails from /keys response and login session result, persist them
in the local database, and display them in the sync settings for both
logged-in and logged-out-but-linked states.
Also unify the separate logged-out-linked and logged-in containers into
a single layout that toggles elements based on login state.
Requires dataserver and stream-server changes
When the server returns an attachment without lastRead (because it was
cleared), fromJSON was not clearing the local value since it only
processes fields present in the JSON. Add explicit check after field
processing to clear lastRead when absent.
Use itemID offset to advance through batches instead of comparing
result counts, which would bail out when the number of remaining items
equaled the previous batch size.
Backfill groupItems rows that have lastModifiedByUserID but null
createdByUserID (not totally sure how), and track completion with a
per-library flag in the settings table so the backfill only runs once
(since createdByUserID could potentially be permanently unavailable).
Fire a modify notifier event after _updateGroupItemUsers() so the
item tree refreshes to show the newly backfilled Added By / Modified By
values. Also change error handling from return to break so partial
progress still triggers the refresh.
When a lastRead synced setting for a group item was downloaded and
applied via AttachmentReadObserver.notify(), the group attachment item
was saved without skipSyncedUpdate, causing it to be marked as synced=0.
On the next sync, this could trigger an upload attempt that failed with
403 for read-only group libraries.
When a user logs out without removing local data, the sync pane now
shows a third UI state with the linked username, a "Log In" button
(constrained to the same account), and a "Switch Accounts…" button
that shows a hard confirmation dialog requiring the user to type
"remove local data" before proceeding. On confirmation, local data
is reset and the app restarts, reopening the account preferences.
Deleted settings were written to syncDeleteLog but never uploaded. Add
dedicated setting deletion upload in _startUpload() using individual
DELETE requests per setting, since the API doesn't support batch setting
deletion.
The same skipEditCheck fix from 5dc9705294 but for the synced setting
handler, which updates lastRead when a lastRead setting arrives via
sync for a group attachment.
Replaces the login form with a "Log In" button that creates a dataserver
login session and opens the website for logging in. The client polls for
session completion every few seconds and also watches for a notification
from the streaming server.
- Non-force delete clears `lastRead` on all child attachments (with
prompt), force delete moves to trash (matching collection behavior)
- Re-run search on item modify in Recently Read view so items disappear
when lastRead is cleared (also handles sync changes)
- Add columns as options in item tree, visible only in group libraries
- Fall back to createdByUserID for Modified By when lastModifiedByUserID
is not set
- Update lastModifiedByUserID on local saves when dateModified changes
- Fix backfill in _updateGroupItemUsers() to process all batches
- Update formatColumnName() to support Fluent string keys
Closes#233
When stop() or stopOnError rejected queued task promises, the rejection
handlers (via Promise.allSettled) weren't attached yet, causing Mozilla
to report each one as "uncaught exception: Object". Add no-op catch
handlers before rejecting so the runtime knows they'll be handled
downstream. Also fix stopOnError path to reject with CanceledException
instead of undefined, and remove legacy Bluebird handledRejection flag.
Track when attachments are last opened or read, storing a `lastRead` Unix timestamp on the attachment. For user library items, `lastRead` syncs as an attachment property in item JSON. For group library items, it syncs via a per-user synced setting (like `lastPageIndex`).
- Add `lastRead` column to `itemAttachments`
- Add `AttachmentReadObserver` to update `lastRead` on file open and page change (throttled to 5 min for page changes)
- Add "Recently Read" virtual collection (items read in last 14 days, sorted by `lastRead` descending)
- Add `lastRead` search condition with date operators
- Add `lastRead` item tree column with new `dependsOnChildren` property for parent item aggregation
- Add `getItemLastRead()` to return max `lastRead` across child attachments
Also:
- Generalize collection tree SCSS to support universal (context-fill) icons alongside themed icons
---------
Co-authored-by: Dan Stillman <dstillman@zotero.org>
After editing the parent item, switching back to the attachment item before the parent item's save completes creates a race condition between the save and the attachment render.
Moved item pane tests related to file renaming to a separate describe() block.
The firstCreator/sortCreator SQL now falls back through primary →
editor → director → contributor. This ensures Video Recording items
with only a Director creator show that creator in the items list.
Also refactors the repetitive SQL-building in _getFirstCreatorSQL()
and _getSortCreatorSQL() into helper functions.
Closes#5720
The %PDF- sniffer entry had no offset constraint, so it matched anywhere
in the file sample. This could cause ZIP files containing PDFs to be
misidentified as application/pdf when opened, since %PDF- could appear
within the first 200 bytes of the archive. Pin the match to offset 0,
where it must appear in a valid PDF.
Fixes https://forums.zotero.org/discussion/130161/zip-files-and-indexing
Plus guidance-panel changes:
- Fix description not updating when multiple panels exist
in the document
- Fix nonfunctional noautohide attribute
- Show "Got It" button for noautohide with no navigation
---------
Co-authored-by: Dan Stillman <dstillman@zotero.org>
resetData() clears user data and sync state using application APIs
instead of doing a full Zotero shutdown/reinit cycle, making it much
faster for tests that just need clean state between runs.
Also make Tags.init(), Creators.init(), Users.init(), and
SyncedSettings.loadAll() properly clear their caches before reloading,
so they are safe to call more than once.
Instead of testing against the build/ directory directly, zip it up first
and then unzip it back into build/ before running tests. This ensures
tests run against the same artifact that gets uploaded to S3 and used by
the deployment scripts, catching issues that only manifest after a zip
round-trip (e.g., resolved symlinks, missing files).
Also makes Ruby setup conditional on deployment branches
Similar to how quickSearch is cleared already.
To avoid rerunning itemTree refresh twice (once for
clearing quickSearch and once for clearing tag filter),
clear tags and search text directly on collectionTreeRow.
And then call itemTree.refreshAndMaintainSelection to
refresh everything. Without it, tag selector sometimes
would find itself out of sync with current search
results and display tags as if quickSearch was not
cleared.
Fixes#5601
Use platform-native file change APIs to track which stored-file
attachments have been modified, avoiding expensive full scans of all
attachment files during sync.
Backends:
- macOS: FSEvents (persistent event journal, survives restarts)
- Windows: ReadDirectoryChangesW (live recursive directory watch
via overlapped I/O polling)
- Linux: inotify (live per-directory watches)
On unsupported platforms or on error, falls back to existing scan logic.
Don't mark attachments for download when server has no file (mtime is
null), and clear TO_DOWNLOAD state on 404 responses in both ZFS and
WebDAV so items aren't re-checked on every subsequent sync.
Move _ObjectType, _objectTypePlural, _ObjectTypePlural, and
_ObjectsClass from per-instance properties to prototype properties,
computed once per subclass on first instantiation. This avoids a huge
number of duplicate string allocations and reduce memory usage a bit
(e.g., ~35 MB in a 19K-item test library).
setOutputFormat() just sets this.opt.mode and this.fun.decorate, so
there shouldn't be a need to cache separate engines per format. The
format is excluded from the cache key, and setOutputFormat() is called
on cache hit instead.
Also simplify _preloadFormat() in quickCopy.js to only create one
engine instead of two (one per format).
Saves ~7 MB of resident memory (~140 MB before the citeproc-js
sort-macro caching optimization).
Previously, macros referenced by sort keys were expanded inline into
the sort token array every time they were encountered. Non-sort macros
were already cached in state.macros[name] and referenced via closures,
but sort-mode macros were duplicated across sort keys.
With a complex style like chicago-shortened-notes-bibliography (155
macros, 6 macro-based sort keys), this produced 179,605 tokens and
355,652 closures in bibliography_sort alone -- 99.9% of all tokens.
This commit caches sort-mode macro expansions in state.sort_macros[name]
using the same closure-reference approach as non-sort macros.
In addition to the memory savings, avoiding the redundant macro
expansion makes engine initialization dramatically faster with complex
styles.
Results with chicago-shortened-notes-bibliography:
- Engine initialization: 464 ms -> 57 ms (8x faster)
- Per-engine heap (Node.js): 123 MB -> 6.7 MB (94.6% reduction)
- Resident per engine (Firefox): ~140 MB -> ~7 MB
- Total tokens: 187,893 -> 11,669 (93.8% reduction)
- Total closures: 370,982 -> 21,823 (94.1% reduction)
- All 1,502 citeproc-js tests pass (at f88a47e6)
- Same 1501/1505 pass on master (same 4 pre-existing failures)
The previous approach of capturing and replaying the Authorization header
from an initial request worked for Basic auth but broke with Digest auth,
where the header includes the HTTP method and URI in the hash. This caused
each replayed request to be rejected with a 401, which Firefox wouldn't
retry because the request already included Authorization.
Instead, split _channelAuthorization into _basicAuthHeader and
_digestParams. For Basic auth, the header is still replayed as-is. For
Digest, the challenge parameters (realm, nonce, qop, etc.) are cached
and a fresh Authorization header is computed per request.
_getAuthorizationHeaders() now takes method and URI parameters so it can
compute the correct Digest response hash for each request.
By adding a missing getter override. If a setter is overridden, the
corresponding getter also needs to be overridden, or it will implicitly
return undefined.
Fixes#5793
Failed when running CollectionTree and ZoteroPane tests together
- Make `deleteSelectedItems()` async and await `deleteSelection()` so
the full Notifier commit chain (including items list refresh)
completes before the function resolves. Update tests to call
`deleteSelectedItems()` directly instead of calling fire-and-forget
keyboard events, and add a stubbed test for the Delete key binding.
- Fix "Delete Item…" menu test to explicitly re-select after `saveTx()`
to guarantee selection state
`checkServer()` was capturing the Authorization header from OPTIONS and
explicitly setting it on PROPFIND via `setRequestHeader()`. If the auth
type didn't match what the server required for PROPFIND (e.g., Basic vs.
Digest, though maybe other things too), the explicit header could
prevent Firefox from negotiating the correct auth scheme on the 401
challenge.
To fix, move `onAuthorizationHeader()` from OPTIONS to PROPFIND and
don't pass captured auth to PROPFIND, letting Firefox handle the auth
transparently.
https://forums.zotero.org/discussion/129665/webdav-error-for-zotero-8-0-3
Some WebDAV servers allow unauthenticated OPTIONS requests, so the
Authorization header capturing added in 089701eca8 wouldn't work.
PROPFIND with Depth: 0 reliably requires authentication while only
returning properties of the directory itself.
https://forums.zotero.org/discussion/comment/506993/#Comment_506993
- on all platforms, Space will open the menulist popup
- on Windows, ArrowUp/ArrowDown will change selection without triggering command event.
This is needed to avoid alerts that are meant to fire after a confirmed
selection when the user is just trying to navigate the menu (e.g. item type menu)
- on Windows, fix odd appearance of checked menulist options in advanced search
condition dropdowns when "More" option is selected
- on Linux, fix menulist focus ring not appearing in Advanced Search
or Preferences (https://forums.zotero.org/discussion/129498/advanced-search-drop-down-menus-are-not-highlighted-during-keyboard-navigation)
Fixes: #5772
Firefox no longer seems to send a previously used Authorization header
with subsequent requests. This results in extra requests, since every
WebDAV request triggers a 401, and also results in errors, because a PUT
is sent without Authorization, causing some WebDAV servers to
immediately send a 401 and close the connection, which the HTTP layer
interprets as a connection failure (status 0). (It's also not good to
try to send a large file just to get a 401.)
There might be some way to share context between requests, but instead,
just get the used Authorization header and include that explicitly in
future requests.
To test this properly, we have to switch to using httpd.js for all
WebDAV requests, since the mocked XHR doesn't trigger the 401 retry.
https://forums.zotero.org/discussion/129194/webdav-uploads-fail-on-zotero-8-put-sent-without-authorization-server-closes-connection
Any modification to an item belonging to collections in the trash would
cause those collections to be removed from the item when the item was
uploaded.
Fixes#5766
* citation dlg: local. CSL locator in guidance panel
Use localized short CSL page locator in guidance panel
text example ("p10")
Fixes: #5745
Also, fix dialog mode switch buttons not properly sizing
in locales where list/library string is longer.
* Remove `overflow: visible` in RTF Scan to prevent richlistbox from expanding the width and causing clipping
* Introduce small margins as an alternative to prevent focus rings from being clipped in the `wizard`
* Fix "Display as" alignment on Windows
- after a new bubble is added to the citation, it is recorded
as a just-added bubble. The next locator typed without a search query
will go to that item instead of going to the item before where the locator was typed.
Same logic applies when multiple bubbles are added at once.
- the record of just-added bubbles is cleared on focusout
or keypress of an arrow key. That way, it's discarded if the user
is almost certainly not intending to immediately type a locator.
- added a special case to recognize a numeric value as a page locator
if it is typed when just-added bubble is recorded. That special locator
will be added to the just-added bubble as one is typing without
pressing Enter after debounce. Enter will immediately add the locator
without waiting for debounce.
- if a just-added bubble is recorded, cmd-z will clear
whatever numeric locator may have been typed and place
it back into the input, in case one meant to type an
actual search query
- added a special case to recognize ":<number>" as a page locator
in the same circumstances that "page <number>" is currently recognized
- do not use year extraction (SearchHandler._cleanYear)
when parsing input. It strips the first number from
a range of numbers and conflicts with the new locator
logic.
- ensure a bubble with a very long locator does not overflow
Instead of adding listeners to the window icon unless
no-titlebar-icon is set on the window, add listeners
to the icon whenever it exists. That way, one doesn't have
to remember to add the no-titlebar-icon attribute to
every window that doesn't have an icon.
Fixes: #5733
- restore guidance panel with instructions appearing on the first run
- clear firstRunGuidanceShown.quickFormat pref
- set slightly higher arrow padding on win/linux for the guidance
panel. If the arrow is too close to the edge, it appears
partly disconnected from the panel.
Fixes: #5738
* makeFormattedBibliographyOrCitationList(): Don't modify CSL.Engine state
Use previewCitationCluster(), which leaves the processor state alone,
instead of calling appendCitationCluster(), which has side effects and
isn't used anywhere else in Zotero.
* Make CSL.Engine caching opt-in
* Remove unnecessary CSL.Engine reinitialization
* Code style
* Add/update tests
After citation-style-language/styles#7928 renamed Vancouver styles to
NLM terminology, Zotero installations end up with both vancouver.csl
and nlm-citation-sequence.csl. To fix, on init, delete any installed style
whose ID appears in the renamed-styles mapping if the target style
also exists.
---------
Co-authored-by: Dan Stillman <dstillman@zotero.org>
Load tags and annotations before itemData, since `_loadItemData()`
calls `updateDisplayTitle()`, which needs creators, tags, and
annotations to be loaded for some item types.
This should fix slow startup times in Zotero 8 in libraries with many
(e.g., tens of thousands) of annotations.
The slowdown here was introduced by #3416 loading annotations in the
`catch`, but it was already incorrect for tags to be loaded that way at
startup. Creators were loaded before itemData, but tags weren't.
The `catch` might not be necessary at all now that the loading order for
`loadAllData()` is fixed, but we'll need to test further to confirm
that.
Fixes#5724
We don't need to try to load annotations for regular items or creators
or tags for annotations.
Every annotation item's annotation is still loaded independently, but we
can try to fix that separately. For now, just don't try to load data
that will never exist.
Addresses #5724
Disable wrap_url_and_doi for citeproc instances used in word processor
integrations to prevent brackets and other special characters in DOIs
from being double-encoded.
Fixes#5557
fix: #5683fix: #5684fix: #5688
Rename tab hook focus -> focusFirst
Replace tab-focus custom event with tab hook refocus
Move context pane _handleTabReady to Zotero_Tabs
Focus note editor after opening tab
Use setTimeout to ensure the focus does not stay in tab element
This avoids redundant field migration on other devices if it has already
run on one. Conflict resolution should automatically ignore identical
changes, so it's not the end of the world if it runs in multiple places,
but we might as well avoid it if we can.
Creators shouldn't be extracted if creators of the same type already
exist on the item, to follow citeproc-js behavior, but the code to do
that was broken.
File renaming has a Notifier handler for item modifications that needs
best-attachment titles, but attachments and their item data might not be
loaded even when parent items are. (This came up when migrating fields
from Extra after a global schema update before the main window had
loaded.)
- Replace polling with a `ready` promise for initialization
- Use fixed height for the rich list and re-order initialization to avoid layout shifting
- Ensure the "accept" button is disabled until initialization completes
Allow one to switch between "Add/Edit Citation" and "Add Note" modes
in the citation dialog via segmented control in the bottom
left corner.
One cannot switch modes when editing an existing citation or
when inserting a citation in the note editor.
Use the same segmented control UI to switch between
list and library modes for clarity. Replace icons with
"List" and "Library" strings.
Collect some of the state variables into DIALOG_STATE object
for better organization.
Fixes: #5582
Also, remove earlier logic of not resizing window on refocus
on Linux if focus was lost <100ms ago. That workaround
no longer works, and now that we retain the window sizing
after search reruns, it is less of an issue.
Addresses: #5590
When fetching open tabs for the citation dialog, fall back
to the last saved session state if the main window is closed.
Session state was already being updated when zotero pane
is closed.
Fixes: #5661
When a Zotero plugin is disabled, user-modified preferences can be inadvertently reset to their default values. This occurs under specific conditions where a parent preference has not been user-modified, but a child preference within that branch *has* been modified.
The root cause lies in the `Zotero.Plugins.clearDefaultPrefs` function. For preferences that do not have a user-set value (`!branch.prefHasUserValue(pref)`), it incorrectly calls `Services.prefs.getDefaultBranch('').deleteBranch(pref)`. While intended to clear unmodified defaults, `deleteBranch(pref)` operates on the *entire preference branch* starting with `pref`, inadvertently removing any user-modified sub-preferences as well.
This commit changes the problematic line from `branch.deleteBranch(pref)` to `branch.clearUserPref(pref)`. `clearUserPref(pref)` correctly removes only the user-set value for the *specific* preference `pref`, leaving default values and any user-modified sub-preferences intact. This ensures that only truly unmodified default preferences are cleared, preserving user data for related sub-preferences.
This fix prevents unintended loss of user settings when plugins are disabled.
After 015769a removed pointer-events: none from table cells,
clicking on the collectionTree would always trigger
a focusout event, ZoteroPane.handleBlur would call
collectionTree.setHighlightedRows, which would always
redraw the collectionTree. This complete tree redraw on
every click made it impossible to register double-clicks.
With this change, setHighlightedRows won't have
any effect if called with the same rows to highlight
as before (including no rows).
Without constant tree redraws, double-clicks fire as expected.
Fixes: #5655
Fix new collection dialog appearing broken when
opened via "New Collection" option of context menu
on "My Library" or a group in collection tree.
On macOS, the popup of collections would never leave
and on windows, subcollection would never appear.
This is a followup to zotero#5409
that fixes this issue for all collections. The reason
why it didn't work for groups is that the command event
would fire not on a menuitem but on the <command> node itself,
which is not what the workaround expects.
Now, if we get such an event, we'll try to use the
original 'command' event dispatched on the <menuitem>
from event.sourceEvent to locate the <menupopup>
and as a blueprint for redispatching the event.
Fix createParentDialog hanging when opened during
quicksearch or in saved search. After a parent item
is added, itemTree is being refreshed. If _refreshPromise
is resolved after a timeout, it will not complete until the
modal dialog is closed. Instead, resolve _refreshPromise
after Zotero.Promise.delay, which uses the XPCOM global
timer unaffected by modals.
Fixes: #3026
Another potential fix to the test failure. Earlier fix
from 30784dd241 seems
to not have worked.
A new explanation is that the test before it does not properly
wait for the trash to refresh before trying to select the library, in
which case collectionTree select event will be suppressed
and library selection will not happen.
Fixes: #5584
This test would sometimes fail, most likely due to
the library sometimes not getting re-selected in the previous test
'should update custom header for items in the trash'.
A likely explanation is that the selection event in
collectionTree would still be suppressed when selectLibrary
is called, so make sure to wait for item deletion to
go through before trying to re-select the library.
Fixes: #5584
If the tag selector is not initialized in setScope,
initialize it before proceeding. This fixes
the tag selector being stuck in loading state
after expanding.
Fixes: #5573
- Remember diffs from test runs, show in sidebar when test is selected
- Add button to immediately update test, without a prompt
- Indicate when test has a custom defer delay set (although no tests currently do)
- Use a persistent test store as source of truth, instead of data attributes on the listbox rows
- A bit of code cleanup
Closes#5420, closes#5419
QuickCopy.init() calls Style#getCiteProc(), and a side effect of
citeproc-js locale initialization could cause a recursive call back to
QuickCopy.init(), which would again call Style#getCiteProc() (after it
had checked its cache but before it had added the new engine to it),
leading to duplicate CSL.Engine initialization. Now we correctly check
whether we've already been initialized, eliminating the race condition.
Separately, remove async keyword from init(). It never actually was
async - even before Bluebird removal, it didn't await anything or return
a promise. And we can't make it await its pre-caching work because
schemaUpdatePromise won't resolve until init() resolves/returns.
* Fixed incorrect new name generation for attachments without a file extension and not present on disk
* Fixed invalid preview in certain edge cases
* Added tests covering file renaming in general and the above issues
When an embedded attachment note is converted to
a standalone note, relate the standalone note to
the attachment and place it in the same collections
as the attachment.
When drag-dropping items into a collection in another library,
perform the addition to collection in the same transaction
as creating a new item in the target library.
When the librariesCollectionsBox refreshes on the `modify` event
when a newly created group item is linked to the selected item,
it re-loads the data of the linked item via item.loadAllData().
This could happen after the item is added to the collection
but before this change is saved. In that case, item._changed.collections
would be cleared, and when the item is saved, there would be
no changes to collections to save.
Fixes: #5539
Also, cleanup leftover unused logic of restoring linked item
from trash on drop that was removed in 2dd16b44d6
When a tab is closed via clicks on "X" button, record
and lock all tabs' widths. That way, the "X" button
of the tab that follows the one that was just deleted
will land under the mouse cursor, allowing one to
delete many tabs. In addition, preserve the scroll position
of the tab bar. If it's impossible because too many tabs
were deleted, add a spacer after the last tab.
Fixes: #5523
Post-fx140, menulist labels in chrome windows are not
announced by VoiceOver. Setting the menulist's role to 'combobox'
addresses this and gets VoiceOver to announce the labels again.
This is interesting because 'combobox' is supposedly
menulist's implicit role, so it's not clear why this helps.
It does slightly change the way VoiceOver announces
menulists: it announces "combobox" instead of "popup button",
but semantically combobox is still appropriate per
https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-onlyFixes: #5424
- Remove the delete button below the style table
- Make selected rows stand out less. They will only have an outline,
like focused rows. This should remove the impression that this is the
table where one selects the style for citations.
- Add minus icon buttons to each row to delete the style without having
to select the row. The icon of the selected row is focusable, so it
can be tabbed onto.
- improve labels and appearance of buttons to add a new style
- move "Reset Styles…" to "Cite" section and rename to "Restore Default
Styles…"
Revert change from 8e2790e2d2.
Post fx140, dragstart fires fine on rows that are not
yet selected. This allows us to remove the workaround that
limited pointer events to the rows of virtualized table (vs its cells).
With it, we no longer need most of the manual handling
of the tooltip in virtualized table - just adding title attribute is enough.
We also don't need to add extra mouse events handling on
clickable button columns as in citationDialog - we can use
proper :hover and :activate effects, as well as attach
click handler directly to the button.
On non-macOS platforms, Delete (not Backspace) on a table row
will remove all of this item's bubbles from the citation.
This is to be consistent with similar delete operation
handling in other tables.
On macOS, this is still done via Backspace as before.
Per https://github.com/zotero/zotero/pull/5518#discussion_r2310293170
We were skipping updating of `clientDateModified` in order to prevent an
attachment item upload, but that hasn't been used since Zotero 4. This
might mean that, in some rare situations (e.g., a filename that needed to
be shortened due to filesystem path length?), people in read-only or
non-files-editable groups may have ended up triggering access errors and
needing to reset local data after a file download.
Now, we properly skip marking the attachment item as unsynced.
We've always done this, but it's no longer necessary:
- If a file exists locally when a remote filename change comes in, we
now rename the local file.
- If a file doesn't exist locally and it's a single file, we save the
file with the new filename on the next file download.
- If a file doesn't exist locally and it's an old multi-file snapshot,
we now try to find the primary HTML file and rename it.
If a file was renamed remotely and a new copy wasn't uploaded for some
reason, the ZIP wouldn't contain the new filename. We already renamed a
single file within the ZIP to match the new filename, but now we also
rename a single HTML file in old multi-file snapshot ZIPs. If there are
multiple HTML files for some reason (old-style ZIP with iframes?), we
let the user fix it.
And then we can stop reuploading files after renames.
Previously, the local file wasn't renamed, so it would become unlinked.
Since we currently force reuploading/reregistering of files when they're
renamed, opening the attachment would then redownload the modified
remote file, but there's no need for the file to become unlinked in the
first place.
restoreProcessorState() breaks locale-specific punctuation (and is
deprecated).
rebuildProcessorState() is enough if we're passing an empty citation
list; if we're reinitializing with a new non-empty list, call
updateItems() first.
Also:
- Add tests for this issue and for potential regressions in disambiguation handling
When resizing the window in list mode to match the height
of the content, account for the difference between
outerHeight and innerHeight of the window on Linux.
outerHeight > innerHeight (perhaps Linux includes chrome,
borders, etc. in its outerHeight), so we have to add their difference
to the desired height for resizing. Otherwise, the visible
dialog ends up receiving only some of the desired height and
the last row is cut off.
Also, ensure that in tests, we wait for searching triggered by
switching dialog mode to finish before running the next search.
Fixes: zotero#5502
By using getSelectedItems() instead of getSelectedObjects(). The code
below all assumes that `items` contains items, and we handle general
(item or collection) options above, so this seems more correct.
And fix duplicate var.
Fix ghost relations to erased items reappearing after
sync. The dataserver ensures that related relations are
bidirectional. If a related item is erased, syncing
would first upload the update to the remaining related item
(where the relation is removed), followed by the deletions.
While handling update upload, the dataserver would return
the old, should-be-removed, "dc:relation" entry for that item
because it does not know that the other item is erased.
To handle this, swap the order of operations during sync:
upload deletion first and then upload updates after.
Then, when the dataserver handles the update of the non-erased item,
there is no related item to enforce bidirectional relations to.
Fixes: #5481
Fixes tabbing out of Scaffold's abstract box causing the test item to be
added to the user's library.
And use same format as itemPaneHeader.js in abstractBox.js.
Hopefully a better fix for #4981, which wasn't working properly on at
least some Linux systems because the variables were getting restored
before the subprocess launched. This delays a (debounced) second, to
give the subprocess time to start, and then automatically restores the
variables.
An explicit restart via app code also immediately restores the
variables, since restarts on Linux inherent the environment and don't
use our startup script. (An upgrade restart could still happen when the
variables were cleared, but you'd have to be extremely unlucky --
launching URLs or files while also performing a manual restart the same
second.)
I've run into this randomly. It only occurs if the first operation on a
Google Doc since Zotero restart is the edit bibliography dialog which
you cancel. Due to how the HTTP integration client is implemented, the
missing await causes it to try to send another response to the Connector
(which is no longer waiting on the /response endpoint), failing and
causing a forever pending promise, which makes subsequent attempts to
interact with the Google Docs plugin no-op.
We get occassional reports from users about Google Docs getting stuck
that is fixed by Zotero restart, so hopefully this will reduce those.
Resize the window to fit content on load after the
header is rendered. If the window is resized on
DOMContentLoaded, nothing is rendered yet and the
window will look cutoff on Linux.
Addresses #5453
- close hyphenationTest window.
- properly close zoteroPane window in preferences_syncTest.
after() hook inside of the test would not close the window until the
very end of the entire test run. Without this, during
subsequent tests that open their own zoteroPane window, there
are two instances of zoteroPane open at the same time.
- close open tabs in readerTest cleanup, since subsequent
tests expect library tab to be selected.
Followup to https://github.com/zotero/zotero/pull/5444#issuecomment-3141879892
- value() is used in noWait export translation
- isResolved() technically means fulfilled or rejected -- unclear if we
ever depended on that, but it might as well be correct
Previously, we simply cleared the queue, leaving any pending promises
unresolved, and we checked for isResolved()/isPending() in
storageEngine.js (and maybe other places). storageEngine.js was changed
to simply await those promises, but that results in a hang if the queue
is stopped, since the promises were never resolved. Instead, when
stopping the queue, reject queued promises with a CanceledException and
ignore those promises in storageEngine.js.
As the comment says, this hasn't been useful (and isn't used anywhere),
and I honestly don't understand how the test ever passed - process()
would've resolved all the feed item promises already.
Fixes#5412
A test using `assert.eventually` was failing after the Bluebird removal
but worked with just `await`, and since there hasn't really been much
point to Chai as Promised since the introduction of `async`/`await` ages
ago, just remove the library instead of figuring out why.
OK, the timeout removed in 05e8da wasn't actually useless, but this test
was.
41706a changed erase() so it no longer forwards skipNotifier when
deleting child items. That means that we *would* get a notify() call for
the deleted child, so the test should have been failing.
However! Because of a bug in Bluebird's filtered catch() implementation,
Promise.TimeoutError wasn't recognized as an Error subclass, and it was
instead used as a predicate function, which would always pass.
So all errors were caught as "timeouts" and suppressed, making this test
always pass. Now it no longer always passes (but does pass with the
current behavior).
This is really messy and not ideal, but these tests were written for
Bluebird Promise state inspection, and it's difficult to make them look
nice without that being available.
Restore Zotero.Promise.method() wrapper on getCodeForTranslator()
because the translation framework needs it to return a synchronously
resolved promise, unfortunately.
Necessary for non-Mozilla C:\ProgramData folder now that we can no
longer do a string replace on the official DLL
The custom component ZIPs are currently stored in S3 and referenced by
SHA-256 hashes, which get committed. This is basically the same as Git
LFS, but since we now support per-arch fetch_xulrunner and building,
this avoids the need to download large xul.dll files for other
architectures. (Git LFS files also can't ever be removed, so
particularly while we're testing this, we don't necessarily want to
permanently store every copy.)
`fetch_xulrunner`, `dir_build`, and `build.sh` now take an optional `-a`
argument on Windows and Linux to specify the architecture to
fetch/build. If `-a` is omitted for `dir_build` (as in `build_and_run`),
it defaults to the current architecture.
xulrunner hashes are now computed for each architecture. This changes
some filenames in xulrunner/, so you may want to clean out some old
folders/files.
This removes the custom handling for CI, since it will now default to
the architecture of the test runner.
nsIURI#pathQueryRef no longer includes the authority component. A URI
that would've had pathQueryRef '//authority/path' before will now have
pathQueryRef '/path'.
Reader seems to be functional after this change.
All module code runs in strict mode, and that breaks Bluebird.
And add a partial shim for the simplest and most useful methods. Not
sure if we actually want that, or if we should just replace it entirely
with native methods.
Opening a dialog window via a command from a menupopup leads to
unexpected glitches in the dialog's popups (e.g., collections menu
in newCollection dialog on macOS won't go away when you click away,
and on Windows, child collections don't appear when a parent is hovered).
This is due to some kind of race condition that arises when the dialog
appears when the popup is still closing.
This workaround captures command events from menupopups, closes the popup,
stops propagation of those events, and then dispatches a copy of
that event after a delay. This gives the popup enough time to close.
Fixes: #5408Fixes: #4891
This was a regression from the switch to `Zotero.HTTP.download()`. 302
wasn't a success code, so `HTTP.download()` would throw, and since the
status wasn't set correctly on the `XMLHttpRequest` within
`HTTP.UnexpectedStatusException`, it would think it was an interrupted
S3 connection and trigger another download after a delay.
* Add missing icon for "My Library" in the "Copy To" menu in cases where there are no other libraries in that menu
* Add icon to the "New Collection..." option in the item context menu
If the id of the collection happens to be the same
as an id of an existing item, those objects may end
up being used interchangeably in itemTree.
The trashed collection could appear in "My Library"
instead of the item, or the not-deleted item could appear
in the trash instead of the collection.
This is addressed by using .treeViewID instead of the
.id of the objects in ItemTree.refresh and ZoteroPane.itemSelected.
* Fixed an issue where the window size did not match the content
* Replaced the semi-transparent progress bar with a spinner
* Fixed a problem that allowed triggering a search while one was already in progress
* Reduced code complexity
This allows the locator dropdown to be as wide as it wants
(depending on the longest option in the current locale),
with inputs occupying the remaining space. This is
a more flexible layout that avoids broken UI in
cases if <select> dropdown tries to become too wide
while <input>s occupy a fixed space to remain aligned.
Fixes: #5350
Fixes missing tag swatches borders, a regression due to a change of the
default values for the `focus-states` mixin in #4961 (61b62d0).
Also introduces opaque tag colors so that tag swatches are not
semi-transparent.
Together with all collections and libraries.
To account for tags that may contain "," characters,
expect connector to send tags as an array. If tags
are sent as a string, it will be split by "," for
compatibility with older connector versions.
Addresses: #1706
* support adding a note from connector
- pass canUserAddNote flag in ping response to tell
the connector that it should display a note field
- when the connector sends a note, create a child note item
if it does not yet exist and set its content. If the
child item for user-added notes is already created,
update it
- if the connector sends an empty note, user-added child
note item is deleted
- record child notes added by the user in _userAddedNotes
cache to distinguish between user added notes and
notes added by the connector automatically
- if the target library changes, child note items are
moved to the new library as well. _userAddedNotes
will be updated to reflect new itemIDs
- if there are multiple toplevel items being added,
the note is added to all of them
Also fix breakage when switching between collections
from different libraries. Fixes: #5331
The predicate name used as an object key was being converted to an id,
so if relations weren't reloaded properly, they would be broken after a
save.
Discovered while working on #5297
Extend scrollToRow in windowed-list.js to allow to
scroll the specified row to the top of the window
even if it is located below the current scroll window.
With this, when scroll position is restored in itemTree,
the same row remains at the top.
Fixes: #5233
* Show "Remove All Tags..." when right-clicking textbox
* Fix pasting multiple tags adding concatenated tag
* Remove unnecessary check
* Fix tag count changing when unsaved tag is removed
On Windows, after right-clicking on a title field.
this.querySelector('popupset') matched the popupset in the
collapsible-section head, not our own.
Fixes#5294
* citation dialog: interactive before io.getFields
- added io.allCitedDataLoadedPromise, which is resolved
when both io.fieldIndexPromise and io.citationsByItemIDPromise
are resolved. Resolved io.allCitedDataLoadedPromise essentially
means that calls to io.sort() and io.getItems()
will be fast because all necessary data is already loaded.
- citation dialog uses io.allCitedDataLoadedPromise to
not await for functions relying on io.sort() and io.getItems()
before the data is loaded, as it could take an arbitrary
amoung of time. Speicifcally, SearchHandler._getCitedItems() and
CitationDataManager.sort. As soon as allCitedDataLoadedPromise
is resolved, cited items will be sorted.
This means that when retrieving fields takes a long time, one can
still add new items, their bubbles will just not immediately
be sorted.
- this replaces earlier SearchHandler.loadCitedItemsPromise, which
was a special case of this handling.
- added a new test ensuring that bubbles can be added even
when io.allCitedDataLoadedPromise is not resolved yet
- cleanup for buildCitation function to remove handling
of io.citation.sortedItems, which is always empty on
load before io.sort() runs
- added a few Zotero.debug statements for future debugging
- Uses a dummy promise if no allCitedDataLoadedPromise
(e.g. to accomodate the note editor)
- replace objects with zoteroItem, citationItem, etc.
as the main representation of an entry in the citation
(CitationDataManager.items) with instances of a new BubbleItem class.
BubbleItem has two static methods to create
a new instance: BubbleItem.fromCitationItem() used
to create entries from io.citation.citationItem on load,
and BubbleItem.fromItem used to create entries from
Zotero.Item when a new item is being added into the citation.
BubbleItem.getCitationItem will return a citationItem
in a format that citeproc consumes. This streamlines
the flow of data, makes it easier to manage and avoids
unnecessary conversions between citationItem and Zotero.Item.
- This refactoring fixes the issue of added suffix/prefix
sticking to an item and persisting even if the bubble
is removed and re-added. Fixes: #5282
- add csl data to virtual item in io.customGetItem
to remain consistent with Zotero.Integration.Citation.loadItemData.
That way, csl data is always found on Zotero.Item
and we don't have to check citationItems.
Also:
- apply deduplication to cited items, so that a cited
item from the library that appears in selected or
opened section does not have a duplicate entry in
the cited section.
- added initial test suite for citation dialog
Use `uninstall: true` in `update_url` JSON `updates` that is valid
for the installed plugin to uninstall the plugin the next time it
checks for updates
This actually checks for a "primary" column, so if an extension hides the "title" column but provides its own "primary" column, the "title" column remains hidden.
Unhandled Enter keypress from inside of a panel will now close it.
Enter on "Omit Author" checkbox or on the inactive locator
label dropdown will close the item details popup.
Enter on "Keep sources sorted" checkbox of settings
popup will close it for consistency.
Fixes: #5277
- right-click on annotation rows in trash will display
"Delete Permanently" option, instead of an empty menu
- in trash and other non-editable collection rows, disable
"Add Note from Annotations" button in itemPane header
- remove ability to add tags to annotations and other
items by dragging them into tag selector from trash
- remove ability to add items to collections by
dragging them into collection tree from trash
- fix attachment row being removed from trash if
its child annotation row is erased
Also:
- fix contextPane in reader tab being editable for item in trash
- fix title being editable in attachmentBox for item in trash
Fixes: #5261
Do not update itemTree selection on each annotation row
removal when an attachment row is collapsed.
It causes a significant delay when attachment with
many annotations is collapsed if selection is on any
row below the attachment. It is also redundant because
the selection is updated at the end of _closeContainer anyway.
Fixes: #5255
- collapse all rows before removing them. It makes
sure that annotation rows will be cleaned up properly
when an attachment row is moved into another parent.
- refactor conditionals to handle changes of parent
regardless of whether the item is a container
(attachment with annotations) or not (a note).
- added tests for itemTree's handling of changing the
parent of attachments and notes, as well as ensuring
there are no leftover annotation rows in Unfiled Items.
Fixes: zotero#5246
- Finalize renaming of pdf-reader to reader
- Remove client- prefixes from URL paths
- Update pdf-worker to use the new document-worker path in preparation for repository rename #5052
- Ensure note-editor properly uses the ZIP build instead of silently falling back to a local rebuild
- Log errors to console
- disable context menu on collapsible-sections of annotations in `itemPane`
- fix `arrowRight`/`arrowLeft` being able to expand/collapse annotation sections when they are focused
- set proper aria-label and aria-description on annotation cards
- fix misaligned annotation icons
Fixes: #5231Fixes: #5234Fixes: #5240
- Annotations are displayed in itemTree under their file attachments
on a third level. The annotation spans the entire row.
- The title is constructed on the go. When possible, it
includes annotation quote and comment as pseudo-columns
of the row. The comment occupies about twice as much space as
the quote. Otherwise, (if there is no quote) only the annotation
comment
is included as the "title" part of the row
- Non-CJK segments of the quote part of the annotation row are
italicized. CJK segments are left as is. If CJK segments are present,
there is more padding between quote and comment parts.
- Search matches the actual attachment instead of its parent file.
- Can create child notes from annotations of the same item or
standalone notes from annotations across different items from
the context menu or the header button.
- When an annotation (or multiple annotations) are selected, the
annotationItemPane component is displayed where annotations are
grouped by their top-level item. Annotations are displayed fully,
without having their content cut off.
- Special treatment for annotations to always prompt
to erase the item regardless of what collectionTree row
is selected (e.g., if a collection is selected, we
still want one to be able to delete the annotation).
This only applies if all selected items are annotations.
If multiple items are selected, some annotations and
some not, do nothing. This is until the trash is
ready. In the future, we may send annotations to trash
- strip all HTML tags from annotation for now, until the logic to
properly render annotation markup is copied over from the reader
(applies to both annotation-row component and the annotation
item rendered in the itemTree)
- Added a generalized "expandToItem" function to itemTree to
expand all ancestors of a given item, similar to "expandToCollection"
from collectionTree
- add annotation conditions to advanced search
- show [Image not available] if no annotation file for ink or image
annotations
- only keep annotation-specific context menu options when some
annotations are selected in itemTree
- enable Quick Copy of annotations from itemTree via drag-drop,
shortcut key, or Edit → Copy Annotation
- Minor refactoring of Zotero.Annotation.toJSON() to pull out async code
that handles ink and image annotations, so that
Zotero.Annotation.toJSONsync() for highlight, underline, and note
annotations does not have to be awaited. Since ink and image
annotation don't seem to work for drag-drop Quick Copy, they are just
skipped for now.
Remove redundant and wrong call to changeCollectionTreeRow() in
advancedSearch which triggers test errors sometimes, and disable prompt
button delay when running tests everywhere, not just CI
- when something is dragged over a collapsed collection
or group, it will be expanded to reveal children
so one could drop the dragged item/collection/ into them
- when drag is over, the expanded collections will
collapse, except for the ancestors of the newly selected
collection
- trigger collapse of expanded collections on dragend
on the entire document (in case dragend happens not over
the collection tree), or from onDrop handler, from where
we will wait for selection to change before collapsing
rows
Fixes: #4952
This is caused by the persisted splitter state value to be blank from either older versions of client or plugins.
Resetting the empty values fixes this bug.
Z7 removed an `OS.File.open()` in `_checkForUpdatedFiles()` that would
throw on missing files and cause them to be marked for download in a
`catch`.
This likely caused the fix for #1753 not to work in Z7.
Do not switch to library tab while changing target
collection in connector.
Deprecate inLibraryRoot as an independent boolean
param in ZoteroPane.selectItems to instead have
a composite options param { inLibraryRoot, noTabSwitch }
Fixes: #4262
* citation dlg: cleaner search value
Additional cleanup of the search value to not include
some punctuation ("(),.;:"), localized "and",
ampersand, and localized "et al". That way, if one
copies a citation like "(Smith et al., 2020)" and pastes it into the
citation dialog, it is more likely that appropriate search
results will appear.
Fixes: #1864
* handle citations with locators
Clean the input before fetching the locator so that
if one copies and pastes (Smith et al., 2025, p. 15),
the locator is still correctly extracted.
This fixes the breakage in tabs menu on initial load
if one of the tab is for an item from not-loaded group.
Now, every component relying on tab items (e.g. tabs menu)
can just assume that items are all loaded.
Fixes: #5192
On initial load, the grippy does not immediately
appear because it needs to be loaded. This means that
when itemBox is rendered for the first time, the grippy
has a width of 0, which later becomes 18px. If the
itemPane is narrow, it may push the columns out
and lead to text of value components wrapping
without the rows getting stretched.
This sets explicit width to the grippy to avoid such behavior.
Fixes: zotero#5166
- removed quickFormat, insertNoteDialog, and addCitationDialog
.xhtml and .js files, since all of them are now replaced
with new citationDialog
- removed quickFormat and insertNote strings from
en-US localization files
- replaced all mentions of quickFormat in comments and
variables of integration.js and editorInstance.js with
citationDialog
- removed big-sur-specific styling for quickFormat
Closes#5046
Do not lowercase the entire title before capitalizing
the first letter of each word to preserve abbreviations
or spelling of unusual words (e.g. 3D or iPhone).
Fixes: #5165
Also:
- Introduce a mixin for a toolbar button that appears on hover or focus
- Unifies the border radius of such buttons, which was previously different for the "Libraries and Collections" toolbar button.
- last-closed -> last-used for citationDialogMode pref value
- pref label citationDialogLastClosedMode -> citationDialogLastUsedMode
- update Zotero.Prefs version. While migrating, if
integration.useClassicAddCitationDialog is true, set
integration.citationDialogLastUsedMode to 'library'
so the citation dialog opens in library mode on the first run.
If citationDialogLastClosedMode pref exists, it is
migrated to citationDialogLastUsedMode and cleared
- redone logic of extracting the locator to allow for better
detection of locators and to not rely on long regular
expressions that are hard to troubleshoot. Added tests for it.
These are the new rules for extracting locators:
- locator value can be either a set of numbers potentially
with some punctuation in between OR text surrounded with
single or double quotes
- locator labels can be typed in any of 3 formats:
full (chapter), short (chap.), or short with no
punctuation (chap)
- locator labels go before the locator values and there
may or may not be a space in between. For example,
chap.10 is the same as chap. 10.
Fixes: #5092Fixes: #5093
- get opened items asynchronously and load their data
in case if there are items opened across multiple
libraries, and some of them are unloaded.
This fixes#5161, which makes the dialog unresponsive
upon initial load.
- enfore io.filterLibraryIDs in library mode, the same
way it already is being applies in list mode. This
is currently used to only show items from the current
library when adding citations in a note.
- account for the change above in collectionTree to
by focusing the first library from io.filterLibraryIDs,
if those are present, and only default to userLibraryID
if io.filterLibraryIDs is empty.
Keep the placeholder node to retain the positioning
of the text consistent but remove the border
so the item card is invisible.
Also: remove spinner
Fixes: #5151
- pass ref to itemsView.changeCollectionTreeRow
to avoid errors thrown when typing numbers (related to
tags shortcuts)
- return string equivalents of true/false in getExtraField.
virtualized-table expects a string in its typing handler.
Previously returned boolean was causing an error.
Remove refocusing of the input on Enter from item details
popup - just let focus go back to the bubble.
Input refocusing was initially added with the idea that once
you make some edits and confirm them with Enter,
you are ready to go back to typing in the search input.
But then, Enter on Done or Cancel is treated as equivalent
to click, so the focus goes back to the bubble. When
the dialog is cancelled via Escape, focus lands
on the bubble as well, in case you need to keep going to
locate the another item to customize.
This different focus behavior on Enter is indeed a bit
inconsistent and likely no longer needed, now that
you can always refocus input with Cmd/Ctrl+F.
https://forums.zotero.org/discussion/122823/some-advice-for-citation-dialog-in-beta
Before making the dialog interactable.
Initialize loading cited items via io.getItems when the dialog appears.
Once they are loaded, itemsList is refreshed to include
cited matching items and all subsequent searches will include
them too. During the refresh immediately after cited items load,
try to preserve focused/selected state of items.
Added IOManager.preInit that is ran as soon as the dialog
is loaded. Move accept/cancel initialization there.
IOManager.init runs at the very end of onLoad because
it relies on most data and layouts being loaded,
It means if there is some delay or error during loading,
the dialog cannot be closed. To avoid it, setup
cancel/accept buttons separately in the beginning.
Also, minor refactoring of SearchHandler to handle
refreshing of cited items separately from selected and
open items.
Fixes: #5121
- add dropmarker to bubbles after the text to indicate
that bubbles are clickable
- remove X remove button that was there before
- no more mask over the bubble on hover, since dropmarker
is just always visible
- 4px distance between the dropmarker and bubble text
Fixes: #2712
In library mode, apply highlighting to bubbles that
correspond to selected items in itemTree only when
the items table is focused. When the focus leaves,
highlighting is removed from bubbles.
Fixes: #5138
Add "Restart in Troubleshooting Mode" checkbox, checked by default, and
remove "Restart Later" option, since it's not possible to restart later
in Troubleshooting Mode. I doubt anyone used that, and if they really
need it, they can use -ZoteroDebug at the command line or toggle the
`debug.store` pref manually.
Closes#4580
- restore earlier behavior of arrowDown on a bubble
opening itemDetails popup
- remove arrowUp closing the popup
- remove arrow navigation between rows of bubbles on arrowUp/down
- remove no longer needed edge case of arrowDown
handling on the last row of bubbles
Fixes: #5118
This resolves an issue where, after the selected theme was deleted, it was impossible to select a newly added custom theme. This happened because the preference key for the selected theme uses consecutive IDs and wasn't cleared when the theme was deleted. As a result, the newly created theme would receive exactly the same ID. When the newly created theme was selected, the preference observer did not trigger (because the ID did not change), leading to this bug.
The fix here is to clear the theme preference when it is deleted. The added benefit is that when this happens in dark mode, the dark theme is auto-picked instead of falling back to "Original".
We switched file downloads to use XHR to fix downloads failing via
authenticated proxies in fx115+, but that made them subject to our
default 30-second timeout in `Zotero.HTTP.request()`. That timeout uses
the XHR `timeout` property, which applies to the whole request, even if
data is actively being downloaded. As a result, file downloads would
fail for people downloading large files or on very slow connections.
This implements manual connect and inactivity timeouts when using
`Zotero.HTTP.download()`. Currently, these both use `options.timeout`,
or the default 30 seconds, but we could probably take separate options
and have lower defaults now that they no longer apply to the whole
request.
Fix issue where clicking accept button would not work
when the dialog itself is unfocused. Add a small delay in the
focus handler of the dialog to allow accept button clicks
to be handled before rerunning the search.
Fixes: #5110
While filtering selected/open/cited items, include
items that match EVERY word from the search string,
as opposed to items that match ANY of the searched words.
Fixes: #5099
In library mode when there are no selected/open/cited items,
Enter keypress from input will add the first row of itemTree
into the citation only if it is the only row.
Fixes: #5098
This is done on purpose. We distinguish between tooltips that are
command/feature titles and those that are action descriptions. So we
have "New Note" but "List all tabs". Apple does the same -- e.g., "Edit
Tags" and "Perform tasks with the selected items" next to each other in
Finder.
This reverts commit 011c5bd6c0.
IOManager relies on everything being loaded, so it
should be initialized last. Otherwise, any IO handler
trying to access currentLayout would throw an error.
Also, swap the order of operations in bubble-input to
set input width only after the first input is added.
Otherwise, first input will have an arbitrary width
during initial load.
https://forums.zotero.org/discussion/122554/beta-new-insertion-bar-problem-with-citations-but-not-notes
Implemented redesigned citation dialog with library
and list modes one can switch between. This dialog is
a direct replacement of quickFormat and classic citation
dialogs.
In Library Mode:
- items table has a new + button column to add items from the
citation and rows of selected items are highlighted
- open, cited and selected items appear in a section
between the items table and bubbleInput. When there are no
matches, a message is shown.
- only top-level items are shown when citing
items and only notes/notes' parents - when adding a note
- selected items are gathered in a collapsible deck to save space.
Click on the deck will expand it. All selected items can be
added via "Add all" button.
- when an item is added, bubble-input may increase in height and
push itemTree lower. To try to preserve relative positioning
of the mouse, itemTree will scroll to be over the row that was just clicked
In List mode:
- arrow up/down from the input will change the selected item
with the focus remaining in the input
- selected items are a collapsible list section
Other behaviors and fixes:
- one can add any locator (not just pages) by typing its full or short name
in an input and pressing Enter (e.g. line 10, or l. 10, or chap. "test chapter")
- Added a new preference to select if the citation dialog
should always open in list mode, library mode or in the last
mode that was used
- bubbles whose items are selected in library or list mode
are highlighted
- arrow up/down from a bubble will focus the bubble
above/below it for easier navigation across bubble-input
- multi-select is supported on items in list mode or item cards in
library mode via Shift-arrow or Cmd+click to select multiple items
- Cmd/Ctrl + Enter will always accept the dialog no matter what
is focused
- when there are no bubbles, accept button is disabled
- after an item is added, bubble-input is always refocused
- added suppressed property to itemTreeMenubar to be able to
hide if in list mode, where it is not relevant. Fixed menubar
getting stuck or re-appearing on Alt keypress on Linux by setting
height: 0 vs hiding it via hidden.
- added initialFolder and onActivate prop to collectionTree to set which collection
should be reopened when dialog opens and to be able to set a custom
onActivate handler.
- added getExtraField prop to itemTree to get data on if
an item is in a citation or not
Implementation details:
- citationDialog.js is the main file. It relies on a number of
helper files in citationDialog/* directory to keep the main
file less cluttered. popupHandler.js contains the logic of
opening/closing the item details popup to add locator/prefix/suffix/etc.
keyboardHandler.js is responsible for overall keyboard navigation
throughout the dialog. searchHandler.js contains the logic
for running the search based on user's query. Finally, Helpers.js
has general helper functions that don't handle any actual logic.
- SearchHandler is set to run search in two ways: for cited/selected/open
items (which are cached and do not involve any actual SQL query) and
general search for items across all libraries. When layout.search
runs, firstly cited/selected/open items are updated after which the
SQL search runs.
- bubbleInput.js is a customElement responsible for bubbles interface.
bubbleInput.refresh takes a list of items and handles adding/removing/reordering
of bubbles as needed. It always has two inputs on each side of
a bubble, as opposed to having inputs inserted dynamically. It
allows keyboardHandler.js to handle navigation with arrows within
bubbleInput.
- when user interacts with bubbleInput, it emits custom events that are
handled by IOManager singleton in citationDialog.js, which
may update the items information and pass them back to bubbleInput.refresh
to have the list of bubbles updated
- CitationDataManager singleton is responsible for storing
items added into the citation in CitationDataManager.items
in an object with both Zotero.Item and the actual citation item.
It is easier to pass both items to other components
and helpers, as opposed to sharing functions to convert
items back and forth.
No idea why this was ever here, but I don't think it's actually used
(and if it is, it shouldn't be). The only options I see are handled
directly within `executeTransaction()` itself.
When an item is erased (removed from the trash or cleaned up from a
feed), we set a flag to purge values in `itemDataValues` on the next
startup, with this query:
DELETE FROM itemDataValues WHERE valueID NOT IN (SELECT valueID FROM itemData);
For some people, that query was incredibly slow and would result in
Zotero intermittently hanging on "Loading items…" for a long time at
startup. It's possible this is mostly limited to people who subscribe to
high-volume feeds and have a lot of item churn. One affected person had
>900K values in `itemDataValues` despite having only 20K items.
It turns out the slow query is due to the foreign-key constraint on
`itemData(valueID)` that references`itemDataValues(valueID)`. SQLite is
checking every row being deleted from `itemDataValues` against
`itemData`, even though the query is specifically removing rows that
don't exist in `itemData`! For the 900K-value DB, disabling foreign-key
checks causes the `DELETE` query to take 25 seconds instead of...some
much longer time that I didn't wait for.
We already had an `executeTransaction()` flag, `disableForeignKeys`, to
temporarily disable foreign-key checks, but it didn't do so in a way
that was safe for post-initialization usage -- a write query outside of
a transaction could've run between the transaction commit and
foreign-key checks being re-enabled. This commit changes it to properly
block all other queries unless they include an `ignoreDBLock` option,
meaning that queries within the function passed to the transaction need
to include that option. (And since that's not realistic for the couple
other uses of `disableForeignKeys` -- one for a test and one in code
that almost certainly hasn't been run by anyone in 15 years -- those now
just run `PRAGMA foreign_keys=OFF|ON` explicitly, leaving this as the
only current use.)
Adds a new function, Zotero.HTTP.download(), that uses
Zotero.HTTP.request(). This fixes downloads via authenticated proxies in
Zotero 7 and gives us other request() functionality (e.g., 5xx retrying)
for free.
The downside is that this is probably less efficient, potentially
loading large downloads in memory. We should create a replacement for
request() based on fetch() that supports getting the body as a
ReadableStream.
Fixes#5062
A browser window was no longer actually needed for charset detection
(on macOS, at least, and hopefully elsewhere) because we switched to a
HiddenFrame-based hidden browser. Remaining uses now call
`loadZoteroWindow()`.
After navigating to the collection from "Libraries and Collections"
section of an item in the trash. Deleted item does not appear in actual
collections and trying to select it will just re-select the trash.
Fixes: #5060
Define missing cancel method on io object passed from the
editor instance to quickFormat dialog. Without this
Escape keypresses throws an error because io.cancel is
undefined.
Followup to #4859
Asks the user if they want to discard the changes in their existing dialog if
they have made changes.
If there is no citation dialog, but a command is running (like updating the document)
an alert is displayed.
Closes#4855
I don't know if something changed in my Ubuntu installation or in the
Firefox runtime (probably the former), but that code crashes Zotero on
wayland.
Window activation (e.g. citation window) does not seem to be affected.
* Display the progress bar on macOS integration commands more
Avoid displaying before Add/Edit Citations, Add Note and Doc Prefs
since that brings Zotero window to the foreground along with the
relevant dialog, but display the progress bar after confirming those
dialogs, which will bring Zotero to the front, but is still better than
no progress indication.
Display the progress bar for Refresh, Doc Prefs, Unlink and Add/Edit
Bibliography
* Fix progress bar indeterminate state not being visible and not working.
- Update _progressMeter.scss for fx115+
- Some adjustments/resizing to progress bar display in quick format
`firstInSession` wasn't being properly set, so previously failed data
objects weren't being retried until a manual sync. Local files also
weren't being scanned for changes for three hours, but that's probably
better in the age of DB-stored annotations, so leave that off for now
and address properly in #5025.
After further consideration, doing this in-app makes more sense. This
way, we'd never be able to update the desktop file automatically (e.g.,
to add a new MimeType), and removing the template zotero.desktop file
would break all existing installs using our symlink instructions.
In-app, we can update the existing desktop file on restart after an
upgrade, and we can remove the existing symlink.
This reverts commit 0ac57f8262.
Possible we'll get complaints about no longer being able to open
multiple copies of at least runJS and csledit, and we can reconsider if
so, but reopening the existing window is certainly the intended
behavior.
Reverts a change I made to the PR. Only `since=0` would be valid in the
web API, but local items don't have a version by default, so it has to
be `-1` to match those. This probably isn't a great solution, since tools
targeting the web API would use `0`, but this will fix the test for now.
- Fix background color on post-upgrade banner
- Add drop shadow between stacked banners (e.g., post-upgrade banner and
Mac Word banner, which will generally appear together on an upgrade
and have the same background color)
- Align right edge of buttons in post-upgrade and Mac Word
- Unify other spacing and font sizes
- Move 16px padding into the tabbox, which lets us eliminate the
negative margin on #right-pane and the 5px padding to make room for
the focus ring
- Remove width: 100% from the textarea - not necessary and causes
horizontal overflow on macOS
Tested on macOS and Windows.
Treat page as immediately ready after readyState == 'complete', or give
it 100ms after 'interactive'. This is rough, but 'interactive' isn't
always fully ready and 'complete' sometimes never happens.
And enable migration of Extra rows to valid fields in non-strict mode,
but since that's only used by translators now, that probably won't do
anything
Fixes#3422
Fix error if local note or attachment is added to a parent item while
the remote version was added to a collection. Now, the new local parent
item will be added to any collections the remote child item was added
to.
Fixes#2934
- Remove duplicate/missorted locales
- Treat empty files in langpacks as missing and use en-US
- Skip empty lines and comments when using en-US file for empty
non-en-US files, in line with Mozilla langpack files
searchButton setter in search-textbox element adds aria-autocomplete
to the input. It makes screen readers announce that there is an autocomplete
popup, which is misleading (even though it is appropriate for
firefox that actually does have autocomplete-like dropdown).
This overrides the searchButton setter to remove aria-autocomplete
attribute.
Addresses: zotero#4932
Do not close details panel on arrowUp when locator
type menulist is focused to allow for default handling
(e.g. select the next/previous option on windows).
Fixes: #4953
A new function, `Zotero.DB.backUpDatabase()`, by default makes offline
backups, by closing the database, doing a regular file copy, and
reopening the database. It takes an options object with an `online`
flag to make online, incremental backups that can take multiple minutes
to complete, though for now we're still running them on idle.
The old function, `Zotero.DB.backupDatabase(suffix, force)`, is
deprecated and proxies to the new function, making offline backups.
Fixes#4935
When tagsBox item is changed by clicking on an itemTree row,
the blur even never fires on the currently focused tag. So
whatever changes were made will be discarded. To avoid it,
blur any opened tag rows (which triggers a saveTx)
when an item is being set, same way it is done in itemBox.
Also, a small tweak to properly fetch the focused tag
via editable-text:focus-within selector, since editable-text:focus is
always empty because the focus is on the input inside of
editable-text.
Fixes: #4942
For a URL like
`https://ezproxy.school.edu/login?url=http://resolver.ebscohost.com/openurl`,
without a trailing `?`, we were just adding a `%` to the end, making the
URL invalid after the redirection (`/openurl&url_ver=Z39.88-2004`
instead of `/openurl?url_ver=Z39.88-2004`).
(And apparently no one who sent in these URLs actually tested them
in-app?)
Followup to: #4924
Addresses: #4922
Pre-fx128, --toolbar-field-border-color was ThreeDShadow,
while right now it is --input-border-color, which looks
more prominent.
Setting border color explicitly for ThreeDShadow now would make
the border look a bit blurry, while transparent border looks closest
to pre-fx128 look.
Caching Basic Auth credentials with an OPTIONS on /zotero no longer
seems to cause Firefox to pass an Authorization header for
/zotero/AAAAAAAA.prop, so update test to send 401 for the .prop request.
(Presumably /zotero/BBBBBBBB.prop would at least still get the Authorization
header without a 401, but I didn't test that.)
To avoid redownloading from Mozilla whenever fetch_xulrunner runs. We
might want to add cleanup of old versions at some point.
We should add this for Windows and Linux too.
And:
- Use a different port for tests so server tests don't break when Zotero
is already running
- Remove no-op httpServer.enabled pref updates in tests
- It's already enabled, no test disables it, and any test that did
would clean up after itself
- Updating that pref has no effect without a separate call to
Zotero.Server.init()
- Remove unused arg to Zotero.Server.init()
- Cramped tabs due to insufficient padding (new in fx128)
- Off-center text due to margins (our styles)
- Off-center text due to padding-top (platform styles)
Something funky is going on with Content-Disposition: attachment on
displayable content in fx128. It's correctly blocking the download, but
we're no longer getting a STATE_STOP notification when that happens.
The HTML test didn't really test anything different from the
octet-stream one, so we can just remove it.
Fixes#4881
Mac Firefox builds now specify the update channel via a custom macOS
framework:
https://bugzilla.mozilla.org/show_bug.cgi?id=1799332
We set the update channel in the build script, so bundle a custom
ChannelPrefs framework set to "source" and then patch the binary as
necessary for non-source channels. What could go wrong?
- Make the annotations importer more resilient to missing data
- Fix notebooks not being imported in some cases
- Fix an instance of a hidden browser leaked by the Mendeley Importer
We fix `://` or `//` automatically after #3483, but a leading ':'
character would still show an internal `NS_ERROR_MALFORMED_URI` error.
Instead, just say "[url] is not a valid WebDAV URL".
Some pages seemingly never become "complete". "interactive" (which we
already use in TranslationChild) is equivalent to DOMContentLoaded, and
that's enough for everything PageDataChild needs to do right now.
This should make Scaffold test runs more reliable. TranslatorTester uses
{ requireSuccessfulStatus: true }, which calls the PageData actor, and
that was occasionally flaking and causing the test to time out.
Fixes#4869
- shift-tab from a note row focuses section header's twisty
- tab from reader into context pane will focus search
field. Previously focused note row will be focused on tab
from a section header's twisty
- fix focus not leaving context notes pane if all
sections are collapsed
- shift-tab from sidenav will focus the last node
in the contextPane, instead of search input. That is to
have consistent tab order in each direction.
Fixes: #4858
If a child item is restored from the trash and its parent row
exists and is open, collapse the parent row and expand it
to re-render child items.
Fixes: #4791
- remember the last focused note-row in the contextPane
and, if possible, refocus it on shift-tab from sidenav, on
tab from the reader into notes pane, or on tab into the
notes list from the collapsible-section. Otherwise, focus the search input.
Per: https://github.com/zotero/zotero/pull/4837#issuecomment-2478108186
- when a note is opened in the context pane, let shift-tab
from sidenav place focus on the links-box. That way,
it is not skipped during tab navigation.
- fix focus not entering note-editor on Tab from the "Go back"
button and instead wrapping around to the current tab
For APIs that have lifecycle control, e.g. item
pane section, the `update` is passed in the init
hook.
For APIs without lifecycle, we can't pass a value.
Instead, we provide a method to refresh in the API
instance.
- fix the issue of focus not landing on search field of the notes
pane when it is opened on tab from the reader
- shift-tab from the sidenav when notes pane is opened will focus
the search input as well, instead of the last focusable
node in the pane
- tab from a note-row in the list of notes will focus the sidenav
- shift-tab from a note-row will focus the header of the collapsible-section
of the row
Per https://github.com/zotero/zotero/pull/4235#issuecomment-2472397642
Fix the glitch where having anyField search condition
along with any other condition would return an empty
result set if joinMode="any".
This would happen due to a conflict between conditions
caused by wrapping "anyField" condition set into a quickSearch
block, which enforces "AND" operator between conditions.
Fixes: #4830
This may help with the issue of locator popup sometimes
not being properly rendered.
Also, it allows the popup to have a proper width on
windows when "Always show scrollbar" setting is on.
Addresses: #4814
Addresses: #4805
And:
- Rework a few ZP functions to take items arrays instead of always
acting on selected items
- Move string to Fluent
- Build entire Add to Collection menu programmatically
- Section buttons, Locate, and Notes in the sidenav are focusable
- itemPane section buttons are combined into one focusable
group. Those buttons by themselves don't mean anything in
the context of keyboard navigation as they just scroll
to the section in the itemPane. In fact, having info,
abstract, attachments, etc. focusable and announceable by
screen readers is just confusing. However, we do want the group
of those buttons to be focusable to switch back to
zotero-context-pane-item-deck from zotero-context-pane-notes-deck
if the notes button in the sidenav is pressed.
- sidenav can be reached by tabbing into it from the end of itemPane
or via shift-tab from the focused tab
- sidenav buttons can be navigated with up/down arrows
- notes list in the context pane can be activated via the button in the
sidenav and navigated via up/down arrows
- use command vs onclick listener for notes list context menus to work
with keyboard-triggered clicks as well
- focus itemPane when tabs are switched in sidenav
Do not try to re-select a previously selected item
if it was filtered out from itemTree, e.g. after
it was removed from currently selected collection.
Otherwise, quick search gets cleared in an attempt to
re-select the item.
Fixes: #4616
Unify plugin API classes
Add info box custom row API tests
Refactor itemBox.js create element
Wrap hooks in API for safe call
Add test for item tree api and hook error handling
Remove try/catch from #4816
Move plugin API definitions to xpcom/pluginAPI
If you unlinked your sync account and then relinked, the ZFS storage
controller would still have the old API key and file-sync requests would
fail. Since that was never supposed to be possible, it resulted in
"Group with libraryID 1 does not exist" errors during file syncing until
you restarted the app.
Move handling of tabs' renaming when an item is
modified from Reader into Zotero_Tabs, so that the titles of
unloaded tabs can get properly renamed.
Have the Reader and Zotero_Tabs share Zotero.Item#getTabTitle()
to handle title updates in both tabs and standalone reader windows.
Fixes: #4646
When items are selected, itemTree scrolls to them. Depending on the
arrangement of items, it will often default to scrolling to
the top-most item in the selection, which is not the focused row
in case of duplicates view. To scroll to focused row without
unnecessary scrolling or items visibly jumping to one row and then
another, added option to skip scrolling in itemTree.selectItems.
Only move focus out of the reader iframe on tab/shift-tab
if the reader instance is opened within a tab, meaning the
sync button and the contextPane exist. If the reader
is opened in a standalone window, do nothing.
Fixes: #4823
LightweightThemeConsumer interacts with the color scheme,
so disabling it prevents the color scheme from changing
whenever zoteroPrint runs.
Fixes: #4782
This seems to be a Firefox bug that exists even in Firefox 132.0a1 (2024-09-29).
Note that right-clicking to open the context menu on a contenteditable element also triggers an update to the Edit menu.
The fix updates all Edit menu options except Redo, which cannot be enabled due to ProseMirror modifying the contenteditable state after each change.
Partial fix for #3398
I think this was only in Everything for historical reasons -- in the
initial implementation of search modes, tags, notes, and (old)
annotations were all in Everything, but tags and notes were later moved
to All Fields & Tags. The old `annotation` condition wasn't moved, and
when I added `annotationComment` and `annotationText`, I just left them
in the same place. But if we match notes in All Fields & Tags, we should
match annotation comments, and if we're matching comments, we might as
well match annotation text as well.
https://forums.zotero.org/discussion/119123/note-annotations-zotero-search-and-zotero-pdf-viewer-search
Selecting custom data directory radio button in advanced
settings opens up a modal to select the directory. It is
not a typical behavior for radio button, so it conflicts
with 3.2.2 success criteria.
This is a rework of the data directory setting interface to be
similar to LABD. It does not have radio buttons and the
directory explorer pops up on button click.
- adds View > Columns and View > Sort By menu options
to all windows that contains an itemTree (Select Items dialog,
Advanced Search, classic Add Citation, Edit Bibliography
dialog).
- the menubar is global on macOS. On Windows/Linux it is
displayed and focused on Alt keypress
- added menu option to move a selected column left,
which we need as an alternative for drag-drop reordering
that would not require using a mouse
* Tweak the script to use `en-us` `.ftl` files as the source of truth.
* Update `ftl-tx` to a version that can handle referencing terms with arguments.
- made the wrapper of the dialog focusable and added aria-description
so that when the dialog opens it announces how to use the dialog
- added aria-labels to buttons, references list, and editor
- make sure that the references list always selects an item when
focused without clicking
- added a border to focused buttons from simple editor to make it visually
clear when they received focus
More specific labels for the button to turn selected item(s)
into standalone:
- "Convert to Standalone Sttachment(s)" when only attachments
are selected
- "Convert to Standalone Note(s)" when only notes are selected
- "Convert to Standalone" otherwise (e.g. if a note and an attachment
are selected together)
Fixes: #4766
Also:
* Remove unused element in select items window
* Move and rename zotero/ingester/selectitems to scaffold/select to
avoid confusion with selectItemsDialog
Filter out ASCII control characters in renderItemTitle, since
an error is thrown if those characters are present when
the innerHTML of a tab name in tab bar is set via
dangerouslySetInnerHTML.
Fixes: #4758
---------
Co-authored-by: Dan Stillman <dstillman@zotero.org>
Set focusring on the child hbox, instead of the actual tab.
The hbox is the element with visibly rounded corners,
so this way the focus-ring will have the right shape.
Fixes: #4744
They cause problems with our import script (I think because they're
processed first, so the non-term placeholder isn't yet available) and
aren't really necessary.
- Added menuitems to move collections within the same
library and to copy collections
- "Move to" only displays collections within the
current library
- "Copy to" displays all libraries, if more than
one library exists. If there is only one library,
top-level collections from "My Library" are displayed.
- while copying within the same library, create copies
of all collections and add items into them, without
actually duplicating items
- while copying between different libraries, items
will be duplicated, the same way it is done when
collections are dragged and dropped in another library
Remove logic constructing a11y announcements from reader.ftl.
Instead, reader will fetch and put together components as needed on the fly.
Follow-up to cde21ac9f2
per https://github.com/zotero/zotero/pull/4752#discussion_r1800481917
Also, add { general-key-command } and { general-key-alt } and add a reader
string that uses those on macOS and non-macOS for textual annotation
instructions. On macOS, the end of highlight and underline annotations is
resized via Shift+Command+arrows, which is different from the Option
modifier used in all other instances
(per https://github.com/zotero/reader/pull/138#issuecomment-2373775068)
Finally, tweak aria-description of "Find in Document" as it sounded
as if Control+Option/Alt+1 would create either highlight or underline
annotation.
Do not move focus from the tab onto the editor/input
during keyboard navigation to not change context per
https://www.w3.org/WAI/WCAG21/Understanding/on-input.
Focus will still shift if tab selection changed on mouse click.
Also:
- added focus ring to tabs. Additional mouseup handling
to prevent the focus ring from briefly appearing on click.
- on Escape from within the editor, focus the current
tab.
- on shift-tab from the beginning of the editor,
tab out of the editor to previous element.
- added "Change Parent Item…" context menu option to
itemTree as a non-drag-drop alternative to changing the
parent of attachments or notes
- context menu option appears only when all selected
items are notes or attachments
- upon activation, a dialog to select the new parent
will appear. Added a tweak to the dialog to disable
the "accept" button if a selected item is not top level
- minor edit to itemTree to set the multiselect property
based on a prop, since we do not want to have multiselect
enabled in this instance
- hide all virtual collections in selectItemsDialog via new
io.hideCollections parameter
- "Convert to Standalone Attachment" button in dialog if
child attachment is selected
And:
- Use eslint instead of teslint
- Remove debug logging, since we're running this constantly now
- Kill ESLint at the end, although it should exit on its own
Includes API changes in Zotero.Translators in order to support
in-memory translator serialization.
Type changes in docstrings are corrections, not actual changes.
Apparently _error() does get called!
detect* methods were already logging syntax errors as expected.
TranslationChild handles errors in detect* and do* identically. Odd.
Minimize, maximize, close, etc. window buttons are generally
not accessible for focus. They can get focused on mac with
voiceover enabled which can be confusing.
Added aria-hidden attribute to prevent voiceover from
placing the cursor on them.
- linked all labels to their respective inputs so they are announced by
screen readers
- added aria-labels for the actual editor and preview iframes
- locator type and locator input are assigned aria-labels
- added visible labels to citation style and language menus,
since vpat 40 specifically asks for visible labels
- shift enter on a creator row will add a new empty creator
row after focused row. If shift-Enter is on the last creator
before "More creators", all creators will be rendered.
- fixed encountered glitch where switching the mode of
creator would not always update the pref, so next time
a new creator row is added, it would not be of correct
mode.
- fixed encountered bug where a newly added creator row
could receive an index of an existing creator that is not rendered
( "More creators" label is displayed). In that case,
saving such creator would override an existing creator
and erasing that creator would remove invisible creators.
- added a few tests for these edge cases
- on shift-enter in unsaved creator row that only
has whitespace, do nothing, the focus will stay
in that row. It will be discarded if the focus leaves
it with nothing but whitespace.
- fix focus getting lost on shift-enter on an edited
creator field after adding just some whitespace.
Fixes: #4393Fixes: #4710
- Use real buttons and other tweaks from design
- Use existing .properties string for "Don't Ask Again" for now
- Remove close button
- Update submodule for reminder after 24 hours instead of 7
Tweak to Zotero.Utilities.Internal.activate to make
it more likely that qf receives focus. Addresses
the issue of qf not becoming focusable after the
first opening in word and makes it clearer when the
dialog appears in google docs when the dialog cannot
be pulled to the foreground.
getAttention() will flash the icon in the app bar
if the window could not be pulled into foreground,
for example with google docs.
focus() will try to bring the window to the foreground,
which fixes the issue of qf not getting focused
with word on the first opening. Also, without it,
a call to getAttention() interferes with focus landing
on the dialog during subsequent opening in gdocs.
* vpat: add more aria-labels to preferences btns
Per the VPAT review. "Add"/"Remove" instead of +/- as those
are not descriptive enough
* vpat: wrap radiogroup in a groupbox with label
That is so that the "Color scheme" label is not announced
as a description, which requires a specific setting
enabled in JAWS.
* vpat: link itemPaneHeader prefs labels to menus
- Replace flex attribute with flex-shrink and flex-grow style
to allow elements to be resized by splitter and window resizing.
- Set the max-width on collections pane to prevent it from pushing
other panes outside window border
- Set the min-width on window according to the size of both library
and reader tabs
- Use stacked layout when window width is small
fix: #4669
We display "Loading Cited items" while fetching fields as an
additional header before "My Library". Navigating the item list before
this change while the double-headers were displayed did not work with
the keyboard
In a new scaffold.ftl file.
Vpat 43 also indicates that the read-only state of the text field
does not get announced - it is a VoiceOver-specific issue on
Firefox, so it is not addressed here as it should be fixed
globally.
resolve: #4611
Try to avoid race condition in dense render calls.
We used to notify the preview to update
after all other renders are done,
which can cause unexpected race condition.
- explicitly add aria-labels to groupbox and vbox
sections of the settings. It's required for voiceover
to announce that label when focus enters the group.
It's not needed for NVDA and JAWS as they recognize
most h2 labels as labels of grouppings.
- add all relevant additionl text for the section
as aria-description/aria-describedby so it is
announced after the section's name when focus enters it
- add role="group" to vbox sections, since vbox
does not have any semantics
- skip tabstop on scrollable "#prefs-content" container
since it is not required
- vpat_7: When a popup is showing, mark menuseparators with
role="presentation" to clear whatever semantics a separator
has. It prevents the screen readers from counting it as
an interactable element while announcing the index of
a menuitem within the popup. It also makes the screen readers
count items across all sections, instead of stopping at
the first separator.
- vpat_14: When menupopup is hiding, for a moment, mark it
as aria-hidden. It forces VoiceOver to move cursor back to
the previously focused element. Otherwise, it is not aware that
the popup went away, the cursor gets stuck inside of the
invisible menu, no elements will be announced until the screen
readers is reloaded.
* qf: remove transparent window background from win
Because it seems to still sometimes cause the qf window
to disappear. I could not reproduce it but disabling
background: transparent did help with the issue of qf
disappearing in the past. It allows us to remove very
specific chromemargins which, apparently, do not work
in all cases.
Removed border-radius from qf on windows because now that
the window is not transparent, its corners would be visible.
But the window itself has rounded corners so it doesn't look
that different.
The main drawback now is that a secondary window without titlebar
on win becomes resizable, and having background: transparent was
a workaround to disable that. So now, we set max width and height
so that the window cannot be stretched but, unfortunately, the
resize cursor appears when you hover over the edge of the dialog.
* qf: minor style fixes
- hardcode light theme value for accept button and the
spinner so they don't disappear when the theme is dark
- make sure the item's color from reference panel is not
overriden by a lighter one from another stylesheet
And add a new beforesynctopreference event to replace the old behavior.
You almost always want the preference to be updated before acting on
the change.
- This commit will retrigger the installation for all word processor
plugins. For the majority of users this will succeed silently.
- It will fail with an error for Windows users that have Word open.
- It will succeed with a warning for macOS users that have Word open.
- The updated error prompt links to the new documentation page for
failed plugin installs
- Return base field instead of throwing if base field is passed for a
type that has a base-mapped field
- Return false instead of throwing for invalid type-field combination
- vpat 31: mark which menuitems' button is checked,
otherwise selected options (e.g. Update plugins automatically)
are not announced as such
- vpat 33: when plugin details pane appears, remove
a misleading role="tabpanel" from its section because
it is not structured as a collection of tabs
So that on Windows, the separator text on white background
has a high enough contrast ratio for accessibility
Also remove opacity when reference panel opens. Without the opacity
on the reference panel, there is a shadow appearing below and to the
right of the panel on Windows. If we let the panel render with
opacity: 0.9 and only then set opacity: 1, the panel will no longer
be transparent and there will be no shadow.
For example, clicking in the middle of the Accessed field would move the
cursor to the end, no matter where the click happened. Clicking and
dragging when the field wasn't already focused would select from the
beginning of the field to the drag position.
Show an appropriate error if the ARM64 installer is run on non-ARM
Windows or if the x64 installer is run on a 32-bit system, and set the
correct architecture in `CurrentVersion\Uninstall` registry key.
Also fix `x86`/`x64` being swapped in the registry
(https://groups.google.com/g/zotero-dev/c/1Aju4t2TNTo/m/Ga4V8vLrAQAJ)
`-jsconsole` somehow prevented Zotero sources from showing up in the
Debugger tab of the Browser Toolbox, but there's no reason to show the
Browser Console when we're showing the Browser Toolbox, so just don't
pass `-jsconsole` when `d` is passed.
Fixes#4503
- Use padding-inline-start for gutter
- Refactor other rules that used left/right margins/paddings, to make
future accidental breakage less likely
Fixes#4538
- hide tooltip on click and make sure it does not appear
for a bit to avoid race conditions (e.g. clicking
right after mousemove stopped)
- do not display a tooltip if a mouse key is being
held (e.g. during drag in itemTree)
Fixes: #4519
Unify richlistitem styling rules
- Fix richlistitem unwanted focus ring on Linux
fix: #4510
- Simplify richlistbox styling rules and do not follow Mozilla code
- Use --default-focusring-width for v-table
- added button to switch mode to creator rows
- removed that option from options menu
- extra tweak to avoid cursor flickering during re-render after
clicking on the button
- increased itemPane min-width to 320px
Fixes: #3959
This reverts commit 06741ceb62.
A couple people on Fedora 40 reported that the desktop file was no
longer working for them after this change, and it's not clear that
anyone was having trouble on Ubuntu before it -- someone just noted that
`desktop-file-validate` was showing some warnings.
Fix regression after 117197e11d
where space or enter would not trigger a click on
link outside of the main window.
".keyboard-clickable" is handled by listener in ZoteroPane,
so links outside of it (e.g. preferences) are not affected by it.
For now, just explicitly handle "keypress" event by the
zotero-text-link component.
Also, added a focus-ring around the link so it is visible
when it is focused.
Fixes: #4521
- minor refactoring to avoid deleting the first ("Custom") menuitem
of the resolver menulist. It is the first item that is selected when
popup opens and deleting it confuses keyboard navigation, so that
arrowDown/Up won't navigate the menu (unless the menu is hovered
over with a mouse)
- explicitly re-select the first item when the menulist closes.
Otherwise, in case of having navigated the menus with arrows
without changing selection and closing the popup, next time
resolver selector appears, arrowUp/Down will not navigate the list.
Only occurs on Windows.
- clear the resolver menus (except for the 1st item) when the popup closes
so that arrowUp/Down on focused dropdown don't select invalid
top-level menus (e.g. "North America").
- make sure that if the URL has been edited, the resolver dropdown's
value will switch to "Custom" even if the resolvers were not loaded
fixes: #4491
- set max-width on the actual citation properties popup
- refactor _buildItemDescription to construct item description
as an array of spans, instead of labels, so that the
text can properly wrap.
Do not use specific "#item-tree-main-default" selector to
find the itemTree node to focus since a different id
can be set for the table, e.g for the list of feeds.
Use a more general "zotero-items-tree .virtualized-table"
selector to focus the itemTree on tab from quickSearch
and on Escape keypress from an editable-text of itemPane.
Fixes: #4494
Previously, an empty value for the pref would be treated as matching
*every* MIME type, because s.startsWith('') is true for any string. That
meant that unchecking all the file type checkboxes would actually enable
renaming for all files.
And include in stylesheets that have been updated.
Have to make sure not to include this mixin in the global scope in
stylesheets that are themselves imported elsewhere (i.e., filename
begins with an underscore).
Move it from the parent element (<checkbox>, <radio>) to the first child
(.checkbox-check, .radio-check). This way, margins set by classes like
.indented-pref don't negate our global styles.
- Don't import global Zotero SCSS
- I think this was always a mistake - preferences have their own
root SCSS file (preferences.scss) and don't need zotero.scss
- Don't add margins on radio or button labels
- Like above - I don't think this was correct even before the latest
button style tweaks!
- Don't add global margins
- Otherwise we start an arms race with all the other elements in the
pane - anything without a margin-inline-start will look like it's
sticking out to the left. Apply specific margins instead.
Tweak to the conditional to check if something is focused
via selection.count (instead of selection.focused which
defaults to 0 and fails on the first focused item).
Post: 40fd5efe05.
Fixes: #4444
* New `attachmentTitle` field, returns the title of the current attachment (or
the future title of the attachment being created)
* New function `match` to enable testing values with a regex.
* New function `start` to enable truncating from the beginning.
* Ignore new line characters in the template for easier editing.
* Avoid repeated characters when changing case (snake/dash)
* Increase the size of the template input field.
Closes#3252
After redesign, tab order became: quickSearch -> itemTree ->
cancel and accept buttons -> collectionTree.
This restores original tab sequence: quickSearch -> collectionTree ->
itemsTree -> cancel and accept buttons.
Mozilla uses a `precomplete` file to delete local files when doing a
complete update, but since Zotero 5 in 2017 we've just been bundling an
empty file, which has meant that deleted or moved files have been left
behind. Among other things, this has likely been the primary cause of
Safari App Extension post-update brokenness for many years.
Incremental updates weren't affected, since those include explicit
removal instructions for moving from the given build to the latest one.
This restores proper generation of the `precomplete` file during builds,
using the Mozilla script added in 74ec6620e.
Separately, we'll update the `removed-file` for each platform to remove
files that should've been removed during previous updates.
Fixes#3133
Instead of focusing the first condition, which ends up placing
focus in the middle of the new window or modal. It is best
to focus the first node, which is the general convention,
per https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/, and
what many screen reader users expect. When we focus a field in
the middle of the window/dialog, it makes it seem for some users
that this is the first node, which will make them miss all the
fields above it.
Also, linked search name input to its label in searchDialog.xhtml.
Since -jsdebugger apparently works fine, there's no reason to connect to
Zotero from Firefox anymore. So just open the Browser Toolbox when
passing the -d flag to build_and_run, and stop having the devtools
server listen on port 6100 for devtools-enabled builds, since
-jsdebugger uses a random port of its own.
<panel> takes over Escape handling by just closing the popup via
what looks like a capturing listener on document, since Escape
keydown events never even reach the popup itself. When the popup
is just closed like that, tagsBox is blurred so all unwanted edits get saved.
To properly handle Escape, attach our own capturing listener that will
reset all edits before the popup is closed.
Fixes: #4398
- Mozilla's ActorManagerParent was only being imported when devtools
were initialized (either by opening the Error Console or by connecting
via remote debugging). Import it in our ActorManager so Mozilla
actors, including AutoScroll, are always registered.
- This may have other effects! I'm assuming most of us test with
the error console visible, though, so we would've had Mozilla
actors, while users running without devtools wouldn't have. We
definitely want those actors. (Most of them, at least.)
- Don't override autoscroller styles with our panel customizations.
After react 18 update, the tab node may not yet be rendered
by tabBar.jsx when we try to scroll it into view in Zotero_Tabs.select.
- To make sure scrolling happens when rendering is done, move scroll-related
logic into a useEffect of tabBar.jxs. It also makes sure that we'll scroll to
a selected tab if it is moved via context menu to the very beginning or the end.
- Added a small scroll-padding to tabs container to make sure the border
does not get cutoff after the tab is scrolled into view instead of
JS code accounting for the border.
- Fixed a glitch where the pinned library tab would not get selected
on shift-tab from opened tabs menu.
Fixes: #4382
* vpat 48: announce selection for default virt table
For simpler virtualized tables
(e.g. style manager in Zotero_Preferences.Cite), the table
does not get re-rendered when selection changes, so
aria-activedescendant does not get updated in render().
- a single function to update aria-activedescendant of the table
- call it in _updateTree of the tree selection because _onSelection
is not called the very first time a table is rendered. In that case, after
restart, collectionTree would have a selected row but no aria-activedescendant
- remove this.forceUpdate() from selection handlers of itemTree and
collectionTree because judging by the coment it's main purpose was to set
aria-activedescendant through render()
- set aria-activedescendant in _onSelection handler
of virtualized table.
- construct and set aria-label for rows build via
makeRowRenderer of VirtualizedTable so that is is
announced when the row is selected.
- remove earlier shift-Enter behavior that makes tag's
input multiline.
- shift-Enter will function just as Enter by saving the
tag and allowing focus to return to reader or itemTree.
- except after pasting multiple tags which will still turn
the tags input into a multiline field. Then, shift-enter
will add a new line, just as with any other multiline
editable-text.
- focus will always enter the tagsBox popup opened from the reader
- escape from within the popup with tagsBox will close the popup (vpat
67)
- one should not be able to collapse the tagsBox. Added `collapsible`
getter and setters to the collapsible panel to prevent the section
from changing its open status. This may be also used in other cases,
such as to prevent itemBox from being collapsed in duplicates mode.
- patched a glitch where tab from the last empty tab input would loose
focus.
- changed the `menupopup` for `panel` to display the `tagsbox` because
`menupopup` has implicit `role="menu"` which is not meant to contain
inputs, so voiceover completely looses cursor when inputs are focused
inside of the popup. Also, tweaked spacing a bit to avoid the
focus-ring getting cutoff.
Addresses: #4222Fixes: #4230Fixes: #4226
Addresses: #4388
As opposed to 'change' event.
Because if the field is editted and another window is focused
while the cursor is in the field, the change event will be
swallowed and when the field finally looses focus, it will not
be saved.
This may address the reported issue of edits not being saved in
title and abstract fields.
Addresses: #4388
- display the first continuous span of emojis in the primary cell of
the itemTree for non-colored tags.
- the emojis appear after the colored tags' circles (if any)
- to keep things consistent with itemTree, sort tags in the tagsBox in
the following order: colored tags first sorted by their position,
emoji tags after sorted alphabetically, followed by remaining tags sorted
alphabetically.
By moving the setAutoAttachmentTitle() calls to importFromFile() /
_addToDB().
Also:
- Chop off file extension when setting the parent's title based on the
filename in Create Parent Item -> Manual Entry.
- Fix Manual Entry not renaming the attachment correctly by awaiting
createEmptyParent().
When tabs' state is updated, refresh library tab's icon.
Do not skip it if the icon already exists (as for reader tabs).
Otherwise, when selected row from collectionTree changes,
the icons in the library tab will not update.
Fixes: #4385
- remove padding between the <dialog> and the window edges
- explicit width for all zoterosearch fields because otherwise
a menulist with a long content (e.g. longer collection name) name
can push the + and - buttons outside of the window
Fixes: #4374
- properly determine between which two bubbles a click landed
- set input direction
- delete the bubble on the left of the cursor
- in rtl, use selectionStart as the expected end of the input
and selectionEnd as the start. It fixes the issue of the
cursor getting stuck at the end of the input, as well as
fixes the Home/End not working from the right end.
- fix spacing issues for item descriptions in both layouts
Fixes: #4371
* Multiple accessiblity fixes
* Increase font size
* Nicer layout of the welcome screen
* Fix description in the welcome screen
* Add stripes to the table in citations matching screen
* Change icons for accept and resolve manually buttons
React 18 introduced automatic batching which tries to avoid unnecessary
re-rendering (https://react.dev/blog/2022/03/08/react-18-upgrade-guide#automatic-batching).
In some components based on react-virtualized (e.g. tag selector),
it leads to visible lagginess on scroll via keypress or
mouse wheel (not trackpad). This patch wraps setState of the scroll handler
of react-virtualized with ReactDOM.flushSync, which opts out
of automatic batching.
Fixes: #4368
- Wait for the itemsView to be initialized in the onload listener of the
Advanced Search window
- moved waitForTagSelector in a few tests to avoid it resolving too
early. For some tests these tweaks are always required, for others -
tests would pass half the time. Again, not clear why it's necessary,
potentially some flakiness from react 18?
- getDOMElement relied on React.renderToStaticMarkup,
which is react 18 was moved to a different file than the
one exposed with react-dom-server. To not add another
file just for that one function, replace getDOMElement
with getCSSIcon.
- getDOMElement was mainly used for a few remaining
png icons that were not replaced with svg. For those
few icons, just record which background-url should be
set when the module loads and add it in getCSSIcon if
applicable. Alternatively, background-image setting
could be moved into a stylesheet?
- a few hardcoded twisty svgs in icons.jsx are not used anywhere
(they would be fetched via IconTwisty), so those are
removed
- await for promise that is resolved in ref attribute
of root.render() as an alternative for removed callback
from ReactDOM.render
- await-ing for promise every time when ref needs to be
used after render (e.g. tag selector container), otherwise
ref will be undefined
- additional window.sizeToContent calls to properly size
dialogs with react-rendered content (e.g. create parent),
otherwise the window can cut off some of the content.
-moz-accent-color variable doesn't seem to exist on linux.
Use --color-accent as the focus border, same as what is
already done in the reader.
Fixes: #4340
- revert change from 2401a34031
that only loads un-trashed collections in _loadCollections.
If an item only belongs to deleted collections, item._loaded.collections = true
from _loadCollections will never run, so an exception
will be thrown in item.toJSON() when syncing happens.
Instead, to address the problem of item.getCollections()
having stale data #4307, add 'includeTrashed' parameter to
item.getCollections() based on which item._collections
will be filtered. Fixes: #4346
- revert earlier, no more necessary, changes from a532cfb475
to not alter item._collections cache when collections are being trashed or restored.
Collection is removed from item._collections only when it is permanently
erased.
- removed unnecessary test checking for consistent item._collections
value before and after reload, since item._collections is no longer
modified
- fix encountered bug where a trashed child collection is not
unloaded if a parent collection is erased without being trashed first.
- tweaked Zotero.Search sql construction to count items
that only belong to trashed collections into 'unfiled'. Fixes: #4347
---------
Co-authored-by: Dan Stillman
Now we give the caller control over whether the pane should still be
shown after it loads. We're fine with showing in all cases when handling
a search, but we don't want to show when handling a navigation select
if the user already selected something else while the pane was still
loading.
Fixes#4357
By extracting Tab into a separate, memoized component, caching handlers
and tweaking how icons and other props are passed, we're able to only
re-render tabs that actually changed, rather than re-rendering the
entire tab bar all the time. This should be especially noticeable when
dragging tabs around but will reduce CPU cycles used in general.
- listen to 'collection' notifier events and re-render
the section if a relevant collection is moved to trash or
modified. That way, a deleted collection will be removed,
a restored collection will be added back, and renaming a
collection will update the name.
- fixed a bug where a restored collection would not
be added into item._collections cache. Fixed false-positive
test for it.
- do not add deleted collections into items. _collections cache in
Zotero.Items._loadCollections. Otherwise, deleted collections will
appear in librariesCollectionsBox after the app is restarted.
Should reduce jank when opening the item context menu and sidenav
Locate menu, especially on Windows (where the flash of blank menuitems
lasted an especially long time for some reason).
Since the header of the collapsible section is not visible,
there is no way to expand the itemBox if it was collapsed
before dulicate items are selected. With this, itembox
is always opened when a new set of duplicate items is selected.
Fixes: #4318
- added dynamic tooltip whose label is set only when it appears.
It allows us to have a visible tooltip without screen readers
announcing it
- fetch the pane name string and pass it as an argument to
fluent to set the appropriate label on expand/collapse buttons
- tooltiptext is set dynamically on expand/collapse buttons to
avoid having screen readers read both the label and the tooltip text
that are almost identical
used to announce which annotation is selected during
keyboard navigation, as well as the index of the current
search result
Followup to zotero/reader#130
- always render options and link buttons - just do
not display them if their respective field is empty.
That allows us to easily handle focus after refresh
because otherwise, the node may does not exist.
If we try to restore focus to such hidden component,
simulate a "tab" from it to focus the next possible node.
This fixes the issue of focus being lost on tab after all
content of editable-text is cleared.
- add tabindex=0 to itemType menulist, otherwise it was
not perceived as a candidate to return focus to.
- somewhat special treatment for restoring focus to
creator rows. If the desired node is not found, we'll
try to focus the respective node in the last creator row.
It prevents focus from being lost on tab after clearing
the very last creator.
Fixes: #4241
this._addCreatorRow needs to be properly initialized to
false (as opposed to being undefined) because 'false'
is the value we check against to decide if an empty
row should be added and focused after rendering.
Fixes: #4268
- only have enabled "Permanently delete" and "Restore to library"
options in the context menu for collections and searches in the trash
- display the custom header with buttons to delete or restore
in the itemPane when a collection or search in the trash is selected
Fixes: #4295
* Fix template and preview overflowing container
* Fix extra whitespace around "documentation" link
* Increase minimal height of the template input field
a532cfb475 added `isCollection()`, `isSearch()`, and `isItem()` methods
to data objects to handle collections and searches in the trash, with
`isItem()` checking whether `._ObjectType` was `Item`. That left out
feed items (`._ObjectType` == `FeedItem`), and when c384fef867 made
`getSelectedItems()` return only items, it used `isItem()`, so feed
items were excluded, which broke feed-item toggling between read and
unread [1] and possibly some other things.
The simple fix would be to make `isItem` match feed items as well (which
could potentially fix other bugs related to feed items), but there was
actually no need to add new methods (which can get confused with
`CollectionTreeRow` methods) when we can just check the object type with
`obj instanceof Zotero.Item`, which gets the benefit of inheritance and
matches `Zotero.FeedItem` instances as well.
[1] https://forums.zotero.org/discussion/115571/cannot-change-the-status-of-title-in-subscribtion
Fix to breakage after 3f45def that would not open a duplicate
tab but instead create another reader instance in the same tab.
Instead of finding a tab for a specific item, use tabID that
is passed when reader should be loaded in an unloaded tab. That
allows us to know if the tab is being duplicated or not.
Fixes: #4272
Sidenav button pane IDs usually correspond to sections with the same
IDs, but `context-notes` actually corresponds to two sections
(`context-all-notes` and `context-item-notes`). We were preventing
those sections from being pinned, but not the button that reveals them.
Fixes#4283
This changes `createDataObject()` to pass `skipSelect: true` for objects
other than items. If a test is creating a bunch of collections, there's
no reason for each one to be selected and for an items list to start to
load. If a test does need a new collection or search to be selected, it
can call the new convenience function `await select(win, obj)`, which
will select the passed object in the collection tree and wait for its
items list to load. I'm hoping this reduces random test failures due to
items list churn.
This adds an explicit function on ZoteroPane and the item tree for
getting objects -- including collections and searches in the trash --
and limits `getSelectedItems()` to returning actual items from the
selection. We shim various item properties on the collections and
searches in the trash, but code that's getting the selection should be
explicit about what it wants. Outside of the trash, they're equivalent,
except `getSelectedObjects()` does have an `asIDs` mode.
This switches to using getSelectedObjects() in various places and fixes
collections in the trash appearing as belonging to My Publications when
using the collections-containing-an-item highlight [1].
This also adds support for highlighting the parent collections of
collections in the trash.
[1] https://forums.zotero.org/discussion/115449/zotero-7-beta-deleted-collection-appears-as-belonging-to-my-publications
- Remove unsaved creator row on blur or escape
- Rename "unsavedRow" for "position" as we want to be able to find
the relative position of creators after the next refresh not only
for unsaved rows. In many cases "unsavedRow" as returned by getCreatorFields
is the actual index of the creator row.
- Calculate and use "position" in getCreatorFields for all creator rows, not
only unsaved one, when a new row is being added.
This fixed a bug where wrong row gets focused if an unsaved creator row is
added, some text is typed and then another creator row below this unsaved row is clicked.
- fixed a bug where autocomplete options would not be updated after creator mode
is switched for the default empty row (if there are no creators)
- simplified paste handler of creators to use modifyCreator that
also shifts creators if a creator is unsaved. Fixed bug brought up in
https://github.com/zotero/zotero/pull/4165#issue-2313280474 where
pasting creators does not always focus the last added creator.
- Fixed another bug brought up in https://github.com/zotero/zotero/pull/4165#issue-2313280474
where shift-enter from creator before "_ more creators" label
will add a new row in the end instead of focusing the next creator.
- Fixed bug where adding a row right before "_ more creators" label
and blurring it will remove all creators after it. Now, clicking +
on a creator right before the "_ more creators" label will display
all creators and add a row after it.
- Fixed a bug where if "_ more creators" is present, editing
a creator name and pressing shift-enter would loose focus
instead of adding and focusing a new row in the end.
- Fixed a bug where focus got lost from some buttons
Fixes#4143Fixes#4241
- linked color scheme radio buttons to their label. Using
aria-describedby on individual radio buttons instead of
linking color scheme label to radiogroup because voiceover
does not announce the label in that case.
- linked item pane header dropdown to its label
- linked markdown and richtext note format checkboxes to their labels.
- added aria description to linked attachments base directory
so the input and "choose" button
- linked virtualized tables to their labels
Data directory location setting needs some more labeling as well
but it's mechanics should be generally reworked per vpat 50.
- if an unloaded tab is being opened, do not close the
tab and have reader re-open a fresh tab. Instead, keep the
tab as is, have reader use the tab as a container and
just change the tab's type. It should fix a glitch on
windows when if you click on a tab during initial loading,
it will disappear until reader re-adds it. A few tweaks
to allow unloaded tabs be generally "selectable".
Fixes: #4149
- during startup loading, select the yet-unloaded tab right
away. That way, a tab is almost immediately selected,
instead of being stuck on the library tab until the reader is ready.
- if the items are not loaded yet, use a placeholder
icon for tabs. Fixes: #4150
- special handling for reader loading message during initial
load. When an unloaded tab is selected, the loading message
needs to be displayed but, since there is no reader yet, we
add it in Zotero_Tabs.select and try to remove by the reader
when it's loaded.
* Omit msg-ref-only strings from Transifex JSON
* Fix msg-ref-only strings not included in translated .ftl files
* Update ftl-tx. Simplify localize-ftl script.
* Tweak FTL -> JSON conversion to produce a single file
- Use SVG icons
- Show "[x] collection selected" or "[x] searches selected" in the item pane
- Show "[x] objects selected" if multiple types are selected, which I
don't love, but I don't have a better idea
- Use existing strings for ARIA labels
When a collection or a saved search is deleted, it appears in
trash among other trashed items. From there, it can be restored
or permanently deleted.
Items of trashed collections are not affected my the trashing/permanent
deletion of a collection and need to be deleted separately like before.
Subcollections of a trashed collection do not appear in the trash and
are restored or permanently deleted with the top-most trashed parent.
Previously we were redownloading only `SYNC_STATE_TO_DOWNLOAD` files,
not `SYNC_STATE_FORCE_DOWNLOAD`, because the latter gets downloaded even
when using on-demand file syncing, but if the download fails for some
reason, it should be retried on a manual open.
If the header is in bibliography entry mode.
Instead of trying to focus editable-text of the header (which
may not be possible), just focus the next node after the header.
- Add pagination, limits, and Link header
- Add schema endpoints and dummy /settings endpoint
- Add /file endpoints
- Browser security restrictions prevent the web library from actually loading
the file: URIs that the local API returns, but out-of-browser use will work
fine
- Add toResponseJSONAsync() DataObject function: delegates to toResponseJSON()
by default, adds information that requires awaiting promises
- Best attachment (links.attachment) and file size (links.enclosure.length)
for items, meta.numItems for groups
- Separate function for compatibility with the existing test code that uses
toResponseJSON(), but we could consider unifying
This commit does not add the Access-Control headers that allow webpages to make
requests to the local API, since I don't think we actually want that.
This required some tweaks to other parts of Zotero infrastructure:
- Search:
- Add 'includeDeleted' condition to match behavior of 'includeTrashed' API
parameter in a single search
- Data objects:
- Improve toResponseJSON() implementations so output better matches the web
API
- Add toResponseJSON() to Zotero.Tags - has to be async so it can query the
database and generally works differently from other toResponseJSON()
functions, but accomplishes the same task
- Remove unused getAPIData() and apiDataGenerator() DataObject functions. They
aren't functional and wouldn't really make implementing the local server
easier, so now seemed like a decent time to remove them
- Server:
- Support resolving routes using pathparser.jsm
- Add allowMissingParams option to PathParser#add(): prevents /route from
matching /route/:param
- Replace the query property of the data object sent to endpoint init()s with
searchParams, an instance of URLSearchParams - supports #getAll() for
repeatable parameters
- URIs:
- Make getObjectURI() public, add utilities for converting URIs to API
endpoints and web library URLs
- Set the window minimum size to 700px x 500px.
- Fix a regression where non-applicable operators remain visible.
- Fix the "✔️" appearing next to multiple operators in the operator popup menu.
- Include the "Year" and "Attachments" columns by default in the results table.
- richlistbox shift-click selects a range starting with
_selectionStart. After a few cmd/ctrl clicks, _selectionStart
ends up not being correct - it can even point at an unselected item.
To help richlistbox properly select a range, before each click is
processed, reset _selectionStart to be either the first
or last selected item, depending on where shift-click landed.
- also, fixed a small glitch where inputs would be created if
you click on the red border outside of the actual editor.
Fixes: #4206
Use a regular paragraph for the intro text rather than an input
placeholder, which gets cut off it's too long, and allow the window to
be sized dynamically based on the title and localized strings.
Fixes#4109
"Note format" section in "Export" tab is technically a table and is
announced as such by screen readers, which is misleading.
It also opens a menulist when a checkbox is clicked, which is
not an expected behavior and can shift focus.
This refactors the "Note format" section to be a vbox container
with <checkbox>es and labels, which is more correct semantically and
does not confuse screen readers. In addition, when a chekbox is
clicked, dispatch a "change" event instead of unnecessarily open
up a menu.
- linked input/output file labels to the input fields so
they have descriptive names
- added more detailed button labels
- fixed "No file selected" not appearing if no file is selected
In libraries, notes and tags boxes, do not hide the actual
"-" toolbarbutton, hide the <image> inside of it. Visually,
it looks the same but the button can be focused via tab
without having to expicitly make it visible. It means that
shift-tab from the section header will not skip the "-" button.
Also, make "remove" toolbarbutton from tagsbox focusable like all
others for consistency.
- Linked remaining found inputs/menulists to their labels.
- Made the "Choose resolver" a proper visible
label, so that it does not oddly disappear if you click
on the dropdown with "Custom" showing after even if the
selection did not change. For the purpose of VPAT, a
visible label is always good for success criteria 3.3.2
https://www.w3.org/WAI/WCAG21/Understanding/labels-or-instructions
- Added explicit names to +/- buttons
- aria-labelledBy for inputs surrounded by text
- fluent strings for resolver preferences
- remove openURL.version preference, hardcode "1.0"
- added 8px padding around text on linux. Mac and win already have some
padding around text through --panel-padding. Fixes: zotero#4187
- guidancePanel.js fetches text from .ftl file instead of zotero.properties
- added first-run-guidance-quickFormat to zotero.ftl with updated
text to better reflect current functionality. Fixes: zotero#4191
- removed unnecessary special treatment for qf guidance panel on mac
- moved guidance panel text for itemBox creator menu into zotero.ftl
---------
strings tweaked by @dstillman
- multiple reference items can be selected with
shift-arrowUp/Down or Cmd/Ctrl-click and added as bubbles
via Enter or click on one of selected items
- Shift-click can be used to select the range of items
- if there is a retracted item among multiselected
that is not OK'd (cancel or view in library clicked),
no bubbles are added and the item is removed from
the multiselection
- added a red note to item's description indicating
if an item is retracted
* Improve feed item pane header (fixes#4141)
* Show item pane custom head when headerMode is none (fixes#4116)
* fx115: Fix button style on windows (fixes#4120)
* Move split-menu-button styles to SCSS
isSnapshotAttachment() checks the link mode (needs to be IMPORTED_URL),
but we actually allow viewing all attachments with text/html MIME types
as "snapshots" in the reader. Don't throw on any HTML attachment that
the reader is willing to open.
- 59afef6 added a duplicate assignment to item.rights that was putting
a TextConstruct(!!!!) in the item JSON field instead of a string
- info.rights was being set to a TextConstruct (preexisting issue!)
The 'blur' event needs to be dispatched by editable-text
before its state is reset (not after) because tagsbox uses
.initialValue in the 'blur' handler.
followup to 101e6d55d5Fixes: #3575
- fix the infinite loop glitch. Tweak of editable-text to only
dispatch 'blur' event if the input was actually blured. It prevents
the hideEditor from being locked in an infinite loop due to the
call stack hideEditor -> modifyCreator -> moveCreator -> blur -> hideEditor
- refactor blurOpenField to not be async and to not be used in most
places. It is mainly redundant now that editable-text handles blur
if clicked anywhere outside of it, so it helps to avoid confusion.
- since moveCreator does not await or yield for anything anymore, remove the
Zotero.spawn part. The rest of the function is unchanged.
- on focusin of the empty unsaved creator row, use its future unsaved id
that reflects its positioning instead of its real id (which technically is
the very last creator row). That way, a tab from a creator row that was just
filled will land to the proper component after render, as opposed to
focusing the very last row.
- similar approach to removing the unsaved creator row when the focus is
in it - focus the row above it, as opposed to the last row.
Fixes: #4145
Addresses part of: #4143
- plus button is enabled on all creator rows, not just the last one
- enabled plus button will add an empty unsaved creator row below
current creator row. After that, plus button is disabled as long as
the next row is unsaved.
- the grippy of a newly added unsaved row is disabled
- adding an unsaved creator row will remove another unsaved creator row
if one exists. That way, only one unsaved row can exist at a time
- once the unsaved creator row is added, it is tentatively perceived as
the "last" creator row based on its id. That is to avoid shifting all
other creators' indicies and saving the item too many times. When the
field is blurred, that new creator is moved into its "unsavedIndex"
before saving the item
Fix to a glitch where after switching creator field mode, the
autocomplete suggestions for creators would still be for the old mode.
This only happened if the field mode was changed on an empty, unsaved
creator row.
- removed ztabindex logic from itemBox. It is no longer needed, adds
unnecessary complexity and is likely at the root of multiple glitches
if a plugin inserts an arbitrary row that does not have ztabindex set.
- if a creator row is deleted when the focus is inside of the row, focus
another creator row to not loose the focus.
- more centralized button handling in `_ensureButtonsFocusable` and
`_updateCreatorButtonsStatus`
- refactoring of hidden toolbarbuttons css so that the icons are still
hidden and don't occupy space (if desired) but are still visible for
screen readers, so they are focusable without JS changing their
visibility (this with ztabindex removal fixes vpat 24)
- removed `escape_enter` event from `editable-text`. It was a workaround
to know when itemBox should move focus back to itemTree. Unhandled
Enter on an input or Escape should focus itemTree (or reader) from
anywhere in the itemPane/contextPane (not just itemBox), so that logic
is moved to itemDetails.js. To avoid conflicts with Shift-Enter, do
not propagate that event outside of multiline editable-text. Fixes:
#3896
- removed not necessary keyboard nav handling from itemDetails.js. It
was only needed for mac, and is redundant if "Keyboard navigation"
setting is on
- using `keydown` instead of `keypress` for itemDetails keyboard nav
handling because `Enter` `keypress` does not seem to get out of
`editable-text` but `keydown` does.
- old handleKeyPress from itemBox is no longer relevant for most
elements, so it is removed and substituted with a dedicated handler
just for creator row.
- moved the creator's paste handler into its own dedicated function
from the autocomplete handler (which was confusing)
- special handling for `enter` and `escape` events on `editable-text`
with autocomplete to not stop event propagation, so that the events
can bubble and be handled in `itemDetails`. It avoids some cases of
the focus being lost and returned to the `window`. It was unnecessary
earlier due to `escape_enter` workaround but only within itemBox and
only within itemPane.
- removed explicit tab navigation handling from `collapsible-section`
header. Currently, it may get stuck when buttons are hidden (e.g. in
the trash mode). It was only added to enable keyboard navigation on
mac before special "Keyboard navigation" setting was discovered (it
was never an issue on windows), so now it's easier to just let mozilla
handle it.
- always use `getTitleField` to find and focus the proper title field in
itemBox
- on shift-tab from the focused tab, just move focus to the first
focusable element before the splitter without any special handling for
attachments, notes and duplicates pane as before. It ensures a more
consistent and predictable keyboard navigation, especially now that
itemPane is fairly keyboard accessible.
Fixes: #4076
:focus-within doesn't match when the window is inactive but the field
is focused.
Addresses #4101, except that focusing the new item's title field and
pressing Escape will clear the field (without modifying the item).
If a parent item and its primary attachment are selected, it should
still say "Show File" rather than "Show Files". This adds the
questionably named `Zotero.Items.numDistinctFileAttachmentsForLabel()`
to try to figure out if we're operating on 0, 1, or >1 items.
Follow-up to #4124
Make sure the vertical scrollbar does not occupy space
on mac when the menu is not scrollable even if
the "Show scroll bar" setting is "Always"
Fixes: #4121
Fixes the issue where long press would not open up
context menu in a virtualized table and tab bar because
it was handled in mousedown events that longpress on a
touchscreen does not dispatch.
This behavior is still observed in the scrollable area
of the reader and the note editor.
Addresses: zotero#4094
When item is created manually, title field in itemBox
will be focused instead of the title in the itemPane as before.
If the itemBox is collapsed, it will be opened.
Added 'open' setter to item pane section for brevity
Fixes#4111
- revert debcb9944d since it breaks
drag-drop reordering of tabs
- when reader is being refocused by contextPane, add a small delay
so that the focus settles on the tab before focusing the reader
- also explicitly refocus the reader tab after drag, since then focus
also lands on the tab and finds itself outside of the reader
If the info section is collapsed, do not try to focus and
open the itemType menu as it can lead to an empty dropdown.
Just focus the header title in that case
I think they're still slightly off center, but now they look exactly
like they do in Firefox. Dropdown labels in Apple's native macOS apps
are actually offset toward the top by a pixel or two.
Fixes#4086
After clicking on a reader tab, the reader will get focused
but the default mousedown event handler can then shift
focus onto the actual tab. It looks like the focus gets "lost",
and breaks reader keyboard shortcuts so we want to avoid it.
Mainly happens on windows.
Fixes: #4077
updateLayoutConstraints() needs its size to be fixed across window
resizes. It didn't shrink automatically until fx115, I think, and this
commit restores that behavior.
Fixes#4062
- If there are too many tabs opened to fit onto the screen,
the tabs menu can be scrolled
- When tabs menu is opened, scroll to the selected tab
- Use margins instead of padding to that the scrollbar does not
overlap with the cross button
- Make sure that if the tabs menu is long, there will
be a gap between it's top/bottom and the edge of the
screen
- On linux, screen.availTop and screen.availHeight are not always
correct and the menu can go outside of what is supposed to be
the available screen area. Special treatment for those
edge cases
Fluent seems to clear out label/placeholders attributes set directly
(not via .ftl file). So quicksearch placeholder added directly ends
up being emptied out.
This is the cleanest workaround found so far: add the placeholder
as the fluent argument
Fixes: #4073
- do not erase the last recorded tag on blur of the tag selector
- on tab from collectionTree or shift-tab from tag selector input,
try to refocus that last focused tag. If one does not exist,
try to focus the first non-disabled tag.
Fixes: #4008
Locator type dropdown just has aria-label "Locator type".
The locator input is labelled by a hidden label with "Locator input"
string, as well as the locator type. That way both are announced
when it is focused.
Fixes: #4039
- vpat 57: remove tabindex values, set tabindex=0 on all focusable
fields. It allows mozilla to determine the best focus sequence
for keyboard navigation and fixes wrong order where some inputs would
go before the "Ok" and "Cancel" buttons and some - after
- vpat 58: added labels to all inputs to be announced when focused.
Added separate labels for locator menulist and for the locator input so
that the locator type has a proper label. It is also better to have
visible explicit labels when possible
Mostly for brevity, but also:
- "Down Arrow" → "space bar" because down-arrow doesn't open the
citation dialog with VoiceOver enabled -- it starts reading individual
words.
- In some cases (selected/open items and notes), Tab takes you to search
results without your needing to type something first.
Follow-up to #4002, #4004, #4005
Since Word can't handle FSI/PDI.
This affects dragging items into notes and dragging/copying annotations
in the reader. Citations inserted from within the note editor already
didn't include bidi control characters.
Fixes#4013
* tag selector focus edits
- no tabstop on the tag selector scrollable area
- change tag selector's role from default "grid" to "group".
"grid" is not quite correct semantically and leads to
voiceover suggesting irrelevant commands.
- move all keyboard handling logic to tagSelectorList.jsx.
Tabbing through tag selector is now handled in ZoteroPane,
so the only logic left there is arrow navigation
between tags, and there's no reason to not have it
together with the tags list.
- a workaround to deal with focused tags when windowing
kicks in. When a tag is focused, record its index.
Each time tags are re-rendered, if the saved index is not
among rendered tags, refocus it, otherwise, move focus
to the tags list.
Screen readers announce the index of the selected item
from the list of citations in the panel. This removes
the separators from the count, since they are not focusable
and should be skipped.
- edits to zoteroPaneTest.js focus tests to expect the updated focus
sequence: selected tab -> tabs menu -> sync button -> collectionTree
toolbar -> collectionTree -> tags selector -> itemTree toolbar
- updated tags selector keydown handling to explicitly handle all
tab/shift-tab events using moveFocus. It is more readable and explicit
focus handling for all components is required for programmic tab/shiftTab
events dispatched in tests to actually move focus
Fixes: #3975
From the sync button, tabbing will move focus in the following mannger:
collections toolbar -> collections -> tags selector -> itemTree toolbar
-> itemTree.
On last tab in the itemPane/contextPane, focus will wrap around
to the selected tab to complete the loop. It ensures
that the focus order is consistent regardless of the
directions.
Also, minor tweaks to focusWrapAround in Zotero_Tabs
to properly move focus to the last focusable entry in
the contextPane.
- set each tag's role as "checkbox"
- set "aria-checked" status depending on if the tag
is selected
- added aria-label to tag selector so it does not
default to "grid"
Added aria labels and button roles for buttons in the
item/Collection pane. This includes buttons in the header
as well as rows from notes, related, collections, and
attachments section that act as buttons. Also added
missing aria-labelledby for mini-itembox rows below
attachment preview to have filename, dateModified
and etc. properly labelled.
This also covers most of vpat 16.
This does not affect how tags/header/itemBox entries are announced.
There is a separate issue with NVDA and JAWS sometimes struggling
with announcing text inside of `editable-text` component,
so this will be handled separately.
- added role="link" to "zotero-text-link" custom component,
using that instead of directly setting the class
wherever possible
- in all other cases, added role="link" directly to links
- added tooltiptext and "noClick" option to zotero-text-link
so that this component could be used in attachmentBox.js
- use "keyboard-clickable" class in zotero-text-link
instead of a separate listener to handle keypresses for brevity
* feed settings: part of the grid, not needed
* progress queue: migrated to flex
* tab-bar-container: already fixed in scss
* zotero-lookup-multiline-progress: migrated to flex
* zotero-lookup-textbox: removed, does nothing
* #zotero-prefpane-advanced #data-dir-path: migrated to flex
* #zotero-prefpane-sync: migrated to flex on #storage-url, removed from #storage-password to fix layout
* .search-condition: migrated to flex
* updates.css: migrated to flex
(Or technically 125, but I don't think any strings we need were
removed)
This fixes the Edit and Window menu (except for some macOS Window
strings that seem to come from the system) in non-English locales.
This reverts commit ecea550a37.
It seems to be causing various UI issues, including the items list
sliding under the item pane when the collections pane is expanded
(though I can no longer reproduce that).
The `app/win/codesign` script should take a path to a file and a
description (`/d` parameter to `signtool.exe`) and sign the file using
whatever process the certificate authority requires.
DOMInputPasswordAdded event is triggered on autocomplete
submission which causes an error in LoginManager.
The error gets logged in the console, though the autocomplete does
work. This just removes the event from ActorManagerParent.
Fixes: #3883
Unimplemented:
- ARM-native installer/uninstaller
Untested:
- Installer/uninstaller/updater
- Word integration DLL
Also updates the launcher and updater to 115.9.0esr for x64. win32 stays
on 115.4.0esr for the launcher and 102.11.0esr for the updater because I
can no longer build it.
Using timeout in credentialsChange caused button to become momentarily disabled
making it flicker. I've verified that the delay is no longer needed and that the
button becomes disabled when using backspace to clear the login/password input field.
- css tweaks for proper sizing and minor cleanup
of unused classes and properties
- added flip="none" for the panels to not bounce
around window's edges
- removed dialog's marginBottom on macOS - it doesn't
seem needed anymore
- close and reopen panel when window's height changes
on all platforms. It used to only happen on windows
but now the panel remains at it's original location on
macOS as well, so we'll always reopen the panel so
that is is positioned close to the dialog
- try to postion the panel in the middle on windows
- fix progress meter not appearing
* improved qf window drag
- not using old windowDraggingUtils since it causes
issues on windows with the mouse outrunning the window.
- instead, set -moz-window-drag: drag on dialog when
the mouse is over the red border
- added chromeutils param to the window. These are required
for dragging to work on windows and their values are
important because wrong values will cause the window to
shrink and basically disappear. 0,0,15,0 seems to work
most reliably though the reason why other values (e.g.
0,2,2,2 as on the ZoteroPane window) break window is not
clear
- added margins around the dialog on windows. moz-window-drag
doesn't seem to properly work for mouse clicks very close
to the window's edge
- with fx115, the <panel> does not follow the window
when it is dragged, so we have to hide the panel during dragging.
Unfortunately, mouseup/down events don't fire on the dialog when
moz-window-drag: drag is set on windows. Without these events,
as a workaround, we check window's position periodically and
hide/display the reference panel based on that.
- removed initial window.resizeTo() right when the content is
loaded. It's not needed and sometimes causes rendering
issues when the dialog appears shrunk.
* fx115 linux css edits
- try to make the dialog look like dialogs on other platforms.
Red background, no chrome etc. The actual window's background can't seem
to be transparant, so no round borders
- minor edits to sizing on linux
- Use min-width: 100vw on the <dialog> element to prevent
window.sizeToContent() from expanding the dialog unnecessarily on
style selection
- Set the window min-width since fx115 width attribute on the window is
ignored
- Set ids to persist position and dimensions
It was technically still there, just offscreen. The wrapper element
was display: block, and some block layout quirk caused the button to
grow to fill the wrapper, which then put it offscreen when rotated.
This removes the wrapper and replaces it with a margin. Also fixes an
incorrect margin in RTL locales.
This fixes the Mac launcher, which had been ARM-only. (This launcher is
built from 115.9.0esr, because 115.9.1esr, a recent security fix,
doesn't seem to have a public tag yet.)
The Windows build seems to work without updating the launcher from
115.4.0esr.
- Output pane no longer gets huge after load
- URL box grows
- Metadata grid fills available space (but still needs work re overflow)
- row-resize cursor on Tests splitter
After the removal of zotero-service.js for fx115, we no longer have a
`components` folder, so we don't need to shuffle things around to merge
our folder with the one from Firefox.
This fixes a build failure we were getting in CI after #3894.
With fx115, changes from 49fe2b98d9
made the autocomplete popup appear blank when the reader tab is opened.
Not re-creating the PopupAutoComplete in tagsBox makes the items from
autocomplete popup visible again. But it makes the autocomplete appear
below the "Add tags" popup for annotations (what 49fe2b98d9
originally fixed), and it also creates a duplicate <popupset>
with a new autocomplete panel at the bottom of the DOM every time autocomplete runs.
The duplicate <panel>s do not have an id.
Manually adding a <popupset> with a <panel> whose id="PopupAutoComplete"
does not create any duplicates and properly positions the popup.
Fixes: #3881
Fix and improve tooltip logic
- fx115: move main html-tooltip outside of the deck, otherwise it only
shows up in the library tab.
- Zotero_Tooltip is not required in the tabs bar and the button of the
tag selector. Setting the tooltip attribute on the closest XUL parent
and adding title attribute makes the tooltip properly appear.
- Remove manual handling of the hover effect from tabs manu. The
hover effect doesn't stick around after drag-drop with fx115 anymore,
so it's not required. And then the usual title attribute works for the
tooltip.
- Set title instead of tooltiptext attribute in tagSelectorList
so that the tooltip appears
After zotero@8e2790e,
the pointer effects don't fire on the actual cells of the table
(only on the parent row), so the tooltips do not appear for the cells.
This is a (hopefully) temporary solution to handle mousemove events over the row
of the table, find the right cell that the mouse is over, and
use Zotero_Tooltip to manually display the tooltip.
Tweaked Zotero_Tooltip to create the fake tooltip and place it into
the DOM. Zotero_Tooltip is also imported by virtualized-table as a module
because one shouldn't have to load it in .xhtml file for every new window
where the virtualized-table is used.
Also:
- Tweak text to be less alarming (fixes#3074)
- Don't show "No updates found" if update was already downloaded
(fixes#3148)
Download progress still isn't shown (#3130) but I'll fix that later
- do not resize based on scrollable content - it shrinks the panel
- add padding into the stylesheet
- remove onShowing, since it doesn't seem needed anymore
Fixed visuals and working drag-drop again.
Fx115 made previously used toolbarbuttons act very strange wih drag-drop:
instead of dragging the actual button, the #text node would receive
dragstart event and a single letter would end up being dragged.
During troubleshooting, elements created via document.createXULElement
had this issues while being dragged (or acted oddly in other ways, e.g.
refusing to be dragged at all).
This includes a minor rewrite to use div-s instead of XUL components.
A number of style changes to fix layout for fx115.
- Make panes occupy all width/height based on layout.
- Display the tag splitter on windows and mac.
- Fix to odd fx115 behavior that gives an un-collapsed pane the
initial width of min-width * 2.
- Ensure that whenever there is a width attribute on contextPane, it will
be present in the style as well.
- Set width = 0 on zotero pane when it is not selected because it kept
preventing the contextPane from properly expanding.
- Context pane has its height changed on splitter drag.
- Set height of the context pane in stacked mode to 0 to avoid
having a blank gap after collapse.
- Remove negative margin before the toolbar if the collection tree
is collapsed on mac.
- Tweaks splitter styles to have mouse target of more than 1px.
Added positive z-index to make sure splitters are not covered by panes.
Splitter styles are somewhat unified for all platforms.
- Fix lookup panel sizing
- Quicksearch margin edits to not squize the input field.
- Collection and quick search fields layout fixed
- Use flex properties to fix layout
- fix outline display for editable-text
- fix the contextpane width going out of bounds
- stacked itemPane is visible after layout change
- In stacked view, prevent itemPane from being dragged so high that
it covers itemTree and overlaps with toolbar
We used them for pie spinners for attachment downloads in the items
list, but I guess we lost that with the new tree, and now they just log
warnings because the property was removed. We should reimplement with
SVG.
It only had `locale.dir`, which it looks like we can handle in different
ways now:
- CSS rules with `:-moz-locale-dir(ltr)`
- `let isLTR = document.documentElement.matches(":-moz-locale-dir(ltr)");`
XPCOM objects have to be statically registered now, so instead of
creating our own command-line handler, just stuff our code into the
Firefox one.
A few parameters require the Zotero object, which isn't available in the
Firefox CLH, so I've left those in zotero-service.js for now until we
decide how to deal with those.
Not awaiting exec() has the side effect that we no longer get errors
if the executable is missing / isn't actually executable. Extract those
checks to prevent problems of the sort fixed in 63f54d3 in the future.
Making this configurable - it's worth testing translators to make sure
that they *aren't* dependent on some specific cookie configuration,
apart from obvious cases like login-gated sites.
This fixes the current build annotations caused by deprecated versions:
Node.js 16 actions are deprecated.
Please update the following actions to use Node.js 20:
actions/checkout@v3, actions/setup-node@v3, actions/cache@v3.
For more information see
https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/.
Signed-off-by: Stefan Weil <sw@weilnetz.de>
Citation dialog: keep reference panel open without selection
- Reference panel remains opened while the focus is on an input
- The first item from the reference panel is no longer selected by default
to avoid unwanted items being added as a bubble
- The first item from the reference panel is selected only when the
dialog has no bubbles or when a search for a non-empty input has ran
- Shift-Enter from input or reference panel will accept the dialog's
state instead of creating a bubble
- Ensure that the reference panel reloads when a bubble is deleted
- Added button to accept the citation dialog
Some other changes:
- Z-icon and spinner/accept icon occupy the same amount
of space
- Ensure that window's width is 800px
- Set the editor's width dynamically when DOM is loaded
- Remove not used css classes and css adding margins to
z-icon.
- Do not accept/bubbleize while loading
- use dialog as the anchor, not the document element
- after the window is resized, the panel will remain
where it was, so if it's far away from the dialog,
if gets closed and re-opened to reposition it.
On windows, if the cross icon from the collection filter
search-textbox is clicked, the 'blur' event fires before
the input is cleared, and the filter is never hidden.
To make sure that the search field ends up going away,
hide it if the field is empty and not focused in input
handler.
Fixes: #3840
2 listeners handle shortcuts and hotkeys: bubbling ZoteroPane.handleKeyDown
and capturing Zoteropane.captureKeyDown.
We generally want shortcuts to be handled by the non-capturing listener
to avoid a conflict with lower level component that may want to handle the event
(e.g. we want to handle Escape keypress only if nothing else took care of it).
Tab navigation is an exception, since we want it to happen all the time.
For example, Ctrl+PageUp can be handled by itemTree but we want to override
it to make sure that it selects the tab regardless of other listeners.
This fixes regression after 5f0e3d5
where the tab selection logic was never reached because the event was handled
by itemTree.
- Bring back un-highlighting of collections on keydown if any other
control key besides Ctrl/Options is pressed (with better comments).
It fixes regression after 527fa5b12f
where if one presses, for example, Ctrl-Shift-A to copy an item,
collection highlighting would scroll the collection tree for no reason.
- To decrease the chance of collection highlighting being activated when
it is not intended, only highlight collections when the itemTree is focused
- handle additional focusElementID options value in Zotero_Tabs.select to
focus a node with the specified ID, instead of what was recorded
as last focused element
- use that parameter in ZoteroPane.selectItems to make sure that
itemTree is focused after the tab is switched to zotero-pane
- removed redundant calls to Zotero_Tabs.select('zotero-pane') after
Zotero_Pane.selectItems
Use <checkbox> instead of <html:input type="checkbox"> for the
suppress author checkbox. <html:input> on mousedown on the actual box
looses focus, so the popover is immediately closed by focusout listener.
Fixes: #3830
- Remove directories that should no longer be present in the build.
- Add watching for new files.
- Add debounce and batching to reduce verbosity and avoid needless cleanup and "add to omni" steps
when entire directories are affected.
We can await /usr/bin/open on macOS because it returns as soon as the
application has begun launching, but we can't await when we exec a
program directly on other platforms.
Fixes#3799
When contextmenu closes, it clears visibility style from all components.
It is added temporarily so that the options buttons do not
disappear if the mouse leaves the row. The visibility setting for
the grippy for items with only one author should be in
a separate class so that grippy is not displayed by accident.
Fixes: zotero#3732
_setState() doesn't await setAttachmentLastPageIndex(), so the setting
sometimes won't be updated in the cache by the time we call
getAttachmentLastPageIndex() below. Notifier event should work every
time (and is a lot simpler).
Hardcode transparent background and black text color for inputs.
Otherwise, the inputs end up with dark background and white text
which does not work with the rest of the dialog that always has white
background.
This is temporary until the rest of the dialog works with the dark mode.
Fixes: #3768
- Backspace from the very beginning of the input will delete the previous
bubble, as opposed to removing the input itself. If it makes it so that
two inputs are next to each other, they are merged.
- When a new input is created via a mouse click, and the current input
is empty, manually delete it right away. Waiting for the blur handler
to remove it makes it so that the bubbles shift back and forth for a moment
when a new input is added and the old input is not removed.
- Minor tweak to make sure that even if there's a 'br' before an input,
clicking before it will not make a new input and just refocus it.
Fixes: zotero#3749
- Run label update logic once instead of repeating on every item
- Show plural attachment type when multiple attachments of same type
are selected (instead of "Attachments")
- Fix incorrect icon and label showing when openReaderInNewWindow = true
- ZoteroPane.handleKeyDown does not capture events. It is not strictly
necessary now but it helps to avoid future issues such as one solved in
b15fb36f1b. One benefit from this now is
if one tabs onto a toolbarbutton that opens a menu in contextPane (e.g.
an options button in itemBox), clicks space to open the menu and then
escape to close it, this will keep focus where it was, while the capturing
listener would immediately shift the focus to the reader.
- Removed shift-tab specific case from the handler. It looks like it
was handling the shift-tab in the following scenarios:
from notes search bar (can just be handled by Tab),
from "Return to notes list button" in note editor toolbar (it's no longer there),
from a child of zotero-view-item e.g. itembox. (does not apply,
since zotero-view-item itself is a focusable scrollable area)
- use reader.focus() instead of reader.focusFirst() in tab handler,
since focusFirst() focuses the <body> of the browser and it's not interactable.
- removed all cursor-related logic. Instead, insert a new input component
each time the user wants to type. The input remain between bubbles
and focusing on one will open the reference panel. Leaving the input hides
the reference panel.
- during drag-drop reordering, lock the editor height so that it doesn't
get out of sync with the window.
- removed the iframe, since it was no longer needed.
- keyboard navigation: Home/End will place an input at the start/end
of the editor and focus it. Tab focuses the last active input if any, or
the input in the end otherwise. Shift-Tab from the editor focuses the
dropdown button if it is active. Tab from the input will focus the first
entry of the reference list. Tab from the reference list will focus the
active input. Shift-ArrowLeft/Right from focused bubble will swap the bubble
with its neighbor. ArrowDown/Up from bubble will open/close the citation dialog.
- when a reference item is selected, previously active input is re-focused.
- aria-properties to have voiceover, JAWS and NVDA read bubbles, inputs and
reference items, as well as announce hints about available keypresses.
- typing from bubble or the reference panel will refocus previously active
input
- different minor updates to make the functionality less janky
- refactoring of refreshing and resizing of the reference panel to
be more straightforward and to only do it when necessary.
E.g. clicking on a bubble and closing the
popover after will not rerun search and just display the old results.
- some throttling logic so that two escape keypresses one after another
when the itemPopover is open do not close the entire dialog
- renamed variables: qfb=dialog, qfe=editor, panel=itemPopover
- use short form of locator string for bubbles
Only stop propagation when the window is inactive, and don't fire the
synthetic event on blur. Does the same thing as efd7ec6 was intended to
do, but in a less heavy-handed way.
Fixes#3734
We weren't stopping 'change' from propagating upwards when the change hadn't
actually been committed yet, and the pane-header and abstract-box CEs
listen to that event instead of 'blur'.
In case something deleted dataset.initialValue after focus and before we
received this keypress.
Addresses #3725. Might still want to rework that listener, but this
change made sense regardless, since the same kind of race condition
could be triggered elsewhere.
- Tab from the tags list or shift-tab from the tags filter field
focuses the first non-disabled tag. If there are none, the tags are
skipped and the focus moves directly to the input field or the tags list.
- Arrow Right/Left move focus between tags skipping over disabled tags
- Space/Enter clicks on the selected tag
- Space/Enter click on the search button when focused
- brought back escape behavior that just focuses the reader whenever
a reader tab is opened, wherever it comes from (unless it's from an
element that is .open - like an item type menu, in which case just
let it get closed)
- removed moving focus from contextPane title to sync button on shift-tab
- using new reader methods onIframeTab, onToolbarShiftTab to move focus
to the contextPane on tab from reader iframe or to the sync button
on shift-tab from the toolbar
So now, the focus path is: tab bar -> tabs menu -> sync button -> reader
toolbar -> reader sidebar -> reader scrollable area -> contextPane title
(if visible) or tab bar
Appending to the menulist sort of worked - the text of the added
menuitem appeared in the menulist label, albeit off to the right -
but the item didn't actually show in the menu and didn't get removed
on subsequent calls.
Firefox restyled Linux menupopups at some point to no longer conform to
the DE defined styles for GTK widgets. This removes that custom firefox
styling and uses DE defined values instead. This is especially apparent
on Linux Unity where menupopups are dark in both light and dark themes
by default for apps.
Reasoning:
- Different Linux DEs specify different titlebar button (min, max,
close) locations which we did not and could not support before.
- Previous approach copied from Firefox depended on custom offsets
specified in CSS instead of allowing the browser engine to place
elements
Changes:
- Titlebar and menubar no longer collapsed into each other with a
negative margin, instead just a single flexbox (on macOS the titlebar
is still collapsed into the tab bar)
- Support for -moz-gtk-csd-reversed-placement to correctly place the
titlebar buttons on Linux
- Removed/changed some code copied from Firefox that does not apply or
uses the wrong selectors
- Fixed the display of Linux Unity DE buttons with icons appearing outside
of button borders
- Removed the Z in the titlebar on Linux since only a few DEs use that.
- Aligned the tabbar on Linux and Windows to the left margin of the
window.
Since ceb1dd7da3 added a tabindex on
#zotero-view-item, clicking anywhere outside a field in the pane will
blur the active field. That's an improvement, but it conflicted with our
custom label behavior - when an active field's label is clicked, we want
to blur it and keep it blurred, but the default behavior is to re-focus
it on mouseup.
Fix by preventing the default focus behavior on mousedown/click.
Since itemPane.js sets mode before item, the editable-text's value would
get reset before save() ran.
Not touching the header since we're replacing that with an item-box
field (which does not have this bug) soon anyway.
Fixes#3679
- handle space and enter in zotero pane to always click on toolbarbutton.
It ensures consistent behavior across all components to avoid Enter
triggering click on some buttons but not others.
- address the issue of focus not being able to leave the reader once it
gets there. On Escape, when reader is opened, instead of re-focusing the reader,
focus the selected tab. From there, Enter will focus the reader, and tabbing
allows you to reach the itemPane.
- Tab/Shift-tab moves focus between Sync button and itemPane when
reader is opened
Increated the ztabindex offset for non-creator rows, so that when a new
creator is added but not saved yet, there is no two elements with
the same ztabindex which would break tab-based navigation
Fixes: #3666
- Instead of setting default name as the value of the field when a
creator name is empty, set the default names as a placeholder so that
it shows up only when the field is empty.
- Have the creator options show up before the creator is saved so that
the mode can be switches before saving the creator.
- Re-do of capitalizeCreatorName to not break when one of the names is focused.
Fixes: #3610
Activate the window only when the document does not have focus.
Some platforms (macOS) require the window to be brought to the front,
while others (Linux) place it at the front from the start, and activation
should be skipped.
- min/max-lines attribute on editable-text determines how tall the field
can/has to be
- if max-lines is not specified (or is below 1), the textarea will expand
as much as needed without overflow
- removed hardcoded height and variables from css, substituted them with
usage of these attributes for consistency
- No fields have any max-lines at this point, so all fields will expand as
needed
- Don't show the default text editing context menu when right-clicking
an unfocused editable-text
- In item box/header context menus, only show Copy and Paste (plus
field-specific transform options) when unfocused
Fixes#3619
url/doi/options do not occupy space unless the row is focused or hovered.
This does not apply to options icons after multiline fields because
it would shrink textarea on hover and shift text in an awkward way.
Fixes: #3612
- Added tooltip to display the entire title of the tab on hover
- Ellipsize the text of the tab title to display as much of the last
word as possible
Fixes: #3609
On Windows, `app.update.auto` is now a per-installation pref stored in
C:\ProgramData\Zotero\Updates\[id]\update-config.json so that it can be
used by the background update service. We don't have a background update
service, so we force it to continue using the profile pref.
Field inputs are XUL in Z6, HTML in pre-redesign Z7, and XUL again in
post-redesign Z7. We switched to the title attribute for HTML but didn't
switch back to tooltipText when going back to XUL.
- after an item is added via "Add item by identifier" panel, end
focus to that item in itemTree.
- restored default behavior when the focus remains visible around an
element when all menupopups open, except for the item type menu in itemBox.
- opening item type menu will still hide the focus-ring
- right/left arrows on section header expand/collapse the sections
- space/enter on section header toggle section collapsed/expanded
- tab from the header goes through the header's buttons and then
tabs into the section if that's opened
- arrow up/down on the header jumps to the previous/next header
- space/enter on clickable elements simulate a click
- if there is a pinned section, tab from the title will focus its header
Wait for all progress-queue rows to be done processing before moving on
to the next test. Without this, preview rendering or other operations
can cause test failures by delaying the `ZoteroPane.selectItems()` call
for the new parent item in `_processItem()` until the middle of a
following test (due to the await for file renaming [1]). If it's delayed
until after the next attachment has been created, the previous parent
item will be selected after the new attachment and `recognizeSelected()`
in the test won't work. This is most pronounced with the reader, but it
was apparently happening previously due to something else, hence the
explicit item selection (now removed) in one test.
[1] 21e50add60/chrome/content/zotero/xpcom/recognizeDocument.js (L289-L301)
- properly re-focus previously focused item type menu in itemBox
- fix strange behavior of re-emerging item type menu popup caused by
itemBox.focusFirstField
- force the focus-ring to be around the itemType menu
When a menupopup is opened, the active element does not change, so
their focus-ring will be hidden until the menupopup closes for it to be
less distracting.
When a panel popup is opened, the focus will be moved inside
of the panel, so we keep track of whichever element was previously
focused, and re-focus it when the panel goes away.
Minor reorganization of focus-ring mixin to use variables instead
of parameters to make hiding of focus-ring easier.
Prevent trash/duplicated/unfiled/etc. rows from getting focused on Enter
if it is the currently selected collection row that technically
passes the filter and Enter is pressed.
Make sure that on escape, the focus is moved to collectionTree strictly
once all filtered rows are rendered. Minor refactoring to achieve
it, since setFilter is async.
Before filtering starts, save the selected row, scroll position, and
which rows were collapsed before filering, and restore them once the
filtering is over if a row was focused during filtering.
Ideally we should cache these/not recreate the menuitems every time, or
something like that, so that localization pop-in doesn't happen at all. But a
brief flash of an empty menuitem is better than a brief flash of placeholder
text.
To preserve the proper scroll behavior to top pane/pinned pane on item
change, remember which pane needs to be scrolled to when itemPane is
collapsed, and scroll to it when the itemPane is expanded.
- Remove margins and add a border to the pseudo-element.
It improves how the text of the pseudo-component matches the text from
textarea and should prevent the scrollbar from appearing due to mismatch.
- Swap explicit width setting for editable-text in itemBox for
min-width: 0. It still allows the editable-text to be shrunk when the
itemBox is narrow but does not cause mismatches between pseudo elements
and the textarea.
We don't have to deal with background images attached to various parts of the
button like we did on macOS before, so we can just flip the icon and be done
with it.
Save the child collection after removing its parent in toJSON() collectionTest.
Otherwise, getDescendents throws an error if the parent collection
tries to access the deleted child in later tests.
It refreshes customRowHeights which is used by windowed list to
calculate the right offsets.
Without this, customRowHeights remains same as before deletion, so windowed
list picks the offset based on stale data, which makes it use smaller
offset that makes headers overlap with other rows.
- always scroll to selected row on empty filter
- fixed a bug that would interfere with focusing on the selected collection
on Enter if there are capital letters in the filter field
- do not display creator buttons until hovered
- make last name have flex-grow priority over first name
- removed inline margins of rows
- removed extra padding from icons
This makes it more likely that the creator name will be displayed
fully without ellipsis until it is hovered. When the buttons are
displayed on focus or hover, the last name tries to remain
unellipsized as long as the first name is still visible after buttons
appear. If the first name is too short, both names will be ellipsized.
* Attachment icon being cut off due to box-sizing issue
* Twisty icon incorrect using color
* Dragged image being slightly wider than the item in the tree
- library tab appears in the tabs menu without the close button
and no drag functionality
- minor refactoring to simplify how the tabs icons are fetched for
items with item.getItemTypeIconName()
- added focus-ring to tabs and filter field via @focus-ring mixin
so that the focus outline looks the same for the input field and buttons
- removed default margin from the <description> component of the tab
titles that moved titles up by a bit
- make sure the visible zotero-pane tab is selected to focus on
- make sure lastFocusedElement is not set to be another tab or
<window> to not move focus on them when Enter is pressed on zotero-pane
tab
- refocus the lastFocusedElement from zoteroPane after a timeout
to make sure focus does not stay on the actual tab after mouse click
- save lastFocusedElement before closing and reopening and unloaded reader
tab so that if we move from zoteroPane to a reader tab that will be loaded
and then go back, the orignially focused element is refocused
- fixed creator names text selection glitch (hide the comma instead
of not displaying it + removed redundant value update that's no longer
needed)
- set selection direction in editableText on tab to not scroll to the
end of input
- added autocomplete to observedAttributes of editable-text to
properly add autocomplete even if autocomplete params are set after
the element has been rendered (which is needed for creator names). This
fixes wrong Esc behavior that erases autocomplete fields instead of
resetting to previous value.
- fixed creator names autocomplete and type switch regression after
the comma was removed
Now it's no worse than it was pre-redesign. But there's still
room for improvement, either by preventing the window from shrinking to the
point that the sidebars overflow or by making the sidebars horizontally
scrollable at very small window sizes.
- tabs menu button is disabled when no reader tabs are opened
- tabs menu popup will be hidden if all tabs are closed
- keypress event handling moved from tabBar.jsx to zoteroPane.js
because all other keyboard navigation events are handled there
and it already has the functionality to skip disabled or hidden components
(e.g. tabs menu button, sync error)
- minor tweaks to tests to wait for collection search bar to hide
to get keyboard navigation tests passing
The previous workaround to ensure the twisty animation plays caused an issue
where, in some cases, tree item entries became unselectable.
Instead of skipping the rendering of the entire row (when it has been marked as
just toggled), we are now forcing the toggle animation to play after the row has
been re-rendered.
Additionally, one case where the just-toggled state wasn't cleared was fixed.
Previously, when you clicked the Add button, typed a tag, and pressed Enter, the
new tag would be added *after* the now-empty editor field, putting the editor at
the top rather than the bottom.
We previously switched to just Add File and New Note buttons, but people
are going to want to create child attachments/notes via the toolbar.
This adds menus for those two buttons with clearer wording.
Also:
- Update the File menu with options that correspond to each of the four
items pane toolbar buttons (including a new menu option for Add by
Identifier)
- Update the order and wording of the Add Attachment submenu in the
items list context menu
- Remove File menu options from the reader view that were added
accidentally during the toolbar redesign
We were originally going to put it in the File menu, and its disabled
state was only being updated when the File menu was opened after
selecting an item instead of the Tools menu. But we shouldn't actually
disable it anyway, only its contents (which we're doing elsewhere), so
just take this out.
Regression from new item pane, since we were using the tab labels for
the column labels. The real fix here is to support Fluent strings for
tree columns, but that's more complicated.
- Table structure rewrite to use grid layout instead
of the <table> component so that screen readers can see the content
- Added icons to mirror right-click actions
- Moved creator actions to the options menu
- Drag-drop to reorder creators
- Using editable-text instead of clicky component
- Consolidated autocomplete logic in one function
- Added @focus-ring to all components of the itemBox
- Fields are focusable and navigatable via keyboard in non-edit modes
- General refactoring to consolidate stylesheets across platforms
and remove code that's not more used (mainly related to handling old
clicky text component).
- Retractions panel background set for --material-background instead
of light pink in dark mode.
- Added nowrap attribute to not add the stretching aspect
of the editable-text and to not have any text wrapping. To be used in cases when
the input is a single line and it's width is stretched with flexbox, for
example tags or itemBox value fields.
- Added 'tight' attribute to set lower padding values on editable-text.
To be applied on fields that are not as prominent as abstract or header
as it makes the actual editable-text component smaller.
- Minor style changes to not shift layout on focus on windows (using
transparent border instead of 0 margin).
- No overflow of textarea to avoid longer-than-needed textareas on
windows.
- Keep track if the component was focused on via mouse click or not.
If the focus happened without prior mouse click, select all text.
- Reset cursor and selection on blur.
- Make sure the .input is always re-added if it disappears after drag-drop.
- Added sizeToContent function to set max-width based on the width
of the input.
- Allow the browser to break up words more freely with overflow-wrap: anywhere
to avoid stretching the itemBox with extra long header without spaces.
- Apply 'nowrap' and 'tight' to tags so that a long tag does not stretch
the itemBox or make the editable-text extra tall.
- Speed up transition to 0.2s
- Focus once the transition is fully done
- The actual input field is hidden and the button is displayed 50 milliseconds
after the field starts shrinking to not have the magnifying glass
appear to the right of the input field right before the input is
hidden.
- Matching substrings from tab's title are bolded.
- Drag-and-drop functionality to reorder tabs.
- ArrowUp/ArrowDown navigate the tab's titles. Tab/Shift-Tab move
across both titles and close buttons. Enter from filter field focuses
the first tab's title. Home/PageUp focuses the filter field. End/PageDown
focuses the last tab's title. ArrowUp/ArrowDown from the filter focus
the last/first tab's title respectively. ArrowUp from the first tab or
ArrowDown from the last tab focus the filter.
New icons and new scaffold.scss go into chrome://zotero/skin. The separate root
for Scaffold is mostly a historical relic, and adding special cases to build
scripts, SCSS mixins, and so on would just make things complicated.
While looking for the first matching row to select, stop looking
and move focus to the tree only after the selection succeeded.
Some matching rows, like groups header, are not selectable. If it's the
first matching row, it will now be skipped.
* After 02b864d icon next to the filter icon in the tab selector has
gone missing. This is because the chevron-6 has been moved to a new
location
* Tweaked CSS for this and other universal icons to use new location
* Removed duplicate icons in the old location
- Actually show un-deleted rows as context rows within trash
- Add all rows, hide ones that shouldn't be shown
- Check whether attachment is file before calling getAnnotations()
- Fixes errors on update and new link attachments being shown as empty rows
In d4f8c7c an entry has been removed from zotero.properties that is still being
used in items table. If `numNotes` column is visible, items table would not
render.
Instead of re-adding to zotero.properties, I've migrated this string to
zotero.ftl to fix this problem.
* Render a colored dot for colored tags
* Improve spacing and positioning, especially on 2x screens
* Add scss map for tag colors in light/dark scheme
* Add support for compact/comfortable in tag selector
* Restyle filter box, add a placeholder and a new icon
1. Swap focus direction for ArrowUp and ArrowDown events when applicable.
ArrowUp = Shift-Tab, ArrowDown - Tab.
2. Remove ArrowLeft, ArrowRight event listeners from quicksearch menus
to not prevent cursor navigation within the actual text field.
3. Tweaks to fix advanced search mode from quicksearch when " is typed.
.icon-item-type is now global, with extra styling to handle row selection when
it's inside the item tree.
getImageSrc() is still used for progress windows, which is probably OK for now
because those don't stay on screen for very long. I've updated it to return SVGs
based on the current UI color scheme.
Other code uses treeitem-* images directly; getting rid of those references will
cause a lot of conflicts and be a bit more involved than this initial pass, so
I'll put that in a separate PR.
1. Toolbarbuttons changes:
1. “New Library” moved from toolbar to File menu
2. “New note” on click makes a standalone note
3. “New attachment” on click adds stored copy of a file
4. Menuitems from old note or attachment menus moved to the File menu
5. “Advanced search” removed. Sync and Locate buttons moved to the top-right corner (Locate - temporarily)
6. Added “Opened tabs” toolbarbutton
7. Updated tooltips
2. Added a toolbarbutton that reveals collection search field on click. Search field is hidden on blur if empty. Keyboard accessibility via tab/shift-tab.
3. Tab based navigation restructuring. For each component, create a mapping of target ids with desired focus destination for each relevant keyboard event and use that to determine where to move focus next. Fixed bugs with focus wraparound not working when a note or attachment is opened in contextMenu.
4. Make quick-search dropmarker a sibling of the textfield. QuickSearchTextbox does not extend search-textfield anymore. It serves as a container to house dropmarker and textfield as siblings. This is needed to remove focus ring from quick search textbook when focus moves onto the dropmarker.
5. Separate toolbars for item and collection trees. Removed unused code manually setting the width of toolbar section
6. Focusable tabs + keyboard navigation:
1. Shift-tab from opened tabs menu moves focus to the currently opened tab
2. When a tab has focus, use left/right arrows to select tabs, or CMD/Ctrl + arrows to move focus between tabs
3. Enter or Space on a focused tab will select it if needed and move focus to the contentPane of the reader of this tab
4. Shift-tab from tabs wraps focus around to itemTree or itemPane
7. “New collection” creates collection contextually within currently selected library or collection.
8. Items pane minimum width increase to avoid quicksearch from being squashed
9. Do not move focus to title field of itemBox after itemTypeMenu is closed with ESC.
10. Display all itemTypes without "Show more" submenu in "New Item" meunu. Removed "Store Copy of File" and "Link to File" from "New Item" menu.
- new icons
- new twisty (also for items tree)
- new mixin focus-states that can be re-used for less boilerplate
- dark/white compatible with tweaks for when tree is focused (these changes also affect items tree)
- removed macOS specific behaviour (blue-ish background, gradient for selected row)
- border-radius on highlight + spacing tweaks
- tweaked colors and fonts
- Support for "compact"/"comfortable"
Icons preloading introduced in 247755c5 overrides entire background
property, not just background-image. On 2x screens (and anywhere else
where svg icons need to be scaled) icons need property `background-size:
contain` to be displayed correctly. This previously came from
`.icon-css` class but have been overriden by code generated in
`svgicon`, so to fix this, these properties need to be added to the
`svgicon`.
* Opt-in for light and dark color schemes
* Convert scss variables in _light.scss to css variables
* Add _dark.scss for dark color scheme
* Remove unused, theme-related variables
This new XPCOM module handles font size and UI density and automatically keeps
registered roots up to date when prefs change. Roots receive a
UIPropertiesChanged event after their properties are updated.
- Sort the Open Documents section by reverse-open order and further by
reverse tab order (if unopened in this session).
- If Library is selected in the Zotero window, automatically show and
filter at the top selected items
- Currently enabled only for ScienceDirect. Can be enabled via a whitelist
- Matches the HiddenBrowser loaded HTML page for a captcha element. If
the captcha element class changes, this will break (but the
alternative is potentially displaying a captcha clearing window when
something else that is not a captcha guard is loaded).
- Captcha clear timeout for 60s.
- Doesn't automatically switch focus back to the browser which intiated
the item save via the Connector.
- Stores the cookies used to clear the captcha for future saves from the
same domain. Discards Connector supplied User Agent, since CF bot
detector checks UA header against actual UA behavior like TLS handshake
and if the UA acts different to what it's supposed to, the bot
challenge is not cleared.
Other changes:
- Adjusted the cookie sandbox to allow multiple cookie sandboxes to be
active (and simplified some legacy code that was meant to cover a bug
in old FX codebase).
- HiddenBrowser API changed to be Object oriented, translator tester
in the translate repo will need to be updated after a merge (have the
change ready).
- Improved Connector Server attachment progress handling
This is needed to let itemBox, tagBox, etc. to handle ESC
events to revent any edits made to textfields. Only applied
if the currently focused element is input or textarea.
Fixes: #3246
1. Trigger ZoteroPane.handleBlur on focusout instead of blur
to remove highlight when itemTree entries loose focus
even if ctrl/alt is still being held.
2. Prevent default event on Alt click on linux to avoid moving
focus to menubar
3. Avoid re-triggering focus multiple times
when ctrl is being held on windows to prevent highlight from becoming
stuck and not going away.
Fixes: zotero#3410
- The underlying changes are in windowed-list, which the item tree and
all virtualized tables are based on, so if there are bugs they might
show up outside of the collection tree.
- The expensive operation is adding/removing rows, since row offsets
have to be recalculated (this includes collapsing/expanding rows).
- The cost on further drawing while scrolling is constant and shouldn't
affect performance much.
Without the /u (Unicode) flag, [\ud800-\udfff] matches *all* surrogate pair code
points, even when they're in a valid position in the string.
Fixes#3421
Some pages (notably the first DOI.js test case) never get to "complete". I don't
know why! But for practically all translators that don't use defer: true,
"interactive" is ready enough.
Amends 30c70a6ecd.
On some machines, the callback added in that change would never actually run, so
memory usage would grow quickly when switching between tabs. By wrapping in
setTimeout() instead of requestIdleCallback(), we keep the performance gains
from removing once the element is no longer visible (so we can avoid repaints,
rebuilding the layout tree, etc.), but we make sure that the callback gets
called nearly immediately.
#3321
It's now possible to tab through the panes and the buttons, and
left/right-arrow also change the selection, so you can use Left/Right +
Return to move through multiple conflicts.
Fixes#3395
In Z7 on Windows 10 (but not 11 for me), nsIZipReader doesn't properly
close the file after `findEntries()` is called (as discovered by
@abaevbog), so a `remove()` on the downloaded ZIP file during file
syncing triggers an access-denied error. Setting the zip-reader variable
to null and forcing garbage collection seems to fix it. Doing this
everywhere we use nsIZipReader just to be safe.
I found the `forceGC()` in only one test file in fx102, but setting the
reader to null is done more widely, so maybe they just don't try to
delete ZIP files before GC happens and manage to avoid this bug.
Fixes#3369
Prevents flashes of unlocalized labels and controls without values set.
Makes switching panes feel speedier overall because of preloading.
I thought there was an issue for the flashes of uninitialized content but can't
find it now.
- Prevent row height from changing when switching between text input conditions
and menulist conditions
- Replace some of (far from all of) the XUL layout with flexbox
- Restore groupbox appearance
- A little retro? We probably want some kind of border like we used to have
- Replace some duplicate IDs with classes for styling
Improves EPUB close performance.
By wrapping with requestIdleCallback(), we ensure:
1. remove() won't be called right away, so the DOM elements being removed
won't be visible and won't trigger a relayout.
2. remove() won't be called while there are pending UI events, so it
shouldn't cause the UI to hang/lag/stutter.
Combined, these two improvements make the UI hang when closing a large EPUB
mostly imperceptible on my machine.
Addresses #3321; doesn't completely fix it because there's likely more to be
done on the reader side to optimize the DOM.
Fixes various logic around what gets selected when collections and
searches are moved to or restored from the trash (which has never been
exposed) or when they're erased
- Move open-in prefs into reader prefs
- Move reader prefs up below File Handling
- Move new strings to Fluent
- Fix clicking on labels to focus open-in drop-downs
- Tweak text
- Need to set width/height for macOS native menu
- CSS named grays are too light to show well on the background, so use #555
- type="checkbox" menuitems don't show their icons, so use the icon stroke
instead
- Move file-renaming prefs to separate prefs section
- Fix template preview not updating on paste
- Update documentation URL
- Tweak text, styling, and ids
- Change "Automatically rename attachment files using parent metadata"
to "Automatically rename locally added files" to reflect that
downloaded files are always renamed, and add an intro saying so
Now that Scaffold remembers and automatically loads the last edited translator
(65048fd624), it needs to be easier to create a
new translator without saving (and bumping the lastModified date on) the
translator you had open.
And actually return -1 if it returns false.
Before this fix, attempting to rename an attachment file to a name that already
exists on disk would never return -1 as the docs say it should. Instead:
1. rename() would fail and return false
2. newName would be set to false
3. renameAttachmentFile() would pass false as the second argument to
OS.Path.join()
4. OS.Path.join() would ignore it because it was falsy and return the attachment
directory path without any modification
5. relinkAttachmentFile() would be called with path set to the attachment
directory
6. relinkAttachmentFile() would notice that path's dirname wasn't the attachment
directory - it was the attachment directory's parent - and attempt to copy it
and its contents, recursively, into itself, using copyToFollowingLinks()
...which created a directory structure on disk over 100 directories deep -
not deeper only because the OS started returning errors due to paths exceeding
32,767 characters (the limit on my filesystem).
Passing unformatted = true to Item#getField() now returns a bidi control
character-less result, and we use that in Reader#updateTitle() and
getFileBaseNameFromItem() to prevent bidi control characters from showing up in
filenames and window titles (the former everywhere, the latter on Windows only).
We also strip bidi control characters in getValidFileName() to be extra safe.
- Fix missing styling in Quick Format dialog
- Fix Book Section panel being immediately hidden
- Remove low-res Zotero icon
- Increase font size and tweak padding
- Call `shutdown()`/`uninstall()` with `ADDON_UPGRADE`/`ADDON_DOWNGRADE`
during plugin upgrade/downgrade
- Actually call new version's bootstrap.js, not cached old version
- Create new scope for new version
- Don't call `shutdown()` on uninstall if not active
Fixes#3159
By creating our Sandbox under the system principal and giving it privileged XHR.
Bonus: no more `wrappedJSObject` required because sandbox code and actor code
are at the same privilege level now.
Fixes#3176
- Set min-width and min-height to width and height so buttons never shrink
- Remove defunct .zotero-clicky-* styles from 2x block in zotero.css
- Include zotero-platform/content/zotero.css in searchDialog.xhtml
- It seems only the defunct 2x styles from zotero.css were being applied, so
the buttons would have been unstyled on a non-hiDPI display
When you uninstall a plugin through the UI, XPIInstall:
1. Sets the plugin's `pendingUninstall` to true
2. Calls our onUninstalling() method
3. Waits for the Add-ons window to be closed
4. Actually uninstalls the plugin
5. Calls our onUninstalled() method
If you undo the uninstallation between steps 2 and 3, the remaining steps
instead look like:
3. Sets the plugin's `pendingUninstall` to false
4. Calls our onOperationCancelled() method
This commit changes our implementation of the bootstrapped plugin lifecycle so
that the shutdown and uninstall hooks are called from onUninstalling() (step 2).
If you close the Add-ons window without undoing, nothing more happens. The
plugin remains uninstalled. If you undo before closing, though, we call the
plugin's lifestyle hooks just as if it had been newly installed (unless it was
disabled before uninstallation, in which case we call install but not startup).
This mirrors the behavior of Firefox WebExtensions and makes things work more
like you'd expect: uninstalling a plugin immediately deactivates it, and undoing
activates it again.
- Fix mistaken reference to event.target (instead of currentTarget) in sync
function
- Move sync functions to fields for easier debugging - can't set a breakpoint
inside an inner function in the Firefox debugger
Fixes#3142
- Use locale directories for JSON files, since that's where the
Transifex client will interact with them
- Skip non-locale directories (e.g., don't create an .ftl file for
.DS_Store)
- Other minor simplification
- Switch to Mozilla's Timer.jsm for timer functions in XPCOM scope
- Add setInterval/clearInterval/requestIdleCallback/cancelIdleCallback
- Add all timer functions to plugins sandbox
Firefox has switched on all platforms, Chrome and Edge on Windows use
"Settings", and we really don't want to have to say different things for
different platforms
ScienceDirect sometimes puts the `name` directive at the end of the Content-Type
header instead of in Content-Disposition. That isn't strictly spec-approved, but
there are other directives (`charset` and `boundary`) that can also be appended
to Content-Type per the spec. We want to strip them before looking for handlers.
https://forums.zotero.org/discussion/105194/sciencedirect-pdf-downloads-not-working-zotero-7
This was triggering an erroneous warning dialog about a failure to check
for Firefox profiles during Linux tests (where the profile is at
something like /tmp/tmp.l5phnqSxBH/Zotero), but it could also affect a
custom profile directory location.
- Set zotero-noteQuickCopy-menu's preference attribute to the correct key
- Warn about all ID-ish preference attribute values
- zotero-noteQuickCopy-menu's preference attribute was being set to the ID of
a now-nonexistent <preference> element. preference attribute values should
be preference keys now, but we were only warning if the associated
<preference> element was actually there
- We can't warn in all cases where the preference doesn't yet exist, because
some preferences don't have default values, and we shouldn't limit to
preferences that don't exist, because then the warning will stop showing
after the preference is persisted once
- When a <preference> ID is replaced by the associated key, update the
preference attribute so future syncFromPref() and syncToPrefOnModify() calls
will set the correct preference
- Listen to a range of events on all bound nodes, no matter their type
- Don't resolve _firstPaneLoadDeferred until actually done loading
Fixes#3131.
In Firefox 102, `-file` was being swallowed by `nsBrowserContentHandler`
in BrowserContentHandler.jsm, so ab7d916e0 overrode that by using the
same contract id for our main nsICommandLineHandler in
zotero-service.js. But `nsBrowserContentHandler` also handles `-chrome`,
which we were using in tests to pass runtests.html. There's no need to
pass in dynamically though -- we can just hard-code that and open the
window ourselves.
To run a short-lived command and return stdout
The Subprocess module can also start long-running process and
communicate with them, but we'll implement something different for that
if we need it.
Updating via the service seems to actually work (or at least it logs
that it's going to use it and the update succeeds -- I didn't verify
that it actually uses the service), but conditionally using a Mozilla
background service only if it's installed on the system seems like a bad
idea.
The old updater used MARs with bzip2-compressed files, but Mozilla switched to
xz compression, so we generate xz-based MARs using the current MAR tools and
`make_(full|incremental)_update.sh` scripts and add a script to generate
bzip2-based MARs to serve to pre-7.0 builds.
We were previously using an old version of the Mozilla updater before they
added Mozilla signature verification, but it's Intel-only, so we need to build
our own version of the current updater with signature verification disabled.
(And update the en-US version with some strings that we don't use but
might if we updated to a slightly newer version of the updater before
they got rid of the dedicated update window)
- Properly handle regular vs. Mozilla locale files
- Avoid whitespaces changes as much as possible
- Keep copyright block at top of all Mozilla files
- Bug fixes
Set MOZ_LEGACY_PROFILES environment variable to disable
dedicated-profile support, which creates dedicated profiles for
different channels (e.g., `esr`).
If the file wasn't an XPI at all, or it didn't contain valid metadata for fx102,
the error message would previously show "%S" where the add-on name should be.
Now we fall back to the file path.
So a custom build doesn't have to modify each .ftl file
`app-name` is redundant with the Firefox strings, but it's what we used
previously and is easier to remember.
`nsIWebProgressListener.onStateChange()` gets called twice at the end of
requests, once with `stateFlags` set to `327696` and once with it set to
`262160`, which corresponds to `STATE_STOP + STATE_IS_NETWORK +
STATE_IS_REQUEST` and `STATE_STOP + STATE_IS_NETWORK`. httpd.js debug
logging shows that the connection is closed between the two calls. In
WebProgressFinishListener, we were previously calling `onFinish` after
the first one, but in Zotero 7, at least on Linux (or maybe just on
slower machines due to a race condition), the file from `saveURI()`
doesn't appear to be reliably written after the first call, causing
`Attachments.downloadFile()` to fail in `_enforcePDF()` due to an empty
file.
This changes WebProgressFinishListener to wait until the second
`STATE_STOP` call. We'll have to confirm whether this is the
state-change pattern for all requests, but it fixes our Find Available
PDF tests in CI.
https://firefox-source-docs.mozilla.org/dom/ioutils_migration.html
This also fixes a bug when `getContentsAsync()` is passed an
`nsIInputStream` or `nsIChannel` where raw bytes were returned instead
of a string. Not sure if we're doing that anywhere. If we are, this
would presumably break that code, but the function is supposed to return
a decoded string.
From some 2017-era langpacks I had downloaded. These strings aren't in
current langpacks, which use a different update mechanism, but we're
bundling a modified version of updates.xhtml from Firefox.
All declared Fluent files need to exist for a locale to be used (in a
window?). Since Mozilla code tries to load Fluent files, we need to copy
non-English Mozilla .ftl files to their default effective path (just in
the app omni.ja instead of the toolkit omni.ja).
Fixes#3094
https://forums.zotero.org/discussion/104431/syncing-problem
Replace Online Library can upload annotations created by others in a
group library, so if the upload resulted in a local write, "Cannot edit
item in library" was thrown, since annotations by others aren't
writable. This should've only been a problem if the uploaded data was
actually modified by the server, but we were also checking whether
objects were editable before checking if they had actually changed, so
it would happen for any upload of another person's annotation.
This fixes the order of checks when saving objects and makes an
edit-check exception for saving uploaded data for group annotations.
- Support removeHandler()
- Return DB items from translate() when called with libraryID
- Don't invoke done until attachments are done
Fixes#3084 ("after all" hook still times out, but that happens even if the test
is disabled)
HTML files are now indexed instead of read directly, and indexing was
previous skipped in tests and otherwise performed on a delay, so set a
flag in the affected tests that triggers inline indexing.
Having a single customElements.js file that we import everywhere we need it
helps with organization, and it gives us a single place to put things like the
<tab> fix.
We could switch to using setElementCreationCallback() like Firefox if the number
of imports gets out of hand, but the overhead right now should be small.
Prevents bug in zotero-citation plugin (at least on macOS) from creating
a search that breaks syncing
We were already checking for a missing name in `saveTx()`, but the
plugin is saving the same search twice in rapid succession, the second
time without a name, and the second attempt clears the search object's
name value after the first save's `_initSave()` check and before its SQL
write. The second save fails, but the first save goes through without a
name, resulting in a sync error.
https://forums.zotero.org/discussion/104274/id-1702002152-cannot-synchttps://github.com/MuiseDestiny/zotero-citation/issues/31
It seems that the issue wasn't that zotero:// URLs can't be loaded in a content
browser, but rather that the report extension was returning a channel that the
content browser couldn't access. For some reason, it handled that failure by
passing the URL off to the OS, which then opened a duplicate instance of Zotero.
Also:
- Remove ensureBrowserType() and always use <browser type="content"> in
basicViewer (see b8966f)
- Fix system principal being used to load extensions without `loadAsChrome` set
to true if an extension with `loadAsChrome` set to true had been loaded in the
past
It does not appear to be possible to create a creator with no values in
Mendeley, however we got reports of these causing the imports to fail.
This tweak makes the importer more resilient by discarding empty/invalid
creators.
- Show "More information" link next to relink option
- Automatically check relink option when coming from integration prompt
- Change done message to "[x] items were relinked" instead of "0 items
were imported"
New option only appears if importer version is < 1 or not present.
It will:
* Skip fetching collections and attachments
* Skip any new items
* Update relations on existing items
* Importer will now ask user for a login and password via form and will
perform sign-in directly using credentials rather than oauth
* Signing in this way enables importer to obtain desktop document ID
which is now stored for each item
* It's possible to switch back to the old method (ouath) by setting
`import.mendeleyUseOAuth` pref to `true`.
* New option to only import new items. This options only appears if
database contains previously imported items.
* Importer will now update mendeleyDB:documentUUID on existing items to
match value used in Mendeley Desktop if available
* Importer will no longer create collections when no new items are
imported * Importer will only report number of new items imported on
re-import * Importer will now preserve dateAdded on re-import
Co-authored-by: Dan Stillman <dstillman@zotero.org>
- Windows global styles make .header bold, so we'll override them
- Prefix IDs in <notes-box>
- Give <tags-box> Add button a normal margin
- I think the CSS this removes was meant to apply to tags in the list, not the
Add button, but that's handled elsewhere now
Follow-up to 6b819e259c: fixes element-specific
selectors mistakenly being applied globally, and some selectors not having the
intended effect because of lower specificity.
Now all included in `itemFields.`
We still use base-only fields as search conditions, and schema.json
consumers might want to do so as well, so it makes sense to include
them.
This fixes an error using Everything if another library hasn't yet been
loaded and should speed up Everything searches for people with multiple
libraries.
This is sort of an awkward fix, because it doesn't fix the underlying
problem of `addCondition('libraryID', ...)` not working the same as
`.libraryID =` for some search conditions that perform subsearches. But
supporting `addCondition('libraryID', ...)` for those would get
complicated, because there could technically be multiple such
conditions, and applying those to a subsearch that used `AND` would get
messy. So let's just fix the problem at hand.
Fixes#3032
Or at least a way that we already have built-in and that only applies to
the call in `Zotero.Item::migrateExtraFields()`. This doesn't
distinguish between CSL fields (`publisher-place`, `event-place`,
`issued`) and actual Zotero field, and we really only need to skip
the former, but it's fine.
Follow-up to e3cfeee81, related to #3030
Removes a huge amount of excessive files and duplication for CE scss.
All CE css is now output in the single
zotero-react-client.css file.
Moving all CE styling into a single stylesheet required removing their
shadow DOMs. It is desirable anyway, since you want to
be able to style CEs from "outside", when embedding in different
contexts.
Shadow removal required some CE code changes to maintain
functionality.
Elements refactored:
- attachment-box (displayed when an attachment (like PDF) is selected in
the item tree)
- color-picker (in the tag color selector)
- guidance-panel (displayed on first run when editing authors for a book
section)
- item-box (info tab in the item pane)
- note-editor
- notes-box (note tab in the item pane)
- quick-search-textbox
- related-box (related tab in the item pane)
- tags-box (tags tab in the item pane)
- zoterosearch (advanced search condition builder form)
Setting a height/max-height was causing the window not to grow correctly when
rows were added. Whatever the purpose of those styles was in past FF versions,
they're no longer necessary now.
cfbfcf2 changed restore{To,From}Server to restore{To,From}OnlineLibrary and
updated most of the references, but not the ones in preferences_sync_reset.
Other languages still have those strings under the old key names.
- Add 'x' accesskey for "Exit" on Windows
- Show "Quit" instead of "Exit" on Linux
- Don't show Ctrl-Q for shortcut key on Windows
But Ctrl-Q still works on Windows when it shouldn't because of the Mac
keybinding, which still gets registered even if disabled
programmatically in platformKeys.js.
fx102 follow-up to #3010
Well this was a wild one to debug.
Creator fields were only initialized for autocomplete due to a series of
>10-year-old bugs:
1) In `showEditor()`, `Zotero.ItemFields.isAutocompleteField(fieldName)`
was called for creator fields, which would pass, e.g.,
`creator-0-lastName`.
2) In `isAutocompleteField()`, `ItemFields.getName()` would normalize
`creator-0-lastName` to `false`, since it's not a valid field.
3) `isAutocompleteField()` listed `place` as a base field despite its
not having any mapped fields, so when `getTypeFieldsFromBase()` was
called on it, the return value would be `false`, which would be added
to the list of autocomplete fields, which would mean that the
normalized field of `false` from `creator-0-lastName` would match,
which would mean that `isAutocompleteField('creator-0-lastName')`
would always return true...as long as `place` never gained a mapped
field.
Except `isAutocompleteField()` wasn't supposed to be the test for
initializing autocomplete for creator fields anyway -- `fieldName ==
'creator'` was. But `fieldName` is something like `creator-0-lastName`,
not `creator`, which meant that that test always failed, which meant
that if `place` did gain a mapped field, both tests would fail, which
would cause the creator field not to be initialized for autocomplete,
which would cause it to break as soon as you started to type into it.
This fixes that.
Panes were being loaded with Zotero_Preferences as their global scope, so global
variables they defined would be set on that object instead of the window.
The file should follow the same `pref("extensions.foo.bar", "value");`
syntax as files previously in defaults/preferences/, which should no
longer be used in Zotero 7. (For an extension that works in both Zotero
6 and 7, it's OK to have a file in defaults/preferences for Zotero 6 and
an identical prefs.js for Zotero 7.)
Files in defaults/preferences/ aren't automatically loaded when a plugin
is installed/enabled but are still loaded at app startup by Mozilla code
for now. Plugins shouldn't count on that continuing to be the case in
Zotero 7 and should switch to prefs.js.
We'll add a way for bootstrapped plugins to manually trigger reading of
a prefs.js file in Zotero 6.
Using the Headers class from the Fetch API.
Before, the added test would fail: `_requestInternal()`, not finding a header
named `Content-Type` (case sensitive), would set it to
`application/x-www-form-urlencoded`. XMLHttpRequest, upon being given both
`content-type`: `application/json`) and `Content-Type`:
`application/x-www-form-urlencoded`, would helpfully merge the two, producing
`content-type`: `application/json, application/x-www-form-urlencoded`. That's
obviously not the correct behavior.
Limited to a hard-coded list
Initially limited to `extensions.zotero.import.mendeleyUseOAuth`,
to switch the Mendeley importer from direct login to OAuth
Services.locale always wants an array now, but it can be empty.
We still get "Uncaught (in promise) undefined" printed to the console, no stack,
not caught by "Pause on exceptions" in the debugger... but it's not clear that
that's actually coming from this code, and it doesn't seem to prevent anything
from working.
- Don't rely on Zotero.hiDPISuffix being initialized in menuToolbarbutton.js --
it probably hasn't been at the time that the code that creates the dropmarker
is running
- Fix merge mistake that created a duplicate block of CSS
- Render cell text in its native direction
- Fix context menu positioning
- Fix item box (localizations needed)
- Fix column resizing
- Fix bidi text in collection tree
- Always right-align in RTL, always left-align in LTR.
I'm going off advice from this excellent guide for RTL website design
by Ahmad Shadeed: https://rtlstyling.com/posts/rtl-styling#tables
- Join creators in the tree ("Smith and Jones") using a format string to
support languages like Arabic and Hebrew where there shouldn't be a
space after the "and".
- Fix tabs
- Fix toolbar on Mac, flip icons on other platforms
When enabled:
- Double-clicking a PDF or choosing "Open PDF" opens a new window
- Shift-double-clicking opens a new tab
- "Open in New Window" locate option becomes "Open in New Tab" and has the
reverse behavior
Previously cookies only got attached on the initial request but not on
any redirect and subsequent request. This may have been the cause for
many reports of import failures behind proxies in the past.
Technically this code has been wrong for a long time, but it only
manifest now. It seems that the equivalent code in translate module
was fixed when that code was extracted into a separate repo
- Correct padding and don't use Mozilla preferences styles
- Fixed v-t issue that applied the wrong class names to columns, giving them
incorrect widths
In this case, Note Markdown wouldn't be preloaded, but it's always used
for copying from the note editor, and since the note editor uses
`noWait`, this would result in "Code promise is not resolved in noWait
mode".
Zotero.Translators.reinit() didn't reinit Quick Copy, so if an export
translator was selected it wouldn't be preloaded again and dragging
would fail with "Code promise is not resolved in noWait mode".
Find Available PDF has its own domain-based retry logic that predated
automatic 5xx retries in Zotero.HTTP, so disable the latter and fix some
bugs in the former.
Fixes#2700
Also:
- Hide format options until translators are loaded (since otherwise
the checkboxes don't have labels and the layout is messed up)
- Remove unnecessary "zotero-" prefix from the new elements
The default JSON pref value used spaces, but changing the drop-down
resulted in a version without spaces, so simply changing the default to
include the new link options wouldn't change all existing installs and
the "Include App Links" checkbox wouldn't show as enabled for Markdown.
This adds a pref migration step to reset the pref to the new default if
it's set to Markdown + Rich Text, regardless of whitespace differences.
And just fully reload the preferences window when the pane array changes. The
preferences window won't often be open when a plugin is enabled or disabled,
considering that the Add-ons window is external to the preferences, and trying
to add/remove panes dynamically without reloading brings lots of bugs.
(No way to dynamically remove a script, for one.)
People are confused by "Zotero Connector Server is Available". I'm not
sure if this will be any better, but not mentioning the connector might
help -- that seems to be part of the problem.
We could display a longer message with a link to
https://www.zotero.org/support/kb/connector_zotero_unavailable, but this
is the /ping page that gets hit constantly, so it seems like we should
keep it short (though probably it doesn't make a difference).
- Word for Windows not tested since there's no build yet
- Word for Mac currently causes a sigsev, and likely we'll see this on
windows too. This is happening on an API call where we pass a callback,
so likely an issue in ctypes that we'll have to work around.
- All integration UIs restored.
- Added a component for richlistitems with a checkbox with corresponding
interactions (spacebar/double-click to toggle).
Fixes the issue when your own created annotation becomes grayish and indicates that it was modified by another user.
(cherry picked from commit 4dd1851140)
Having the macOS Help button in a scrolling view feels kind of weird, but that's
what Apple is doing in the Ventura System Settings app, so I guess we go with
it. I like it better than Firefox's non-contextual "Firefox Support" button
under the pane list in their preferences, anyway - having pane-by-pane
contextual help buttons seems like good UX that there isn't a good reason for us
to ditch.
- The protocol can no longer be marked "dangerous to load," only "UI resource"
(accessible inside browsers but not by web pages).
- The protocol needs to run in the main process.
- We need to replace the XUL browser to reset its type attribute depending on
whether we're loading a zotero protocol URI - zotero protocol URIs, maybe due
to the protocol handler's tight coupling with the main process, cannot load in
type="content" browsers.
To allow creation of a standalone note with annotations from all the
selected top-level items and/or attachments. Annotations will be sorted
by the order of the items in the items list.
"Add Note from Annotations" remains when a single regular item or one or
more attachments under a single regular item are selected.
So that it works for more than two levels of items
Also fix a bug where the parent item could be returned more than once if
multiple child items were selected.
Built-in panes were creating a semi-invalid DOM in which the pane container and
the root element of the pane's XHTML got the same ID. There's no reason to give
containers IDs in the DOM, since we already have a Map of IDs to pane objects,
so let's just not.
- Fix sync and advanced preferences tests; use a new waitForFirstPaneLoad()
functions instead of the old paneload event listener
- Remove empty preferences_searchTest.js
- Rename some Zotero_Preferences members/functions for better clarity and
public/private differentiation
- Reorder, also for clarity
- Fix tabIndex parameter causing an error if invalid
- Remove window.sizeToContent() call in Sync.displayFields()
- So *that's* why the window resized every time the sync pane was loaded...
- Deprecate openURL() and simplify openInViewer()
A manual pane ID isn't often necessary, since you can just give a manual ID to
the root element in your XHTML. Let's make things a bit simpler for the caller
and reduce the potential for conflicts.
There were a few problems causing the incorrect behavior:
1. Rows were being removed only if they had no non-deleted children, which
wasn't the right check. We want to remove all rows with no *deleted*
children.
2. Children of the removed rows weren't being removed with them.
3. We weren't invalidating the tree (which _removeRows() doesn't do).
Also:
* Erase trashed annotation after getAnnotations() test
Because ItemTree#notify() doesn't yet correctly handle refresh events on
parent items that are themselves children (three-level nesting: item ->
attachment -> annotation), this test was causing a failure in
itemTreeTest.js.
We don't usually want an async detectWeb, since HTTP requests should only be
used there in very exceptional cases. We do usually want an async scrape (and we
were already - mistakenly - awaiting it).
20c6fe6737 caused this to start failing, but only because the test was
testing something too specific. The change in that commit caused more
rows to be left behind when emptying the trash (for reasons I should
probably look into), but the trash wasn't being emptied properly before,
which #2606 should fix. This test should be restored as part of that PR.
Expose annotation tags in tag selector and match parent attachments when
filtering/searching
This also fixes searching for annotation text or comments when using
Everything quick search.
This is temporary until we display annotations in the items list
directly.
Application menu items, contrary to the comment I added in a654ad291f,
actually *do* belong in hiddenWindow.xhtml so that they still work when
zoteroPane.xhtml is closed. They just can't be split between there and
zoteroPane.xhtml. This commit moves all of them to hiddenWindow.xhtml and
reimplements openPreferences() in a non-Zotero-object-dependent way.
Fixes#2738
We can do it because it was only used to create a note from annotations.
No need to update schema version in Zotero client, unless using new
features when creating a note from annotations.
Part of zotero/zotero-connectors#394
When saving an item (e.g. on arxiv) with a site translator, the
translator may add a custom snapshot attachment URL which does not match
the translation URL (the URL in the browser where translation was
initiated) in which case Zotero handles snapshot saving. Zotero loads
the page in a background browser. By default images are not loaded in
this browser. SingleFile due to CORS restrictions has to refetch all
resources that are not from the same domain or subdomain, but uses
pre-loaded resources (images) from the same domain. Before this change
any images that were from the same domain as the saved website were not
saved in the snapshot. This commit fixes that.
Date columns start in descending order, so put empty rows at end
Also remove a seemingly unnecessary exception for Title when not the
primary sort and just always sort it with empty rows first
Apparently necessary for some people to get PDFs on ScienceDirect. We
should come up with a better way of detecting the page load if we can,
so that we can avoid this long of a timeout for people who just will
never have access to ScienceDirect PDFs.
https://forums.zotero.org/discussion/comment/411056/#Comment_411056
Addresses #2698
We added a `toggleSort()` call on the title, so then when called again
it reverses the direction. To avoid, sort by creator and then title in
the second test.
Because regex is built using a template string, \s* is actually escaped
into s*, i.e. literal "s" appearing 0 or more times. In most cases this
would mean that output can have spacing slightly off. In extreme case,
when identifier starts with letter "s", this could this could lead to
identifier being stored incorrectly.
Also adjusted tests to be more strict and mock data to cover this case.
* Detect wizard cancel/close and interrupt import. This will still
have to wait for current fetch (file or metadata) to complete but will
then advance to the cleanup stage
* Advance progress bar during metadata fetch
* Add some extra logging
Previously we had a structure that also created folders in temp dir,
that has since changed but code in cleanup phase to remove containing
dir has been accidentally left unremoved.
This adds a space after the the number in some numeric styles ("[1]" in
IEEE style, "1." in Nature) when pasting bibliography entries into a
plain-text editor.
Some styles (ACS, AMA, Vancouver) will need to be updated to remove
their hard-coded spaces to avoid double spaces after this change.
Patch from @zepinglee
Fixes#2633
Emoji such as ⭐️ that began as text-based Unicode characters and depend
on Variation Selector-16 to display the emoji presentation weren't being
properly detected.
PDF-handling code from `ZoteroPane.viewAttachment()` predates
`Zotero.OpenPDF` and didn't handle page numbers for external PDF
readers, so when called from "Show on Page" in a note (via
`ZoteroPane.viewPDF()`) it just launched the file itself in the
specified handler or via the OS.
For now, change to use `Zotero.OpenPDF.openToPage()` when there's a page
number. Later we should use `Zotero.OpenPDF` for everything, but that
code is more complicated than just using `launchFile()`, so we'll need
to do some testing (particularly on Linux) to see what's best.
(Note that "Show on Page" with an external reader doesn't make a ton of
sense, since you can't actually see the annotation, but opening the
right page is better than nothing.)
Fixes#2612
And remove all references to old id
We can do this because plugins will use new mechanisms for install and
update manifests that don't use the id (which also means it's now only
used internally).
We'll want to update the rest of the instances of "extensions" or
"add-ons" to say "plugins" (unless we think we might support themes, in
which case we can keep "add-ons").
_getFieldValue() and _setFieldValue() were using `.value` instead of
`.textContent`, so the few places that use it were getting/setting ignored
fields on divs.
fbc25834 messed up the tab index logic, shifting the responsibility for
preserving _lastTabIndex across refreshes: originally, the default was to throw
it away, and the caller was responsible for saving and restoring it if the
refresh was caused by tabbing between fields. The bad commit reversed the
situation and made the item box *remember* the _lastTabIndex by default,
meaning that a refresh caused by the notifier, for example, would focus the
last focused field even if it had been closed by the user in the meantime.
That bad change was a misguided attempt to fix the Tab key cycling infinitely
between Item Type, Title, and creators and never reaching further fields. The
actual fix was just to add `, textarea` to the query on line 1459.
All already being handled by CSS (besides the exact character length limits, but
it's not clear that we really need those - we just ellipsize if it visually
overflows now, which seems fine).
Plugins are ZIPs containing at least these two files:
- manifest.json: A WebExtension MV2 manifest with basic metadata
properties and `applications.gecko` with `id` and `update_url`
- bootstrap.js: A bootstrap file, similar to old bootstrapped extensions,
with `installed()`, `startup()`, `shutdown()`, and `uninstalled()`
The fieldMode attribute of the input was being set by the call to
switchCreatorMode(), but it didn't carry over to the label that replaces it on
line 1997.
Also removed a debug line and cleaned up a bit.
Not the exact same sizing as Fx60, because that relied on some quirks
of the Mozilla box model that I can't reproduce with flexbox, but still
works about as well.
Hasn't done anything since in-browser viewing of certain file types in
Zotero 4 for Firefox. Perhaps we'd bring it back at some point, but
disable for now.
Hasn't been relevant since the Firefox NPAPI days, but was returning
true for `application/pdf` (possibly due to the bundled pdf.js in
Firefox still being included in our builds, which we should remove if
so), which was causing "Open in External Viewer" to appear, even though
it just opened the internal reader.
Fixes an error that prevents changing the join mode from having an effect
after clearing the search when a join mode was already set. Bug also
present on master, not introduced with fx102.
This fixes the dictionary manager and various other places where a XUL
element was being created with createElement(), and also simplifies a lot
of createElementNS(HTML_NS, ...) calls.
This might cause some minor regressions but everything tested has
worked. Not touching preferences (#2659) or bindings.
Pressing Ctrl-C via the terminal is counted as a crash, and after three
recent crashes automatic safe mode is triggered. In Firefox, this
displays a prompt:
> Firefox closed unexpectedly while starting. This might be caused by
> add-ons or other problems. You can try to resolve the problem by
> troubleshooting in Safe Mode.
For us, that dialog doesn't appear for some reason (maybe we could make
it?) and Zotero just automatically opens in Safe Mode, which causes
state not to be restored and results in an annoying process fork that
breaks Ctrl-C. We might as well keep the automatic-safe-mode behavior
for end users, but we don't want to trigger it while working via the
terminal, so we just clear the recent crash counter when using
`-ZoteroDebug` or `-ZoteroDebugText`. (`extensions.zotero.debug.log`
won't trigger it, so it's best to use `-ZoteroDebugText`, which is
included automatically via the `build_and_run` script.)
`waitForDialog()` now returns a regular window, and
`window.document.documentElement.textContent` includes all form
elements, so this updates a test to include the checkbox label.
And fix magic numbers for content-type sniffing, which wrongly used the
Unicode replacement character (which likely just meant we were falling
back to file-extension-based detection)
Use the new PageData mechanism for character set detection, don't try to
index HTML files directly without properly detecting the charset, and
generally simplify the indexing code.
HTML files are now considered cached files that require indexing and
won't be indexed automatically in Zotero.FullText.findTextInItems(),
which breaks certain expectations, including in some tests. This will
need to be addressed.
Remove Zotero.Browser and add HiddenBrowser.jsm. Post-Fission, web/file
content loads in a separate process, so it's not possible (as best as I
can tell) to directly access the contents of a hidden browser -- it just
appears as about:blank in the parent process. We now use Mozilla's
JSWindowActor mechanism [1] to get page data, including character set
and body text for full-text indexing. We'll have to evaluate other uses
of hidden browsers to see how to handle them.
This also adds include.jsm for loading the Zotero object into a JSM.
[1] https://firefox-source-docs.mozilla.org/dom/ipc/jsactors.html
`loadBrowserWindow()` will need to be updated for loading remote URLs,
but anything needing a Zotero window (including `loadZoteroPane()`) can
use this.
The test runner now uses the Zotero executable from
`zotero-standalone-build/staging` rather than the Firefox from
`zotero-standalone-build/xulrunner`. Along with testing the actual
program, this restores visible UI updates during tests, which should
make debugging various things easier. We can also now remove anything
related to Zotero being an extension.
Many tests are still broken, but this at least lets us start running
them.
- Widths are now correct
- Rows are a little too tall
- Field value vertical alignment is off
- Platform styling isn't done and some styles might be redundant
- Still can't scroll vertically
- Creator name inputs still displayed wrong
Not working:
- Web tests (hidden browser is broken)
- Test Updated status text
- Various platform menu things (code copied from ZP and will use whichever
approach we settle on there)
- Add links-box component (inside noteEditor.js).
- Add related-box component and fix related pane.
- Use tagsBox.jsx instead of tagsbox.xml in note editor links box popup.
- Remove CSS styles and bindings for noteeditor, relatedbox and tagsbox.
- Use IDs as list item values
- Use addEventListener() so events are actually received
- Put menulist inside <td>
* This causes a small appearance regression at the moment because
<td> margins/padding are a bit off, but that issue is visible on
all other fields already.
When switching between master and fx102 bogus files were created outside
of the build/ directory. That's because previously we had a symlinked
file itembox.css and now we compile a file itemBox.css into the same
directory. However when running `npm start`, after switching branch, the
symlink still existed and thus when writing a new file, symlink was
followed and output has been written inside a source file instead.
Build system will now run cleanup step first (where it checks if files
frim `.signatures.json` still exist in src), then proceed with build,
so old symlinks will be removed thus producing a valid build.
This problem probably only happened on case-insensitive file-systems
(like default config on macOS HFS+).
listbox is gone, but richlistbox is still here as a custom element and
works fine for cases where we don't need virtualization.
groupbox label and richlistitem styles should probably be copied to
somewhere global once tuned a bit.
We now copy the prefs file into the app omni.ja, which requires
`pref()`, but for tests we copy it to the profile directory, where it
needs to stay as `user_pref()`.
- Keep all tabs unloaded on Zotero opening.
- Keep loaded only the last five selected tabs.
- Keep loaded only in the last 24h selected tabs.
Fixes#2383
https://forums.zotero.org/discussion/96138/lost-content-in-tag-selector
An entered character from the CJK_COMPATIBILITY_IDEOGRAPHS block (char
code 63834, or similar) was normalized to the CJK_UNIFIED_IDEOGRAPHS
block (char code 35712), which then caused an expected key not to exist.
To fix, normalize colored tag values coming from the DB.
Handles items with an alternate field that maps to title (like cases)
and items for which the title is usually filled in by updateDisplayTitle()
(like letters).
This is a confusing, bad option, which was already disabled in groups,
and this removes it from personal libraries as well. People seem to be
using it mainly because they think annotations are locked into Zotero if
they don't, which is causing them to ask for this to be done
automatically on every edit, which we don't do because it would result
in constant file syncing and file conflicts [1].
We provide multiple export options for exporting PDFs with annotations,
and people can use those when sharing a file with people who don't use
Zotero. Otherwise, if storing annotations in the file is a priority,
they can use an external reader that's not designed to be tightly
integrated into Zotero.
(As a rare hack, it's of course also possible to export the PDF with
annotations, replace the original file, and import the annotations.)
[1] https://www.zotero.org/support/kb/annotations_in_database
91faa73b19 made getString return the key
instead of throwing on non-en-US locales, so we check if the getString
result is equal to the key even if it doesn't throw.
Fixes#2472.
Just show the key
We still merge strings before pushing betas, but this means it won't
crash for source builds, or if we forget. Not sure why we didn't do this
15 years ago.
`OS.Path.join()` on Windows throws if an integer is passed. Seems like
this would happen on another computer whenever an image annotation is
deleted in a group?
Follow-up to 734057ff9b
This was supposed to allow ZotFile to continue to work but 1) it
contained a bug and 2) another transaction messed things up for ZotFile
anyway, so we'll just fix this in jlegewie/zotfile#593.
The call to _window.addEventListener() might have been causing an error
when _window was null or undefined (no main Zotero window could be
found), which would prevent _windowLoading from being set to true and
the window from being added to the ProgressWindowSet, which might have
caused unpredictable behavior when positioning windows later on.
I could only briefly reproduce the issue, so this is just one idea. The
call to addEventListener on a potentially null _window was definitely a
bug but I don't know if it caused the crash.
This reverts commit 8763190328.
This is caused by the Scite plugin, and it's better to block startup
until this is fixed, since it's wrecking other fields too.
Now it:
1. Strips punctuation at the beginning, no matter what it is.
2. Strips non-dash punctuation in other positions.
3. Trims the result.
This should better prevent numerical ranges from being joined into a
single number that ends up incorrectly being sorted to the very bottom.
- Clarification between focused row and pivot:
- Pivot is only the row from which shift-selection pivots
- Focused row is the one with the border around it
- Fixed an issue where clicking the focused row didn't select it.
Closes#2402
- Allows to create a non-contiguous range-selection with ctrl/cmd+shift.
Closes#2403
Follow-up to 58f515058 with a better approach: if no full-text cache
file, just get text directly without indexing. In the one existing use
of `attachmentText`, attachment merging, this is better anyway, because
we might be deleting the file, so there's no point wasting time
inserting words into the database.
Indexing starts a transaction, which will cause `.attachmentText` to
hang if accessed within another transaction. If a caller wants to make
sure it has attachment text, it should index items first outside a
transaction.
Fixes#2405. This also fixes an issue where Cmd/Ctrl-Shift-N in a
collection like Duplicate Items would open the item type menu on top of
a blank white item pane (since the new item didn't get selected first).
We follow a different merge procedure for each attachment type:
- For PDF attachments, compare by MD5. If no match, get the top 50 words
in the attachment's text and hash those, then check again for a match.
Update references to item keys in notes and annotations.
- For web (snapshot / link) attachments, compare by title and URL.
Prefer a title + URL match but accept a title-only match.
- For other attachment types, keep all attachments from all items being
merged.
Also:
- Move most merge tests from Duplicates to Items#merge(). It just doesn't
make sense to worry about the UI in these.
- Adjust text
- Add "Learn More" button with link to sync documentation for
set-up-sync notification
- Show "Don't Show Again" for set-up-sync even on first display
- Don't perform any action when clicking unlinked text
- Use yellow/orange background instead of blue for notification bar
- Prevent text from wrapping out of the bar at narrow window widths
- Use local variable for observer id
There are two:
- Sync never set up (no username in DB) / set up, but unlinked
- Sync set up but auto-sync disabled and sync not completed in 30 days
Checks are trigged on item adds.
We've been seeing some behavior in `Schema::_migrateUserDataSchema()`
that should be impossible, with conditional blocks totally skipped. I
can't see how this is anything other than either a JS engine bug with
generators or some sort of bug in our version of Bluebird, but with any
luck it will go away by switching everything to async/await.
Since in many locales it would contain slashes and colons, both of which
would be stripped in the filename, leaving long strings of numbers in
the filename
And pass both `authorName` and `lastCreatedByUser` to the PDF reader.
The former can either come from `createdByUser` or be set directly on
the item (for group annotations dragged to personal libraries).
a1267bc fixed repeated re-appending of '@2x' by moving the modified URL
to a local variable, but it still set hasHiDPI to false permanently;
this meant that icons displayed repeatedly in the same window, like the
tab close button, would only use their 2x versions the first time they
were displayed (in the case of tabs, this is the library tab's invisible
close button, so *no* tabs got visible 2x buttons).
This commit moves the HiDPI check and URL modification out of the render
function and instead runs it a single time when i() is first called.
* Tweaked ProgressQueue to accept multiple listeners per event.
Removing listeners now requires specifying which one to remove.
* Decoupled progress queue table into new ProgressQueueTable component
Update `DataObject::isEditable()` to take an optional `op` argument to
test individual operations as opposed to general library editing.
Erasing objects now tests `erase`, and `Item::isEditable()` allows
`erase` for unowned group annotations while disallowing the default
`edit`.
It's still up to the reader to handle this appropriately in the UI and
not allow operations it shouldn't, but this enforces it in the data
layer.
- Show "Export Note…"/"Export Notes…" if only notes or attachments
selected
- Don't show export option if only attachments and no embedded notes
(was previously disabled, and still is if all notes or a mixture of
empty notes and attachments)
Closes#2265
There's also new code for showing a different icon for snapshots, files,
and DOI/URL links, like the web library and iOS app, but it's commented
out for now. The bitmap icons create too much visual noise with the
greater information density and hierarchical tree of the desktop app
(not to mention many more low-DPI displays). We can revisit after
switching to SVGs across the board.
Cmd/Ctrl-Shift-T is now used for reopening a closed tab
This is now a View menu option, so macOS users can assign an
app-specific shortcut key for it. For other platforms that can't do
that, Zutilo could add it, and it will be an option if we add general
key-binding configurability in a future version.
Closes#2281
So that this works for PDFs with external annotations that haven't yet
been processed. Going forward, if we add automatic/background scanning,
we can avoid showing this option if the file hasn't changed and there
are no Zotero annotations, which would probably be the majority of
attachments.
Addresses #2268, but better support for other en-* locales will require
other changes, including `timeStyle` support in `Intl.DateTimeFormat` in
a newer Firefox version, which will let us use a custom `DateTimeFormat`
for dates instead of relying on `toLocaleString()`.
Previously only individual objects from successful requests that
couldn't be processed for some reason would be added to the queue.
`Sync.APIClient.downloadObjects()` now returns clearer and more
consistent results. It now returns an array of promises for objects with
a `keys` array of requested keys and either a `json` array of returned
API JSON objects or an `error` Error, depending on whether the request
succeeded or failed. This makes it easier to detect remotely missing
objects and request failures.
This includes the `Zotero.RDF.getResourceURI()` fix in
zotero/translate@85b39a5be.
This position change here for `this._handleRelated()` for attachments
isn't necessary, since the attachment is already saved and already has
an id, but it's done for consistency with the call for notes (where the
note previously didn't yet have an id).
Institutional login process goes through multiple pages, the URL regexp
is tweaked to be more precise to ensure that no arbitrary URLs are
matched while polling for the final URL with auth code.
It seems that style calculation gets insane when `overflow: auto` is used inside a collapsed box (specifically inside contextPane), although there might be more factors
Extended Mendeley Import test to include a scenario where other users
attached an annotation to an item in a group library that also exists
in user's library.
This prevents metadata changes from clearing the isRead status of feed
items as long as GUIDs remain constant. Previously, feed items with
randomized properties (like URLs generated dynamically each time the
feed is served) would be incorrectly marked as unread on every update.
I didn't check the previous behavior, but I assume it's the same:
This will persist the same settings for the select items dialog in the
classic citation view as well as the add related item dialog.
This resolves a problem where, in certain scenarios,
Zotero.file.download throws an exception even though file is
successfully downloaded.
Furthermore this new download function should be more memory-efficient,
improving performance when dealing with large files.
This test covers couple of scenarios downloading a 16MB file from a
local http server (simple and concurrently) using Zotero.File.download.
When running current implementation, this seems to fail persistently.
In real-world usage, current implementation seems to work
intermittently, failing more often for larger files.
This intermittent behaviour is most likely cause of #2216.
- Make citation node a leaf node.
- Don't parse citation node inner text.
- Don't trigger citation inner text updating on citation item changes.
- Format citation text only when serializing note HTML.
- Wrap individual citation items into `<span class="citation-item"></span>`.
And also the tag selector, while we're at it. Leaving enabled for the
item pane.
(Clickthrough is responding to a click when the window doesn't have
focus, rather than having the first click just raise the window.)
Fixes#2203
Closes#2168, closes#2169.
Adds find-as-you-type to locate, preference style and export
formats managers.
To enable find as you type you need to specify the getRowString(index) prop
on the VirtualizedTable. See prop comment for more info.
- Messages are shown once a day by default (within the same session for
id-less messages)
- Messages with an `id` attribute include a checkbox to not show again
for 30 days
- If an `infoURL` attribute is provided, a "More Information" button is
shown that launches that URL
- If `title` is provided, it's used for the dialog title. Otherwise
"Warning" is shown.
- Select last reopened tab instead of opening all in background
- Rearrange Close/Close Other Tabs/Reopen Closed Tabs options
- Use proper plural for Reopen Closed Tab[s]
7ef7943a17 made "Loading items…" small again after it got big during
HTMLification but made everything else (e.g., welcome message, My
Publications intro) small as well. This adds an ugly hack to keep the
loading message small for now (without passing dedicated HTML from a
bunch of different places).
Removes `defaultRowHeight` prop added in adb8aa39f in favor of a prop
that disables font size scaling. A non-default row height can still be
specified with `rowHeight`.
Most of our existing trees need to disable font size scaling, but the
idea is that pretty much everything _should_ scale with font size for
accessibility, and it's a limitation of the current prefs and other UIs
that they don't currently, so it's better to default to scaling and
gradually remove uses of this prop.
This fixes a bug where the HTML trees other than the collection and item
trees would have larger rows but without larger text when font size was
increased.
Also:
- Fixes#2157 by fixing line height adjustment on macOS when at the
default font size, applying it to all trees, and moving the explicit
`rowHeight` override from the item tree to the collection tree, which
should have more spacing than all other trees (rather than the item tree
having less).
We can do fun things now.
Also:
- Make colored tag swatches resize with the font size
- Increase border radius for color swatches, and adjust with size
Roughly center child item icons between the parent item icon and text,
which looks a bit less awkward than the previous tree's positioning. An
alternative would be to align with the beginning of the parent item text
(a value of 21), but that's a bit spaced out, at least until we have
multiple levels with additional twisties.
- Fixes selection events always being debounced
- Fixes some failing tests
- Ensures Select All command selects search matching children of
collapsed parents. Adds tests for this case
- Just a single huge commit. This has been developed over too long a
time, required many tiny changes across too many files and has seen too
many iterations to be separated into separate commits.
The original branch with all the messy commits will be kept around for
posterity
bb220ad0f2...adomasven:feature/react-item-tree
- Replaces XUL <tree> element across the whole zotero client codebase
with a custom supermegafast virtualized-table inspired by
react-virtualized yet mimicking old XUL treeview API. The
virtualized-table sits on top on a raw-to-the-metal,
interpreted-at-runtime JS based windowing solution inspired by
react-window. React-based solutions could not be used because they were
slow and Zotero UI needs to be responsive and be able to
display thousands of rows in a treeview without any slowdowns.
- Attempts were made at making this screen-reader friendly, but yet to
be tested with something like JAWS
- RTL-friendly
- Styling and behaviour across all platforms was copied as closely as
possible to the original XUL tree
- Instead of row-based scroll snapping this has smooth-scrolling. If
you're using arrow keys to browse through the tree then it effectively
snap-scrolls. Current CSS snap scroll attributes do not seem to work in
the way we would require even on up-to-date browsers, yet alone the ESR
version of FX that Zotero is on. JS solutions are either terrible for
performance or produce inexcusable jitter.
- When dragging-and-dropping items the initial drag freezes the UI for
a fairly jarring amount of time. Does not seem to be fixable due to
the synchronous code that needs to be run in the dragstart handler.
Used to be possible to run that code async with the XUL tree.
- Item tree column picker no longer has a dedicated button. Just
right-click the columns. The column preferences (width, order, etc) are
no longer handled by XUL, which required a custom serialization and
storage solution that throws warnings in the developer console due to
the amount of data being stored. Might cause temporary freezing on HDDs
upon column resize/reorder/visibility toggling.
- Context menu handling code basically unchanged, but any UI changes
that plugins may have wanted to do (including adding new columns) will
have to be redone by them. No serious thought has gone into how plugin
developers would achieve that yet.
- Opens up the possibility for awesome alternative ways to render the
tree items, including things like multiple-row view for the item tree,
which has been requested for a long while especially by users switching
from other referencing software
During Mendeley api import all attachments are fetched into temporary
location and were previously copied to the storage folder. Only after
import completed, temporary files were deleted. Therefore a complete
import required storage equal to twice the final library size. Switching
to move avoids this problem.
Also:
- Fix _isTempDownloadedFile() check
This won't make a difference until we update Firefox and get a new
SQLite version, but at that point the existing schema update steps that
recreate tables by renaming the old table would result in broken foreign
keys.
This patch will make sure that newer SQLite versions will use the legacy
behavior for the existing steps, and going forward schema update steps
that want to recreate tables will need to create a new table, migrate
the data, delete the old table, and rename the new one into place.
- We were updating global schema before migrating userdata, but a 4 → 5
upgrade involved a system.sql version bump, which wiped out itemTypes,
causing 'annotation' to not exist after the upgrade. This moves global
schema updates after userdata migration and bumps the global schema
version to repair DBs that were already upgraded and broken.
- A system.sql bump without a global schema update would result in empty
tables. This moves the global-schema-related tables to userdata.sql.
- The DB integrity check before userdata updates added in 5b9e6497a
could fail when coming from an older DB, because the checks assume
current schema. An integrity check is now done after a userdata update.
(We were already skipping the new table/index reconciliation stuff. If
old DBs are discovered to have problems that would cause a migration
step to fail, we'll fix those explicitly in the steps.)
Also:
- Make sure `version` is `versionNumber` in the `fields` table. It was
changed with a system.sql bump in 5.0, but hard-coded fields were later
removed from system.sql in favor of schema.json, meaning that anyone who
upgraded from 4.0 after that would never have `version` removed and so
would have both fields (one from before and one from schema.json).
zotero://open-pdf/library/items/AABBCCDD?annotation=[annotation-key]
Fallback to a page if annotation is missing:
zotero://open-pdf/library/items/AABBCCDD?page=123&annotation=[annotation-key]
Fixes#2125
- Set `annotation.attachmentURI` instead of `annotation.uri` when inserting annotations
- Migrate `annotation.uri` to `annotation.attachmentURI`
- Add `annotation.annotationKey` that references to the original annotation
- Delete unused embedded images when note is closed.
- Load images as soon as they are downloaded.
- Introduce new notification for download event, and a test for it.
- Prevent simultaneous downloads of the same attachment.
Just don't consider items in the trash to be linked items in
item.getLinkedItem(). I'm not totally sure why we didn't do this many
years ago, since it's one of the biggest sources of confusion in Zotero.
This addresses #1648 and closes#1610, though not by undeleting and
overwriting the item in the trash. When deleting an item and then
re-dragging it from another library, I think most people would expect
the item in the trash to still exist (possibly with notes, etc.), rather
than having been automatically restored and overwritten with new data.
And since `Zotero.Libraries.isGroupLibrary()` just checked that
property, that hasn't worked either since `isGroup` was added in 2015.
There's no test for `.isGroup`, and the test for `isGroupLibrary()` used
`if()` instead of `it()`, so it never actually ran.
Also:
- Remove old code block in search.js that called `isGroupLibrary()`.
Since `isGroupLibrary()` didn't work, this block was unused, and its
logic was previously added elsewhere.
- Properly transform HTML tags flavored plain-text into actual HTML
- Add support for multiline comments and highlights
- Insert newline before citation/comment when necessary
We're relying on Bluebird's synchronous promise inspection for noWait
translate mode (as used in Quick Copy). We should decide if we want to
keep the Bluebird dependency or rewrite this to use native promises
(e.g., with a separate synchronous `_loadTranslator()` function for
noWait mode), but for now just restore the `Zotero.Promise.method()`.
The bug fixed in bde9a74f9d was triggering an error getting code for a
deleted search translator, which due to this bug caused a double
complete() call that got detection all out of sync, which caused some
search translators to be skipped, which resulted in ISBN lookup
failures.
Among other things, if the name of a translator was changed, until
restart Zotero.Translators.getAllForType() would return the old cache
entry pointing to a file that no longer existed.
- Make a copy of the database after first initialization that can be
swapped in when reinitializing in resetDB()
- Avoid unnecessary one-second delay on every reset
Probably more that can be done, but this should take minutes off the
test runs
- Add/Remove Dictionaries window
- Better account for the (unlikely) possibility that a dictionary could
be replaced by another more popular dictionary provided by a different
extension id (tested)
- Better account for the (very unlikely) possibility that an extension
could bundle multiple dictionaries (untested)
- Use toolkit version comparator for proper extension version
comparisons
- Localize strings
- Add tests for updating
Otherwise switching back to release from beta doesn't show the last-used
version if the beta hasn't gone through at least one
compatibility-breaking schema update.
This connects the new feed processor to Zotero's existing feed reader. The new
feed processor assumes a content window environment, so a sandbox in a parent
window is used to load it.
This removes the feed processor's feature of converting enclosure MIME types to
a textual type description. This is not used by Zotero and removes an XPCOM
dependency.
This adds extra tests to check parsing behavior such as entities, tag handling,
CDATA, etc. This will help ensure the new feed processor matches the previous
behavior.
There is no isInstitution field, so OpenURL search was producing invalid
data for items with institutional authors. Instead we use the name field
in API JSON and the isInstitution flag in internal JSON.
- Limit field autocomplete to current library in item pane (previously
only done for creator and access date)
- Improve positional parameter usage to avoid duplicate parameters
- Code cleanup
It's not totally clear how it's calculated, but it doesn't seem to be
just characters.
And make the path limit 255, which is the File Explorer limit, even if
the filesystem can allow up to 260.
Filesystems care about byte length, not character length, so treat
maxLength as the byte length limit and truncate accordingly.
This will also now remove entire emoji characters without corrupting
them.
If a file attached to an item with a URL wasn't found in the temp
directory, it would trigger an error that stopped the sync. (I'm not
sure how a file wouldn't exist.)
By changing to `mousemove` to start the tab title tooltip, the behavior matches
applications like Firefox more closely: you can always get the latest tooltip
back after changing state by nudging the mouse (without having to exit and
re-enter anything).
This hides tooltips (such as the tab title tooltip) whenever the tab bar state
changes, such as when adding or removing a tab, to ensure we don't leave any
tooltips behind that no longer match reality.
Fixes https://github.com/zotero/zotero/issues/2060
It's still possible to trigger a local import from a 1.18 database by
selecting the .sqlite file directly from the file option, but don't
offer it in the dialog.
E.g., if a sync is ongoing and there are multiple item saves that would
trigger auto-sync with the same options, only queue a single sync.
7ace5ea29e fixed the main cause of this, but in case some other
saves end up happening during a sync, this will prevent them from
triggering multiple sync loops with the same options.
An extra sync loop would be performed for every object downloaded, so a
download to an empty database could result in a huge number of
unnecessary loops. This was a regression from 52932b6eb, which started
queuing auto-syncs while a sync was in progress. The fix here is to skip
auto-sync for all objects saved from a sync download.
There are two new mechanisms involved:
- Event-level notifier options that get passed to passed to notify() at
the top level of extraData rather than being included with every
object (e.g., because `skipAutoSync` should apply to an entire save
transaction)
- The ability to pass event-level notifier options when initializing
a Zotero.Notifier.Queue, such as the one used for sync downloads
This expands the test filtering in the test runner so that you can supply e.g.
`test/tests/feed*` from your shell in the `zotero` directory and correctly
filter to all feed-related tests.
This imports Mozilla's feed processor which has been removed upstream as part of
Firefox 66. Some changes are likely needed before this will actually run in
Zotero.
This hides the tab bar on Windows and Linux, where it currently takes up a lot
of vertical space below the menu bar. On macOS, the tab bar remains always shown
since we don't have the menu bar and can move tabs in the window there.
Fixes https://github.com/zotero/zotero/issues/2019
This dramatically speeds up Mendeley imports by avoiding UI updates.
Regular imports in fileInterface have done this for a while, but the
Mendeley importer used a fake translation object that didn't use the
passed queue.
After this change, a 10,000-item import goes from 9m30s to 2m45s, a 71%
decrease.
- Add "(online import)" and "(local import)" next to "Mendeley Referance
Manager" and "Mendeley Desktop", respectively
- Adjust size of OAuth window
- Use bound parameter instead of string literal for Fx78 compatibility
All annotations in all groups are fetched and hashes are compared to
detect annotations created on the same file while it was in a group
library. Annotations created by other users are filtered out.
Many DB statements that were being cached included embedded ids or temp
table names, so they would permanently use memory and never get reused.
During large imports, this could result in hundreds of megabytes of
extra memory usage.
This commit marks many more statements as `noCache`, adds `noCache`
support for column and value queries, adds a log line at DB shutdown in
source builds with the number of cached statements, and adds
Zotero.DB.getCachedStatements() for auditing of cached statements.
Addresses #1531
- Allow to format citations inside note-editor
- Allow quickFormat dialog to display and pick already cited items, even if an item no longer exists
- Watch and automatically update citation itemData in metadata container and re-format citations in body in opened notes
- Reorganize note metadata container handling and improve resistance to accidentally breaking it in further development
- Improve performance when typing in larger notes
- Rewrite note saving mechanism to support automatic note changes and reduce complexity for further development
- Cleanup and comment some core parts or note-editor as the preparation for further development
- Prepopulate quickFormat dialog with the currently opened PDF parent #1984 (doesn't include the currently scrolled page label yet)
Reduce timeout after an object change from 15 seconds to 3 seconds,
ecxept for individual notes. We should probably kick off an auto-sync
3 seconds after a note is blurred, but that's not implemented.
We also now skip file syncing and full-text content syncing altogether
unless an attachment has changed.
Reindexing an item now kicks off auto-sync, whereas before it didn't.
- Automatically upgrade all editable v1 notes when note is being opened
- Pull `itemData` from existing citations and annotations into metadata container attribute `data-citation-items`
- Strip `abstract` from `itemData`
- Fill `citationItem` (in citations and annotations) with `itemData` when passing an HTML fragment for further processing in `note-editor`
- Keep only `uri`, `text`, `color`, `pageLabel`, `position`, `citationItem` annotation properties
- Add Zotero logo
- Move most acknowledgements to the website, where they can be more more
easily updated and more comprehensive
- Increase font size
- Center window on screen
We weren't making actual upload requests after a quota error if the file
would exceed the quota, but we were still going through all attachments
to upload, which in some cases involves making stat() calls. We now just
stop the queue immediately after a quota error or when starting a new
background sync after a previous quota error.
Closes#1255
This adds a very basic reimplementation of the annotation widget for use
in the CR window. It's not pixel-perfect, but it's close enough that
most people probably won't notice. We'll want to replace this with a
real version that's shared between the PDF reader and the rest of the
codebase.
Image annotations currently show an "[image not shown]" placeholder.
Showing images is tricky: we don't have the remote cache image, so if
the remote position has changed, we could only show an image by
rendering it from the file, and only if the file itself hasn't changed.
Just for a better user experience, we could use the local image as long
as the position and file are both the same, but that would take some
rejiggering of the CR window.
Tags aren't shown because they're not shown for CR at all, though that
could be changed in the future.
And remove unused _removeNote() function
We prompt to delete in the items list to distinguish from collection
removals, but it's arguably not necessary here, since you can always get
the item out of the trash. Undo would be nice, though.
Using new PDF worker functionality
If you've previously imported, you should be able to import again to get
annotations in the new PDF reader.
Details:
- Text highlights will be processed to extract text
- Rectangle highlights will turn into image annotations
- Any extracted-annotation notes created by previous improts won't be
removed, but they're no longer created as long as the file exists.
- Show both sections even on search
- Add new note creation popups with an option to create from annotations
- Hide child notes section if no parent item
The PDF reader pref is now "Use Zotero PDF reader and new note editor
(beta, My Library only)". Toggling the pref prompts to restart, and
Zotero.isPDFBuild -- which currently controls the PDF reader, new note
editor, and tabs UI -- is set based on that pref at startup.
- Always show both sections
- Do not show empty sections when searching
- Collapse notes list when all tabs are closed
- Work around search input to take full width without breaking its stuff
Using new Zotero.Date.toFriendlyDate() function
- Time if today
- Day of week if earlier this week
- Date with two-digit year otherwise
We could consider adding 'Yesterday' as well.
While the code can technically disable various things (e.g., tab bar) to
behave like 5.0, other things (e.g., word processor plugins) would be
tricky to do conditionally.
Shows as a menu option when there's a single PDF attachment with
annotations and as a submenu containing titles when there are multiple
(even if there's only one attachment that actually has annotations)
Also removes "Create Note from Annotations" on attachment items for now.
(It might make sense to add that back, though "Add" would be a little
weird for creating a sibling note, while using a different label would
be confusing.)
- Show child notes at top of notes list, with separate headers for "Item
Notes" and "All Notes"
- Fix "New Child Note" option
- Add parent item title above note editor when editing child note
- Cleanup
TODO:
- Search on parent item title for child notes
Not sure if this can happen, but if there's any way for the schema
integrity check to run before the global schema is updated, use -1 for
the annotation itemTypeID.
- Remove item pane tabs
- Show all notes in notes pane, showing the parent title where
appropriate
- Show drop-down menu for "New Note" button in notes pane that allows
creating standalone or child notes
- Add some temporary styling for notes in the notes pane
TODO:
- Show child notes at top of notes list, with separate headers for "Item
Notes" and "All Notes"
- Fix "New Child Note" option
- Add parent item title above note editor when editing child note
- Search on parent item title for child notes
The page index needs to be per-person in group libraries, and it should
still work in read-only libraries, so it doesn't make sense to store it
on the item. This uses a synced setting in the user's library instead.
And use when opening PDFs in My Library from items list and
zotero://open-pdf
This also adds "Zotero" to the main PDF reader pref, which will be the
real way to set this, but that option is hidden for now so that group
library items will still open with the configured reader.
The recent change to sniff for PDFs when an attachment didn't have a
content type resulted in a ZoteroPane test failing, because a stub for
Zotero.launchFile() wouldn't be called by the time the viewAttachment()
promise resolved.
This changes the attachment saveFile() function in translators to be
async. In order for errors to be properly caught, translators will need
to be changed to make doExport() async and await on saveFile() calls.
(The translation architecture theoretically already allows doExport() to
be async.)
Returning ids mirrors getAttachments() and getNotes(), but I think we
want to move towards a world where those return actual objects, even if
we need async load calls on the returned objects.
- Revert `notesList` to standalone notes mode only
- Fix splitter styling on Windows
- Fix `contextPane` performance fixes
- Add a customizable throttle function to `Zotero.Utilities`
- Fix reader tab selection issues
- Improve error handling in the new editor
- Update pdf-reader and zotero-note-editor submodules
- Fix contextPane child notes list
- Properly notify editor about new images
- Fix note title and body snippet extraction
- Persist right-hand pane state when switching tab
- Hopefully fix two panes visible at the same time
- Rollback all redundant changes made in the last few months
- Introduce `contextPane`
- Show child notes in the notes pane
- Fix splitter styling
- Various bug fixes
- Fix contextPane switching and states persistence
- Persist reader sidebar open/close state
- Fix bottom pane placeholder updating concurrency issues
- Fix toolbar placeholder width updating
- Display titles for split button
- Fix toolbar position when switching tabs
- Add PDF tab loading cover
- Improve notes and citations insertion
- Clean up and refactor code
- Fixes and cleanups to PDF reader
- Show item title at top of item pane in contextual mode not in a
library tab
- Show "All Notes" at top of standalone notes pane
- Add background color when clicking notes in standalone notes list
- Clean up back buttons
Mode button is still a placeholder but uses emoji to show the mode
E.g., embedded attachment notes with no note don't have an itemNotes row
and don't output noteSchemaVersion in their JSON, but they shouldn't
trigger a conflict
And replace most uses of isImportedAttachment(), which doesn't include
embedded-image attachments
There might not be much reason to keep isImportedAttachment() around.
Added Zotero.DataObjects.sortByParent() to sort child items immediately
after their parent items. Zotero.DataObjects.sortByLevel(), which is
used for collections, sorts each level together, but that's less
appropriate for items where, e.g., an embedded-image attachment should
immediately follow the note that depends on it.
```
var noteContents = item.note; // was item.getNote()
var schemaVersion = item.noteSchemaVersion;
item.setNote(contents) // default to Zotero.Notes.schemaVersion
item.setNote(contents, schemaVersion) - explicit version
```
Generally, don't clear the change state for primaryData when calling
loadPrimaryData() on an item that doesn't exist. Specifically, this
fixes an issue where calling loadPrimaryData() on a nonexistent item
after setting a libraryID and key would also clear any item type set in
the constructor. The same would apply for props set in the
Collection/Search constructors.
Make it easier to use the new `_changedData` approach to object
modifications (currently used only for `tags` and `deleted`) where
changed data is stored in a separate object rather than in the primary
variables. _getLatestField() can be used to return either the new
unsaved value or the current saved value.
Instead of showing warning button on toolbar
Otherwise if one person in a group upgrades to a beta with new fields,
everyone in the group will get a warning, even if they're already on the
latest release version. In a personal library, the user can upgrade to
the same version.
E.g., if a local item has been modified in a way that conflicts with a
remote item that also has a new, unknown field, don't show the CR
window -- just add the item to the sync queue and show the
some-data-could-not-be-downloaded error.
If an upload fails with a 412, and then we download, and then we go to
upload but there was nothing to upload (e.g., because the remote version
was chosen or there was an error saving it), there's no reason to do a
download again after that.
E.g., Actions menu in tag selector
Neither 'title' (HTML) nor 'tooltiptext' (XUL) works on HTML elements in
XUL documents, so add a mechanism to fake tooltip behavior and use it in
our Button component. This can be triggered by other React components if
necessary.
This is (mostly) mirroring Firefox tooltip behavior, which is a bit less
sophisicated than macOS behavior. If we end up using this for
everything, we can improve the behavior (e.g., start the delay when
entering the element, not when the mouse stops).
Fixes#1947
While we need to pass all items from descendant collections to
translators, only the immediate child collections of the selected
library or collection should be returned from Zotero.nextCollection().
I'm not sure how long we've been doing this wrong, but it resulted in
duplicated subcollections when round-tripping Zotero RDF. (TEI is the
only other translator that uses nextCollection(), so its exports were
probably similarly incorrect.)
https://forums.zotero.org/discussion/87135/export-import-creates-numerous-duplicate-subcollections
We're now flagging the DB for repair on startup errors, so if that fixes
the problem (e.g., because of our undiagnosed skipped-schema-update-step
problem), we'd still like to know about it.
But skip it at startup, even if flagged on, if there are schema update
steps to perform, to avoid creating tables that aren't expected to exist
yet.
Originally added in 5b9e6497a but disabled in c4cc44528 and 7a434df53
This lays the groundwork for moving collections and searches to the
trash instead of deleting them outright. We're not doing that yet, so
the `deleted` property will never be set (except for items), but this
will allow clients from this point forward to sync collections and
searches with that property for when it's used in the future. For now,
such objects will just be hidden from the collections pane as if they
had been deleted.
changedData is the new approach to updating DataObject data, storing the
new values in a separate object until save so that they don't actually
change the object unless the save goes through.
- Add DataObject._getLatestField() to return either the changed value or
the last-saved value
- Clarify that _markFieldChange() takes different values depending on
whether the field uses 'changed' or 'changedData'
- Fix bug calculating hasChanged() when a 'changedData' value is a
boolean
If a group library was deselected in the "Choose Libraries…" window, a
remote change would still trigger a sync if auto-sync was enabled. (My
Library was not affected.)
If we add the notifier queue, another save function will use that queue
but not necessarily know that it needs to commit. Instead, we only use
the notifier queue for this function.
- Use the "Chrome" theme
- Hide the gutter
- Don't highlight the active line
- Remove default iframe borders
- Focus the editor on window open
Ace instances (e.g., Scaffold) are now responsible for setting their own
theme (to avoid a flash of Monokai before a lighter theme appears).
Originally added in 5b9e6497a. We stopped running the integrity check
before userdata upgrades in c4cc44528 because this new behavior was
breaking upgrades, but it could still be run when coming from the DB
Repair Tool, which could cause problems if, say, you recovered a
database from a computer that had an older version of Zotero and ran the
DB Repair Tool on it. Disabling this for now until we have a better
solution.
The Quick Format dialog's shape is based on the OS window corners, and
that changed on Big Sur, which resulted in whitespace along the bottom
edge and a misaligned search results box.
The tops of the citation bubbles were also cut off (at least for me) on
Catalina.
ConcurrentCaller wasn't waiting properly if start() was called again
while it was pausing, so 429 caused an immediate retry, which is pretty
much exactly what you don't want a 429 to do.
If a translator couldn't be parsed, `_translatorInfo` wouldn't be set,
and that would somehow cause the translator info for subsequent
detections to be out of sync with the code loading, and nothing would
work. Putting a try/catch around the eval() allows subsequent
translators to continue to work normally. (There might be a better fix,
but this seems to work.)
This was happening for Better BibTeX translators that were still
installed without the extension (which they need to be parsed properly),
causing all imports to break.
https://forums.zotero.org/discussion/86613/error-id-1093166052-upload-of-ris-and-bibtex-data-failed
- Use cookies from browser pane when running detectWeb or doWeb
- Use cookies from document when running or updating tests
This should fix various cases where translators can't be properly
developed or tested in Scaffold (e.g., [1]).
[1] https://github.com/zotero/translators/pull/2296#issuecomment-739116620
After a local item change, Zotero uploads the JSON and then applies the
saved JSON returned from the API to the local object using `fromJSON()`,
the same as it would apply any other remote change.
`fromJSON()` is meant to migrate Extra lines into real types and fields
after future item type/field changes. It calls
`Z.Utilities.Internal.extractExtraFields()`, which looks for valid item
type or CSL type values in Type lines in Extra, handles the rest of
parsing accordingly, and passes back the parsed item type. `fromJSON()`
wasn't handling `itemType` in the response object, so the item type
didn't get applied and the Type line was stripped. This fixes that.
Since valid type values are now parsed, if you have a Journal Article
item with a Pages field and enter "Type: song" into Extra and sync, the
item will be converted to Audio Recording and `Pages: 123` will be
placed in Extra.
https://forums.zotero.org/discussion/comment/369221/#Comment_369221
Creating missing tables breaks schema update steps that assume those
tables don't exist, so we'll need another solution here. For now,
disable the automatic integrity check and make a couple recent schema
update steps more forgiving for people on the beta who already triggered
this.
If there was an existing ~/Zotero directory, another profile was already
pointing to it, and there was a Firefox profile pointing to a custom
data directory or with an embedded 'zotero' directory, the Firefox data
directory would be used instead of creating a new data directory named
after the new profile.
This skips the Firefox logic for new profiles when there's an existing
profile pointing at ~/Zotero and just creates the new data directory.
- Create userdata tables and indexes that are missing
- Delete tables and triggers that should no longer exist
- Run schema integrity check before user data migration
- Run schema integrity check after restart error
This is meant to address two problems:
1) Database damage, and subsequent use of the DB Repair Tool, that
results in missing tables
2) A small number of cases of schema update steps somehow not being
reflected in users' databases despite their having updated userdata
numbers, which are set within the same transaction. Until we figure
out how that's happening, we should start adding conditional versions
of schema update steps to the integrity check.
This is currently only running the update check after a restart error,
which might not occur for all missed schema update steps, so we might
want other triggers for calling setIntegrityCheckRequired().
- Using `sandboxPrototype` properly uses window as prototype
- This commit removes the need for our patch in babel-worker.js:
3d0bc4cf9f
- We properly inject into frames in the client if we ever include frames
4bc8fab4f5 added support for `proxy: false` on attachment URLs in
translators, but it made that flag skip `resolveURL()` altogether, which
meant that relative URLs weren't resolved, rather than just skipping
proxying.
This may fix PDF downloads for some ScienceDirect pages (though I can't
test this one):
https://forums.zotero.org/discussion/85884/problem-downloading-pdf-via-zotero
4be877ca1e
breaks some things in our hidden browser. They can probably all be fixed
by adding `window.` (e.g., `setTimeout()` → `window.setTimeout()`), but
for now just use the commit before that one.
Our SingleFileZ integration would save images inside directories following the
SingleFileZ format. However, Zotero does not support syncing sub-directories of
attachments. This commit switch back to a single HTML file with base64 encoded
resources. We think that the 33% increase in resources will be offset by the
compression of HTML and removal of JavaScript and unused CSS.
This commit does not fix past snapshots that were saved using SingleFileZ.
- Remove TODO comment to add a warning if a Document is passed, since a
document will always be required when operating on something other than
the main page (e.g., for multiples).
- Always return a string instead of null. A translator can always use
querySelector() if it really needs to know if a node exists.
- Automatically trim the returned string
- Add innerText() function that uses .innerText instead of .textContent,
since there are situations where that might be helpful. Some of those we
currently address with trimInternal(), but there can be other
cases where .innerText gives cleaner, user-centric output.
Translators won't be able to rely on these changes for a while, so we'll
need to be careful to watch for problems here, or translators will need
to include a v3 polyfill.
Addresses zotero/translators#1913
Sending value as a string to "removeCreator" breaks the logic in the
loop in that method which is supposed to remove target creator and shift
remaining creators in the array. This in turn triggers undesired removal
of the last creator in the array.
- Use `new URL()`, available in all modern environments, instead of
various other methods. In addition to being consistent and simple,
this allows setting the base URL explicitly, regardless of the
environment.
- Default protocol-relative URLs to 'https' if no document location
(though I'm not sure if that ever happens)
1e0ad3aba changed the test for DB corruption to look for "database disk
image is malformed", but at least Sqlite.openConnection() can throw a
"Could not open connection" error with "2152857611"
(NS_ERROR_FILE_CORRUPTED) in it.
Fixes issues when integration plugin field appearance order
in the document is hard-linked and fields can be nested,
which breaks those links when updating a field with nested fields, in
the process removing the nested fields and upsetting document field
order.
Report: https://forums.zotero.org/discussion/83840/error-while-refreshing-a-large-document
Include flags in repo update checks indicating whether a database table
that was previously supposed to be removed was removed and whether a new
table was created. This should help us figure out whether we can safely
perform schema update steps or whether we need to figure out why some
schema update steps aren't being applied.
Possibly caused by a third-party client uploading mtimes that then
aren't synced, or that differ from what get synced. When we detect this,
try to correct it by updating mtimes on WebDAV and the API to match the
local file.
https://forums.zotero.org/discussion/83554/zotero-loop-syncs-2000-items
Show a popup if DB corruption is detected with a warning to avoid
storing the data directory in cloud storage, a link to a support page
with more information, and a button to restore from the last automatic
backup.
Also show the warning about cloud storage again after restoring from the
last automatic backup.
- Support Adobe Acrobat on macOS, with page number support via System
Events. Tested with Acrobat Reader, but hopefully functional with
non-Reader versions as well
- Use full path to configured .app via AppleScript rather than a
hard-coded name, in case there are multiple versions on the system
- Simplify code and improve logging
This seems like it would be sufficient for most tests that need to wait
for a page to load and is much less annoying in Scaffold. We could
adjust for automated runs if those end up using a browser again.
Previously, while updating a test used loadDocuments() (for a real
browser load) and honored `defer: true`, running a test did not.
This updates Zotero_TranslatorTester to use loadDocuments() when run in
Zotero. In other environments (translation-server, connector), it will
continue to use processDocuments(), which is based on XHR.
Fixeszotero/scaffold#96
Zotero.File.move() now forces `overwrite` if the old and new filenames
differ only by case, since otherwise on a case-insensitive filesystem
OS.File.move() does an existence check and thinks that the target file
exists.
Prior to fdfa8052d1, it was possible to create an item in a feed
library using Add Item by Identifier. If you did that, and then copied
it to a collection in your personal library, it would somehow end up
with an owl:sameAs relation to a `/users/local` URI (probably because
the URI functions don't work properly on a feed library).
This will clean up such relations in a schema update.
Most importantly, don't allow Add Item by Identifier to create an item
in a feed library...
The other buttons didn't do anything but still showed an effect on
mousedown, which this prevents.
If repo.zotero.org returned a 500 during Zotero startup, translators and
styles wouldn't be initialized until a 200 was returned, and various
things that depended on translators or styles wouldn't work in the
meantime. Now a single retry is made after 5 seconds, and if that fails
the promise is resolved regardless.
Regression from dc60e5f840, which added automatic 5xx retrying to
Zotero.HTTP.request()
Previously, if an object was uploaded but the API returned 'unchanged',
the uploaded data would be written to the sync cache, which, given that
most requests are patch requests, could result in an empty or mostly
empty object being saved to the sync cache. That would cause the next
sync to treat most/all local fields as changed and either upload them
unnecessarily or trigger a conflict instead of merging changes
automatically.
When non-conflicting changes were automatically merged, the local object
would be correctly marked as unsynced, but the merged object rather than
the remote object would be saved to the sync cache. When the object was
then uploaded, it matched the cache version exactly, so an empty patch
object (other than an unchanged dateModified, which is always included)
would be uploaded and the local change wouldn't make it to the server.
The empty patch would result in an 'unchanged' response, which would
cause the empty patch object to be saved to the sync cache (which is a
bug that I'll fix separately). If the local object was modified again,
the patch would include all fields (since the cache object was empty)
and the local change would be uploaded, but there could also be
unnecessary conflicts due to it looking like all local fields had been
modified.
This patch causes the remote object to be saved to the sync cache
instead, so the local change looks like a local edit and is correctly
uploaded.
Don't include linked-object or replaced-item relations. Previously, if
you duplicated an item, modified it to represent a different source, and
dragged it to another library where you had already copied the original
item, the new item wouldn't be transferred.
https://forums.zotero.org/discussion/comment/353246/#Comment_353246
Apparently we implemented this 5 years ago in #873 and then broke it
(with the move to React, I think) without ever announcing it as a new
feature in Zotero 5? Anyway, this restores it, using Cmd on macOS (the
same as for moving vs copying items, or files in Finder).
Since at least 2009, for reasons unclear, we've used `dialog=no` when
`instantApply` is true (macOS/Linux) and `modal` (with implicit
`dialog=yes`) when false (Windows). `dialog=no` doesn't seem to have an
effect on macOS, and `modal` doesn't seem to have an effect on Windows
(at least with `openDialog()`), but on Linux `dialog=no` allows the
dialog to be resized, which it shouldn't be, and causes problems with a
tiling window manager. This change switches to the implicit `dialog=yes`
on all platforms. (On Linux, this also centers the dialog on the screen,
keeps it in front of other windows, and does a weird opening animation.)
https://forums.zotero.org/discussion/82050/zotero-preferences-window-issue-with-tiling-window-managers
This should dramatically improve import speed for large imports by
delaying UI updates until the import finishes.
Additional Zotero.Attachments methods now support `saveOptions` to
support `notifierQueue`.
'podcast' is now the first mapped type for 'broadcast', per
https://forums.zotero.org/discussion/comment/351113/#Comment_351113, so
we need to check for fields not available in 'podcast'. 'radioBroadcast'
will be converted to 'tvBroadcast' on re-import, but that was the case
before.
- When changing type based on 'type:' line, move existing fields that
are no longer valid to Extra
- Remove 'type:' line with CSL type if the item's existing type is one
of the types mapped to it
getAllAddons() waits for extensions to be initialized, so the code in
Prefs::loadExtensionDefaults() to delete an extensions.json with an
AppTranslocation path should be sufficient, and it shouldn't be
necessary to check for it after showing the AppTranslocation startup
error.
If a Mac user first starts Zotero from the disk image, Zotero
shows an error telling them to install the app in Applications, but the
integration plugins will have already be registered in extensions.json
with paths to the temporary AppTranslocation directory. This meant that
the next time the user restarted their computer (or sooner), the AppTranslocation
directory would be cleared and the integration plugins would no longer
be loaded.
This deletes extensions.json if it contains an AppTranslocation path for
an extension when the app is started from the disk image, which should
prevent this going forward. For existing users experiencing this, it
also checks installed extensions at startup for AppTranslocation paths
and deletes extensions.json if any appear.
This shouldn't actually be used currently, since only notes and
attachments lack creators and it shouldn't be possible to change regular
items into notes or attachments.
81739c7a66 prevented new windows from working after a restart error,
and full-text tests (and possibly others) create attachments quickly in
a way that was causing init callbacks to receive a TinyMCE editor with
missing functions (possibly because they were already removed?). This
just ignores those errors, which isn't a great solution, but it probably
won't happen in real use, so let's see if it works.
It shouldn't be possible to nest two collections inside each other, but
if it happens, fix it in the integrity check.
Also detect it from CollectionTreeView::expandToCollection() (used when
showing the collections containing an item) and crash Zotero with a flag
to run an integrity check after restart. Previously, this would result
in an infinite loop.
This may be the cause of some of the collection disappearances people
have reported. If parentCollectionID never leads to a null, the
collection won't appear anywhere in the tree.
TODO:
- Figure out how this is happening
- Detect and fix it automatically for people it's happened to
If you'd never set a Quick Copy locale, the option would show as using
the current locale, but Quick Copy itself would use the last locale from
"Create Bibliography from Items". That was a side effect of behavior we
put in place in 2015 so that documents created before 4.0.27 that relied
on the removed bibliographyLocale pref would continue using the migrated
locale, but now that we've had an explicit locale option for years in
the document preferences I think we can stop doing that.
This prevents a second copy of Zotero from being launched if you're
running the Zotero beta and click on a word processor plugin button
while a dialog is already open.
Previously, files updated remotely wouldn't be downloaded in "as needed"
mode if a copy of the file already existed locally and could only be
re-downloaded by deleting the file via Show File.
This causes remotely modified files that exist locally to be downloaded
at sync time, even in "as needed" mode, by marking them as
"force_download". While this might not be ideal for people who use "as
needed" to limit data transfer, it's better for people who use it simply
to limit local storage, and ending up with an outdated file while
offline seems worse than a little bit of extra data transfer.
In the future, we'll likely also provide ways to explicitly download and
remove files, so keeping chosen files in sync makes sense.
Files modified remotely before this change (which were marked as
"to_download" instead of "force_download") won't be downloaded as sync
time in "as needed" mode, but they'll now be re-downloaded on open.
Fixes#1322
- Use full-text cache file from syncing if available when reindexing via
info pane or Rebuild Index → Index Unindexed Items. Only discard it for
full index rebuild. This allows Index Unindexed Items to be used to
force immediate processing of queued content from syncing and avoids
unnecessary syncing back of identical content. Previously, the cache
file was used for a manual index only when the local file didn't exist.
- When rebuilding index, don't clear indexed items with missing local
file that are missing stats due to a pre-411180ef bug.
- indexItems() now takes an 'options' object as its second parameter
- Minor code cleanup
- Don't clear item's index stats (and show "Unknown") when an item is
reindexed remotely and the content matches the local content
- Always update an item's state and its stats in the same query, to
avoid incorrect feedback immediately after indexing
- Clean up `setItemContent()` tests
This causes problems with filtered test runs, since the request is
cancelled and the long response gets dumped to the console, making the
test result hard to find.
This is a prerequisite for starting to use new fields in translators,
since otherwise switching from, say, storing originalDate in Extra to
using an originalDate field would cause the value to be lost in clients
without the newer schema.
Closes#1504
Follow-up to df40ee7216
- Restore opening of non-HTTP URL schemes in link attachments
- Remove scheme whitelist for link attachments, since it's not enforced
via the API anyway and we prompt before an external application is
opened. Instead, just block a few schemes (e.g., 'javascript') from
launching.
TODO:
- Provide some way to change/reset an application association if the
user checks the box to automatically open that scheme.
- Show an error message if a link attachment with an invalid URL is
double-clicked
E.g., show "Foo Bar" for field fooBar if not available
This could happen after downgrading if the global schema couldn't be
read from the DB for some reason.
- Support launching plausible HTTP URLs without schemes from
Zotero.loadURI(), and use that when launching from URL field
- Show correct cursor feedback on URL label -- only show pointer if the
URL is launchable
- Don't launch non-HTTP URLs (e.g., zotero://) from URL field
- Don't open HTTP URLs in viewer when using ZoteroPane.launchURI()
I'm not totally sure why we didn't do this years ago, but this fixes
performances problems in the new React tags box, and should improve
autocomplete performance generally in large libraries.
And switch to Unicode green checkmark and red prohibited characters on
all platforms, which fixes#1675.
The treecol checkmark stopped working on Windows in Fx60.
It shouldn't be possible for collections to be nested this way, if it
happens, it shouldn't result in an infinite loop.
This removes one of the parent assignments at sync time.
- Properly truncate and ellipsize long tags
- Show scrollbar if tags go off the bottom of the pane
These both improve on the pre-React version, which didn't properly
truncate tags (at least in the current version, though I think it used
to) and scrolled the Add button off the top of the screen.
I don't think this ever should've been here. It was added in 2010 but
then added to itembox.xml a couple weeks later in d500b50e2, since
that's where the conversion should happen.
Closes#1749
Improvements:
- Fixes autocomplete text remaining in field after selection in Fx60
- No more text or icon shifting on select (tested on macOS)
Changes:
- Tags are now selected on mousedown with no active state, as in web
library
Regressions:
- Tooltip with tag type doesn't appear when hovering over icon
- Pressing Tab after modifying a tag loses focus
- Right-click in textbox shows custom menu instead of default text
editing context menu (Cut/Copy/Paste)
To-do:
- Switch to this version for note tags box
- Style colored tags in autocomplete drop-down? Sort to top?
- Only show delete button on row hover, as in web library?
When browserifying anything that depends on React, don't bundle another
copy of React in the file. There might be some other common dependencies
that we should include here.
`intl.regional_prefs.use_os_locales` isn't actually equivalent to
`intl.locale.matchOS` -- an empty string for `intl.locale.requested` is,
and that's set by calling `setRequestedLocales(null)`.
Mozilla documentation:
https://firefox-source-docs.mozilla.org/intl/locale.html
- When upgrading from Fx52, don't show "Automatic" for
a manually selected locale.
- Make sure Zotero.locale is always set to a known language tag
- Fix inconsistencies between Zotero.Locale.availableLocales and
chrome.manifest
When we added the View menu pane options in 3aeef69ad3, we removed
automatic reopening of the panes on startup, since the View menu is more
discoverable than the narrow collapsed splitters. Unfortunately we're
now getting many reports of people not knowing where their panes are.
It turns out the View menu pane options in 5.0.76 are actually broken,
so it's not a great test, but enough people are doing this by accident
and enough of them don't seem to be finding the View menu options
(broken or otherwise) that I think it's better to return to reopening
the panes. We can always add a hidden pref to change that behavior, but
no one has complained in years, so it's probably not necessary.
This was a regression from 4b60c6ca27. The original plan for introducing
new fields was to have them save to the sync cache even if they weren't
supported by the current Zotero version and process them on upgrade, and
so I changed `DataObjectUtilities.patch()` to omit fields that didn't
exist locally when patching the sync cache JSON so they wouldn't be
wiped on the server. That caused this bug where locally deleted fields
were restored on every sync. It's also no longer necessary now that
we decided to just reject unknown fields from saving, so we can just
revert to the previous behavior of blanking out locally missing fields
(with the tweak that fields that are already false or empty in the base
version can be omitted).
- Include file path
- Show separate message for linked files mentioning Linked Attachment
Base Directory
- Link to separate support page for linked files
And fix a couple things for if we turn it back on
This code came along with the type/field handling overhaul, but I think
it was originally intended for handling unknown fields during sync
before we decided on strict mode, so it wasn't finished and causes
various problems [1]. It could still be useful for preserving fields
from translators before they're available on items, but the better fix
there is just to add the missing fields, so I'm not sure if we'll end up
needing it.
[1] https://groups.google.com/d/msg/zotero-dev/a1IPUJ2m_3s/hfmdK2P3BwAJ
We're not currently setting a timeout, so this shouldn't happen, but
someone [1] is getting a timeout error with undefined ms and
NS_BINDING_ABORTED from the channel. It also happened during a /fulltext
upload, so it's not limited to S3, but this fix should give a more
helpful error message for such errors during file syncing.
[1] https://forums.zotero.org/discussion/79286/
We're not migrating fields yet, but when we do, I think marking them as
changed will be the safer option. We'll just have to make sure that
conflicts without differences are resolved automatically on all
platforms.
Say "Use the [local|remote] version for all remaining conflicts" for
everything instead of saying "Use [local|remote] fields for all
remaining conflicts" for some conflicts.
This also fixes a test failure after 54343c49fb.
This allows Zotero.Prefs to be used instead of Services.prefs for pref
observing in plugins.
Zotero.Prefs.prefBranch was replaced by Zotero.Prefs.rootBranch.
Default prefs are no longer read automatically, so we need to do it
manually. Preferences may not be read before extensions load, so they
should wait for Zotero.Schema.schemaUpdatePromise or
Zotero.uiReadyPromise before initializing.
This changes the way item types, item fields, creator types, and CSL
mappings are defined and handled, in preparation for updated types and
fields.
Instead of being predefined in SQL files or code, type/field info is
read from a bundled JSON file shared with other parts of the Zotero
ecosystem [1], referred to as the "global schema". Updates to the
bundled schema file are automatically applied to the database at first
run, allowing changes to be made consistently across apps.
When syncing, invalid JSON properties are now rejected instead of being
ignored and processed later, which will allow for schema changes to be
made without causing problems in existing clients. We considered many
alternative approaches, but this approach is by far the simplest,
safest, and most transparent to the user.
For now, there are no actual changes to types and fields, since we'll
first need to do a sync cut-off for earlier versions that don't reject
invalid properties.
For third-party code, the main change is that type and field IDs should
no longer be hard-coded, since they may not be consistent in new
installs. For example, code should use `Zotero.ItemTypes.getID('note')`
instead of hard-coding `1`.
[1] https://github.com/zotero/zotero-schema
- Move 5xx retries and connection checking out of the sync API client
and into HTTP.request() so that they apply to all requests. 429 handling
remains in the API client, since not all callers necessarily want to
handle that the same way. Callers can still handle 5xx themselves by
including the relevant 5xx status codes in `successCodes` or by passing
`errorDelayMax: 0`.
- Add `cancellerReceiver` option, which is a callback that receives a
function that will cancel the request, whether it's an active request
or an automatic delay before a 5xx retry.
This also updates Sinon to 7.3.2.
- nsIURI is now immutable, so it's necessary to use nsIURIMutator via
mutate() to change it
- .path is replaced with .pathQueryRef
- Only nsIURL has .fileName
The XBL prefwindow bindings are removed in Firefox 60, so this adds them
back, along with necessary styling, to allow the existing preferences to
work until the preferences are rewritten with React. The preferences.xml
file in the Mozilla source has platform ifdefs, but since this is a
temporary hack I've just duplicated the file for each platform with the
necessary lines included.
I haven't yet tested the styling on Windows/Linux.
Use the same version of Firefox that's used to build the standalone
version. Really this should just run fetch_xulrunner.sh itself so that
it stays up to date with any changes to the modifications, but that can
wait until we move zotero-standalone-build into the main repo.
In particular, remove code related to opening/closing the Zotero pane,
which affects tests. The pane is now opened by default in Firefox, which
brings its behavior closer to the main version.
As of Fx60, XPathResult is no longer available as nsIDOMXPathResult in
XPCOM, so just shim its constants, which are all we need, when adding it
to the sandbox.
`nsIFilePicker::show()` is removed in Firefox 60 in favor of `open()`,
which takes a callback (and apparently has been preferred for a long
time).
There's no point switching to that, so this module is a version of
nsIFilePicker with an async `show()` that returns a promise and some
XPCOM-isms replaced (e.g., string paths instead of nsIFile).
Pulling from chrome/content/zotero/modules/
This may or may not be what we want to do, but it's better than
intermingling our modules with third-party modules in 'resource/'.
Legacy/unsigned add-on warnings are now hidden via CSS in the Firefox
build, so remove the code that tries to do that, and instead add a class
to any other warnings so they can be selectively shown.
StopIteration is no longer supported in Firefox 60, so instead of taking
a generator function that might throw StopIteration for the second
parameter, take a function that is passed to iterator.forEach() that
receives an OS.File.DirectoryIterator.Entry for each directory entry. If
the function returns a promise, it's waited for.
Also update other direct uses of OS.File.DirectoryIterator to remove
StopIteration use.
nsIURL doesn't seem to work anymore, so add Zotero.Utilities.parseURL(),
which uses the `url` package from NPM and adds fileName, fileExtension,
and fileBaseName.
fetch_xulrunner.sh updates the built-in path to point to this file, but
it's not currently being read properly for some reason (and I'm not sure
it does anything we need).
- getStringPref/setStringPref are now used for strings instead of
getComplexValue/setComplexValue
- Remove nsIPrefBranch2 reference
- If there was a pref failure during initialization, nothing was logged
to the terminal
- The Mozilla CommonJS loader is no longer available, so bundle the
Fx52 version of it
- Strict mode is enforced
- `this` is only defined as a global object in .jsm files, not .js files
- `this` can't be converted to a string for BackstagePass test, so check
for presence of Components.utils.import instead
- The return value from import() is no longer available
The menu includes all user interface options from the General
preferences (now removed from the preferences), toggles for the
collections pane, item pane, and tag selector, and, at long last, a
toggle for recursive collections ("Display Items from Subcollections").
The collections pane and item pane no longer reopen automatically when
restarting Zotero. People might still close them by mistake and not find
this menu, but we'll see how it goes.
Closes#1372
When the filename limit isn't 255 on Linux, it's probably because of
eCryptfs, but we were checking the character length instead of the byte
length before shortening the filename to 143 bytes.
If a parent item was expanded and all items in the tree, including the
item's child items, were selected, Cmd + Left Arrow would break the
items tree until restart.
This will cause a conflict and need to be applied to the new tree.
(Worth noting that collapseSelectedRows() is currently only called on
Cmd-left-arrow because the XUL tree seemingly swallows a regular
left-arrow (and only applies the collapse to the last-selected row). The
comment in the keypress listener where collapseSelectedRows() is called
suggests that that was meant to be used for all left-arrow keypresses,
but either that stopped working at some point or it only ever worked for
Cmd + Left Arrow because it bypasses the normal tree handling of left-
arrow. In any case, it would be better if left-arrow always collapsed
selected rows, with or without Cmd.)
https://forums.zotero.org/discussion/78515/bug-collapsing-all-items-in-a-collection-breaks-display-of-items-in-all-collections
And move Error Console and Run JavaScript to a Developer menu that's
shown in Tools if that option is enabled.
The Memory Info button is now behind its own undefined
extensions.zotero.debug.memoryInfo pref.
Zotero.Translate::setTranslatorProviderMethods(methods) can be used to
provide custom 'get' and 'getAllForType' methods that override the
default Zotero.Translators methods.
I don't know how this is happening -- people are ending up with an
updated DB version but missing schema changes, despite the DB version
being updated after the schema updates and in the same transaction --
but until we figure it out, apply the schema update steps in
Zotero.Retractions.init() if necessary.
Separate flags for hiding the retraction altogether and for hiding
citation warnings for it
New functions:
Zotero.Retractions.hideRetraction(item)
Zotero.Retractions.shouldShowCitationWarning(item)
Zotero.Retractions.disableCitationWarningsForItem(item)
Addresses #1710
Without an accompanying "Restore from Online Library" option, which
doesn't currently exist, "Restore to Online Library" can be interpreted
two ways. It's clear if you read the text below or the confirmation
prompt, but if you don't you might accidentally do something very bad.
This was almost certainly also translated in misleading ways in other
locales.
If a retracted item was in the trash at startup or when detected and
there were no other retracted items in the library, an error would occur
if the item was modified.
This shouldn't be possible, but there've been a couple reports of people
ending up on version 103 without the table, so create it again with IF
NOT EXISTS. This is obviously a bad fix, but until we know how this
happened it's the best we can do.
Zotero does not use GitHub Issues for bug reports or feature requests.
Please post all such requests to the Zotero Forums at https://forums.zotero.org, where Zotero developers and many others can help. For confirmed bugs or agreed-upon changes, Zotero developers will create new issues in the relevant repositories.
See https://www.zotero.org/support/zotero_support for more information on how Zotero support works.
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Zotero is a Firefox-based desktop research management application. It runs as a XUL/XHTML application on a Mozilla platform (not Electron, not a web app). The UI uses a mix of XUL/XHTML, custom elements (Web Components), and React components.
## Build & Development Commands
```bash
npm install # Install dependencies
npm run build # Full build (Babel transpile JS/JSX, compile SCSS, browserify, symlink)
npm run clean-build # Clean build output and rebuild
```
### Running Tests
Tests run inside a built Zotero application instance via `test/runtests.sh`:
```bash
test/runtests.sh # Run all tests
test/runtests.sh item # Run a single test file (omit "Test" and .js extension)
test/runtests.sh item collections # Run multiple test files
test/runtests.sh -g "pattern" # Run only tests matching a grep pattern
test/runtests.sh -f # Stop after first failure
test/runtests.sh -s item # Start at a specific test file
test/runtests.sh -e item # End at a specific test file
test/runtests.sh -c # Open JS console, don't quit on completion
test/runtests.sh -b # Skip bundled translator/style installation (for faster startup when not necessary)
Tests use Mocha + Chai (assert style) + Sinon. Test files live in `test/tests/` and follow the naming convention `<module>Test.js`. Test helpers are in `test/content/support.js`.
The test runner automatically triggers `npm run build` if the watch process isn't running, so it's not necessary to manually run a build before running tests.
Tips:
- Pass flags before test names (e.g., `test/runtests.sh -f item`).
- `-f` should almost always be used, since test failures can cause later spurious test failures.
- To view debug logging for a specific test when running multiple tests, add `Zotero.Debug.init(1)` at the beginning of the test and run with `-d 5`.
### Linting
```bash
npx eslint <file> # Lint a specific file
```
ESLint v9 flat config is in `eslint.config.mjs`. Uses `@zotero/eslint-config`, Babel parser, and React plugin. Currently, the code base is not fully linted, so only worry about issues with new or modified code lines.
## Architecture
### Core Layers
- **XPCOM modules** (`chrome/content/zotero/xpcom/`) -- Core business logic. Loaded sequentially by `chrome/content/zotero/zotero.mjs`. All modules attach to the global `Zotero` namespace (e.g., `Zotero.Items`, `Zotero.Sync.Runner`).
- **Data model** (`chrome/content/zotero/xpcom/data/`) -- ORM-like classes for database entities. `Zotero.DataObject` is the base class; `Zotero.Item`, `Zotero.Collection`, `Zotero.Search`, `Zotero.Library`, etc. extend it. Plural classes (`Zotero.Items`, `Zotero.Collections`) manage object caches and queries. Data objects use an async `saveTx()` pattern for persistence.
- **Database** (`chrome/content/zotero/xpcom/db.js`) -- SQLite via Mozilla's mozStorage API. Accessed through `Zotero.DB.queryAsync()`, `Zotero.DB.executeTransaction()`.
- **Sync system** (`chrome/content/zotero/xpcom/sync/`) -- `syncRunner.js` orchestrates sync. `syncEngine.js` handles object-level sync logic. `syncAPIClient.js` communicates with the Zotero API server. `syncLocal.js` manages local sync state. Similar `storage*` files for file syncing, along with zfs.js (Zotero Storage) and webdav.js (WebDAV).
- **HTTP server** (`chrome/content/zotero/xpcom/server/`) -- Local HTTP server for browser connector integration and local API.
- **Custom elements** (`chrome/content/zotero/elements/`) -- XUL custom elements inheriting from `XULElementBase` (defined in `elements/base.js`), which provides lifecycle helpers (`init()`, `destroy()`, `content` getter for templates). Registered via `customElements.define()`.
- **React components** (`chrome/content/zotero/components/`) -- Used for complex interactive UI (tag selector, virtualized table, item tree, collection tree). Major tree views are `itemTree.jsx` and `collectionTree.jsx`.
- **SCSS styles** (`scss/`) -- Compiled to CSS. Platform-specific overrides in `scss/mac/`, `scss/win/`, `scss/linux/`.
- **Localization** -- Fluent (`.ftl` files) in `chrome/locale/en-US/zotero/`. Accessed via `data-l10n-id` attributes or `Zotero.getString()` for legacy `.properties` strings. All new strings must be added to `.ftl` files -- do not add new strings to `.properties` or `.dtd` files.
### Submodules
Several features are developed in separate repos and included as Git submodules:
- `reader/` -- PDF/EPUB/snapshot reader with annotations
- `note-editor/` -- Rich text note editor
- `document-worker/` -- PDF processing (extraction, manipulation)
- `translators/` -- 760+ web translators for importing metadata from websites
Custom Node.js build system (not Webpack). `js-build/config.js` defines what gets built:
- **JS/JSX files** in `chrome/`, `components/`, `defaults/`, `test/` are transpiled via Babel (React JSX, CommonJS modules)
- **SCSS files** in `scss/` and `chrome/skin/` are compiled with Dart Sass
- **Browserify** bundles select npm packages (react-select, sinon, url) for use in the XUL environment
- **Symlinks** connect source files and submodules into the `build/` directory
- The built app reads from `build/`, not directly from source
## Code Style
- Tabs for indentation, not spaces
- Use `let` instead of `const` except for true scalar constants (e.g., `const MAX_SECONDS = 5 * 60;`)
- No cuddled braces (opening brace on same line, but `else`/`catch`/etc. on their own line)
- Use two hyphens `--` in comments, not an em dash
- Indent blank lines to match surrounding indentation level
- Objects attach to the `Zotero` global namespace rather than using ES module exports
- Async code uses `async`/`await` throughout
- Mozilla/XPCOM APIs are available globally: `Cc`, `Ci`, `Cu`, `Cr`, `Services`, `ChromeUtils`, `IOUtils`, `PathUtils`
## Translators
See `translators/CLAUDE.md` for guidelines. Key rules: **never** generate translators from scratch, **never** generate UUIDs, **never** generate test cases -- all of these must be done through Zotero's Scaffold tool.
Zotero does not use GitHub Issues for bug reports or feature requests. Please post all such requests to the [Zotero Forums](https://forums.zotero.org), where Zotero developers and many others can help. See [How Zotero Support Works](https://www.zotero.org/support/zotero_support) for more information.
Zotero does not use GitHub Issues for bug reports, feature requests, or support questions. Please post all such requests to the [Zotero Forums](https://forums.zotero.org), where Zotero developers and many others can help. See [How Zotero Support Works](https://www.zotero.org/support/zotero_support) for more information.
For confirmed bugs or agreed-upon changes, Zotero developers will create new issues in the relevant repositories.
[Zotero](https://www.zotero.org/) is a free, easy-to-use tool to help you collect, organize, cite, and share your research sources.
Please post feature requests or bug reports to the [Zotero Forums](https://forums.zotero.org/). If you're having trouble with Zotero, see [Getting Help](https://www.zotero.org/support/getting_help).
For more information on how to use this source code, see the [Zotero wiki](https://www.zotero.org/support/dev/source_code).
For more information on how to use this source code, see the [Zotero documentation](https://www.zotero.org/support/dev/source_code).
These files are used to bundle the [Zotero core](https://github.com/zotero/zotero) into distributable bundles for Mac, Windows, and Linux.
Instructions for building and packaging are available on the [Zotero wiki](https://www.zotero.org/support/dev/client_coding/building_the_standalone_client).
print("%s\n\tis a duplicate of\n%s\n---"%(entry.filename,crc_mapping[entry.crc32]))
else:
crc_mapping[entry.crc32]=entry.filename;
ifinlogisnotNone:
ifwritten_count==reordered_count:
readahead=out_offset
print("%s: startup data ends at byte %d"%(outjar,readahead));
elifwritten_count<reordered_count:
pass
#print("%s @ %d" % (entry.filename, out_offset))
elifreadahead>=old_entry_offset+len(data):
outlog.append(entry.filename)
reordered_count+=1
ifinlogisNone:
dirend.cdir_offset=out_offset
ifdups_found>0:
print("WARNING: Found %d duplicate files taking %d bytes"%(dups_found,dupe_bytes))
dirend.cdir_size=len(cdir_data)
dirend.disk_entries=dirend.cdir_entries
dirend_data=dirend.pack()
assert_true(size_of(cdir_end)==len(dirend_data),"Failed to serialize directory end correctly. Serialized size;%d, expected:%d"%(len(dirend_data),size_of(cdir_end)));
outfd.seek(dirend.cdir_offset)
outfd.write(cdir_data)
outfd.write(dirend_data)
# for ordered jars the central directory is written in the begining of the file, so a second central-directory
# entry has to be written in the end of the file
ifinlogisnotNone:
outfd.seek(0)
outfd.write(struct.pack("<I",readahead));
outfd.seek(out_offset)
outfd.write(dirend_data)
print("Stripped %d bytes"%total_stripped)
print("%s%d/%d in %s"%(("Ordered"ifinlogisnotNoneelse"Deoptimized"),
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
pass
parser = argparse.ArgumentParser(
description='Prepare build/ files for the app build process',
formatter_class=CustomFormatter)
parser.add_argument('--source-dir', '-s', required=True, metavar='BUILD_DIR', help='Directory to build from')
parser.add_argument('--output-dir', '-o', required=True, metavar='OUTPUT_DIR', help='Directory to write files to')
parser.add_argument('-c', '--channel', default='source', help='channel to add to dev build version number (e.g., "beta" for "5.0-beta.3+a5f28ca8"), or "release" or "source" to skip')
parser.add_argument('--commit-hash', '-m', metavar='HASH', help='Commit hash (required for non-release builds)')