##// END OF EJS Templates
Fixed tag problems with python3 (related to the 'map' function that was...
neko259 -
r766:10f4d0f7 default
parent child Browse files
Show More
@@ -1,343 +1,347 b''
1 1 from datetime import datetime, timedelta, date
2 2 from datetime import time as dtime
3 3 import logging
4 4 import re
5 5
6 6 from django.core.cache import cache
7 7 from django.core.urlresolvers import reverse
8 8 from django.db import models, transaction
9 9 from django.template.loader import render_to_string
10 10 from django.utils import timezone
11 11 from markupfield.fields import MarkupField
12 12
13 13 from boards.models import PostImage
14 14 from boards.models.base import Viewable
15 15 from boards.models.thread import Thread
16 16
17 17
18 18 APP_LABEL_BOARDS = 'boards'
19 19
20 20 CACHE_KEY_PPD = 'ppd'
21 21 CACHE_KEY_POST_URL = 'post_url'
22 22
23 23 POSTS_PER_DAY_RANGE = 7
24 24
25 25 BAN_REASON_AUTO = 'Auto'
26 26
27 27 IMAGE_THUMB_SIZE = (200, 150)
28 28
29 29 TITLE_MAX_LENGTH = 200
30 30
31 31 DEFAULT_MARKUP_TYPE = 'bbcode'
32 32
33 33 # TODO This should be removed
34 34 NO_IP = '0.0.0.0'
35 35
36 36 # TODO Real user agent should be saved instead of this
37 37 UNKNOWN_UA = ''
38 38
39 39 REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]')
40 40
41 41 logger = logging.getLogger(__name__)
42 42
43 43
44 44 class PostManager(models.Manager):
45 45 def create_post(self, title, text, image=None, thread=None, ip=NO_IP,
46 46 tags=None):
47 47 """
48 48 Creates new post
49 49 """
50 50
51 if not tags:
52 tags = []
53
51 54 posting_time = timezone.now()
52 55 if not thread:
53 56 thread = Thread.objects.create(bump_time=posting_time,
54 57 last_edit_time=posting_time)
55 58 new_thread = True
56 59 else:
57 60 thread.bump()
58 61 thread.last_edit_time = posting_time
59 62 thread.save()
60 63 new_thread = False
61 64
62 65 post = self.create(title=title,
63 66 text=text,
64 67 pub_time=posting_time,
65 68 thread_new=thread,
66 69 poster_ip=ip,
67 70 poster_user_agent=UNKNOWN_UA, # TODO Get UA at
68 71 # last!
69 72 last_edit_time=posting_time)
70 73
71 74 if image:
72 75 post_image = PostImage.objects.create(image=image)
73 76 post.images.add(post_image)
74 77 logger.info('Created image #%d for post #%d' % (post_image.id,
75 78 post.id))
76 79
77 80 thread.replies.add(post)
78 if tags:
79 map(thread.add_tag, tags)
81 for tag in tags:
82 thread.add_tag(tag)
80 83
81 84 if new_thread:
82 85 Thread.objects.process_oldest_threads()
83 86 self.connect_replies(post)
84 87
85 88 logger.info('Created post #%d with title %s' % (post.id,
86 89 post.get_title()))
87 90
88 91 return post
89 92
90 93 def delete_post(self, post):
91 94 """
92 95 Deletes post and update or delete its thread
93 96 """
94 97
95 98 post_id = post.id
96 99
97 100 thread = post.get_thread()
98 101
99 102 if post.is_opening():
100 103 thread.delete()
101 104 else:
102 105 thread.last_edit_time = timezone.now()
103 106 thread.save()
104 107
105 108 post.delete()
106 109
107 110 logger.info('Deleted post #%d (%s)' % (post_id, post.get_title()))
108 111
109 112 def delete_posts_by_ip(self, ip):
110 113 """
111 114 Deletes all posts of the author with same IP
112 115 """
113 116
114 117 posts = self.filter(poster_ip=ip)
115 map(self.delete_post, posts)
118 for post in posts:
119 self.delete_post(post)
116 120
117 121 def connect_replies(self, post):
118 122 """
119 123 Connects replies to a post to show them as a reflink map
120 124 """
121 125
122 126 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
123 127 post_id = reply_number.group(1)
124 128 ref_post = self.filter(id=post_id)
125 129 if ref_post.count() > 0:
126 130 referenced_post = ref_post[0]
127 131 referenced_post.referenced_posts.add(post)
128 132 referenced_post.last_edit_time = post.pub_time
129 133 referenced_post.build_refmap()
130 134 referenced_post.save(update_fields=['refmap', 'last_edit_time'])
131 135
132 136 referenced_thread = referenced_post.get_thread()
133 137 referenced_thread.last_edit_time = post.pub_time
134 138 referenced_thread.save(update_fields=['last_edit_time'])
135 139
136 140 def get_posts_per_day(self):
137 141 """
138 142 Gets average count of posts per day for the last 7 days
139 143 """
140 144
141 145 day_end = date.today()
142 146 day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
143 147
144 148 cache_key = CACHE_KEY_PPD + str(day_end)
145 149 ppd = cache.get(cache_key)
146 150 if ppd:
147 151 return ppd
148 152
149 153 day_time_start = timezone.make_aware(datetime.combine(
150 154 day_start, dtime()), timezone.get_current_timezone())
151 155 day_time_end = timezone.make_aware(datetime.combine(
152 156 day_end, dtime()), timezone.get_current_timezone())
153 157
154 158 posts_per_period = float(self.filter(
155 159 pub_time__lte=day_time_end,
156 160 pub_time__gte=day_time_start).count())
157 161
158 162 ppd = posts_per_period / POSTS_PER_DAY_RANGE
159 163
160 164 cache.set(cache_key, ppd)
161 165 return ppd
162 166
163 167
164 168 class Post(models.Model, Viewable):
165 169 """A post is a message."""
166 170
167 171 objects = PostManager()
168 172
169 173 class Meta:
170 174 app_label = APP_LABEL_BOARDS
171 175 ordering = ('id',)
172 176
173 177 title = models.CharField(max_length=TITLE_MAX_LENGTH)
174 178 pub_time = models.DateTimeField()
175 179 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
176 180 escape_html=False)
177 181
178 182 images = models.ManyToManyField(PostImage, null=True, blank=True,
179 183 related_name='ip+', db_index=True)
180 184
181 185 poster_ip = models.GenericIPAddressField()
182 186 poster_user_agent = models.TextField()
183 187
184 188 thread_new = models.ForeignKey('Thread', null=True, default=None,
185 189 db_index=True)
186 190 last_edit_time = models.DateTimeField()
187 191
188 192 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
189 193 null=True,
190 194 blank=True, related_name='rfp+',
191 195 db_index=True)
192 196 refmap = models.TextField(null=True, blank=True)
193 197
194 198 def __unicode__(self):
195 199 return '#' + str(self.id) + ' ' + self.title + ' (' + \
196 200 self.text.raw[:50] + ')'
197 201
198 202 def get_title(self):
199 203 """
200 204 Gets original post title or part of its text.
201 205 """
202 206
203 207 title = self.title
204 208 if not title:
205 209 title = self.text.rendered
206 210
207 211 return title
208 212
209 213 def build_refmap(self):
210 214 """
211 215 Builds a replies map string from replies list. This is a cache to stop
212 216 the server from recalculating the map on every post show.
213 217 """
214 218 map_string = ''
215 219
216 220 first = True
217 221 for refpost in self.referenced_posts.all():
218 222 if not first:
219 223 map_string += ', '
220 224 map_string += '<a href="%s">&gt;&gt;%s</a>' % (refpost.get_url(),
221 225 refpost.id)
222 226 first = False
223 227
224 228 self.refmap = map_string
225 229
226 230 def get_sorted_referenced_posts(self):
227 231 return self.refmap
228 232
229 233 def is_referenced(self):
230 234 return len(self.refmap) > 0
231 235
232 236 def is_opening(self):
233 237 """
234 238 Checks if this is an opening post or just a reply.
235 239 """
236 240
237 241 return self.get_thread().get_opening_post_id() == self.id
238 242
239 243 @transaction.atomic
240 244 def add_tag(self, tag):
241 245 edit_time = timezone.now()
242 246
243 247 thread = self.get_thread()
244 248 thread.add_tag(tag)
245 249 self.last_edit_time = edit_time
246 250 self.save(update_fields=['last_edit_time'])
247 251
248 252 thread.last_edit_time = edit_time
249 253 thread.save(update_fields=['last_edit_time'])
250 254
251 255 @transaction.atomic
252 256 def remove_tag(self, tag):
253 257 edit_time = timezone.now()
254 258
255 259 thread = self.get_thread()
256 260 thread.remove_tag(tag)
257 261 self.last_edit_time = edit_time
258 262 self.save(update_fields=['last_edit_time'])
259 263
260 264 thread.last_edit_time = edit_time
261 265 thread.save(update_fields=['last_edit_time'])
262 266
263 267 def get_url(self, thread=None):
264 268 """
265 269 Gets full url to the post.
266 270 """
267 271
268 272 cache_key = CACHE_KEY_POST_URL + str(self.id)
269 273 link = cache.get(cache_key)
270 274
271 275 if not link:
272 276 if not thread:
273 277 thread = self.get_thread()
274 278
275 279 opening_id = thread.get_opening_post_id()
276 280
277 281 if self.id != opening_id:
278 282 link = reverse('thread', kwargs={
279 283 'post_id': opening_id}) + '#' + str(self.id)
280 284 else:
281 285 link = reverse('thread', kwargs={'post_id': self.id})
282 286
283 287 cache.set(cache_key, link)
284 288
285 289 return link
286 290
287 291 def get_thread(self):
288 292 """
289 293 Gets post's thread.
290 294 """
291 295
292 296 return self.thread_new
293 297
294 298 def get_referenced_posts(self):
295 299 return self.referenced_posts.only('id', 'thread_new')
296 300
297 301 def get_text(self):
298 302 return self.text
299 303
300 304 def get_view(self, moderator=False, need_open_link=False,
301 305 truncated=False, *args, **kwargs):
302 306 if 'is_opening' in kwargs:
303 307 is_opening = kwargs['is_opening']
304 308 else:
305 309 is_opening = self.is_opening()
306 310
307 311 if 'thread' in kwargs:
308 312 thread = kwargs['thread']
309 313 else:
310 314 thread = self.get_thread()
311 315
312 316 if 'can_bump' in kwargs:
313 317 can_bump = kwargs['can_bump']
314 318 else:
315 319 can_bump = thread.can_bump()
316 320
317 321 if is_opening:
318 322 opening_post_id = self.id
319 323 else:
320 324 opening_post_id = thread.get_opening_post_id()
321 325
322 326 return render_to_string('boards/post.html', {
323 327 'post': self,
324 328 'moderator': moderator,
325 329 'is_opening': is_opening,
326 330 'thread': thread,
327 331 'bumpable': can_bump,
328 332 'need_open_link': need_open_link,
329 333 'truncated': truncated,
330 334 'opening_post_id': opening_post_id,
331 335 })
332 336
333 337 def get_first_image(self):
334 338 return self.images.earliest('id')
335 339
336 340 def delete(self, using=None):
337 341 """
338 342 Deletes all post images and the post itself.
339 343 """
340 344
341 345 self.images.all().delete()
342 346
343 347 super(Post, self).delete(using)
@@ -1,270 +1,263 b''
1 1 # coding=utf-8
2 2 import time
3 3 import logging
4 4 from django.core.paginator import Paginator
5 5
6 6 from django.test import TestCase
7 7 from django.test.client import Client
8 8 from django.core.urlresolvers import reverse, NoReverseMatch
9 9 from boards.abstracts.settingsmanager import get_settings_manager
10 10
11 11 from boards.models import Post, Tag, Thread
12 12 from boards import urls
13 13 from boards import settings
14 14 import neboard
15 15
16 16 TEST_TAG = 'test_tag'
17 17
18 18 PAGE_404 = 'boards/404.html'
19 19
20 20 TEST_TEXT = 'test text'
21 21
22 22 NEW_THREAD_PAGE = '/'
23 23 THREAD_PAGE_ONE = '/thread/1/'
24 24 THREAD_PAGE = '/thread/'
25 25 TAG_PAGE = '/tag/'
26 26 HTTP_CODE_REDIRECT = 302
27 27 HTTP_CODE_OK = 200
28 28 HTTP_CODE_NOT_FOUND = 404
29 29
30 30 logger = logging.getLogger(__name__)
31 31
32 32
33 33 class PostTests(TestCase):
34 34
35 35 def _create_post(self):
36 36 tag = Tag.objects.create(name=TEST_TAG)
37 37 return Post.objects.create_post(title='title', text='text',
38 38 tags=[tag])
39 39
40 40 def test_post_add(self):
41 41 """Test adding post"""
42 42
43 43 post = self._create_post()
44 44
45 45 self.assertIsNotNone(post, 'No post was created.')
46 46 self.assertEqual(TEST_TAG, post.get_thread().tags.all()[0].name,
47 47 'No tags were added to the post.')
48 48
49 49 def test_delete_post(self):
50 50 """Test post deletion"""
51 51
52 52 post = self._create_post()
53 53 post_id = post.id
54 54
55 55 Post.objects.delete_post(post)
56 56
57 57 self.assertFalse(Post.objects.filter(id=post_id).exists())
58 58
59 59 def test_delete_thread(self):
60 60 """Test thread deletion"""
61 61
62 62 opening_post = self._create_post()
63 63 thread = opening_post.get_thread()
64 64 reply = Post.objects.create_post("", "", thread=thread)
65 65
66 66 thread.delete()
67 67
68 68 self.assertFalse(Post.objects.filter(id=reply.id).exists())
69 69
70 70 def test_post_to_thread(self):
71 71 """Test adding post to a thread"""
72 72
73 73 op = self._create_post()
74 74 post = Post.objects.create_post("", "", thread=op.get_thread())
75 75
76 76 self.assertIsNotNone(post, 'Reply to thread wasn\'t created')
77 77 self.assertEqual(op.get_thread().last_edit_time, post.pub_time,
78 78 'Post\'s create time doesn\'t match thread last edit'
79 79 ' time')
80 80
81 81 def test_delete_posts_by_ip(self):
82 82 """Test deleting posts with the given ip"""
83 83
84 84 post = self._create_post()
85 85 post_id = post.id
86 86
87 87 Post.objects.delete_posts_by_ip('0.0.0.0')
88 88
89 89 self.assertFalse(Post.objects.filter(id=post_id).exists())
90 90
91 91 def test_get_thread(self):
92 92 """Test getting all posts of a thread"""
93 93
94 94 opening_post = self._create_post()
95 95
96 96 for i in range(0, 2):
97 97 Post.objects.create_post('title', 'text',
98 98 thread=opening_post.get_thread())
99 99
100 100 thread = opening_post.get_thread()
101 101
102 102 self.assertEqual(3, thread.replies.count())
103 103
104 104 def test_create_post_with_tag(self):
105 105 """Test adding tag to post"""
106 106
107 107 tag = Tag.objects.create(name='test_tag')
108 108 post = Post.objects.create_post(title='title', text='text', tags=[tag])
109 109
110 110 thread = post.get_thread()
111 111 self.assertIsNotNone(post, 'Post not created')
112 112 self.assertTrue(tag in thread.tags.all(), 'Tag not added to thread')
113 113 self.assertTrue(thread in tag.threads.all(), 'Thread not added to tag')
114 114
115 115 def test_thread_max_count(self):
116 116 """Test deletion of old posts when the max thread count is reached"""
117 117
118 118 for i in range(settings.MAX_THREAD_COUNT + 1):
119 119 self._create_post()
120 120
121 121 self.assertEqual(settings.MAX_THREAD_COUNT,
122 122 len(Thread.objects.filter(archived=False)))
123 123
124 124 def test_pages(self):
125 125 """Test that the thread list is properly split into pages"""
126 126
127 127 for i in range(settings.MAX_THREAD_COUNT):
128 128 self._create_post()
129 129
130 130 all_threads = Thread.objects.filter(archived=False)
131 131
132 132 paginator = Paginator(Thread.objects.filter(archived=False),
133 133 settings.THREADS_PER_PAGE)
134 134 posts_in_second_page = paginator.page(2).object_list
135 135 first_post = posts_in_second_page[0]
136 136
137 137 self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id,
138 138 first_post.id)
139 139
140 140
141 141 class PagesTest(TestCase):
142 142
143 143 def test_404(self):
144 144 """Test receiving error 404 when opening a non-existent page"""
145 145
146 146 tag_name = u'test_tag'
147 147 tag = Tag.objects.create(name=tag_name)
148 148 client = Client()
149 149
150 150 Post.objects.create_post('title', TEST_TEXT, tags=[tag])
151 151
152 152 existing_post_id = Post.objects.all()[0].id
153 153 response_existing = client.get(THREAD_PAGE + str(existing_post_id) +
154 154 '/')
155 155 self.assertEqual(HTTP_CODE_OK, response_existing.status_code,
156 156 u'Cannot open existing thread')
157 157
158 158 response_not_existing = client.get(THREAD_PAGE + str(
159 159 existing_post_id + 1) + '/')
160 160 self.assertEqual(PAGE_404, response_not_existing.templates[0].name,
161 161 u'Not existing thread is opened')
162 162
163 163 response_existing = client.get(TAG_PAGE + tag_name + '/')
164 164 self.assertEqual(HTTP_CODE_OK,
165 165 response_existing.status_code,
166 166 u'Cannot open existing tag')
167 167
168 168 response_not_existing = client.get(TAG_PAGE + u'not_tag' + '/')
169 169 self.assertEqual(PAGE_404,
170 170 response_not_existing.templates[0].name,
171 171 u'Not existing tag is opened')
172 172
173 173 reply_id = Post.objects.create_post('', TEST_TEXT,
174 174 thread=Post.objects.all()[0]
175 175 .get_thread())
176 176 response_not_existing = client.get(THREAD_PAGE + str(
177 177 reply_id) + '/')
178 178 self.assertEqual(PAGE_404,
179 179 response_not_existing.templates[0].name,
180 180 u'Reply is opened as a thread')
181 181
182 182
183 183 class FormTest(TestCase):
184 184 def test_post_validation(self):
185 # Disable captcha for the test
186 captcha_enabled = neboard.settings.ENABLE_CAPTCHA
187 neboard.settings.ENABLE_CAPTCHA = False
188
189 185 client = Client()
190 186
191 187 valid_tags = u'tag1 tag_2 Ρ‚Π΅Π³_3'
192 188 invalid_tags = u'$%_356 ---'
193 189
194 190 response = client.post(NEW_THREAD_PAGE, {'title': 'test title',
195 191 'text': TEST_TEXT,
196 192 'tags': valid_tags})
197 193 self.assertEqual(response.status_code, HTTP_CODE_REDIRECT,
198 194 msg='Posting new message failed: got code ' +
199 195 str(response.status_code))
200 196
201 197 self.assertEqual(1, Post.objects.count(),
202 198 msg='No posts were created')
203 199
204 200 client.post(NEW_THREAD_PAGE, {'text': TEST_TEXT,
205 201 'tags': invalid_tags})
206 202 self.assertEqual(1, Post.objects.count(), msg='The validation passed '
207 203 'where it should fail')
208 204
209 205 # Change posting delay so we don't have to wait for 30 seconds or more
210 206 old_posting_delay = neboard.settings.POSTING_DELAY
211 207 # Wait fot the posting delay or we won't be able to post
212 208 settings.POSTING_DELAY = 1
213 209 time.sleep(neboard.settings.POSTING_DELAY + 1)
214 210 response = client.post(THREAD_PAGE_ONE, {'text': TEST_TEXT,
215 211 'tags': valid_tags})
216 212 self.assertEqual(HTTP_CODE_REDIRECT, response.status_code,
217 213 msg=u'Posting new message failed: got code ' +
218 214 str(response.status_code))
219 215 # Restore posting delay
220 216 settings.POSTING_DELAY = old_posting_delay
221 217
222 218 self.assertEqual(2, Post.objects.count(),
223 219 msg=u'No posts were created')
224 220
225 # Restore captcha setting
226 settings.ENABLE_CAPTCHA = captcha_enabled
227
228 221
229 222 class ViewTest(TestCase):
230 223
231 224 def test_all_views(self):
232 225 """
233 226 Try opening all views defined in ulrs.py that don't need additional
234 227 parameters
235 228 """
236 229
237 230 client = Client()
238 231 for url in urls.urlpatterns:
239 232 try:
240 233 view_name = url.name
241 234 logger.debug('Testing view %s' % view_name)
242 235
243 236 try:
244 237 response = client.get(reverse(view_name))
245 238
246 239 self.assertEqual(HTTP_CODE_OK, response.status_code,
247 240 '%s view not opened' % view_name)
248 241 except NoReverseMatch:
249 242 # This view just needs additional arguments
250 243 pass
251 244 except Exception as e:
252 245 self.fail('Got exception %s at %s view' % (e, view_name))
253 246 except AttributeError:
254 247 # This is normal, some views do not have names
255 248 pass
256 249
257 250
258 251 class AbstractTest(TestCase):
259 252 def test_settings_manager(self):
260 253 request = MockRequest()
261 254 settings_manager = get_settings_manager(request)
262 255
263 256 settings_manager.set_setting('test_setting', 'test_value')
264 257 self.assertEqual('test_value', settings_manager.get_setting(
265 258 'test_setting'), u'Setting update failed.')
266 259
267 260
268 261 class MockRequest:
269 262 def __init__(self):
270 263 self.session = dict()
@@ -1,139 +1,139 b''
1 1 import string
2 2
3 3 from django.db import transaction
4 4 from django.shortcuts import render, redirect
5 5
6 6 from boards import utils, settings
7 7 from boards.abstracts.paginator import get_paginator
8 8 from boards.abstracts.settingsmanager import get_settings_manager
9 9 from boards.forms import ThreadForm, PlainErrorList
10 10 from boards.models import Post, Thread, Ban, Tag
11 11 from boards.views.banned import BannedView
12 12 from boards.views.base import BaseBoardView, CONTEXT_FORM
13 13 from boards.views.posting_mixin import PostMixin
14 14
15 15 FORM_TAGS = 'tags'
16 16 FORM_TEXT = 'text'
17 17 FORM_TITLE = 'title'
18 18 FORM_IMAGE = 'image'
19 19
20 20 TAG_DELIMITER = ' '
21 21
22 22 PARAMETER_CURRENT_PAGE = 'current_page'
23 23 PARAMETER_PAGINATOR = 'paginator'
24 24 PARAMETER_THREADS = 'threads'
25 25
26 26 TEMPLATE = 'boards/posting_general.html'
27 27 DEFAULT_PAGE = 1
28 28
29 29
30 30 class AllThreadsView(PostMixin, BaseBoardView):
31 31
32 32 def __init__(self):
33 33 self.settings_manager = None
34 34 super(AllThreadsView, self).__init__()
35 35
36 36 def get(self, request, page=DEFAULT_PAGE, form=None):
37 37 context = self.get_context_data(request=request)
38 38
39 39 if not form:
40 40 form = ThreadForm(error_class=PlainErrorList)
41 41
42 42 self.settings_manager = get_settings_manager(request)
43 43 paginator = get_paginator(self.get_threads(),
44 44 settings.THREADS_PER_PAGE)
45 45 paginator.current_page = int(page)
46 46
47 47 threads = paginator.page(page).object_list
48 48
49 49 context[PARAMETER_THREADS] = threads
50 50 context[CONTEXT_FORM] = form
51 51
52 52 self._get_page_context(paginator, context, page)
53 53
54 54 return render(request, TEMPLATE, context)
55 55
56 56 def post(self, request, page=DEFAULT_PAGE):
57 57 form = ThreadForm(request.POST, request.FILES,
58 58 error_class=PlainErrorList)
59 59 form.session = request.session
60 60
61 61 if form.is_valid():
62 62 return self.create_thread(request, form)
63 63 if form.need_to_ban:
64 64 # Ban user because he is suspected to be a bot
65 65 self._ban_current_user(request)
66 66
67 67 return self.get(request, page, form)
68 68
69 69 @staticmethod
70 70 def _get_page_context(paginator, context, page):
71 71 """
72 72 Get pagination context variables
73 73 """
74 74
75 75 context[PARAMETER_PAGINATOR] = paginator
76 76 context[PARAMETER_CURRENT_PAGE] = paginator.page(int(page))
77 77
78 78 @staticmethod
79 79 def parse_tags_string(tag_strings):
80 80 """
81 81 Parses tag list string and returns tag object list.
82 82 """
83 83
84 84 tags = []
85 85
86 86 if tag_strings:
87 87 tag_strings = tag_strings.split(TAG_DELIMITER)
88 88 for tag_name in tag_strings:
89 tag_name = string.lower(tag_name.strip())
89 tag_name = tag_name.strip().lower()
90 90 if len(tag_name) > 0:
91 91 tag, created = Tag.objects.get_or_create(name=tag_name)
92 92 tags.append(tag)
93 93
94 94 return tags
95 95
96 96 @transaction.atomic
97 97 def create_thread(self, request, form, html_response=True):
98 98 """
99 99 Creates a new thread with an opening post.
100 100 """
101 101
102 102 ip = utils.get_client_ip(request)
103 103 is_banned = Ban.objects.filter(ip=ip).exists()
104 104
105 105 if is_banned:
106 106 if html_response:
107 107 return redirect(BannedView().as_view())
108 108 else:
109 109 return
110 110
111 111 data = form.cleaned_data
112 112
113 113 title = data[FORM_TITLE]
114 114 text = data[FORM_TEXT]
115 115
116 116 text = self._remove_invalid_links(text)
117 117
118 118 if FORM_IMAGE in data.keys():
119 119 image = data[FORM_IMAGE]
120 120 else:
121 121 image = None
122 122
123 123 tag_strings = data[FORM_TAGS]
124 124
125 125 tags = self.parse_tags_string(tag_strings)
126 126
127 127 post = Post.objects.create_post(title=title, text=text, image=image,
128 128 ip=ip, tags=tags)
129 129
130 130 if html_response:
131 131 return redirect(post.get_url())
132 132
133 133 def get_threads(self):
134 134 """
135 135 Gets list of threads that will be shown on a page.
136 136 """
137 137
138 138 return Thread.objects.all().order_by('-bump_time')\
139 139 .exclude(tags__in=self.settings_manager.get_hidden_tags())
@@ -1,260 +1,250 b''
1 1 # Django settings for neboard project.
2 2 import os
3 3 from boards.mdx_neboard import bbcode_extended
4 4
5 5 DEBUG = True
6 6 TEMPLATE_DEBUG = DEBUG
7 7
8 8 ADMINS = (
9 9 # ('Your Name', 'your_email@example.com'),
10 10 ('admin', 'admin@example.com')
11 11 )
12 12
13 13 MANAGERS = ADMINS
14 14
15 15 DATABASES = {
16 16 'default': {
17 17 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
18 18 'NAME': 'database.db', # Or path to database file if using sqlite3.
19 19 'USER': '', # Not used with sqlite3.
20 20 'PASSWORD': '', # Not used with sqlite3.
21 21 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
22 22 'PORT': '', # Set to empty string for default. Not used with sqlite3.
23 23 'CONN_MAX_AGE': None,
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 )
86 86
87 87 if DEBUG:
88 88 STATICFILES_STORAGE = \
89 89 'django.contrib.staticfiles.storage.StaticFilesStorage'
90 90 else:
91 91 STATICFILES_STORAGE = \
92 92 'django.contrib.staticfiles.storage.CachedStaticFilesStorage'
93 93
94 94 # Make this unique, and don't share it with anybody.
95 95 SECRET_KEY = '@1rc$o(7=tt#kd+4s$u6wchm**z^)4x90)7f6z(i&amp;55@o11*8o'
96 96
97 97 # List of callables that know how to import templates from various sources.
98 98 TEMPLATE_LOADERS = (
99 99 'django.template.loaders.filesystem.Loader',
100 100 'django.template.loaders.app_directories.Loader',
101 101 )
102 102
103 103 TEMPLATE_CONTEXT_PROCESSORS = (
104 104 'django.core.context_processors.media',
105 105 'django.core.context_processors.static',
106 106 'django.core.context_processors.request',
107 107 'django.contrib.auth.context_processors.auth',
108 108 'boards.context_processors.user_and_ui_processor',
109 109 )
110 110
111 111 MIDDLEWARE_CLASSES = (
112 112 'django.contrib.sessions.middleware.SessionMiddleware',
113 113 'django.middleware.locale.LocaleMiddleware',
114 114 'django.middleware.common.CommonMiddleware',
115 115 'django.contrib.auth.middleware.AuthenticationMiddleware',
116 116 'django.contrib.messages.middleware.MessageMiddleware',
117 117 'boards.middlewares.BanMiddleware',
118 118 'boards.middlewares.MinifyHTMLMiddleware',
119 119 )
120 120
121 121 ROOT_URLCONF = 'neboard.urls'
122 122
123 123 # Python dotted path to the WSGI application used by Django's runserver.
124 124 WSGI_APPLICATION = 'neboard.wsgi.application'
125 125
126 126 TEMPLATE_DIRS = (
127 127 # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
128 128 # Always use forward slashes, even on Windows.
129 129 # Don't forget to use absolute paths, not relative paths.
130 130 'templates',
131 131 )
132 132
133 133 INSTALLED_APPS = (
134 134 'django.contrib.auth',
135 135 'django.contrib.contenttypes',
136 136 'django.contrib.sessions',
137 137 # 'django.contrib.sites',
138 138 'django.contrib.messages',
139 139 'django.contrib.staticfiles',
140 140 # Uncomment the next line to enable the admin:
141 141 'django.contrib.admin',
142 142 # Uncomment the next line to enable admin documentation:
143 143 # 'django.contrib.admindocs',
144 144 'django.contrib.humanize',
145 145 'django_cleanup',
146 146
147 147 # Migrations
148 148 'south',
149 149 'debug_toolbar',
150 150
151 'captcha',
152
153 151 # Search
154 152 'haystack',
155 153
156 154 'boards',
157 155 )
158 156
159 157 DEBUG_TOOLBAR_PANELS = (
160 158 'debug_toolbar.panels.version.VersionDebugPanel',
161 159 'debug_toolbar.panels.timer.TimerDebugPanel',
162 160 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
163 161 'debug_toolbar.panels.headers.HeaderDebugPanel',
164 162 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
165 163 'debug_toolbar.panels.template.TemplateDebugPanel',
166 164 'debug_toolbar.panels.sql.SQLDebugPanel',
167 165 'debug_toolbar.panels.signals.SignalDebugPanel',
168 166 'debug_toolbar.panels.logger.LoggingPanel',
169 167 )
170 168
171 # TODO: NEED DESIGN FIXES
172 CAPTCHA_OUTPUT_FORMAT = (u' %(hidden_field)s '
173 u'<div class="form-label">%(image)s</div>'
174 u'<div class="form-text">%(text_field)s</div>')
175
176 169 # A sample logging configuration. The only tangible logging
177 170 # performed by this configuration is to send an email to
178 171 # the site admins on every HTTP 500 error when DEBUG=False.
179 172 # See http://docs.djangoproject.com/en/dev/topics/logging for
180 173 # more details on how to customize your logging configuration.
181 174 LOGGING = {
182 175 'version': 1,
183 176 'disable_existing_loggers': False,
184 177 'formatters': {
185 178 'verbose': {
186 179 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
187 180 },
188 181 'simple': {
189 182 'format': '%(levelname)s %(asctime)s [%(module)s] %(message)s'
190 183 },
191 184 },
192 185 'filters': {
193 186 'require_debug_false': {
194 187 '()': 'django.utils.log.RequireDebugFalse'
195 188 }
196 189 },
197 190 'handlers': {
198 191 'console': {
199 192 'level': 'DEBUG',
200 193 'class': 'logging.StreamHandler',
201 194 'formatter': 'simple'
202 195 },
203 196 },
204 197 'loggers': {
205 198 'boards': {
206 199 'handlers': ['console'],
207 200 'level': 'DEBUG',
208 201 }
209 202 },
210 203 }
211 204
212 205 HAYSTACK_CONNECTIONS = {
213 206 'default': {
214 207 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
215 208 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
216 209 },
217 210 }
218 211
219 212 MARKUP_FIELD_TYPES = (
220 213 ('bbcode', bbcode_extended),
221 214 )
222 215
223 216 THEMES = [
224 217 ('md', 'Mystic Dark'),
225 218 ('md_centered', 'Mystic Dark (centered)'),
226 219 ('sw', 'Snow White'),
227 220 ('pg', 'Photon Gray'),
228 221 ]
229 222
230 223 POPULAR_TAGS = 10
231 224
232 ENABLE_CAPTCHA = False
233 # if user tries to post before CAPTCHA_DEFAULT_SAFE_TIME. Captcha will be shown
234 CAPTCHA_DEFAULT_SAFE_TIME = 30 # seconds
235 225 POSTING_DELAY = 20 # seconds
236 226
237 227 COMPRESS_HTML = True
238 228
239 229 # Debug mode middlewares
240 230 if DEBUG:
241 231 MIDDLEWARE_CLASSES += (
242 232 #'boards.profiler.ProfilerMiddleware',
243 233 'debug_toolbar.middleware.DebugToolbarMiddleware',
244 234 )
245 235
246 236 def custom_show_toolbar(request):
247 237 return DEBUG
248 238
249 239 DEBUG_TOOLBAR_CONFIG = {
250 240 'INTERCEPT_REDIRECTS': False,
251 241 'SHOW_TOOLBAR_CALLBACK': custom_show_toolbar,
252 242 'HIDE_DJANGO_SQL': False,
253 243 'ENABLE_STACKTRACES': True,
254 244 }
255 245
256 246 # FIXME Uncommenting this fails somehow. Need to investigate this
257 247 #DEBUG_TOOLBAR_PANELS += (
258 248 # 'debug_toolbar.panels.profiling.ProfilingDebugPanel',
259 249 #)
260 250
General Comments 0
You need to be logged in to leave comments. Login now