diff --git a/gajim/common/config.py b/gajim/common/config.py index 47299b3fc6e90dc2c2a27f82311b593e19a576de..d120d90eacf6089c391f2c064cc95481dd82ffef 100644 --- a/gajim/common/config.py +++ b/gajim/common/config.py @@ -250,6 +250,8 @@ class Config: 'groupchat_roster_width': [opt_int, 210, _('Width of group chat roster in pixel')], 'dev_force_bookmark_2': [opt_bool, False, _('Force Bookmark 2 usage')], 'show_help_start_chat': [opt_bool, True, _('Shows an info bar with helpful hints in the Start / Join Chat dialog')], + 'check_for_update': [opt_bool, True, _('Check for Gajim updates periodically')], + 'last_update_check': [opt_str, '', _('Date of the last update check')], }, {}) # type: Tuple[Dict[str, List[Any]], Dict[Any, Any]] __options_per_key = { diff --git a/gajim/data/gui/preferences_window.ui b/gajim/data/gui/preferences_window.ui index 3cc3a9bd68f840d687cc1e1da9e39bab2b21feda..94bd9a01bbc4fb6895b152dc14fda4646600db9e 100644 --- a/gajim/data/gui/preferences_window.ui +++ b/gajim/data/gui/preferences_window.ui @@ -2771,6 +2771,26 @@ to discover one from the server. (Example: stun.iptel.org)</property> <property name="position">3</property> </packing> </child> + <child> + <object class="GtkCheckButton" id="update_check"> + <property name="label" translatable="yes">_Check for Gajim updates periodically</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="no_show_all">True</property> + <property name="tooltip_text" translatable="yes">Enables a weekly update check (Windows only)</property> + <property name="halign">start</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="_on_update_check_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> <child> <object class="GtkButton" id="reset_help"> <property name="label" translatable="yes">_Reset Hints</property> diff --git a/gajim/gtk/preferences.py b/gajim/gtk/preferences.py index 99587c34d1616264be00915e7ef20d977a84e176..1f0e00359668f18e3c1987418f32c06f63ef2931 100644 --- a/gajim/gtk/preferences.py +++ b/gajim/gtk/preferences.py @@ -14,6 +14,7 @@ import logging import os +import sys from gi.repository import GLib from gi.repository import Gtk @@ -429,6 +430,11 @@ def create_av_combobox(opt_name, device_dict, config_name=None, self._ui.enable_logging.set_active(app.get_debug_mode()) self._ui.enable_logging.show() + if sys.platform in ('win32', 'darwin'): + st = app.config.get('check_for_update') + self._ui.update_check.set_active(st) + self._ui.update_check.show() + self._ui.connect_signals(self) self.connect('key-press-event', self._on_key_press) @@ -1066,6 +1072,9 @@ def on_enable_logging_toggled(self, widget): def _on_debug_folder_clicked(self, _widget): open_file(configpaths.get('DEBUG')) + def _on_update_check_toggled(self, widget): + self.on_checkbutton_toggled(widget, 'check_for_update') + def _on_reset_help_clicked(self, widget): widget.set_sensitive(False) helping_hints = [ diff --git a/gajim/gui_interface.py b/gajim/gui_interface.py index c0d112d725e77306ed18f2581c76a7193f2bec56..08a81a5d86c6ccf52bd00089fa571b2512db53f2 100644 --- a/gajim/gui_interface.py +++ b/gajim/gui_interface.py @@ -35,14 +35,18 @@ import sys import re import time +import json import logging from functools import partial from threading import Thread +from datetime import datetime from importlib.util import find_spec +from packaging.version import Version as V from gi.repository import Gtk from gi.repository import GLib from gi.repository import Gio +from gi.repository import Soup from nbxmpp import idlequeue from nbxmpp import Hashes2 @@ -91,6 +95,7 @@ from gajim.gtk.dialogs import WarningDialog from gajim.gtk.dialogs import InformationDialog from gajim.gtk.dialogs import NewConfirmationDialog +from gajim.gtk.dialogs import NewConfirmationCheckDialog from gajim.gtk.dialogs import InputDialog from gajim.gtk.dialogs import PassphraseDialog from gajim.gtk.dialogs import InvitationReceivedDialog @@ -1839,6 +1844,81 @@ def create_zeroconf_default_config(self): app.config.set_per('accounts', app.ZEROCONF_ACC_NAME, 'active', False) + def check_for_updates(self): + if not app.config.get('check_for_update'): + return + + now = datetime.now() + last_check = app.config.get('last_update_check') + if not last_check: + def _on_cancel(): + app.config.set('check_for_update', False) + + def _on_check(): + self._get_latest_release() + + NewConfirmationDialog( + _('Update Check'), + _('Gajim Update Check'), + _('Search for Gajim updates periodically?'), + [DialogButton.make('Cancel', + text=_('_No'), + callback=_on_cancel), + DialogButton.make('Accept', + text=_('_Search Periodically'), + callback=_on_check)]).show() + return + + last_check_time = datetime.strptime(last_check, '%Y-%m-%d %H:%M') + if (now - last_check_time).days < 7: + return + + self._get_latest_release() + + def _get_latest_release(self): + log.info('Checking for Gajim updates') + session = Soup.Session() + session.props.user_agent = 'Gajim %s' % app.version + message = Soup.Message.new('GET', 'https://gajim.org/current-version.json') + session.queue_message(message, self._on_update_checked) + + def _on_update_checked(self, _session, message): + now = datetime.now() + app.config.set('last_update_check', now.strftime('%Y-%m-%d %H:%M')) + + body = message.props.response_body.data + if not body: + log.warning('Could not reach gajim.org for update check') + return + + data = json.loads(body) + latest_version = data['current_version'] + + if V(latest_version) > V(app.version): + def _on_cancel(is_checked): + if is_checked: + app.config.set('check_for_update', False) + + def _on_update(is_checked): + if is_checked: + app.config.set('check_for_update', False) + helpers.open_uri('https://gajim.org/download') + + NewConfirmationCheckDialog( + _('Update Available'), + _('Gajim Update Available'), + _('There is an update available for Gajim ' + '(latest version: %s)' % str(latest_version)), + _('_Do not show again'), + [DialogButton.make('Cancel', + text=_('_Later'), + callback=_on_cancel), + DialogButton.make('Accept', + text=_('_Update Now'), + callback=_on_update)]).show() + else: + log.info('Gajim is up to date') + def run(self, application): if app.config.get('trayicon') != 'never': self.show_systray() @@ -1997,6 +2077,8 @@ def __init__(self): if sys.platform not in ('win32', 'darwin'): logind.enable() music_track.enable() + else: + GLib.timeout_add_seconds(20, self.check_for_updates) idle.Monitor.set_interval(app.config.get('autoawaytime') * 60, app.config.get('autoxatime') * 60)