4735: Check for updates in a separate thread
svn: r22924
This commit is contained in:
		| @@ -358,6 +358,7 @@ class DisplayState(gen.utils.Callback): | ||||
|         'filters-changed' : (str, ),  | ||||
|         'filter-name-changed' : (str, unicode, unicode),  | ||||
|         'nameformat-changed' : None,  | ||||
|         'update-available' : (list, ),  | ||||
|         } | ||||
|      | ||||
|     #nav_type to message | ||||
|   | ||||
| @@ -32,6 +32,15 @@ General utility functions useful for the generic plugin system | ||||
| import locale | ||||
| import sys | ||||
| import os | ||||
| import datetime | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # set up logging | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| import logging | ||||
| LOG = logging.getLogger(".gen.plug") | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| @@ -41,6 +50,9 @@ import os | ||||
| from gen.plug._pluginreg import make_environment | ||||
| import const | ||||
| import Utils | ||||
| from gen.plug import BasePluginManager | ||||
| from gen.utils.configmanager import safe_eval | ||||
| import config | ||||
| from gen.ggettext import gettext as _ | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| @@ -188,6 +200,96 @@ class Zipfile(object): | ||||
|         """ | ||||
|         return os.path.split(name)[1] | ||||
|  | ||||
| def available_updates(): | ||||
|     whattypes = config.get('behavior.check-for-update-types') | ||||
|     if sys.version_info[0] < 3: | ||||
|         from urllib2 import urlopen | ||||
|     else: | ||||
|         from urllib.request import urlopen | ||||
|     LOG.debug("Checking for updated addons...") | ||||
|     langs = [] | ||||
|     lang = locale.getlocale()[0] # not None | ||||
|     if lang: | ||||
|         langs.append(lang) | ||||
|         if "_" in lang: | ||||
|             lang, variation = lang.split("_", 1) | ||||
|             langs.append(lang) | ||||
|     langs.append("en") | ||||
|     # now we have a list of languages to try: | ||||
|     fp = None | ||||
|     for lang in langs: | ||||
|         URL = ("%s/listings/addons-%s.txt" % | ||||
|                (config.get("behavior.addons-url"), lang)) | ||||
|         LOG.debug("   trying: %s" % URL) | ||||
|         try: | ||||
|             fp = urlopen(URL, timeout=10) # abort after 10 seconds | ||||
|         except: | ||||
|             try: | ||||
|                 URL = ("%s/listings/addons-%s.txt" % | ||||
|                        (config.get("behavior.addons-url"), lang[:2])) | ||||
|                 fp = urlopen(URL, timeout=10) | ||||
|             except Exception as err: # some error | ||||
|                 LOG.warn("Failed to open %s: %s" % (lang, err)) | ||||
|                 fp = None | ||||
|         if fp and fp.getcode() == 200: # ok | ||||
|             break | ||||
|  | ||||
|     pmgr = BasePluginManager.get_instance() | ||||
|     addon_update_list = [] | ||||
|     if fp and fp.getcode() == 200: | ||||
|         lines = list(fp.readlines()) | ||||
|         count = 0 | ||||
|         for line in lines: | ||||
|             line = line.decode('utf-8') | ||||
|             try: | ||||
|                 plugin_dict = safe_eval(line) | ||||
|                 if type(plugin_dict) != type({}): | ||||
|                     raise TypeError("Line with addon metadata is not " | ||||
|                                     "a dictionary") | ||||
|             except: | ||||
|                 LOG.warning("Skipped a line in the addon listing: " + | ||||
|                          str(line)) | ||||
|                 continue | ||||
|             id = plugin_dict["i"] | ||||
|             plugin = pmgr.get_plugin(id) | ||||
|             if plugin: | ||||
|                 LOG.debug("Comparing %s > %s" % | ||||
|                           (version_str_to_tup(plugin_dict["v"], 3), | ||||
|                            version_str_to_tup(plugin.version, 3))) | ||||
|                 if (version_str_to_tup(plugin_dict["v"], 3) > | ||||
|                     version_str_to_tup(plugin.version, 3)): | ||||
|                     LOG.debug("   Downloading '%s'..." % plugin_dict["z"]) | ||||
|                     if "update" in whattypes: | ||||
|                         if (not config.get('behavior.do-not-show-previously-seen-updates') or | ||||
|                              plugin_dict["i"] not in config.get('behavior.previously-seen-updates')): | ||||
|                             addon_update_list.append((_("Updated"), | ||||
|                                                       "%s/download/%s" % | ||||
|                                                       (config.get("behavior.addons-url"), | ||||
|                                                        plugin_dict["z"]), | ||||
|                                                       plugin_dict)) | ||||
|                 else: | ||||
|                     LOG.debug("   '%s' is ok" % plugin_dict["n"]) | ||||
|             else: | ||||
|                 LOG.debug("   '%s' is not installed" % plugin_dict["n"]) | ||||
|                 if "new" in whattypes: | ||||
|                     if (not config.get('behavior.do-not-show-previously-seen-updates') or | ||||
|                          plugin_dict["i"] not in config.get('behavior.previously-seen-updates')): | ||||
|                         addon_update_list.append((_("New"), | ||||
|                                                   "%s/download/%s" % | ||||
|                                                   (config.get("behavior.addons-url"), | ||||
|                                                    plugin_dict["z"]), | ||||
|                                                   plugin_dict)) | ||||
|         config.set("behavior.last-check-for-updates", | ||||
|                    datetime.date.today().strftime("%Y/%m/%d")) | ||||
|         count += 1 | ||||
|         if fp: | ||||
|             fp.close() | ||||
|     else: | ||||
|         LOG.debug("Checking Addons Failed") | ||||
|     LOG.debug("Done checking!") | ||||
|  | ||||
|     return addon_update_list | ||||
|  | ||||
| def load_addon_file(path, callback=None): | ||||
|     """ | ||||
|     Load an addon from a particular path (from URL or file system). | ||||
|   | ||||
| @@ -58,6 +58,9 @@ import ManagedWindow | ||||
| from gui.widgets import MarkupLabel, BasicLabel | ||||
| from QuestionDialog import ErrorDialog, QuestionDialog2, OkDialog | ||||
| from glade import Glade | ||||
| from gen.plug.utils import available_updates | ||||
| from gui.plug import PluginWindows | ||||
| from Errors import WindowActiveError | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| @@ -1142,12 +1145,39 @@ class GrampsPreferences(ConfigureDialog): | ||||
|  | ||||
|         table.attach(checkbutton, 1, 2, 9, 10, yoptions=0) | ||||
|         button = gtk.Button(_("Check now")) | ||||
|         button.connect("clicked", lambda obj: \ | ||||
|                   self.uistate.viewmanager.check_for_updates(force=True)) | ||||
|         button.connect("clicked", self.check_for_updates) | ||||
|         table.attach(button, 3, 4, 9, 10, yoptions=0) | ||||
|  | ||||
|         return _('General'), table | ||||
|  | ||||
|     def check_for_updates(self, button): | ||||
|         try: | ||||
|             addon_update_list = available_updates() | ||||
|         except: | ||||
|             OkDialog(_("Checking Addons Failed"), | ||||
|                      _("The addon repository appears to be unavailable. " | ||||
|                        "Please try again later."), | ||||
|                      self.window) | ||||
|             return | ||||
|  | ||||
|         if len(addon_update_list) > 0: | ||||
|             try: | ||||
|                 PluginWindows.UpdateAddons(self.uistate, [], addon_update_list) | ||||
|             except WindowActiveError: | ||||
|                 pass | ||||
|         else: | ||||
|             check_types = config.get('behavior.check-for-update-types') | ||||
|             OkDialog(_("There are no available addons of this type"), | ||||
|                      _("Checked for '%s'") % | ||||
|                      _("' and '").join([_(t) for t in check_types]), | ||||
|                      self.window) | ||||
|  | ||||
|         # List of translated strings used here | ||||
|         # Dead code for l10n | ||||
|         _('new'), _('update') | ||||
|  | ||||
|         self.uistate.viewmanager.do_reg_plugins(self.dbstate, self.uistate) | ||||
|  | ||||
|     def add_famtree_panel(self, configdialog): | ||||
|         table = gtk.Table(2, 2) | ||||
|         table.set_border_width(12) | ||||
|   | ||||
| @@ -32,6 +32,14 @@ import traceback | ||||
| import os | ||||
| import sys | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # set up logging | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| import logging | ||||
| LOG = logging.getLogger(".gui.plug") | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GTK modules | ||||
| @@ -50,14 +58,19 @@ import ManagedWindow | ||||
| import Errors | ||||
| from gen.plug import PluginRegister, PTYPE_STR, load_addon_file | ||||
| from gen.ggettext import gettext as _ | ||||
| from gen.ggettext import ngettext | ||||
| from gui.utils import open_file_with_default_application | ||||
| from gui.pluginmanager import GuiPluginManager | ||||
| from gui.plug import tool, add_gui_options | ||||
| from QuestionDialog import InfoDialog | ||||
| from QuestionDialog import InfoDialog, OkDialog | ||||
| from gui.editors import EditPerson | ||||
| from glade import Glade | ||||
| from ListModel import ListModel, NOSORT, TOGGLE | ||||
| import Utils | ||||
| import const | ||||
| import config | ||||
| from gui.widgets.progressdialog import (LongOpStatus, ProgressMonitor, | ||||
|                                         GtkProgressDialog) | ||||
|  | ||||
| def display_message(message): | ||||
|     """ | ||||
| @@ -1041,3 +1054,176 @@ class ToolManagedWindow(tool.Tool, ToolManagedWindowBase): | ||||
|         tool.Tool.__init__(self, dbstate, options_class, name) | ||||
|         ToolManagedWindowBase.__init__(self, dbstate, uistate, options_class,  | ||||
|                                        name, callback) | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # UpdateAddons | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class UpdateAddons(ManagedWindow.ManagedWindow): | ||||
|  | ||||
|     def __init__(self, uistate, track, addon_update_list): | ||||
|         self.title = _('Available Gramps Updates for Addons') | ||||
|         ManagedWindow.ManagedWindow.__init__(self, uistate, track,  | ||||
|                                              self.__class__) | ||||
|  | ||||
|         glade = Glade("updateaddons.glade") | ||||
|         self.update_dialog = glade.toplevel | ||||
|         self.set_window(self.update_dialog, glade.get_object('title'),  | ||||
|                         self.title) | ||||
|         self.window.set_size_request(750, 400) | ||||
|  | ||||
|         apply_button = glade.get_object('apply') | ||||
|         cancel_button = glade.get_object('cancel') | ||||
|         select_all = glade.get_object('select_all') | ||||
|         select_all.connect("clicked", self.select_all_clicked) | ||||
|         select_none = glade.get_object('select_none') | ||||
|         select_none.connect("clicked", self.select_none_clicked) | ||||
|         apply_button.connect("clicked", self.install_addons) | ||||
|         cancel_button.connect("clicked", self.close) | ||||
|  | ||||
|         self.list = ListModel(glade.get_object("list"), [ | ||||
|                 # name, click?, width, toggle | ||||
|                 {"name": _('Select'), | ||||
|                  "width": 60, | ||||
|                  "type": TOGGLE, | ||||
|                  "visible_col": 6, | ||||
|                  "editable": True},                         # 0 selected? | ||||
|                 (_('Type'), 1, 180),                        # 1 new gramplet | ||||
|                 (_('Name'), 2, 200),                        # 2 name (version) | ||||
|                 (_('Description'), 3, 200),                 # 3 description | ||||
|                 ('', NOSORT, 0),                            # 4 url | ||||
|                 ('', NOSORT, 0),                            # 5 id | ||||
|                 {"name": '', "type": TOGGLE},               # 6 visible? bool | ||||
|                 ], list_mode="tree") | ||||
|         pos = None | ||||
|         addon_update_list.sort(key=lambda x: "%s %s" % (x[0], x[2]["t"])) | ||||
|         last_category = None | ||||
|         for (status,plugin_url,plugin_dict) in addon_update_list: | ||||
|             count = get_count(addon_update_list, plugin_dict["t"]) | ||||
|             category = _("%(adjective)s: %(addon)s") % { | ||||
|                 "adjective": status,  | ||||
|                 "addon": _(plugin_dict["t"])} | ||||
|             if last_category != category: | ||||
|                 last_category = category | ||||
|                 node = self.list.add([False, # initially selected? | ||||
|                                       category, | ||||
|                                       "", | ||||
|                                       "", | ||||
|                                       "", | ||||
|                                       "", | ||||
|                                       False]) # checkbox visible? | ||||
|             iter = self.list.add([False, # initially selected? | ||||
|                                   "%s %s" % (status, _(plugin_dict["t"])), | ||||
|                                   "%s (%s)" % (plugin_dict["n"], | ||||
|                                                plugin_dict["v"]), | ||||
|                                   plugin_dict["d"], | ||||
|                                   plugin_url, | ||||
|                                   plugin_dict["i"], | ||||
|                                   True], node=node) | ||||
|             if pos is None: | ||||
|                 pos = iter | ||||
|         if pos: | ||||
|             self.list.selection.select_iter(pos) | ||||
|         self.update_dialog.run() | ||||
|  | ||||
|     def build_menu_names(self, obj): | ||||
|         return (self.title, "") | ||||
|      | ||||
|     def select_all_clicked(self, widget): | ||||
|         """ | ||||
|         Select all of the addons for download. | ||||
|         """ | ||||
|         self.list.model.foreach(update_rows, True) | ||||
|         self.list.tree.expand_all() | ||||
|  | ||||
|     def select_none_clicked(self, widget): | ||||
|         """ | ||||
|         Select none of the addons for download. | ||||
|         """ | ||||
|         self.list.model.foreach(update_rows, False) | ||||
|         self.list.tree.expand_all() | ||||
|  | ||||
|     def install_addons(self, obj): | ||||
|         """ | ||||
|         Process all of the selected addons. | ||||
|         """ | ||||
|         self.update_dialog.hide() | ||||
|         model = self.list.model | ||||
|  | ||||
|         iter = model.get_iter_first() | ||||
|         length = 0 | ||||
|         while iter: | ||||
|             iter = model.iter_next(iter) | ||||
|             if iter: | ||||
|                 length += model.iter_n_children(iter) | ||||
|  | ||||
|         longop = LongOpStatus( | ||||
|             _("Downloading and installing selected addons..."), | ||||
|             length, 1, # total, increment-by | ||||
|             can_cancel=True) | ||||
|         pm = ProgressMonitor(GtkProgressDialog, | ||||
|                              ("Title", self.window, gtk.DIALOG_MODAL)) | ||||
|         pm.add_op(longop) | ||||
|         count = 0 | ||||
|         if not config.get('behavior.do-not-show-previously-seen-updates'): | ||||
|             # reset list | ||||
|             config.get('behavior.previously-seen-updates')[:] = [] | ||||
|  | ||||
|         iter = model.get_iter_first() | ||||
|         while iter: | ||||
|             for rowcnt in range(model.iter_n_children(iter)): | ||||
|                 child = model.iter_nth_child(iter, rowcnt) | ||||
|                 row = [model.get_value(child, n) for n in range(6)] | ||||
|                 if longop.should_cancel(): | ||||
|                     break | ||||
|                 elif row[0]: # toggle on | ||||
|                     load_addon_file(row[4], callback=LOG.debug) | ||||
|                     count += 1 | ||||
|                 else: # add to list of previously seen, but not installed | ||||
|                     if row[5] not in config.get('behavior.previously-seen-updates'): | ||||
|                         config.get('behavior.previously-seen-updates').append(row[5]) | ||||
|                 longop.heartbeat() | ||||
|                 pm._get_dlg()._process_events() | ||||
|             iter = model.iter_next(iter) | ||||
|  | ||||
|         if not longop.was_cancelled(): | ||||
|             longop.end() | ||||
|         if count: | ||||
|             OkDialog(_("Done downloading and installing addons"), | ||||
|                      "%s %s" % (ngettext("%d addon was installed.", | ||||
|                                          "%d addons were installed.", | ||||
|                                          count) % count, | ||||
|                                 _("You need to restart Gramps to see new views.")), | ||||
|                      self.window) | ||||
|         else: | ||||
|             OkDialog(_("Done downloading and installing addons"), | ||||
|                      _("No addons were installed."), | ||||
|                      self.window) | ||||
|         self.close() | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # Local Functions | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| def update_rows(model, path, iter, user_data): | ||||
|     """ | ||||
|     Update the rows of a model. | ||||
|     """ | ||||
|     #path: (8,)   iter: <GtkTreeIter at 0xbfa89fa0> | ||||
|     #path: (8, 0) iter: <GtkTreeIter at 0xbfa89f60> | ||||
|     if len(path) == 2: | ||||
|         row = model[path] | ||||
|         row[0] = user_data | ||||
|         model.row_changed(path, iter) | ||||
|  | ||||
| def get_count(addon_update_list, category): | ||||
|     """ | ||||
|     Get the count of matching category items. | ||||
|     """ | ||||
|     count = 0 | ||||
|     for (status,plugin_url,plugin_dict) in addon_update_list: | ||||
|         if plugin_dict["t"] == category and plugin_url: | ||||
|             count += 1 | ||||
|     return count | ||||
|   | ||||
| @@ -32,6 +32,7 @@ Utility functions that depend on GUI components or for GUI components | ||||
| import os | ||||
| import sys | ||||
| import subprocess | ||||
| import threading | ||||
| from gen.ggettext import gettext as _ | ||||
| import constfunc | ||||
| # gtk is not included here, because this file is currently imported | ||||
| @@ -43,6 +44,7 @@ import constfunc | ||||
| # GNOME/GTK | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| import gobject | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| @@ -52,6 +54,7 @@ import constfunc | ||||
| import gen.lib | ||||
| import Errors | ||||
| import constfunc | ||||
| from gen.plug.utils import available_updates | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| @@ -391,3 +394,22 @@ def is_right_click(event): | ||||
|  | ||||
|         if event.button == 3: | ||||
|             return True | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # AvailableUpdates | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class AvailableUpdates(threading.Thread): | ||||
|     def __init__(self, uistate): | ||||
|         threading.Thread.__init__(self) | ||||
|         self.uistate = uistate | ||||
|         self.addon_update_list = [] | ||||
|  | ||||
|     def emit_update_available(self): | ||||
|         self.uistate.emit('update-available', (self.addon_update_list, )) | ||||
|  | ||||
|     def run(self): | ||||
|         self.addon_update_list = available_updates() | ||||
|         if len(self.addon_update_list) > 0: | ||||
|             gobject.idle_add(self.emit_update_available) | ||||
|   | ||||
| @@ -40,7 +40,6 @@ import os | ||||
| import time | ||||
| import datetime | ||||
| from gen.ggettext import sgettext as _ | ||||
| from gen.ggettext import ngettext | ||||
| from cStringIO import StringIO | ||||
| from collections import defaultdict | ||||
| import sys | ||||
| @@ -73,7 +72,7 @@ from gen.plug import REPORT | ||||
| from gen.plug.report._constants import standalone_categories | ||||
| from gui.plug import (PluginWindows, ReportPluginDialog, ToolPluginDialog) | ||||
| from gui.plug.report import report | ||||
| from gen.plug.utils import version_str_to_tup, load_addon_file | ||||
| from gui.utils import AvailableUpdates | ||||
| from gui.pluginmanager import GuiPluginManager | ||||
| import Relationship | ||||
| import DisplayState | ||||
| @@ -95,7 +94,6 @@ from gen.db.exceptions import DbException | ||||
| from gui.aboutdialog import GrampsAboutDialog | ||||
| from gui.navigator import Navigator | ||||
| from gui.views.tags import Tags | ||||
| from gen.utils.configmanager import safe_eval | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| @@ -228,32 +226,6 @@ WIKI_HELP_PAGE_FAQ = '%s_-_FAQ' % const.URL_MANUAL_PAGE | ||||
| WIKI_HELP_PAGE_KEY = '%s_-_Keybindings' % const.URL_MANUAL_PAGE | ||||
| WIKI_HELP_PAGE_MAN = '%s' % const.URL_MANUAL_PAGE | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # Local Functions | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| def update_rows(model, path, iter, user_data): | ||||
|     """ | ||||
|     Update the rows of a model. | ||||
|     """ | ||||
|     #path: (8,)   iter: <GtkTreeIter at 0xbfa89fa0> | ||||
|     #path: (8, 0) iter: <GtkTreeIter at 0xbfa89f60> | ||||
|     if len(path) == 2: | ||||
|         row = model[path] | ||||
|         row[0] = user_data | ||||
|         model.row_changed(path, iter) | ||||
|  | ||||
| def get_count(addon_update_list, category): | ||||
|     """ | ||||
|     Get the count of matching category items. | ||||
|     """ | ||||
|     count = 0 | ||||
|     for (status,plugin_url,plugin_dict) in addon_update_list: | ||||
|         if plugin_dict["t"] == category and plugin_url: | ||||
|             count += 1 | ||||
|     return count | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # ViewManager | ||||
| @@ -333,19 +305,18 @@ class ViewManager(CLIManager): | ||||
|         self.rel_class = Relationship.get_relationship_calculator() | ||||
|         self.uistate.set_relationship_class() | ||||
|         # Need to call after plugins have been registered | ||||
|         self.uistate.connect('update-available', self.process_updates) | ||||
|         self.check_for_updates() | ||||
|  | ||||
|     def check_for_updates(self, force=False): | ||||
|     def check_for_updates(self): | ||||
|         """ | ||||
|         Check for add-on updates. | ||||
|         """ | ||||
|         howoften = config.get("behavior.check-for-updates") | ||||
|         whattypes = config.get('behavior.check-for-update-types') | ||||
|         update = False | ||||
|         if force: | ||||
|             update = True | ||||
|         elif howoften != 0: # update never if zero | ||||
|             y,m,d = map(int, | ||||
|                   config.get("behavior.last-check-for-updates").split("/")) | ||||
|         if howoften != 0: # update never if zero | ||||
|             y,m,d = list(map(int, | ||||
|                   config.get("behavior.last-check-for-updates").split("/"))) | ||||
|             days = (datetime.date.today() - datetime.date(y, m, d)).days | ||||
|             if howoften == 1 and days >= 30: # once a month | ||||
|                 update = True | ||||
| @@ -355,233 +326,20 @@ class ViewManager(CLIManager): | ||||
|                 update = True | ||||
|             elif howoften == 4: # always | ||||
|                 update = True | ||||
|  | ||||
|         if update: | ||||
|             import urllib2, locale | ||||
|             LOG.debug("Checking for updated addons...") | ||||
|             langs = [] | ||||
|             lang = locale.getlocale()[0] # not None | ||||
|             if lang: | ||||
|                 langs.append(lang) | ||||
|                 if "_" in lang: | ||||
|                     lang, variation = lang.split("_", 1) | ||||
|                     langs.append(lang) | ||||
|             langs.append("en") | ||||
|             # now we have a list of languages to try: | ||||
|             fp = None | ||||
|             for lang in langs: | ||||
|                 URL = "%s/listings/addons-%s.txt" % (config.get("behavior.addons-url"), lang) | ||||
|                 LOG.debug("   trying: %s" % URL) | ||||
|                 try: | ||||
|                     fp = urllib2.urlopen(URL, timeout=10) # wait up to 10 seconds | ||||
|                 except: # some error | ||||
|                     LOG.debug("   IOError!") | ||||
|                     fp = None | ||||
|                 if fp and fp.getcode() == 200: # ok | ||||
|                     break | ||||
|             addon_update_list = [] | ||||
|             if fp and fp.getcode() == 200: | ||||
|                 lines = list(fp.readlines()) | ||||
|                 count = 0 | ||||
|                 for line in lines: | ||||
|                     try: | ||||
|                         plugin_dict = safe_eval(line) | ||||
|                         if type(plugin_dict) != type({}): | ||||
|                             raise TypeError("Line with addon metadata is not " | ||||
|                                             "a dictionary") | ||||
|                     except: | ||||
|                         LOG.debug("Skipped a line in the addon listing: " + | ||||
|                                  str(line)) | ||||
|                         continue | ||||
|                     id = plugin_dict["i"] | ||||
|                     plugin = self._pmgr.get_plugin(id) | ||||
|                     if plugin: | ||||
|                         LOG.debug("Comparing %s > %s" % | ||||
|                                   (version_str_to_tup(plugin_dict["v"], 3), | ||||
|                                    version_str_to_tup(plugin.version, 3))) | ||||
|                         if (version_str_to_tup(plugin_dict["v"], 3) > | ||||
|                             version_str_to_tup(plugin.version, 3)): | ||||
|                             LOG.debug("   Downloading '%s'..." % plugin_dict["z"]) | ||||
|                             if "update" in whattypes: | ||||
|                                 if (not config.get('behavior.do-not-show-previously-seen-updates') or | ||||
|                                      plugin_dict["i"] not in config.get('behavior.previously-seen-updates')): | ||||
|                                     addon_update_list.append((_("Updated"), | ||||
|                                                               "%s/download/%s" % | ||||
|                                                               (config.get("behavior.addons-url"), | ||||
|                                                                plugin_dict["z"]), | ||||
|                                                               plugin_dict)) | ||||
|                         else: | ||||
|                             LOG.debug("   '%s' is ok" % plugin_dict["n"]) | ||||
|                     else: | ||||
|                         LOG.debug("   '%s' is not installed" % plugin_dict["n"]) | ||||
|                         if "new" in whattypes: | ||||
|                             if (not config.get('behavior.do-not-show-previously-seen-updates') or | ||||
|                                  plugin_dict["i"] not in config.get('behavior.previously-seen-updates')): | ||||
|                                 addon_update_list.append((_("New"), | ||||
|                                                           "%s/download/%s" % | ||||
|                                                           (config.get("behavior.addons-url"), | ||||
|                                                            plugin_dict["z"]), | ||||
|                                                           plugin_dict)) | ||||
|                 config.set("behavior.last-check-for-updates", | ||||
|                            datetime.date.today().strftime("%Y/%m/%d")) | ||||
|                 count += 1 | ||||
|             else: | ||||
|                 from QuestionDialog import OkDialog | ||||
|                 OkDialog(_("Checking Addons Failed"), | ||||
|                          _("The addon repository appears to be unavailable. Please try again later."), | ||||
|                          self.window) | ||||
|                 if fp: | ||||
|                     fp.close() | ||||
|                 return | ||||
|             LOG.debug("Done checking!") | ||||
|             # List of translated strings used here | ||||
|             # Dead code for l10n | ||||
|             _('new'), _('update') | ||||
|             if addon_update_list: | ||||
|                 self.update_addons(addon_update_list) | ||||
|             elif force: | ||||
|                 from QuestionDialog import OkDialog | ||||
|                 OkDialog(_("There are no available addons of this type"), | ||||
|                          _("Checked for '%s'") % | ||||
|                          _("' and '").join([_(t) for t in config.get('behavior.check-for-update-types')]), | ||||
|                          self.window) | ||||
|             AvailableUpdates(self.uistate).start() | ||||
|  | ||||
|     def update_addons(self, addon_update_list): | ||||
|         from glade import Glade | ||||
|         import ManagedWindow | ||||
|         from ListModel import ListModel, NOSORT, TOGGLE | ||||
|         glade = Glade("updateaddons.glade") | ||||
|         self.update_dialog = glade.toplevel | ||||
|         ManagedWindow.set_titles(self.update_dialog, | ||||
|                                  glade.get_object('title'), | ||||
|                                  _('Available Gramps Updates for Addons')) | ||||
|         apply_button = glade.get_object('apply') | ||||
|         cancel_button = glade.get_object('cancel') | ||||
|         select_all = glade.get_object('select_all') | ||||
|         select_all.connect("clicked", self.select_all_clicked) | ||||
|         select_none = glade.get_object('select_none') | ||||
|         select_none.connect("clicked", self.select_none_clicked) | ||||
|         apply_button.connect("clicked", self.install_addons) | ||||
|         cancel_button.connect("clicked", | ||||
|                               lambda obj: self.update_dialog.destroy()) | ||||
|         self.list = ListModel(glade.get_object("list"), [ | ||||
|                 # name, click?, width, toggle | ||||
|                 {"name": _('Select'), | ||||
|                  "width": 60, | ||||
|                  "type": TOGGLE, | ||||
|                  "visible_col": 6, | ||||
|                  "editable": True},                         # 0 selected? | ||||
|                 (_('Type'), 1, 180),                        # 1 new gramplet | ||||
|                 (_('Name'), 2, 200),                        # 2 name (version) | ||||
|                 (_('Description'), 3, 200),                 # 3 description | ||||
|                 ('', NOSORT, 0),                            # 4 url | ||||
|                 ('', NOSORT, 0),                            # 5 id | ||||
|                 {"name": '', "type": TOGGLE},               # 6 visible? bool | ||||
|                 ], list_mode="tree") | ||||
|         pos = None | ||||
|         addon_update_list.sort(key=lambda x: "%s %s" % (x[0], x[2]["t"])) | ||||
|         last_category = None | ||||
|         for (status,plugin_url,plugin_dict) in addon_update_list: | ||||
|             count = get_count(addon_update_list, plugin_dict["t"]) | ||||
|             category = _("%(adjective)s: %(addon)s") % { | ||||
|                 "adjective": status,  | ||||
|                 "addon": _(plugin_dict["t"])} | ||||
|             if last_category != category: | ||||
|                 last_category = category | ||||
|                 node = self.list.add([False, # initially selected? | ||||
|                                       category, | ||||
|                                       "", | ||||
|                                       "", | ||||
|                                       "", | ||||
|                                       "", | ||||
|                                       False]) # checkbox visible? | ||||
|             iter = self.list.add([False, # initially selected? | ||||
|                                   "%s %s" % (status, _(plugin_dict["t"])), | ||||
|                                   "%s (%s)" % (plugin_dict["n"], | ||||
|                                                plugin_dict["v"]), | ||||
|                                   plugin_dict["d"], | ||||
|                                   plugin_url, | ||||
|                                   plugin_dict["i"], | ||||
|                                   True], node=node) | ||||
|             if pos is None: | ||||
|                 pos = iter | ||||
|         if pos: | ||||
|             self.list.selection.select_iter(pos) | ||||
|         self.update_dialog.run() | ||||
|  | ||||
|     def select_all_clicked(self, widget): | ||||
|     def process_updates(self, addon_update_list): | ||||
|         """ | ||||
|         Select all of the addons for download. | ||||
|         Called when add-on updates are available. | ||||
|         """ | ||||
|         self.list.model.foreach(update_rows, True) | ||||
|         self.list.tree.expand_all() | ||||
|         try: | ||||
|             PluginWindows.UpdateAddons(self.uistate, [], addon_update_list) | ||||
|         except WindowActiveError: | ||||
|             pass | ||||
|  | ||||
|     def select_none_clicked(self, widget): | ||||
|         """ | ||||
|         Select none of the addons for download. | ||||
|         """ | ||||
|         self.list.model.foreach(update_rows, False) | ||||
|         self.list.tree.expand_all() | ||||
|  | ||||
|     def install_addons(self, obj): | ||||
|         """ | ||||
|         Process all of the selected addons. | ||||
|         """ | ||||
|         from QuestionDialog import OkDialog | ||||
|         from gui.widgets.progressdialog import LongOpStatus | ||||
|         self.update_dialog.hide() | ||||
|         model = self.list.model | ||||
|  | ||||
|         iter = model.get_iter_first() | ||||
|         length = 0 | ||||
|         while iter: | ||||
|             iter = model.iter_next(iter) | ||||
|             if iter: | ||||
|                 length += model.iter_n_children(iter) | ||||
|  | ||||
|         longop = LongOpStatus( | ||||
|             _("Downloading and installing selected addons..."), | ||||
|             length, 1, # total, increment-by | ||||
|             can_cancel=True) | ||||
|         pm = ProgressMonitor(GtkProgressDialog, | ||||
|                              ("Title", self.window, gtk.DIALOG_MODAL)) | ||||
|         pm.add_op(longop) | ||||
|         count = 0 | ||||
|         if not config.get('behavior.do-not-show-previously-seen-updates'): | ||||
|             # reset list | ||||
|             config.get('behavior.previously-seen-updates')[:] = [] | ||||
|  | ||||
|         iter = model.get_iter_first() | ||||
|         while iter: | ||||
|             for rowcnt in range(model.iter_n_children(iter)): | ||||
|                 child = model.iter_nth_child(iter, rowcnt) | ||||
|                 row = [model.get_value(child, n) for n in range(6)] | ||||
|                 if longop.should_cancel(): | ||||
|                     break | ||||
|                 elif row[0]: # toggle on | ||||
|                     load_addon_file(row[4], callback=LOG.debug) | ||||
|                     count += 1 | ||||
|                 else: # add to list of previously seen, but not installed | ||||
|                     if row[5] not in config.get('behavior.previously-seen-updates'): | ||||
|                         config.get('behavior.previously-seen-updates').append(row[5]) | ||||
|                 longop.heartbeat() | ||||
|                 pm._get_dlg()._process_events() | ||||
|             iter = model.iter_next(iter) | ||||
|  | ||||
|         if not longop.was_cancelled(): | ||||
|             longop.end() | ||||
|         if count: | ||||
|             self.do_reg_plugins(self.dbstate, self.uistate) | ||||
|             OkDialog(_("Done downloading and installing addons"), | ||||
|                      "%s %s" % (ngettext("%d addon was installed.", | ||||
|                                          "%d addons were installed.", | ||||
|                                          count) % count, | ||||
|                                 _("You need to restart Gramps to see new views.")), | ||||
|                      self.window) | ||||
|         else: | ||||
|             OkDialog(_("Done downloading and installing addons"), | ||||
|                      _("No addons were installed."), | ||||
|                      self.window) | ||||
|         self.update_dialog.destroy() | ||||
|         self.do_reg_plugins(self.dbstate, self.uistate) | ||||
|  | ||||
|     def _errordialog(self, title, errormessage): | ||||
|         """ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user