<s> from __future__ import unicode_literals <EOL> import logging <EOL> import posixpath <EOL> import re <EOL> import warnings <EOL> import functools <EOL> from cssutils import CSSParser <EOL> from lxml import etree <EOL> from premailer import Premailer <EOL> from premailer . premailer import ExternalNotFoundError <EOL> from . compat import urlparse , to_unicode , is_callable <EOL> from . store import MemoryFileStore , LazyHTTPFile <EOL> from . template . base import BaseTemplate <EOL> from . loader . local_store import FileNotFound <EOL> class LocalPremailer ( Premailer ) : <EOL> def __init__ ( self , html , local_loader = None , attribute_name = None , ** kw ) : <EOL> if '<STR_LIT>' not in kw : <EOL> kw [ '<STR_LIT>' ] = True <EOL> self . local_loader = local_loader <EOL> if attribute_name : <EOL> self . attribute_name = attribute_name <EOL> super ( LocalPremailer , self ) . __init__ ( html = html , ** kw ) <EOL> def _load_external ( self , url ) : <EOL> """<STR_LIT>""" <EOL> if url . startswith ( '<STR_LIT>' ) : <EOL> if self . base_url and '<STR_LIT>' in self . base_url : <EOL> url = '<STR_LIT>' + url <EOL> else : <EOL> url = '<STR_LIT>' + url <EOL> if url . startswith ( '<STR_LIT>' ) or url . startswith ( '<STR_LIT>' ) : <EOL> content = self . _load_external_url ( url ) <EOL> else : <EOL> content = None <EOL> if self . local_loader : <EOL> try : <EOL> content = self . local_loader [ url ] <EOL> except FileNotFound : <EOL> content = None <EOL> if content is None : <EOL> if self . base_url : <EOL> return self . _load_external ( urlparse . urljoin ( self . base_url , url ) ) <EOL> else : <EOL> raise ExternalNotFoundError ( url ) <EOL> return content <EOL> class HTMLParser ( object ) : <EOL> _cdata_regex = re . compile ( r'<STR_LIT>' , re . DOTALL ) <EOL> _xml_title_regex = re . compile ( r'<STR_LIT>' , re . IGNORECASE ) <EOL> def __init__ ( self , html , method = "<STR_LIT:html>" , output_method = "<STR_LIT>" ) : <EOL> if output_method == '<STR_LIT>' : <EOL> self . _html = html . replace ( '<STR_LIT:\r\n>' , '<STR_LIT:\n>' ) <EOL> else : <EOL> self . _html = html <EOL> self . _method = method <EOL> self . _output_method = output_method <EOL> self . _tree = None <EOL> @ property <EOL> def html ( self ) : <EOL> return self . _html <EOL> @ property <EOL> def tree ( self ) : <EOL> if self . _tree is None : <EOL> parser = self . _method == '<STR_LIT>' and etree . XMLParser ( ns_clean = False , resolve_entities = False ) or etree . HTMLParser ( ) <EOL> self . _tree = etree . fromstring ( self . _html . strip ( ) , parser ) <EOL> return self . _tree <EOL> def to_string ( self , encoding = '<STR_LIT:utf-8>' , ** kwargs ) : <EOL> if self . tree is None : <EOL> return "<STR_LIT>" <EOL> method = self . _output_method <EOL> out = etree . tostring ( self . tree , encoding = encoding , method = method , ** kwargs ) . decode ( encoding ) <EOL> if method == '<STR_LIT>' : <EOL> out = self . _cdata_regex . sub ( <EOL> lambda m : '<STR_LIT>' % m . group ( <NUM_LIT:1> ) , <EOL> out <EOL> ) <EOL> out = self . _xml_title_regex . sub ( '<STR_LIT>' , out ) <EOL> return out <EOL> def apply_to_images ( self , func , images = True , backgrounds = True , styles_uri = True ) : <EOL> def _apply_to_style_uri ( style_text , func ) : <EOL> dirty = False <EOL> parser = CSSParser ( ) . parseStyle ( style_text ) <EOL> for prop in parser . getProperties ( all = True ) : <EOL> for value in prop . propertyValue : <EOL> if value . type == '<STR_LIT>' : <EOL> old_uri = value . uri <EOL> new_uri = func ( old_uri , element = value ) <EOL> if new_uri != old_uri : <EOL> dirty = True <EOL> value . uri = new_uri <EOL> if dirty : <EOL> return to_unicode ( parser . cssText , '<STR_LIT:utf-8>' ) <EOL> else : <EOL> return style_text <EOL> if images : <EOL> for img in self . tree . xpath ( "<STR_LIT>" ) : <EOL> if '<STR_LIT:src>' in img . attrib : <EOL> img . attrib [ '<STR_LIT:src>' ] = func ( img . attrib [ '<STR_LIT:src>' ] , element = img ) <EOL> if backgrounds : <EOL> for item in self . tree . xpath ( "<STR_LIT>" ) : <EOL> tag = item . getparent ( ) <EOL> tag . attrib [ '<STR_LIT>' ] = func ( tag . attrib [ '<STR_LIT>' ] , element = tag ) <EOL> if styles_uri : <EOL> for item in self . tree . xpath ( "<STR_LIT>" ) : <EOL> tag = item . getparent ( ) <EOL> tag . attrib [ '<STR_LIT>' ] = _apply_to_style_uri ( tag . attrib [ '<STR_LIT>' ] , func = func ) <EOL> def apply_to_links ( self , func ) : <EOL> for a in self . tree . xpath ( "<STR_LIT>" ) : <EOL> if '<STR_LIT>' in a . attrib : <EOL> a . attrib [ '<STR_LIT>' ] = func ( a . attrib [ '<STR_LIT>' ] , element = a ) <EOL> def add_content_type_meta ( self , content_type = "<STR_LIT>" , charset = "<STR_LIT:utf-8>" , element_cls = etree . Element ) : <EOL> def _get_content_type_meta ( head ) : <EOL> content_type_meta = None <EOL> for meta in head . find ( '<STR_LIT>' ) or [ ] : <EOL> http_equiv = meta . get ( '<STR_LIT>' , None ) <EOL> if http_equiv and ( http_equiv . lower ( ) == '<STR_LIT>' ) : <EOL> content_type_meta = meta <EOL> break <EOL> if content_type_meta is None : <EOL> content_type_meta = element_cls ( '<STR_LIT>' ) <EOL> head . append ( content_type_meta ) <EOL> return content_type_meta <EOL> head = self . tree . find ( '<STR_LIT>' ) <EOL> if head is None : <EOL> logging . warning ( '<STR_LIT>' ) <EOL> return <EOL> meta = _get_content_type_meta ( head ) <EOL> meta . set ( '<STR_LIT:content>' , '<STR_LIT>' % ( content_type , charset ) ) <EOL> meta . set ( '<STR_LIT>' , "<STR_LIT:Content-Type>" ) <EOL> def save ( self , ** kwargs ) : <EOL> self . _html = self . to_string ( ** kwargs ) <EOL> class BaseTransformer ( HTMLParser ) : <EOL> UNSAFE_TAGS = [ '<STR_LIT>' , '<STR_LIT:object>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] <EOL> attachment_store_cls = MemoryFileStore <EOL> attachment_file_cls = LazyHTTPFile <EOL> html_attribute_name = '<STR_LIT>' <EOL> def __init__ ( self , html , local_loader = None , <EOL> attachment_store = None , <EOL> requests_params = None , method = "<STR_LIT:html>" , base_url = None ) : <EOL> HTMLParser . __init__ ( self , html = html , method = method ) <EOL> self . attachment_store = attachment_store if attachment_store is not None else self . attachment_store_cls ( ) <EOL> self . local_loader = local_loader <EOL> if base_url and not base_url . endswith ( '<STR_LIT:/>' ) : <EOL> base_url = base_url + '<STR_LIT:/>' <EOL> self . base_url = base_url <EOL> self . requests_params = requests_params <EOL> self . _premailer = None <EOL> def get_absolute_url ( self , url ) : <EOL> if not self . base_url : <EOL> return url <EOL> if url . startswith ( '<STR_LIT>' ) : <EOL> if '<STR_LIT>' in self . base_url : <EOL> url = '<STR_LIT>' + url <EOL> else : <EOL> url = '<STR_LIT>' + url <EOL> return url <EOL> if not ( url . startswith ( '<STR_LIT>' ) or url . startswith ( '<STR_LIT>' ) ) : <EOL> url = urlparse . urljoin ( self . base_url , posixpath . normpath ( url ) ) <EOL> return url <EOL> def attribute_value ( self , el ) : <EOL> return el is not None and hasattr ( el , '<STR_LIT>' ) and el . attrib . get ( self . html_attribute_name ) or None <EOL> _attribute_value = attribute_value <EOL> def _default_attachment_check ( self , el , hints ) : <EOL> if hints [ '<STR_LIT>' ] == '<STR_LIT:ignore>' : <EOL> return False <EOL> else : <EOL> return True <EOL> def _load_attachment_func ( self , uri , element = None , callback = None , ** kw ) : <EOL> if callback is None : <EOL> callback = lambda _ , hints : hints [ '<STR_LIT>' ] != '<STR_LIT:ignore>' <EOL> attribute_value = self . attribute_value ( element ) or '<STR_LIT>' <EOL> if not callback ( element , hints = { '<STR_LIT>' : attribute_value } ) : <EOL> return uri <EOL> attachment = self . attachment_store . by_uri ( uri ) <EOL> if attachment is None : <EOL> attachment = self . attachment_file_cls ( <EOL> uri = uri , <EOL> absolute_url = self . get_absolute_url ( uri ) , <EOL> local_loader = self . local_loader , <EOL> content_disposition = '<STR_LIT>' if '<STR_LIT>' in attribute_value else None , <EOL> requests_args = self . requests_params ) <EOL> self . attachment_store . add ( attachment ) <EOL> return attachment . filename <EOL> def get_premailer ( self , ** kw ) : <EOL> kw . setdefault ( '<STR_LIT>' , self . html_attribute_name ) <EOL> kw . setdefault ( '<STR_LIT>' , self . _method ) <EOL> kw . setdefault ( '<STR_LIT>' , self . base_url ) <EOL> kw . setdefault ( '<STR_LIT>' , self . local_loader ) <EOL> return LocalPremailer ( html = self . tree , ** kw ) <EOL> @ property <EOL> def premailer ( self ) : <EOL> if self . _premailer is None : <EOL> self . _premailer = self . get_premailer ( ) <EOL> return self . _premailer <EOL> def remove_unsafe_tags ( self ) : <EOL> for tag in self . UNSAFE_TAGS : <EOL> for el in self . tree . xpath ( "<STR_LIT>" % tag ) : <EOL> parent = el . getparent ( ) <EOL> if parent is not None : <EOL> parent . remove ( el ) <EOL> return self <EOL> def load_and_transform ( self , <EOL> css_inline = True , <EOL> remove_unsafe_tags = True , <EOL> make_links_absolute = True , <EOL> set_content_type_meta = True , <EOL> update_stylesheet = True , <EOL> load_images = True , <EOL> images_inline = False , <EOL> ** kw ) : <EOL> if not make_links_absolute : <EOL> warnings . warn ( "<STR_LIT>" , DeprecationWarning ) <EOL> if update_stylesheet : <EOL> warnings . warn ( "<STR_LIT>" , DeprecationWarning ) <EOL> if css_inline : <EOL> self . get_premailer ( ** kw ) . transform ( ) <EOL> if load_images : <EOL> if is_callable ( load_images ) : <EOL> func = functools . partial ( self . _load_attachment_func , callback = load_images ) <EOL> else : <EOL> func = self . _load_attachment_func <EOL> self . apply_to_images ( func ) <EOL> if remove_unsafe_tags : <EOL> self . remove_unsafe_tags ( ) <EOL> if set_content_type_meta : <EOL> self . add_content_type_meta ( ) <EOL> if load_images and images_inline : <EOL> self . make_all_images_inline ( ) <EOL> return self <EOL> def make_all_images_inline ( self ) : <EOL> for a in self . attachment_store : <EOL> a . is_inline = True <EOL> self . synchronize_inline_images ( ) <EOL> return self <EOL> def synchronize_inline_images ( self , inline_names = None , non_inline_names = None ) : <EOL> """<STR_LIT>""" <EOL> if inline_names is None or non_inline_names is None : <EOL> inline_names = { } <EOL> non_inline_names = { } <EOL> for a in self . attachment_store : <EOL> if a . is_inline : <EOL> inline_names [ a . filename ] = a . content_id <EOL> else : <EOL> non_inline_names [ a . content_id ] = a . filename <EOL> def _src_update_func ( src , ** kw ) : <EOL> if src . startswith ( '<STR_LIT>' ) : <EOL> content_id = src [ <NUM_LIT:4> : ] <EOL> if content_id in non_inline_names : <EOL> return non_inline_names [ content_id ] <EOL> else : <EOL> if src in inline_names : <EOL> return '<STR_LIT>' + inline_names [ src ] <EOL> return src <EOL> self . apply_to_images ( _src_update_func ) <EOL> return self <EOL> class Transformer ( BaseTransformer ) : <EOL> pass <EOL> class MessageTransformer ( BaseTransformer ) : <EOL> def __init__ ( self , message , ** kw ) : <EOL> self . message = message <EOL> html = message . _html <EOL> if isinstance ( html , BaseTemplate ) : <EOL> html = html . template_text <EOL> params = { '<STR_LIT:html>' : html , '<STR_LIT>' : message . attachments } <EOL> params . update ( kw ) <EOL> BaseTransformer . __init__ ( self , ** params ) <EOL> def save ( self ) : <EOL> m = self . message <EOL> if isinstance ( m . _html , BaseTemplate ) : <EOL> m . _html . set_template_text ( self . to_string ( ) ) <EOL> else : <EOL> m . _html = self . to_string ( ) </s>
