Fix to make Gtk 'action names' always valid

Fixes #11291
This commit is contained in:
prculley 2019-08-31 09:39:07 -05:00 committed by Nick Hall
parent e10df9eb6d
commit e983578fff
7 changed files with 45 additions and 23 deletions

View File

@ -193,7 +193,7 @@ class PluginData:
.. attribute:: id
A unique identifier for the plugin. This is eg used to store the plugin
settings.
settings. MUST be in ASCII, with only "_- ().,'" special characters.
.. attribute:: name
A friendly name to call this plugin (normally translated)
.. attribute:: name_accell

View File

@ -49,7 +49,7 @@ from gramps.gen.const import GLADE_FILE, ICON
from gramps.gen.errors import WindowActiveError
from gramps.gen.config import config
from gramps.gen.constfunc import is_quartz
from .uimanager import ActionGroup
from .uimanager import ActionGroup, valid_action_name
from .glade import Glade
#-------------------------------------------------------------------------
@ -280,7 +280,7 @@ class GrampsWindowManager:
return func
def generate_id(self, item):
return 'wm/' + str(item.window_id).replace(' ', '_')
return valid_action_name('wm-' + str(item.window_id))
def display_menu_list(self, data, action_data, mlist):
menuitem = ('<item>\n'

View File

@ -55,6 +55,7 @@ from gi.repository import Gtk
#
#-------------------------------------------------------------------------
from ...pluginmanager import GuiPluginManager
from ...uimanager import valid_action_name
from gramps.gen.plug import (CATEGORY_QR_PERSON, CATEGORY_QR_FAMILY, CATEGORY_QR_MEDIA,
CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE, CATEGORY_QR_MISC,
CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY,
@ -97,11 +98,9 @@ def create_web_connect_menu(dbstate, uistate, nav_group, handle, prefix):
top = ("<placeholder id='WebConnect'><submenu>\n"
'<attribute name="label" translatable="yes">'
'Web Connection</attribute>\n')
actions = []
ofile = StringIO()
ofile.write(top)
#select the web connects to show
showlst = []
pmgr = GuiPluginManager.get_instance()
plugins = pmgr.process_plugin_data('WebConnect')
try:
@ -114,8 +113,10 @@ def create_web_connect_menu(dbstate, uistate, nav_group, handle, prefix):
connections = flatten(connections)
connections.sort(key=lambda plug: plug.name)
actions = []
for connect in connections:
action = connect.key.replace(' ', '-')
for indx, connect in enumerate(connections):
# action would be better with "connect.key", but it seems to be
# non-ASCII sometimes. So we use an action number instead.
action = "web-con-%d" % indx
ofile.write(MENUITEM.format(prefix=prefix, action=action,
label=connect.name))
callback = connect(dbstate, uistate, nav_group, handle)
@ -156,7 +157,7 @@ def create_quickreport_menu(category, dbstate, uistate, handle, prefix, track=[]
showlst.sort(key=lambda x: x.name)
for pdata in showlst:
new_key = pdata.id.replace(' ', '-')
new_key = valid_action_name("qr-%s" % pdata.id)
ofile.write(MENUITEM.format(prefix=prefix, action=new_key,
label=pdata.name))
actions.append((new_key, make_quick_report_callback(

View File

@ -364,6 +364,8 @@ class UIManager():
else:
window_group = group.act_group = self.app.window
for item in group.actionlist:
if not Gio.action_name_is_valid(item[ACTION_NAME]):
LOG.warning('**Invalid action name %s', item[ACTION_NAME])
# deal with accelerator overrides from a file
accel = self.accel_dict.get(group.prefix + item[ACTION_NAME])
if accel:
@ -526,3 +528,17 @@ class UIManager():
with open(filename, 'r') as hndl:
accels = hndl.read()
self.accel_dict = ast.literal_eval(accels)
INVALID_CHARS = [' ', '_', '(', ')', ',', "'"]
def valid_action_name(text):
""" This function cleans up action names to avoid some illegal
characters. It does NOT clean up non-ASCII characters.
This is used for plugin IDs to clean them up. It would be better if we
made all plugin ids:
ASCII Alphanumeric and the '.' or '-' characters."""
for char in INVALID_CHARS:
text = text.replace(char, '-')
return text

View File

@ -92,7 +92,7 @@ from .configure import GrampsPreferences
from .aboutdialog import GrampsAboutDialog
from .navigator import Navigator
from .views.tags import Tags
from .uimanager import ActionGroup
from .uimanager import ActionGroup, valid_action_name
from gramps.gen.lib import (Person, Surname, Family, Media, Note, Place,
Source, Repository, Citation, Event, EventType,
ChildRef)
@ -1429,7 +1429,7 @@ class ViewManager(CLIManager):
pdatas = hash_data[key]
pdatas.sort(key=lambda x: x.name)
for pdata in pdatas:
new_key = pdata.id.replace(' ', '-')
new_key = valid_action_name(pdata.id)
ofile.write(menuitem % (new_key, pdata.name))
actions.append((new_key, func(pdata, self.dbstate,
self.uistate)))

View File

@ -241,7 +241,7 @@ class Tags(DbGUIElement):
tag_menu = ''
menuitem = '''
<item>
<attribute name="action">win.TAG_%s</attribute>
<attribute name="action">win.TAG-%s</attribute>
<attribute name="label">%s</attribute>
</item>'''

View File

@ -31,6 +31,7 @@ GrampletView interface.
from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import Pango
from xml.sax.saxutils import escape
import time
import os
import configparser
@ -49,7 +50,7 @@ from gramps.gen.const import URL_MANUAL_PAGE, VERSION_DIR, COLON
from ..editors import EditPerson, EditFamily
from ..managedwindow import ManagedWindow
from ..utils import is_right_click, match_primary_mask, get_link_color
from ..uimanager import ActionGroup
from ..uimanager import ActionGroup, valid_action_name
from ..plug import make_gui_option
from ..plug.quick import run_quick_report_by_name
from ..display import display_help, display_url
@ -971,8 +972,11 @@ class GridGramplet(GuiGramplet):
def set_title(self, new_title, set_override=True):
# can't do it if already titled that way
if self.title == new_title: return True
if new_title in self.pane.gramplet_map: return False
if self.title == new_title:
return True
if(new_title in self.pane.gramplet_map or
new_title != escape(new_title)): # avoid XML specific characters
return False
if set_override:
self.title_override = True
del self.pane.gramplet_map[self.title]
@ -1464,21 +1468,22 @@ class GrampletPane(Gtk.ScrolledWindow):
actions = []
r_menuitems = ''
a_menuitems = ''
names = [gplug.name for gplug in PLUGMAN.get_reg_gramplets()
if gplug.navtypes == []
or 'Dashboard' in gplug.navtypes]
names.sort()
for name in names:
action_name = name.replace(' ', '-')
a_menuitems += menuitem % (action_name, name)
plugs = [gplug for gplug in PLUGMAN.get_reg_gramplets() if
gplug.navtypes == [] or 'Dashboard' in gplug.navtypes]
plugs.sort(key=lambda x: x.name)
for plug in plugs:
action_name = valid_action_name(plug.id)
a_menuitems += menuitem % (action_name, plug.name)
actions.append((action_name,
make_callback(self.add_gramplet, name)))
make_callback(self.add_gramplet, plug.name)))
names = [gramplet.title for gramplet in self.closed_gramplets]
names.extend(opts["title"] for opts in self.closed_opts)
names.sort()
if len(names) > 0:
for name in names:
action_name = name.replace(' ', '-')
# 'name' could be non-ASCII when in non-English language
# action names must be in ASCII, so use 'id' instead.
action_name = valid_action_name(str(id(name)))
r_menuitems += menuitem % (action_name, name)
actions.append((action_name,
make_callback(self.restore_gramplet,