From fb20db9a5885a2a796f98a263e2b5492539c0259 Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Mon, 29 Dec 2025 15:55:18 -0500 Subject: [PATCH] Delay Extra field migration until after auto-sync and show progress 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. --- chrome/content/zotero/xpcom/schema.js | 42 ++++++++++++++++++------ chrome/content/zotero/xpcom/zotero.js | 46 +++++++++++++++++++++++++-- chrome/locale/en-US/zotero/zotero.ftl | 2 ++ 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/chrome/content/zotero/xpcom/schema.js b/chrome/content/zotero/xpcom/schema.js index 9c99daa036..93d7e75e63 100644 --- a/chrome/content/zotero/xpcom/schema.js +++ b/chrome/content/zotero/xpcom/schema.js @@ -671,7 +671,7 @@ Zotero.Schema = new function () { * This needs the data object architecture to be initialized, so it's called from zotero.js * rather than in _updateGlobalSchema(). */ - this.migrateExtraFields = async function () { + this.migrateExtraFields = async function ({ onProgress }) { // Check for a flag set by _updateGlobalSchema() var needsUpdate = await Zotero.DB.valueQueryAsync( "SELECT COUNT(*) FROM settings WHERE setting='globalSchema' AND key='migrateExtra'" @@ -680,11 +680,14 @@ Zotero.Schema = new function () { return; } + Zotero.debug("Migrating fields from Extra"); + var fieldID = Zotero.ItemFields.getID('extra'); var sql = "SELECT itemID, value FROM itemData " + "JOIN itemDataValues USING (valueID) " + "WHERE fieldID=?"; var rows = await Zotero.DB.queryAsync(sql, fieldID); + var itemIDs = []; for (let row of rows) { let { itemType, fields, creators } = Zotero.Utilities.Internal.extractExtraFields( @@ -696,14 +699,35 @@ Zotero.Schema = new function () { } var items = await Zotero.Items.getAsync(itemIDs); - await Zotero.Items.loadDataTypes(items); - for (let item of items) { - let changed = item.migrateExtraFields(); - if (!changed) continue; - await item.saveTx({ - skipDateModifiedUpdate: true, - skipSelect: true - }); + + Zotero.debug(`${items.length} items to migrate`); + + if (items.length) { + await Zotero.Items.loadDataTypes(items); + + let progress = 0; + let progressMax = items.length; + if (onProgress) { + onProgress({ progress, progressMax }); + } + + let notifierQueue = new Zotero.Notifier.Queue; + try { + for (let item of items) { + let changed = item.migrateExtraFields(); + if (changed) { + await item.saveTx({ + skipDateModifiedUpdate: true, + skipSelect: true, + notifierQueue + }); + } + onProgress({ progress: ++progress, progressMax }); + } + } + finally { + await Zotero.Notifier.commit(notifierQueue); + } } await Zotero.DB.queryAsync( diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js index dc0039528e..4687b3a2fb 100644 --- a/chrome/content/zotero/xpcom/zotero.js +++ b/chrome/content/zotero/xpcom/zotero.js @@ -745,9 +745,6 @@ const { CommandLineOptions } = ChromeUtils.importESModule("chrome://zotero/conte } }); - // Migrate fields from Extra that can be moved to item fields after a schema update - await Zotero.Schema.migrateExtraFields(); - Zotero.Items.startEmptyTrashTimer(); Zotero.QuickCopy.init(); @@ -758,6 +755,49 @@ const { CommandLineOptions } = ChromeUtils.importESModule("chrome://zotero/conte Zotero.Schema.schemaUpdatePromise.then(Zotero.purgeDataObjects.bind(Zotero)); + // Migrate fields from Extra that can be moved to item fields after a schema update + // + // To test migration after the server has been updated, disable auto-sync and uncomment + // this line instead of the following one + //Zotero.Schema.schemaUpdatePromise.then(async () => { + Zotero.startupSyncPromise.then(async () => { + let progressWin; + let itemProgress; + // Feed updates (e.g., deleting old items) can interfere with this, so pause them + // until we're done + let feedPauser = await Zotero.Feeds.pause(); + try { + await Zotero.Schema.migrateExtraFields({ + onProgress: ({ progress, progressMax }) => { + if (!progressWin) { + progressWin = new Zotero.ProgressWindow({ + closeOnClick: false + }); + let title = Zotero.getString('upgrade.status'); + progressWin.changeHeadline(title); + itemProgress = new progressWin.ItemProgress( + 'journalArticle', + Zotero.getString('migrate-extra-fields-progress-message') + ); + progressWin.show(); + } + + itemProgress.setProgress(progress / progressMax * 100); + } + }); + } + catch (e) { + Zotero.logError(e); + itemProgress.setError(); + } + finally { + feedPauser.resume(); + } + if (progressWin) { + progressWin.startCloseTimer(3000); + } + }); + return true; } catch (e) { diff --git a/chrome/locale/en-US/zotero/zotero.ftl b/chrome/locale/en-US/zotero/zotero.ftl index 082c3e94a1..083edfa2e2 100644 --- a/chrome/locale/en-US/zotero/zotero.ftl +++ b/chrome/locale/en-US/zotero/zotero.ftl @@ -841,6 +841,8 @@ connector-version-warning = The { -app-name } Connector must be updated to work userjs-pref-warning = Some { -app-name } settings have been overridden using an unsupported method. { -app-name } will revert them and restart. +migrate-extra-fields-progress-message = Migrating new fields from Extra field + long-tag-fixer-window-title = .title = Split Tags long-tag-fixer-button-dont-split =