Throw an error when an update to word processor plugin fails to install (#4579)
- 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
This commit is contained in:
parent
2a0d245018
commit
aef0e51186
2 changed files with 113 additions and 66 deletions
|
@ -13,6 +13,7 @@ general-open-settings = Open Settings
|
||||||
general-help = Help
|
general-help = Help
|
||||||
general-tag = Tag
|
general-tag = Tag
|
||||||
general-done = Done
|
general-done = Done
|
||||||
|
general-view-troubleshooting-instructions = View Troubleshooting Instructions
|
||||||
|
|
||||||
menu-file-show-in-finder =
|
menu-file-show-in-finder =
|
||||||
.label = Show in Finder
|
.label = Show in Finder
|
||||||
|
|
|
@ -30,67 +30,100 @@
|
||||||
var EXPORTED_SYMBOLS = ["ZoteroPluginInstaller"];
|
var EXPORTED_SYMBOLS = ["ZoteroPluginInstaller"];
|
||||||
|
|
||||||
var { Zotero } = ChromeUtils.importESModule("chrome://zotero/content/zotero.mjs");
|
var { Zotero } = ChromeUtils.importESModule("chrome://zotero/content/zotero.mjs");
|
||||||
|
var { setTimeout } = ChromeUtils.importESModule("resource://gre/modules/Timer.sys.mjs");
|
||||||
|
|
||||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
var installationInProgress = false;
|
var installationInProgress = false;
|
||||||
|
|
||||||
|
// Prefs:
|
||||||
|
// version - last successfully installed version
|
||||||
|
// lastAttemptedVersion - last version that was attempted to automatically install
|
||||||
|
// skipInstallation - if user cancels an attempt to automatically install the plugin, we do not
|
||||||
|
// automatically install it again until a successful manual installation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic plugin installation orchestrator for word processor installers
|
||||||
|
* @param addon {Object}
|
||||||
|
* @param failSilently {Boolean} whether the installation should not throw errors (typically when installing automatically)
|
||||||
|
* @param force {Boolean} force install even if the plugin version is up-to-date
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
var ZoteroPluginInstaller = function (addon, failSilently, force) {
|
var ZoteroPluginInstaller = function (addon, failSilently, force) {
|
||||||
this._addon = addon;
|
this._addon = addon;
|
||||||
this.failSilently = failSilently;
|
this.failSilently = failSilently;
|
||||||
this.force = force;
|
this.force = force;
|
||||||
|
|
||||||
this.prefBranch = Services.prefs.getBranch(this._addon.EXTENSION_PREF_BRANCH);
|
this.prefBranch = Services.prefs.getBranch(this._addon.EXTENSION_PREF_BRANCH);
|
||||||
// Prefs:
|
|
||||||
// version - last successfully installed version
|
|
||||||
// skipInstallation - if user cancels an attempt to automatically install the plugin, we do not
|
|
||||||
// attempt to automatically install it again until a successful installation
|
|
||||||
|
|
||||||
this.prefPaneDoc = null;
|
this.prefPaneDoc = null;
|
||||||
|
this._errorDisplayed = false;
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
};
|
};
|
||||||
|
|
||||||
ZoteroPluginInstaller.prototype = {
|
ZoteroPluginInstaller.prototype = {
|
||||||
init: async function () {
|
init: async function () {
|
||||||
if (this._initialized) return;
|
this.debug('Fetching addon info');
|
||||||
Zotero.debug("PluginInstaller: fetching addon info");
|
|
||||||
Zotero.debug("PluginInstaller: addon info fetched");
|
|
||||||
|
|
||||||
|
this._currentPluginVersion = (await Zotero.File.getContentsFromURLAsync(this._addon.VERSION_FILE)).trim();
|
||||||
|
this.debug('Addon info fetched');
|
||||||
|
let lastInstalledVersion = this.prefBranch.getCharPref("version");
|
||||||
|
let lastAttemptedVersion = this.prefBranch.getCharPref("lastAttemptedVersion", "");
|
||||||
|
let lastPluginFileVersion = this._addon.LAST_INSTALLED_FILE_UPDATE;
|
||||||
|
const newVersionSinceLastInstall = Services.vc.compare(lastInstalledVersion, lastPluginFileVersion) < 0;
|
||||||
|
const newVersionSinceLastAttempt = Services.vc.compare(lastAttemptedVersion, lastPluginFileVersion) < 0;
|
||||||
|
const shouldSkipInstallation = this.prefBranch.getBoolPref("skipInstallation");
|
||||||
|
if (this.force) {
|
||||||
|
this.debug('Force-installing');
|
||||||
|
// Should never fail silently
|
||||||
|
this.failSilently = false;
|
||||||
|
return this.install();
|
||||||
|
}
|
||||||
|
else if (shouldSkipInstallation) {
|
||||||
|
this.debug('Skipping automatic installation because skipInstallation is true');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newVersionSinceLastAttempt) {
|
||||||
|
this.debug('New version since last attempt to install. Will display prompt upon failure.');
|
||||||
|
this.failSilently = false;
|
||||||
|
this.prefBranch.setCharPref("lastAttemptedVersion", this._currentPluginVersion);
|
||||||
|
return this.install();
|
||||||
|
}
|
||||||
|
else if (newVersionSinceLastInstall) {
|
||||||
|
this.debug('New version since last successful install. Attempting to install silently.');
|
||||||
|
return this.install();
|
||||||
|
}
|
||||||
|
this.debug('No new updates');
|
||||||
|
},
|
||||||
|
|
||||||
|
install: async function () {
|
||||||
|
if (installationInProgress) {
|
||||||
|
this.debug('Extension installation is already in progress');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
installationInProgress = true;
|
||||||
try {
|
try {
|
||||||
this._version = (await Zotero.File.getContentsFromURLAsync(this._addon.VERSION_FILE)).trim();
|
if (!this._addon.DISABLE_PROGRESS_WINDOW) {
|
||||||
var version = this.prefBranch.getCharPref("version");
|
this._progressWindow = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
||||||
if (this.force || (Services.vc.compare(version, this._addon.LAST_INSTALLED_FILE_UPDATE) < 0
|
.getService(Components.interfaces.nsIWindowWatcher)
|
||||||
&& !this.prefBranch.getBoolPref("skipInstallation"))) {
|
.openWindow(null, "chrome://zotero/content/progressWindow.xhtml", '',
|
||||||
if (installationInProgress) {
|
"chrome,resizable=no,close=no,centerscreen", null);
|
||||||
Zotero.debug(`${this._addon.APP} extension installation is already in progress`);
|
this._progressWindow.addEventListener("load", () => this._firstRunListener(), false);
|
||||||
return;
|
}
|
||||||
}
|
else {
|
||||||
|
let result = this._addon.install(this);
|
||||||
installationInProgress = true;
|
if (result.then) await result;
|
||||||
if (!this._addon.DISABLE_PROGRESS_WINDOW) {
|
|
||||||
this._progressWindow = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
|
||||||
.getService(Components.interfaces.nsIWindowWatcher)
|
|
||||||
.openWindow(null, "chrome://zotero/content/progressWindow.xhtml", '',
|
|
||||||
"chrome,resizable=no,close=no,centerscreen", null);
|
|
||||||
this._progressWindow.addEventListener("load", () => this._firstRunListener(), false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let result = this._addon.install(this);
|
|
||||||
if (result.then) await result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
Zotero.logError(e);
|
Zotero.logError(e);
|
||||||
|
this.error(e);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
installationInProgress = false;
|
installationInProgress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._initialized = true;
|
|
||||||
},
|
},
|
||||||
_errorDisplayed: false,
|
|
||||||
|
|
||||||
isInstalled: function () {
|
isInstalled: function () {
|
||||||
return !!this.prefBranch.getCharPref("version");
|
return !!this.prefBranch.getCharPref("version");
|
||||||
|
@ -105,51 +138,62 @@ ZoteroPluginInstaller.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
success: function () {
|
success: function () {
|
||||||
|
this.debug(`Installation was successful. Version ${this._currentPluginVersion}`);
|
||||||
installationInProgress = false;
|
installationInProgress = false;
|
||||||
this.closeProgressWindow();
|
this.closeProgressWindow();
|
||||||
this.prefBranch.setCharPref("version", this._version);
|
this.prefBranch.setCharPref("version", this._currentPluginVersion);
|
||||||
this.updateInstallStatus();
|
this.updateInstallStatus();
|
||||||
this.prefBranch.setBoolPref("skipInstallation", false);
|
this.prefBranch.setBoolPref("skipInstallation", false);
|
||||||
if (this.force && !this._addon.DISABLE_PROGRESS_WINDOW) {
|
if (this.force && !this._addon.DISABLE_PROGRESS_WINDOW) {
|
||||||
var addon = this._addon;
|
setTimeout(() => {
|
||||||
setTimeout(function () {
|
|
||||||
Services.prompt.alert(
|
Services.prompt.alert(
|
||||||
null,
|
null,
|
||||||
addon.EXTENSION_STRING,
|
this._addon.EXTENSION_STRING,
|
||||||
Zotero.getString("zotero.preferences.wordProcessors.installationSuccess")
|
Zotero.getString("zotero.preferences.wordProcessors.installationSuccess")
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
error: function (error, notFailure) {
|
error: async function (error, notFailure) {
|
||||||
|
this.debug(`Installation failed with error ${error}`);
|
||||||
installationInProgress = false;
|
installationInProgress = false;
|
||||||
this.closeProgressWindow();
|
this.closeProgressWindow();
|
||||||
if (!notFailure) {
|
if (notFailure) {
|
||||||
this.prefBranch.setCharPref("version", this._version);
|
this.prefBranch.setCharPref("version", this._currentPluginVersion);
|
||||||
this.updateInstallStatus();
|
this.updateInstallStatus();
|
||||||
}
|
}
|
||||||
if (this.failSilently) return;
|
if (this.failSilently) {
|
||||||
|
this.debug('Not displaying error because failSilently is true');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this._errorDisplayed) return;
|
if (this._errorDisplayed) return;
|
||||||
this._errorDisplayed = true;
|
this._errorDisplayed = true;
|
||||||
var addon = this._addon;
|
let errorMessage = await Zotero.getString("zotero.preferences.wordProcessors.installationError", [
|
||||||
setTimeout(function () {
|
this._addon.APP,
|
||||||
|
Zotero.appName
|
||||||
|
]);
|
||||||
|
if (error) {
|
||||||
|
errorMessage += "\n\n" + error;
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
var ps = Services.prompt;
|
var ps = Services.prompt;
|
||||||
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK
|
var buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) + ps.BUTTON_POS_0_DEFAULT
|
||||||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING);
|
+ (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL);
|
||||||
var result = ps.confirmEx(null,
|
var result = ps.confirmEx(null,
|
||||||
addon.EXTENSION_STRING,
|
this._addon.EXTENSION_STRING,
|
||||||
(error ? error : Zotero.getString("zotero.preferences.wordProcessors.installationError", [addon.APP, Zotero.appName])),
|
errorMessage,
|
||||||
buttonFlags, null,
|
buttonFlags,
|
||||||
Zotero.getString('zotero.preferences.wordProcessors.manualInstallation.button'),
|
Zotero.getString('general-view-troubleshooting-instructions'),
|
||||||
null, null, {});
|
null, null, null, {});
|
||||||
if (result == 1) {
|
if (result == 0) {
|
||||||
Zotero.launchURL("https://www.zotero.org/support/word_processor_plugin_manual_installation");
|
Zotero.launchURL("https://www.zotero.org/support/kb/word_processor_plugin_installation_error");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelled: function (dontSkipInstallation) {
|
cancelled: function (dontSkipInstallation) {
|
||||||
|
this.debug('Installation cancelled');
|
||||||
installationInProgress = false;
|
installationInProgress = false;
|
||||||
this.closeProgressWindow();
|
this.closeProgressWindow();
|
||||||
if (!this.force && !dontSkipInstallation) this.prefBranch.setBoolPref("skipInstallation", true);
|
if (!this.force && !dontSkipInstallation) this.prefBranch.setBoolPref("skipInstallation", true);
|
||||||
|
@ -181,8 +225,8 @@ ZoteroPluginInstaller.prototype = {
|
||||||
? Zotero.getString('zotero.preferences.wordProcessors.reinstall', this._addon.APP)
|
? Zotero.getString('zotero.preferences.wordProcessors.reinstall', this._addon.APP)
|
||||||
: Zotero.getString('zotero.preferences.wordProcessors.install', this._addon.APP));
|
: Zotero.getString('zotero.preferences.wordProcessors.install', this._addon.APP));
|
||||||
|
|
||||||
button.addEventListener("command", function () {
|
button.addEventListener("command", () => {
|
||||||
Zotero.debug(`Install button pressed for ${addon.APP} plugin`);
|
this.debug('Install button pressed');
|
||||||
try {
|
try {
|
||||||
var zpi = new ZoteroPluginInstaller(addon, false, true);
|
var zpi = new ZoteroPluginInstaller(addon, false, true);
|
||||||
zpi.showPreferences(document);
|
zpi.showPreferences(document);
|
||||||
|
@ -218,22 +262,24 @@ ZoteroPluginInstaller.prototype = {
|
||||||
: Zotero.getString('zotero.preferences.wordProcessors.install', this._addon.APP));
|
: Zotero.getString('zotero.preferences.wordProcessors.install', this._addon.APP));
|
||||||
},
|
},
|
||||||
|
|
||||||
_firstRunListener: function () {
|
_firstRunListener: async function () {
|
||||||
this._progressWindowLabel = this._progressWindow.document.getElementById("progress-label");
|
this._progressWindowLabel = this._progressWindow.document.getElementById("progress-label");
|
||||||
this._progressWindowLabel.value = Zotero.getString('zotero.preferences.wordProcessors.installing', this._addon.EXTENSION_STRING);
|
this._progressWindowLabel.value = Zotero.getString('zotero.preferences.wordProcessors.installing', this._addon.EXTENSION_STRING);
|
||||||
this._progressWindow.sizeToContent();
|
this._progressWindow.sizeToContent();
|
||||||
setTimeout(() => {
|
await Zotero.Promise.delay(100);
|
||||||
this._progressWindow.focus();
|
this._progressWindow.focus();
|
||||||
setTimeout(async () => {
|
await Zotero.Promise.delay(500);
|
||||||
this._progressWindow.focus();
|
this._progressWindow.focus();
|
||||||
try {
|
try {
|
||||||
await this._addon.install(this);
|
await this._addon.install(this);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
this.error();
|
Zotero.logError(e);
|
||||||
throw e;
|
this.error(e);
|
||||||
}
|
}
|
||||||
}, 500);
|
|
||||||
}, 100);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
debug: function (message) {
|
||||||
|
Zotero.debug(`PluginInstaller ${this._addon.APP}: ${message}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue