web/views/embedding.py
changeset 1808 aa09e20dd8c0
parent 1802 d628defebc17
child 1977 606923dff11b
equal deleted inserted replaced
1693:49075f57cf2c 1808:aa09e20dd8c0
     1 """Objects interacting together to provides the external page embeding
     1 """Objects interacting together to provides the external page embeding
     2 functionality.
     2 functionality.
     3 
     3 
     4 
     4 
     5 :organization: Logilab
     5 :organization: Logilab
     6 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     6 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     7 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     7 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     8 """
     8 """
     9 __docformat__ = "restructuredtext en"
     9 __docformat__ = "restructuredtext en"
    10 
    10 
    11 import re
    11 import re
    13 from urllib2 import urlopen, Request, HTTPError
    13 from urllib2 import urlopen, Request, HTTPError
    14 
    14 
    15 from logilab.mtconverter import guess_encoding
    15 from logilab.mtconverter import guess_encoding
    16 
    16 
    17 from cubicweb import urlquote # XXX should use view.url_quote method
    17 from cubicweb import urlquote # XXX should use view.url_quote method
       
    18 from cubicweb.selectors import (one_line_rset, score_entity,
       
    19                                 match_search_state, implements)
    18 from cubicweb.interfaces import IEmbedable
    20 from cubicweb.interfaces import IEmbedable
       
    21 from cubicweb.view import NOINDEX, NOFOLLOW
    19 from cubicweb.common.uilib import soup2xhtml
    22 from cubicweb.common.uilib import soup2xhtml
    20 from cubicweb.common.selectors import (one_line_rset, score_entity_selector,
       
    21                                     match_search_state, implement_interface)
       
    22 from cubicweb.common.view import NOINDEX, NOFOLLOW
       
    23 from cubicweb.web.controller import Controller
    23 from cubicweb.web.controller import Controller
    24 from cubicweb.web.action import Action
    24 from cubicweb.web.action import Action
    25 from cubicweb.web.views import basetemplates
    25 from cubicweb.web.views import basetemplates
    26 
    26 
    27 
    27 
    28 class ExternalTemplate(basetemplates.TheMainTemplate):
    28 class ExternalTemplate(basetemplates.TheMainTemplate):
    29     """template embeding an external web pages into CubicWeb web interface
    29     """template embeding an external web pages into CubicWeb web interface
    30     """
    30     """
    31     id = 'external'
    31     id = 'external'
    32     
    32 
    33     def call(self, body):
    33     def call(self, body):
    34         # XXX fallback to HTML 4 mode when embeding ?
    34         # XXX fallback to HTML 4 mode when embeding ?
    35         self.set_request_content_type()
    35         self.set_request_content_type()
    36         self.process_rql(self.req.form.get('rql'))
       
    37         self.req.search_state = ('normal',)
    36         self.req.search_state = ('normal',)
    38         self.template_header(self.content_type, None, self.req._('external page'),
    37         self.template_header(self.content_type, None, self.req._('external page'),
    39                              [NOINDEX, NOFOLLOW])
    38                              [NOINDEX, NOFOLLOW])
    40         self.content_header()
    39         self.content_header()
    41         self.w(body)
    40         self.w(body)
    70                                            headers, req.form.get('custom_css'))
    69                                            headers, req.form.get('custom_css'))
    71                 body = soup2xhtml(body, self.req.encoding)
    70                 body = soup2xhtml(body, self.req.encoding)
    72             except HTTPError, err:
    71             except HTTPError, err:
    73                 body = '<h2>%s</h2><h3>%s</h3>' % (
    72                 body = '<h2>%s</h2><h3>%s</h3>' % (
    74                     _('error while embedding page'), err)
    73                     _('error while embedding page'), err)
    75         return self.vreg.main_template(req, self.template, body=body)
    74         self.process_rql(req.form.get('rql'))
       
    75         return self.vreg.main_template(req, self.template, rset=self.rset, body=body)
       
    76 
       
    77 
       
    78 def entity_has_embedable_url(entity):
       
    79     """return 1 if the entity provides an allowed embedable url"""
       
    80     url = entity.embeded_url()
       
    81     if not url or not url.strip():
       
    82         return 0
       
    83     allowed = entity.config['embed-allowed']
       
    84     if allowed is None or not allowed.match(url):
       
    85         return 0
       
    86     return 1
    76 
    87 
    77 
    88 
    78 class EmbedAction(Action):
    89 class EmbedAction(Action):
    79     """display an 'embed' link on entity implementing `embeded_url` method
    90     """display an 'embed' link on entity implementing `embeded_url` method
    80     if the returned url match embeding configuration
    91     if the returned url match embeding configuration
    81     """
    92     """
    82     id = 'embed'
    93     id = 'embed'
       
    94     __select__ = (one_line_rset() & match_search_state('normal')
       
    95                   & implements(IEmbedable)
       
    96                   & score_entity(entity_has_embedable_url))
       
    97 
       
    98     title = _('embed')
    83     controller = 'embed'
    99     controller = 'embed'
    84     __selectors__ = (one_line_rset, match_search_state,
   100 
    85                      implement_interface, score_entity_selector)
       
    86     accepts_interfaces = (IEmbedable,)
       
    87     
       
    88     title = _('embed')
       
    89         
       
    90     @classmethod
       
    91     def score_entity(cls, entity):
       
    92         """return a score telling how well I can display the given 
       
    93         entity instance (required by the value_selector)
       
    94         """
       
    95         url = entity.embeded_url()
       
    96         if not url or not url.strip():
       
    97             return 0
       
    98         allowed = cls.config['embed-allowed']
       
    99         if allowed is None or not allowed.match(url):
       
   100             return 0
       
   101         return 1
       
   102     
       
   103     def url(self, row=0):
   101     def url(self, row=0):
   104         entity = self.rset.get_entity(row, 0)
   102         entity = self.rset.get_entity(row, 0)
   105         url = urljoin(self.req.base_url(), entity.embeded_url())
   103         url = urljoin(self.req.base_url(), entity.embeded_url())
   106         if self.req.form.has_key('rql'):
   104         if self.req.form.has_key('rql'):
   107             return self.build_url(url=url, rql=self.req.form['rql'])
   105             return self.build_url(url=url, rql=self.req.form['rql'])
   119 
   117 
   120 class replace_href:
   118 class replace_href:
   121     def __init__(self, prefix, custom_css=None):
   119     def __init__(self, prefix, custom_css=None):
   122         self.prefix = prefix
   120         self.prefix = prefix
   123         self.custom_css = custom_css
   121         self.custom_css = custom_css
   124         
   122 
   125     def __call__(self, match):
   123     def __call__(self, match):
   126         original_url = match.group(1)
   124         original_url = match.group(1)
   127         url = self.prefix + urlquote(original_url, safe='')
   125         url = self.prefix + urlquote(original_url, safe='')
   128         if self.custom_css is not None:
   126         if self.custom_css is not None:
   129             if '?' in url:
   127             if '?' in url:
   130                 url = '%s&amp;custom_css=%s' % (url, self.custom_css)
   128                 url = '%s&amp;custom_css=%s' % (url, self.custom_css)
   131             else:
   129             else:
   132                 url = '%s?custom_css=%s' % (url, self.custom_css)
   130                 url = '%s?custom_css=%s' % (url, self.custom_css)
   133         return '<a href="%s"' % url
   131         return '<a href="%s"' % url
   134 
   132 
       
   133 
   135 class absolutize_links:
   134 class absolutize_links:
   136     def __init__(self, embedded_url, tag, custom_css=None):
   135     def __init__(self, embedded_url, tag, custom_css=None):
   137         self.embedded_url = embedded_url
   136         self.embedded_url = embedded_url
   138         self.tag = tag
   137         self.tag = tag
   139         self.custom_css = custom_css
   138         self.custom_css = custom_css
   140     
   139 
   141     def __call__(self, match):
   140     def __call__(self, match):
   142         original_url = match.group(1)
   141         original_url = match.group(1)
   143         if '://' in original_url:
   142         if '://' in original_url:
   144             return match.group(0) # leave it unchanged
   143             return match.group(0) # leave it unchanged
   145         return '%s="%s"' % (self.tag, urljoin(self.embedded_url, original_url))
   144         return '%s="%s"' % (self.tag, urljoin(self.embedded_url, original_url))
   150                (SRC_RGX, absolutize_links(embedded_url, '<img src')),
   149                (SRC_RGX, absolutize_links(embedded_url, '<img src')),
   151                (HREF_RGX, replace_href(prefix, custom_css)))
   150                (HREF_RGX, replace_href(prefix, custom_css)))
   152     for rgx, repl in filters:
   151     for rgx, repl in filters:
   153         body = rgx.sub(repl, body)
   152         body = rgx.sub(repl, body)
   154     return body
   153     return body
   155     
   154 
       
   155 
   156 def embed_external_page(url, prefix, headers=None, custom_css=None):
   156 def embed_external_page(url, prefix, headers=None, custom_css=None):
   157     req = Request(url, headers=(headers or {}))
   157     req = Request(url, headers=(headers or {}))
   158     content = urlopen(req).read()
   158     content = urlopen(req).read()
   159     page_source = unicode(content, guess_encoding(content), 'replace')
   159     page_source = unicode(content, guess_encoding(content), 'replace')
   160     page_source =page_source
   160     page_source = page_source
   161     match = BODY_RGX.search(page_source)
   161     match = BODY_RGX.search(page_source)
   162     if match is None:
   162     if match is None:
   163         return page_source
   163         return page_source
   164     return prefix_links(match.group(1), prefix, url, custom_css)
   164     return prefix_links(match.group(1), prefix, url, custom_css)