##// END OF EJS Templates
Refactored attachment viewers to store view htmls as constants
neko259 -
r1828:fb6d2612 default
parent child Browse files
Show More
@@ -1,231 +1,238 b''
1 import re
1 import re
2
2
3 from PIL import Image
3 from PIL import Image
4
4
5 from django.contrib.staticfiles import finders
5 from django.contrib.staticfiles import finders
6 from django.contrib.staticfiles.templatetags.staticfiles import static
6 from django.contrib.staticfiles.templatetags.staticfiles import static
7 from django.core.files.images import get_image_dimensions
7 from django.core.files.images import get_image_dimensions
8 from django.template.defaultfilters import filesizeformat
8 from django.template.defaultfilters import filesizeformat
9 from django.core.urlresolvers import reverse
9 from django.core.urlresolvers import reverse
10 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
10 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
11
11
12 from boards.utils import get_domain, cached_result
12 from boards.utils import get_domain, cached_result
13 from boards import settings
13 from boards import settings
14
14
15
15
16 FILE_STUB_IMAGE = 'images/file.png'
16 FILE_STUB_IMAGE = 'images/file.png'
17 FILE_STUB_URL = 'url'
17 FILE_STUB_URL = 'url'
18 FILE_FILEFORMAT = 'images/fileformats/{}.png'
18 FILE_FILEFORMAT = 'images/fileformats/{}.png'
19
19
20
20
21 FILE_TYPES_VIDEO = (
21 FILE_TYPES_VIDEO = (
22 'webm',
22 'webm',
23 'mp4',
23 'mp4',
24 'mpeg',
24 'mpeg',
25 'ogv',
25 'ogv',
26 )
26 )
27 FILE_TYPE_SVG = 'svg'
27 FILE_TYPE_SVG = 'svg'
28 FILE_TYPES_AUDIO = (
28 FILE_TYPES_AUDIO = (
29 'ogg',
29 'ogg',
30 'mp3',
30 'mp3',
31 'opus',
31 'opus',
32 )
32 )
33 FILE_TYPES_IMAGE = (
33 FILE_TYPES_IMAGE = (
34 'jpeg',
34 'jpeg',
35 'jpg',
35 'jpg',
36 'png',
36 'png',
37 'bmp',
37 'bmp',
38 'gif',
38 'gif',
39 )
39 )
40
40
41 PLAIN_FILE_FORMATS = {
41 PLAIN_FILE_FORMATS = {
42 'zip': 'archive',
42 'zip': 'archive',
43 'tar': 'archive',
43 'tar': 'archive',
44 'gz': 'archive',
44 'gz': 'archive',
45 'mid' : 'midi',
45 'mid' : 'midi',
46 }
46 }
47
47
48 URL_PROTOCOLS = {
48 URL_PROTOCOLS = {
49 'magnet': 'magnet',
49 'magnet': 'magnet',
50 }
50 }
51
51
52 CSS_CLASS_IMAGE = 'image'
52 CSS_CLASS_IMAGE = 'image'
53 CSS_CLASS_THUMB = 'thumb'
53 CSS_CLASS_THUMB = 'thumb'
54
54
55 ABSTRACT_VIEW = '<div class="image">'\
56 '{}'\
57 '<div class="image-metadata"><a href="{}" download >{}, {}</a>'\
58 ' <a class="file-menu" href="#" data-type="{}" data-search-url="{}" data-filename="{}">πŸ” </a></div>'\
59 '</div>'
60 URL_VIEW = '<div class="image">' \
61 '{}' \
62 '<div class="image-metadata">{}</div>' \
63 '</div>'
64 ABSTRACT_FORMAT_VIEW = '<a href="{}">'\
65 '<img class="url-image" src="{}" width="{}" height="{}"/>'\
66 '</a>'
67 VIDEO_FORMAT_VIEW = '<video width="200" height="150" controls src="{}"></video>'
68 AUDIO_FORMAT_VIEW = '<audio controls src="{}"></audio>'
69 IMAGE_FORMAT_VIEW = '<a class="{}" href="{full}">' \
70 '<img class="post-image-preview"' \
71 ' src="{}"' \
72 ' alt="{}"' \
73 ' width="{}"' \
74 ' height="{}"' \
75 ' data-width="{}"' \
76 ' data-height="{}" />' \
77 '</a>'
78 SVG_FORMAT_VIEW = '<a class="thumb" href="{}">'\
79 '<img class="post-image-preview" width="200" height="150" src="{}" />'\
80 '</a>'
81 URL_FORMAT_VIEW = '<a href="{}">' \
82 '<img class="url-image" src="{}" width="{}" height="{}"/>' \
83 '</a>'
84
55
85
56 def get_viewers():
86 def get_viewers():
57 return AbstractViewer.__subclasses__()
87 return AbstractViewer.__subclasses__()
58
88
59
89
60 def get_static_dimensions(filename):
90 def get_static_dimensions(filename):
61 file_path = finders.find(filename)
91 file_path = finders.find(filename)
62 return get_image_dimensions(file_path)
92 return get_image_dimensions(file_path)
63
93
64
94
65 # TODO Move this to utils
95 # TODO Move this to utils
66 def file_exists(filename):
96 def file_exists(filename):
67 return finders.find(filename) is not None
97 return finders.find(filename) is not None
68
98
69
99
70 class AbstractViewer:
100 class AbstractViewer:
71 def __init__(self, file, file_type, hash, url):
101 def __init__(self, file, file_type, hash, url):
72 self.file = file
102 self.file = file
73 self.file_type = file_type
103 self.file_type = file_type
74 self.hash = hash
104 self.hash = hash
75 self.url = url
105 self.url = url
76
106
77 @staticmethod
107 @staticmethod
78 def supports(file_type):
108 def supports(file_type):
79 return True
109 return True
80
110
81 def get_view(self):
111 def get_view(self):
82 search_host = settings.get('External', 'ImageSearchHost')
112 search_host = settings.get('External', 'ImageSearchHost')
83 if search_host:
113 if search_host:
84 if search_host.endswith('/'):
114 if search_host.endswith('/'):
85 search_host = search_host[:-1]
115 search_host = search_host[:-1]
86 search_url = search_host + self.file.url
116 search_url = search_host + self.file.url
87 else:
117 else:
88 search_url = ''
118 search_url = ''
89
119
90 return '<div class="image">'\
120 return ABSTRACT_VIEW.format(self.get_format_view(), self.file.url,
91 '{}'\
121 self.file_type, filesizeformat(self.file.size),
92 '<div class="image-metadata"><a href="{}" download >{}, {}</a>'\
122 self.file_type, search_url, self.file.name)
93 ' <a class="file-menu" href="#" data-type="{}" data-search-url="{}" data-filename="{}">πŸ” </a></div>'\
94 '</div>'.format(self.get_format_view(), self.file.url,
95 self.file_type, filesizeformat(self.file.size),
96 self.file_type, search_url, self.file.name)
97
123
98 def get_format_view(self):
124 def get_format_view(self):
99 image_name = PLAIN_FILE_FORMATS.get(self.file_type, self.file_type)
125 image_name = PLAIN_FILE_FORMATS.get(self.file_type, self.file_type)
100 file_name = FILE_FILEFORMAT.format(image_name)
126 file_name = FILE_FILEFORMAT.format(image_name)
101
127
102 if file_exists(file_name):
128 if file_exists(file_name):
103 image = file_name
129 image = file_name
104 else:
130 else:
105 image = FILE_STUB_IMAGE
131 image = FILE_STUB_IMAGE
106
132
107 w, h = get_static_dimensions(image)
133 w, h = get_static_dimensions(image)
108
134
109 return '<a href="{}">'\
135 return ABSTRACT_FORMAT_VIEW.format(self.file.url, static(image), w, h)
110 '<img class="url-image" src="{}" width="{}" height="{}"/>'\
111 '</a>'.format(self.file.url, static(image), w, h)
112
136
113
137
114 class VideoViewer(AbstractViewer):
138 class VideoViewer(AbstractViewer):
115 @staticmethod
139 @staticmethod
116 def supports(file_type):
140 def supports(file_type):
117 return file_type in FILE_TYPES_VIDEO
141 return file_type in FILE_TYPES_VIDEO
118
142
119 def get_format_view(self):
143 def get_format_view(self):
120 return '<video width="200" height="150" controls src="{}"></video>'\
144 return VIDEO_FORMAT_VIEW.format(self.file.url)
121 .format(self.file.url)
122
145
123
146
124 class AudioViewer(AbstractViewer):
147 class AudioViewer(AbstractViewer):
125 @staticmethod
148 @staticmethod
126 def supports(file_type):
149 def supports(file_type):
127 return file_type in FILE_TYPES_AUDIO
150 return file_type in FILE_TYPES_AUDIO
128
151
129 def get_format_view(self):
152 def get_format_view(self):
130 return '<audio controls src="{}"></audio>'.format(self.file.url)
153 return AUDIO_FORMAT_VIEW.format(self.file.url)
131
154
132
155
133 class SvgViewer(AbstractViewer):
156 class SvgViewer(AbstractViewer):
134 @staticmethod
157 @staticmethod
135 def supports(file_type):
158 def supports(file_type):
136 return file_type == FILE_TYPE_SVG
159 return file_type == FILE_TYPE_SVG
137
160
138 def get_format_view(self):
161 def get_format_view(self):
139 return '<a class="thumb" href="{}">'\
162 return SVG_FORMAT_VIEW.format(self.file.url, self.file.url)
140 '<img class="post-image-preview" width="200" height="150" src="{}" />'\
141 '</a>'.format(self.file.url, self.file.url)
142
163
143
164
144 class ImageViewer(AbstractViewer):
165 class ImageViewer(AbstractViewer):
145 @staticmethod
166 @staticmethod
146 def supports(file_type):
167 def supports(file_type):
147 return file_type in FILE_TYPES_IMAGE
168 return file_type in FILE_TYPES_IMAGE
148
169
149 def get_format_view(self):
170 def get_format_view(self):
150 metadata = '{}, {}'.format(self.file.name.split('.')[-1],
171 metadata = '{}, {}'.format(self.file.name.split('.')[-1],
151 filesizeformat(self.file.size))
172 filesizeformat(self.file.size))
152
173
153 try:
174 try:
154 width, height = get_image_dimensions(self.file.path)
175 width, height = get_image_dimensions(self.file.path)
155 except Exception:
176 except Exception:
156 # If the image is a decompression bomb, treat it as just a regular
177 # If the image is a decompression bomb, treat it as just a regular
157 # file
178 # file
158 return super().get_format_view()
179 return super().get_format_view()
159
180
160 preview_path = self.file.path.replace('.', '.200x150.')
181 preview_path = self.file.path.replace('.', '.200x150.')
161 pre_width, pre_height = get_image_dimensions(preview_path)
182 pre_width, pre_height = get_image_dimensions(preview_path)
162
183
163 split = self.file.url.rsplit('.', 1)
184 split = self.file.url.rsplit('.', 1)
164 w, h = 200, 150
185 w, h = 200, 150
165 thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
186 thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
166
187
167 return '<a class="{}" href="{full}">' \
188 return IMAGE_FORMAT_VIEW.format(CSS_CLASS_THUMB,
168 '<img class="post-image-preview"' \
189 thumb_url,
169 ' src="{}"' \
190 self.hash,
170 ' alt="{}"' \
191 str(pre_width),
171 ' width="{}"' \
192 str(pre_height), str(width), str(height),
172 ' height="{}"' \
193 full=self.file.url, image_meta=metadata)
173 ' data-width="{}"' \
174 ' data-height="{}" />' \
175 '</a>' \
176 .format(CSS_CLASS_THUMB,
177 thumb_url,
178 self.hash,
179 str(pre_width),
180 str(pre_height), str(width), str(height),
181 full=self.file.url, image_meta=metadata)
182
194
183
195
184 class UrlViewer(AbstractViewer):
196 class UrlViewer(AbstractViewer):
185 @staticmethod
197 @staticmethod
186 def supports(file_type):
198 def supports(file_type):
187 return file_type is None
199 return file_type is None
188
200
189 def get_view(self):
201 def get_view(self):
190 return '<div class="image">' \
202 return URL_VIEW.format(self.get_format_view(), get_domain(self.url))
191 '{}' \
192 '<div class="image-metadata">{}</div>' \
193 '</div>'.format(self.get_format_view(), get_domain(self.url))
194
203
195 def get_format_view(self):
204 def get_format_view(self):
196 protocol = self.url.split(':')[0]
205 protocol = self.url.split(':')[0]
197
206
198 domain = get_domain(self.url)
207 domain = get_domain(self.url)
199
208
200 if protocol in URL_PROTOCOLS:
209 if protocol in URL_PROTOCOLS:
201 url_image_name = URL_PROTOCOLS.get(protocol)
210 url_image_name = URL_PROTOCOLS.get(protocol)
202 elif domain:
211 elif domain:
203 url_image_name = self._find_image_for_domains(domain) or FILE_STUB_URL
212 url_image_name = self._find_image_for_domains(domain) or FILE_STUB_URL
204 else:
213 else:
205 url_image_name = FILE_STUB_URL
214 url_image_name = FILE_STUB_URL
206
215
207 image_path = 'images/{}.png'.format(url_image_name)
216 image_path = 'images/{}.png'.format(url_image_name)
208 image = static(image_path)
217 image = static(image_path)
209 w, h = get_static_dimensions(image_path)
218 w, h = get_static_dimensions(image_path)
210
219
211 return '<a href="{}">' \
220 return URL_FORMAT_VIEW.format(self.url, image, w, h)
212 '<img class="url-image" src="{}" width="{}" height="{}"/>' \
213 '</a>'.format(self.url, image, w, h)
214
221
215 @cached_result()
222 @cached_result()
216 def _find_image_for_domains(self, domain):
223 def _find_image_for_domains(self, domain):
217 """
224 """
218 Searches for the domain image for every domain level except top.
225 Searches for the domain image for every domain level except top.
219 E.g. for l3.example.co.uk it will search for l3.example.co.uk, then
226 E.g. for l3.example.co.uk it will search for l3.example.co.uk, then
220 example.co.uk, then co.uk
227 example.co.uk, then co.uk
221 """
228 """
222 levels = domain.split('.')
229 levels = domain.split('.')
223 while len(levels) > 1:
230 while len(levels) > 1:
224 domain = '.'.join(levels)
231 domain = '.'.join(levels)
225
232
226 filename = 'images/domains/{}.png'.format(domain)
233 filename = 'images/domains/{}.png'.format(domain)
227 if file_exists(filename):
234 if file_exists(filename):
228 return 'domains/' + domain
235 return 'domains/' + domain
229 else:
236 else:
230 del levels[0]
237 del levels[0]
231
238
General Comments 0
You need to be logged in to leave comments. Login now