##// END OF EJS Templates
Fixed 'last update' time of the thread in bumplimit. Added database cache for thread view and threads list.
neko259 -
r186:d5b3b0c2 default
parent child Browse files
Show More
@@ -0,0 +1,74 b''
1 # -*- coding: utf-8 -*-
2 import 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 'Post.bump_time'
12 db.add_column(u'boards_post', 'bump_time',
13 self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2013, 9, 14, 0, 0)),
14 keep_default=False)
15
16
17 def backwards(self, orm):
18 # Deleting field 'Post.bump_time'
19 db.delete_column(u'boards_post', 'bump_time')
20
21
22 models = {
23 u'boards.ban': {
24 'Meta': {'object_name': 'Ban'},
25 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
26 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
27 },
28 u'boards.post': {
29 'Meta': {'object_name': 'Post'},
30 '_text_rendered': ('django.db.models.fields.TextField', [], {}),
31 'bump_time': ('django.db.models.fields.DateTimeField', [], {}),
32 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
33 'image': ('boards.thumbs.ImageWithThumbsField', [], {'max_length': '100', 'blank': 'True'}),
34 'image_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
35 'image_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
36 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
37 'parent': ('django.db.models.fields.BigIntegerField', [], {'default': '-1'}),
38 'poster_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
39 'poster_user_agent': ('django.db.models.fields.TextField', [], {}),
40 'pub_time': ('django.db.models.fields.DateTimeField', [], {}),
41 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'re+'", 'null': 'True', 'symmetrical': 'False', 'to': u"orm['boards.Post']"}),
42 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['boards.Tag']", 'symmetrical': 'False'}),
43 'text': ('markupfield.fields.MarkupField', [], {'rendered_field': 'True'}),
44 'text_markup_type': ('django.db.models.fields.CharField', [], {'default': "'markdown'", 'max_length': '30'}),
45 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['boards.Post']", 'null': 'True'}),
46 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
47 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['boards.User']", 'null': 'True'})
48 },
49 u'boards.setting': {
50 'Meta': {'object_name': 'Setting'},
51 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
52 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
53 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['boards.User']"}),
54 'value': ('django.db.models.fields.CharField', [], {'max_length': '50'})
55 },
56 u'boards.tag': {
57 'Meta': {'object_name': 'Tag'},
58 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
59 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
60 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tag+'", 'null': 'True', 'symmetrical': 'False', 'to': u"orm['boards.Post']"})
61 },
62 u'boards.user': {
63 'Meta': {'object_name': 'User'},
64 'fav_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['boards.Tag']", 'null': 'True', 'blank': 'True'}),
65 'fav_threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'symmetrical': 'False', 'to': u"orm['boards.Post']"}),
66 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
67 'last_access_time': ('django.db.models.fields.DateTimeField', [], {}),
68 'rank': ('django.db.models.fields.IntegerField', [], {}),
69 'registration_time': ('django.db.models.fields.DateTimeField', [], {}),
70 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '50'})
71 }
72 }
73
74 complete_apps = ['boards'] No newline at end of file
@@ -1,329 +1,332 b''
1 import os
1 import os
2 from random import random
2 from random import random
3 import re
3 import re
4 import time
4 import time
5 import math
5 import math
6
6
7 from django.db import models
7 from django.db import models
8 from django.db.models import Count
8 from django.db.models import Count
9 from django.http import Http404
9 from django.http import Http404
10 from django.utils import timezone
10 from django.utils import timezone
11 from markupfield.fields import MarkupField
11 from markupfield.fields import MarkupField
12
12
13 from neboard import settings
13 from neboard import settings
14 import thumbs
14 import thumbs
15
15
16 IMAGE_THUMB_SIZE = (200, 150)
16 IMAGE_THUMB_SIZE = (200, 150)
17
17
18 TITLE_MAX_LENGTH = 50
18 TITLE_MAX_LENGTH = 50
19
19
20 DEFAULT_MARKUP_TYPE = 'markdown'
20 DEFAULT_MARKUP_TYPE = 'markdown'
21
21
22 NO_PARENT = -1
22 NO_PARENT = -1
23 NO_IP = '0.0.0.0'
23 NO_IP = '0.0.0.0'
24 UNKNOWN_UA = ''
24 UNKNOWN_UA = ''
25 ALL_PAGES = -1
25 ALL_PAGES = -1
26 OPENING_POST_POPULARITY_WEIGHT = 2
26 OPENING_POST_POPULARITY_WEIGHT = 2
27 IMAGES_DIRECTORY = 'images/'
27 IMAGES_DIRECTORY = 'images/'
28 FILE_EXTENSION_DELIMITER = '.'
28 FILE_EXTENSION_DELIMITER = '.'
29
29
30 RANK_ADMIN = 0
30 RANK_ADMIN = 0
31 RANK_MODERATOR = 10
31 RANK_MODERATOR = 10
32 RANK_USER = 100
32 RANK_USER = 100
33
33
34
34
35 class PostManager(models.Manager):
35 class PostManager(models.Manager):
36
36
37 def create_post(self, title, text, image=None, thread=None,
37 def create_post(self, title, text, image=None, thread=None,
38 ip=NO_IP, tags=None, user=None):
38 ip=NO_IP, tags=None, user=None):
39 post = self.create(title=title,
39 post = self.create(title=title,
40 text=text,
40 text=text,
41 pub_time=timezone.now(),
41 pub_time=timezone.now(),
42 thread=thread,
42 thread=thread,
43 image=image,
43 image=image,
44 poster_ip=ip,
44 poster_ip=ip,
45 poster_user_agent=UNKNOWN_UA,
45 poster_user_agent=UNKNOWN_UA,
46 last_edit_time=timezone.now(),
46 last_edit_time=timezone.now(),
47 bump_time=timezone.now(),
47 user=user)
48 user=user)
48
49
49 if tags:
50 if tags:
50 map(post.tags.add, tags)
51 map(post.tags.add, tags)
51 for tag in tags:
52 for tag in tags:
52 tag.threads.add(post)
53 tag.threads.add(post)
53
54
54 if thread:
55 if thread:
55 thread.replies.add(post)
56 thread.replies.add(post)
56 thread.bump()
57 thread.bump()
58 thread.last_edit_time = timezone.now()
59 thread.save()
57 else:
60 else:
58 self._delete_old_threads()
61 self._delete_old_threads()
59
62
60 return post
63 return post
61
64
62 def delete_post(self, post):
65 def delete_post(self, post):
63 if post.replies.count() > 0:
66 if post.replies.count() > 0:
64 map(self.delete_post, post.replies.all())
67 map(self.delete_post, post.replies.all())
65 post.delete()
68 post.delete()
66
69
67 def delete_posts_by_ip(self, ip):
70 def delete_posts_by_ip(self, ip):
68 posts = self.filter(poster_ip=ip)
71 posts = self.filter(poster_ip=ip)
69 map(self.delete_post, posts)
72 map(self.delete_post, posts)
70
73
71 def get_threads(self, tag=None, page=ALL_PAGES,
74 def get_threads(self, tag=None, page=ALL_PAGES,
72 order_by='-last_edit_time'):
75 order_by='-bump_time'):
73 if tag:
76 if tag:
74 threads = tag.threads
77 threads = tag.threads
75
78
76 # TODO This needs to be uncommented when 'all tags' view won't
79 # TODO This needs to be uncommented when 'all tags' view won't
77 # use this method to get threads for tag
80 # use this method to get threads for tag
78
81
79 # if threads.count() == 0:
82 # if threads.count() == 0:
80 # raise Http404
83 # raise Http404
81 else:
84 else:
82 threads = self.filter(thread=None)
85 threads = self.filter(thread=None)
83
86
84 threads = threads.order_by(order_by)
87 threads = threads.order_by(order_by)
85
88
86 if page != ALL_PAGES:
89 if page != ALL_PAGES:
87 thread_count = threads.count()
90 thread_count = threads.count()
88
91
89 if page < self.get_thread_page_count(tag=tag):
92 if page < self.get_thread_page_count(tag=tag):
90 start_thread = page * settings.THREADS_PER_PAGE
93 start_thread = page * settings.THREADS_PER_PAGE
91 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
94 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
92 thread_count)
95 thread_count)
93 threads = threads[start_thread:end_thread]
96 threads = threads[start_thread:end_thread]
94
97
95 return threads
98 return threads
96
99
97 def get_thread(self, opening_post_id):
100 def get_thread(self, opening_post_id):
98 try:
101 try:
99 opening_post = self.get(id=opening_post_id, thread=None)
102 opening_post = self.get(id=opening_post_id, thread=None)
100 except Post.DoesNotExist:
103 except Post.DoesNotExist:
101 raise Http404
104 raise Http404
102
105
103 if opening_post.replies:
106 if opening_post.replies:
104 thread = [opening_post]
107 thread = [opening_post]
105 thread.extend(opening_post.replies.all())
108 thread.extend(opening_post.replies.all())
106
109
107 return thread
110 return thread
108
111
109 def exists(self, post_id):
112 def exists(self, post_id):
110 posts = self.filter(id=post_id)
113 posts = self.filter(id=post_id)
111
114
112 return posts.count() > 0
115 return posts.count() > 0
113
116
114 def get_thread_page_count(self, tag=None):
117 def get_thread_page_count(self, tag=None):
115 if tag:
118 if tag:
116 threads = self.filter(thread=None, tags=tag)
119 threads = self.filter(thread=None, tags=tag)
117 else:
120 else:
118 threads = self.filter(thread=None)
121 threads = self.filter(thread=None)
119
122
120 return int(math.ceil(threads.count() / float(
123 return int(math.ceil(threads.count() / float(
121 settings.THREADS_PER_PAGE)))
124 settings.THREADS_PER_PAGE)))
122
125
123 def _delete_old_threads(self):
126 def _delete_old_threads(self):
124 """
127 """
125 Preserves maximum thread count. If there are too many threads,
128 Preserves maximum thread count. If there are too many threads,
126 delete the old ones.
129 delete the old ones.
127 """
130 """
128
131
129 # TODO Move old threads to the archive instead of deleting them.
132 # TODO Move old threads to the archive instead of deleting them.
130 # Maybe make some 'old' field in the model to indicate the thread
133 # Maybe make some 'old' field in the model to indicate the thread
131 # must not be shown and be able for replying.
134 # must not be shown and be able for replying.
132
135
133 threads = self.get_threads()
136 threads = self.get_threads()
134 thread_count = len(threads)
137 thread_count = len(threads)
135
138
136 if thread_count > settings.MAX_THREAD_COUNT:
139 if thread_count > settings.MAX_THREAD_COUNT:
137 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
140 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
138 old_threads = threads[thread_count - num_threads_to_delete:]
141 old_threads = threads[thread_count - num_threads_to_delete:]
139
142
140 map(self.delete_post, old_threads)
143 map(self.delete_post, old_threads)
141
144
142
145
143 class TagManager(models.Manager):
146 class TagManager(models.Manager):
144
147
145 def get_not_empty_tags(self):
148 def get_not_empty_tags(self):
146 tags = self.annotate(Count('threads')) \
149 tags = self.annotate(Count('threads')) \
147 .filter(threads__count__gt=0).order_by('name')
150 .filter(threads__count__gt=0).order_by('name')
148
151
149 return tags
152 return tags
150
153
151
154
152 class Tag(models.Model):
155 class Tag(models.Model):
153 """
156 """
154 A tag is a text node assigned to the post. The tag serves as a board
157 A tag is a text node assigned to the post. The tag serves as a board
155 section. There can be multiple tags for each message
158 section. There can be multiple tags for each message
156 """
159 """
157
160
158 objects = TagManager()
161 objects = TagManager()
159
162
160 name = models.CharField(max_length=100)
163 name = models.CharField(max_length=100)
161 threads = models.ManyToManyField('Post', null=True,
164 threads = models.ManyToManyField('Post', null=True,
162 blank=True, related_name='tag+')
165 blank=True, related_name='tag+')
163
166
164 def __unicode__(self):
167 def __unicode__(self):
165 return self.name
168 return self.name
166
169
167 def is_empty(self):
170 def is_empty(self):
168 return self.get_post_count() == 0
171 return self.get_post_count() == 0
169
172
170 def get_post_count(self):
173 def get_post_count(self):
171 return self.threads.count()
174 return self.threads.count()
172
175
173 def get_popularity(self):
176 def get_popularity(self):
174 posts_with_tag = Post.objects.get_threads(tag=self)
177 posts_with_tag = Post.objects.get_threads(tag=self)
175 reply_count = 0
178 reply_count = 0
176 for post in posts_with_tag:
179 for post in posts_with_tag:
177 reply_count += post.get_reply_count()
180 reply_count += post.get_reply_count()
178 reply_count += OPENING_POST_POPULARITY_WEIGHT
181 reply_count += OPENING_POST_POPULARITY_WEIGHT
179
182
180 return reply_count
183 return reply_count
181
184
182
185
183 class Post(models.Model):
186 class Post(models.Model):
184 """A post is a message."""
187 """A post is a message."""
185
188
186 objects = PostManager()
189 objects = PostManager()
187
190
188 def _update_image_filename(self, filename):
191 def _update_image_filename(self, filename):
189 """Get unique image filename"""
192 """Get unique image filename"""
190
193
191 path = IMAGES_DIRECTORY
194 path = IMAGES_DIRECTORY
192 new_name = str(int(time.mktime(time.gmtime())))
195 new_name = str(int(time.mktime(time.gmtime())))
193 new_name += str(int(random() * 1000))
196 new_name += str(int(random() * 1000))
194 new_name += FILE_EXTENSION_DELIMITER
197 new_name += FILE_EXTENSION_DELIMITER
195 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
198 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
196
199
197 return os.path.join(path, new_name)
200 return os.path.join(path, new_name)
198
201
199 title = models.CharField(max_length=TITLE_MAX_LENGTH)
202 title = models.CharField(max_length=TITLE_MAX_LENGTH)
200 pub_time = models.DateTimeField()
203 pub_time = models.DateTimeField()
201 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
204 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
202 escape_html=False)
205 escape_html=False)
203
206
204 image_width = models.IntegerField(default=0)
207 image_width = models.IntegerField(default=0)
205 image_height = models.IntegerField(default=0)
208 image_height = models.IntegerField(default=0)
206
209
207 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
210 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
208 blank=True, sizes=(IMAGE_THUMB_SIZE,),
211 blank=True, sizes=(IMAGE_THUMB_SIZE,),
209 width_field='image_width',
212 width_field='image_width',
210 height_field='image_height')
213 height_field='image_height')
211
214
212 poster_ip = models.GenericIPAddressField()
215 poster_ip = models.GenericIPAddressField()
213 poster_user_agent = models.TextField()
216 poster_user_agent = models.TextField()
214
217
215 # TODO Remove this field after everything has been updated to 'thread'
218 # TODO Remove this field after everything has been updated to 'thread'
216 parent = models.BigIntegerField(default=NO_PARENT)
219 parent = models.BigIntegerField(default=NO_PARENT)
217
220
218 thread = models.ForeignKey('Post', null=True, default=None)
221 thread = models.ForeignKey('Post', null=True, default=None)
219 tags = models.ManyToManyField(Tag)
222 tags = models.ManyToManyField(Tag)
220 last_edit_time = models.DateTimeField()
223 last_edit_time = models.DateTimeField()
224 bump_time = models.DateTimeField()
221 user = models.ForeignKey('User', null=True, default=None)
225 user = models.ForeignKey('User', null=True, default=None)
222
226
223 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
227 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
224 blank=True, related_name='re+')
228 blank=True, related_name='re+')
225
229
226 def __unicode__(self):
230 def __unicode__(self):
227 return '#' + str(self.id) + ' ' + self.title + ' (' + \
231 return '#' + str(self.id) + ' ' + self.title + ' (' + \
228 self.text.raw[:50] + ')'
232 self.text.raw[:50] + ')'
229
233
230 def get_title(self):
234 def get_title(self):
231 title = self.title
235 title = self.title
232 if len(title) == 0:
236 if len(title) == 0:
233 title = self.text.raw[:20]
237 title = self.text.raw[:20]
234
238
235 return title
239 return title
236
240
237 def get_reply_count(self):
241 def get_reply_count(self):
238 return self.replies.count()
242 return self.replies.count()
239
243
240 def get_images_count(self):
244 def get_images_count(self):
241 images_count = 1 if self.image else 0
245 images_count = 1 if self.image else 0
242 images_count += self.replies.filter(image_width__gt=0).count()
246 images_count += self.replies.filter(image_width__gt=0).count()
243
247
244 return images_count
248 return images_count
245
249
246 def can_bump(self):
250 def can_bump(self):
247 """Check if the thread can be bumped by replying"""
251 """Check if the thread can be bumped by replying"""
248
252
249 post_count = self.get_reply_count() + 1
253 post_count = self.get_reply_count() + 1
250
254
251 return post_count <= settings.MAX_POSTS_PER_THREAD
255 return post_count <= settings.MAX_POSTS_PER_THREAD
252
256
253 def bump(self):
257 def bump(self):
254 """Bump (move to up) thread"""
258 """Bump (move to up) thread"""
255
259
256 if self.can_bump():
260 if self.can_bump():
257 self.last_edit_time = timezone.now()
261 self.bump_time = timezone.now()
258 self.save()
259
262
260 def get_last_replies(self):
263 def get_last_replies(self):
261 if settings.LAST_REPLIES_COUNT > 0:
264 if settings.LAST_REPLIES_COUNT > 0:
262 reply_count = self.get_reply_count()
265 reply_count = self.get_reply_count()
263
266
264 if reply_count > 0:
267 if reply_count > 0:
265 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
268 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
266 reply_count)
269 reply_count)
267 last_replies = self.replies.all()[reply_count -
270 last_replies = self.replies.all()[reply_count -
268 reply_count_to_show:]
271 reply_count_to_show:]
269
272
270 return last_replies
273 return last_replies
271
274
272
275
273 class User(models.Model):
276 class User(models.Model):
274
277
275 user_id = models.CharField(max_length=50)
278 user_id = models.CharField(max_length=50)
276 rank = models.IntegerField()
279 rank = models.IntegerField()
277
280
278 registration_time = models.DateTimeField()
281 registration_time = models.DateTimeField()
279 last_access_time = models.DateTimeField()
282 last_access_time = models.DateTimeField()
280
283
281 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
284 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
282 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
285 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
283 blank=True)
286 blank=True)
284
287
285 def save_setting(self, name, value):
288 def save_setting(self, name, value):
286 setting, created = Setting.objects.get_or_create(name=name, user=self)
289 setting, created = Setting.objects.get_or_create(name=name, user=self)
287 setting.value = value
290 setting.value = value
288 setting.save()
291 setting.save()
289
292
290 return setting
293 return setting
291
294
292 def get_setting(self, name):
295 def get_setting(self, name):
293 if Setting.objects.filter(name=name, user=self).exists():
296 if Setting.objects.filter(name=name, user=self).exists():
294 setting = Setting.objects.get(name=name, user=self)
297 setting = Setting.objects.get(name=name, user=self)
295 setting_value = setting.value
298 setting_value = setting.value
296 else:
299 else:
297 setting_value = None
300 setting_value = None
298
301
299 return setting_value
302 return setting_value
300
303
301 def is_moderator(self):
304 def is_moderator(self):
302 return RANK_MODERATOR >= self.rank
305 return RANK_MODERATOR >= self.rank
303
306
304 def get_sorted_fav_tags(self):
307 def get_sorted_fav_tags(self):
305 tags = self.fav_tags.annotate(Count('threads'))\
308 tags = self.fav_tags.annotate(Count('threads'))\
306 .filter(threads__count__gt=0).order_by('name')
309 .filter(threads__count__gt=0).order_by('name')
307
310
308 return tags
311 return tags
309
312
310 def get_post_count(self):
313 def get_post_count(self):
311 return Post.objects.filter(user=self).count()
314 return Post.objects.filter(user=self).count()
312
315
313 def __unicode__(self):
316 def __unicode__(self):
314 return self.user_id + '(' + str(self.rank) + ')'
317 return self.user_id + '(' + str(self.rank) + ')'
315
318
316
319
317 class Setting(models.Model):
320 class Setting(models.Model):
318
321
319 name = models.CharField(max_length=50)
322 name = models.CharField(max_length=50)
320 value = models.CharField(max_length=50)
323 value = models.CharField(max_length=50)
321 user = models.ForeignKey(User)
324 user = models.ForeignKey(User)
322
325
323
326
324 class Ban(models.Model):
327 class Ban(models.Model):
325
328
326 ip = models.GenericIPAddressField()
329 ip = models.GenericIPAddressField()
327
330
328 def __unicode__(self):
331 def __unicode__(self):
329 return self.ip
332 return self.ip
@@ -1,190 +1,193 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load markup %}
4 {% load markup %}
5 {% load cache %}
5
6
6 {% block head %}
7 {% block head %}
7 {% if tag %}
8 {% if tag %}
8 <title>Neboard - {{ tag.name }}</title>
9 <title>Neboard - {{ tag.name }}</title>
9 {% else %}
10 {% else %}
10 <title>Neboard</title>
11 <title>Neboard</title>
11 {% endif %}
12 {% endif %}
12 {% endblock %}
13 {% endblock %}
13
14
14 {% block content %}
15 {% block content %}
15
16
16 {% if tag %}
17 {% if tag %}
17 <div class="tag_info">
18 <div class="tag_info">
18 <h2>{% trans 'Tag: ' %}{{ tag.name }}
19 <h2>{% trans 'Tag: ' %}{{ tag.name }}
19 {% if tag in user.fav_tags.all %}
20 {% if tag in user.fav_tags.all %}
20 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
21 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
21 class="fav">β˜…</a>
22 class="fav">β˜…</a>
22 {% else %}
23 {% else %}
23 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
24 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
24 class="not_fav">β˜…</a>
25 class="not_fav">β˜…</a>
25 {% endif %}
26 {% endif %}
26 </h2>
27 </h2>
27 </div>
28 </div>
28 {% endif %}
29 {% endif %}
29
30
30 {% if threads %}
31 {% if threads %}
31 {% for thread in threads %}
32 {% for thread in threads %}
33 {% cache 600 thread_cache thread.thread.last_edit_time %}
32 <div class="thread">
34 <div class="thread">
33 {% if thread.bumpable %}
35 {% if thread.bumpable %}
34 <div class="post" id="{{ thread.thread.id }}">
36 <div class="post" id="{{ thread.thread.id }}">
35 {% else %}
37 {% else %}
36 <div class="post dead_post" id="{{ thread.thread.id }}">
38 <div class="post dead_post" id="{{ thread.thread.id }}">
37 {% endif %}
39 {% endif %}
38 {% if thread.thread.image %}
40 {% if thread.thread.image %}
39 <div class="image">
41 <div class="image">
40 <a class="fancy"
42 <a class="fancy"
41 href="{{ thread.thread.image.url }}"><img
43 href="{{ thread.thread.image.url }}"><img
42 src="{{ thread.thread.image.url_200x150 }}"
44 src="{{ thread.thread.image.url_200x150 }}"
43 alt="{% trans 'Post image' %}"
45 alt="{% trans 'Post image' %}"
44 data-width="{{ thread.thread.image_width }}"
46 data-width="{{ thread.thread.image_width }}"
45 data-height="{{ thread.thread.image_height }}" />
47 data-height="{{ thread.thread.image_height }}" />
46 </a>
48 </a>
47 </div>
49 </div>
48 {% endif %}
50 {% endif %}
49 <div class="message">
51 <div class="message">
50 <div class="post-info">
52 <div class="post-info">
51 <span class="title">{{ thread.thread.title }}</span>
53 <span class="title">{{ thread.thread.title }}</span>
52 <a class="post_id" href="{% url 'thread' thread.thread.id %}"
54 <a class="post_id" href="{% url 'thread' thread.thread.id %}"
53 >(#{{ thread.thread.id }})</a>
55 >(#{{ thread.thread.id }})</a>
54 [{{ thread.thread.pub_time }}]
56 [{{ thread.thread.pub_time }}]
55 [<a class="link" href="{% url 'thread' thread.thread.id %}#form"
57 [<a class="link" href="{% url 'thread' thread.thread.id %}#form"
56 >{% trans "Reply" %}</a>]
58 >{% trans "Reply" %}</a>]
57
59
58 {% if moderator %}
60 {% if moderator %}
59 <span class="moderator_info">
61 <span class="moderator_info">
60 [<a href="{% url 'delete' post_id=thread.thread.id %}?next={{ request.path }}"
62 [<a href="{% url 'delete' post_id=thread.thread.id %}?next={{ request.path }}"
61 >{% trans 'Delete' %}</a>]
63 >{% trans 'Delete' %}</a>]
62 ({{ thread.thread.poster_ip }})
64 ({{ thread.thread.poster_ip }})
63 [<a href="{% url 'ban' post_id=thread.thread.id %}?next={{ request.path }}"
65 [<a href="{% url 'ban' post_id=thread.thread.id %}?next={{ request.path }}"
64 >{% trans 'Ban IP' %}</a>]
66 >{% trans 'Ban IP' %}</a>]
65 </span>
67 </span>
66 {% endif %}
68 {% endif %}
67 </div>
69 </div>
68 {% autoescape off %}
70 {% autoescape off %}
69 {{ thread.thread.text.rendered|truncatewords_html:50 }}
71 {{ thread.thread.text.rendered|truncatewords_html:50 }}
70 {% endautoescape %}
72 {% endautoescape %}
71 </div>
73 </div>
72 <div class="metadata">
74 <div class="metadata">
73 {{ thread.thread.get_reply_count }} {% trans 'replies' %},
75 {{ thread.thread.get_reply_count }} {% trans 'replies' %},
74 {{ thread.thread.get_images_count }} {% trans 'images' %}.
76 {{ thread.thread.get_images_count }} {% trans 'images' %}.
75 {% if thread.thread.tags %}
77 {% if thread.thread.tags %}
76 <span class="tags">{% trans 'Tags' %}:
78 <span class="tags">{% trans 'Tags' %}:
77 {% for tag in thread.thread.tags.all %}
79 {% for tag in thread.thread.tags.all %}
78 <a class="tag" href="
80 <a class="tag" href="
79 {% url 'tag' tag_name=tag.name %}">
81 {% url 'tag' tag_name=tag.name %}">
80 {{ tag.name }}</a>
82 {{ tag.name }}</a>
81 {% endfor %}
83 {% endfor %}
82 </span>
84 </span>
83 {% endif %}
85 {% endif %}
84 </div>
86 </div>
85 </div>
87 </div>
86 {% if thread.thread.get_last_replies.exists %}
88 {% if thread.thread.get_last_replies.exists %}
87 <div class="last-replies">
89 <div class="last-replies">
88 {% for post in thread.thread.get_last_replies %}
90 {% for post in thread.thread.get_last_replies %}
89 {% if thread.bumpable %}
91 {% if thread.bumpable %}
90 <div class="post" id="{{ post.id }}">
92 <div class="post" id="{{ post.id }}">
91 {% else %}
93 {% else %}
92 <div class="post dead_post" id="{{ post.id }}">
94 <div class="post dead_post" id="{{ post.id }}">
93 {% endif %}
95 {% endif %}
94 {% if post.image %}
96 {% if post.image %}
95 <div class="image">
97 <div class="image">
96 <a class="fancy"
98 <a class="fancy"
97 href="{{ post.image.url }}"><img
99 href="{{ post.image.url }}"><img
98 src=" {{ post.image.url_200x150 }}"
100 src=" {{ post.image.url_200x150 }}"
99 alt="{% trans 'Post image' %}"
101 alt="{% trans 'Post image' %}"
100 data-width="{{ post.image_width }}"
102 data-width="{{ post.image_width }}"
101 data-height="{{ post.image_height }}"/>
103 data-height="{{ post.image_height }}"/>
102 </a>
104 </a>
103 </div>
105 </div>
104 {% endif %}
106 {% endif %}
105 <div class="message">
107 <div class="message">
106 <div class="post-info">
108 <div class="post-info">
107 <span class="title">{{ post.title }}</span>
109 <span class="title">{{ post.title }}</span>
108 <a class="post_id" href="
110 <a class="post_id" href="
109 {% url 'thread' thread.thread.id %}#{{ post.id }}">
111 {% url 'thread' thread.thread.id %}#{{ post.id }}">
110 (#{{ post.id }})</a>
112 (#{{ post.id }})</a>
111 [{{ post.pub_time }}]
113 [{{ post.pub_time }}]
112 </div>
114 </div>
113 {% autoescape off %}
115 {% autoescape off %}
114 {{ post.text.rendered|truncatewords_html:50 }}
116 {{ post.text.rendered|truncatewords_html:50 }}
115 {% endautoescape %}
117 {% endautoescape %}
116 </div>
118 </div>
117 </div>
119 </div>
118 {% endfor %}
120 {% endfor %}
119 </div>
121 </div>
120 {% endif %}
122 {% endif %}
123 {% endcache %}
121 </div>
124 </div>
122 {% endfor %}
125 {% endfor %}
123 {% else %}
126 {% else %}
124 <div class="post">
127 <div class="post">
125 {% trans 'No threads exist. Create the first one!' %}</div>
128 {% trans 'No threads exist. Create the first one!' %}</div>
126 {% endif %}
129 {% endif %}
127
130
128 <form enctype="multipart/form-data" method="post">{% csrf_token %}
131 <form enctype="multipart/form-data" method="post">{% csrf_token %}
129 <div class="post-form-w">
132 <div class="post-form-w">
130
133
131 <div class="form-title">{% trans "Create new thread" %}</div>
134 <div class="form-title">{% trans "Create new thread" %}</div>
132 <div class="post-form">
135 <div class="post-form">
133 <div class="form-row">
136 <div class="form-row">
134 <div class="form-label">{% trans 'Title' %}</div>
137 <div class="form-label">{% trans 'Title' %}</div>
135 <div class="form-input">{{ form.title }}</div>
138 <div class="form-input">{{ form.title }}</div>
136 <div class="form-errors">{{ form.title.errors }}</div>
139 <div class="form-errors">{{ form.title.errors }}</div>
137 </div>
140 </div>
138 <div class="form-row">
141 <div class="form-row">
139 <div class="form-label">{% trans 'Text' %}</div>
142 <div class="form-label">{% trans 'Text' %}</div>
140 <div class="form-input">{{ form.text }}</div>
143 <div class="form-input">{{ form.text }}</div>
141 <div class="form-errors">{{ form.text.errors }}</div>
144 <div class="form-errors">{{ form.text.errors }}</div>
142 </div>
145 </div>
143 <div class="form-row">
146 <div class="form-row">
144 <div class="form-label">{% trans 'Image' %}</div>
147 <div class="form-label">{% trans 'Image' %}</div>
145 <div class="form-input">{{ form.image }}</div>
148 <div class="form-input">{{ form.image }}</div>
146 <div class="form-errors">{{ form.image.errors }}</div>
149 <div class="form-errors">{{ form.image.errors }}</div>
147 </div>
150 </div>
148 <div class="form-row">
151 <div class="form-row">
149 <div class="form-label">{% trans 'Tags' %}</div>
152 <div class="form-label">{% trans 'Tags' %}</div>
150 <div class="form-input">{{ form.tags }}</div>
153 <div class="form-input">{{ form.tags }}</div>
151 <div class="form-errors">{{ form.tags.errors }}</div>
154 <div class="form-errors">{{ form.tags.errors }}</div>
152 </div>
155 </div>
153 <div class="form-row">
156 <div class="form-row">
154 {{ form.captcha }}
157 {{ form.captcha }}
155 <div class="form-errors">{{ form.captcha.errors }}</div>
158 <div class="form-errors">{{ form.captcha.errors }}</div>
156 </div>
159 </div>
157 <div class="form-row">
160 <div class="form-row">
158 <div class="form-errors">{{ form.other.errors }}</div>
161 <div class="form-errors">{{ form.other.errors }}</div>
159 </div>
162 </div>
160 </div>
163 </div>
161 <div class="form-submit">
164 <div class="form-submit">
162 <input type="submit" value="{% trans "Post" %}"/></div>
165 <input type="submit" value="{% trans "Post" %}"/></div>
163 <div>
166 <div>
164 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
167 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
165 </div>
168 </div>
166 <div><a href="{% url "staticpage" name="help" %}">
169 <div><a href="{% url "staticpage" name="help" %}">
167 {% trans 'Text syntax' %}</a></div>
170 {% trans 'Text syntax' %}</a></div>
168 </div>
171 </div>
169 </form>
172 </form>
170
173
171 {% endblock %}
174 {% endblock %}
172
175
173 {% block metapanel %}
176 {% block metapanel %}
174
177
175 <span class="metapanel">
178 <span class="metapanel">
176 <b><a href="{% url "authors" %}">Neboard</a> 1.1</b>
179 <b><a href="{% url "authors" %}">Neboard</a> 1.1</b>
177 {% trans "Pages:" %}
180 {% trans "Pages:" %}
178 {% for page in pages %}
181 {% for page in pages %}
179 [<a href="
182 [<a href="
180 {% if tag %}
183 {% if tag %}
181 {% url "tag" tag_name=tag page=page %}
184 {% url "tag" tag_name=tag page=page %}
182 {% else %}
185 {% else %}
183 {% url "index" page=page %}
186 {% url "index" page=page %}
184 {% endif %}
187 {% endif %}
185 ">{{ page }}</a>]
188 ">{{ page }}</a>]
186 {% endfor %}
189 {% endfor %}
187 [<a href="rss/">RSS</a>]
190 [<a href="rss/">RSS</a>]
188 </span>
191 </span>
189
192
190 {% endblock %}
193 {% endblock %}
@@ -1,118 +1,121 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load markup %}
4 {% load markup %}
5 {% load cache %}
5
6
6 {% block head %}
7 {% block head %}
7 <title>Neboard - {{ posts.0.get_title }}</title>
8 <title>Neboard - {{ posts.0.get_title }}</title>
8 {% endblock %}
9 {% endblock %}
9
10
10 {% block content %}
11 {% block content %}
11 <script src="{{ STATIC_URL }}js/thread.js"></script>
12 <script src="{{ STATIC_URL }}js/thread.js"></script>
12
13
13 {% if posts %}
14 {% if posts %}
15 {% cache 600 thread_view posts.0.last_edit_time %}
14 <div id="posts">
16 <div id="posts">
15 {% for post in posts %}
17 {% for post in posts %}
16 {% if bumpable %}
18 {% if bumpable %}
17 <div class="post" id="{{ post.id }}">
19 <div class="post" id="{{ post.id }}">
18 {% else %}
20 {% else %}
19 <div class="post dead_post" id="{{ post.id }}">
21 <div class="post dead_post" id="{{ post.id }}">
20 {% endif %}
22 {% endif %}
21 {% if post.image %}
23 {% if post.image %}
22 <div class="image">
24 <div class="image">
23 <a
25 <a
24 class="fancy"
26 class="fancy"
25 href="{{ post.image.url }}"><img
27 href="{{ post.image.url }}"><img
26 src="{{ post.image.url_200x150 }}"
28 src="{{ post.image.url_200x150 }}"
27 alt="{% trans 'Post image' %}"v
29 alt="{% trans 'Post image' %}"v
28 data-width="{{ post.image_width }}"
30 data-width="{{ post.image_width }}"
29 data-height="{{ post.image_height }}"/>
31 data-height="{{ post.image_height }}"/>
30 </a>
32 </a>
31 </div>
33 </div>
32 {% endif %}
34 {% endif %}
33 <div class="message">
35 <div class="message">
34 <div class="post-info">
36 <div class="post-info">
35 <span class="title">{{ post.title }}</span>
37 <span class="title">{{ post.title }}</span>
36 <a class="post_id" href="#{{ post.id }}">
38 <a class="post_id" href="#{{ post.id }}">
37 (#{{ post.id }})</a>
39 (#{{ post.id }})</a>
38 [{{ post.pub_time }}]
40 [{{ post.pub_time }}]
39 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
41 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
40 ; return false;">&gt;&gt;</a>]
42 ; return false;">&gt;&gt;</a>]
41
43
42 {% if moderator %}
44 {% if moderator %}
43 <span class="moderator_info">
45 <span class="moderator_info">
44 [<a href="{% url 'delete' post_id=post.id %}"
46 [<a href="{% url 'delete' post_id=post.id %}"
45 >{% trans 'Delete' %}</a>]
47 >{% trans 'Delete' %}</a>]
46 ({{ post.poster_ip }})
48 ({{ post.poster_ip }})
47 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
49 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
48 >{% trans 'Ban IP' %}</a>]
50 >{% trans 'Ban IP' %}</a>]
49 </span>
51 </span>
50 {% endif %}
52 {% endif %}
51 </div>
53 </div>
52 {% autoescape off %}
54 {% autoescape off %}
53 {{ post.text.rendered }}
55 {{ post.text.rendered }}
54 {% endautoescape %}
56 {% endautoescape %}
55 </div>
57 </div>
56 {% if post.id == posts.0.id %}
58 {% if post.id == posts.0.id %}
57 <div class="metadata">
59 <div class="metadata">
58 <span class="tags">{% trans 'Tags' %}:
60 <span class="tags">{% trans 'Tags' %}:
59 {% for tag in post.tags.all %}
61 {% for tag in post.tags.all %}
60 <a class="tag" href="{% url 'tag' tag.name %}">
62 <a class="tag" href="{% url 'tag' tag.name %}">
61 {{ tag.name }}</a>
63 {{ tag.name }}</a>
62 {% endfor %}
64 {% endfor %}
63 </span>
65 </span>
64 </div>
66 </div>
65 {% endif %}
67 {% endif %}
66 </div>
68 </div>
67 {% endfor %}
69 {% endfor %}
68 </div>
70 </div>
71 {% endcache %}
69 {% endif %}
72 {% endif %}
70
73
71 <form id="form" enctype="multipart/form-data" method="post"
74 <form id="form" enctype="multipart/form-data" method="post"
72 >{% csrf_token %}
75 >{% csrf_token %}
73 <div class="post-form-w">
76 <div class="post-form-w">
74 <div class="form-title">{% trans "Reply to thread" %} #{{ posts.0.id }}</div>
77 <div class="form-title">{% trans "Reply to thread" %} #{{ posts.0.id }}</div>
75 <div class="post-form">
78 <div class="post-form">
76 <div class="form-row">
79 <div class="form-row">
77 <div class="form-label">{% trans 'Title' %}</div>
80 <div class="form-label">{% trans 'Title' %}</div>
78 <div class="form-input">{{ form.title }}</div>
81 <div class="form-input">{{ form.title }}</div>
79 <div class="form-errors">{{ form.title.errors }}</div>
82 <div class="form-errors">{{ form.title.errors }}</div>
80 </div>
83 </div>
81 <div class="form-row">
84 <div class="form-row">
82 <div class="form-label">{% trans 'Text' %}</div>
85 <div class="form-label">{% trans 'Text' %}</div>
83 <div class="form-input">{{ form.text }}</div>
86 <div class="form-input">{{ form.text }}</div>
84 <div class="form-errors">{{ form.text.errors }}</div>
87 <div class="form-errors">{{ form.text.errors }}</div>
85 </div>
88 </div>
86 <div class="form-row">
89 <div class="form-row">
87 <div class="form-label">{% trans 'Image' %}</div>
90 <div class="form-label">{% trans 'Image' %}</div>
88 <div class="form-input">{{ form.image }}</div>
91 <div class="form-input">{{ form.image }}</div>
89 <div class="form-errors">{{ form.image.errors }}</div>
92 <div class="form-errors">{{ form.image.errors }}</div>
90 </div>
93 </div>
91 <div class="form-row">
94 <div class="form-row">
92 {{ form.captcha }}
95 {{ form.captcha }}
93 <div class="form-errors">{{ form.captcha.errors }}</div>
96 <div class="form-errors">{{ form.captcha.errors }}</div>
94 </div>
97 </div>
95 <div class="form-row">
98 <div class="form-row">
96 <div class="form-errors">{{ form.other.errors }}</div>
99 <div class="form-errors">{{ form.other.errors }}</div>
97 </div>
100 </div>
98 </div>
101 </div>
99
102
100 <div class="form-submit"><input type="submit"
103 <div class="form-submit"><input type="submit"
101 value="{% trans "Post" %}"/></div>
104 value="{% trans "Post" %}"/></div>
102 <div><a href="{% url "staticpage" name="help" %}">
105 <div><a href="{% url "staticpage" name="help" %}">
103 {% trans 'Text syntax' %}</a></div>
106 {% trans 'Text syntax' %}</a></div>
104 </div>
107 </div>
105 </form>
108 </form>
106
109
107 {% endblock %}
110 {% endblock %}
108
111
109 {% block metapanel %}
112 {% block metapanel %}
110
113
111 <span class="metapanel">
114 <span class="metapanel">
112 {{ posts.0.get_reply_count }} {% trans 'replies' %},
115 {{ posts.0.get_reply_count }} {% trans 'replies' %},
113 {{ posts.0.get_images_count }} {% trans 'images' %}.
116 {{ posts.0.get_images_count }} {% trans 'images' %}.
114 {% trans 'Last update: ' %}{{ posts.0.last_edit_time }}
117 {% trans 'Last update: ' %}{{ posts.0.last_edit_time }}
115 [<a href="rss/">RSS</a>]
118 [<a href="rss/">RSS</a>]
116 </span>
119 </span>
117
120
118 {% endblock %}
121 {% endblock %}
@@ -1,202 +1,209 b''
1 # Django settings for neboard project.
1 # Django settings for neboard project.
2 import os
2 import os
3 import markdown
3 import markdown
4 from boards.mdx_neboard import markdown_extended
4 from boards.mdx_neboard import markdown_extended
5
5
6 DEBUG = True
6 DEBUG = True
7 TEMPLATE_DEBUG = DEBUG
7 TEMPLATE_DEBUG = DEBUG
8
8
9 ADMINS = (
9 ADMINS = (
10 # ('Your Name', 'your_email@example.com'),
10 # ('Your Name', 'your_email@example.com'),
11 ('admin', 'admin@example.com')
11 ('admin', 'admin@example.com')
12 )
12 )
13
13
14 MANAGERS = ADMINS
14 MANAGERS = ADMINS
15
15
16 DATABASES = {
16 DATABASES = {
17 'default': {
17 'default': {
18 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
18 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
19 'NAME': 'database.db', # Or path to database file if using sqlite3.
19 'NAME': 'database.db', # Or path to database file if using sqlite3.
20 'USER': '', # Not used with sqlite3.
20 'USER': '', # Not used with sqlite3.
21 'PASSWORD': '', # Not used with sqlite3.
21 'PASSWORD': '', # Not used with sqlite3.
22 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
22 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
23 'PORT': '', # Set to empty string for default. Not used with sqlite3.
23 'PORT': '', # Set to empty string for default. Not used with sqlite3.
24 }
24 }
25 }
25 }
26
26
27 CACHES = {
28 'default': {
29 'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
30 'LOCATION': 'neboard_cache',
31 }
32 }
33
27 # Local time zone for this installation. Choices can be found here:
34 # Local time zone for this installation. Choices can be found here:
28 # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
35 # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
29 # although not all choices may be available on all operating systems.
36 # 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.
37 # In a Windows environment this must be set to your system time zone.
31 TIME_ZONE = 'Europe/Kiev'
38 TIME_ZONE = 'Europe/Kiev'
32
39
33 # Language code for this installation. All choices can be found here:
40 # Language code for this installation. All choices can be found here:
34 # http://www.i18nguy.com/unicode/language-identifiers.html
41 # http://www.i18nguy.com/unicode/language-identifiers.html
35 LANGUAGE_CODE = 'en'
42 LANGUAGE_CODE = 'en'
36
43
37 SITE_ID = 1
44 SITE_ID = 1
38
45
39 # If you set this to False, Django will make some optimizations so as not
46 # If you set this to False, Django will make some optimizations so as not
40 # to load the internationalization machinery.
47 # to load the internationalization machinery.
41 USE_I18N = True
48 USE_I18N = True
42
49
43 # If you set this to False, Django will not format dates, numbers and
50 # If you set this to False, Django will not format dates, numbers and
44 # calendars according to the current locale.
51 # calendars according to the current locale.
45 USE_L10N = True
52 USE_L10N = True
46
53
47 # If you set this to False, Django will not use timezone-aware datetimes.
54 # If you set this to False, Django will not use timezone-aware datetimes.
48 USE_TZ = True
55 USE_TZ = True
49
56
50 # Absolute filesystem path to the directory that will hold user-uploaded files.
57 # Absolute filesystem path to the directory that will hold user-uploaded files.
51 # Example: "/home/media/media.lawrence.com/media/"
58 # Example: "/home/media/media.lawrence.com/media/"
52 MEDIA_ROOT = './media/'
59 MEDIA_ROOT = './media/'
53
60
54 # URL that handles the media served from MEDIA_ROOT. Make sure to use a
61 # URL that handles the media served from MEDIA_ROOT. Make sure to use a
55 # trailing slash.
62 # trailing slash.
56 # Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
63 # Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
57 MEDIA_URL = '/media/'
64 MEDIA_URL = '/media/'
58
65
59 # Absolute path to the directory static files should be collected to.
66 # Absolute path to the directory static files should be collected to.
60 # Don't put anything in this directory yourself; store your static files
67 # Don't put anything in this directory yourself; store your static files
61 # in apps' "static/" subdirectories and in STATICFILES_DIRS.
68 # in apps' "static/" subdirectories and in STATICFILES_DIRS.
62 # Example: "/home/media/media.lawrence.com/static/"
69 # Example: "/home/media/media.lawrence.com/static/"
63 STATIC_ROOT = ''
70 STATIC_ROOT = ''
64
71
65 # URL prefix for static files.
72 # URL prefix for static files.
66 # Example: "http://media.lawrence.com/static/"
73 # Example: "http://media.lawrence.com/static/"
67 STATIC_URL = '/static/'
74 STATIC_URL = '/static/'
68
75
69 # Additional locations of static files
76 # Additional locations of static files
70 # It is really a hack, put real paths, not related
77 # It is really a hack, put real paths, not related
71 STATICFILES_DIRS = (
78 STATICFILES_DIRS = (
72 os.path.dirname(__file__) + '/boards/static',
79 os.path.dirname(__file__) + '/boards/static',
73
80
74 # '/d/work/python/django/neboard/neboard/boards/static',
81 # '/d/work/python/django/neboard/neboard/boards/static',
75 # Put strings here, like "/home/html/static" or "C:/www/django/static".
82 # Put strings here, like "/home/html/static" or "C:/www/django/static".
76 # Always use forward slashes, even on Windows.
83 # Always use forward slashes, even on Windows.
77 # Don't forget to use absolute paths, not relative paths.
84 # Don't forget to use absolute paths, not relative paths.
78 )
85 )
79
86
80 # List of finder classes that know how to find static files in
87 # List of finder classes that know how to find static files in
81 # various locations.
88 # various locations.
82 STATICFILES_FINDERS = (
89 STATICFILES_FINDERS = (
83 'django.contrib.staticfiles.finders.FileSystemFinder',
90 'django.contrib.staticfiles.finders.FileSystemFinder',
84 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
91 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
85 # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
92 # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
86 )
93 )
87
94
88 # Make this unique, and don't share it with anybody.
95 # Make this unique, and don't share it with anybody.
89 SECRET_KEY = '@1rc$o(7=tt#kd+4s$u6wchm**z^)4x90)7f6z(i&amp;55@o11*8o'
96 SECRET_KEY = '@1rc$o(7=tt#kd+4s$u6wchm**z^)4x90)7f6z(i&amp;55@o11*8o'
90
97
91 # List of callables that know how to import templates from various sources.
98 # List of callables that know how to import templates from various sources.
92 TEMPLATE_LOADERS = (
99 TEMPLATE_LOADERS = (
93 'django.template.loaders.filesystem.Loader',
100 'django.template.loaders.filesystem.Loader',
94 'django.template.loaders.app_directories.Loader',
101 'django.template.loaders.app_directories.Loader',
95 # 'django.template.loaders.eggs.Loader',
102 # 'django.template.loaders.eggs.Loader',
96 )
103 )
97
104
98 TEMPLATE_CONTEXT_PROCESSORS = (
105 TEMPLATE_CONTEXT_PROCESSORS = (
99 'django.core.context_processors.media',
106 'django.core.context_processors.media',
100 'django.core.context_processors.static',
107 'django.core.context_processors.static',
101 'django.core.context_processors.request',
108 'django.core.context_processors.request',
102 'django.contrib.auth.context_processors.auth',
109 'django.contrib.auth.context_processors.auth',
103 )
110 )
104
111
105 MIDDLEWARE_CLASSES = (
112 MIDDLEWARE_CLASSES = (
106 'django.contrib.sessions.middleware.SessionMiddleware',
113 'django.contrib.sessions.middleware.SessionMiddleware',
107 'django.middleware.locale.LocaleMiddleware',
114 'django.middleware.locale.LocaleMiddleware',
108 'django.middleware.common.CommonMiddleware',
115 'django.middleware.common.CommonMiddleware',
109 # 'django.middleware.csrf.CsrfViewMiddleware',
116 # 'django.middleware.csrf.CsrfViewMiddleware',
110 'django.contrib.auth.middleware.AuthenticationMiddleware',
117 'django.contrib.auth.middleware.AuthenticationMiddleware',
111 'django.contrib.messages.middleware.MessageMiddleware',
118 'django.contrib.messages.middleware.MessageMiddleware',
112 # Uncomment the next line for simple clickjacking protection:
119 # Uncomment the next line for simple clickjacking protection:
113 # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
120 # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
114 )
121 )
115
122
116 ROOT_URLCONF = 'neboard.urls'
123 ROOT_URLCONF = 'neboard.urls'
117
124
118 # Python dotted path to the WSGI application used by Django's runserver.
125 # Python dotted path to the WSGI application used by Django's runserver.
119 WSGI_APPLICATION = 'neboard.wsgi.application'
126 WSGI_APPLICATION = 'neboard.wsgi.application'
120
127
121 TEMPLATE_DIRS = (
128 TEMPLATE_DIRS = (
122 # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
129 # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
123 # Always use forward slashes, even on Windows.
130 # Always use forward slashes, even on Windows.
124 # Don't forget to use absolute paths, not relative paths.
131 # Don't forget to use absolute paths, not relative paths.
125 'templates',
132 'templates',
126 )
133 )
127
134
128 INSTALLED_APPS = (
135 INSTALLED_APPS = (
129 'django.contrib.auth',
136 'django.contrib.auth',
130 'django.contrib.contenttypes',
137 'django.contrib.contenttypes',
131 'django.contrib.sessions',
138 'django.contrib.sessions',
132 # 'django.contrib.sites',
139 # 'django.contrib.sites',
133 'django.contrib.messages',
140 'django.contrib.messages',
134 'django.contrib.staticfiles',
141 'django.contrib.staticfiles',
135 # Uncomment the next line to enable the admin:
142 # Uncomment the next line to enable the admin:
136 'django.contrib.admin',
143 'django.contrib.admin',
137 # Uncomment the next line to enable admin documentation:
144 # Uncomment the next line to enable admin documentation:
138 # 'django.contrib.admindocs',
145 # 'django.contrib.admindocs',
139 'django.contrib.markup',
146 'django.contrib.markup',
140 'django_cleanup',
147 'django_cleanup',
141 'boards',
148 'boards',
142 'captcha',
149 'captcha',
143 'south',
150 'south',
144 )
151 )
145
152
146 # TODO: NEED DESIGN FIXES
153 # TODO: NEED DESIGN FIXES
147 CAPTCHA_OUTPUT_FORMAT = (u' %(hidden_field)s '
154 CAPTCHA_OUTPUT_FORMAT = (u' %(hidden_field)s '
148 u'<div class="form-label">%(image)s</div>'
155 u'<div class="form-label">%(image)s</div>'
149 u'<div class="form-text">%(text_field)s</div>')
156 u'<div class="form-text">%(text_field)s</div>')
150
157
151 # A sample logging configuration. The only tangible logging
158 # A sample logging configuration. The only tangible logging
152 # performed by this configuration is to send an email to
159 # performed by this configuration is to send an email to
153 # the site admins on every HTTP 500 error when DEBUG=False.
160 # the site admins on every HTTP 500 error when DEBUG=False.
154 # See http://docs.djangoproject.com/en/dev/topics/logging for
161 # See http://docs.djangoproject.com/en/dev/topics/logging for
155 # more details on how to customize your logging configuration.
162 # more details on how to customize your logging configuration.
156 LOGGING = {
163 LOGGING = {
157 'version': 1,
164 'version': 1,
158 'disable_existing_loggers': False,
165 'disable_existing_loggers': False,
159 'filters': {
166 'filters': {
160 'require_debug_false': {
167 'require_debug_false': {
161 '()': 'django.utils.log.RequireDebugFalse'
168 '()': 'django.utils.log.RequireDebugFalse'
162 }
169 }
163 },
170 },
164 'handlers': {
171 'handlers': {
165 'mail_admins': {
172 'mail_admins': {
166 'level': 'ERROR',
173 'level': 'ERROR',
167 'filters': ['require_debug_false'],
174 'filters': ['require_debug_false'],
168 'class': 'django.utils.log.AdminEmailHandler'
175 'class': 'django.utils.log.AdminEmailHandler'
169 }
176 }
170 },
177 },
171 'loggers': {
178 'loggers': {
172 'django.request': {
179 'django.request': {
173 'handlers': ['mail_admins'],
180 'handlers': ['mail_admins'],
174 'level': 'ERROR',
181 'level': 'ERROR',
175 'propagate': True,
182 'propagate': True,
176 },
183 },
177 }
184 }
178 }
185 }
179
186
180 MARKUP_FIELD_TYPES = (
187 MARKUP_FIELD_TYPES = (
181 ('markdown', markdown_extended),
188 ('markdown', markdown_extended),
182 )
189 )
183 # Custom imageboard settings
190 # Custom imageboard settings
184 MAX_POSTS_PER_THREAD = 10 # Thread bumplimit
191 MAX_POSTS_PER_THREAD = 10 # Thread bumplimit
185 MAX_THREAD_COUNT = 500 # Old threads will be deleted to preserve this count
192 MAX_THREAD_COUNT = 500 # Old threads will be deleted to preserve this count
186 THREADS_PER_PAGE = 10
193 THREADS_PER_PAGE = 10
187 SITE_NAME = 'Neboard'
194 SITE_NAME = 'Neboard'
188
195
189 THEMES = [
196 THEMES = [
190 ('md', 'Mystic Dark'),
197 ('md', 'Mystic Dark'),
191 ('sw', 'Snow White')
198 ('sw', 'Snow White')
192 ]
199 ]
193
200
194 DEFAULT_THEME = 'md'
201 DEFAULT_THEME = 'md'
195
202
196 POPULAR_TAGS = 10
203 POPULAR_TAGS = 10
197 LAST_REPLIES_COUNT = 3
204 LAST_REPLIES_COUNT = 3
198
205
199 ENABLE_CAPTCHA = False
206 ENABLE_CAPTCHA = False
200 # if user tries to post before CAPTCHA_DEFAULT_SAFE_TIME. Captcha will be shown
207 # if user tries to post before CAPTCHA_DEFAULT_SAFE_TIME. Captcha will be shown
201 CAPTCHA_DEFAULT_SAFE_TIME = 30 # seconds
208 CAPTCHA_DEFAULT_SAFE_TIME = 30 # seconds
202 POSTING_DELAY = 30 # seconds No newline at end of file
209 POSTING_DELAY = 30 # seconds
General Comments 0
You need to be logged in to leave comments. Login now