mirror of https://github.com/keeweb/keeweb.git
updating plugins
This commit is contained in:
parent
782488f8fa
commit
c084746b3e
|
@ -455,6 +455,8 @@
|
|||
"setPlInstallBtn": "Install",
|
||||
"setPlInstallBtnProgress": "Installing",
|
||||
"setPlUninstallBtn": "Uninstall",
|
||||
"setPlUpdateBtn": "Update",
|
||||
"setPlUpdating": "Updating",
|
||||
"setPlLocaleBtn": "Switch to this language",
|
||||
"setPlThemeBtn": "Switch to this theme",
|
||||
"setPlJs": "code",
|
||||
|
@ -463,6 +465,7 @@
|
|||
"setPlCreatedBy": "created by {}",
|
||||
"setPlLoadTime": "took {} to load",
|
||||
"setPlLoadError": "error loading plugin",
|
||||
"setPlUpdateError": "There was an error during update",
|
||||
|
||||
"setAboutTitle": "About",
|
||||
"setAboutBuilt": "This app is built with these awesome tools",
|
||||
|
|
|
@ -9,6 +9,7 @@ const PluginManager = Backbone.Model.extend({
|
|||
state: '',
|
||||
installing: null,
|
||||
uninstalling: null,
|
||||
updating: null,
|
||||
lastInstall: null,
|
||||
plugins: new PluginCollection()
|
||||
},
|
||||
|
@ -33,15 +34,15 @@ const PluginManager = Backbone.Model.extend({
|
|||
install(url) {
|
||||
const lastInstall = { url, dt: new Date() };
|
||||
this.set({ installing: url, lastInstall: lastInstall });
|
||||
return Plugin.loadFromUrl(url).then(plugin =>
|
||||
this.uninstall(plugin.id).then(() => {
|
||||
return Plugin.loadFromUrl(url).then(plugin => {
|
||||
return this.uninstall(plugin.id).then(() => {
|
||||
return plugin.install().then(() => {
|
||||
this.get('plugins').push(plugin);
|
||||
this.set({ installing: null });
|
||||
this.saveState();
|
||||
});
|
||||
})
|
||||
).catch(e => {
|
||||
});
|
||||
}).catch(e => {
|
||||
this.set({ installing: null, lastInstall: _.extend(lastInstall, { error: e.toString() }) });
|
||||
throw e;
|
||||
});
|
||||
|
@ -61,8 +62,34 @@ const PluginManager = Backbone.Model.extend({
|
|||
});
|
||||
},
|
||||
|
||||
update(id) {
|
||||
const plugins = this.get('plugins');
|
||||
const oldPlugin = plugins.get(id);
|
||||
if (!oldPlugin) {
|
||||
return Promise.reject();
|
||||
}
|
||||
const url = oldPlugin.get('url');
|
||||
this.set({ updating: id });
|
||||
return Plugin.loadFromUrl(url).then(newPlugin => {
|
||||
return oldPlugin.update(newPlugin).then(() => {
|
||||
this.set({ updating: null });
|
||||
this.saveState();
|
||||
}).catch(e => {
|
||||
this.set('updating', null);
|
||||
throw e;
|
||||
});
|
||||
}).catch(e => {
|
||||
this.set({ updating: null });
|
||||
throw e;
|
||||
});
|
||||
},
|
||||
|
||||
loadPlugin(desc) {
|
||||
const plugin = new Plugin(desc.manifest, desc.url, true);
|
||||
const plugin = new Plugin({
|
||||
manifest: desc.manifest,
|
||||
url: desc.url,
|
||||
local: true
|
||||
});
|
||||
return plugin.install()
|
||||
.then(() => plugin)
|
||||
.catch(() => plugin);
|
||||
|
|
|
@ -20,6 +20,7 @@ const Plugin = Backbone.Model.extend({
|
|||
STATUS_INACTIVE: 'inactive',
|
||||
STATUS_INSTALLING: 'installing',
|
||||
STATUS_UNINSTALLING: 'uninstalling',
|
||||
STATUS_UPDATING: 'updating',
|
||||
STATUS_INVALID: 'invalid',
|
||||
STATUS_ERROR: 'error',
|
||||
|
||||
|
@ -29,19 +30,16 @@ const Plugin = Backbone.Model.extend({
|
|||
url: '',
|
||||
status: 'inactive',
|
||||
installTime: null,
|
||||
installError: null
|
||||
installError: null,
|
||||
updateError: null
|
||||
},
|
||||
|
||||
resources: null,
|
||||
module: null,
|
||||
|
||||
initialize(manifest, url, local) {
|
||||
const name = manifest.name;
|
||||
this.set({
|
||||
name,
|
||||
manifest,
|
||||
url,
|
||||
local
|
||||
});
|
||||
initialize(options) {
|
||||
const name = options.manifest.name;
|
||||
this.set({ name });
|
||||
this.logger = new Logger(`plugin:${name}`);
|
||||
},
|
||||
|
||||
|
@ -62,7 +60,8 @@ const Plugin = Backbone.Model.extend({
|
|||
this.set({
|
||||
status: this.STATUS_ERROR,
|
||||
installError: err,
|
||||
installTime: this.logger.ts() - ts
|
||||
installTime: this.logger.ts() - ts,
|
||||
updateError: null
|
||||
});
|
||||
throw err;
|
||||
});
|
||||
|
@ -101,6 +100,16 @@ const Plugin = Backbone.Model.extend({
|
|||
}
|
||||
},
|
||||
|
||||
validateUpdatedManifest(newManifest) {
|
||||
const manifest = this.get('manifest');
|
||||
if (manifest.name !== newManifest.name) {
|
||||
return 'Plugin name mismatch';
|
||||
}
|
||||
if (manifest.publicKey !== newManifest.publicKey) {
|
||||
return 'Public key mismatch';
|
||||
}
|
||||
},
|
||||
|
||||
installWithManifest() {
|
||||
const manifest = this.get('manifest');
|
||||
const local = this.get('local');
|
||||
|
@ -187,7 +196,7 @@ const Plugin = Backbone.Model.extend({
|
|||
},
|
||||
|
||||
installWithResources() {
|
||||
this.logger.info('Installing loaded plugin');
|
||||
this.logger.info('Installing plugin code');
|
||||
const manifest = this.get('manifest');
|
||||
const promises = [];
|
||||
if (this.resources.css) {
|
||||
|
@ -337,6 +346,14 @@ const Plugin = Backbone.Model.extend({
|
|||
delete BaseLocale[this.getThemeLocaleKey(theme.name)];
|
||||
},
|
||||
|
||||
uninstallPluginCode() {
|
||||
try {
|
||||
this.module.exports.uninstall();
|
||||
} catch (e) {
|
||||
this.logger.error('Plugin uninstall method returned an error', e);
|
||||
}
|
||||
},
|
||||
|
||||
uninstall() {
|
||||
const manifest = this.get('manifest');
|
||||
this.logger.info('Uninstalling plugin with resources', Object.keys(manifest.resources).join(', '));
|
||||
|
@ -347,11 +364,7 @@ const Plugin = Backbone.Model.extend({
|
|||
this.removeElement('plugin-css-' + this.get('name'));
|
||||
}
|
||||
if (manifest.resources.js) {
|
||||
try {
|
||||
this.module.exports.uninstall();
|
||||
} catch (e) {
|
||||
this.logger.error('Plugin uninstall method returned an error', e);
|
||||
}
|
||||
this.uninstallPluginCode();
|
||||
this.removeElement('plugin-js-' + this.get('name'));
|
||||
}
|
||||
if (manifest.resources.loc) {
|
||||
|
@ -365,6 +378,54 @@ const Plugin = Backbone.Model.extend({
|
|||
this.logger.info('Uninstall complete', this.logger.ts(ts));
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
update(newPlugin) {
|
||||
const ts = this.logger.ts();
|
||||
const prevStatus = this.get('status');
|
||||
this.set('status', this.STATUS_UPDATING);
|
||||
return Promise.resolve().then(() => {
|
||||
const manifest = this.get('manifest');
|
||||
const newManifest = newPlugin.get('manifest');
|
||||
if (manifest.version === newManifest.version) {
|
||||
this.set({ status: prevStatus, updateError: null });
|
||||
this.logger.info(`v${manifest.version} is the latest plugin version`);
|
||||
return;
|
||||
}
|
||||
this.logger.info(`Updating plugin from v${manifest.version} to v${newManifest.version}`);
|
||||
const error = newPlugin.validateManifest() || this.validateUpdatedManifest(newManifest);
|
||||
if (error) {
|
||||
this.logger.error('Manifest validation error', error);
|
||||
this.set({ status: prevStatus, updateError: error });
|
||||
throw 'Plugin validation error: ' + error;
|
||||
}
|
||||
this.uninstallPluginCode();
|
||||
return newPlugin.installWithManifest()
|
||||
.then(() => {
|
||||
this.module = newPlugin.module;
|
||||
this.resources = newPlugin.resources;
|
||||
this.set({
|
||||
status: this.STATUS_ACTIVE,
|
||||
manifest: newManifest,
|
||||
installTime: this.logger.ts() - ts,
|
||||
installError: null,
|
||||
updateError: null
|
||||
});
|
||||
this.logger.info('Update complete', this.logger.ts(ts));
|
||||
})
|
||||
.catch(err => {
|
||||
this.logger.error('Error updating plugin', err);
|
||||
if (prevStatus === this.STATUS_ACTIVE) {
|
||||
this.logger.info('Activating previous version');
|
||||
this.installWithResources();
|
||||
}
|
||||
this.set({
|
||||
status: prevStatus,
|
||||
updateError: err
|
||||
});
|
||||
throw err;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -387,7 +448,9 @@ Plugin.loadFromUrl = function(url) {
|
|||
throw 'Failed to parse manifest';
|
||||
}
|
||||
commonLogger.debug('Loaded manifest', manifest);
|
||||
return new Plugin(manifest, url);
|
||||
return new Plugin({
|
||||
manifest, url
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -9,12 +9,13 @@ const SettingsPluginsView = Backbone.View.extend({
|
|||
events: {
|
||||
'click .settings_plugins-install-btn': 'installClick',
|
||||
'click .settings_plugins-uninstall-btn': 'uninstallClick',
|
||||
'click .settings_plugins-update-btn': 'updateClick',
|
||||
'click .settings_plugins-use-locale-btn': 'useLocaleClick',
|
||||
'click .settings_plugins-use-theme-btn': 'useThemeClick'
|
||||
},
|
||||
|
||||
initialize() {
|
||||
this.listenTo(PluginManager, 'change:installing change:uninstalling', this.render.bind(this));
|
||||
this.listenTo(PluginManager, 'change:installing change:uninstalling change:updating', this.render.bind(this));
|
||||
},
|
||||
|
||||
render() {
|
||||
|
@ -24,10 +25,12 @@ const SettingsPluginsView = Backbone.View.extend({
|
|||
id: plugin.id,
|
||||
manifest: plugin.get('manifest'),
|
||||
status: plugin.get('status'),
|
||||
installTime: Math.round(plugin.get('installTime'))
|
||||
installTime: Math.round(plugin.get('installTime')),
|
||||
updateError: plugin.get('updateError')
|
||||
})),
|
||||
lastInstallUrl: PluginManager.get('installing') || (lastInstall.error ? lastInstall.url : ''),
|
||||
lastInstallError: lastInstall.error
|
||||
lastInstallError: lastInstall.error,
|
||||
updating: PluginManager.get('updating')
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
@ -63,9 +66,20 @@ const SettingsPluginsView = Backbone.View.extend({
|
|||
|
||||
uninstallClick(e) {
|
||||
const pluginId = $(e.target).data('plugin');
|
||||
if (PluginManager.get('updating') === pluginId || PluginManager.get('uninstalling') === pluginId) {
|
||||
return;
|
||||
}
|
||||
PluginManager.uninstall(pluginId);
|
||||
},
|
||||
|
||||
updateClick(e) {
|
||||
const pluginId = $(e.target).data('plugin');
|
||||
if (PluginManager.get('updating') === pluginId || PluginManager.get('uninstalling') === pluginId) {
|
||||
return;
|
||||
}
|
||||
PluginManager.update(pluginId);
|
||||
},
|
||||
|
||||
useLocaleClick(e) {
|
||||
const locale = $(e.target).data('locale');
|
||||
AppSettingsModel.instance.set('locale', locale);
|
||||
|
|
|
@ -160,5 +160,8 @@
|
|||
&-plugin-desc {
|
||||
margin-bottom: $base-padding-v;
|
||||
}
|
||||
&-upd-error {
|
||||
margin-top: $base-padding-v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,13 @@
|
|||
{{#res 'setPlCreatedBy'}}<a href="{{plugin.manifest.author.url}}" target="_blank">{{plugin.manifest.author.name}}</a> ({{plugin.manifest.author.email}}){{/res}},
|
||||
{{#res 'setPlLoadTime'}}{{plugin.installTime}}ms{{/res}}
|
||||
{{#ifeq plugin.status 'error'}}<span class="error-color"> ({{res 'setPlLoadError'}}){{/ifeq}}
|
||||
{{#if plugin.updateError}}<div class="error-color settings__plugins-upd-error">{{res 'setPlUpdateError'}}: <pre>{{plugin.updateError}}</pre></div>{{/if}}
|
||||
</div>
|
||||
<div class="settings__plugins-plugin-buttons">
|
||||
<button class="settings_plugins-uninstall-btn btn-silent" data-plugin="{{plugin.id}}">{{res 'setPlUninstallBtn'}}</button>
|
||||
<button class="settings_plugins-uninstall-btn btn-error" data-plugin="{{plugin.id}}">{{res 'setPlUninstallBtn'}}</button>
|
||||
<button class="settings_plugins-update-btn btn-silent" data-plugin="{{plugin.id}}">
|
||||
{{~#ifeq plugin.id ../updating}}{{res 'setPlUpdating'}}…{{else}}{{res 'setPlUpdateBtn'}}{{/ifeq~}}
|
||||
</button>
|
||||
{{#if plugin.manifest.locale}}
|
||||
<button class="settings_plugins-use-locale-btn btn-silent" data-locale="{{plugin.manifest.locale.name}}">{{res 'setPlLocaleBtn'}}</button>
|
||||
{{/if}}
|
||||
|
|
Loading…
Reference in New Issue