[plugin_installer] Add plugin auto update

This commit is contained in:
Daniel
2018-01-23 12:52:35 +01:00
committed by Philipp Hörist
parent 87650d09e7
commit d760a1ada1
2 changed files with 143 additions and 47 deletions

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 --> <!-- Generated with glade 3.20.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.14"/> <requires lib="gtk+" version="3.14"/>
<object class="GtkWindow" id="ConfigWindow"> <object class="GtkWindow" id="ConfigWindow">
@@ -14,7 +14,7 @@
<property name="margin_bottom">12</property> <property name="margin_bottom">12</property>
<child> <child>
<object class="GtkCheckButton" id="check_update"> <object class="GtkCheckButton" id="check_update">
<property name="label" translatable="yes">Check update after start</property> <property name="label" translatable="yes">Check for updates after start</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="focus_on_click">False</property> <property name="focus_on_click">False</property>
@@ -27,6 +27,38 @@
<property name="top_attach">0</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkCheckButton" id="auto_update">
<property name="label" translatable="yes">Update plugins automatically</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="focus_on_click">False</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_auto_update_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="auto_update_feedback">
<property name="label" translatable="yes">Show message when auto update was successful</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="focus_on_click">False</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_auto_update_feedback_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
</object> </object>
</child> </child>
</object> </object>
@@ -171,6 +203,7 @@ Upgrade</property>
<object class="GtkButton" id="install_button"> <object class="GtkButton" id="install_button">
<property name="label" translatable="yes">Install/Upgrade</property> <property name="label" translatable="yes">Install/Upgrade</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="valign">start</property> <property name="valign">start</property>

View File

@@ -44,14 +44,16 @@ try:
from plugins import GajimPlugin from plugins import GajimPlugin
from plugins.gui import GajimPluginConfigDialog from plugins.gui import GajimPluginConfigDialog
from htmltextview import HtmlTextView from htmltextview import HtmlTextView
from dialogs import WarningDialog, HigDialog, YesNoDialog from dialogs import WarningDialog, HigDialog, YesNoDialog, \
ConfirmationDialogCheck
from gtkgui_helpers import get_action from gtkgui_helpers import get_action
except ImportError: except ImportError:
from gajim.common import app from gajim.common import app
from gajim.plugins import GajimPlugin from gajim.plugins import GajimPlugin
from gajim.plugins.gui import GajimPluginConfigDialog from gajim.plugins.gui import GajimPluginConfigDialog
from gajim.htmltextview import HtmlTextView from gajim.htmltextview import HtmlTextView
from gajim.dialogs import WarningDialog, HigDialog, YesNoDialog from gajim.dialogs import WarningDialog, HigDialog, YesNoDialog, \
ConfirmationDialogCheck
from gajim.gtkgui_helpers import get_action from gajim.gtkgui_helpers import get_action
log = logging.getLogger('gajim.plugin_system.plugin_installer') log = logging.getLogger('gajim.plugin_system.plugin_installer')
@@ -113,7 +115,9 @@ class PluginInstaller(GajimPlugin):
def init(self): def init(self):
self.description = _('Install and Upgrade Plugins') self.description = _('Install and Upgrade Plugins')
self.config_dialog = PluginInstallerPluginConfigDialog(self) self.config_dialog = PluginInstallerPluginConfigDialog(self)
self.config_default_values = {'check_update': (True, '')} self.config_default_values = {'check_update': (True, ''),
'auto_update': (False, ''),
'auto_update_feedback': (True, '')}
self.gui_extension_points = {'plugin_window': (self.on_activate, None)} self.gui_extension_points = {'plugin_window': (self.on_activate, None)}
self.window = None self.window = None
self.progressbar = None self.progressbar = None
@@ -128,17 +132,20 @@ class PluginInstaller(GajimPlugin):
self.on_activate(app.interface.instances['plugins']) self.on_activate(app.interface.instances['plugins'])
def warn_update(self, plugins): def warn_update(self, plugins):
def open_update(dummy): def open_update(checked):
if checked:
self.config['auto_update'] = True
get_action('plugins').activate() get_action('plugins').activate()
page = self.notebook.page_num(self.paned) page = self.notebook.page_num(self.paned)
self.notebook.set_current_page(page) self.notebook.set_current_page(page)
if plugins: if plugins:
plugins_str = '\n' + '\n'.join(plugins) plugins_str = '\n' + '\n'.join(plugins)
YesNoDialog( YesNoDialog(
_('Plugins updates'), _('Plugin updates'),
_('Some updates are available for your installer plugins. ' _('There are updates available for plugins you have installed.\n'
'Do you want to update those plugins:\n%s') 'Do you want to update those plugins:\n%s') % plugins_str,
% plugins_str, on_response_yes=open_update) checktext=_('_Update plugins automatically next time'),
on_response_yes=open_update)
else: else:
log.info('No updates found') log.info('No updates found')
if hasattr(self, 'thread'): if hasattr(self, 'thread'):
@@ -148,7 +155,8 @@ class PluginInstaller(GajimPlugin):
if hasattr(self, 'thread'): if hasattr(self, 'thread'):
return return
log.info('Checking for Updates...') log.info('Checking for Updates...')
self.start_download(check_update=True) auto_update = self.config['auto_update']
self.start_download(check_update=True, auto_update=auto_update)
self.timeout_id = 0 self.timeout_id = 0
def deactivate(self): def deactivate(self):
@@ -243,7 +251,7 @@ class PluginInstaller(GajimPlugin):
if self.available_plugins_model[i][Column.UPGRADE]: if self.available_plugins_model[i][Column.UPGRADE]:
dir_list.append(self.available_plugins_model[i][Column.DIR]) dir_list.append(self.available_plugins_model[i][Column.DIR])
self.start_download(remote_dirs=dir_list) self.start_download(remote_dirs=dir_list, auto_update=False)
def on_error(self, reason): def on_error(self, reason):
if reason == 'CERTIFICATE_VERIFY_FAILED': if reason == 'CERTIFICATE_VERIFY_FAILED':
@@ -269,18 +277,20 @@ class PluginInstaller(GajimPlugin):
_('An error occurred when downloading\n\n' _('An error occurred when downloading\n\n'
'<tt>[%s]</tt>' % (str(text))), self.window) '<tt>[%s]</tt>' % (str(text))), self.window)
def start_download(self, secure=True, remote_dirs=False, def start_download(self, secure=True, remote_dirs=False, upgrading=False,
upgrading=False, check_update=False): check_update=False, auto_update=False):
log.info('Start Download...') log.info('Start Download...')
log.debug( log.debug(
'secure: %s, remote_dirs: %s, upgrading: %s, check_update: %s', 'secure: %s, remote_dirs: %s, upgrading: %s, '
secure, remote_dirs, upgrading, check_update) 'check_update: %s, auto_update: %s',
secure, remote_dirs, upgrading, check_update, auto_update)
self.thread = DownloadAsync( self.thread = DownloadAsync(
self, secure=secure, remote_dirs=remote_dirs, self, secure=secure, remote_dirs=remote_dirs,
upgrading=upgrading, check_update=check_update) upgrading=upgrading, check_update=check_update,
auto_update=auto_update)
self.thread.start() self.thread.start()
def on_plugin_downloaded(self, plugin_dirs): def on_plugin_downloaded(self, plugin_dirs, auto_update):
for _dir in plugin_dirs: for _dir in plugin_dirs:
is_active = False is_active = False
plugins = None plugins = None
@@ -293,11 +303,12 @@ class PluginInstaller(GajimPlugin):
app.plugin_manager.deactivate_plugin(plugin) app.plugin_manager.deactivate_plugin(plugin)
app.plugin_manager.plugins.remove(plugin) app.plugin_manager.plugins.remove(plugin)
model = self.installed_plugins_model if not auto_update:
for row in range(len(model)): model = self.installed_plugins_model
if plugin == model[row][0]: for row in range(len(model)):
model.remove(model.get_iter((row, 0))) if plugin == model[row][0]:
break model.remove(model.get_iter((row, 0)))
break
log.info('Load Plugin from: %s', plugin_dir) log.info('Load Plugin from: %s', plugin_dir)
plugins = app.plugin_manager.scan_dir_for_plugins( plugins = app.plugin_manager.scan_dir_for_plugins(
@@ -308,28 +319,42 @@ class PluginInstaller(GajimPlugin):
app.plugin_manager.add_plugin(plugins[0]) app.plugin_manager.add_plugin(plugins[0])
plugin = app.plugin_manager.plugins[-1] plugin = app.plugin_manager.plugins[-1]
log.info('Loading successful') log.info('Loading successful')
for row in range(len(self.available_plugins_model)): if not auto_update:
model_row = self.available_plugins_model[row] for row in range(len(self.available_plugins_model)):
if plugin.name == model_row[Column.NAME]: model_row = self.available_plugins_model[row]
model_row[Column.LOCAL_VERSION] = plugin.version if plugin.name == model_row[Column.NAME]:
model_row[Column.UPGRADE] = False model_row[Column.LOCAL_VERSION] = plugin.version
model_row[Column.UPGRADE] = False
if is_active: if is_active:
log.info('Activate Plugin: %s', plugin) log.info('Activate Plugin: %s', plugin)
app.plugin_manager.activate_plugin(plugin) app.plugin_manager.activate_plugin(plugin)
# get plugin icon if not auto_update:
icon_file = os.path.join(plugin.__path__, os.path.split( # get plugin icon
plugin.__path__)[1]) + '.png' icon_file = os.path.join(plugin.__path__, os.path.split(
icon = FALLBACK_ICON plugin.__path__)[1]) + '.png'
if os.path.isfile(icon_file): icon = FALLBACK_ICON
icon = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_file, 16, 16) if os.path.isfile(icon_file):
row = [plugin, plugin.name, is_active, plugin.activatable, icon] icon = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_file, 16, 16)
self.installed_plugins_model.append(row) row = [plugin, plugin.name, is_active, plugin.activatable, icon]
self.installed_plugins_model.append(row)
dialog = HigDialog( if not auto_update:
self.window, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, dialog = HigDialog(
'', _('All selected plugins downloaded')) self.window, Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
dialog.set_modal(False) '', _('All selected plugins downloaded'))
dialog.popup() dialog.set_modal(False)
dialog.popup()
if auto_update and self.config['auto_update_feedback']:
def on_ok(checked):
if checked:
self.config['auto_update_feedback'] = False
# Hide cancel button to mimic InfoDialogCheck
ConfirmationDialogCheck(_('Plugins updated'),
_('Plugins were updated successfully'),
_('_Do not show this message again'),
on_response_ok=on_ok).get_widget_for_response(
Gtk.ResponseType.CANCEL).hide()
def available_plugins_treeview_selection_changed(self, treeview_selection): def available_plugins_treeview_selection_changed(self, treeview_selection):
model, iter_ = treeview_selection.get_selected() model, iter_ = treeview_selection.get_selected()
@@ -373,7 +398,8 @@ class PluginInstaller(GajimPlugin):
class DownloadAsync(threading.Thread): class DownloadAsync(threading.Thread):
def __init__(self, plugin, secure, remote_dirs, upgrading, check_update): def __init__(self, plugin, secure, remote_dirs,
upgrading, check_update, auto_update):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.plugin = plugin self.plugin = plugin
self.window = plugin.window self.window = plugin.window
@@ -383,6 +409,7 @@ class DownloadAsync(threading.Thread):
self.upgrading = upgrading self.upgrading = upgrading
self.secure = secure self.secure = secure
self.check_update = check_update self.check_update = check_update
self.auto_update = auto_update
self.pulse = None self.pulse = None
def model_append(self, row): def model_append(self, row):
@@ -403,9 +430,10 @@ class DownloadAsync(threading.Thread):
if self.check_update: if self.check_update:
self.run_check_update() self.run_check_update()
else: else:
GLib.idle_add(self.progressbar.show) if not self.auto_update:
self.pulse = GLib.timeout_add(150, self.progressbar_pulse) GLib.idle_add(self.progressbar.show)
self.run_download_plugin_list() self.pulse = GLib.timeout_add(150, self.progressbar_pulse)
self.run_download_plugin_list()
except urllib.error.URLError as exc: except urllib.error.URLError as exc:
if isinstance(exc.reason, ssl.SSLError): if isinstance(exc.reason, ssl.SSLError):
ssl_reason = exc.reason.reason ssl_reason = exc.reason.reason
@@ -495,6 +523,7 @@ class DownloadAsync(threading.Thread):
def run_check_update(self): def run_check_update(self):
to_update = [] to_update = []
auto_update_list = []
zipbuf = self.download_url(MANIFEST_URL) zipbuf = self.download_url(MANIFEST_URL)
plugin_list = self.parse_manifest(zipbuf) plugin_list = self.parse_manifest(zipbuf)
for plugin in plugin_list: for plugin in plugin_list:
@@ -508,7 +537,17 @@ class DownloadAsync(threading.Thread):
if (V(plugin['version']) > V(local_version)) and \ if (V(plugin['version']) > V(local_version)) and \
(gajim_v >= min_v) and (gajim_v <= max_v): (gajim_v >= min_v) and (gajim_v <= max_v):
to_update.append(plugin['name']) to_update.append(plugin['name'])
GLib.idle_add(self.plugin.warn_update, to_update) auto_update_list.append(plugin['remote_dir'])
if not self.auto_update:
GLib.idle_add(self.plugin.warn_update, to_update)
else:
if auto_update_list:
self.remote_dirs = auto_update_list
GLib.idle_add(self.download_plugin)
else:
log.info('No updates found')
if hasattr(self.plugin, 'thread'):
del self.plugin.thread
def run_download_plugin_list(self): def run_download_plugin_list(self):
if not self.remote_dirs: if not self.remote_dirs:
@@ -549,7 +588,8 @@ class DownloadAsync(threading.Thread):
continue continue
with ZipFile(buf) as zip_file: with ZipFile(buf) as zip_file:
zip_file.extractall(local_dir) zip_file.extractall(local_dir)
GLib.idle_add(self.plugin.on_plugin_downloaded, self.remote_dirs) GLib.idle_add(self.plugin.on_plugin_downloaded,
self.remote_dirs, self.auto_update)
class PluginInstallerPluginConfigDialog(GajimPluginConfigDialog): class PluginInstallerPluginConfigDialog(GajimPluginConfigDialog):
@@ -566,6 +606,29 @@ class PluginInstallerPluginConfigDialog(GajimPluginConfigDialog):
def on_run(self): def on_run(self):
self.xml.get_object('check_update').set_active( self.xml.get_object('check_update').set_active(
self.plugin.config['check_update']) self.plugin.config['check_update'])
if self.plugin.config['check_update']:
self.xml.get_object('auto_update').set_sensitive(True)
self.xml.get_object('auto_update').set_active(
self.plugin.config['auto_update'])
self.xml.get_object('auto_update_feedback').set_sensitive(True)
self.xml.get_object('auto_update_feedback').set_active(
self.plugin.config['auto_update_feedback'])
def on_check_update_toggled(self, widget): def on_check_update_toggled(self, widget):
self.plugin.config['check_update'] = widget.get_active() self.plugin.config['check_update'] = widget.get_active()
if widget.get_active():
self.xml.get_object('auto_update').set_sensitive(True)
self.xml.get_object('auto_update').set_active(
self.plugin.config['auto_update'])
self.xml.get_object('auto_update_feedback').set_sensitive(True)
self.xml.get_object('auto_update_feedback').set_active(
self.plugin.config['auto_update_feedback'])
else:
self.xml.get_object('auto_update').set_sensitive(False)
self.xml.get_object('auto_update_feedback').set_sensitive(False)
def on_auto_update_toggled(self, widget):
self.plugin.config['auto_update'] = widget.get_active()
def on_auto_update_feedback_toggled(self, widget):
self.plugin.config['auto_update_feedback'] = widget.get_active()