Show More
@@ -1,461 +1,474 b'' | |||||
1 | from datetime import datetime, timedelta, date |
|
1 | from datetime import datetime, timedelta, date | |
2 | from datetime import time as dtime |
|
2 | from datetime import time as dtime | |
|
3 | import logging | |||
3 | import os |
|
4 | import os | |
4 | from random import random |
|
5 | from random import random | |
5 | import time |
|
6 | import time | |
6 | import re |
|
7 | import re | |
7 | import hashlib |
|
8 | import hashlib | |
8 |
|
9 | |||
9 | from django.core.cache import cache |
|
10 | from django.core.cache import cache | |
10 | from django.core.urlresolvers import reverse |
|
11 | from django.core.urlresolvers import reverse | |
11 | from django.db import models, transaction |
|
12 | from django.db import models, transaction | |
12 | from django.utils import timezone |
|
13 | from django.utils import timezone | |
13 | from markupfield.fields import MarkupField |
|
14 | from markupfield.fields import MarkupField | |
14 |
|
15 | |||
15 | from neboard import settings |
|
16 | from neboard import settings | |
16 | from boards import thumbs |
|
17 | from boards import thumbs | |
17 |
|
18 | |||
18 |
|
19 | |||
19 | APP_LABEL_BOARDS = 'boards' |
|
20 | APP_LABEL_BOARDS = 'boards' | |
20 |
|
21 | |||
21 | CACHE_KEY_PPD = 'ppd' |
|
22 | CACHE_KEY_PPD = 'ppd' | |
22 | CACHE_KEY_POST_URL = 'post_url' |
|
23 | CACHE_KEY_POST_URL = 'post_url' | |
23 | CACHE_KEY_OPENING_POST = 'opening_post_id' |
|
24 | CACHE_KEY_OPENING_POST = 'opening_post_id' | |
24 |
|
25 | |||
25 | POSTS_PER_DAY_RANGE = range(7) |
|
26 | POSTS_PER_DAY_RANGE = range(7) | |
26 |
|
27 | |||
27 | BAN_REASON_AUTO = 'Auto' |
|
28 | BAN_REASON_AUTO = 'Auto' | |
28 |
|
29 | |||
29 | IMAGE_THUMB_SIZE = (200, 150) |
|
30 | IMAGE_THUMB_SIZE = (200, 150) | |
30 |
|
31 | |||
31 | TITLE_MAX_LENGTH = 200 |
|
32 | TITLE_MAX_LENGTH = 200 | |
32 |
|
33 | |||
33 | DEFAULT_MARKUP_TYPE = 'markdown' |
|
34 | DEFAULT_MARKUP_TYPE = 'markdown' | |
34 |
|
35 | |||
35 | NO_PARENT = -1 |
|
36 | NO_PARENT = -1 | |
36 | NO_IP = '0.0.0.0' |
|
37 | NO_IP = '0.0.0.0' | |
37 | UNKNOWN_UA = '' |
|
38 | UNKNOWN_UA = '' | |
38 | ALL_PAGES = -1 |
|
39 | ALL_PAGES = -1 | |
39 | IMAGES_DIRECTORY = 'images/' |
|
40 | IMAGES_DIRECTORY = 'images/' | |
40 | FILE_EXTENSION_DELIMITER = '.' |
|
41 | FILE_EXTENSION_DELIMITER = '.' | |
41 |
|
42 | |||
42 | SETTING_MODERATE = "moderate" |
|
43 | SETTING_MODERATE = "moderate" | |
43 |
|
44 | |||
44 | REGEX_REPLY = re.compile('>>(\d+)') |
|
45 | REGEX_REPLY = re.compile('>>(\d+)') | |
45 |
|
46 | |||
|
47 | logger = logging.getLogger(__name__) | |||
|
48 | ||||
46 |
|
49 | |||
47 | class PostManager(models.Manager): |
|
50 | class PostManager(models.Manager): | |
48 |
|
51 | |||
49 | def create_post(self, title, text, image=None, thread=None, |
|
52 | def create_post(self, title, text, image=None, thread=None, | |
50 | ip=NO_IP, tags=None, user=None): |
|
53 | ip=NO_IP, tags=None, user=None): | |
51 | """ |
|
54 | """ | |
52 | Creates new post |
|
55 | Creates new post | |
53 | """ |
|
56 | """ | |
54 |
|
57 | |||
55 | posting_time = timezone.now() |
|
58 | posting_time = timezone.now() | |
56 | if not thread: |
|
59 | if not thread: | |
57 | thread = Thread.objects.create(bump_time=posting_time, |
|
60 | thread = Thread.objects.create(bump_time=posting_time, | |
58 | last_edit_time=posting_time) |
|
61 | last_edit_time=posting_time) | |
59 | new_thread = True |
|
62 | new_thread = True | |
60 | else: |
|
63 | else: | |
61 | thread.bump() |
|
64 | thread.bump() | |
62 | thread.last_edit_time = posting_time |
|
65 | thread.last_edit_time = posting_time | |
63 | thread.save() |
|
66 | thread.save() | |
64 | new_thread = False |
|
67 | new_thread = False | |
65 |
|
68 | |||
66 | post = self.create(title=title, |
|
69 | post = self.create(title=title, | |
67 | text=text, |
|
70 | text=text, | |
68 | pub_time=posting_time, |
|
71 | pub_time=posting_time, | |
69 | thread_new=thread, |
|
72 | thread_new=thread, | |
70 | image=image, |
|
73 | image=image, | |
71 | poster_ip=ip, |
|
74 | poster_ip=ip, | |
72 | poster_user_agent=UNKNOWN_UA, # TODO Get UA at |
|
75 | poster_user_agent=UNKNOWN_UA, # TODO Get UA at | |
73 | # last! |
|
76 | # last! | |
74 | last_edit_time=posting_time, |
|
77 | last_edit_time=posting_time, | |
75 | user=user) |
|
78 | user=user) | |
76 |
|
79 | |||
77 | thread.replies.add(post) |
|
80 | thread.replies.add(post) | |
78 | if tags: |
|
81 | if tags: | |
79 | linked_tags = [] |
|
82 | linked_tags = [] | |
80 | for tag in tags: |
|
83 | for tag in tags: | |
81 | tag_linked_tags = tag.get_linked_tags() |
|
84 | tag_linked_tags = tag.get_linked_tags() | |
82 | if len(tag_linked_tags) > 0: |
|
85 | if len(tag_linked_tags) > 0: | |
83 | linked_tags.extend(tag_linked_tags) |
|
86 | linked_tags.extend(tag_linked_tags) | |
84 |
|
87 | |||
85 | tags.extend(linked_tags) |
|
88 | tags.extend(linked_tags) | |
86 | map(thread.add_tag, tags) |
|
89 | map(thread.add_tag, tags) | |
87 |
|
90 | |||
88 | if new_thread: |
|
91 | if new_thread: | |
89 | self._delete_old_threads() |
|
92 | self._delete_old_threads() | |
90 | self.connect_replies(post) |
|
93 | self.connect_replies(post) | |
91 |
|
94 | |||
|
95 | logger.info('Created post #%d' % post.id) | |||
|
96 | ||||
92 | return post |
|
97 | return post | |
93 |
|
98 | |||
94 | def delete_post(self, post): |
|
99 | def delete_post(self, post): | |
95 | """ |
|
100 | """ | |
96 | Deletes post and update or delete its thread |
|
101 | Deletes post and update or delete its thread | |
97 | """ |
|
102 | """ | |
98 |
|
103 | |||
|
104 | post_id = post.id | |||
|
105 | ||||
99 | thread = post.get_thread() |
|
106 | thread = post.get_thread() | |
100 |
|
107 | |||
101 | if post.is_opening(): |
|
108 | if post.is_opening(): | |
102 | thread.delete_with_posts() |
|
109 | thread.delete_with_posts() | |
103 | else: |
|
110 | else: | |
104 | thread.last_edit_time = timezone.now() |
|
111 | thread.last_edit_time = timezone.now() | |
105 | thread.save() |
|
112 | thread.save() | |
106 |
|
113 | |||
107 | post.delete() |
|
114 | post.delete() | |
108 |
|
115 | |||
|
116 | logger.info('Deleted post #%d' % post_id) | |||
|
117 | ||||
109 | def delete_posts_by_ip(self, ip): |
|
118 | def delete_posts_by_ip(self, ip): | |
110 | """ |
|
119 | """ | |
111 | Deletes all posts of the author with same IP |
|
120 | Deletes all posts of the author with same IP | |
112 | """ |
|
121 | """ | |
113 |
|
122 | |||
114 | posts = self.filter(poster_ip=ip) |
|
123 | posts = self.filter(poster_ip=ip) | |
115 | map(self.delete_post, posts) |
|
124 | map(self.delete_post, posts) | |
116 |
|
125 | |||
117 | # TODO Move this method to thread manager |
|
126 | # TODO Move this method to thread manager | |
118 | def _delete_old_threads(self): |
|
127 | def _delete_old_threads(self): | |
119 | """ |
|
128 | """ | |
120 | Preserves maximum thread count. If there are too many threads, |
|
129 | Preserves maximum thread count. If there are too many threads, | |
121 | archive the old ones. |
|
130 | archive the old ones. | |
122 | """ |
|
131 | """ | |
123 |
|
132 | |||
124 | threads = Thread.objects.filter(archived=False).order_by('-bump_time') |
|
133 | threads = Thread.objects.filter(archived=False).order_by('-bump_time') | |
125 | thread_count = threads.count() |
|
134 | thread_count = threads.count() | |
126 |
|
135 | |||
127 | if thread_count > settings.MAX_THREAD_COUNT: |
|
136 | if thread_count > settings.MAX_THREAD_COUNT: | |
128 | num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT |
|
137 | num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT | |
129 | old_threads = threads[thread_count - num_threads_to_delete:] |
|
138 | old_threads = threads[thread_count - num_threads_to_delete:] | |
130 |
|
139 | |||
131 | for thread in old_threads: |
|
140 | for thread in old_threads: | |
132 | thread.archived = True |
|
141 | thread.archived = True | |
133 | thread.last_edit_time = timezone.now() |
|
142 | thread.last_edit_time = timezone.now() | |
134 | thread.save() |
|
143 | thread.save() | |
135 |
|
144 | |||
|
145 | logger.info('Archived %d old threads' % num_threads_to_delete) | |||
|
146 | ||||
136 | def connect_replies(self, post): |
|
147 | def connect_replies(self, post): | |
137 | """ |
|
148 | """ | |
138 | Connects replies to a post to show them as a reflink map |
|
149 | Connects replies to a post to show them as a reflink map | |
139 | """ |
|
150 | """ | |
140 |
|
151 | |||
141 | for reply_number in re.finditer(REGEX_REPLY, post.text.raw): |
|
152 | for reply_number in re.finditer(REGEX_REPLY, post.text.raw): | |
142 | post_id = reply_number.group(1) |
|
153 | post_id = reply_number.group(1) | |
143 | ref_post = self.filter(id=post_id) |
|
154 | ref_post = self.filter(id=post_id) | |
144 | if ref_post.count() > 0: |
|
155 | if ref_post.count() > 0: | |
145 | referenced_post = ref_post[0] |
|
156 | referenced_post = ref_post[0] | |
146 | referenced_post.referenced_posts.add(post) |
|
157 | referenced_post.referenced_posts.add(post) | |
147 | referenced_post.last_edit_time = post.pub_time |
|
158 | referenced_post.last_edit_time = post.pub_time | |
148 | referenced_post.save() |
|
159 | referenced_post.save() | |
149 |
|
160 | |||
150 | referenced_thread = referenced_post.get_thread() |
|
161 | referenced_thread = referenced_post.get_thread() | |
151 | referenced_thread.last_edit_time = post.pub_time |
|
162 | referenced_thread.last_edit_time = post.pub_time | |
152 | referenced_thread.save() |
|
163 | referenced_thread.save() | |
153 |
|
164 | |||
154 | def get_posts_per_day(self): |
|
165 | def get_posts_per_day(self): | |
155 | """ |
|
166 | """ | |
156 | Gets average count of posts per day for the last 7 days |
|
167 | Gets average count of posts per day for the last 7 days | |
157 | """ |
|
168 | """ | |
158 |
|
169 | |||
159 | today = date.today() |
|
170 | today = date.today() | |
160 | ppd = cache.get(CACHE_KEY_PPD + str(today)) |
|
171 | ppd = cache.get(CACHE_KEY_PPD + str(today)) | |
161 | if ppd: |
|
172 | if ppd: | |
162 | return ppd |
|
173 | return ppd | |
163 |
|
174 | |||
164 | posts_per_days = [] |
|
175 | posts_per_days = [] | |
165 | for i in POSTS_PER_DAY_RANGE: |
|
176 | for i in POSTS_PER_DAY_RANGE: | |
166 | day_end = today - timedelta(i + 1) |
|
177 | day_end = today - timedelta(i + 1) | |
167 | day_start = today - timedelta(i + 2) |
|
178 | day_start = today - timedelta(i + 2) | |
168 |
|
179 | |||
169 | day_time_start = timezone.make_aware(datetime.combine( |
|
180 | day_time_start = timezone.make_aware(datetime.combine( | |
170 | day_start, dtime()), timezone.get_current_timezone()) |
|
181 | day_start, dtime()), timezone.get_current_timezone()) | |
171 | day_time_end = timezone.make_aware(datetime.combine( |
|
182 | day_time_end = timezone.make_aware(datetime.combine( | |
172 | day_end, dtime()), timezone.get_current_timezone()) |
|
183 | day_end, dtime()), timezone.get_current_timezone()) | |
173 |
|
184 | |||
174 | posts_per_days.append(float(self.filter( |
|
185 | posts_per_days.append(float(self.filter( | |
175 | pub_time__lte=day_time_end, |
|
186 | pub_time__lte=day_time_end, | |
176 | pub_time__gte=day_time_start).count())) |
|
187 | pub_time__gte=day_time_start).count())) | |
177 |
|
188 | |||
178 | ppd = (sum(posts_per_day for posts_per_day in posts_per_days) / |
|
189 | ppd = (sum(posts_per_day for posts_per_day in posts_per_days) / | |
179 | len(posts_per_days)) |
|
190 | len(posts_per_days)) | |
180 | cache.set(CACHE_KEY_PPD + str(today), ppd) |
|
191 | cache.set(CACHE_KEY_PPD + str(today), ppd) | |
181 | return ppd |
|
192 | return ppd | |
182 |
|
193 | |||
183 |
|
194 | |||
184 | class Post(models.Model): |
|
195 | class Post(models.Model): | |
185 | """A post is a message.""" |
|
196 | """A post is a message.""" | |
186 |
|
197 | |||
187 | objects = PostManager() |
|
198 | objects = PostManager() | |
188 |
|
199 | |||
189 | class Meta: |
|
200 | class Meta: | |
190 | app_label = APP_LABEL_BOARDS |
|
201 | app_label = APP_LABEL_BOARDS | |
191 |
|
202 | |||
192 | # TODO Save original file name to some field |
|
203 | # TODO Save original file name to some field | |
193 | def _update_image_filename(self, filename): |
|
204 | def _update_image_filename(self, filename): | |
194 | """ |
|
205 | """ | |
195 | Gets unique image filename |
|
206 | Gets unique image filename | |
196 | """ |
|
207 | """ | |
197 |
|
208 | |||
198 | path = IMAGES_DIRECTORY |
|
209 | path = IMAGES_DIRECTORY | |
199 | new_name = str(int(time.mktime(time.gmtime()))) |
|
210 | new_name = str(int(time.mktime(time.gmtime()))) | |
200 | new_name += str(int(random() * 1000)) |
|
211 | new_name += str(int(random() * 1000)) | |
201 | new_name += FILE_EXTENSION_DELIMITER |
|
212 | new_name += FILE_EXTENSION_DELIMITER | |
202 | new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0] |
|
213 | new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0] | |
203 |
|
214 | |||
204 | return os.path.join(path, new_name) |
|
215 | return os.path.join(path, new_name) | |
205 |
|
216 | |||
206 | title = models.CharField(max_length=TITLE_MAX_LENGTH) |
|
217 | title = models.CharField(max_length=TITLE_MAX_LENGTH) | |
207 | pub_time = models.DateTimeField() |
|
218 | pub_time = models.DateTimeField() | |
208 | text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE, |
|
219 | text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE, | |
209 | escape_html=False) |
|
220 | escape_html=False) | |
210 |
|
221 | |||
211 | image_width = models.IntegerField(default=0) |
|
222 | image_width = models.IntegerField(default=0) | |
212 | image_height = models.IntegerField(default=0) |
|
223 | image_height = models.IntegerField(default=0) | |
213 |
|
224 | |||
214 | image_pre_width = models.IntegerField(default=0) |
|
225 | image_pre_width = models.IntegerField(default=0) | |
215 | image_pre_height = models.IntegerField(default=0) |
|
226 | image_pre_height = models.IntegerField(default=0) | |
216 |
|
227 | |||
217 | image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename, |
|
228 | image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename, | |
218 | blank=True, sizes=(IMAGE_THUMB_SIZE,), |
|
229 | blank=True, sizes=(IMAGE_THUMB_SIZE,), | |
219 | width_field='image_width', |
|
230 | width_field='image_width', | |
220 | height_field='image_height', |
|
231 | height_field='image_height', | |
221 | preview_width_field='image_pre_width', |
|
232 | preview_width_field='image_pre_width', | |
222 | preview_height_field='image_pre_height') |
|
233 | preview_height_field='image_pre_height') | |
223 | image_hash = models.CharField(max_length=36) |
|
234 | image_hash = models.CharField(max_length=36) | |
224 |
|
235 | |||
225 | poster_ip = models.GenericIPAddressField() |
|
236 | poster_ip = models.GenericIPAddressField() | |
226 | poster_user_agent = models.TextField() |
|
237 | poster_user_agent = models.TextField() | |
227 |
|
238 | |||
228 | thread = models.ForeignKey('Post', null=True, default=None) |
|
239 | thread = models.ForeignKey('Post', null=True, default=None) | |
229 | thread_new = models.ForeignKey('Thread', null=True, default=None) |
|
240 | thread_new = models.ForeignKey('Thread', null=True, default=None) | |
230 | last_edit_time = models.DateTimeField() |
|
241 | last_edit_time = models.DateTimeField() | |
231 | user = models.ForeignKey('User', null=True, default=None) |
|
242 | user = models.ForeignKey('User', null=True, default=None) | |
232 |
|
243 | |||
233 | referenced_posts = models.ManyToManyField('Post', symmetrical=False, |
|
244 | referenced_posts = models.ManyToManyField('Post', symmetrical=False, | |
234 | null=True, |
|
245 | null=True, | |
235 | blank=True, related_name='rfp+') |
|
246 | blank=True, related_name='rfp+') | |
236 |
|
247 | |||
237 | def __unicode__(self): |
|
248 | def __unicode__(self): | |
238 | return '#' + str(self.id) + ' ' + self.title + ' (' + \ |
|
249 | return '#' + str(self.id) + ' ' + self.title + ' (' + \ | |
239 | self.text.raw[:50] + ')' |
|
250 | self.text.raw[:50] + ')' | |
240 |
|
251 | |||
241 | def get_title(self): |
|
252 | def get_title(self): | |
242 | """ |
|
253 | """ | |
243 | Gets original post title or part of its text. |
|
254 | Gets original post title or part of its text. | |
244 | """ |
|
255 | """ | |
245 |
|
256 | |||
246 | title = self.title |
|
257 | title = self.title | |
247 | if not title: |
|
258 | if not title: | |
248 | title = self.text.rendered |
|
259 | title = self.text.rendered | |
249 |
|
260 | |||
250 | return title |
|
261 | return title | |
251 |
|
262 | |||
252 | def get_sorted_referenced_posts(self): |
|
263 | def get_sorted_referenced_posts(self): | |
253 | return self.referenced_posts.order_by('id') |
|
264 | return self.referenced_posts.order_by('id') | |
254 |
|
265 | |||
255 | def is_referenced(self): |
|
266 | def is_referenced(self): | |
256 | return self.referenced_posts.exists() |
|
267 | return self.referenced_posts.exists() | |
257 |
|
268 | |||
258 | def is_opening(self): |
|
269 | def is_opening(self): | |
259 | """ |
|
270 | """ | |
260 | Checks if this is an opening post or just a reply. |
|
271 | Checks if this is an opening post or just a reply. | |
261 | """ |
|
272 | """ | |
262 |
|
273 | |||
263 | return self.get_thread().get_opening_post_id() == self.id |
|
274 | return self.get_thread().get_opening_post_id() == self.id | |
264 |
|
275 | |||
265 | def save(self, *args, **kwargs): |
|
276 | def save(self, *args, **kwargs): | |
266 | """ |
|
277 | """ | |
267 | Saves the model and computes the image hash for deduplication purposes. |
|
278 | Saves the model and computes the image hash for deduplication purposes. | |
268 | """ |
|
279 | """ | |
269 |
|
280 | |||
270 | if not self.pk and self.image: |
|
281 | if not self.pk and self.image: | |
271 | md5 = hashlib.md5() |
|
282 | md5 = hashlib.md5() | |
272 | for chunk in self.image.chunks(): |
|
283 | for chunk in self.image.chunks(): | |
273 | md5.update(chunk) |
|
284 | md5.update(chunk) | |
274 | self.image_hash = md5.hexdigest() |
|
285 | self.image_hash = md5.hexdigest() | |
275 | super(Post, self).save(*args, **kwargs) |
|
286 | super(Post, self).save(*args, **kwargs) | |
276 |
|
287 | |||
277 | @transaction.atomic |
|
288 | @transaction.atomic | |
278 | def add_tag(self, tag): |
|
289 | def add_tag(self, tag): | |
279 | edit_time = timezone.now() |
|
290 | edit_time = timezone.now() | |
280 |
|
291 | |||
281 | thread = self.get_thread() |
|
292 | thread = self.get_thread() | |
282 | thread.add_tag(tag) |
|
293 | thread.add_tag(tag) | |
283 | self.last_edit_time = edit_time |
|
294 | self.last_edit_time = edit_time | |
284 | self.save() |
|
295 | self.save() | |
285 |
|
296 | |||
286 | thread.last_edit_time = edit_time |
|
297 | thread.last_edit_time = edit_time | |
287 | thread.save() |
|
298 | thread.save() | |
288 |
|
299 | |||
289 | @transaction.atomic |
|
300 | @transaction.atomic | |
290 | def remove_tag(self, tag): |
|
301 | def remove_tag(self, tag): | |
291 | edit_time = timezone.now() |
|
302 | edit_time = timezone.now() | |
292 |
|
303 | |||
293 | thread = self.get_thread() |
|
304 | thread = self.get_thread() | |
294 | thread.remove_tag(tag) |
|
305 | thread.remove_tag(tag) | |
295 | self.last_edit_time = edit_time |
|
306 | self.last_edit_time = edit_time | |
296 | self.save() |
|
307 | self.save() | |
297 |
|
308 | |||
298 | thread.last_edit_time = edit_time |
|
309 | thread.last_edit_time = edit_time | |
299 | thread.save() |
|
310 | thread.save() | |
300 |
|
311 | |||
301 | def get_url(self, thread=None): |
|
312 | def get_url(self, thread=None): | |
302 | """ |
|
313 | """ | |
303 | Gets full url to the post. |
|
314 | Gets full url to the post. | |
304 | """ |
|
315 | """ | |
305 |
|
316 | |||
306 | cache_key = CACHE_KEY_POST_URL + str(self.id) |
|
317 | cache_key = CACHE_KEY_POST_URL + str(self.id) | |
307 | link = cache.get(cache_key) |
|
318 | link = cache.get(cache_key) | |
308 |
|
319 | |||
309 | if not link: |
|
320 | if not link: | |
310 | if not thread: |
|
321 | if not thread: | |
311 | thread = self.get_thread() |
|
322 | thread = self.get_thread() | |
312 |
|
323 | |||
313 | opening_id = thread.get_opening_post_id() |
|
324 | opening_id = thread.get_opening_post_id() | |
314 |
|
325 | |||
315 | if self.id != opening_id: |
|
326 | if self.id != opening_id: | |
316 | link = reverse('thread', kwargs={ |
|
327 | link = reverse('thread', kwargs={ | |
317 | 'post_id': opening_id}) + '#' + str(self.id) |
|
328 | 'post_id': opening_id}) + '#' + str(self.id) | |
318 | else: |
|
329 | else: | |
319 | link = reverse('thread', kwargs={'post_id': self.id}) |
|
330 | link = reverse('thread', kwargs={'post_id': self.id}) | |
320 |
|
331 | |||
321 | cache.set(cache_key, link) |
|
332 | cache.set(cache_key, link) | |
322 |
|
333 | |||
323 | return link |
|
334 | return link | |
324 |
|
335 | |||
325 | def get_thread(self): |
|
336 | def get_thread(self): | |
326 | """ |
|
337 | """ | |
327 | Gets post's thread. |
|
338 | Gets post's thread. | |
328 | """ |
|
339 | """ | |
329 |
|
340 | |||
330 | return self.thread_new |
|
341 | return self.thread_new | |
331 |
|
342 | |||
332 |
|
343 | |||
333 | class Thread(models.Model): |
|
344 | class Thread(models.Model): | |
334 |
|
345 | |||
335 | class Meta: |
|
346 | class Meta: | |
336 | app_label = APP_LABEL_BOARDS |
|
347 | app_label = APP_LABEL_BOARDS | |
337 |
|
348 | |||
338 | tags = models.ManyToManyField('Tag') |
|
349 | tags = models.ManyToManyField('Tag') | |
339 | bump_time = models.DateTimeField() |
|
350 | bump_time = models.DateTimeField() | |
340 | last_edit_time = models.DateTimeField() |
|
351 | last_edit_time = models.DateTimeField() | |
341 | replies = models.ManyToManyField('Post', symmetrical=False, null=True, |
|
352 | replies = models.ManyToManyField('Post', symmetrical=False, null=True, | |
342 | blank=True, related_name='tre+') |
|
353 | blank=True, related_name='tre+') | |
343 | archived = models.BooleanField(default=False) |
|
354 | archived = models.BooleanField(default=False) | |
344 |
|
355 | |||
345 | def get_tags(self): |
|
356 | def get_tags(self): | |
346 | """ |
|
357 | """ | |
347 | Gets a sorted tag list. |
|
358 | Gets a sorted tag list. | |
348 | """ |
|
359 | """ | |
349 |
|
360 | |||
350 | return self.tags.order_by('name') |
|
361 | return self.tags.order_by('name') | |
351 |
|
362 | |||
352 | def bump(self): |
|
363 | def bump(self): | |
353 | """ |
|
364 | """ | |
354 | Bumps (moves to up) thread if possible. |
|
365 | Bumps (moves to up) thread if possible. | |
355 | """ |
|
366 | """ | |
356 |
|
367 | |||
357 | if self.can_bump(): |
|
368 | if self.can_bump(): | |
358 | self.bump_time = timezone.now() |
|
369 | self.bump_time = timezone.now() | |
359 |
|
370 | |||
|
371 | logger.info('Bumped thread %d' % self.id) | |||
|
372 | ||||
360 | def get_reply_count(self): |
|
373 | def get_reply_count(self): | |
361 | return self.replies.count() |
|
374 | return self.replies.count() | |
362 |
|
375 | |||
363 | def get_images_count(self): |
|
376 | def get_images_count(self): | |
364 | return self.replies.filter(image_width__gt=0).count() |
|
377 | return self.replies.filter(image_width__gt=0).count() | |
365 |
|
378 | |||
366 | def can_bump(self): |
|
379 | def can_bump(self): | |
367 | """ |
|
380 | """ | |
368 | Checks if the thread can be bumped by replying to it. |
|
381 | Checks if the thread can be bumped by replying to it. | |
369 | """ |
|
382 | """ | |
370 |
|
383 | |||
371 | if self.archived: |
|
384 | if self.archived: | |
372 | return False |
|
385 | return False | |
373 |
|
386 | |||
374 | post_count = self.get_reply_count() |
|
387 | post_count = self.get_reply_count() | |
375 |
|
388 | |||
376 | return post_count < settings.MAX_POSTS_PER_THREAD |
|
389 | return post_count < settings.MAX_POSTS_PER_THREAD | |
377 |
|
390 | |||
378 | def delete_with_posts(self): |
|
391 | def delete_with_posts(self): | |
379 | """ |
|
392 | """ | |
380 | Completely deletes thread and all its posts |
|
393 | Completely deletes thread and all its posts | |
381 | """ |
|
394 | """ | |
382 |
|
395 | |||
383 | if self.replies.exists(): |
|
396 | if self.replies.exists(): | |
384 | self.replies.all().delete() |
|
397 | self.replies.all().delete() | |
385 |
|
398 | |||
386 | self.delete() |
|
399 | self.delete() | |
387 |
|
400 | |||
388 | def get_last_replies(self): |
|
401 | def get_last_replies(self): | |
389 | """ |
|
402 | """ | |
390 | Gets several last replies, not including opening post |
|
403 | Gets several last replies, not including opening post | |
391 | """ |
|
404 | """ | |
392 |
|
405 | |||
393 | if settings.LAST_REPLIES_COUNT > 0: |
|
406 | if settings.LAST_REPLIES_COUNT > 0: | |
394 | reply_count = self.get_reply_count() |
|
407 | reply_count = self.get_reply_count() | |
395 |
|
408 | |||
396 | if reply_count > 0: |
|
409 | if reply_count > 0: | |
397 | reply_count_to_show = min(settings.LAST_REPLIES_COUNT, |
|
410 | reply_count_to_show = min(settings.LAST_REPLIES_COUNT, | |
398 | reply_count - 1) |
|
411 | reply_count - 1) | |
399 | last_replies = self.replies.order_by( |
|
412 | last_replies = self.replies.order_by( | |
400 | 'pub_time')[reply_count - reply_count_to_show:] |
|
413 | 'pub_time')[reply_count - reply_count_to_show:] | |
401 |
|
414 | |||
402 | return last_replies |
|
415 | return last_replies | |
403 |
|
416 | |||
404 | def get_skipped_replies_count(self): |
|
417 | def get_skipped_replies_count(self): | |
405 | """ |
|
418 | """ | |
406 | Gets number of posts between opening post and last replies. |
|
419 | Gets number of posts between opening post and last replies. | |
407 | """ |
|
420 | """ | |
408 |
|
421 | |||
409 | last_replies = self.get_last_replies() |
|
422 | last_replies = self.get_last_replies() | |
410 | return self.get_reply_count() - len(last_replies) - 1 |
|
423 | return self.get_reply_count() - len(last_replies) - 1 | |
411 |
|
424 | |||
412 | def get_replies(self): |
|
425 | def get_replies(self): | |
413 | """ |
|
426 | """ | |
414 | Gets sorted thread posts |
|
427 | Gets sorted thread posts | |
415 | """ |
|
428 | """ | |
416 |
|
429 | |||
417 | return self.replies.all().order_by('pub_time') |
|
430 | return self.replies.all().order_by('pub_time') | |
418 |
|
431 | |||
419 | def add_tag(self, tag): |
|
432 | def add_tag(self, tag): | |
420 | """ |
|
433 | """ | |
421 | Connects thread to a tag and tag to a thread |
|
434 | Connects thread to a tag and tag to a thread | |
422 | """ |
|
435 | """ | |
423 |
|
436 | |||
424 | self.tags.add(tag) |
|
437 | self.tags.add(tag) | |
425 | tag.threads.add(self) |
|
438 | tag.threads.add(self) | |
426 |
|
439 | |||
427 | def remove_tag(self, tag): |
|
440 | def remove_tag(self, tag): | |
428 | self.tags.remove(tag) |
|
441 | self.tags.remove(tag) | |
429 | tag.threads.remove(self) |
|
442 | tag.threads.remove(self) | |
430 |
|
443 | |||
431 | def get_opening_post(self): |
|
444 | def get_opening_post(self): | |
432 | """ |
|
445 | """ | |
433 | Gets the first post of the thread |
|
446 | Gets the first post of the thread | |
434 | """ |
|
447 | """ | |
435 |
|
448 | |||
436 | opening_post = self.get_replies()[0] |
|
449 | opening_post = self.get_replies()[0] | |
437 |
|
450 | |||
438 | return opening_post |
|
451 | return opening_post | |
439 |
|
452 | |||
440 | def get_opening_post_id(self): |
|
453 | def get_opening_post_id(self): | |
441 | """ |
|
454 | """ | |
442 | Gets ID of the first thread post. |
|
455 | Gets ID of the first thread post. | |
443 | """ |
|
456 | """ | |
444 |
|
457 | |||
445 | cache_key = CACHE_KEY_OPENING_POST + str(self.id) |
|
458 | cache_key = CACHE_KEY_OPENING_POST + str(self.id) | |
446 | opening_post_id = cache.get(cache_key) |
|
459 | opening_post_id = cache.get(cache_key) | |
447 | if not opening_post_id: |
|
460 | if not opening_post_id: | |
448 | opening_post_id = self.get_opening_post().id |
|
461 | opening_post_id = self.get_opening_post().id | |
449 | cache.set(cache_key, opening_post_id) |
|
462 | cache.set(cache_key, opening_post_id) | |
450 |
|
463 | |||
451 | return opening_post_id |
|
464 | return opening_post_id | |
452 |
|
465 | |||
453 | def __unicode__(self): |
|
466 | def __unicode__(self): | |
454 | return str(self.id) |
|
467 | return str(self.id) | |
455 |
|
468 | |||
456 | def get_pub_time(self): |
|
469 | def get_pub_time(self): | |
457 | """ |
|
470 | """ | |
458 | Gets opening post's pub time because thread does not have its own one. |
|
471 | Gets opening post's pub time because thread does not have its own one. | |
459 | """ |
|
472 | """ | |
460 |
|
473 | |||
461 | return self.get_opening_post().pub_time |
|
474 | return self.get_opening_post().pub_time |
@@ -1,228 +1,237 b'' | |||||
1 | from datetime import datetime |
|
1 | from datetime import datetime | |
2 | import json |
|
2 | import json | |
|
3 | import logging | |||
3 | from django.db import transaction |
|
4 | from django.db import transaction | |
4 | from django.http import HttpResponse |
|
5 | from django.http import HttpResponse | |
5 | from django.shortcuts import get_object_or_404, render |
|
6 | from django.shortcuts import get_object_or_404, render | |
6 | from django.template import RequestContext |
|
7 | from django.template import RequestContext | |
7 | from django.utils import timezone |
|
8 | from django.utils import timezone | |
8 | from django.core import serializers |
|
9 | from django.core import serializers | |
9 |
|
10 | |||
10 | from boards.forms import PostForm, PlainErrorList |
|
11 | from boards.forms import PostForm, PlainErrorList | |
11 | from boards.models import Post, Thread, Tag |
|
12 | from boards.models import Post, Thread, Tag | |
12 | from boards.utils import datetime_to_epoch |
|
13 | from boards.utils import datetime_to_epoch | |
13 | from boards.views.thread import ThreadView |
|
14 | from boards.views.thread import ThreadView | |
14 |
|
15 | |||
15 | __author__ = 'neko259' |
|
16 | __author__ = 'neko259' | |
16 |
|
17 | |||
17 | PARAMETER_TRUNCATED = 'truncated' |
|
18 | PARAMETER_TRUNCATED = 'truncated' | |
18 | PARAMETER_TAG = 'tag' |
|
19 | PARAMETER_TAG = 'tag' | |
19 | PARAMETER_OFFSET = 'offset' |
|
20 | PARAMETER_OFFSET = 'offset' | |
20 | PARAMETER_DIFF_TYPE = 'type' |
|
21 | PARAMETER_DIFF_TYPE = 'type' | |
21 |
|
22 | |||
22 | DIFF_TYPE_HTML = 'html' |
|
23 | DIFF_TYPE_HTML = 'html' | |
23 | DIFF_TYPE_JSON = 'json' |
|
24 | DIFF_TYPE_JSON = 'json' | |
24 |
|
25 | |||
25 | STATUS_OK = 'ok' |
|
26 | STATUS_OK = 'ok' | |
26 | STATUS_ERROR = 'error' |
|
27 | STATUS_ERROR = 'error' | |
27 |
|
28 | |||
|
29 | logger = logging.getLogger(__name__) | |||
|
30 | ||||
28 |
|
31 | |||
29 | @transaction.atomic |
|
32 | @transaction.atomic | |
30 | def api_get_threaddiff(request, thread_id, last_update_time): |
|
33 | def api_get_threaddiff(request, thread_id, last_update_time): | |
31 | """ |
|
34 | """ | |
32 | Gets posts that were changed or added since time |
|
35 | Gets posts that were changed or added since time | |
33 | """ |
|
36 | """ | |
34 |
|
37 | |||
|
38 | logger.info('Getting thread diff since %s' % last_update_time) | |||
|
39 | ||||
35 | thread = get_object_or_404(Post, id=thread_id).thread_new |
|
40 | thread = get_object_or_404(Post, id=thread_id).thread_new | |
36 |
|
41 | |||
37 | filter_time = datetime.fromtimestamp(float(last_update_time) / 1000000, |
|
42 | filter_time = datetime.fromtimestamp(float(last_update_time) / 1000000, | |
38 | timezone.get_current_timezone()) |
|
43 | timezone.get_current_timezone()) | |
39 |
|
44 | |||
40 | json_data = { |
|
45 | json_data = { | |
41 | 'added': [], |
|
46 | 'added': [], | |
42 | 'updated': [], |
|
47 | 'updated': [], | |
43 | 'last_update': None, |
|
48 | 'last_update': None, | |
44 | } |
|
49 | } | |
45 | added_posts = Post.objects.filter(thread_new=thread, |
|
50 | added_posts = Post.objects.filter(thread_new=thread, | |
46 | pub_time__gt=filter_time) \ |
|
51 | pub_time__gt=filter_time) \ | |
47 | .order_by('pub_time') |
|
52 | .order_by('pub_time') | |
48 | updated_posts = Post.objects.filter(thread_new=thread, |
|
53 | updated_posts = Post.objects.filter(thread_new=thread, | |
49 | pub_time__lte=filter_time, |
|
54 | pub_time__lte=filter_time, | |
50 | last_edit_time__gt=filter_time) |
|
55 | last_edit_time__gt=filter_time) | |
51 |
|
56 | |||
52 | diff_type = DIFF_TYPE_HTML |
|
57 | diff_type = DIFF_TYPE_HTML | |
53 | if PARAMETER_DIFF_TYPE in request.GET: |
|
58 | if PARAMETER_DIFF_TYPE in request.GET: | |
54 | diff_type = request.GET[PARAMETER_DIFF_TYPE] |
|
59 | diff_type = request.GET[PARAMETER_DIFF_TYPE] | |
55 |
|
60 | |||
56 | for post in added_posts: |
|
61 | for post in added_posts: | |
57 | json_data['added'].append(_get_post_data(post.id, diff_type, request)) |
|
62 | json_data['added'].append(_get_post_data(post.id, diff_type, request)) | |
58 | for post in updated_posts: |
|
63 | for post in updated_posts: | |
59 | json_data['updated'].append(_get_post_data(post.id, diff_type, request)) |
|
64 | json_data['updated'].append(_get_post_data(post.id, diff_type, request)) | |
60 | json_data['last_update'] = datetime_to_epoch(thread.last_edit_time) |
|
65 | json_data['last_update'] = datetime_to_epoch(thread.last_edit_time) | |
61 |
|
66 | |||
62 | return HttpResponse(content=json.dumps(json_data)) |
|
67 | return HttpResponse(content=json.dumps(json_data)) | |
63 |
|
68 | |||
64 |
|
69 | |||
65 | def api_add_post(request, opening_post_id): |
|
70 | def api_add_post(request, opening_post_id): | |
66 | """ |
|
71 | """ | |
67 | Adds a post and return the JSON response for it |
|
72 | Adds a post and return the JSON response for it | |
68 | """ |
|
73 | """ | |
69 |
|
74 | |||
70 | opening_post = get_object_or_404(Post, id=opening_post_id) |
|
75 | opening_post = get_object_or_404(Post, id=opening_post_id) | |
71 |
|
76 | |||
72 | status = STATUS_OK |
|
77 | status = STATUS_OK | |
73 | errors = [] |
|
78 | errors = [] | |
74 |
|
79 | |||
75 | if request.method == 'POST': |
|
80 | if request.method == 'POST': | |
76 | form = PostForm(request.POST, request.FILES, |
|
81 | form = PostForm(request.POST, request.FILES, | |
77 | error_class=PlainErrorList) |
|
82 | error_class=PlainErrorList) | |
78 | form.session = request.session |
|
83 | form.session = request.session | |
79 |
|
84 | |||
80 | if form.need_to_ban: |
|
85 | if form.need_to_ban: | |
81 | # Ban user because he is suspected to be a bot |
|
86 | # Ban user because he is suspected to be a bot | |
82 | # _ban_current_user(request) |
|
87 | # _ban_current_user(request) | |
83 | status = STATUS_ERROR |
|
88 | status = STATUS_ERROR | |
84 | if form.is_valid(): |
|
89 | if form.is_valid(): | |
85 | result = ThreadView().new_post(request, form, opening_post, |
|
90 | result = ThreadView().new_post(request, form, opening_post, | |
86 | html_response=False) |
|
91 | html_response=False) | |
87 | if not result: |
|
92 | if not result: | |
88 | status = STATUS_ERROR |
|
93 | status = STATUS_ERROR | |
89 | else: |
|
94 | else: | |
90 | status = STATUS_ERROR |
|
95 | status = STATUS_ERROR | |
91 | errors = form.as_json_errors() |
|
96 | errors = form.as_json_errors() | |
92 |
|
97 | |||
93 | response = { |
|
98 | response = { | |
94 | 'status': status, |
|
99 | 'status': status, | |
95 | 'errors': errors, |
|
100 | 'errors': errors, | |
96 | } |
|
101 | } | |
97 |
|
102 | |||
|
103 | logger.info('Added post via api. Status: %s' % status) | |||
|
104 | ||||
98 | return HttpResponse(content=json.dumps(response)) |
|
105 | return HttpResponse(content=json.dumps(response)) | |
99 |
|
106 | |||
100 |
|
107 | |||
101 | def get_post(request, post_id): |
|
108 | def get_post(request, post_id): | |
102 | """ |
|
109 | """ | |
103 | Gets the html of a post. Used for popups. Post can be truncated if used |
|
110 | Gets the html of a post. Used for popups. Post can be truncated if used | |
104 | in threads list with 'truncated' get parameter. |
|
111 | in threads list with 'truncated' get parameter. | |
105 | """ |
|
112 | """ | |
106 |
|
113 | |||
|
114 | logger.info('Getting post #%s' % post_id) | |||
|
115 | ||||
107 | post = get_object_or_404(Post, id=post_id) |
|
116 | post = get_object_or_404(Post, id=post_id) | |
108 |
|
117 | |||
109 | context = RequestContext(request) |
|
118 | context = RequestContext(request) | |
110 | context['post'] = post |
|
119 | context['post'] = post | |
111 | if PARAMETER_TRUNCATED in request.GET: |
|
120 | if PARAMETER_TRUNCATED in request.GET: | |
112 | context[PARAMETER_TRUNCATED] = True |
|
121 | context[PARAMETER_TRUNCATED] = True | |
113 |
|
122 | |||
114 | return render(request, 'boards/api_post.html', context) |
|
123 | return render(request, 'boards/api_post.html', context) | |
115 |
|
124 | |||
116 |
|
125 | |||
117 | # TODO Test this |
|
126 | # TODO Test this | |
118 | def api_get_threads(request, count): |
|
127 | def api_get_threads(request, count): | |
119 | """ |
|
128 | """ | |
120 | Gets the JSON thread opening posts list. |
|
129 | Gets the JSON thread opening posts list. | |
121 | Parameters that can be used for filtering: |
|
130 | Parameters that can be used for filtering: | |
122 | tag, offset (from which thread to get results) |
|
131 | tag, offset (from which thread to get results) | |
123 | """ |
|
132 | """ | |
124 |
|
133 | |||
125 | if PARAMETER_TAG in request.GET: |
|
134 | if PARAMETER_TAG in request.GET: | |
126 | tag_name = request.GET[PARAMETER_TAG] |
|
135 | tag_name = request.GET[PARAMETER_TAG] | |
127 | if tag_name is not None: |
|
136 | if tag_name is not None: | |
128 | tag = get_object_or_404(Tag, name=tag_name) |
|
137 | tag = get_object_or_404(Tag, name=tag_name) | |
129 | threads = tag.threads.filter(archived=False) |
|
138 | threads = tag.threads.filter(archived=False) | |
130 | else: |
|
139 | else: | |
131 | threads = Thread.objects.filter(archived=False) |
|
140 | threads = Thread.objects.filter(archived=False) | |
132 |
|
141 | |||
133 | if PARAMETER_OFFSET in request.GET: |
|
142 | if PARAMETER_OFFSET in request.GET: | |
134 | offset = request.GET[PARAMETER_OFFSET] |
|
143 | offset = request.GET[PARAMETER_OFFSET] | |
135 | offset = int(offset) if offset is not None else 0 |
|
144 | offset = int(offset) if offset is not None else 0 | |
136 | else: |
|
145 | else: | |
137 | offset = 0 |
|
146 | offset = 0 | |
138 |
|
147 | |||
139 | threads = threads.order_by('-bump_time') |
|
148 | threads = threads.order_by('-bump_time') | |
140 | threads = threads[offset:offset + int(count)] |
|
149 | threads = threads[offset:offset + int(count)] | |
141 |
|
150 | |||
142 | opening_posts = [] |
|
151 | opening_posts = [] | |
143 | for thread in threads: |
|
152 | for thread in threads: | |
144 | opening_post = thread.get_opening_post() |
|
153 | opening_post = thread.get_opening_post() | |
145 |
|
154 | |||
146 | # TODO Add tags, replies and images count |
|
155 | # TODO Add tags, replies and images count | |
147 | opening_posts.append(_get_post_data(opening_post.id, |
|
156 | opening_posts.append(_get_post_data(opening_post.id, | |
148 | include_last_update=True)) |
|
157 | include_last_update=True)) | |
149 |
|
158 | |||
150 | return HttpResponse(content=json.dumps(opening_posts)) |
|
159 | return HttpResponse(content=json.dumps(opening_posts)) | |
151 |
|
160 | |||
152 |
|
161 | |||
153 | # TODO Test this |
|
162 | # TODO Test this | |
154 | def api_get_tags(request): |
|
163 | def api_get_tags(request): | |
155 | """ |
|
164 | """ | |
156 | Gets all tags or user tags. |
|
165 | Gets all tags or user tags. | |
157 | """ |
|
166 | """ | |
158 |
|
167 | |||
159 | # TODO Get favorite tags for the given user ID |
|
168 | # TODO Get favorite tags for the given user ID | |
160 |
|
169 | |||
161 | tags = Tag.objects.get_not_empty_tags() |
|
170 | tags = Tag.objects.get_not_empty_tags() | |
162 | tag_names = [] |
|
171 | tag_names = [] | |
163 | for tag in tags: |
|
172 | for tag in tags: | |
164 | tag_names.append(tag.name) |
|
173 | tag_names.append(tag.name) | |
165 |
|
174 | |||
166 | return HttpResponse(content=json.dumps(tag_names)) |
|
175 | return HttpResponse(content=json.dumps(tag_names)) | |
167 |
|
176 | |||
168 |
|
177 | |||
169 | # TODO The result can be cached by the thread last update time |
|
178 | # TODO The result can be cached by the thread last update time | |
170 | # TODO Test this |
|
179 | # TODO Test this | |
171 | def api_get_thread_posts(request, opening_post_id): |
|
180 | def api_get_thread_posts(request, opening_post_id): | |
172 | """ |
|
181 | """ | |
173 | Gets the JSON array of thread posts |
|
182 | Gets the JSON array of thread posts | |
174 | """ |
|
183 | """ | |
175 |
|
184 | |||
176 | opening_post = get_object_or_404(Post, id=opening_post_id) |
|
185 | opening_post = get_object_or_404(Post, id=opening_post_id) | |
177 | thread = opening_post.get_thread() |
|
186 | thread = opening_post.get_thread() | |
178 | posts = thread.get_replies() |
|
187 | posts = thread.get_replies() | |
179 |
|
188 | |||
180 | json_data = { |
|
189 | json_data = { | |
181 | 'posts': [], |
|
190 | 'posts': [], | |
182 | 'last_update': None, |
|
191 | 'last_update': None, | |
183 | } |
|
192 | } | |
184 | json_post_list = [] |
|
193 | json_post_list = [] | |
185 |
|
194 | |||
186 | for post in posts: |
|
195 | for post in posts: | |
187 | json_post_list.append(_get_post_data(post.id)) |
|
196 | json_post_list.append(_get_post_data(post.id)) | |
188 | json_data['last_update'] = datetime_to_epoch(thread.last_edit_time) |
|
197 | json_data['last_update'] = datetime_to_epoch(thread.last_edit_time) | |
189 | json_data['posts'] = json_post_list |
|
198 | json_data['posts'] = json_post_list | |
190 |
|
199 | |||
191 | return HttpResponse(content=json.dumps(json_data)) |
|
200 | return HttpResponse(content=json.dumps(json_data)) | |
192 |
|
201 | |||
193 |
|
202 | |||
194 | def api_get_post(request, post_id): |
|
203 | def api_get_post(request, post_id): | |
195 | """ |
|
204 | """ | |
196 | Gets the JSON of a post. This can be |
|
205 | Gets the JSON of a post. This can be | |
197 | used as and API for external clients. |
|
206 | used as and API for external clients. | |
198 | """ |
|
207 | """ | |
199 |
|
208 | |||
200 | post = get_object_or_404(Post, id=post_id) |
|
209 | post = get_object_or_404(Post, id=post_id) | |
201 |
|
210 | |||
202 | json = serializers.serialize("json", [post], fields=( |
|
211 | json = serializers.serialize("json", [post], fields=( | |
203 | "pub_time", "_text_rendered", "title", "text", "image", |
|
212 | "pub_time", "_text_rendered", "title", "text", "image", | |
204 | "image_width", "image_height", "replies", "tags" |
|
213 | "image_width", "image_height", "replies", "tags" | |
205 | )) |
|
214 | )) | |
206 |
|
215 | |||
207 | return HttpResponse(content=json) |
|
216 | return HttpResponse(content=json) | |
208 |
|
217 | |||
209 |
|
218 | |||
210 | # TODO Add pub time and replies |
|
219 | # TODO Add pub time and replies | |
211 | def _get_post_data(post_id, format_type=DIFF_TYPE_JSON, request=None, |
|
220 | def _get_post_data(post_id, format_type=DIFF_TYPE_JSON, request=None, | |
212 | include_last_update=False): |
|
221 | include_last_update=False): | |
213 | if format_type == DIFF_TYPE_HTML: |
|
222 | if format_type == DIFF_TYPE_HTML: | |
214 | return get_post(request, post_id).content.strip() |
|
223 | return get_post(request, post_id).content.strip() | |
215 | elif format_type == DIFF_TYPE_JSON: |
|
224 | elif format_type == DIFF_TYPE_JSON: | |
216 | post = get_object_or_404(Post, id=post_id) |
|
225 | post = get_object_or_404(Post, id=post_id) | |
217 | post_json = { |
|
226 | post_json = { | |
218 | 'id': post.id, |
|
227 | 'id': post.id, | |
219 | 'title': post.title, |
|
228 | 'title': post.title, | |
220 | 'text': post.text.rendered, |
|
229 | 'text': post.text.rendered, | |
221 | } |
|
230 | } | |
222 | if post.image: |
|
231 | if post.image: | |
223 | post_json['image'] = post.image.url |
|
232 | post_json['image'] = post.image.url | |
224 | post_json['image_preview'] = post.image.url_200x150 |
|
233 | post_json['image_preview'] = post.image.url_200x150 | |
225 | if include_last_update: |
|
234 | if include_last_update: | |
226 | post_json['bump_time'] = datetime_to_epoch( |
|
235 | post_json['bump_time'] = datetime_to_epoch( | |
227 | post.thread_new.bump_time) |
|
236 | post.thread_new.bump_time) | |
228 | return post_json |
|
237 | return post_json |
@@ -1,252 +1,259 b'' | |||||
1 | # Django settings for neboard project. |
|
1 | # Django settings for neboard project. | |
2 | import os |
|
2 | import os | |
3 | from boards.mdx_neboard import markdown_extended |
|
3 | from boards.mdx_neboard import markdown_extended | |
4 |
|
4 | |||
5 | DEBUG = True |
|
5 | DEBUG = True | |
6 | TEMPLATE_DEBUG = DEBUG |
|
6 | TEMPLATE_DEBUG = DEBUG | |
7 |
|
7 | |||
8 | ADMINS = ( |
|
8 | ADMINS = ( | |
9 | # ('Your Name', 'your_email@example.com'), |
|
9 | # ('Your Name', 'your_email@example.com'), | |
10 | ('admin', 'admin@example.com') |
|
10 | ('admin', 'admin@example.com') | |
11 | ) |
|
11 | ) | |
12 |
|
12 | |||
13 | MANAGERS = ADMINS |
|
13 | MANAGERS = ADMINS | |
14 |
|
14 | |||
15 | DATABASES = { |
|
15 | DATABASES = { | |
16 | 'default': { |
|
16 | 'default': { | |
17 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. |
|
17 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. | |
18 | 'NAME': 'database.db', # Or path to database file if using sqlite3. |
|
18 | 'NAME': 'database.db', # Or path to database file if using sqlite3. | |
19 | 'USER': '', # Not used with sqlite3. |
|
19 | 'USER': '', # Not used with sqlite3. | |
20 | 'PASSWORD': '', # Not used with sqlite3. |
|
20 | 'PASSWORD': '', # Not used with sqlite3. | |
21 | 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. |
|
21 | 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. | |
22 | 'PORT': '', # Set to empty string for default. Not used with sqlite3. |
|
22 | 'PORT': '', # Set to empty string for default. Not used with sqlite3. | |
23 | 'CONN_MAX_AGE': None, |
|
23 | 'CONN_MAX_AGE': None, | |
24 | } |
|
24 | } | |
25 | } |
|
25 | } | |
26 |
|
26 | |||
27 | # Local time zone for this installation. Choices can be found here: |
|
27 | # Local time zone for this installation. Choices can be found here: | |
28 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name |
|
28 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name | |
29 | # although not all choices may be available on all operating systems. |
|
29 | # although not all choices may be available on all operating systems. | |
30 | # In a Windows environment this must be set to your system time zone. |
|
30 | # In a Windows environment this must be set to your system time zone. | |
31 | TIME_ZONE = 'Europe/Kiev' |
|
31 | TIME_ZONE = 'Europe/Kiev' | |
32 |
|
32 | |||
33 | # Language code for this installation. All choices can be found here: |
|
33 | # Language code for this installation. All choices can be found here: | |
34 | # http://www.i18nguy.com/unicode/language-identifiers.html |
|
34 | # http://www.i18nguy.com/unicode/language-identifiers.html | |
35 | LANGUAGE_CODE = 'en' |
|
35 | LANGUAGE_CODE = 'en' | |
36 |
|
36 | |||
37 | SITE_ID = 1 |
|
37 | SITE_ID = 1 | |
38 |
|
38 | |||
39 | # If you set this to False, Django will make some optimizations so as not |
|
39 | # If you set this to False, Django will make some optimizations so as not | |
40 | # to load the internationalization machinery. |
|
40 | # to load the internationalization machinery. | |
41 | USE_I18N = True |
|
41 | USE_I18N = True | |
42 |
|
42 | |||
43 | # If you set this to False, Django will not format dates, numbers and |
|
43 | # If you set this to False, Django will not format dates, numbers and | |
44 | # calendars according to the current locale. |
|
44 | # calendars according to the current locale. | |
45 | USE_L10N = True |
|
45 | USE_L10N = True | |
46 |
|
46 | |||
47 | # If you set this to False, Django will not use timezone-aware datetimes. |
|
47 | # If you set this to False, Django will not use timezone-aware datetimes. | |
48 | USE_TZ = True |
|
48 | USE_TZ = True | |
49 |
|
49 | |||
50 | # Absolute filesystem path to the directory that will hold user-uploaded files. |
|
50 | # Absolute filesystem path to the directory that will hold user-uploaded files. | |
51 | # Example: "/home/media/media.lawrence.com/media/" |
|
51 | # Example: "/home/media/media.lawrence.com/media/" | |
52 | MEDIA_ROOT = './media/' |
|
52 | MEDIA_ROOT = './media/' | |
53 |
|
53 | |||
54 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a |
|
54 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a | |
55 | # trailing slash. |
|
55 | # trailing slash. | |
56 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" |
|
56 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" | |
57 | MEDIA_URL = '/media/' |
|
57 | MEDIA_URL = '/media/' | |
58 |
|
58 | |||
59 | # Absolute path to the directory static files should be collected to. |
|
59 | # Absolute path to the directory static files should be collected to. | |
60 | # Don't put anything in this directory yourself; store your static files |
|
60 | # Don't put anything in this directory yourself; store your static files | |
61 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. |
|
61 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. | |
62 | # Example: "/home/media/media.lawrence.com/static/" |
|
62 | # Example: "/home/media/media.lawrence.com/static/" | |
63 | STATIC_ROOT = '' |
|
63 | STATIC_ROOT = '' | |
64 |
|
64 | |||
65 | # URL prefix for static files. |
|
65 | # URL prefix for static files. | |
66 | # Example: "http://media.lawrence.com/static/" |
|
66 | # Example: "http://media.lawrence.com/static/" | |
67 | STATIC_URL = '/static/' |
|
67 | STATIC_URL = '/static/' | |
68 |
|
68 | |||
69 | # Additional locations of static files |
|
69 | # Additional locations of static files | |
70 | # It is really a hack, put real paths, not related |
|
70 | # It is really a hack, put real paths, not related | |
71 | STATICFILES_DIRS = ( |
|
71 | STATICFILES_DIRS = ( | |
72 | os.path.dirname(__file__) + '/boards/static', |
|
72 | os.path.dirname(__file__) + '/boards/static', | |
73 |
|
73 | |||
74 | # '/d/work/python/django/neboard/neboard/boards/static', |
|
74 | # '/d/work/python/django/neboard/neboard/boards/static', | |
75 | # Put strings here, like "/home/html/static" or "C:/www/django/static". |
|
75 | # Put strings here, like "/home/html/static" or "C:/www/django/static". | |
76 | # Always use forward slashes, even on Windows. |
|
76 | # Always use forward slashes, even on Windows. | |
77 | # Don't forget to use absolute paths, not relative paths. |
|
77 | # Don't forget to use absolute paths, not relative paths. | |
78 | ) |
|
78 | ) | |
79 |
|
79 | |||
80 | # List of finder classes that know how to find static files in |
|
80 | # List of finder classes that know how to find static files in | |
81 | # various locations. |
|
81 | # various locations. | |
82 | STATICFILES_FINDERS = ( |
|
82 | STATICFILES_FINDERS = ( | |
83 | 'django.contrib.staticfiles.finders.FileSystemFinder', |
|
83 | 'django.contrib.staticfiles.finders.FileSystemFinder', | |
84 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', |
|
84 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', | |
85 | ) |
|
85 | ) | |
86 |
|
86 | |||
87 | if DEBUG: |
|
87 | if DEBUG: | |
88 | STATICFILES_STORAGE = \ |
|
88 | STATICFILES_STORAGE = \ | |
89 | 'django.contrib.staticfiles.storage.StaticFilesStorage' |
|
89 | 'django.contrib.staticfiles.storage.StaticFilesStorage' | |
90 | else: |
|
90 | else: | |
91 | STATICFILES_STORAGE = \ |
|
91 | STATICFILES_STORAGE = \ | |
92 | 'django.contrib.staticfiles.storage.CachedStaticFilesStorage' |
|
92 | 'django.contrib.staticfiles.storage.CachedStaticFilesStorage' | |
93 |
|
93 | |||
94 | # Make this unique, and don't share it with anybody. |
|
94 | # Make this unique, and don't share it with anybody. | |
95 | SECRET_KEY = '@1rc$o(7=tt#kd+4s$u6wchm**z^)4x90)7f6z(i&55@o11*8o' |
|
95 | SECRET_KEY = '@1rc$o(7=tt#kd+4s$u6wchm**z^)4x90)7f6z(i&55@o11*8o' | |
96 |
|
96 | |||
97 | # List of callables that know how to import templates from various sources. |
|
97 | # List of callables that know how to import templates from various sources. | |
98 | TEMPLATE_LOADERS = ( |
|
98 | TEMPLATE_LOADERS = ( | |
99 | 'django.template.loaders.filesystem.Loader', |
|
99 | 'django.template.loaders.filesystem.Loader', | |
100 | 'django.template.loaders.app_directories.Loader', |
|
100 | 'django.template.loaders.app_directories.Loader', | |
101 | ) |
|
101 | ) | |
102 |
|
102 | |||
103 | TEMPLATE_CONTEXT_PROCESSORS = ( |
|
103 | TEMPLATE_CONTEXT_PROCESSORS = ( | |
104 | 'django.core.context_processors.media', |
|
104 | 'django.core.context_processors.media', | |
105 | 'django.core.context_processors.static', |
|
105 | 'django.core.context_processors.static', | |
106 | 'django.core.context_processors.request', |
|
106 | 'django.core.context_processors.request', | |
107 | 'django.contrib.auth.context_processors.auth', |
|
107 | 'django.contrib.auth.context_processors.auth', | |
108 | ) |
|
108 | ) | |
109 |
|
109 | |||
110 | MIDDLEWARE_CLASSES = ( |
|
110 | MIDDLEWARE_CLASSES = ( | |
111 | 'django.contrib.sessions.middleware.SessionMiddleware', |
|
111 | 'django.contrib.sessions.middleware.SessionMiddleware', | |
112 | 'django.middleware.locale.LocaleMiddleware', |
|
112 | 'django.middleware.locale.LocaleMiddleware', | |
113 | 'django.middleware.common.CommonMiddleware', |
|
113 | 'django.middleware.common.CommonMiddleware', | |
114 | 'django.contrib.auth.middleware.AuthenticationMiddleware', |
|
114 | 'django.contrib.auth.middleware.AuthenticationMiddleware', | |
115 | 'django.contrib.messages.middleware.MessageMiddleware', |
|
115 | 'django.contrib.messages.middleware.MessageMiddleware', | |
116 | 'boards.middlewares.BanMiddleware', |
|
116 | 'boards.middlewares.BanMiddleware', | |
117 | 'boards.middlewares.MinifyHTMLMiddleware', |
|
117 | 'boards.middlewares.MinifyHTMLMiddleware', | |
118 | ) |
|
118 | ) | |
119 |
|
119 | |||
120 | ROOT_URLCONF = 'neboard.urls' |
|
120 | ROOT_URLCONF = 'neboard.urls' | |
121 |
|
121 | |||
122 | # Python dotted path to the WSGI application used by Django's runserver. |
|
122 | # Python dotted path to the WSGI application used by Django's runserver. | |
123 | WSGI_APPLICATION = 'neboard.wsgi.application' |
|
123 | WSGI_APPLICATION = 'neboard.wsgi.application' | |
124 |
|
124 | |||
125 | TEMPLATE_DIRS = ( |
|
125 | TEMPLATE_DIRS = ( | |
126 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". |
|
126 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". | |
127 | # Always use forward slashes, even on Windows. |
|
127 | # Always use forward slashes, even on Windows. | |
128 | # Don't forget to use absolute paths, not relative paths. |
|
128 | # Don't forget to use absolute paths, not relative paths. | |
129 | 'templates', |
|
129 | 'templates', | |
130 | ) |
|
130 | ) | |
131 |
|
131 | |||
132 | INSTALLED_APPS = ( |
|
132 | INSTALLED_APPS = ( | |
133 | 'django.contrib.auth', |
|
133 | 'django.contrib.auth', | |
134 | 'django.contrib.contenttypes', |
|
134 | 'django.contrib.contenttypes', | |
135 | 'django.contrib.sessions', |
|
135 | 'django.contrib.sessions', | |
136 | # 'django.contrib.sites', |
|
136 | # 'django.contrib.sites', | |
137 | 'django.contrib.messages', |
|
137 | 'django.contrib.messages', | |
138 | 'django.contrib.staticfiles', |
|
138 | 'django.contrib.staticfiles', | |
139 | # Uncomment the next line to enable the admin: |
|
139 | # Uncomment the next line to enable the admin: | |
140 | 'django.contrib.admin', |
|
140 | 'django.contrib.admin', | |
141 | # Uncomment the next line to enable admin documentation: |
|
141 | # Uncomment the next line to enable admin documentation: | |
142 | # 'django.contrib.admindocs', |
|
142 | # 'django.contrib.admindocs', | |
143 | 'django.contrib.humanize', |
|
143 | 'django.contrib.humanize', | |
144 | 'django_cleanup', |
|
144 | 'django_cleanup', | |
145 | 'boards', |
|
145 | 'boards', | |
146 | 'captcha', |
|
146 | 'captcha', | |
147 | 'south', |
|
147 | 'south', | |
148 | 'debug_toolbar', |
|
148 | 'debug_toolbar', | |
149 | ) |
|
149 | ) | |
150 |
|
150 | |||
151 | DEBUG_TOOLBAR_PANELS = ( |
|
151 | DEBUG_TOOLBAR_PANELS = ( | |
152 | 'debug_toolbar.panels.version.VersionDebugPanel', |
|
152 | 'debug_toolbar.panels.version.VersionDebugPanel', | |
153 | 'debug_toolbar.panels.timer.TimerDebugPanel', |
|
153 | 'debug_toolbar.panels.timer.TimerDebugPanel', | |
154 | 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', |
|
154 | 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', | |
155 | 'debug_toolbar.panels.headers.HeaderDebugPanel', |
|
155 | 'debug_toolbar.panels.headers.HeaderDebugPanel', | |
156 | 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', |
|
156 | 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', | |
157 | 'debug_toolbar.panels.template.TemplateDebugPanel', |
|
157 | 'debug_toolbar.panels.template.TemplateDebugPanel', | |
158 | 'debug_toolbar.panels.sql.SQLDebugPanel', |
|
158 | 'debug_toolbar.panels.sql.SQLDebugPanel', | |
159 | 'debug_toolbar.panels.signals.SignalDebugPanel', |
|
159 | 'debug_toolbar.panels.signals.SignalDebugPanel', | |
160 | 'debug_toolbar.panels.logger.LoggingPanel', |
|
160 | 'debug_toolbar.panels.logger.LoggingPanel', | |
161 | ) |
|
161 | ) | |
162 |
|
162 | |||
163 | # TODO: NEED DESIGN FIXES |
|
163 | # TODO: NEED DESIGN FIXES | |
164 | CAPTCHA_OUTPUT_FORMAT = (u' %(hidden_field)s ' |
|
164 | CAPTCHA_OUTPUT_FORMAT = (u' %(hidden_field)s ' | |
165 | u'<div class="form-label">%(image)s</div>' |
|
165 | u'<div class="form-label">%(image)s</div>' | |
166 | u'<div class="form-text">%(text_field)s</div>') |
|
166 | u'<div class="form-text">%(text_field)s</div>') | |
167 |
|
167 | |||
168 | # A sample logging configuration. The only tangible logging |
|
168 | # A sample logging configuration. The only tangible logging | |
169 | # performed by this configuration is to send an email to |
|
169 | # performed by this configuration is to send an email to | |
170 | # the site admins on every HTTP 500 error when DEBUG=False. |
|
170 | # the site admins on every HTTP 500 error when DEBUG=False. | |
171 | # See http://docs.djangoproject.com/en/dev/topics/logging for |
|
171 | # See http://docs.djangoproject.com/en/dev/topics/logging for | |
172 | # more details on how to customize your logging configuration. |
|
172 | # more details on how to customize your logging configuration. | |
173 | LOGGING = { |
|
173 | LOGGING = { | |
174 | 'version': 1, |
|
174 | 'version': 1, | |
175 | 'disable_existing_loggers': False, |
|
175 | 'disable_existing_loggers': False, | |
|
176 | 'formatters': { | |||
|
177 | 'verbose': { | |||
|
178 | 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' | |||
|
179 | }, | |||
|
180 | 'simple': { | |||
|
181 | 'format': '%(levelname)s %(asctime)s [%(module)s] %(message)s' | |||
|
182 | }, | |||
|
183 | }, | |||
176 | 'filters': { |
|
184 | 'filters': { | |
177 | 'require_debug_false': { |
|
185 | 'require_debug_false': { | |
178 | '()': 'django.utils.log.RequireDebugFalse' |
|
186 | '()': 'django.utils.log.RequireDebugFalse' | |
179 | } |
|
187 | } | |
180 | }, |
|
188 | }, | |
181 | 'handlers': { |
|
189 | 'handlers': { | |
182 |
' |
|
190 | 'console': { | |
183 |
'level': ' |
|
191 | 'level': 'DEBUG', | |
184 | 'filters': ['require_debug_false'], |
|
192 | 'class': 'logging.StreamHandler', | |
185 | 'class': 'django.utils.log.AdminEmailHandler' |
|
193 | 'formatter': 'simple' | |
186 | } |
|
194 | }, | |
187 | }, |
|
195 | }, | |
188 | 'loggers': { |
|
196 | 'loggers': { | |
189 |
' |
|
197 | 'boards': { | |
190 |
'handlers': [' |
|
198 | 'handlers': ['console'], | |
191 |
'level': ' |
|
199 | 'level': 'DEBUG', | |
192 | 'propagate': True, |
|
200 | } | |
193 |
|
|
201 | }, | |
194 | } |
|
|||
195 | } |
|
202 | } | |
196 |
|
203 | |||
197 | MARKUP_FIELD_TYPES = ( |
|
204 | MARKUP_FIELD_TYPES = ( | |
198 | ('markdown', markdown_extended), |
|
205 | ('markdown', markdown_extended), | |
199 | ) |
|
206 | ) | |
200 | # Custom imageboard settings |
|
207 | # Custom imageboard settings | |
201 | # TODO These should me moved to |
|
208 | # TODO These should me moved to | |
202 | MAX_POSTS_PER_THREAD = 10 # Thread bumplimit |
|
209 | MAX_POSTS_PER_THREAD = 10 # Thread bumplimit | |
203 | MAX_THREAD_COUNT = 5 # Old threads will be deleted to preserve this count |
|
210 | MAX_THREAD_COUNT = 5 # Old threads will be deleted to preserve this count | |
204 | THREADS_PER_PAGE = 3 |
|
211 | THREADS_PER_PAGE = 3 | |
205 | SITE_NAME = 'Neboard' |
|
212 | SITE_NAME = 'Neboard' | |
206 |
|
213 | |||
207 | THEMES = [ |
|
214 | THEMES = [ | |
208 | ('md', 'Mystic Dark'), |
|
215 | ('md', 'Mystic Dark'), | |
209 | ('md_centered', 'Mystic Dark (centered)'), |
|
216 | ('md_centered', 'Mystic Dark (centered)'), | |
210 | ('sw', 'Snow White'), |
|
217 | ('sw', 'Snow White'), | |
211 | ('pg', 'Photon Gray'), |
|
218 | ('pg', 'Photon Gray'), | |
212 | ] |
|
219 | ] | |
213 |
|
220 | |||
214 | DEFAULT_THEME = 'md' |
|
221 | DEFAULT_THEME = 'md' | |
215 |
|
222 | |||
216 | POPULAR_TAGS = 10 |
|
223 | POPULAR_TAGS = 10 | |
217 | LAST_REPLIES_COUNT = 3 |
|
224 | LAST_REPLIES_COUNT = 3 | |
218 |
|
225 | |||
219 | ENABLE_CAPTCHA = False |
|
226 | ENABLE_CAPTCHA = False | |
220 | # if user tries to post before CAPTCHA_DEFAULT_SAFE_TIME. Captcha will be shown |
|
227 | # if user tries to post before CAPTCHA_DEFAULT_SAFE_TIME. Captcha will be shown | |
221 | CAPTCHA_DEFAULT_SAFE_TIME = 30 # seconds |
|
228 | CAPTCHA_DEFAULT_SAFE_TIME = 30 # seconds | |
222 | POSTING_DELAY = 20 # seconds |
|
229 | POSTING_DELAY = 20 # seconds | |
223 |
|
230 | |||
224 | COMPRESS_HTML = True |
|
231 | COMPRESS_HTML = True | |
225 |
|
232 | |||
226 | VERSION = '1.7.2 Anubis' |
|
233 | VERSION = '1.7.2 Anubis' | |
227 |
|
234 | |||
228 | # Debug mode middlewares |
|
235 | # Debug mode middlewares | |
229 | if DEBUG: |
|
236 | if DEBUG: | |
230 |
|
237 | |||
231 | SITE_NAME += ' DEBUG' |
|
238 | SITE_NAME += ' DEBUG' | |
232 |
|
239 | |||
233 | MIDDLEWARE_CLASSES += ( |
|
240 | MIDDLEWARE_CLASSES += ( | |
234 | 'boards.profiler.ProfilerMiddleware', |
|
241 | 'boards.profiler.ProfilerMiddleware', | |
235 | 'debug_toolbar.middleware.DebugToolbarMiddleware', |
|
242 | 'debug_toolbar.middleware.DebugToolbarMiddleware', | |
236 | ) |
|
243 | ) | |
237 |
|
244 | |||
238 | def custom_show_toolbar(request): |
|
245 | def custom_show_toolbar(request): | |
239 | return DEBUG |
|
246 | return DEBUG | |
240 |
|
247 | |||
241 | DEBUG_TOOLBAR_CONFIG = { |
|
248 | DEBUG_TOOLBAR_CONFIG = { | |
242 | 'INTERCEPT_REDIRECTS': False, |
|
249 | 'INTERCEPT_REDIRECTS': False, | |
243 | 'SHOW_TOOLBAR_CALLBACK': custom_show_toolbar, |
|
250 | 'SHOW_TOOLBAR_CALLBACK': custom_show_toolbar, | |
244 | 'HIDE_DJANGO_SQL': False, |
|
251 | 'HIDE_DJANGO_SQL': False, | |
245 | 'ENABLE_STACKTRACES': True, |
|
252 | 'ENABLE_STACKTRACES': True, | |
246 | } |
|
253 | } | |
247 |
|
254 | |||
248 | # FIXME Uncommenting this fails somehow. Need to investigate this |
|
255 | # FIXME Uncommenting this fails somehow. Need to investigate this | |
249 | #DEBUG_TOOLBAR_PANELS += ( |
|
256 | #DEBUG_TOOLBAR_PANELS += ( | |
250 | # 'debug_toolbar.panels.profiling.ProfilingDebugPanel', |
|
257 | # 'debug_toolbar.panels.profiling.ProfilingDebugPanel', | |
251 | #) |
|
258 | #) | |
252 |
|
259 |
General Comments 0
You need to be logged in to leave comments.
Login now