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