##// END OF EJS Templates
Added user to the posts. This refs #12
neko259 -
r113:aabf6422 1.1
parent child Browse files
Show More
@@ -1,333 +1,335 b''
1 1 import os
2 2 from random import random
3 3 import re
4 4 import time
5 5 import math
6 6
7 7 from django.db import models
8 8 from django.http import Http404
9 9 from django.utils import timezone
10 10 from markupfield.fields import MarkupField
11 11 from threading import Thread
12 12
13 13 from neboard import settings
14 14 import thumbs
15 15
16 16 IMAGE_THUMB_SIZE = (200, 150)
17 17
18 18 TITLE_MAX_LENGTH = 50
19 19
20 20 DEFAULT_MARKUP_TYPE = 'markdown'
21 21
22 22 NO_PARENT = -1
23 23 NO_IP = '0.0.0.0'
24 24 UNKNOWN_UA = ''
25 25 ALL_PAGES = -1
26 26 OPENING_POST_POPULARITY_WEIGHT = 2
27 27 IMAGES_DIRECTORY = 'images/'
28 28 FILE_EXTENSION_DELIMITER = '.'
29 29
30 30 REGEX_PRETTY = re.compile(r'^\d(0)+$')
31 31 REGEX_SAME = re.compile(r'^(.)\1+$')
32 32
33 33 RANK_ADMIN = 0
34 34 RANK_MODERATOR = 10
35 35 RANK_USER = 100
36 36
37 37
38 class User(models.Model):
39
40 user_id = models.CharField(max_length=50)
41 rank = models.IntegerField()
42
43 def save_setting(self, name, value):
44 setting, created = Setting.objects.get_or_create(name=name, user=self)
45 setting.value = value
46 setting.save()
47
48 return setting
49
50 def get_setting(self, name):
51 settings = Setting.objects.filter(name=name, user=self)
52 if len(settings) > 0:
53 setting = settings[0]
54 else:
55 setting = None
56
57 if setting:
58 setting_value = setting.value
59 else:
60 setting_value = None
61
62 return setting_value
63
64 def is_moderator(self):
65 return RANK_MODERATOR >= self.rank
66
67 def __unicode__(self):
68 return self.user_id
69
70
38 71 class PostManager(models.Manager):
39 72 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
40 ip=NO_IP, tags=None):
73 ip=NO_IP, tags=None, user=None):
41 74 post = self.create(title=title,
42 75 text=text,
43 76 pub_time=timezone.now(),
44 77 parent=parent_id,
45 78 image=image,
46 79 poster_ip=ip,
47 80 poster_user_agent=UNKNOWN_UA,
48 last_edit_time=timezone.now())
81 last_edit_time=timezone.now(),
82 user=user)
49 83
50 84 if tags:
51 85 map(post.tags.add, tags)
52 86
53 87 if parent_id != NO_PARENT:
54 88 self._bump_thread(parent_id)
55 89 else:
56 90 self._delete_old_threads()
57 91
58 92 return post
59 93
60 94 def delete_post(self, post):
61 95 children = self.filter(parent=post.id)
62 96 for child in children:
63 97 self.delete_post(child)
64 98 post.delete()
65 99
66 100 def delete_posts_by_ip(self, ip):
67 101 posts = self.filter(poster_ip=ip)
68 102 for post in posts:
69 103 self.delete_post(post)
70 104
71 105 def get_threads(self, tag=None, page=ALL_PAGES,
72 106 order_by='-last_edit_time'):
73 107 if tag:
74 108 threads = self.filter(parent=NO_PARENT, tags=tag)
75 109
76 110 # TODO Throw error 404 if no threads for tag found?
77 111 else:
78 112 threads = self.filter(parent=NO_PARENT)
79 113
80 114 threads = threads.order_by(order_by)
81 115
82 116 if page != ALL_PAGES:
83 117 thread_count = len(threads)
84 118
85 119 if page < self.get_thread_page_count(tag=tag):
86 120 start_thread = page * settings.THREADS_PER_PAGE
87 121 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
88 122 thread_count)
89 123 threads = threads[start_thread:end_thread]
90 124
91 125 return threads
92 126
93 127 def get_thread(self, opening_post_id):
94 128 try:
95 129 opening_post = self.get(id=opening_post_id, parent=NO_PARENT)
96 130 except Post.DoesNotExist:
97 131 raise Http404
98 132
99 133 if opening_post.parent == NO_PARENT:
100 134 replies = self.filter(parent=opening_post_id)
101 135
102 136 thread = [opening_post]
103 137 thread.extend(replies)
104 138
105 139 return thread
106 140
107 141 def exists(self, post_id):
108 142 posts = self.filter(id=post_id)
109 143
110 144 return posts.count() > 0
111 145
112 146 def get_thread_page_count(self, tag=None):
113 147 if tag:
114 148 threads = self.filter(parent=NO_PARENT, tags=tag)
115 149 else:
116 150 threads = self.filter(parent=NO_PARENT)
117 151
118 152 return int(math.ceil(threads.count() / float(
119 153 settings.THREADS_PER_PAGE)))
120 154
121 155 def _delete_old_threads(self):
122 156 """
123 157 Preserves maximum thread count. If there are too many threads,
124 158 delete the old ones.
125 159 """
126 160
127 161 # TODO Move old threads to the archive instead of deleting them.
128 162 # Maybe make some 'old' field in the model to indicate the thread
129 163 # must not be shown and be able for replying.
130 164
131 165 threads = self.get_threads()
132 166 thread_count = len(threads)
133 167
134 168 if thread_count > settings.MAX_THREAD_COUNT:
135 169 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
136 170 old_threads = threads[thread_count - num_threads_to_delete:]
137 171
138 172 for thread in old_threads:
139 173 self.delete_post(thread)
140 174
141 175 def _bump_thread(self, thread_id):
142 176 thread = self.get(id=thread_id)
143 177
144 178 if thread.can_bump():
145 179 thread.last_edit_time = timezone.now()
146 180 thread.save()
147 181
148 182
149 183 class TagManager(models.Manager):
150 184 def get_not_empty_tags(self):
151 185 all_tags = self.all().order_by('name')
152 186 tags = []
153 187 for tag in all_tags:
154 188 if not tag.is_empty():
155 189 tags.append(tag)
156 190
157 191 return tags
158 192
159 193 def get_popular_tags(self):
160 194 all_tags = self.get_not_empty_tags()
161 195
162 196 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
163 197 reverse=True)
164 198
165 199 return sorted_tags[:settings.POPULAR_TAGS]
166 200
167 201
168 202 class Tag(models.Model):
169 203 """
170 204 A tag is a text node assigned to the post. The tag serves as a board
171 205 section. There can be multiple tags for each message
172 206 """
173 207
174 208 objects = TagManager()
175 209
176 210 name = models.CharField(max_length=100)
177 211 # TODO Connect the tag to its posts to check the number of threads for
178 212 # the tag.
179 213
180 214 def __unicode__(self):
181 215 return self.name
182 216
183 217 def is_empty(self):
184 218 return self.get_post_count() == 0
185 219
186 220 def get_post_count(self):
187 221 posts_with_tag = Post.objects.get_threads(tag=self)
188 222 return posts_with_tag.count()
189 223
190 224 def get_popularity(self):
191 225 posts_with_tag = Post.objects.get_threads(tag=self)
192 226 reply_count = 0
193 227 for post in posts_with_tag:
194 228 reply_count += post.get_reply_count()
195 229 reply_count += OPENING_POST_POPULARITY_WEIGHT
196 230
197 231 return reply_count
198 232
199 233
200 234 class Post(models.Model):
201 235 """A post is a message."""
202 236
203 237 objects = PostManager()
204 238
205 239 def _update_image_filename(self, filename):
206 240 """Get unique image filename"""
207 241
208 242 path = IMAGES_DIRECTORY
209 243 new_name = str(int(time.mktime(time.gmtime())))
210 244 new_name += str(int(random() * 1000))
211 245 new_name += FILE_EXTENSION_DELIMITER
212 246 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
213 247
214 248 return os.path.join(path, new_name)
215 249
216 250 title = models.CharField(max_length=TITLE_MAX_LENGTH)
217 251 pub_time = models.DateTimeField()
218 252 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
219 253 escape_html=False)
220 254 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
221 255 blank=True, sizes=(IMAGE_THUMB_SIZE,))
222 256 poster_ip = models.IPAddressField()
223 257 poster_user_agent = models.TextField()
224 258 parent = models.BigIntegerField()
225 259 tags = models.ManyToManyField(Tag)
226 260 last_edit_time = models.DateTimeField()
261 user = models.ForeignKey(User, null=True, default=None)
227 262
228 263 def __unicode__(self):
229 264 return '#' + str(self.id) + ' ' + self.title + ' (' + self.text.raw + \
230 265 ')'
231 266
232 267 def _get_replies(self):
233 268 return Post.objects.filter(parent=self.id)
234 269
235 270 def get_reply_count(self):
236 271 return self._get_replies().count()
237 272
238 273 def get_images_count(self):
239 274 images_count = 1 if self.image else 0
240 275 for reply in self._get_replies():
241 276 if reply.image:
242 277 images_count += 1
243 278
244 279 return images_count
245 280
246 281 def get_gets_count(self):
247 282 gets_count = 1 if self.is_get() else 0
248 283 for reply in self._get_replies():
249 284 if reply.is_get():
250 285 gets_count += 1
251 286
252 287 return gets_count
253 288
254 289 def is_get(self):
255 290 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
256 291
257 292 first = self.id == 1
258 293
259 294 id_str = str(self.id)
260 295 pretty = REGEX_PRETTY.match(id_str)
261 296 same_digits = REGEX_SAME.match(id_str)
262 297
263 298 return first or pretty or same_digits
264 299
265 300 def can_bump(self):
266 301 """Check if the thread can be bumped by replying"""
267 302
268 303 replies_count = len(Post.objects.get_thread(self.id))
269 304
270 305 return replies_count <= settings.MAX_POSTS_PER_THREAD
271 306
272 307 def get_last_replies(self):
273 308 if settings.LAST_REPLIES_COUNT > 0:
274 309 reply_count = self.get_reply_count()
275 310
276 311 if reply_count > 0:
277 312 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
278 313 reply_count)
279 314 last_replies = self._get_replies()[reply_count
280 315 - reply_count_to_show:]
281 316
282 317 return last_replies
283 318
284 319
285 320 class Admin(models.Model):
286 321 """
287 322 Model for admin users
288 323 """
289 324 name = models.CharField(max_length=100)
290 325 password = models.CharField(max_length=100)
291 326
292 327 def __unicode__(self):
293 328 return self.name + '/' + '*' * len(self.password)
294 329
295 330
296 class User(models.Model):
297
298 user_id = models.CharField(max_length=50)
299 rank = models.IntegerField()
300
301 def save_setting(self, name, value):
302 setting, created = Setting.objects.get_or_create(name=name, user=self)
303 setting.value = value
304 setting.save()
305
306 return setting
307
308 def get_setting(self, name):
309 settings = Setting.objects.filter(name=name, user=self)
310 if len(settings) > 0:
311 setting = settings[0]
312 else:
313 setting = None
314
315 if setting:
316 setting_value = setting.value
317 else:
318 setting_value = None
319
320 return setting_value
321
322 def is_moderator(self):
323 return RANK_MODERATOR >= self.rank
324
325 def __unicode__(self):
326 return self.user_id
327
328
329 331 class Setting(models.Model):
330 332
331 333 name = models.CharField(max_length=50)
332 334 value = models.CharField(max_length=50)
333 335 user = models.ForeignKey(User)
@@ -1,160 +1,173 b''
1 1 # -*- encoding: utf-8 -*-
2 2 """
3 3 django-thumbs by Antonio MelΓ©
4 4 http://django.es
5 5 """
6 6 from django.db.models import ImageField
7 7 from django.db.models.fields.files import ImageFieldFile
8 8 from PIL import Image
9 9 from django.core.files.base import ContentFile
10 10 import cStringIO
11 11
12
12 13 def generate_thumb(img, thumb_size, format):
13 14 """
14 15 Generates a thumbnail image and returns a ContentFile object with the thumbnail
15 16
16 17 Parameters:
17 18 ===========
18 19 img File object
19 20
20 21 thumb_size desired thumbnail size, ie: (200,120)
21 22
22 23 format format of the original image ('jpeg','gif','png',...)
23 24 (this format will be used for the generated thumbnail, too)
24 25 """
25 26
26 27 img.seek(0) # see http://code.djangoproject.com/ticket/8222 for details
27 28 image = Image.open(img)
28 29
29 30 # get size
30 31 thumb_w, thumb_h = thumb_size
31 32 # If you want to generate a square thumbnail
32 33 if thumb_w == thumb_h:
33 34 # quad
34 35 xsize, ysize = image.size
35 36 # get minimum size
36 37 minsize = min(xsize,ysize)
37 38 # largest square possible in the image
38 39 xnewsize = (xsize-minsize)/2
39 40 ynewsize = (ysize-minsize)/2
40 41 # crop it
41 image2 = image.crop((xnewsize, ynewsize, xsize-xnewsize, ysize-ynewsize))
42 image2 = image.crop(
43 (xnewsize, ynewsize, xsize - xnewsize, ysize - ynewsize))
42 44 # load is necessary after crop
43 45 image2.load()
44 46 # thumbnail of the cropped image (with ANTIALIAS to make it look better)
45 47 image2.thumbnail(thumb_size, Image.ANTIALIAS)
46 48 else:
47 49 # not quad
48 50 image2 = image
49 51 image2.thumbnail(thumb_size, Image.ANTIALIAS)
50 52
51 53 io = cStringIO.StringIO()
52 54 # PNG and GIF are the same, JPG is JPEG
53 55 if format.upper()=='JPG':
54 56 format = 'JPEG'
55 57
56 58 image2.save(io, format)
57 59 return ContentFile(io.getvalue())
58 60
61
59 62 class ImageWithThumbsFieldFile(ImageFieldFile):
60 63 """
61 64 See ImageWithThumbsField for usage example
62 65 """
66
63 67 def __init__(self, *args, **kwargs):
64 68 super(ImageWithThumbsFieldFile, self).__init__(*args, **kwargs)
65 69 self.sizes = self.field.sizes
66 70
67 71 if self.sizes:
68 72 def get_size(self, size):
69 73 if not self:
70 74 return ''
71 75 else:
72 76 split = self.url.rsplit('.',1)
73 77 thumb_url = '%s.%sx%s.%s' % (split[0],w,h,split[1])
74 78 return thumb_url
75 79
76 80 for size in self.sizes:
77 81 (w,h) = size
78 82 setattr(self, 'url_%sx%s' % (w,h), get_size(self, size))
79 83
80 84 def save(self, name, content, save=True):
81 85 super(ImageWithThumbsFieldFile, self).save(name, content, save)
82 86
83 87 if self.sizes:
84 88 for size in self.sizes:
85 89 (w,h) = size
86 90 split = self.name.rsplit('.',1)
87 91 thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
88 92
89 93 # you can use another thumbnailing function if you like
90 94 thumb_content = generate_thumb(content, size, split[1])
91 95
92 96 thumb_name_ = self.storage.save(thumb_name, thumb_content)
93 97
94 98 if not thumb_name == thumb_name_:
95 raise ValueError('There is already a file named %s' % thumb_name)
99 raise ValueError(
100 'There is already a file named %s' % thumb_name)
96 101
97 102 def delete(self, save=True):
98 103 name=self.name
99 104 super(ImageWithThumbsFieldFile, self).delete(save)
100 105 if self.sizes:
101 106 for size in self.sizes:
102 107 (w,h) = size
103 108 split = name.rsplit('.',1)
104 109 thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
105 110 try:
106 111 self.storage.delete(thumb_name)
107 112 except:
108 113 pass
109 114
115
110 116 class ImageWithThumbsField(ImageField):
111 117 attr_class = ImageWithThumbsFieldFile
112 118 """
113 119 Usage example:
114 120 ==============
115 121 photo = ImageWithThumbsField(upload_to='images', sizes=((125,125),(300,200),)
116 122
117 123 To retrieve image URL, exactly the same way as with ImageField:
118 124 my_object.photo.url
119 125 To retrieve thumbnails URL's just add the size to it:
120 126 my_object.photo.url_125x125
121 127 my_object.photo.url_300x200
122 128
123 129 Note: The 'sizes' attribute is not required. If you don't provide it,
124 130 ImageWithThumbsField will act as a normal ImageField
125 131
126 132 How it works:
127 133 =============
128 134 For each size in the 'sizes' atribute of the field it generates a
129 135 thumbnail with that size and stores it following this format:
130 136
131 137 available_filename.[width]x[height].extension
132 138
133 139 Where 'available_filename' is the available filename returned by the storage
134 140 backend for saving the original file.
135 141
136 142 Following the usage example above: For storing a file called "photo.jpg" it saves:
137 143 photo.jpg (original file)
138 144 photo.125x125.jpg (first thumbnail)
139 145 photo.300x200.jpg (second thumbnail)
140 146
141 147 With the default storage backend if photo.jpg already exists it will use these filenames:
142 148 photo_.jpg
143 149 photo_.125x125.jpg
144 150 photo_.300x200.jpg
145 151
146 152 Note: django-thumbs assumes that if filename "any_filename.jpg" is available
147 153 filenames with this format "any_filename.[widht]x[height].jpg" will be available, too.
148 154
149 155 To do:
150 156 ======
151 157 Add method to regenerate thubmnails
152 158
153 159 """
154 def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, sizes=None, **kwargs):
160
161 def __init__(self, verbose_name=None, name=None, width_field=None,
162 height_field=None, sizes=None, **kwargs):
155 163 self.verbose_name=verbose_name
156 164 self.name=name
157 165 self.width_field=width_field
158 166 self.height_field=height_field
159 167 self.sizes = sizes
160 super(ImageField, self).__init__(**kwargs) No newline at end of file
168 super(ImageField, self).__init__(**kwargs)
169
170
171 from south.modelsinspector import add_introspection_rules
172
173 add_introspection_rules([], ["^boards\.thumbs\.ImageWithThumbsField"]) No newline at end of file
@@ -1,199 +1,201 b''
1 1 # Django settings for neboard project.
2 2 import os
3 3 import markdown
4 4 from boards.mdx_neboard import markdown_extended
5 5
6 6 DEBUG = True
7 7 TEMPLATE_DEBUG = DEBUG
8 8
9 9 ADMINS = (
10 10 # ('Your Name', 'your_email@example.com'),
11 11 ('admin', 'admin@example.com')
12 12 )
13 13
14 14 MANAGERS = ADMINS
15 15
16 16 DATABASES = {
17 17 'default': {
18 18 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
19 19 'NAME': 'database.db', # Or path to database file if using sqlite3.
20 20 'USER': '', # Not used with sqlite3.
21 21 'PASSWORD': '', # Not used with sqlite3.
22 22 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
23 23 'PORT': '', # Set to empty string for default. Not used with sqlite3.
24 24 }
25 25 }
26 26
27 27 # Local time zone for this installation. Choices can be found here:
28 28 # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
29 29 # although not all choices may be available on all operating systems.
30 30 # In a Windows environment this must be set to your system time zone.
31 31 TIME_ZONE = 'Europe/Kiev'
32 32
33 33 # Language code for this installation. All choices can be found here:
34 34 # http://www.i18nguy.com/unicode/language-identifiers.html
35 35 LANGUAGE_CODE = 'en'
36 36
37 37 SITE_ID = 1
38 38
39 39 # If you set this to False, Django will make some optimizations so as not
40 40 # to load the internationalization machinery.
41 41 USE_I18N = True
42 42
43 43 # If you set this to False, Django will not format dates, numbers and
44 44 # calendars according to the current locale.
45 45 USE_L10N = True
46 46
47 47 # If you set this to False, Django will not use timezone-aware datetimes.
48 48 USE_TZ = True
49 49
50 50 # Absolute filesystem path to the directory that will hold user-uploaded files.
51 51 # Example: "/home/media/media.lawrence.com/media/"
52 52 MEDIA_ROOT = './media/'
53 53
54 54 # URL that handles the media served from MEDIA_ROOT. Make sure to use a
55 55 # trailing slash.
56 56 # Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
57 57 MEDIA_URL = '/media/'
58 58
59 59 # Absolute path to the directory static files should be collected to.
60 60 # Don't put anything in this directory yourself; store your static files
61 61 # in apps' "static/" subdirectories and in STATICFILES_DIRS.
62 62 # Example: "/home/media/media.lawrence.com/static/"
63 63 STATIC_ROOT = ''
64 64
65 65 # URL prefix for static files.
66 66 # Example: "http://media.lawrence.com/static/"
67 67 STATIC_URL = '/static/'
68 68
69 69 # Additional locations of static files
70 70 # It is really a hack, put real paths, not related
71 71 STATICFILES_DIRS = (
72 72 os.path.dirname(__file__) + '/boards/static',
73 73
74 74 # '/d/work/python/django/neboard/neboard/boards/static',
75 75 # Put strings here, like "/home/html/static" or "C:/www/django/static".
76 76 # Always use forward slashes, even on Windows.
77 77 # Don't forget to use absolute paths, not relative paths.
78 78 )
79 79
80 80 # List of finder classes that know how to find static files in
81 81 # various locations.
82 82 STATICFILES_FINDERS = (
83 83 'django.contrib.staticfiles.finders.FileSystemFinder',
84 84 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
85 85 # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
86 86 )
87 87
88 88 # Make this unique, and don't share it with anybody.
89 89 SECRET_KEY = '@1rc$o(7=tt#kd+4s$u6wchm**z^)4x90)7f6z(i&amp;55@o11*8o'
90 90
91 91 # List of callables that know how to import templates from various sources.
92 92 TEMPLATE_LOADERS = (
93 93 'django.template.loaders.filesystem.Loader',
94 94 'django.template.loaders.app_directories.Loader',
95 95 # 'django.template.loaders.eggs.Loader',
96 96 )
97 97
98 98 TEMPLATE_CONTEXT_PROCESSORS = (
99 99 'django.core.context_processors.media',
100 100 'django.core.context_processors.static',
101 101 'django.core.context_processors.request',
102 102 'django.contrib.auth.context_processors.auth',
103 103 )
104 104
105 105 MIDDLEWARE_CLASSES = (
106 106 'django.contrib.sessions.middleware.SessionMiddleware',
107 107 'django.middleware.locale.LocaleMiddleware',
108 108 'django.middleware.common.CommonMiddleware',
109 109 # 'django.middleware.csrf.CsrfViewMiddleware',
110 110 'django.contrib.auth.middleware.AuthenticationMiddleware',
111 111 'django.contrib.messages.middleware.MessageMiddleware',
112 112 # Uncomment the next line for simple clickjacking protection:
113 113 # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
114 114 )
115 115
116 116 ROOT_URLCONF = 'neboard.urls'
117 117
118 118 # Python dotted path to the WSGI application used by Django's runserver.
119 119 WSGI_APPLICATION = 'neboard.wsgi.application'
120 120
121 121 TEMPLATE_DIRS = (
122 122 # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
123 123 # Always use forward slashes, even on Windows.
124 124 # Don't forget to use absolute paths, not relative paths.
125 125 'templates',
126 126 )
127 127
128 128 INSTALLED_APPS = (
129 129 'django.contrib.auth',
130 130 'django.contrib.contenttypes',
131 131 'django.contrib.sessions',
132 132 # 'django.contrib.sites',
133 133 'django.contrib.messages',
134 134 'django.contrib.staticfiles',
135 135 # Uncomment the next line to enable the admin:
136 136 'django.contrib.admin',
137 137 # Uncomment the next line to enable admin documentation:
138 138 # 'django.contrib.admindocs',
139 139 'django.contrib.markup',
140 140 'django_cleanup',
141 141 'boards',
142 142 'captcha',
143 'south',
143 144 )
144 145
145 146 # TODO: NEED DESIGN FIXES
146 147 CAPTCHA_OUTPUT_FORMAT = (u' %(hidden_field)s '
147 148 u'<div class="form-label">%(image)s</div>'
148 149 u'<div class="form-text">%(text_field)s</div>')
149 150
150 151 # A sample logging configuration. The only tangible logging
151 152 # performed by this configuration is to send an email to
152 153 # the site admins on every HTTP 500 error when DEBUG=False.
153 154 # See http://docs.djangoproject.com/en/dev/topics/logging for
154 155 # more details on how to customize your logging configuration.
155 156 LOGGING = {
156 157 'version': 1,
157 158 'disable_existing_loggers': False,
158 159 'filters': {
159 160 'require_debug_false': {
160 161 '()': 'django.utils.log.RequireDebugFalse'
161 162 }
162 163 },
163 164 'handlers': {
164 165 'mail_admins': {
165 166 'level': 'ERROR',
166 167 'filters': ['require_debug_false'],
167 168 'class': 'django.utils.log.AdminEmailHandler'
168 169 }
169 170 },
170 171 'loggers': {
171 172 'django.request': {
172 173 'handlers': ['mail_admins'],
173 174 'level': 'ERROR',
174 175 'propagate': True,
175 176 },
176 177 }
177 178 }
178 179
179 180 MARKUP_FIELD_TYPES = (
180 181 ('markdown', markdown_extended),
181 182 )
182 183 # Custom imageboard settings
183 184 MAX_POSTS_PER_THREAD = 10 # Thread bumplimit
184 185 MAX_THREAD_COUNT = 500 # Old threads will be deleted to preserve this count
185 186 THREADS_PER_PAGE = 10
186 187 SITE_NAME = 'Neboard'
187 188
188 189 THEMES = [
189 190 ('md', 'Mystic Dark'),
190 ('sw', 'Snow White') ]
191 ('sw', 'Snow White')
192 ]
191 193
192 194 DEFAULT_THEME = 'md'
193 195
194 196 POPULAR_TAGS = 10
195 197 LAST_REPLIES_COUNT = 3
196 198
197 ENABLE_CAPTCHA = True
199 ENABLE_CAPTCHA = False
198 200 # if user tries to post before CAPTCHA_DEFAULT_SAFE_TIME. Captcha will be shown
199 201 CAPTCHA_DEFAULT_SAFE_TIME = 30 # seconds
General Comments 0
You need to be logged in to leave comments. Login now