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&custom_css=%s' % (url, self.custom_css) |
128 url = '%s&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) |