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.