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/
(cherry picked from commit 06e16c297c)
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
(cherry picked from commit cfd5596814)
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
(cherry picked from commit 70c4cadab1)
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
(cherry picked from commit 8f44172342)
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".
(cherry picked from commit 504447de41)
Add the registered main key to menu class list so that they can be found when plugin is removed.
Add test for the fix.
(cherry picked from commit 55672ba933)
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
- 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
- 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
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.
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)