[plugin_installer] Add automatic updates
This commit is contained in:
@@ -7,14 +7,66 @@
|
|||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="margin_top">12</property>
|
<property name="margin_top">12</property>
|
||||||
<property name="margin_bottom">12</property>
|
<property name="margin_bottom">12</property>
|
||||||
|
<property name="border_width">18</property>
|
||||||
|
<property name="row_spacing">6</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkCheckButton" id="check_update">
|
<object class="GtkCheckButton" id="auto_update_feedback">
|
||||||
<property name="label" translatable="yes">Check update after start</property>
|
<property name="label" translatable="yes">_Show message when automatic update was successful</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="focus_on_click">False</property>
|
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
<property name="draw_indicator">True</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">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="auto_update">
|
||||||
|
<property name="label" translatable="yes">_Update plugins automatically</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="use_underline">True</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">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="check_update">
|
||||||
|
<property name="label" translatable="yes">_Check for updates after start</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<signal name="toggled" handler="on_check_update_toggled" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="label" translatable="yes">Plugin updates</property>
|
||||||
|
<style>
|
||||||
|
<class name="bold"/>
|
||||||
|
<class name="dim-label"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">0</property>
|
<property name="left_attach">0</property>
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ from gajim.plugins.helpers import get_builder
|
|||||||
from gajim.gtk.dialogs import WarningDialog
|
from gajim.gtk.dialogs import WarningDialog
|
||||||
from gajim.gtk.dialogs import HigDialog
|
from gajim.gtk.dialogs import HigDialog
|
||||||
from gajim.gtk.dialogs import YesNoDialog
|
from gajim.gtk.dialogs import YesNoDialog
|
||||||
|
from gajim.gtk.dialogs import 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')
|
||||||
@@ -108,7 +109,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
|
||||||
@@ -123,17 +126,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.available_plugins_box)
|
page = self.notebook.page_num(self.available_plugins_box)
|
||||||
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'):
|
||||||
@@ -143,7 +149,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):
|
||||||
@@ -225,19 +232,18 @@ 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':
|
||||||
YesNoDialog(
|
YesNoDialog(
|
||||||
_('Security error during download'),
|
_('Security error during download'),
|
||||||
_('A security error occurred when '
|
_('A security error occurred while '
|
||||||
'downloading. The certificate of the '
|
'downloading. The certificate of the '
|
||||||
'plugin archive could not be verified. '
|
'plugin archive could not be verified. '
|
||||||
'this might be a security attack. '
|
'This might be a security attack. '
|
||||||
'\n\nYou can continue at your risk. '
|
'\n\nYou can continue at your own risk (not recommended). '
|
||||||
'Do you want to do so? '
|
'Do you want to continue?'
|
||||||
'(not recommended)'
|
|
||||||
),
|
),
|
||||||
on_response_yes=lambda dlg:
|
on_response_yes=lambda dlg:
|
||||||
self.start_download(secure=False, upgrading=True))
|
self.start_download(secure=False, upgrading=True))
|
||||||
@@ -251,49 +257,66 @@ 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, check_update: %s, auto_update: %s',
|
||||||
secure, remote_dirs, upgrading, check_update)
|
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,
|
||||||
upgrading=upgrading, check_update=check_update)
|
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):
|
||||||
need_restart = False
|
need_restart = False
|
||||||
for _dir in plugin_dirs:
|
for _dir in plugin_dirs:
|
||||||
updated = app.plugin_manager.update_plugins(replace=False, activate=True, plugin_name=_dir)
|
updated = app.plugin_manager.update_plugins(replace=False, activate=True, plugin_name=_dir)
|
||||||
if updated:
|
if updated:
|
||||||
plugin = app.plugin_manager.get_active_plugin(updated[0])
|
if not auto_update:
|
||||||
for row in range(len(self.available_plugins_model)):
|
plugin = app.plugin_manager.get_active_plugin(updated[0])
|
||||||
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
|
||||||
break
|
model_row[Column.UPGRADE] = False
|
||||||
# get plugin icon
|
break
|
||||||
icon_file = os.path.join(plugin.__path__, os.path.split(
|
if not auto_update:
|
||||||
plugin.__path__)[1]) + '.png'
|
# Get plugin icon
|
||||||
icon = FALLBACK_ICON
|
icon_file = os.path.join(plugin.__path__, os.path.split(
|
||||||
if os.path.isfile(icon_file):
|
plugin.__path__)[1]) + '.png'
|
||||||
icon = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_file, 16, 16)
|
icon = FALLBACK_ICON
|
||||||
row = [plugin, plugin.name, True, plugin.activatable, icon]
|
if os.path.isfile(icon_file):
|
||||||
self.installed_plugins_model.append(row)
|
icon = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_file, 16, 16)
|
||||||
|
row = [plugin, plugin.name, True, plugin.activatable, icon]
|
||||||
|
self.installed_plugins_model.append(row)
|
||||||
else:
|
else:
|
||||||
need_restart = True
|
need_restart = True
|
||||||
|
|
||||||
if need_restart:
|
if need_restart:
|
||||||
txt = _('All plugins downloaded.\nThe updates will '
|
txt = _('All plugins downloaded.\nThe updates will '
|
||||||
'be installed on next Gajim restart.')
|
'be installed the next time Gajim is started.')
|
||||||
else:
|
else:
|
||||||
txt = _('All selected plugins downloaded and activated')
|
txt = _('All selected plugins downloaded and activated')
|
||||||
dialog = HigDialog(
|
if not auto_update:
|
||||||
self.window, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, '', txt)
|
dialog = HigDialog(
|
||||||
dialog.set_modal(False)
|
self.window, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, '', txt)
|
||||||
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'),
|
||||||
|
_('Plugin updates have successfully been downloaded.'
|
||||||
|
'Updates will be installed on next Gajim restart.'),
|
||||||
|
_('Do not show this message again'),
|
||||||
|
on_response_ok=on_ok).get_widget_for_response(
|
||||||
|
Gtk.ResponseType.CANCEL).hide()
|
||||||
|
if auto_update and not self.config['auto_update_feedback']:
|
||||||
|
log.info('Updates downloaded, will install on next restart')
|
||||||
|
|
||||||
|
|
||||||
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()
|
||||||
@@ -325,7 +348,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
|
||||||
@@ -335,6 +359,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):
|
||||||
@@ -355,9 +380,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
|
||||||
@@ -413,7 +439,7 @@ class DownloadAsync(threading.Thread):
|
|||||||
pix.close()
|
pix.close()
|
||||||
icon = pix.get_pixbuf()
|
icon = pix.get_pixbuf()
|
||||||
|
|
||||||
# transform to dictonary
|
# Transform to dictonary
|
||||||
config_dict = {}
|
config_dict = {}
|
||||||
for key, value in config.items('info'):
|
for key, value in config.items('info'):
|
||||||
config_dict[key] = value
|
config_dict[key] = value
|
||||||
@@ -457,6 +483,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:
|
||||||
@@ -465,7 +492,17 @@ class DownloadAsync(threading.Thread):
|
|||||||
if (V(plugin['version']) > V(local_version)) and \
|
if (V(plugin['version']) > V(local_version)) and \
|
||||||
self.plugin_is_valid(plugin):
|
self.plugin_is_valid(plugin):
|
||||||
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:
|
||||||
@@ -501,7 +538,7 @@ class DownloadAsync(threading.Thread):
|
|||||||
os.mkdir(local_dir)
|
os.mkdir(local_dir)
|
||||||
local_dir = os.path.dirname(local_dir)
|
local_dir = os.path.dirname(local_dir)
|
||||||
|
|
||||||
# downloading zip file
|
# Downloading zip file
|
||||||
try:
|
try:
|
||||||
plugin = posixpath.join(PLUGINS_URL, filename)
|
plugin = posixpath.join(PLUGINS_URL, filename)
|
||||||
buf = self.download_url(plugin)
|
buf = self.download_url(plugin)
|
||||||
@@ -510,7 +547,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):
|
||||||
@@ -523,6 +561,23 @@ class PluginInstallerPluginConfigDialog(GajimPluginConfigDialog):
|
|||||||
|
|
||||||
def on_run(self):
|
def on_run(self):
|
||||||
self.xml.check_update.set_active(self.plugin.config['check_update'])
|
self.xml.check_update.set_active(self.plugin.config['check_update'])
|
||||||
|
self.xml.auto_update.set_sensitive(self.plugin.config['check_update'])
|
||||||
|
self.xml.auto_update.set_active(self.plugin.config['auto_update'])
|
||||||
|
self.xml.auto_update_feedback.set_sensitive(self.plugin.config['check_update'])
|
||||||
|
self.xml.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 not self.plugin.config['check_update']:
|
||||||
|
self.plugin.config['auto_update'] = False
|
||||||
|
self.xml.auto_update.set_sensitive(self.plugin.config['check_update'])
|
||||||
|
self.xml.auto_update.set_active(self.plugin.config['auto_update'])
|
||||||
|
self.xml.auto_update_feedback.set_sensitive(self.plugin.config['auto_update'])
|
||||||
|
self.xml.auto_update_feedback.set_active(self.plugin.config['auto_update_feedback'])
|
||||||
|
|
||||||
|
def on_auto_update_toggled(self, widget):
|
||||||
|
self.plugin.config['auto_update'] = widget.get_active()
|
||||||
|
self.xml.auto_update_feedback.set_sensitive(self.plugin.config['auto_update'])
|
||||||
|
|
||||||
|
def on_auto_update_feedback_toggled(self, widget):
|
||||||
|
self.plugin.config['auto_update_feedback'] = widget.get_active()
|
||||||
|
|||||||
Reference in New Issue
Block a user