4735: Check for updates in a separate thread
svn: r22924
This commit is contained in:
parent
314cf24850
commit
b0a4f75106
@ -358,6 +358,7 @@ class DisplayState(gen.utils.Callback):
|
|||||||
'filters-changed' : (str, ),
|
'filters-changed' : (str, ),
|
||||||
'filter-name-changed' : (str, unicode, unicode),
|
'filter-name-changed' : (str, unicode, unicode),
|
||||||
'nameformat-changed' : None,
|
'nameformat-changed' : None,
|
||||||
|
'update-available' : (list, ),
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav_type to message
|
#nav_type to message
|
||||||
|
@ -32,6 +32,15 @@ General utility functions useful for the generic plugin system
|
|||||||
import locale
|
import locale
|
||||||
import sys
|
import sys
|
||||||
import os
|
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
|
from gen.plug._pluginreg import make_environment
|
||||||
import const
|
import const
|
||||||
import Utils
|
import Utils
|
||||||
|
from gen.plug import BasePluginManager
|
||||||
|
from gen.utils.configmanager import safe_eval
|
||||||
|
import config
|
||||||
from gen.ggettext import gettext as _
|
from gen.ggettext import gettext as _
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
@ -188,6 +200,96 @@ class Zipfile(object):
|
|||||||
"""
|
"""
|
||||||
return os.path.split(name)[1]
|
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):
|
def load_addon_file(path, callback=None):
|
||||||
"""
|
"""
|
||||||
Load an addon from a particular path (from URL or file system).
|
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 gui.widgets import MarkupLabel, BasicLabel
|
||||||
from QuestionDialog import ErrorDialog, QuestionDialog2, OkDialog
|
from QuestionDialog import ErrorDialog, QuestionDialog2, OkDialog
|
||||||
from glade import Glade
|
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)
|
table.attach(checkbutton, 1, 2, 9, 10, yoptions=0)
|
||||||
button = gtk.Button(_("Check now"))
|
button = gtk.Button(_("Check now"))
|
||||||
button.connect("clicked", lambda obj: \
|
button.connect("clicked", self.check_for_updates)
|
||||||
self.uistate.viewmanager.check_for_updates(force=True))
|
|
||||||
table.attach(button, 3, 4, 9, 10, yoptions=0)
|
table.attach(button, 3, 4, 9, 10, yoptions=0)
|
||||||
|
|
||||||
return _('General'), table
|
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):
|
def add_famtree_panel(self, configdialog):
|
||||||
table = gtk.Table(2, 2)
|
table = gtk.Table(2, 2)
|
||||||
table.set_border_width(12)
|
table.set_border_width(12)
|
||||||
|
@ -32,6 +32,14 @@ import traceback
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# set up logging
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
import logging
|
||||||
|
LOG = logging.getLogger(".gui.plug")
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# GTK modules
|
# GTK modules
|
||||||
@ -50,14 +58,19 @@ import ManagedWindow
|
|||||||
import Errors
|
import Errors
|
||||||
from gen.plug import PluginRegister, PTYPE_STR, load_addon_file
|
from gen.plug import PluginRegister, PTYPE_STR, load_addon_file
|
||||||
from gen.ggettext import gettext as _
|
from gen.ggettext import gettext as _
|
||||||
|
from gen.ggettext import ngettext
|
||||||
from gui.utils import open_file_with_default_application
|
from gui.utils import open_file_with_default_application
|
||||||
from gui.pluginmanager import GuiPluginManager
|
from gui.pluginmanager import GuiPluginManager
|
||||||
from gui.plug import tool, add_gui_options
|
from gui.plug import tool, add_gui_options
|
||||||
from QuestionDialog import InfoDialog
|
from QuestionDialog import InfoDialog, OkDialog
|
||||||
from gui.editors import EditPerson
|
from gui.editors import EditPerson
|
||||||
|
from glade import Glade
|
||||||
|
from ListModel import ListModel, NOSORT, TOGGLE
|
||||||
import Utils
|
import Utils
|
||||||
import const
|
import const
|
||||||
import config
|
import config
|
||||||
|
from gui.widgets.progressdialog import (LongOpStatus, ProgressMonitor,
|
||||||
|
GtkProgressDialog)
|
||||||
|
|
||||||
def display_message(message):
|
def display_message(message):
|
||||||
"""
|
"""
|
||||||
@ -1041,3 +1054,176 @@ class ToolManagedWindow(tool.Tool, ToolManagedWindowBase):
|
|||||||
tool.Tool.__init__(self, dbstate, options_class, name)
|
tool.Tool.__init__(self, dbstate, options_class, name)
|
||||||
ToolManagedWindowBase.__init__(self, dbstate, uistate, options_class,
|
ToolManagedWindowBase.__init__(self, dbstate, uistate, options_class,
|
||||||
name, callback)
|
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 os
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import threading
|
||||||
from gen.ggettext import gettext as _
|
from gen.ggettext import gettext as _
|
||||||
import constfunc
|
import constfunc
|
||||||
# gtk is not included here, because this file is currently imported
|
# gtk is not included here, because this file is currently imported
|
||||||
@ -43,6 +44,7 @@ import constfunc
|
|||||||
# GNOME/GTK
|
# GNOME/GTK
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
import gobject
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
@ -52,6 +54,7 @@ import constfunc
|
|||||||
import gen.lib
|
import gen.lib
|
||||||
import Errors
|
import Errors
|
||||||
import constfunc
|
import constfunc
|
||||||
|
from gen.plug.utils import available_updates
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
@ -391,3 +394,22 @@ def is_right_click(event):
|
|||||||
|
|
||||||
if event.button == 3:
|
if event.button == 3:
|
||||||
return True
|
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 time
|
||||||
import datetime
|
import datetime
|
||||||
from gen.ggettext import sgettext as _
|
from gen.ggettext import sgettext as _
|
||||||
from gen.ggettext import ngettext
|
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import sys
|
import sys
|
||||||
@ -73,7 +72,7 @@ from gen.plug import REPORT
|
|||||||
from gen.plug.report._constants import standalone_categories
|
from gen.plug.report._constants import standalone_categories
|
||||||
from gui.plug import (PluginWindows, ReportPluginDialog, ToolPluginDialog)
|
from gui.plug import (PluginWindows, ReportPluginDialog, ToolPluginDialog)
|
||||||
from gui.plug.report import report
|
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
|
from gui.pluginmanager import GuiPluginManager
|
||||||
import Relationship
|
import Relationship
|
||||||
import DisplayState
|
import DisplayState
|
||||||
@ -95,7 +94,6 @@ from gen.db.exceptions import DbException
|
|||||||
from gui.aboutdialog import GrampsAboutDialog
|
from gui.aboutdialog import GrampsAboutDialog
|
||||||
from gui.navigator import Navigator
|
from gui.navigator import Navigator
|
||||||
from gui.views.tags import Tags
|
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_KEY = '%s_-_Keybindings' % const.URL_MANUAL_PAGE
|
||||||
WIKI_HELP_PAGE_MAN = '%s' % 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
|
# ViewManager
|
||||||
@ -333,19 +305,18 @@ class ViewManager(CLIManager):
|
|||||||
self.rel_class = Relationship.get_relationship_calculator()
|
self.rel_class = Relationship.get_relationship_calculator()
|
||||||
self.uistate.set_relationship_class()
|
self.uistate.set_relationship_class()
|
||||||
# Need to call after plugins have been registered
|
# Need to call after plugins have been registered
|
||||||
|
self.uistate.connect('update-available', self.process_updates)
|
||||||
self.check_for_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")
|
howoften = config.get("behavior.check-for-updates")
|
||||||
whattypes = config.get('behavior.check-for-update-types')
|
|
||||||
update = False
|
update = False
|
||||||
if force:
|
if howoften != 0: # update never if zero
|
||||||
update = True
|
y,m,d = list(map(int,
|
||||||
elif howoften != 0: # update never if zero
|
config.get("behavior.last-check-for-updates").split("/")))
|
||||||
y,m,d = map(int,
|
|
||||||
config.get("behavior.last-check-for-updates").split("/"))
|
|
||||||
days = (datetime.date.today() - datetime.date(y, m, d)).days
|
days = (datetime.date.today() - datetime.date(y, m, d)).days
|
||||||
if howoften == 1 and days >= 30: # once a month
|
if howoften == 1 and days >= 30: # once a month
|
||||||
update = True
|
update = True
|
||||||
@ -355,233 +326,20 @@ class ViewManager(CLIManager):
|
|||||||
update = True
|
update = True
|
||||||
elif howoften == 4: # always
|
elif howoften == 4: # always
|
||||||
update = True
|
update = True
|
||||||
|
|
||||||
if update:
|
if update:
|
||||||
import urllib2, locale
|
AvailableUpdates(self.uistate).start()
|
||||||
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)
|
|
||||||
|
|
||||||
def update_addons(self, addon_update_list):
|
def process_updates(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):
|
|
||||||
"""
|
"""
|
||||||
Select all of the addons for download.
|
Called when add-on updates are available.
|
||||||
"""
|
"""
|
||||||
self.list.model.foreach(update_rows, True)
|
try:
|
||||||
self.list.tree.expand_all()
|
PluginWindows.UpdateAddons(self.uistate, [], addon_update_list)
|
||||||
|
except WindowActiveError:
|
||||||
|
pass
|
||||||
|
|
||||||
def select_none_clicked(self, widget):
|
self.do_reg_plugins(self.dbstate, self.uistate)
|
||||||
"""
|
|
||||||
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()
|
|
||||||
|
|
||||||
def _errordialog(self, title, errormessage):
|
def _errordialog(self, title, errormessage):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user