Fix Book XML handler to deal with unusual characters in Book name Gramps42 (#536)

* Fix Book XML handler to deal with unusual characters in Book name

Fixes #10387

* Fix 'Generate Book' dialog for bad transient parent

* Fix Book XML handler for unusual characters in report name

Issue #10387
This commit is contained in:
Paul Culley 2018-01-28 21:29:43 -06:00 committed by Sam Manzi
parent 4d68f68742
commit 09bc188a1c
3 changed files with 91 additions and 71 deletions

View File

@ -457,79 +457,98 @@ class BookList(object):
""" """
Saves the current BookList to the associated file. Saves the current BookList to the associated file.
""" """
f = open(self.file, "w") with open(self.file, "w", encoding="utf-8") as b_f:
f.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n") b_f.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
f.write('<booklist>\n') b_f.write('<booklist>\n')
for name in sorted(self.bookmap): # enable a diff of archived copies for name in sorted(self.bookmap): # enable a diff of archived copies
book = self.get_book(name) book = self.get_book(name)
dbname = book.get_dbname() dbname = escape(book.get_dbname())
f.write('<book name="%s" database="%s">\n' % (name, dbname) ) b_f.write(' <book name="%s" database="%s">'
for item in book.get_item_list(): '\n' % (escape(name), dbname))
f.write(' <item name="%s" trans_name="%s">\n' % for item in book.get_item_list():
(item.get_name(),item.get_translated_name() ) ) b_f.write(' <item name="%s" '
options = item.option_class.handler.options_dict 'trans_name="%s">\n' % (
for option_name in sorted(options.keys()): # enable a diff item.get_name(),
option_value = options[option_name] item.get_translated_name()))
if isinstance(option_value, (list, tuple)): options = item.option_class.handler.options_dict
f.write(' <option name="%s" value="" ' for option_name in sorted(options.keys()): # enable a diff
'length="%d">\n' % ( option_value = options[option_name]
escape(option_name), if isinstance(option_value, (list, tuple)):
len(options[option_name]) ) ) b_f.write(' <option name="%s" value="" '
for list_index in range(len(option_value)): 'length="%d">\n' % (
option_type = type_name(option_value[list_index]) escape(option_name),
value = escape(str(option_value[list_index])) len(options[option_name])))
for list_index in range(len(option_value)):
option_type = type_name(
option_value[list_index])
value = escape(str(option_value[list_index]))
value = value.replace('"', '&quot;')
b_f.write(' <listitem number="%d" '
'type="%s" value="%s"/>\n' % (
list_index,
option_type,
value))
b_f.write(' </option>\n')
else:
option_type = type_name(option_value)
value = escape(str(option_value))
value = value.replace('"', '&quot;') value = value.replace('"', '&quot;')
f.write(' <listitem number="%d" type="%s" ' b_f.write(' <option name="%s" type="%s" '
'value="%s"/>\n' % ( 'value="%s"/>\n' % (
list_index, escape(option_name),
option_type, option_type,
value ) ) value))
f.write(' </option>\n')
else:
option_type = type_name(option_value)
value = escape(str(option_value))
value = value.replace('"', '&quot;')
f.write(' <option name="%s" type="%s" '
'value="%s"/>\n' % (
escape(option_name),
option_type,
value) )
f.write(' <style name="%s"/>\n' % item.get_style_name() ) b_f.write(' <style name="%s"/>'
f.write(' </item>\n') '\n' % item.get_style_name())
if book.get_paper_name(): b_f.write(' </item>\n')
f.write(' <paper name="%s"/>\n' % book.get_paper_name() ) if book.get_paper_name():
if book.get_orientation() is not None: # 0 is legal b_f.write(' <paper name="%s"/>'
f.write(' <orientation value="%s"/>\n' % '\n' % book.get_paper_name())
book.get_orientation() ) if book.get_orientation() is not None: # 0 is legal
if book.get_paper_metric() is not None: # 0 is legal b_f.write(' <orientation value="%s"/>'
f.write(' <metric value="%s"/>\n' % book.get_paper_metric() ) '\n' % book.get_orientation())
if book.get_custom_paper_size(): if book.get_paper_metric() is not None: # 0 is legal
size = book.get_custom_paper_size() b_p_metric = book.get_paper_metric()
f.write(' <size value="%f %f"/>\n' % (size[0], size[1]) ) if isinstance(b_p_metric, bool):
if book.get_margins(): b_p_metric = int(b_p_metric)
for pos in range(len(book.get_margins())): b_f.write(' <metric value="%s"/>'
f.write(' <margin number="%s" value="%f"/>\n' % '\n' % b_p_metric)
(pos, book.get_margin(pos)) ) if book.get_custom_paper_size():
if book.get_format_name(): size = book.get_custom_paper_size()
f.write(' <format name="%s"/>\n' % book.get_format_name() ) b_f.write(' <size value="%f %f"/>'
if book.get_output(): '\n' % (size[0], size[1]))
f.write(' <output name="%s"/>\n' % book.get_output() ) if book.get_margins():
f.write('</book>\n') for pos in range(len(book.get_margins())):
b_f.write(' <margin number="%s" '
'value="%f"/>\n' % (
pos, book.get_margin(pos)))
if book.get_format_name():
b_f.write(' <format name="%s"/>'
'\n' % book.get_format_name())
if book.get_output():
b_f.write(' <output name="%s"/>'
'\n' % escape(book.get_output()))
b_f.write(' </book>\n')
b_f.write('</booklist>\n')
f.write('</booklist>\n')
f.close()
def parse(self): def parse(self):
""" """
Loads the BookList from the associated file, if it exists. Loads the BookList from the associated file, if it exists.
""" """
try: try:
p = make_parser() parser = make_parser()
p.setContentHandler(BookParser(self, self.dbase)) parser.setContentHandler(BookParser(self, self.dbase))
the_file = open(self.file) # bug 10387; XML should be utf8, but was not previously saved
p.parse(the_file) # that way. So try to read utf8, if fails, try with system
the_file.close() # encoding. Only an issue on non-utf8 systems.
try:
with open(self.file, encoding="utf-8") as the_file:
parser.parse(the_file)
except UnicodeDecodeError:
with open(self.file) as the_file:
parser.parse(the_file)
except (IOError, OSError, ValueError, SAXParseException, KeyError, except (IOError, OSError, ValueError, SAXParseException, KeyError,
AttributeError): AttributeError):
pass pass

View File

@ -723,7 +723,7 @@ class BookSelector(ManagedWindow):
""" """
if self.book.item_list: if self.book.item_list:
BookDialog(self.dbstate, self.uistate, BookDialog(self.dbstate, self.uistate,
self.book, BookOptions) self.book, BookOptions, track=self.track)
else: else:
WarningDialog(_('No items'), _('This book has no items.'), WarningDialog(_('No items'), _('This book has no items.'),
parent=self.window) parent=self.window)
@ -916,14 +916,14 @@ class BookDialog(DocReportDialog):
Create a dialog selecting target, format, and paper/HTML options. Create a dialog selecting target, format, and paper/HTML options.
""" """
def __init__(self, dbstate, uistate, book, options): def __init__(self, dbstate, uistate, book, options, track=[]):
self.format_menu = None self.format_menu = None
self.options = options self.options = options
self.is_from_saved_book = False self.is_from_saved_book = False
self.page_html_added = False self.page_html_added = False
self.book = book self.book = book
DocReportDialog.__init__(self, dbstate, uistate, options, DocReportDialog.__init__(self, dbstate, uistate, options,
'book', _("Book")) 'book', _("Book"), track=track)
self.options.options_dict['bookname'] = self.book.name self.options.options_dict['bookname'] = self.book.name
self.database = dbstate.db self.database = dbstate.db

View File

@ -58,16 +58,17 @@ class DocReportDialog(ReportDialog):
dialogs for docgen derived reports. dialogs for docgen derived reports.
""" """
def __init__(self, dbstate, uistate, option_class, name, trans_name): def __init__(self, dbstate, uistate, option_class, name, trans_name,
track=[]):
"""Initialize a dialog to request that the user select options """Initialize a dialog to request that the user select options
for a basic *stand-alone* report.""" for a basic *stand-alone* report."""
self.style_name = "default" self.style_name = "default"
self.firstpage_added = False self.firstpage_added = False
self.CSS = PLUGMAN.process_plugin_data('WEBSTUFF') self.CSS = PLUGMAN.process_plugin_data('WEBSTUFF')
self.dbname = dbstate.db.get_dbname() self.dbname = dbstate.db.get_dbname()
ReportDialog.__init__(self, dbstate, uistate, option_class, ReportDialog.__init__(self, dbstate, uistate, option_class,
name, trans_name) name, trans_name, track=track)
# Allow for post processing of the format frame, since the # Allow for post processing of the format frame, since the
# show_all task calls events that may reset values # show_all task calls events that may reset values