##// END OF EJS Templates
Merged in the 1.6 version
neko259 -
r513:4f928387 merge 1.6 default
parent child Browse files
Show More
@@ -0,0 +1,85 b''
1 # -*- coding: utf-8 -*-
2 from south.utils import datetime_utils as datetime
3 from south.db import db
4 from south.v2 import SchemaMigration
5 from django.db import models
6
7
8 class Migration(SchemaMigration):
9
10 def forwards(self, orm):
11 # Adding field 'Thread.archived'
12 db.add_column(u'boards_thread', 'archived',
13 self.gf('django.db.models.fields.BooleanField')(default=0),
14 keep_default=False)
15
16
17 def backwards(self, orm):
18 # Deleting field 'Thread.archived'
19 db.delete_column(u'boards_thread', 'archived')
20
21
22 models = {
23 'boards.ban': {
24 'Meta': {'object_name': 'Ban'},
25 'can_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
26 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
27 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
28 'reason': ('django.db.models.fields.CharField', [], {'default': "'Auto'", 'max_length': '200'})
29 },
30 'boards.post': {
31 'Meta': {'object_name': 'Post'},
32 '_text_rendered': ('django.db.models.fields.TextField', [], {}),
33 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34 'image': ('boards.thumbs.ImageWithThumbsField', [], {'max_length': '100', 'blank': 'True'}),
35 'image_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
36 'image_pre_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
37 'image_pre_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
38 'image_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
39 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
40 'poster_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
41 'poster_user_agent': ('django.db.models.fields.TextField', [], {}),
42 'pub_time': ('django.db.models.fields.DateTimeField', [], {}),
43 'referenced_posts': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rfp+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}),
44 'text': ('markupfield.fields.MarkupField', [], {'rendered_field': 'True'}),
45 'text_markup_type': ('django.db.models.fields.CharField', [], {'default': "'markdown'", 'max_length': '30'}),
46 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Post']", 'null': 'True'}),
47 'thread_new': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Thread']", 'null': 'True'}),
48 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
49 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.User']", 'null': 'True'})
50 },
51 'boards.setting': {
52 'Meta': {'object_name': 'Setting'},
53 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
54 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
55 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.User']"}),
56 'value': ('django.db.models.fields.CharField', [], {'max_length': '50'})
57 },
58 'boards.tag': {
59 'Meta': {'object_name': 'Tag'},
60 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
61 'linked': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}),
62 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
63 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tag+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Thread']"})
64 },
65 'boards.thread': {
66 'Meta': {'object_name': 'Thread'},
67 'archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
68 'bump_time': ('django.db.models.fields.DateTimeField', [], {}),
69 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
70 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
71 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tre+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}),
72 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['boards.Tag']", 'symmetrical': 'False'})
73 },
74 'boards.user': {
75 'Meta': {'object_name': 'User'},
76 'fav_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}),
77 'fav_threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}),
78 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
79 'rank': ('django.db.models.fields.IntegerField', [], {}),
80 'registration_time': ('django.db.models.fields.DateTimeField', [], {}),
81 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '50'})
82 }
83 }
84
85 complete_apps = ['boards'] No newline at end of file
@@ -0,0 +1,25 b''
1 # INTRO #
2
3 The API is provided to query the data from a neaboard server by any client
4 application.
5
6 Tha data is returned in the json format and got by an http query.
7
8 # METHODS #
9
10 /api/threads/N/?offset=M&tag=O
11
12 Get a thread list. You will get N threads (required parameter) starting from
13 Mth one (optional parameter, default is 0) with the tag O (optional parameter,
14 threads with any tags are shown by default).
15
16 /api/tags/
17
18 Get all active tag list. Active tag is a tag that has at least 1 active thread
19 associated with it.
20
21 /api/thread/N/
22
23 Get all Nth thread post. N is an opening post ID for the thread.
24
25 In case of incorrect request you can get http error 404.
1 NO CONTENT: modified file, binary diff hidden
@@ -7,7 +7,7 b' msgid ""'
7 7 msgstr ""
8 8 "Project-Id-Version: PACKAGE VERSION\n"
9 9 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2013-12-24 20:39+0200\n"
10 "POT-Creation-Date: 2014-01-06 23:43+0200\n"
11 11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 13 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -74,8 +74,7 b' msgstr "\xd0\xa2\xd0\xb5\xd0\xba\xd1\x81\xd1\x82 \xd0\xb8\xd0\xbb\xd0\xb8 \xd0\xba\xd0\xb0\xd1\x80\xd1\x82\xd0\xb8\xd0\xbd\xd0\xba\xd0\xb0 \xd0\xb4\xd0\xbe\xd0\xbb\xd0\xb6\xd0\xbd\xd1\x8b \xd0\xb1\xd1\x8b\xd1\x82\xd1\x8c \xd0\xb2\xd0\xb2\xd0\xb5\xd0\xb4\xd0\xb5\xd0\xbd\xd1\x8b."'
74 74 msgid "Wait %s seconds after last posting"
75 75 msgstr "Подождите %s секунд после последнего постинга"
76 76
77 #: forms.py:163 templates/boards/post.html:61 templates/boards/tags.html:6
78 #: templates/boards/rss/post.html:10
77 #: forms.py:163 templates/boards/tags.html:6 templates/boards/rss/post.html:10
79 78 msgid "Tags"
80 79 msgstr "Теги"
81 80
@@ -112,6 +111,52 b' msgstr "\xd0\x9d\xd0\xb5 \xd0\xbd\xd0\xb0\xd0\xb9\xd0\xb4\xd0\xb5\xd0\xbd\xd0\xbe"'
112 111 msgid "This page does not exist"
113 112 msgstr "Этой страницы не существует"
114 113
114 #: templates/boards/archive.html:45 templates/boards/posting_general.html:64
115 msgid "Previous page"
116 msgstr "Предыдущая страница"
117
118 #: templates/boards/archive.html:75
119 msgid "Open"
120 msgstr "Открыть"
121
122 #: templates/boards/archive.html:81 templates/boards/post.html:37
123 #: templates/boards/posting_general.html:103 templates/boards/thread.html:69
124 msgid "Delete"
125 msgstr "Удалить"
126
127 #: templates/boards/archive.html:85 templates/boards/post.html:40
128 #: templates/boards/posting_general.html:107 templates/boards/thread.html:72
129 msgid "Ban IP"
130 msgstr "Заблокировать IP"
131
132 #: templates/boards/archive.html:94 templates/boards/post.html:53
133 #: templates/boards/posting_general.html:116
134 #: templates/boards/posting_general.html:180 templates/boards/thread.html:81
135 msgid "Replies"
136 msgstr "Ответы"
137
138 #: templates/boards/archive.html:103 templates/boards/posting_general.html:125
139 #: templates/boards/thread.html:138 templates/boards/thread_gallery.html:58
140 msgid "images"
141 msgstr "изображений"
142
143 #: templates/boards/archive.html:104 templates/boards/thread.html:137
144 #: templates/boards/thread_gallery.html:57
145 msgid "replies"
146 msgstr "ответов"
147
148 #: templates/boards/archive.html:129 templates/boards/posting_general.html:203
149 msgid "Next page"
150 msgstr "Следующая страница"
151
152 #: templates/boards/archive.html:134 templates/boards/posting_general.html:208
153 msgid "No threads exist. Create the first one!"
154 msgstr "Нет тем. Создайте первую!"
155
156 #: templates/boards/archive.html:143 templates/boards/posting_general.html:235
157 msgid "Pages:"
158 msgstr "Страницы: "
159
115 160 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
116 161 msgid "Authors"
117 162 msgstr "Авторы"
@@ -149,12 +194,16 b' msgstr "\xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8"'
149 194 msgid "Login"
150 195 msgstr "Вход"
151 196
152 #: templates/boards/base.html:52
197 #: templates/boards/base.html:51
198 msgid "Archive"
199 msgstr "Архив"
200
201 #: templates/boards/base.html:53
153 202 #, python-format
154 203 msgid "Speed: %(ppd)s posts per day"
155 204 msgstr "Скорость: %(ppd)s сообщений в день"
156 205
157 #: templates/boards/base.html:54
206 #: templates/boards/base.html:55
158 207 msgid "Up"
159 208 msgstr "Вверх"
160 209
@@ -166,52 +215,20 b' msgstr "ID \xd0\xbf\xd0\xbe\xd0\xbb\xd1\x8c\xd0\xb7\xd0\xbe\xd0\xb2\xd0\xb0\xd1\x82\xd0\xb5\xd0\xbb\xd1\x8f"'
166 215 msgid "Insert your user id above"
167 216 msgstr "Вставьте свой ID пользователя выше"
168 217
169 #: templates/boards/post.html:35 templates/boards/posting_general.html:103
170 #: templates/boards/thread.html:68
171 msgid "Delete"
172 msgstr "Удалить"
173
174 #: templates/boards/post.html:38 templates/boards/posting_general.html:107
175 #: templates/boards/thread.html:71
176 msgid "Ban IP"
177 msgstr "Заблокировать IP"
178
179 #: templates/boards/post.html:51 templates/boards/posting_general.html:116
180 #: templates/boards/posting_general.html:180 templates/boards/thread.html:80
181 msgid "Replies"
182 msgstr "Ответы"
183
184 #: templates/boards/posting_general.html:64
185 msgid "Previous page"
186 msgstr "Предыдущая страница"
187
188 218 #: templates/boards/posting_general.html:97
189 219 msgid "Reply"
190 220 msgstr "Ответ"
191 221
192 #: templates/boards/posting_general.html:125 templates/boards/thread.html:130
193 #: templates/boards/thread_gallery.html:52
194 msgid "images"
195 msgstr "изображений"
196
197 222 #: templates/boards/posting_general.html:142
198 223 #, python-format
199 224 msgid "Skipped %(count)s replies. Open thread to see all replies."
200 225 msgstr "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы."
201 226
202 #: templates/boards/posting_general.html:203
203 msgid "Next page"
204 msgstr "Следующая страница"
205
206 #: templates/boards/posting_general.html:208
207 msgid "No threads exist. Create the first one!"
208 msgstr "Нет тем. Создайте первую!"
209
210 227 #: templates/boards/posting_general.html:214
211 228 msgid "Create new thread"
212 229 msgstr "Создать новую тему"
213 230
214 #: templates/boards/posting_general.html:218 templates/boards/thread.html:112
231 #: templates/boards/posting_general.html:218 templates/boards/thread.html:115
215 232 msgid "Post"
216 233 msgstr "Отправить"
217 234
@@ -220,14 +237,10 b' msgid "Tags must be delimited by spaces.'
220 237 msgstr ""
221 238 "Теги должны быть разделены пробелами. Текст или изображение обязательны."
222 239
223 #: templates/boards/posting_general.html:225 templates/boards/thread.html:116
240 #: templates/boards/posting_general.html:225 templates/boards/thread.html:119
224 241 msgid "Text syntax"
225 242 msgstr "Синтаксис текста"
226 243
227 #: templates/boards/posting_general.html:235
228 msgid "Pages:"
229 msgstr "Страницы: "
230
231 244 #: templates/boards/settings.html:14
232 245 msgid "User:"
233 246 msgstr "Пользователь:"
@@ -260,27 +273,23 b' msgstr "\xd1\x82\xd0\xb5\xd0\xbc"'
260 273 msgid "No tags found."
261 274 msgstr "Теги не найдены."
262 275
263 #: templates/boards/thread.html:22 templates/boards/thread_gallery.html:20
276 #: templates/boards/thread.html:19 templates/boards/thread_gallery.html:20
264 277 msgid "Normal mode"
265 278 msgstr "Нормальный режим"
266 279
267 #: templates/boards/thread.html:23 templates/boards/thread_gallery.html:21
280 #: templates/boards/thread.html:20 templates/boards/thread_gallery.html:21
268 281 msgid "Gallery mode"
269 282 msgstr "Режим галереи"
270 283
271 #: templates/boards/thread.html:31
284 #: templates/boards/thread.html:28
272 285 msgid "posts to bumplimit"
273 286 msgstr "сообщений до бамплимита"
274 287
275 #: templates/boards/thread.html:106
288 #: templates/boards/thread.html:109
276 289 msgid "Reply to thread"
277 290 msgstr "Ответить в тему"
278 291
279 #: templates/boards/thread.html:129 templates/boards/thread_gallery.html:51
280 msgid "replies"
281 msgstr "ответов"
282
283 #: templates/boards/thread.html:131 templates/boards/thread_gallery.html:53
292 #: templates/boards/thread.html:139 templates/boards/thread_gallery.html:59
284 293 msgid "Last update: "
285 294 msgstr "Последнее обновление: "
286 295
@@ -10,7 +10,7 b' import boards'
10 10 AUTOLINK_PATTERN = r'(https?://\S+)'
11 11 QUOTE_PATTERN = r'^(?<!>)(>[^>].+)$'
12 12 REFLINK_PATTERN = r'((>>)(\d+))'
13 SPOILER_PATTERN = r'%%(.+)%%'
13 SPOILER_PATTERN = r'%%([^(%%)]+)%%'
14 14 COMMENT_PATTERN = r'^(//(.+))'
15 15 STRIKETHROUGH_PATTERN = r'~(.+)~'
16 16
@@ -6,6 +6,7 b' import time'
6 6 import math
7 7 import re
8 8 from django.core.cache import cache
9 from django.core.paginator import Paginator
9 10
10 11 from django.db import models
11 12 from django.http import Http404
@@ -15,6 +16,8 b' from markupfield.fields import MarkupFie'
15 16 from neboard import settings
16 17 from boards import thumbs
17 18
19 MAX_TITLE_LENGTH = 50
20
18 21 APP_LABEL_BOARDS = 'boards'
19 22
20 23 CACHE_KEY_PPD = 'ppd'
@@ -64,7 +67,8 b' class PostManager(models.Manager):'
64 67 thread_new=thread,
65 68 image=image,
66 69 poster_ip=ip,
67 poster_user_agent=UNKNOWN_UA, # TODO Get UA at last!
70 poster_user_agent=UNKNOWN_UA, # TODO Get UA at
71 # last!
68 72 last_edit_time=posting_time,
69 73 user=user)
70 74
@@ -111,7 +115,7 b' class PostManager(models.Manager):'
111 115
112 116 # TODO Move this method to thread manager
113 117 def get_threads(self, tag=None, page=ALL_PAGES,
114 order_by='-bump_time'):
118 order_by='-bump_time', archived=False):
115 119 if tag:
116 120 threads = tag.threads
117 121
@@ -120,39 +124,21 b' class PostManager(models.Manager):'
120 124 else:
121 125 threads = Thread.objects.all()
122 126
123 threads = threads.order_by(order_by)
127 threads = threads.filter(archived=archived).order_by(order_by)
124 128
125 129 if page != ALL_PAGES:
126 thread_count = threads.count()
127
128 if page < self._get_page_count(thread_count):
129 start_thread = page * settings.THREADS_PER_PAGE
130 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
131 thread_count)
132 threads = threads[start_thread:end_thread]
130 threads = Paginator(threads, settings.THREADS_PER_PAGE).page(
131 page).object_list
133 132
134 133 return threads
135 134
136 135 # TODO Move this method to thread manager
137 def get_thread_page_count(self, tag=None):
138 if tag:
139 threads = Thread.objects.filter(tags=tag)
140 else:
141 threads = Thread.objects.all()
142
143 return self._get_page_count(threads.count())
144
145 # TODO Move this method to thread manager
146 136 def _delete_old_threads(self):
147 137 """
148 138 Preserves maximum thread count. If there are too many threads,
149 delete the old ones.
139 archive the old ones.
150 140 """
151 141
152 # TODO Move old threads to the archive instead of deleting them.
153 # Maybe make some 'old' field in the model to indicate the thread
154 # must not be shown and be able for replying.
155
156 142 threads = self.get_threads()
157 143 thread_count = threads.count()
158 144
@@ -160,7 +146,10 b' class PostManager(models.Manager):'
160 146 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
161 147 old_threads = threads[thread_count - num_threads_to_delete:]
162 148
163 map(Thread.delete_with_posts, old_threads)
149 for thread in old_threads:
150 thread.archived = True
151 thread.last_edit_time = timezone.now()
152 thread.save()
164 153
165 154 def connect_replies(self, post):
166 155 """
@@ -176,13 +165,6 b' class PostManager(models.Manager):'
176 165 referenced_post.last_edit_time = post.pub_time
177 166 referenced_post.save()
178 167
179 def _get_page_count(self, thread_count):
180 """
181 Get number of pages that will be needed for all threads
182 """
183
184 return int(math.ceil(thread_count / float(settings.THREADS_PER_PAGE)))
185
186 168 def get_posts_per_day(self):
187 169 """
188 170 Get average count of posts per day for the last 7 days
@@ -270,7 +252,7 b' class Post(models.Model):'
270 252 def get_title(self):
271 253 title = self.title
272 254 if len(title) == 0:
273 title = self.text.raw[:20]
255 title = self.text.rendered[:MAX_TITLE_LENGTH]
274 256
275 257 return title
276 258
@@ -294,6 +276,7 b' class Thread(models.Model):'
294 276 last_edit_time = models.DateTimeField()
295 277 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
296 278 blank=True, related_name='tre+')
279 archived = models.BooleanField(default=False)
297 280
298 281 def get_tags(self):
299 282 """
@@ -321,6 +304,9 b' class Thread(models.Model):'
321 304 Check if the thread can be bumped by replying
322 305 """
323 306
307 if self.archived:
308 return False
309
324 310 post_count = self.get_reply_count()
325 311
326 312 return post_count < settings.MAX_POSTS_PER_THREAD
@@ -6,7 +6,10 b' from django.db.models import Count'
6 6
7 7 TAG_FONT_MULTIPLIER = 0.1
8 8 MAX_TAG_FONT = 10
9 OPENING_POST_POPULARITY_WEIGHT = 2
9
10 OPENING_POST_POPULARITY = 0.5
11 ARCHIVE_POPULARITY = 0.01
12 REPLY_POPULARITY = 0.1
10 13
11 14
12 15 class TagManager(models.Manager):
@@ -43,15 +46,19 b' class Tag(models.Model):'
43 46 def get_post_count(self):
44 47 return self.threads.count()
45 48
46 # TODO Reenable this method after migration
47 # def get_popularity(self):
48 # posts_with_tag = Thread.objects.get_threads(tag=self)
49 # reply_count = 0
50 # for post in posts_with_tag:
51 # reply_count += post.get_reply_count()
52 # reply_count += OPENING_POST_POPULARITY_WEIGHT
53 #
54 # return reply_count
49 def get_popularity(self):
50 popularity = 0.0
51
52 for thread in self.threads.all():
53 reply_count = thread.get_reply_count()
54
55 if thread.archived:
56 popularity += ARCHIVE_POPULARITY * reply_count
57 else:
58 popularity += REPLY_POPULARITY * reply_count
59 popularity += OPENING_POST_POPULARITY
60
61 return popularity
55 62
56 63 def get_linked_tags(self):
57 64 tag_list = []
@@ -75,10 +82,10 b' class Tag(models.Model):'
75 82 def get_font_value(self):
76 83 """Get tag font value to differ most popular tags in the list"""
77 84
78 post_count = self.get_post_count()
79 if post_count > MAX_TAG_FONT:
80 post_count = MAX_TAG_FONT
85 popularity = self.get_popularity()
86 if popularity > MAX_TAG_FONT:
87 popularity = MAX_TAG_FONT
81 88
82 font_value = str(1 + (post_count - 1) * TAG_FONT_MULTIPLIER)
89 font_value = str(1 + (popularity - 1) * TAG_FONT_MULTIPLIER)
83 90
84 return font_value No newline at end of file
91 return font_value
@@ -43,7 +43,7 b' html {'
43 43 color: #fff380;
44 44 }
45 45
46 .post, .dead_post, #posts-table {
46 .post, .dead_post, .archive_post, #posts-table {
47 47 background: #333;
48 48 margin: 5px;
49 49 padding: 10px;
@@ -191,6 +191,10 b' blockquote {'
191 191 background-color: #442222;
192 192 }
193 193
194 .archive_post {
195 background-color: #000;
196 }
197
194 198 .mark_btn {
195 199 border: 1px solid;
196 200 min-width: 2ex;
@@ -381,3 +385,16 b' code {'
381 385 pre {
382 386 overflow: auto;
383 387 }
388
389 .img-full {
390 background: #222;
391 border: solid 1px white;
392 }
393
394 .tag_item {
395 display: inline-block;
396 background: #555;
397 border: 1px solid #ccc;
398 margin: 0.3ex;
399 padding: 0.2ex;
400 }
@@ -344,4 +344,11 b' input[type="submit"]:hover {'
344 344 .current_page, .current_mode {
345 345 border: solid 1px #000;
346 346 padding: 2px;
347 } No newline at end of file
347 }
348
349 .tag_item {
350 display: inline-block;
351 border: 1px solid #ccc;
352 margin: 0.3ex;
353 padding: 0.2ex;
354 }
@@ -6,27 +6,23 b''
6 6 {% load static %}
7 7
8 8 {% block head %}
9 {% if tag %}
10 <title>Neboard - {{ tag.name }}</title>
11 {% else %}
12 <title>Neboard</title>
13 {% endif %}
9 <title>Neboard - {% trans 'Archive' %}</title>
14 10
15 {% if prev_page %}
16 <link rel="next" href="
11 {% if current_page.has_previous %}
12 <link rel="prev" href="
17 13 {% if tag %}
18 {% url "tag" tag_name=tag page=prev_page %}
14 {% url "tag" tag_name=tag page=current_page.previous_page_number %}
19 15 {% else %}
20 {% url "index" page=prev_page %}
16 {% url "index" page=current_page.previous_page_number %}
21 17 {% endif %}
22 18 " />
23 19 {% endif %}
24 {% if next_page %}
20 {% if current_page.has_next %}
25 21 <link rel="next" href="
26 22 {% if tag %}
27 {% url "tag" tag_name=tag page=next_page %}
23 {% url "tag" tag_name=tag page=current_page.next_page_number %}
28 24 {% else %}
29 {% url "index" page=next_page %}
25 {% url "index" page=current_page.next_page_number %}
30 26 {% endif %}
31 27 " />
32 28 {% endif %}
@@ -37,42 +33,17 b''
37 33
38 34 {% get_current_language as LANGUAGE_CODE %}
39 35
40 {% if tag %}
41 <div class="tag_info">
42 <h2>
43 {% if tag in user.fav_tags.all %}
44 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
45 class="fav"></a>
46 {% else %}
47 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
48 class="not_fav"></a>
49 {% endif %}
50 #{{ tag.name }}
51 </h2>
52 </div>
53 {% endif %}
54
55 36 {% if threads %}
56 {% if prev_page %}
37 {% if current_page.has_previous %}
57 38 <div class="page_link">
58 <a href="
59 {% if tag %}
60 {% url "tag" tag_name=tag page=prev_page %}
61 {% else %}
62 {% url "index" page=prev_page %}
63 {% endif %}
64 ">{% trans "Previous page" %}</a>
39 <a href="{% url "archive" page=current_page.previous_page_number %}">{% trans "Previous page" %}</a>
65 40 </div>
66 41 {% endif %}
67 42
68 43 {% for thread in threads %}
69 44 {% cache 600 thread_short thread.id thread.thread.last_edit_time moderator LANGUAGE_CODE %}
70 45 <div class="thread">
71 {% if thread.bumpable %}
72 <div class="post" id="{{ thread.op.id }}">
73 {% else %}
74 <div class="post dead_post" id="{{ thread.op.id }}">
75 {% endif %}
46 <div class="post archive_post" id="{{ thread.op.id }}">
76 47 {% if thread.op.image %}
77 48 <div class="image">
78 49 <a class="thumb"
@@ -91,10 +62,10 b''
91 62 <span class="title">{{ thread.op.title }}</span>
92 63 <a class="post_id" href="{% url 'thread' thread.op.id %}"
93 64 > ({{ thread.op.id }})</a>
94 [{{ thread.op.pub_time }}]
65 [{{ thread.op.pub_time }}] — [{{ thread.thread.last_edit_time }}]
66
95 67 [<a class="link" href="
96 {% url 'thread' thread.op.id %}#form"
97 >{% trans "Reply" %}</a>]
68 {% url 'thread' thread.op.id %}">{% trans "Open" %}</a>]
98 69
99 70 {% if moderator %}
100 71 <span class="moderator_info">
@@ -122,7 +93,8 b''
122 93 {% endif %}
123 94 </div>
124 95 <div class="metadata">
125 {{ thread.thread.get_images_count }} {% trans 'images' %}.
96 {{ thread.thread.get_images_count }} {% trans 'images' %},
97 {{ thread.thread.get_reply_count }} {% trans 'replies' %}.
126 98 {% if thread.thread.tags %}
127 99 <span class="tags">
128 100 {% for tag in thread.thread.get_tags %}
@@ -135,72 +107,13 b''
135 107 {% endif %}
136 108 </div>
137 109 </div>
138 {% if thread.last_replies.exists %}
139 {% if thread.skipped_replies %}
140 <div class="skipped_replies">
141 <a href="{% url 'thread' thread.op.id %}">
142 {% blocktrans with count=thread.skipped_replies %}Skipped {{ count }} replies. Open thread to see all replies.{% endblocktrans %}
143 </a>
144 </div>
145 {% endif %}
146 <div class="last-replies">
147 {% for post in thread.last_replies %}
148 {% if thread.bumpable %}
149 <div class="post" id="{{ post.id }}">
150 {% else %}
151 <div class="post dead_post" id="{{ post.id }}">
152 {% endif %}
153 {% if post.image %}
154 <div class="image">
155 <a class="thumb"
156 href="{{ post.image.url }}"><img
157 src=" {{ post.image.url_200x150 }}"
158 alt="{{ post.id }}"
159 width="{{ post.image_pre_width }}"
160 height="{{ post.image_pre_height }}"
161 data-width="{{ post.image_width }}"
162 data-height="{{ post.image_height }}"/>
163 </a>
164 </div>
165 {% endif %}
166 <div class="message">
167 <div class="post-info">
168 <span class="title">{{ post.title }}</span>
169 <a class="post_id" href="
170 {% url 'thread' thread.op.id %}#{{ post.id }}">
171 ({{ post.id }})</a>
172 [{{ post.pub_time }}]
173 </div>
174 {% autoescape off %}
175 {{ post.text.rendered|truncatewords_html:50 }}
176 {% endautoescape %}
177 </div>
178 {% if post.is_referenced %}
179 <div class="refmap">
180 {% trans "Replies" %}:
181 {% for ref_post in post.get_sorted_referenced_posts %}
182 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
183 >{% if not forloop.last %},{% endif %}
184 {% endfor %}
185 </div>
186 {% endif %}
187 </div>
188 {% endfor %}
189 </div>
190 {% endif %}
191 110 </div>
192 111 {% endcache %}
193 112 {% endfor %}
194 113
195 {% if next_page %}
114 {% if current_page.has_next %}
196 115 <div class="page_link">
197 <a href="
198 {% if tag %}
199 {% url "tag" tag_name=tag page=next_page %}
200 {% else %}
201 {% url "index" page=next_page %}
202 {% endif %}
203 ">{% trans "Next page" %}</a>
116 <a href="{% url "archive" page=current_page.next_page_number %}">{% trans "Next page" %}</a>
204 117 </div>
205 118 {% endif %}
206 119 {% else %}
@@ -208,42 +121,20 b''
208 121 {% trans 'No threads exist. Create the first one!' %}</div>
209 122 {% endif %}
210 123
211 <div class="post-form-w">
212 <script src="{% static 'js/panel.js' %}"></script>
213 <div class="post-form">
214 <div class="form-title">{% trans "Create new thread" %}</div>
215 <form enctype="multipart/form-data" method="post">{% csrf_token %}
216 {{ form.as_div }}
217 <div class="form-submit">
218 <input type="submit" value="{% trans "Post" %}"/>
219 </div>
220 </form>
221 <div>
222 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
223 </div>
224 <div><a href="{% url "staticpage" name="help" %}">
225 {% trans 'Text syntax' %}</a></div>
226 </div>
227 </div>
228
229 124 {% endblock %}
230 125
231 126 {% block metapanel %}
232 127
233 128 <span class="metapanel">
234 <b><a href="{% url "authors" %}">Neboard</a> 1.5 Aker</b>
129 <b><a href="{% url "authors" %}">Neboard</a> 1.6 Amon</b>
235 130 {% trans "Pages:" %}[
236 {% for page in pages %}
131 {% for page in paginator.page_range %}
237 132 <a
238 {% ifequal page current_page %}
133 {% ifequal page current_page.number %}
239 134 class="current_page"
240 135 {% endifequal %}
241 136 href="
242 {% if tag %}
243 {% url "tag" tag_name=tag page=page %}
244 {% else %}
245 {% url "index" page=page %}
246 {% endif %}
137 {% url "archive" page=page %}
247 138 ">{{ page }}</a>
248 139 {% if not forloop.last %},{% endif %}
249 140 {% endfor %}
@@ -48,6 +48,7 b''
48 48 <div class="navigation_panel">
49 49 {% block metapanel %}{% endblock %}
50 50 [<a href="{% url "login" %}">{% trans 'Login' %}</a>]
51 [<a href="{% url "archive" %}">{% trans 'Archive' %}</a>]
51 52 {% with ppd=posts_per_day|floatformat:2 %}
52 53 {% blocktrans %}Speed: {{ ppd }} posts per day{% endblocktrans %}
53 54 {% endwith %}
@@ -12,21 +12,21 b''
12 12 <title>Neboard</title>
13 13 {% endif %}
14 14
15 {% if prev_page %}
16 <link rel="next" href="
15 {% if current_page.has_previous %}
16 <link rel="prev" href="
17 17 {% if tag %}
18 {% url "tag" tag_name=tag page=prev_page %}
18 {% url "tag" tag_name=tag page=current_page.previous_page_number %}
19 19 {% else %}
20 {% url "index" page=prev_page %}
20 {% url "index" page=current_page.previous_page_number %}
21 21 {% endif %}
22 22 " />
23 23 {% endif %}
24 {% if next_page %}
24 {% if current_page.has_next %}
25 25 <link rel="next" href="
26 26 {% if tag %}
27 {% url "tag" tag_name=tag page=next_page %}
27 {% url "tag" tag_name=tag page=current_page.next_page_number %}
28 28 {% else %}
29 {% url "index" page=next_page %}
29 {% url "index" page=current_page.next_page_number %}
30 30 {% endif %}
31 31 " />
32 32 {% endif %}
@@ -53,13 +53,13 b''
53 53 {% endif %}
54 54
55 55 {% if threads %}
56 {% if prev_page %}
56 {% if current_page.has_previous %}
57 57 <div class="page_link">
58 58 <a href="
59 59 {% if tag %}
60 {% url "tag" tag_name=tag page=prev_page %}
60 {% url "tag" tag_name=tag page=current_page.previous_page_number %}
61 61 {% else %}
62 {% url "index" page=prev_page %}
62 {% url "index" page=current_page.previous_page_number %}
63 63 {% endif %}
64 64 ">{% trans "Previous page" %}</a>
65 65 </div>
@@ -69,9 +69,9 b''
69 69 {% cache 600 thread_short thread.id thread.thread.last_edit_time moderator LANGUAGE_CODE %}
70 70 <div class="thread">
71 71 {% if thread.bumpable %}
72 <div class="post" id="{{ thread.op.id }}">
72 <div class="post" id="{{ thread.op.id }}">
73 73 {% else %}
74 <div class="post dead_post" id="{{ thread.op.id }}">
74 <div class="post dead_post" id="{{ thread.op.id }}">
75 75 {% endif %}
76 76 {% if thread.op.image %}
77 77 <div class="image">
@@ -192,13 +192,13 b''
192 192 {% endcache %}
193 193 {% endfor %}
194 194
195 {% if next_page %}
195 {% if current_page.has_next %}
196 196 <div class="page_link">
197 197 <a href="
198 198 {% if tag %}
199 {% url "tag" tag_name=tag page=next_page %}
199 {% url "tag" tag_name=tag page=current_page.next_page_number %}
200 200 {% else %}
201 {% url "index" page=next_page %}
201 {% url "index" page=current_page.next_page_number %}
202 202 {% endif %}
203 203 ">{% trans "Next page" %}</a>
204 204 </div>
@@ -231,11 +231,11 b''
231 231 {% block metapanel %}
232 232
233 233 <span class="metapanel">
234 <b><a href="{% url "authors" %}">Neboard</a> 1.5 Aker</b>
234 <b><a href="{% url "authors" %}">Neboard</a> 1.6 Amon</b>
235 235 {% trans "Pages:" %}[
236 {% for page in pages %}
236 {% for page in paginator.page_range %}
237 237 <a
238 {% ifequal page current_page %}
238 {% ifequal page current_page.number %}
239 239 class="current_page"
240 240 {% endifequal %}
241 241 href="
@@ -9,33 +9,34 b''
9 9 {% block content %}
10 10
11 11 <div class="post">
12 {% if all_tags %}
13 {% for tag in all_tags %}
14 {% if tag in user.fav_tags.all %}
15 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
16 class="fav"></a>
17 {% else %}
18 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
19 class="not_fav"></a>
20 {% endif %}
21 <a class="tag" href="{% url 'tag' tag.name %}"
22 style="font-size: {{ tag.get_font_value }}em">
23 #{{ tag.name }}</a>
24 ({{ tag.get_post_count }} {% trans 'threads' %})
25 {% if tag.linked %}
26 ( +
27 {% for linked_tag in tag.get_linked_tags %}
28 <a class="tag" href="{% url 'tag' linked_tag.name %}">
29 #{{ linked_tag.name }}
30 </a>
31 {% endfor %}
32 )
33 {% endif %}
34 <br />
35 {% endfor %}
36 {% else %}
37 {% trans 'No tags found.' %}
38 {% endif %}
12 {% if all_tags %}
13 {% for tag in all_tags %}
14 <div class="tag_item">
15 {% if tag in user.fav_tags.all %}
16 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
17 class="fav"></a>
18 {% else %}
19 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
20 class="not_fav"></a>
21 {% endif %}
22 <a class="tag" href="{% url 'tag' tag.name %}"
23 style="font-size: {{ tag.get_font_value }}em">
24 #{{ tag.name }}</a>
25 ({{ tag.get_post_count }} {% trans 'threads' %})
26 {% if tag.linked %}
27 ( +
28 {% for linked_tag in tag.get_linked_tags %}
29 <a class="tag" href="{% url 'tag' linked_tag.name %}">
30 #{{ linked_tag.name }}
31 </a>
32 {% endfor %}
33 )
34 {% endif %}
35 </div>
36 {% endfor %}
37 {% else %}
38 {% trans 'No tags found.' %}
39 {% endif %}
39 40 </div>
40 41
41 42 {% endblock %}
@@ -6,7 +6,7 b''
6 6 {% load board %}
7 7
8 8 {% block head %}
9 <title>Neboard - {{ thread.get_opening_post.get_title }}</title>
9 <title>Neboard - {{ thread.get_opening_post.get_title|striptags }}</title>
10 10 {% endblock %}
11 11
12 12 {% block content %}
@@ -33,6 +33,8 b''
33 33 {% for post in posts %}
34 34 {% if bumpable %}
35 35 <div class="post" id="{{ post.id }}">
36 {% elif thread.archived %}
37 <div class="post archive_post" id="{{ post.id }}">
36 38 {% else %}
37 39 <div class="post dead_post" id="{{ post.id }}">
38 40 {% endif %}
@@ -56,8 +58,10 b''
56 58 <a class="post_id" href="#{{ post.id }}">
57 59 ({{ post.id }})</a>
58 60 [{{ post.pub_time }}]
61 {% if not thread.archived %}
59 62 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
60 63 ; return false;">&gt;&gt;</a>]
64 {% endif %}
61 65
62 66 {% if moderator %}
63 67 <span class="moderator_info">
@@ -98,6 +102,8 b''
98 102 </div>
99 103 {% endcache %}
100 104
105 {% if not thread.archived %}
106
101 107 <div class="post-form-w">
102 108 <script src="{% static 'js/panel.js' %}"></script>
103 109 <div class="form-title">{% trans "Reply to thread" %} #{{ thread.get_opening_post.id }}</div>
@@ -114,7 +120,9 b''
114 120 </div>
115 121 </div>
116 122
117 <script src="{% static 'js/thread_update.js' %}"></script>
123 <script src="{% static 'js/thread_update.js' %}"></script>
124 {% endif %}
125
118 126 <script src="{% static 'js/thread.js' %}"></script>
119 127
120 128 {% endspaceless %}
@@ -6,7 +6,7 b''
6 6 {% load board %}
7 7
8 8 {% block head %}
9 <title>Neboard - {{ thread.get_opening_post.get_title }}</title>
9 <title>Neboard - {{ thread.get_opening_post.get_title|striptags }}</title>
10 10 {% endblock %}
11 11
12 12 {% block content %}
@@ -115,7 +115,7 b' class PostTests(TestCase):'
115 115
116 116 all_threads = Post.objects.get_threads()
117 117
118 posts_in_second_page = Post.objects.get_threads(page=1)
118 posts_in_second_page = Post.objects.get_threads(page=2)
119 119 first_post = posts_in_second_page[0]
120 120
121 121 self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id,
@@ -230,4 +230,4 b' class ViewTest(TestCase):'
230 230 self.assertEqual(HTTP_CODE_OK, response.status_code, 'Index page not '
231 231 'opened')
232 232 self.assertEqual('boards/posting_general.html', response.templates[0]
233 .name, 'Index page should open posting_general template') No newline at end of file
233 .name, 'Index page should open posting_general template')
@@ -1,7 +1,7 b''
1 1 from django.conf.urls import patterns, url, include
2 2 from boards import views
3 3 from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed
4 from boards.views.api import api_get_threaddiff
4 from boards.views import api
5 5
6 6 js_info_dict = {
7 7 'packages': ('boards',),
@@ -14,6 +14,9 b" urlpatterns = patterns('',"
14 14 # /boards/page/
15 15 url(r'^page/(?P<page>\w+)/$', views.index, name='index'),
16 16
17 url(r'^archive/$', views.archive, name='archive'),
18 url(r'^archive/page/(?P<page>\w+)/$', views.archive, name='archive'),
19
17 20 # login page
18 21 url(r'^login/$', views.login, name='login'),
19 22
@@ -54,7 +57,13 b" urlpatterns = patterns('',"
54 57 url(r'^jsi18n/$', 'boards.views.cached_js_catalog', js_info_dict, name='js_info_dict'),
55 58
56 59 # API
57 url(r'^api/post/(?P<post_id>\w+)/$', views.get_post, name="get_post"),
60 url(r'^api/post/(?P<post_id>\w+)/$', api.get_post, name="get_post"),
58 61 url(r'^api/diff_thread/(?P<thread_id>\w+)/(?P<last_update_time>\w+)/$',
59 api_get_threaddiff, name="get_thread_diff"),
62 api.api_get_threaddiff, name="get_thread_diff"),
63 url(r'^api/threads/(?P<count>\w+)/$', api.api_get_threads,
64 name='get_threads'),
65 url(r'api/tags/$', api.api_get_tags, name='get_tags'),
66 url(r'api/thread/(?P<opening_post_id>\w+)/$', api.api_get_thread_posts,
67 name='get_thread'),
68
60 69 )
@@ -1,3 +1,10 b''
1 from datetime import datetime, timedelta
2
3 from django.db.models import Count
4
5
6 OLD_USER_AGE_DAYS = 90
7
1 8 __author__ = 'neko259'
2 9
3 10 import hashlib
@@ -15,13 +22,14 b' from django.utils import timezone'
15 22 from django.db import transaction
16 23 from django.views.decorators.cache import cache_page
17 24 from django.views.i18n import javascript_catalog
25 from django.core.paginator import Paginator
18 26
19 27 from boards import forms
20 28 import boards
21 29 from boards import utils
22 30 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
23 31 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
24 from boards.models import Post, Tag, Ban, User
32 from boards.models import Post, Tag, Ban, User, Thread
25 33 from boards.models.post import SETTING_MODERATE, REGEX_REPLY
26 34 from boards.models.user import RANK_USER
27 35 from boards import authors
@@ -33,8 +41,10 b" BAN_REASON_SPAM = 'Autoban: spam bot'"
33 41 MODE_GALLERY = 'gallery'
34 42 MODE_NORMAL = 'normal'
35 43
44 DEFAULT_PAGE = 1
36 45
37 def index(request, page=0):
46
47 def index(request, page=DEFAULT_PAGE):
38 48 context = _init_default_context(request)
39 49
40 50 if utils.need_include_captcha(request):
@@ -64,20 +74,36 b' def index(request, page=0):'
64 74 # TODO Make this generic for tag and threads list pages
65 75 context['threads'] = None if len(threads) == 0 else threads
66 76 context['form'] = form
67 context['current_page'] = int(page)
68 77
69 page_count = Post.objects.get_thread_page_count()
70 context['pages'] = range(page_count)
71 page = int(page)
72 if page < page_count - 1:
73 context['next_page'] = str(page + 1)
74 if page > 0:
75 context['prev_page'] = str(page - 1)
78 paginator = Paginator(Thread.objects.filter(archived=False),
79 neboard.settings.THREADS_PER_PAGE)
80 _get_page_context(paginator, context, page)
76 81
77 82 return render(request, 'boards/posting_general.html',
78 83 context)
79 84
80 85
86 def archive(request, page=DEFAULT_PAGE):
87 """
88 Get archived posts
89 """
90
91 context = _init_default_context(request)
92
93 threads = []
94 for thread_to_show in Post.objects.get_threads(page=int(page),
95 archived=True):
96 threads.append(_get_template_thread(thread_to_show))
97
98 context['threads'] = threads
99
100 paginator = Paginator(Thread.objects.filter(archived=True),
101 neboard.settings.THREADS_PER_PAGE)
102 _get_page_context(paginator, context, page)
103
104 return render(request, 'boards/archive.html', context)
105
106
81 107 @transaction.atomic
82 108 def _new_post(request, form, opening_post=None):
83 109 """Add a new post (in thread or as a reply)."""
@@ -129,10 +155,10 b' def _new_post(request, form, opening_pos'
129 155 return redirect(thread, post_id=thread_to_show)
130 156
131 157
132 def tag(request, tag_name, page=0):
158 def tag(request, tag_name, page=DEFAULT_PAGE):
133 159 """
134 160 Get all tag threads. Threads are split in pages, so some page is
135 requested. Default page is 0.
161 requested.
136 162 """
137 163
138 164 tag = get_object_or_404(Tag, name=tag_name)
@@ -157,15 +183,10 b' def tag(request, tag_name, page=0):'
157 183 context = _init_default_context(request)
158 184 context['threads'] = None if len(threads) == 0 else threads
159 185 context['tag'] = tag
160 context['current_page'] = int(page)
161 186
162 page_count = Post.objects.get_thread_page_count(tag=tag)
163 context['pages'] = range(page_count)
164 page = int(page)
165 if page < page_count - 1:
166 context['next_page'] = str(page + 1)
167 if page > 0:
168 context['prev_page'] = str(page - 1)
187 paginator = Paginator(Post.objects.get_threads(tag=tag),
188 neboard.settings.THREADS_PER_PAGE)
189 _get_page_context(paginator, context, page)
169 190
170 191 context['form'] = form
171 192
@@ -184,7 +205,12 b' def thread(request, post_id, mode=MODE_N'
184 205 kwargs = {}
185 206
186 207 opening_post = get_object_or_404(Post, id=post_id)
187 if request.method == 'POST':
208
209 # If this is not OP, don't show it as it is
210 if not opening_post.is_opening():
211 raise Http404
212
213 if request.method == 'POST' and not opening_post.thread_new.archived:
188 214 form = postFormClass(request.POST, request.FILES,
189 215 error_class=PlainErrorList, **kwargs)
190 216 form.session = request.session
@@ -422,20 +448,6 b' def api_get_post(request, post_id):'
422 448 return HttpResponse(content=json)
423 449
424 450
425 def get_post(request, post_id):
426 """Get the html of a post. Used for popups."""
427
428 post = get_object_or_404(Post, id=post_id)
429 thread = post.thread_new
430
431 context = RequestContext(request)
432 context["post"] = post
433 context["can_bump"] = thread.can_bump()
434 if "truncated" in request.GET:
435 context["truncated"] = True
436
437 return render(request, 'boards/post.html', context)
438
439 451 @cache_page(86400)
440 452 def cached_js_catalog(request, domain='djangojs', packages=None):
441 453 return javascript_catalog(request, domain, packages)
@@ -491,10 +503,16 b' def _get_user(request):'
491 503 md5.update(session.session_key)
492 504 new_id = md5.hexdigest()
493 505
506 while User.objects.filter(user_id=new_id).exists():
507 md5.update(str(timezone.now()))
508 new_id = md5.hexdigest()
509
494 510 time_now = timezone.now()
495 511 user = User.objects.create(user_id=new_id, rank=RANK_USER,
496 512 registration_time=time_now)
497 513
514 _delete_old_users()
515
498 516 session['user_id'] = user.id
499 517 else:
500 518 user = User.objects.get(id=session['user_id'])
@@ -562,3 +580,26 b' def _get_template_thread(thread_to_show)'
562 580 'last_replies': last_replies,
563 581 'skipped_replies': skipped_replies_count,
564 582 }
583
584
585 def _delete_old_users():
586 """
587 Delete users with no favorite tags and posted messages. These can be spam
588 bots or just old user accounts
589 """
590
591 old_registration_date = datetime.now().date() - timedelta(OLD_USER_AGE_DAYS)
592
593 for user in User.objects.annotate(tags_count=Count('fav_tags')).filter(
594 tags_count=0).filter(registration_time__lt=old_registration_date):
595 if not Post.objects.filter(user=user).exists():
596 user.delete()
597
598
599 def _get_page_context(paginator, context, page):
600 """
601 Get pagination context variables
602 """
603
604 context['paginator'] = paginator
605 context['current_page'] = paginator.page(int(page))
@@ -2,15 +2,20 b' from datetime import datetime'
2 2 import json
3 3 from django.db import transaction
4 4 from django.http import HttpResponse
5 from django.shortcuts import get_object_or_404
5 from django.shortcuts import get_object_or_404, render
6 from django.template import RequestContext
6 7 from django.utils import timezone
7 8 from boards.forms import ThreadForm, PlainErrorList
8 from boards.models import Post
9 from boards.views import get_post, _datetime_to_epoch, _new_post, \
9 from boards.models import Post, Thread, Tag
10 from boards.views import _datetime_to_epoch, _new_post, \
10 11 _ban_current_user
11 12
12 13 __author__ = 'neko259'
13 14
15 PARAMETER_TRUNCATED = 'truncated'
16 PARAMETER_TAG = 'tag'
17 PARAMETER_OFFSET = 'offset'
18
14 19
15 20 @transaction.atomic
16 21 def api_get_threaddiff(request, thread_id, last_update_time):
@@ -25,7 +30,7 b' def api_get_threaddiff(request, thread_i'
25 30 'added': [],
26 31 'updated': [],
27 32 'last_update': None,
28 }
33 }
29 34 added_posts = Post.objects.filter(thread_new=thread,
30 35 pub_time__gt=filter_time) \
31 36 .order_by('pub_time')
@@ -73,3 +78,108 b' def api_get_threaddiff(request, thread_i'
73 78 # }
74 79 #
75 80 # return HttpResponse(content=json.dumps(response))
81
82
83 def get_post(request, post_id):
84 """
85 Get the html of a post. Used for popups. Post can be truncated if used
86 in threads list with 'truncated' get parameter.
87 """
88
89 post = get_object_or_404(Post, id=post_id)
90 thread = post.thread_new
91
92 context = RequestContext(request)
93 context['post'] = post
94 context['can_bump'] = thread.can_bump()
95 if PARAMETER_TRUNCATED in request.GET:
96 context[PARAMETER_TRUNCATED] = True
97
98 return render(request, 'boards/post.html', context)
99
100
101 # TODO Test this
102 def api_get_threads(request, count):
103 """
104 Get the JSON thread opening posts list.
105 Parameters that can be used for filtering:
106 tag, offset (from which thread to get results)
107 """
108
109 if PARAMETER_TAG in request.GET:
110 tag_name = request.GET[PARAMETER_TAG]
111 if tag_name is not None:
112 tag = get_object_or_404(Tag, name=tag_name)
113 threads = tag.threads.filter(archived=False)
114 else:
115 threads = Thread.objects.filter(archived=False)
116
117 if PARAMETER_OFFSET in request.GET:
118 offset = request.GET[PARAMETER_OFFSET]
119 offset = int(offset) if offset is not None else 0
120 else:
121 offset = 0
122
123 threads = threads.order_by('-bump_time')
124 threads = threads[offset:offset + int(count)]
125
126 opening_posts = []
127 for thread in threads:
128 opening_post = thread.get_opening_post()
129
130 # TODO Add pub time, tags, replies and images count
131 post_json = {
132 'id': opening_post.id,
133 'title': opening_post.title,
134 'text': opening_post.text.rendered,
135 }
136 if opening_post.image:
137 post_json['image'] = opening_post.image.url
138 post_json['image_preview'] = opening_post.image.url_200x150
139 opening_posts.append(post_json)
140
141 return HttpResponse(content=json.dumps(opening_posts))
142
143
144 # TODO Test this
145 def api_get_tags(request):
146 """
147 Get all tags or user tags.
148 """
149
150 # TODO Get favorite tags for the given user ID
151
152 tags = Tag.objects.get_not_empty_tags()
153 tag_names = []
154 for tag in tags:
155 tag_names.append(tag.name)
156
157 return HttpResponse(content=json.dumps(tag_names))
158
159
160 # TODO The result can be cached by the thread last update time
161 # TODO Test this
162 def api_get_thread_posts(request, opening_post_id):
163 """
164 Get the JSON array of thread posts
165 """
166
167 opening_post = get_object_or_404(Post, id=opening_post_id)
168 thread = opening_post.thread_new
169 posts = thread.get_replies()
170
171 json_post_list = []
172
173 for post in posts:
174 # TODO Add pub time and replies
175 post_json = {
176 'id': post.id,
177 'title': post.title,
178 'text': post.text.rendered,
179 }
180 if post.image:
181 post_json['image'] = post.image.url
182 post_json['image_preview'] = post.image.url_200x150
183 json_post_list.append(post_json)
184
185 return HttpResponse(content=json.dumps(json_post_list))
@@ -4,4 +4,13 b' styles.'
4 4 * Showing notification in page title when new posts are loaded into the open
5 5 thread.
6 6 * Thread moderation fixes
7 * Added new gallery with search links and image metadata No newline at end of file
7 * Added new gallery with search links and image metadata
8
9 # 1.6 Amon #
10 * Deleted threads are moved to archive instead of permanent delete
11 * User management fixes and optimizations
12 * Markdown fixes
13 * Pagination changes. Pages counter now starts from 1 instead of 0
14 * Added API for viewing threads and posts
15 * New tag popularity algorithm
16 * Tags list page changes. Now tags list is more like a tag cloud
@@ -34,8 +34,6 b' post or its part (delimited by N charact'
34 34 [NOT STARTED] Get thread graph image using pygraphviz
35 35 [NOT STARTED] Creating post via AJAX without reloading page
36 36 [NOT STARTED] Subscribing to tag via AJAX
37 [NOT STARTED] Count posts by user not by current active posts, but by adding 1
38 on evety posting
39 37
40 38 = Bugs =
41 39 [DONE] Fix bug with creating threads from tag view
General Comments 0
You need to be logged in to leave comments. Login now