From c92c3312ed91978a190b8c191e3462badeb3bc8e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 24 May 2010 11:43:59 -0600 Subject: [PATCH] Replace use of genshi with lxml for templates in the news download subsystem --- src/calibre/web/feeds/templates.py | 332 ++++++++++++++--------------- 1 file changed, 159 insertions(+), 173 deletions(-) diff --git a/src/calibre/web/feeds/templates.py b/src/calibre/web/feeds/templates.py index 4b2156b6a1..4de7c42daa 100644 --- a/src/calibre/web/feeds/templates.py +++ b/src/calibre/web/feeds/templates.py @@ -2,207 +2,193 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -from calibre.utils.genshi.template import MarkupTemplate -from calibre import preferred_encoding, strftime +from lxml import html, etree +from lxml.html.builder import HTML, HEAD, TITLE, STYLE, DIV, BODY, \ + STRONG, BR, H1, SPAN, A, HR, UL, LI, H2, IMG, P as PT -class Template(MarkupTemplate): +from calibre import preferred_encoding, strftime, isbytestring + +def CLASS(*args, **kwargs): # class is a reserved word in Python + kwargs['class'] = ' '.join(args) + return kwargs + +class Template(object): + + IS_HTML = True def generate(self, *args, **kwargs): if not kwargs.has_key('style'): kwargs['style'] = '' for key in kwargs.keys(): - if isinstance(kwargs[key], basestring) and not isinstance(kwargs[key], unicode): - kwargs[key] = unicode(kwargs[key], 'utf-8', 'replace') - for arg in args: - if isinstance(arg, basestring) and not isinstance(arg, unicode): - arg = unicode(arg, 'utf-8', 'replace') + if isbytestring(kwargs[key]): + kwargs[key] = kwargs[key].decode('utf-8', 'replace') + if kwargs[key] is None: + kwargs[key] = u'' + args = list(args) + for i in range(len(args)): + if isbytestring(args[i]): + args[i] = args[i].decode('utf-8', 'replace') + if args[i] is None: + args[i] = u'' - return MarkupTemplate.generate(self, *args, **kwargs) + self._generate(*args, **kwargs) + + return self + + def render(self, *args, **kwargs): + if self.IS_HTML: + return html.tostring(self.root, encoding='utf-8', + include_meta_content_type=True, pretty_print=True) + return etree.tostring(self.root, encoding='utf-8', xml_declaration=True, + pretty_print=True) class NavBarTemplate(Template): - def __init__(self): - Template.__init__(self, u'''\ - - - - - - -
-
-

- This article was downloaded by ${__appname__} from ${url} -

-

- - | Next - - - | Next - - | Section menu - - | Main menu - - - | Previous - - | -
-
- - -''') - - def generate(self, bottom, feed, art, number_of_articles_in_feed, + def _generate(self, bottom, feed, art, number_of_articles_in_feed, two_levels, url, __appname__, prefix='', center=True, - extra_css=None): + extra_css=None, style=None): + head = HEAD(TITLE('navbar')) + if style: + head.append(STYLE(style, type='text/css')) + if extra_css: + head.append(STYLE(extra_css, type='text/css')) + if prefix and not prefix.endswith('/'): prefix += '/' - return Template.generate(self, bottom=bottom, art=art, feed=feed, - num=number_of_articles_in_feed, - two_levels=two_levels, url=url, - __appname__=__appname__, prefix=prefix, - center=center, extra_css=extra_css) + align = 'center' if center else 'left' + navbar = DIV(CLASS('calibre_navbar', 'calibre_rescale_70', + style='text-align:'+align)) + if bottom: + navbar.append(HR()) + text = 'This article was downloaded by ' + p = PT(text, STRONG(__appname__), A(url, href=url), style='text-align:left') + p[0].tail = ' from ' + navbar.append(BR()) + navbar.append(BR()) + else: + next = 'feed_%d'%(feed+1) if art == number_of_articles_in_feed - 1 \ + else 'article_%d'%(art+1) + up = '../..' if art == number_of_articles_in_feed - 1 else '..' + href = '%s%s/%s/index.html'%(prefix, up, next) + navbar.text = '| ' + navbar.append(A('Next', href=href)) + href = '%s../index.html#article_%d'%(prefix, art) + navbar.iterchildren(reversed=True).next().tail = ' | ' + navbar.append(A('Section Menu', href=href)) + href = '%s../../index.html#feed_%d'%(prefix, feed) + navbar.iterchildren(reversed=True).next().tail = ' | ' + navbar.append(A('Main Menu', href=href)) + if art > 0 and not bottom: + href = '%s../article_%d/index.html'%(prefix, art-1) + navbar.iterchildren(reversed=True).next().tail = ' | ' + navbar.append(A('Previous', href=href)) + navbar.iterchildren(reversed=True).next().tail = ' | ' + if not bottom: + navbar.append(HR()) + + self.root = HTML(head, BODY(navbar)) + + class IndexTemplate(Template): - def __init__(self): - Template.__init__(self, u'''\ - - - - - ${title} - - - - -
-

${title}

-

${date}

- -
- - -''') - - def generate(self, title, datefmt, feeds, extra_css=None): + def _generate(self, title, datefmt, feeds, extra_css=None, style=None): if isinstance(datefmt, unicode): datefmt = datefmt.encode(preferred_encoding) date = strftime(datefmt) - return Template.generate(self, title=title, date=date, feeds=feeds, - extra_css=extra_css) - + head = HEAD(TITLE(title)) + if style: + head.append(STYLE(style, type='text/css')) + if extra_css: + head.append(STYLE(extra_css, type='text/css')) + ul = UL(CLASS('calibre_feed_list')) + for i, feed in enumerate(feeds): + if feed: + li = LI(A(feed.title, CLASS('feed', 'calibre_rescale_120', + href='feed_%d/index.html'%i)), id='feed_%d'%i) + ul.append(li) + div = DIV( + H1(title, CLASS('calibre_recipe_title', 'calibre_rescale_180')), + PT(date, style='text-align:right'), + ul, + CLASS('calibre_rescale_100')) + self.root = HTML(head, BODY(div)) class FeedTemplate(Template): - def __init__(self): - Template.__init__(self, u'''\ - - - - - ${feed.title} - - - - -
-

${feed.title}

- -
- ${feed.image_alt} -
-
-
- ${feed.description}
-
-
    - -
  • - ${article.title} - -
    - ${Markup(cutoff(article.text_summary))} -
    -
  • -
    -
-
- | Up one level | -
-
- - -''') + self.root = HTML(head, body) - def generate(self, feed, cutoff, extra_css=None): - return Template.generate(self, feed=feed, cutoff=cutoff, - extra_css=extra_css) class EmbeddedContent(Template): - def __init__(self): - Template.__init__(self, u'''\ - - len(summary) else summary + head = HEAD(TITLE(article.title)) + if style: + head.append(STYLE(style, type='text/css')) + if extra_css: + head.append(STYLE(extra_css, type='text/css')) -> - - ${article.title} - + if isbytestring(text): + text = text.decode('utf-8', 'replace') + elements = html.fragments_fromstring(text) + self.root = HTML(head, + BODY(H2(article.title), DIV())) + div = self.root.find('body').find('div') + if elements and isinstance(elements[0], unicode): + div.text = elements[0] + elements = list(elements)[1:] + for elem in elements: + elem.getparent().remove(elem) + div.append(elem) - -

${article.title}

-
- ${Markup(article.content if len(article.content if article.content else '') > len(article.summary if article.summary else '') else article.summary)} -
- - -''') - - def generate(self, article): - return Template.generate(self, article=article)