author | Sylvain Thénault <sylvain.thenault@logilab.fr> |
Tue, 13 Sep 2011 15:41:11 +0200 | |
changeset 7784 | 7f5c455ec7d1 |
parent 6864 | ea95004494a2 |
child 8190 | 2a3c1b787688 |
permissions | -rw-r--r-- |
5421
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
1 |
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
2 |
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
3 |
# |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
4 |
# This file is part of CubicWeb. |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
5 |
# |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
6 |
# CubicWeb is free software: you can redistribute it and/or modify it under the |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
7 |
# terms of the GNU Lesser General Public License as published by the Free |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
8 |
# Software Foundation, either version 2.1 of the License, or (at your option) |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
9 |
# any later version. |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
10 |
# |
5424
8ecbcbff9777
replace logilab-common by CubicWeb in disclaimer
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5421
diff
changeset
|
11 |
# CubicWeb is distributed in the hope that it will be useful, but WITHOUT |
5421
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
12 |
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
13 |
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
14 |
# details. |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
15 |
# |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
16 |
# You should have received a copy of the GNU Lesser General Public License along |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
17 |
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
0 | 18 |
"""Objects interacting together to provides the external page embeding |
19 |
functionality. |
|
5886 | 20 |
""" |
0 | 21 |
|
22 |
__docformat__ = "restructuredtext en" |
|
6582
8eb7883b4223
[pylint] fix a bug of pylint detected errors and i18n pb (calling builtins._ instead of req._)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5895
diff
changeset
|
23 |
_ = unicode |
0 | 24 |
|
25 |
import re |
|
26 |
from urlparse import urljoin |
|
27 |
from urllib2 import urlopen, Request, HTTPError |
|
2808
497424219fb0
fix urlquote imports
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2798
diff
changeset
|
28 |
from urllib import quote as urlquote # XXX should use view.url_quote method |
0 | 29 |
|
30 |
from logilab.mtconverter import guess_encoding |
|
31 |
||
5556
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
32 |
from cubicweb.selectors import (one_line_rset, score_entity, implements, |
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
33 |
adaptable, match_search_state) |
0 | 34 |
from cubicweb.interfaces import IEmbedable |
5556
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
35 |
from cubicweb.view import NOINDEX, NOFOLLOW, EntityAdapter, implements_adapter_compat |
4023
eae23c40627a
drop common subpackage
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3460
diff
changeset
|
36 |
from cubicweb.uilib import soup2xhtml |
0 | 37 |
from cubicweb.web.controller import Controller |
38 |
from cubicweb.web.action import Action |
|
39 |
from cubicweb.web.views import basetemplates |
|
40 |
||
41 |
||
5556
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
42 |
class IEmbedableAdapter(EntityAdapter): |
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
43 |
"""interface for embedable entities""" |
6864
ea95004494a2
[adapters] fix #1424467 caused by precedence of bw compat method while the adapter is concret for a given class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
6582
diff
changeset
|
44 |
__needs_bw_compat__ = True |
5556
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
45 |
__regid__ = 'IEmbedable' |
5895
6a3f776292a5
[selectors] avoid spurious warning when using implements by design
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5890
diff
changeset
|
46 |
__select__ = implements(IEmbedable, warn=False) # XXX for bw compat, should be abstract |
5556
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
47 |
|
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
48 |
@implements_adapter_compat('IEmbedable') |
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
49 |
def embeded_url(self): |
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
50 |
"""embed action interface""" |
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
51 |
raise NotImplementedError |
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
52 |
|
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
53 |
|
0 | 54 |
class ExternalTemplate(basetemplates.TheMainTemplate): |
55 |
"""template embeding an external web pages into CubicWeb web interface |
|
56 |
""" |
|
3377
dd9d292b6a6d
use __regid__ instead of id on appobject classes
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2808
diff
changeset
|
57 |
__regid__ = 'external' |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1132
diff
changeset
|
58 |
|
0 | 59 |
def call(self, body): |
60 |
# XXX fallback to HTML 4 mode when embeding ? |
|
61 |
self.set_request_content_type() |
|
3451
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3377
diff
changeset
|
62 |
self._cw.search_state = ('normal',) |
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3377
diff
changeset
|
63 |
self.template_header(self.content_type, None, self._cw._('external page'), |
0 | 64 |
[NOINDEX, NOFOLLOW]) |
65 |
self.content_header() |
|
66 |
self.w(body) |
|
67 |
self.content_footer() |
|
68 |
self.template_footer() |
|
69 |
||
70 |
||
71 |
class EmbedController(Controller): |
|
3377
dd9d292b6a6d
use __regid__ instead of id on appobject classes
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2808
diff
changeset
|
72 |
__regid__ = 'embed' |
0 | 73 |
template = 'external' |
74 |
||
75 |
def publish(self, rset=None): |
|
3451
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3377
diff
changeset
|
76 |
req = self._cw |
0 | 77 |
if 'custom_css' in req.form: |
78 |
req.add_css(req.form['custom_css']) |
|
79 |
embedded_url = req.form['url'] |
|
4083
3b285889b8e9
3.6 api update
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4023
diff
changeset
|
80 |
allowed = self._cw.vreg.config['embed-allowed'] |
0 | 81 |
_ = req._ |
82 |
if allowed is None or not allowed.match(embedded_url): |
|
83 |
body = '<h2>%s</h2><h3>%s</h3>' % ( |
|
84 |
_('error while embedding page'), |
|
85 |
_('embedding this url is forbidden')) |
|
86 |
else: |
|
3451
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3377
diff
changeset
|
87 |
prefix = req.build_url(self.__regid__, url='') |
0 | 88 |
authorization = req.get_header('Authorization') |
89 |
if authorization: |
|
90 |
headers = {'Authorization' : authorization} |
|
91 |
else: |
|
92 |
headers = {} |
|
93 |
try: |
|
94 |
body = embed_external_page(embedded_url, prefix, |
|
95 |
headers, req.form.get('custom_css')) |
|
3451
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3377
diff
changeset
|
96 |
body = soup2xhtml(body, self._cw.encoding) |
0 | 97 |
except HTTPError, err: |
98 |
body = '<h2>%s</h2><h3>%s</h3>' % ( |
|
99 |
_('error while embedding page'), err) |
|
5715
2c3e83817a8e
[view] add a new entity_call method to entity view protocol, allowing some to work with not yet created entities. Also, start considering 'eid' form parameters where we only consider 'rql', so we can move on bloquing arbitrary rql inputs (more to do on this...)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5556
diff
changeset
|
100 |
rset = self.process_rql() |
3451
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3377
diff
changeset
|
101 |
return self._cw.vreg['views'].main_template(req, self.template, |
5715
2c3e83817a8e
[view] add a new entity_call method to entity view protocol, allowing some to work with not yet created entities. Also, start considering 'eid' form parameters where we only consider 'rql', so we can move on bloquing arbitrary rql inputs (more to do on this...)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5556
diff
changeset
|
102 |
rset=rset, body=body) |
0 | 103 |
|
104 |
||
631
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
105 |
def entity_has_embedable_url(entity): |
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
106 |
"""return 1 if the entity provides an allowed embedable url""" |
5556
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
107 |
url = entity.cw_adapt_to('IEmbedable').embeded_url() |
631
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
108 |
if not url or not url.strip(): |
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
109 |
return 0 |
4083
3b285889b8e9
3.6 api update
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4023
diff
changeset
|
110 |
allowed = entity._cw.vreg.config['embed-allowed'] |
631
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
111 |
if allowed is None or not allowed.match(url): |
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
112 |
return 0 |
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
113 |
return 1 |
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
114 |
|
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
115 |
|
0 | 116 |
class EmbedAction(Action): |
117 |
"""display an 'embed' link on entity implementing `embeded_url` method |
|
118 |
if the returned url match embeding configuration |
|
119 |
""" |
|
3377
dd9d292b6a6d
use __regid__ instead of id on appobject classes
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2808
diff
changeset
|
120 |
__regid__ = 'embed' |
742
99115e029dca
replaced most of __selectors__ assignments with __select__
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
692
diff
changeset
|
121 |
__select__ = (one_line_rset() & match_search_state('normal') |
5556
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
122 |
& adaptable('IEmbedable') |
742
99115e029dca
replaced most of __selectors__ assignments with __select__
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
692
diff
changeset
|
123 |
& score_entity(entity_has_embedable_url)) |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1132
diff
changeset
|
124 |
|
0 | 125 |
title = _('embed') |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1132
diff
changeset
|
126 |
|
0 | 127 |
def url(self, row=0): |
3451
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3377
diff
changeset
|
128 |
entity = self.cw_rset.get_entity(row, 0) |
5556
9ab2b4c74baf
[entity] introduce a new 'adapters' registry
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5424
diff
changeset
|
129 |
url = urljoin(self._cw.base_url(), entity.cw_adapt_to('IEmbedable').embeded_url()) |
3451
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3377
diff
changeset
|
130 |
if self._cw.form.has_key('rql'): |
3460
e4843535db25
[api] some more _cw / __regid__, automatic tests now pass again
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3451
diff
changeset
|
131 |
return self._cw.build_url('embed', url=url, rql=self._cw.form['rql']) |
e4843535db25
[api] some more _cw / __regid__, automatic tests now pass again
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3451
diff
changeset
|
132 |
return self._cw.build_url('embed', url=url) |
0 | 133 |
|
134 |
||
135 |
||
136 |
# functions doing necessary substitutions to embed an external html page ###### |
|
137 |
||
138 |
||
139 |
BODY_RGX = re.compile('<body.*?>(.*?)</body>', re.I | re.S | re.U) |
|
140 |
HREF_RGX = re.compile('<a\s+href="([^"]*)"', re.I | re.S | re.U) |
|
141 |
SRC_RGX = re.compile('<img\s+src="([^"]*)"', re.I | re.S | re.U) |
|
142 |
||
143 |
||
144 |
class replace_href: |
|
145 |
def __init__(self, prefix, custom_css=None): |
|
146 |
self.prefix = prefix |
|
147 |
self.custom_css = custom_css |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1132
diff
changeset
|
148 |
|
0 | 149 |
def __call__(self, match): |
150 |
original_url = match.group(1) |
|
151 |
url = self.prefix + urlquote(original_url, safe='') |
|
152 |
if self.custom_css is not None: |
|
153 |
if '?' in url: |
|
154 |
url = '%s&custom_css=%s' % (url, self.custom_css) |
|
155 |
else: |
|
156 |
url = '%s?custom_css=%s' % (url, self.custom_css) |
|
157 |
return '<a href="%s"' % url |
|
158 |
||
631
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
159 |
|
0 | 160 |
class absolutize_links: |
161 |
def __init__(self, embedded_url, tag, custom_css=None): |
|
162 |
self.embedded_url = embedded_url |
|
163 |
self.tag = tag |
|
164 |
self.custom_css = custom_css |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1132
diff
changeset
|
165 |
|
0 | 166 |
def __call__(self, match): |
167 |
original_url = match.group(1) |
|
168 |
if '://' in original_url: |
|
169 |
return match.group(0) # leave it unchanged |
|
170 |
return '%s="%s"' % (self.tag, urljoin(self.embedded_url, original_url)) |
|
171 |
||
172 |
||
173 |
def prefix_links(body, prefix, embedded_url, custom_css=None): |
|
174 |
filters = ((HREF_RGX, absolutize_links(embedded_url, '<a href', custom_css)), |
|
175 |
(SRC_RGX, absolutize_links(embedded_url, '<img src')), |
|
176 |
(HREF_RGX, replace_href(prefix, custom_css))) |
|
177 |
for rgx, repl in filters: |
|
178 |
body = rgx.sub(repl, body) |
|
179 |
return body |
|
631
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
180 |
|
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
431
diff
changeset
|
181 |
|
0 | 182 |
def embed_external_page(url, prefix, headers=None, custom_css=None): |
183 |
req = Request(url, headers=(headers or {})) |
|
184 |
content = urlopen(req).read() |
|
185 |
page_source = unicode(content, guess_encoding(content), 'replace') |
|
1132 | 186 |
page_source = page_source |
0 | 187 |
match = BODY_RGX.search(page_source) |
188 |
if match is None: |
|
189 |
return page_source |
|
190 |
return prefix_links(match.group(1), prefix, url, custom_css) |