From 1a782eb0ffa9ca7bfc063902b35b06f6e8399271 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 23 Sep 2010 20:36:52 +0100 Subject: [PATCH] - Some cleanups on templates. - Make save_to_disk templates sanitize all fields except composites --- src/calibre/ebooks/metadata/book/base.py | 9 ++- src/calibre/library/save_to_disk.py | 12 ++-- src/calibre/utils/formatter.py | 72 ++++++++++++------------ 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 9e1085df25..929dc01aec 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -34,9 +34,10 @@ NULL_VALUES = { field_metadata = FieldMetadata() class SafeFormat(TemplateFormatter): - def get_value(self, key, args, mi): + + def get_value(self, key, args, kwargs): try: - ign, v = mi.format_field(key.lower(), series_with_index=False) + ign, v = self.book.format_field(key.lower(), series_with_index=False) if v is None: return '' if v == '': @@ -100,7 +101,9 @@ class Metadata(object): cf['#value#'] = 'RECURSIVE_COMPOSITE FIELD ' + field cf['#value#'] = composite_formatter.safe_format( cf['display']['composite_template'], - self, _('TEMPLATE ERROR')).strip() + self, + _('TEMPLATE ERROR'), + self).strip() return d['#value#'] raise AttributeError( diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index 11922b7154..2d0a3d1277 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -108,8 +108,12 @@ class SafeFormat(TemplateFormatter): ''' def get_value(self, key, args, kwargs): try: - if kwargs[key.lower()]: - return kwargs[key.lower()] + b = self.book.get_user_metadata(key, False) + key = key.lower() + if b is not None and b['datatype'] == 'composite': + return self.vformat(b['display']['composite_template'], [], kwargs) + if kwargs[key]: + return self.sanitize(kwargs[key.lower()]) return '' except: return '' @@ -159,9 +163,9 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250, elif custom_metadata[key]['datatype'] == 'bool': format_args[key] = _('yes') if format_args[key] else _('no') - components = safe_formatter.safe_format(template, format_args, '') + components = safe_formatter.safe_format(template, format_args, '', mi, + sanitize=sanitize_func) components = [x.strip() for x in components.split('/') if x.strip()] - components = [sanitize_func(x) for x in components if x] if not components: components = [str(id)] components = [x.encode(filesystem_encoding, 'replace') if isinstance(x, diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index f9ef4e0846..95870d9c61 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -6,48 +6,48 @@ Created on 23 Sep 2010 import re, string -def _lookup(val, mi, field_if_set, field_not_set): - if hasattr(mi, 'format_field'): - if val: - return mi.format_field(field_if_set.strip())[1] - else: - return mi.format_field(field_not_set.strip())[1] - else: - if val: - return mi.get(field_if_set.strip(), '') - else: - return mi.get(field_not_set.strip(), '') - -def _ifempty(val, mi, value_if_empty): - if val: - return val - else: - return value_if_empty - -def _shorten(val, mi, leading, center_string, trailing): - l = int(leading) - t = int(trailing) - if len(val) > l + len(center_string) + t: - return val[0:l] + center_string + val[-t:] - else: - return val - class TemplateFormatter(string.Formatter): ''' Provides a format function that substitutes '' for any missing value ''' + def __init__(self): + string.Formatter.__init__(self) + self.book = None + self.kwargs = None + self.sanitize = None + + def _lookup(self, val, field_if_set, field_not_set): + if val: + return self.vformat('{'+field_if_set.strip()+'}', [], self.kwargs) + else: + return self.vformat('{'+field_not_set.strip()+'}', [], self.kwargs) + + def _ifempty(self, val, value_if_empty): + if val: + return val + else: + return value_if_empty + + def _shorten(self, val, leading, center_string, trailing): + l = int(leading) + t = int(trailing) + if len(val) > l + len(center_string) + t: + return val[0:l] + center_string + val[-t:] + else: + return val + functions = { - 'uppercase' : (0, lambda x: x.upper()), - 'lowercase' : (0, lambda x: x.lower()), - 'titlecase' : (0, lambda x: x.title()), - 'capitalize' : (0, lambda x: x.capitalize()), + 'uppercase' : (0, lambda s,x: x.upper()), + 'lowercase' : (0, lambda s,x: x.lower()), + 'titlecase' : (0, lambda s,x: x.title()), + 'capitalize' : (0, lambda s,x: x.capitalize()), 'ifempty' : (1, _ifempty), 'lookup' : (2, _lookup), 'shorten' : (3, _shorten), } - def get_value(self, key, args, mi): + def get_value(self, key, args): raise Exception('get_value must be implemented in the subclass') format_string_re = re.compile(r'^(.*)\|(.*)\|(.*)$') @@ -75,9 +75,9 @@ class TemplateFormatter(string.Formatter): (func[0] > 0 and func[0] != len(args)): raise Exception ('Incorrect number of arguments for function '+ fmt[0:p]) if func[0] == 0: - val = func[1](val, self.mi) + val = func[1](self, val) else: - val = func[1](val, self.mi, *args) + val = func[1](self, val, *args) else: val = string.Formatter.format_field(self, val, fmt) if not val: @@ -87,11 +87,13 @@ class TemplateFormatter(string.Formatter): compress_spaces = re.compile(r'\s+') def vformat(self, fmt, args, kwargs): - self.mi = kwargs ans = string.Formatter.vformat(self, fmt, args, kwargs) return self.compress_spaces.sub(' ', ans).strip() - def safe_format(self, fmt, kwargs, error_value): + def safe_format(self, fmt, kwargs, error_value, book, sanitize=None): + self.kwargs = kwargs + self.book = book + self.sanitize = sanitize try: ans = self.vformat(fmt, [], kwargs).strip() except: