##// END OF EJS Templates
Removed null=True from some text fields
neko259 -
r1751:3a810c6e default
parent child Browse files
Show More
@@ -0,0 +1,25 b''
1 # -*- coding: utf-8 -*-
2 # Generated by Django 1.10.4 on 2016-12-29 09:32
3 from __future__ import unicode_literals
4
5 from django.db import migrations, models
6
7
8 class Migration(migrations.Migration):
9
10 dependencies = [
11 ('boards', '0054_auto_20161228_2244'),
12 ]
13
14 operations = [
15 migrations.AlterField(
16 model_name='post',
17 name='text',
18 field=models.TextField(blank=True, default=''),
19 ),
20 migrations.AlterField(
21 model_name='post',
22 name='title',
23 field=models.CharField(blank=True, default='', max_length=200),
24 ),
25 ]
@@ -1,388 +1,388 b''
1 import uuid
1 import uuid
2 import hashlib
2 import hashlib
3 import re
3 import re
4
4
5 from boards import settings
5 from boards import settings
6 from boards.abstracts.tripcode import Tripcode
6 from boards.abstracts.tripcode import Tripcode
7 from boards.models import Attachment, KeyPair, GlobalId
7 from boards.models import Attachment, KeyPair, GlobalId
8 from boards.models.attachment import FILE_TYPES_IMAGE
8 from boards.models.attachment import FILE_TYPES_IMAGE
9 from boards.models.base import Viewable
9 from boards.models.base import Viewable
10 from boards.models.post.export import get_exporter, DIFF_TYPE_JSON
10 from boards.models.post.export import get_exporter, DIFF_TYPE_JSON
11 from boards.models.post.manager import PostManager, NO_IP
11 from boards.models.post.manager import PostManager, NO_IP
12 from boards.utils import datetime_to_epoch
12 from boards.utils import datetime_to_epoch
13 from django.core.exceptions import ObjectDoesNotExist
13 from django.core.exceptions import ObjectDoesNotExist
14 from django.core.urlresolvers import reverse
14 from django.core.urlresolvers import reverse
15 from django.db import models
15 from django.db import models
16 from django.db.models import TextField, QuerySet, F
16 from django.db.models import TextField, QuerySet, F
17 from django.template.defaultfilters import truncatewords, striptags
17 from django.template.defaultfilters import truncatewords, striptags
18 from django.template.loader import render_to_string
18 from django.template.loader import render_to_string
19
19
20 CSS_CLS_HIDDEN_POST = 'hidden_post'
20 CSS_CLS_HIDDEN_POST = 'hidden_post'
21 CSS_CLS_DEAD_POST = 'dead_post'
21 CSS_CLS_DEAD_POST = 'dead_post'
22 CSS_CLS_ARCHIVE_POST = 'archive_post'
22 CSS_CLS_ARCHIVE_POST = 'archive_post'
23 CSS_CLS_POST = 'post'
23 CSS_CLS_POST = 'post'
24 CSS_CLS_MONOCHROME = 'monochrome'
24 CSS_CLS_MONOCHROME = 'monochrome'
25
25
26 TITLE_MAX_WORDS = 10
26 TITLE_MAX_WORDS = 10
27
27
28 APP_LABEL_BOARDS = 'boards'
28 APP_LABEL_BOARDS = 'boards'
29
29
30 BAN_REASON_AUTO = 'Auto'
30 BAN_REASON_AUTO = 'Auto'
31
31
32 TITLE_MAX_LENGTH = 200
32 TITLE_MAX_LENGTH = 200
33
33
34 REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]')
34 REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]')
35 REGEX_GLOBAL_REPLY = re.compile(r'\[post\](\w+)::([^:]+)::(\d+)\[/post\]')
35 REGEX_GLOBAL_REPLY = re.compile(r'\[post\](\w+)::([^:]+)::(\d+)\[/post\]')
36 REGEX_URL = re.compile(r'https?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?')
36 REGEX_URL = re.compile(r'https?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?')
37 REGEX_NOTIFICATION = re.compile(r'\[user\](\w+)\[/user\]')
37 REGEX_NOTIFICATION = re.compile(r'\[user\](\w+)\[/user\]')
38
38
39 PARAMETER_TRUNCATED = 'truncated'
39 PARAMETER_TRUNCATED = 'truncated'
40 PARAMETER_TAG = 'tag'
40 PARAMETER_TAG = 'tag'
41 PARAMETER_OFFSET = 'offset'
41 PARAMETER_OFFSET = 'offset'
42 PARAMETER_DIFF_TYPE = 'type'
42 PARAMETER_DIFF_TYPE = 'type'
43 PARAMETER_CSS_CLASS = 'css_class'
43 PARAMETER_CSS_CLASS = 'css_class'
44 PARAMETER_THREAD = 'thread'
44 PARAMETER_THREAD = 'thread'
45 PARAMETER_IS_OPENING = 'is_opening'
45 PARAMETER_IS_OPENING = 'is_opening'
46 PARAMETER_POST = 'post'
46 PARAMETER_POST = 'post'
47 PARAMETER_OP_ID = 'opening_post_id'
47 PARAMETER_OP_ID = 'opening_post_id'
48 PARAMETER_NEED_OPEN_LINK = 'need_open_link'
48 PARAMETER_NEED_OPEN_LINK = 'need_open_link'
49 PARAMETER_REPLY_LINK = 'reply_link'
49 PARAMETER_REPLY_LINK = 'reply_link'
50 PARAMETER_NEED_OP_DATA = 'need_op_data'
50 PARAMETER_NEED_OP_DATA = 'need_op_data'
51
51
52 POST_VIEW_PARAMS = (
52 POST_VIEW_PARAMS = (
53 'need_op_data',
53 'need_op_data',
54 'reply_link',
54 'reply_link',
55 'need_open_link',
55 'need_open_link',
56 'truncated',
56 'truncated',
57 'mode_tree',
57 'mode_tree',
58 'perms',
58 'perms',
59 'tree_depth',
59 'tree_depth',
60 )
60 )
61
61
62
62
63 class Post(models.Model, Viewable):
63 class Post(models.Model, Viewable):
64 """A post is a message."""
64 """A post is a message."""
65
65
66 objects = PostManager()
66 objects = PostManager()
67
67
68 class Meta:
68 class Meta:
69 app_label = APP_LABEL_BOARDS
69 app_label = APP_LABEL_BOARDS
70 ordering = ('id',)
70 ordering = ('id',)
71
71
72 title = models.CharField(max_length=TITLE_MAX_LENGTH, null=True, blank=True)
72 title = models.CharField(max_length=TITLE_MAX_LENGTH, blank=True, default='')
73 pub_time = models.DateTimeField(db_index=True)
73 pub_time = models.DateTimeField(db_index=True)
74 text = TextField(blank=True, null=True)
74 text = TextField(blank=True, default='')
75 _text_rendered = TextField(blank=True, null=True, editable=False)
75 _text_rendered = TextField(blank=True, null=True, editable=False)
76
76
77 attachments = models.ManyToManyField(Attachment, null=True, blank=True,
77 attachments = models.ManyToManyField(Attachment, null=True, blank=True,
78 related_name='attachment_posts')
78 related_name='attachment_posts')
79
79
80 poster_ip = models.GenericIPAddressField()
80 poster_ip = models.GenericIPAddressField()
81
81
82 # Used for cache and threads updating
82 # Used for cache and threads updating
83 last_edit_time = models.DateTimeField()
83 last_edit_time = models.DateTimeField()
84
84
85 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
85 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
86 null=True,
86 null=True,
87 blank=True, related_name='refposts',
87 blank=True, related_name='refposts',
88 db_index=True)
88 db_index=True)
89 refmap = models.TextField(null=True, blank=True)
89 refmap = models.TextField(null=True, blank=True)
90 thread = models.ForeignKey('Thread', db_index=True, related_name='replies')
90 thread = models.ForeignKey('Thread', db_index=True, related_name='replies')
91
91
92 url = models.TextField()
92 url = models.TextField()
93 uid = models.TextField(db_index=True)
93 uid = models.TextField(db_index=True)
94
94
95 # Global ID with author key. If the message was downloaded from another
95 # Global ID with author key. If the message was downloaded from another
96 # server, this indicates the server.
96 # server, this indicates the server.
97 global_id = models.OneToOneField(GlobalId, null=True, blank=True,
97 global_id = models.OneToOneField(GlobalId, null=True, blank=True,
98 on_delete=models.CASCADE)
98 on_delete=models.CASCADE)
99
99
100 tripcode = models.CharField(max_length=50, blank=True, default='')
100 tripcode = models.CharField(max_length=50, blank=True, default='')
101 opening = models.BooleanField(db_index=True)
101 opening = models.BooleanField(db_index=True)
102 hidden = models.BooleanField(default=False)
102 hidden = models.BooleanField(default=False)
103 version = models.IntegerField(default=1)
103 version = models.IntegerField(default=1)
104
104
105 def __str__(self):
105 def __str__(self):
106 return 'P#{}/{}'.format(self.id, self.get_title())
106 return 'P#{}/{}'.format(self.id, self.get_title())
107
107
108 def get_title(self) -> str:
108 def get_title(self) -> str:
109 return self.title
109 return self.title
110
110
111 def get_title_or_text(self):
111 def get_title_or_text(self):
112 title = self.get_title()
112 title = self.get_title()
113 if not title:
113 if not title:
114 title = truncatewords(striptags(self.get_text()), TITLE_MAX_WORDS)
114 title = truncatewords(striptags(self.get_text()), TITLE_MAX_WORDS)
115
115
116 return title
116 return title
117
117
118 def build_refmap(self, excluded_ids=None) -> None:
118 def build_refmap(self, excluded_ids=None) -> None:
119 """
119 """
120 Builds a replies map string from replies list. This is a cache to stop
120 Builds a replies map string from replies list. This is a cache to stop
121 the server from recalculating the map on every post show.
121 the server from recalculating the map on every post show.
122 """
122 """
123
123
124 replies = self.referenced_posts
124 replies = self.referenced_posts
125 if excluded_ids is not None:
125 if excluded_ids is not None:
126 replies = replies.exclude(id__in=excluded_ids)
126 replies = replies.exclude(id__in=excluded_ids)
127 else:
127 else:
128 replies = replies.all()
128 replies = replies.all()
129
129
130 post_urls = [refpost.get_link_view() for refpost in replies]
130 post_urls = [refpost.get_link_view() for refpost in replies]
131
131
132 self.refmap = ', '.join(post_urls)
132 self.refmap = ', '.join(post_urls)
133
133
134 def is_referenced(self) -> bool:
134 def is_referenced(self) -> bool:
135 return self.refmap and len(self.refmap) > 0
135 return self.refmap and len(self.refmap) > 0
136
136
137 def is_opening(self) -> bool:
137 def is_opening(self) -> bool:
138 """
138 """
139 Checks if this is an opening post or just a reply.
139 Checks if this is an opening post or just a reply.
140 """
140 """
141
141
142 return self.opening
142 return self.opening
143
143
144 def get_absolute_url(self, thread=None):
144 def get_absolute_url(self, thread=None):
145 # Url is cached only for the "main" thread. When getting url
145 # Url is cached only for the "main" thread. When getting url
146 # for other threads, do it manually.
146 # for other threads, do it manually.
147 return self.url
147 return self.url
148
148
149 def get_thread(self):
149 def get_thread(self):
150 return self.thread
150 return self.thread
151
151
152 def get_thread_id(self):
152 def get_thread_id(self):
153 return self.thread_id
153 return self.thread_id
154
154
155 def _get_cache_key(self):
155 def _get_cache_key(self):
156 return [datetime_to_epoch(self.last_edit_time)]
156 return [datetime_to_epoch(self.last_edit_time)]
157
157
158 def get_view_params(self, *args, **kwargs):
158 def get_view_params(self, *args, **kwargs):
159 """
159 """
160 Gets the parameters required for viewing the post based on the arguments
160 Gets the parameters required for viewing the post based on the arguments
161 given and the post itself.
161 given and the post itself.
162 """
162 """
163 thread = kwargs.get('thread') or self.get_thread()
163 thread = kwargs.get('thread') or self.get_thread()
164
164
165 css_classes = [CSS_CLS_POST]
165 css_classes = [CSS_CLS_POST]
166 if thread.is_archived():
166 if thread.is_archived():
167 css_classes.append(CSS_CLS_ARCHIVE_POST)
167 css_classes.append(CSS_CLS_ARCHIVE_POST)
168 elif not thread.can_bump():
168 elif not thread.can_bump():
169 css_classes.append(CSS_CLS_DEAD_POST)
169 css_classes.append(CSS_CLS_DEAD_POST)
170 if self.is_hidden():
170 if self.is_hidden():
171 css_classes.append(CSS_CLS_HIDDEN_POST)
171 css_classes.append(CSS_CLS_HIDDEN_POST)
172 if thread.is_monochrome():
172 if thread.is_monochrome():
173 css_classes.append(CSS_CLS_MONOCHROME)
173 css_classes.append(CSS_CLS_MONOCHROME)
174
174
175 params = dict()
175 params = dict()
176 for param in POST_VIEW_PARAMS:
176 for param in POST_VIEW_PARAMS:
177 if param in kwargs:
177 if param in kwargs:
178 params[param] = kwargs[param]
178 params[param] = kwargs[param]
179
179
180 params.update({
180 params.update({
181 PARAMETER_POST: self,
181 PARAMETER_POST: self,
182 PARAMETER_IS_OPENING: self.is_opening(),
182 PARAMETER_IS_OPENING: self.is_opening(),
183 PARAMETER_THREAD: thread,
183 PARAMETER_THREAD: thread,
184 PARAMETER_CSS_CLASS: ' '.join(css_classes),
184 PARAMETER_CSS_CLASS: ' '.join(css_classes),
185 })
185 })
186
186
187 return params
187 return params
188
188
189 def get_view(self, *args, **kwargs) -> str:
189 def get_view(self, *args, **kwargs) -> str:
190 """
190 """
191 Renders post's HTML view. Some of the post params can be passed over
191 Renders post's HTML view. Some of the post params can be passed over
192 kwargs for the means of caching (if we view the thread, some params
192 kwargs for the means of caching (if we view the thread, some params
193 are same for every post and don't need to be computed over and over.
193 are same for every post and don't need to be computed over and over.
194 """
194 """
195 params = self.get_view_params(*args, **kwargs)
195 params = self.get_view_params(*args, **kwargs)
196
196
197 return render_to_string('boards/post.html', params)
197 return render_to_string('boards/post.html', params)
198
198
199 def get_first_image(self) -> Attachment:
199 def get_first_image(self) -> Attachment:
200 image = None
200 image = None
201 try:
201 try:
202 image = self.attachments.filter(mimetype__in=FILE_TYPES_IMAGE).earliest('id')
202 image = self.attachments.filter(mimetype__in=FILE_TYPES_IMAGE).earliest('id')
203 except Attachment.DoesNotExist:
203 except Attachment.DoesNotExist:
204 pass
204 pass
205 return image
205 return image
206
206
207 def set_global_id(self, key_pair=None):
207 def set_global_id(self, key_pair=None):
208 """
208 """
209 Sets global id based on the given key pair. If no key pair is given,
209 Sets global id based on the given key pair. If no key pair is given,
210 default one is used.
210 default one is used.
211 """
211 """
212
212
213 if key_pair:
213 if key_pair:
214 key = key_pair
214 key = key_pair
215 else:
215 else:
216 try:
216 try:
217 key = KeyPair.objects.get(primary=True)
217 key = KeyPair.objects.get(primary=True)
218 except KeyPair.DoesNotExist:
218 except KeyPair.DoesNotExist:
219 # Do not update the global id because there is no key defined
219 # Do not update the global id because there is no key defined
220 return
220 return
221 global_id = GlobalId(key_type=key.key_type,
221 global_id = GlobalId(key_type=key.key_type,
222 key=key.public_key,
222 key=key.public_key,
223 local_id=self.id)
223 local_id=self.id)
224 global_id.save()
224 global_id.save()
225
225
226 self.global_id = global_id
226 self.global_id = global_id
227
227
228 self.save(update_fields=['global_id'])
228 self.save(update_fields=['global_id'])
229
229
230 def get_pub_time_str(self):
230 def get_pub_time_str(self):
231 return str(self.pub_time)
231 return str(self.pub_time)
232
232
233 def get_replied_ids(self):
233 def get_replied_ids(self):
234 """
234 """
235 Gets ID list of the posts that this post replies.
235 Gets ID list of the posts that this post replies.
236 """
236 """
237
237
238 raw_text = self.get_raw_text()
238 raw_text = self.get_raw_text()
239
239
240 local_replied = REGEX_REPLY.findall(raw_text)
240 local_replied = REGEX_REPLY.findall(raw_text)
241 global_replied = []
241 global_replied = []
242 for match in REGEX_GLOBAL_REPLY.findall(raw_text):
242 for match in REGEX_GLOBAL_REPLY.findall(raw_text):
243 key_type = match[0]
243 key_type = match[0]
244 key = match[1]
244 key = match[1]
245 local_id = match[2]
245 local_id = match[2]
246
246
247 try:
247 try:
248 global_id = GlobalId.objects.get(key_type=key_type,
248 global_id = GlobalId.objects.get(key_type=key_type,
249 key=key, local_id=local_id)
249 key=key, local_id=local_id)
250 for post in Post.objects.filter(global_id=global_id).only('id'):
250 for post in Post.objects.filter(global_id=global_id).only('id'):
251 global_replied.append(post.id)
251 global_replied.append(post.id)
252 except GlobalId.DoesNotExist:
252 except GlobalId.DoesNotExist:
253 pass
253 pass
254 return local_replied + global_replied
254 return local_replied + global_replied
255
255
256 def get_post_data(self, format_type=DIFF_TYPE_JSON, request=None,
256 def get_post_data(self, format_type=DIFF_TYPE_JSON, request=None,
257 include_last_update=False) -> str:
257 include_last_update=False) -> str:
258 """
258 """
259 Gets post HTML or JSON data that can be rendered on a page or used by
259 Gets post HTML or JSON data that can be rendered on a page or used by
260 API.
260 API.
261 """
261 """
262
262
263 return get_exporter(format_type).export(self, request,
263 return get_exporter(format_type).export(self, request,
264 include_last_update)
264 include_last_update)
265
265
266 def notify_clients(self, recursive=True):
266 def notify_clients(self, recursive=True):
267 """
267 """
268 Sends post HTML data to the thread web socket.
268 Sends post HTML data to the thread web socket.
269 """
269 """
270
270
271 if not settings.get_bool('External', 'WebsocketsEnabled'):
271 if not settings.get_bool('External', 'WebsocketsEnabled'):
272 return
272 return
273
273
274 thread_ids = list()
274 thread_ids = list()
275 self.get_thread().notify_clients()
275 self.get_thread().notify_clients()
276
276
277 if recursive:
277 if recursive:
278 for reply_number in re.finditer(REGEX_REPLY, self.get_raw_text()):
278 for reply_number in re.finditer(REGEX_REPLY, self.get_raw_text()):
279 post_id = reply_number.group(1)
279 post_id = reply_number.group(1)
280
280
281 try:
281 try:
282 ref_post = Post.objects.get(id=post_id)
282 ref_post = Post.objects.get(id=post_id)
283
283
284 if ref_post.get_thread().id not in thread_ids:
284 if ref_post.get_thread().id not in thread_ids:
285 # If post is in this thread, its thread was already notified.
285 # If post is in this thread, its thread was already notified.
286 # Otherwise, notify its thread separately.
286 # Otherwise, notify its thread separately.
287 ref_post.notify_clients(recursive=False)
287 ref_post.notify_clients(recursive=False)
288 except ObjectDoesNotExist:
288 except ObjectDoesNotExist:
289 pass
289 pass
290
290
291 def _build_url(self):
291 def _build_url(self):
292 opening = self.is_opening()
292 opening = self.is_opening()
293 opening_id = self.id if opening else self.get_thread().get_opening_post_id()
293 opening_id = self.id if opening else self.get_thread().get_opening_post_id()
294 url = reverse('thread', kwargs={'post_id': opening_id})
294 url = reverse('thread', kwargs={'post_id': opening_id})
295 if not opening:
295 if not opening:
296 url += '#' + str(self.id)
296 url += '#' + str(self.id)
297
297
298 return url
298 return url
299
299
300 def save(self, force_insert=False, force_update=False, using=None,
300 def save(self, force_insert=False, force_update=False, using=None,
301 update_fields=None):
301 update_fields=None):
302 new_post = self.id is None
302 new_post = self.id is None
303
303
304 self.uid = str(uuid.uuid4())
304 self.uid = str(uuid.uuid4())
305 if update_fields is not None and 'uid' not in update_fields:
305 if update_fields is not None and 'uid' not in update_fields:
306 update_fields += ['uid']
306 update_fields += ['uid']
307
307
308 if not new_post:
308 if not new_post:
309 thread = self.get_thread()
309 thread = self.get_thread()
310 if thread:
310 if thread:
311 thread.last_edit_time = self.last_edit_time
311 thread.last_edit_time = self.last_edit_time
312 thread.save(update_fields=['last_edit_time', 'status'])
312 thread.save(update_fields=['last_edit_time', 'status'])
313
313
314 super().save(force_insert, force_update, using, update_fields)
314 super().save(force_insert, force_update, using, update_fields)
315
315
316 if new_post:
316 if new_post:
317 self.url = self._build_url()
317 self.url = self._build_url()
318 super().save(update_fields=['url'])
318 super().save(update_fields=['url'])
319
319
320 def get_text(self) -> str:
320 def get_text(self) -> str:
321 return self._text_rendered
321 return self._text_rendered
322
322
323 def get_raw_text(self) -> str:
323 def get_raw_text(self) -> str:
324 return self.text
324 return self.text
325
325
326 def get_sync_text(self) -> str:
326 def get_sync_text(self) -> str:
327 """
327 """
328 Returns text applicable for sync. It has absolute post reflinks.
328 Returns text applicable for sync. It has absolute post reflinks.
329 """
329 """
330
330
331 replacements = dict()
331 replacements = dict()
332 for post_id in REGEX_REPLY.findall(self.get_raw_text()):
332 for post_id in REGEX_REPLY.findall(self.get_raw_text()):
333 try:
333 try:
334 absolute_post_id = str(Post.objects.get(id=post_id).global_id)
334 absolute_post_id = str(Post.objects.get(id=post_id).global_id)
335 replacements[post_id] = absolute_post_id
335 replacements[post_id] = absolute_post_id
336 except Post.DoesNotExist:
336 except Post.DoesNotExist:
337 pass
337 pass
338
338
339 text = self.get_raw_text() or ''
339 text = self.get_raw_text() or ''
340 for key in replacements:
340 for key in replacements:
341 text = text.replace('[post]{}[/post]'.format(key),
341 text = text.replace('[post]{}[/post]'.format(key),
342 '[post]{}[/post]'.format(replacements[key]))
342 '[post]{}[/post]'.format(replacements[key]))
343 text = text.replace('\r\n', '\n').replace('\r', '\n')
343 text = text.replace('\r\n', '\n').replace('\r', '\n')
344
344
345 return text
345 return text
346
346
347 def get_tripcode(self):
347 def get_tripcode(self):
348 if self.tripcode:
348 if self.tripcode:
349 return Tripcode(self.tripcode)
349 return Tripcode(self.tripcode)
350
350
351 def get_link_view(self):
351 def get_link_view(self):
352 """
352 """
353 Gets view of a reflink to the post.
353 Gets view of a reflink to the post.
354 """
354 """
355 result = '<a href="{}">&gt;&gt;{}</a>'.format(self.get_absolute_url(),
355 result = '<a href="{}">&gt;&gt;{}</a>'.format(self.get_absolute_url(),
356 self.id)
356 self.id)
357 if self.is_opening():
357 if self.is_opening():
358 result = '<b>{}</b>'.format(result)
358 result = '<b>{}</b>'.format(result)
359
359
360 return result
360 return result
361
361
362 def is_hidden(self) -> bool:
362 def is_hidden(self) -> bool:
363 return self.hidden
363 return self.hidden
364
364
365 def set_hidden(self, hidden):
365 def set_hidden(self, hidden):
366 self.hidden = hidden
366 self.hidden = hidden
367
367
368 def increment_version(self):
368 def increment_version(self):
369 self.version = F('version') + 1
369 self.version = F('version') + 1
370
370
371 def clear_cache(self):
371 def clear_cache(self):
372 """
372 """
373 Clears sync data (content cache, signatures etc).
373 Clears sync data (content cache, signatures etc).
374 """
374 """
375 global_id = self.global_id
375 global_id = self.global_id
376 if global_id is not None and global_id.is_local()\
376 if global_id is not None and global_id.is_local()\
377 and global_id.content is not None:
377 and global_id.content is not None:
378 global_id.clear_cache()
378 global_id.clear_cache()
379
379
380 def get_tags(self):
380 def get_tags(self):
381 return self.get_thread().get_tags()
381 return self.get_thread().get_tags()
382
382
383 def get_ip_color(self):
383 def get_ip_color(self):
384 return hashlib.md5(self.poster_ip.encode()).hexdigest()[:6]
384 return hashlib.md5(self.poster_ip.encode()).hexdigest()[:6]
385
385
386 def has_ip(self):
386 def has_ip(self):
387 return self.poster_ip != NO_IP
387 return self.poster_ip != NO_IP
388
388
General Comments 0
You need to be logged in to leave comments. Login now