|
1 # copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
3 # |
|
4 # This file is part of CubicWeb. |
|
5 # |
|
6 # CubicWeb is free software: you can redistribute it and/or modify it under the |
|
7 # terms of the GNU Lesser General Public License as published by the Free |
|
8 # Software Foundation, either version 2.1 of the License, or (at your option) |
|
9 # any later version. |
|
10 # |
|
11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT |
|
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
|
14 # details. |
|
15 # |
|
16 # You should have received a copy of the GNU Lesser General Public License along |
|
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
|
18 """ |
|
19 Specific views for entities adapting to IDownloadable |
|
20 ===================================================== |
|
21 """ |
|
22 __docformat__ = "restructuredtext en" |
|
23 from cubicweb import _ |
|
24 |
|
25 from six.moves import range |
|
26 |
|
27 from logilab.mtconverter import BINARY_ENCODINGS, TransformError, xml_escape |
|
28 from logilab.common.deprecation import class_renamed, deprecated |
|
29 |
|
30 from cubicweb import tags |
|
31 from cubicweb.view import EntityView |
|
32 from cubicweb.predicates import (one_line_rset, is_instance, match_context_prop, |
|
33 adaptable, has_mimetype) |
|
34 from cubicweb.mttransforms import ENGINE |
|
35 from cubicweb.web import component, httpcache |
|
36 from cubicweb.web.views import primary, baseviews |
|
37 |
|
38 |
|
39 class DownloadBox(component.EntityCtxComponent): |
|
40 """add download box""" |
|
41 __regid__ = 'download_box' # no download box for images |
|
42 __select__ = (component.EntityCtxComponent.__select__ & |
|
43 adaptable('IDownloadable') & ~has_mimetype('image/')) |
|
44 |
|
45 order = 10 |
|
46 title = _('download') |
|
47 |
|
48 def init_rendering(self): |
|
49 self.items = [self.entity] |
|
50 |
|
51 def render_body(self, w): |
|
52 for item in self.items: |
|
53 idownloadable = item.cw_adapt_to('IDownloadable') |
|
54 w(u'<a href="%s"><img src="%s" alt="%s"/> %s</a>' |
|
55 % (xml_escape(idownloadable.download_url()), |
|
56 self._cw.uiprops['DOWNLOAD_ICON'], |
|
57 self._cw._('download icon'), |
|
58 xml_escape(idownloadable.download_file_name()))) |
|
59 |
|
60 |
|
61 class DownloadView(EntityView): |
|
62 """download view |
|
63 |
|
64 this view is replacing the deprecated 'download' controller and allow |
|
65 downloading of entities providing the necessary interface |
|
66 """ |
|
67 __regid__ = 'download' |
|
68 __select__ = one_line_rset() & adaptable('IDownloadable') |
|
69 |
|
70 templatable = False |
|
71 content_type = 'application/octet-stream' |
|
72 binary = True |
|
73 http_cache_manager = httpcache.EntityHTTPCacheManager |
|
74 add_to_breadcrumbs = False |
|
75 |
|
76 def set_request_content_type(self): |
|
77 """overriden to set the correct filetype and filename""" |
|
78 entity = self.cw_rset.complete_entity(self.cw_row or 0, self.cw_col or 0) |
|
79 adapter = entity.cw_adapt_to('IDownloadable') |
|
80 encoding = adapter.download_encoding() |
|
81 if encoding in BINARY_ENCODINGS: |
|
82 contenttype = 'application/%s' % encoding |
|
83 encoding = None |
|
84 else: |
|
85 contenttype = adapter.download_content_type() |
|
86 self._cw.set_content_type(contenttype or self.content_type, |
|
87 filename=adapter.download_file_name(), |
|
88 encoding=encoding, |
|
89 disposition='attachment') |
|
90 |
|
91 def call(self): |
|
92 entity = self.cw_rset.complete_entity(self.cw_row or 0, self.cw_col or 0) |
|
93 adapter = entity.cw_adapt_to('IDownloadable') |
|
94 self.w(adapter.download_data()) |
|
95 |
|
96 def last_modified(self): |
|
97 return self.cw_rset.get_entity(self.cw_row or 0, self.cw_col or 0).modification_date |
|
98 |
|
99 |
|
100 class DownloadLinkView(EntityView): |
|
101 """view displaying a link to download the file""" |
|
102 __regid__ = 'downloadlink' |
|
103 __select__ = adaptable('IDownloadable') |
|
104 title = None # should not be listed in possible views |
|
105 |
|
106 |
|
107 def cell_call(self, row, col, title=None, **kwargs): |
|
108 entity = self.cw_rset.get_entity(row, col) |
|
109 url = xml_escape(entity.cw_adapt_to('IDownloadable').download_url()) |
|
110 self.w(u'<a href="%s">%s</a>' % (url, xml_escape(title or entity.dc_title()))) |
|
111 |
|
112 |
|
113 class IDownloadablePrimaryView(primary.PrimaryView): |
|
114 __select__ = adaptable('IDownloadable') |
|
115 |
|
116 def render_entity_attributes(self, entity): |
|
117 self.w(u'<div class="content">') |
|
118 adapter = entity.cw_adapt_to('IDownloadable') |
|
119 contenttype = adapter.download_content_type() |
|
120 if contenttype.startswith('image/'): |
|
121 self._cw.add_js('cubicweb.image.js') |
|
122 self.wview('image', entity.cw_rset, row=entity.cw_row, col=entity.cw_col, |
|
123 link=True, klass='contentimage') |
|
124 super(IDownloadablePrimaryView, self).render_entity_attributes(entity) |
|
125 elif contenttype.endswith('html'): |
|
126 self.wview('downloadlink', entity.cw_rset, title=self._cw._('download'), row=entity.cw_row) |
|
127 self.wview('ehtml', entity.cw_rset, row=entity.cw_row, col=entity.cw_col, |
|
128 height='600px', width='100%') |
|
129 else: |
|
130 super(IDownloadablePrimaryView, self).render_entity_attributes(entity) |
|
131 self.wview('downloadlink', entity.cw_rset, title=self._cw._('download'), row=entity.cw_row) |
|
132 self.render_data(entity, contenttype, 'text/html') |
|
133 self.w(u'</div>') |
|
134 |
|
135 def render_data(self, entity, sourcemt, targetmt): |
|
136 adapter = entity.cw_adapt_to('IDownloadable') |
|
137 if ENGINE.find_path(sourcemt, targetmt): |
|
138 try: |
|
139 self.w(entity._cw_mtc_transform(adapter.download_data(), sourcemt, |
|
140 targetmt, adapter.download_encoding())) |
|
141 except Exception as ex: |
|
142 self.exception('while rendering data for %s', entity) |
|
143 msg = self._cw._("can't display data, unexpected error: %s") \ |
|
144 % xml_escape(unicode(ex)) |
|
145 self.w('<div class="error">%s</div>' % msg) |
|
146 return True |
|
147 return False |
|
148 |
|
149 |
|
150 class IDownloadableOneLineView(baseviews.OneLineView): |
|
151 __select__ = adaptable('IDownloadable') |
|
152 |
|
153 def cell_call(self, row, col, title=None, **kwargs): |
|
154 """the oneline view is a link to download the file""" |
|
155 entity = self.cw_rset.get_entity(row, col) |
|
156 url = xml_escape(entity.absolute_url()) |
|
157 adapter = entity.cw_adapt_to('IDownloadable') |
|
158 name = xml_escape(title or entity.dc_title()) |
|
159 durl = xml_escape(adapter.download_url()) |
|
160 self.w(u'<a href="%s">%s</a> [<a href="%s">%s</a>]' % |
|
161 (url, name, durl, self._cw._('download'))) |
|
162 |
|
163 |
|
164 class AbstractEmbeddedView(EntityView): |
|
165 __abstract__ = True |
|
166 |
|
167 _embedding_tag = None |
|
168 |
|
169 def call(self, **kwargs): |
|
170 rset = self.cw_rset |
|
171 for i in range(len(rset)): |
|
172 self.w(u'<div class="efile">') |
|
173 self.wview(self.__regid__, rset, row=i, col=0, **kwargs) |
|
174 self.w(u'</div>') |
|
175 |
|
176 def cell_call(self, row, col, link=False, **kwargs): |
|
177 entity = self.cw_rset.get_entity(row, col) |
|
178 adapter = entity.cw_adapt_to('IDownloadable') |
|
179 tag = self._embedding_tag(src=adapter.download_url(), # pylint: disable=E1102 |
|
180 alt=(self._cw._('download %s') % adapter.download_file_name()), |
|
181 **kwargs) |
|
182 if link: |
|
183 self.w(u'<a href="%s">%s</a>' % (adapter.download_url(), tag)) |
|
184 else: |
|
185 self.w(tag) |
|
186 |
|
187 |
|
188 class ImageView(AbstractEmbeddedView): |
|
189 """image embedded view""" |
|
190 __regid__ = 'image' |
|
191 __select__ = has_mimetype('image/') |
|
192 |
|
193 title = _('image') |
|
194 _embedding_tag = tags.img |
|
195 |
|
196 |
|
197 class EHTMLView(AbstractEmbeddedView): |
|
198 """html embedded view""" |
|
199 __regid__ = 'ehtml' |
|
200 __select__ = has_mimetype('text/html') |
|
201 |
|
202 title = _('embedded html') |
|
203 _embedding_tag = tags.iframe |