##// END OF EJS Templates
Search for image for every domain level starting from the lowest one. Cache this into memcached
neko259 -
r1772:1342675d default
parent child Browse files
Show More
@@ -1,201 +1,210 b''
1 1 import re
2 2
3 3 from django.contrib.staticfiles import finders
4 4 from django.contrib.staticfiles.templatetags.staticfiles import static
5 5 from django.core.files.images import get_image_dimensions
6 6 from django.template.defaultfilters import filesizeformat
7 7
8 from boards.utils import get_domain
8 from boards.utils import get_domain, cached_result
9 9
10 10
11 11 FILE_STUB_IMAGE = 'images/file.png'
12 12 FILE_STUB_URL = 'url'
13 13
14 14
15 15 FILE_TYPES_VIDEO = (
16 16 'webm',
17 17 'mp4',
18 18 'mpeg',
19 19 'ogv',
20 20 )
21 21 FILE_TYPE_SVG = 'svg'
22 22 FILE_TYPES_AUDIO = (
23 23 'ogg',
24 24 'mp3',
25 25 'opus',
26 26 )
27 27 FILE_TYPES_IMAGE = (
28 28 'jpeg',
29 29 'jpg',
30 30 'png',
31 31 'bmp',
32 32 'gif',
33 33 )
34 34
35 35 PLAIN_FILE_FORMATS = {
36 36 'pdf': 'pdf',
37 37 'djvu': 'djvu',
38 38 'txt': 'txt',
39 39 'tex': 'tex',
40 40 'xcf': 'xcf',
41 41 'zip': 'archive',
42 42 'tar': 'archive',
43 43 'gz': 'archive',
44 44 }
45 45
46 46 URL_PROTOCOLS = {
47 47 'magnet': 'magnet',
48 48 }
49 49
50 50 CSS_CLASS_IMAGE = 'image'
51 51 CSS_CLASS_THUMB = 'thumb'
52 52
53 53
54 54 def get_viewers():
55 55 return AbstractViewer.__subclasses__()
56 56
57 57
58 58 def get_static_dimensions(filename):
59 59 file_path = finders.find(filename)
60 60 return get_image_dimensions(file_path)
61 61
62 62
63 63 # TODO Move this to utils
64 64 def file_exists(filename):
65 65 return finders.find(filename) is not None
66 66
67 67
68 68 class AbstractViewer:
69 69 def __init__(self, file, file_type, hash, url):
70 70 self.file = file
71 71 self.file_type = file_type
72 72 self.hash = hash
73 73 self.url = url
74 74
75 75 @staticmethod
76 76 def supports(file_type):
77 77 return True
78 78
79 79 def get_view(self):
80 80 return '<div class="image">'\
81 81 '{}'\
82 82 '<div class="image-metadata"><a href="{}" download >{}, {}</a></div>'\
83 83 '</div>'.format(self.get_format_view(), self.file.url,
84 84 self.file_type, filesizeformat(self.file.size))
85 85
86 86 def get_format_view(self):
87 87 if self.file_type in PLAIN_FILE_FORMATS:
88 88 image = 'images/fileformats/{}.png'.format(
89 89 PLAIN_FILE_FORMATS[self.file_type])
90 90 else:
91 91 image = FILE_STUB_IMAGE
92 92
93 93 w, h = get_static_dimensions(image)
94 94
95 95 return '<a href="{}">'\
96 96 '<img class="url-image" src="{}" width="{}" height="{}"/>'\
97 97 '</a>'.format(self.file.url, static(image), w, h)
98 98
99 99
100 100 class VideoViewer(AbstractViewer):
101 101 @staticmethod
102 102 def supports(file_type):
103 103 return file_type in FILE_TYPES_VIDEO
104 104
105 105 def get_format_view(self):
106 106 return '<video width="200" height="150" controls src="{}"></video>'\
107 107 .format(self.file.url)
108 108
109 109
110 110 class AudioViewer(AbstractViewer):
111 111 @staticmethod
112 112 def supports(file_type):
113 113 return file_type in FILE_TYPES_AUDIO
114 114
115 115 def get_format_view(self):
116 116 return '<audio controls src="{}"></audio>'.format(self.file.url)
117 117
118 118
119 119 class SvgViewer(AbstractViewer):
120 120 @staticmethod
121 121 def supports(file_type):
122 122 return file_type == FILE_TYPE_SVG
123 123
124 124 def get_format_view(self):
125 125 return '<a class="thumb" href="{}">'\
126 126 '<img class="post-image-preview" width="200" height="150" src="{}" />'\
127 127 '</a>'.format(self.file.url, self.file.url)
128 128
129 129
130 130 class ImageViewer(AbstractViewer):
131 131 @staticmethod
132 132 def supports(file_type):
133 133 return file_type in FILE_TYPES_IMAGE
134 134
135 135 def get_format_view(self):
136 136 metadata = '{}, {}'.format(self.file.name.split('.')[-1],
137 137 filesizeformat(self.file.size))
138 138 width, height = get_image_dimensions(self.file.file)
139 139 preview_path = self.file.path.replace('.', '.200x150.')
140 140 pre_width, pre_height = get_image_dimensions(preview_path)
141 141
142 142 split = self.file.url.rsplit('.', 1)
143 143 w, h = 200, 150
144 144 thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
145 145
146 146 return '<a class="{}" href="{full}">' \
147 147 '<img class="post-image-preview"' \
148 148 ' src="{}"' \
149 149 ' alt="{}"' \
150 150 ' width="{}"' \
151 151 ' height="{}"' \
152 152 ' data-width="{}"' \
153 153 ' data-height="{}" />' \
154 154 '</a>' \
155 155 .format(CSS_CLASS_THUMB,
156 156 thumb_url,
157 157 self.hash,
158 158 str(pre_width),
159 159 str(pre_height), str(width), str(height),
160 160 full=self.file.url, image_meta=metadata)
161 161
162 162
163 163 class UrlViewer(AbstractViewer):
164 164 @staticmethod
165 165 def supports(file_type):
166 166 return file_type is None
167 167
168 168 def get_view(self):
169 169 return '<div class="image">' \
170 170 '{}' \
171 171 '<div class="image-metadata">{}</div>' \
172 172 '</div>'.format(self.get_format_view(), get_domain(self.url))
173 173
174 174 def get_format_view(self):
175 175 protocol = self.url.split('://')[0]
176 176
177 177 domain = get_domain(self.url)
178 178
179 179 if protocol in URL_PROTOCOLS:
180 180 url_image_name = URL_PROTOCOLS.get(protocol)
181 181 elif domain:
182 filename = 'images/domains/{}.png'.format(domain)
183 if file_exists(filename):
184 url_image_name = 'domains/' + domain
185 else:
186 url_image_name = FILE_STUB_URL
182 url_image_name = self._find_image_for_domains(domain) or FILE_STUB_URL
187 183 else:
188 184 url_image_name = FILE_STUB_URL
189 185
190 186 image_path = 'images/{}.png'.format(url_image_name)
191 187 image = static(image_path)
192 188 w, h = get_static_dimensions(image_path)
193 189
194 190 return '<a href="{}">' \
195 191 '<img class="url-image" src="{}" width="{}" height="{}"/>' \
196 192 '</a>'.format(self.url, image, w, h)
197 193
194 @cached_result()
195 def _find_image_for_domains(self, domain):
196 """
197 Searches for the domain image for every domain level except top.
198 E.g. for l3.example.co.uk it will search for l3.example.co.uk, then
199 example.co.uk, then co.uk
200 """
201 levels = domain.split('.')
202 while len(levels) > 1:
203 domain = '.'.join(levels)
198 204
199 def _get_protocol(self):
200 pass
205 filename = 'images/domains/{}.png'.format(domain)
206 if file_exists(filename):
207 return 'domains/' + domain
208 else:
209 del levels[0]
201 210
@@ -1,177 +1,157 b''
1 1 """
2 2 This module contains helper functions and helper classes.
3 3 """
4 4 import hashlib
5 5 import uuid
6 6
7 7 from boards.abstracts.constants import FILE_DIRECTORY
8 8 from random import random
9 9 import time
10 10 import hmac
11 11
12 12 from django.core.cache import cache
13 13 from django.db.models import Model
14 14 from django import forms
15 15 from django.template.defaultfilters import filesizeformat
16 16 from django.utils import timezone
17 17 from django.utils.translation import ugettext_lazy as _
18 18 import magic
19 19 import os
20 20
21 21 import boards
22 22 from boards.settings import get_bool
23 23 from neboard import settings
24 24
25 25 CACHE_KEY_DELIMITER = '_'
26 26
27 27 HTTP_FORWARDED = 'HTTP_X_FORWARDED_FOR'
28 28 META_REMOTE_ADDR = 'REMOTE_ADDR'
29 29
30 30 SETTING_MESSAGES = 'Messages'
31 31 SETTING_ANON_MODE = 'AnonymousMode'
32 32
33 33 ANON_IP = '127.0.0.1'
34 34
35 35 FILE_EXTENSION_DELIMITER = '.'
36 36
37 KNOWN_DOMAINS = (
38 'org.ru',
39 )
40
41 37
42 38 def is_anonymous_mode():
43 39 return get_bool(SETTING_MESSAGES, SETTING_ANON_MODE)
44 40
45 41
46 42 def get_client_ip(request):
47 43 if is_anonymous_mode():
48 44 ip = ANON_IP
49 45 else:
50 46 x_forwarded_for = request.META.get(HTTP_FORWARDED)
51 47 if x_forwarded_for:
52 48 ip = x_forwarded_for.split(',')[-1].strip()
53 49 else:
54 50 ip = request.META.get(META_REMOTE_ADDR)
55 51 return ip
56 52
57 53
58 54 # TODO The output format is not epoch because it includes microseconds
59 55 def datetime_to_epoch(datetime):
60 56 return int(time.mktime(timezone.localtime(
61 57 datetime,timezone.get_current_timezone()).timetuple())
62 58 * 1000000 + datetime.microsecond)
63 59
64 60
65 61 def get_websocket_token(user_id='', timestamp=''):
66 62 """
67 63 Create token to validate information provided by new connection.
68 64 """
69 65
70 66 sign = hmac.new(settings.CENTRIFUGE_PROJECT_SECRET.encode())
71 67 sign.update(settings.CENTRIFUGE_PROJECT_ID.encode())
72 68 sign.update(user_id.encode())
73 69 sign.update(timestamp.encode())
74 70 token = sign.hexdigest()
75 71
76 72 return token
77 73
78 74
79 75 # TODO Test this carefully
80 76 def cached_result(key_method=None):
81 77 """
82 78 Caches method result in the Django's cache system, persisted by object name,
83 79 object name, model id if object is a Django model, args and kwargs if any.
84 80 """
85 81 def _cached_result(function):
86 82 def inner_func(obj, *args, **kwargs):
87 83 cache_key_params = [obj.__class__.__name__, function.__name__]
88 84
89 85 cache_key_params += args
90 86 for key, value in kwargs:
91 87 cache_key_params.append(key + ':' + value)
92 88
93 89 if isinstance(obj, Model):
94 90 cache_key_params.append(str(obj.id))
95 91
96 92 if key_method is not None:
97 93 cache_key_params += [str(arg) for arg in key_method(obj)]
98 94
99 95 cache_key = CACHE_KEY_DELIMITER.join(cache_key_params)
100 96
101 97 persisted_result = cache.get(cache_key)
102 98 if persisted_result is not None:
103 99 result = persisted_result
104 100 else:
105 101 result = function(obj, *args, **kwargs)
106 102 if result is not None:
107 103 cache.set(cache_key, result)
108 104
109 105 return result
110 106
111 107 return inner_func
112 108 return _cached_result
113 109
114 110
115 111 def get_file_hash(file) -> str:
116 112 md5 = hashlib.md5()
117 113 for chunk in file.chunks():
118 114 md5.update(chunk)
119 115 return md5.hexdigest()
120 116
121 117
122 118 def validate_file_size(size: int):
123 119 max_size = boards.settings.get_int('Forms', 'MaxFileSize')
124 120 if size > max_size:
125 121 raise forms.ValidationError(
126 122 _('File must be less than %s but is %s.')
127 123 % (filesizeformat(max_size), filesizeformat(size)))
128 124
129 125
130 126 def get_extension(filename):
131 127 return filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
132 128
133 129
134 130 def get_upload_filename(model_instance, old_filename):
135 131 extension = get_extension(old_filename)
136 132 new_name = '{}.{}'.format(uuid.uuid4(), extension)
137 133
138 134 return os.path.join(FILE_DIRECTORY, new_name)
139 135
140 136
141 137 def get_file_mimetype(file) -> str:
142 138 file_type = magic.from_buffer(file.chunks().__next__(), mime=True)
143 139 if file_type is None:
144 140 file_type = 'application/octet-stream'
145 141 elif type(file_type) == bytes:
146 142 file_type = file_type.decode()
147 143 return file_type
148 144
149 145
150 146 def get_domain(url: str) -> str:
151 147 """
152 148 Gets domain from an URL with random number of domain levels.
153 149 """
154 150 domain_parts = url.split('/')
155 151 if len(domain_parts) >= 2:
156 152 full_domain = domain_parts[2]
157 153 else:
158 154 full_domain = ''
159 155
160 result = full_domain
161 if full_domain:
162 levels = full_domain.split('.')
163 if len(levels) >= 2:
164 top = levels[-1]
165 second = levels[-2]
156 return full_domain
166 157
167 has_third_level = len(levels) > 2
168 if has_third_level:
169 third = levels[-3]
170
171 if has_third_level and ('{}.{}'.format(second, top) in KNOWN_DOMAINS):
172 result = '{}.{}.{}'.format(third, second, top)
173 else:
174 result = '{}.{}'.format(second, top)
175
176 return result
177
General Comments 0
You need to be logged in to leave comments. Login now