[web/views] extract cube embed (closes #1916015)
authorVladimir Popescu <vladimir.popescu@logilab.fr>
Wed, 27 Mar 2013 16:33:48 +0100
changeset 8803 47dd517d6c6f
parent 8802 d92919c995cc
child 8804 21770f74a078
[web/views] extract cube embed (closes #1916015)
doc/3.17.rst
misc/migration/bootstrapmigration_repository.py
web/test/unittest_views_basecontrollers.py
web/test/unittest_views_embeding.py
web/views/embedding.py
--- a/doc/3.17.rst	Tue Mar 26 12:26:12 2013 +0100
+++ b/doc/3.17.rst	Wed Mar 27 16:33:48 2013 +0100
@@ -7,6 +7,9 @@
 * The SIOC views and adapters have been removed from CubicWeb and moved to the
   `sioc` cube.
 
+* The web page embedding views and adapters have been removed from CubicWeb and
+  moved to the `embed` cube.
+
 
 Deprecated Code Drops
 ----------------------
--- a/misc/migration/bootstrapmigration_repository.py	Tue Mar 26 12:26:12 2013 +0100
+++ b/misc/migration/bootstrapmigration_repository.py	Wed Mar 27 16:33:48 2013 +0100
@@ -41,6 +41,12 @@
         if not confirm('In cubicweb 3.17 sioc views have been moved to the sioc '
                        'cube, which is not installed.  Continue anyway?'):
             raise
+    try:
+        add_cube('embed', update_database=False)
+    except ImportError:
+        if not confirm('In cubicweb 3.17 embedding views have been moved to the embed '
+                       'cube, which is not installed.  Continue anyway?'):
+            raise
 
 if applcubicwebversion <= (3, 13, 0) and cubicwebversion >= (3, 13, 1):
     sql('ALTER TABLE entities ADD asource VARCHAR(64)')
--- a/web/test/unittest_views_basecontrollers.py	Tue Mar 26 12:26:12 2013 +0100
+++ b/web/test/unittest_views_basecontrollers.py	Wed Mar 27 16:33:48 2013 +0100
@@ -533,18 +533,6 @@
             p.__class__.skip_copy_for = old_skips
 
 
-class EmbedControllerTC(CubicWebTC):
-
-    def test_nonregr_embed_publish(self):
-        # This test looks a bit stupid but at least it will probably
-        # fail if the controller API changes and if EmbedController is not
-        # updated (which is what happened before this test)
-        req = self.request()
-        req.form['url'] = 'http://www.logilab.fr/'
-        controller = self.vreg['controllers'].select('embed', req)
-        result = controller.publish(rset=None)
-
-
 class ReportBugControllerTC(CubicWebTC):
 
     def test_usable_by_guest(self):
--- a/web/test/unittest_views_embeding.py	Tue Mar 26 12:26:12 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""
-
-"""
-
-from logilab.common.testlib import TestCase, unittest_main
-
-from cubicweb.web.views.embedding import prefix_links
-
-class UILIBTC(TestCase):
-
-
-    def test_prefix_links(self):
-        """suppose we are embedding http://embedded.com/page1.html"""
-        orig = ['<a href="http://www.perdu.com">perdu ?</a>',
-        '<a href="http://embedded.com/page1.html">perdu ?</a>',
-        '<a href="/page2.html">perdu ?</a>',
-        '<a href="page3.html">perdu ?</a>',
-        '<img src="http://www.perdu.com/img.png"/>',
-        '<img src="/img.png"/>',
-        '<img src="img.png"/>',
-        ]
-        expected = ['<a href="PREFIXhttp%3A%2F%2Fwww.perdu.com">perdu ?</a>',
-        '<a href="PREFIXhttp%3A%2F%2Fembedded.com%2Fpage1.html">perdu ?</a>',
-        '<a href="PREFIXhttp%3A%2F%2Fembedded.com%2Fpage2.html">perdu ?</a>',
-        '<a href="PREFIXhttp%3A%2F%2Fembedded.com%2Fpage3.html">perdu ?</a>',
-        '<img src="http://www.perdu.com/img.png"/>',
-        '<img src="http://embedded.com/img.png"/>',
-        '<img src="http://embedded.com/img.png"/>',
-        ]
-        for orig_a, expected_a in zip(orig, expected):
-            got = prefix_links(orig_a, 'PREFIX', 'http://embedded.com/page1.html')
-            self.assertEqual(got, expected_a)
-
-if __name__ == '__main__':
-    unittest_main()
-
--- a/web/views/embedding.py	Tue Mar 26 12:26:12 2013 +0100
+++ b/web/views/embedding.py	Wed Mar 27 16:33:48 2013 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -19,172 +19,20 @@
 functionality.
 """
 
-__docformat__ = "restructuredtext en"
-_ = unicode
-
-import re
-from urlparse import urljoin
-from urllib2 import urlopen, Request, HTTPError
-from urllib import quote as urlquote # XXX should use view.url_quote method
-
-from logilab.mtconverter import guess_encoding
-
-from cubicweb.predicates import (one_line_rset, score_entity, implements,
-                                adaptable, match_search_state)
-from cubicweb.interfaces import IEmbedable
-from cubicweb.view import NOINDEX, NOFOLLOW, EntityAdapter, implements_adapter_compat
-from cubicweb.uilib import soup2xhtml
-from cubicweb.web.controller import Controller
-from cubicweb.web.action import Action
-from cubicweb.web.views import basetemplates
-
-
-class IEmbedableAdapter(EntityAdapter):
-    """interface for embedable entities"""
-    __needs_bw_compat__ = True
-    __regid__ = 'IEmbedable'
-    __select__ = implements(IEmbedable, warn=False) # XXX for bw compat, should be abstract
-
-    @implements_adapter_compat('IEmbedable')
-    def embeded_url(self):
-        """embed action interface"""
-        raise NotImplementedError
-
-
-class ExternalTemplate(basetemplates.TheMainTemplate):
-    """template embeding an external web pages into CubicWeb web interface
-    """
-    __regid__ = 'external'
+from logilab.common.deprecation import class_moved, moved
 
-    def call(self, body):
-        # XXX fallback to HTML 4 mode when embeding ?
-        self.set_request_content_type()
-        self._cw.search_state = ('normal',)
-        self.template_header(self.content_type, None, self._cw._('external page'),
-                             [NOINDEX, NOFOLLOW])
-        self.content_header()
-        self.w(body)
-        self.content_footer()
-        self.template_footer()
-
-
-class EmbedController(Controller):
-    __regid__ = 'embed'
-    template = 'external'
-
-    def publish(self, rset=None):
-        req = self._cw
-        if 'custom_css' in req.form:
-            req.add_css(req.form['custom_css'])
-        embedded_url = req.form['url']
-        allowed = self._cw.vreg.config['embed-allowed']
-        _ = req._
-        if allowed is None or not allowed.match(embedded_url):
-            body = '<h2>%s</h2><h3>%s</h3>' % (
-                _('error while embedding page'),
-                _('embedding this url is forbidden'))
-        else:
-            prefix = req.build_url(self.__regid__, url='')
-            authorization = req.get_header('Authorization')
-            if authorization:
-                headers = {'Authorization' : authorization}
-            else:
-                headers = {}
-            try:
-                body = embed_external_page(embedded_url, prefix,
-                                           headers, req.form.get('custom_css'))
-                body = soup2xhtml(body, self._cw.encoding)
-            except HTTPError as err:
-                body = '<h2>%s</h2><h3>%s</h3>' % (
-                    _('error while embedding page'), err)
-        rset = self.process_rql()
-        return self._cw.vreg['views'].main_template(req, self.template,
-                                                    rset=rset, body=body)
-
+try:
+    from cubes.embed.views import *
 
-def entity_has_embedable_url(entity):
-    """return 1 if the entity provides an allowed embedable url"""
-    url = entity.cw_adapt_to('IEmbedable').embeded_url()
-    if not url or not url.strip():
-        return 0
-    allowed = entity._cw.vreg.config['embed-allowed']
-    if allowed is None or not allowed.match(url):
-        return 0
-    return 1
-
-
-class EmbedAction(Action):
-    """display an 'embed' link on entity implementing `embeded_url` method
-    if the returned url match embeding configuration
-    """
-    __regid__ = 'embed'
-    __select__ = (one_line_rset() & match_search_state('normal')
-                  & adaptable('IEmbedable')
-                  & score_entity(entity_has_embedable_url))
-
-    title = _('embed')
-
-    def url(self, row=0):
-        entity = self.cw_rset.get_entity(row, 0)
-        url = urljoin(self._cw.base_url(), entity.cw_adapt_to('IEmbedable').embeded_url())
-        if 'rql' in self._cw.form:
-            return self._cw.build_url('embed', url=url, rql=self._cw.form['rql'])
-        return self._cw.build_url('embed', url=url)
-
-
-
-# functions doing necessary substitutions to embed an external html page ######
-
-
-BODY_RGX = re.compile('<body.*?>(.*?)</body>', re.I | re.S | re.U)
-HREF_RGX = re.compile('<a\s+href="([^"]*)"', re.I | re.S | re.U)
-SRC_RGX = re.compile('<img\s+src="([^"]*)"', re.I | re.S | re.U)
-
-
-class replace_href:
-    def __init__(self, prefix, custom_css=None):
-        self.prefix = prefix
-        self.custom_css = custom_css
-
-    def __call__(self, match):
-        original_url = match.group(1)
-        url = self.prefix + urlquote(original_url, safe='')
-        if self.custom_css is not None:
-            if '?' in url:
-                url = '%s&amp;custom_css=%s' % (url, self.custom_css)
-            else:
-                url = '%s?custom_css=%s' % (url, self.custom_css)
-        return '<a href="%s"' % url
-
-
-class absolutize_links:
-    def __init__(self, embedded_url, tag, custom_css=None):
-        self.embedded_url = embedded_url
-        self.tag = tag
-        self.custom_css = custom_css
-
-    def __call__(self, match):
-        original_url = match.group(1)
-        if '://' in original_url:
-            return match.group(0) # leave it unchanged
-        return '%s="%s"' % (self.tag, urljoin(self.embedded_url, original_url))
-
-
-def prefix_links(body, prefix, embedded_url, custom_css=None):
-    filters = ((HREF_RGX, absolutize_links(embedded_url, '<a href', custom_css)),
-               (SRC_RGX, absolutize_links(embedded_url, '<img src')),
-               (HREF_RGX, replace_href(prefix, custom_css)))
-    for rgx, repl in filters:
-        body = rgx.sub(repl, body)
-    return body
-
-
-def embed_external_page(url, prefix, headers=None, custom_css=None):
-    req = Request(url, headers=(headers or {}))
-    content = urlopen(req).read()
-    page_source = unicode(content, guess_encoding(content), 'replace')
-    page_source = page_source
-    match = BODY_RGX.search(page_source)
-    if match is None:
-        return page_source
-    return prefix_links(match.group(1), prefix, url, custom_css)
+    IEmbedableAdapter = class_moved(IEmbedableAdapter, message='[3.17] IEmbedableAdapter moved to cubes.embed.views')
+    ExternalTemplate = class_moved(ExternalTemplate, message='[3.17] IEmbedableAdapter moved to cubes.embed.views')
+    EmbedController = class_moved(EmbedController, message='[3.17] IEmbedableAdapter moved to cubes.embed.views')
+    entity_has_embedable_url = moved('cubes.embed.views', 'entity_has_embedable_url')
+    EmbedAction = class_moved(EmbedAction, message='[3.17] EmbedAction moved to cubes.embed.views')
+    replace_href = class_moved(replace_href, message='[3.17] replace_href moved to cubes.embed.views')
+    embed_external_page = moved('cubes.embed.views', 'embed_external_page')
+    absolutize_links = class_moved(absolutize_links, message='[3.17] absolutize_links moved to cubes.embed.views')
+    prefix_links = moved('cubes.embed.views', 'prefix_links')
+except ImportError:
+    from cubicweb.web import LOGGER
+    LOGGER.warning('[3.17] embedding extracted to cube embed that was not found. try installing it.')