##// END OF EJS Templates
Merged in 2.0 branch
neko259 -
r747:668c0b11 merge default
parent child Browse files
Show More
@@ -0,0 +1,143 b''
1 from django.shortcuts import get_object_or_404
2 from boards.models import Tag
3
4 __author__ = 'neko259'
5
6 SESSION_SETTING = 'setting'
7
8 PERMISSION_MODERATE = 'moderator'
9
10 SETTING_THEME = 'theme'
11 SETTING_FAVORITE_TAGS = 'favorite_tags'
12 SETTING_HIDDEN_TAGS = 'hidden_tags'
13 SETTING_PERMISSIONS = 'permissions'
14
15 DEFAULT_THEME = 'md'
16
17
18 def get_settings_manager(request):
19 """
20 Get settings manager based on the request object. Currently only
21 session-based manager is supported. In the future, cookie-based or
22 database-based managers could be implemented.
23 """
24 return SessionSettingsManager(request.session)
25
26
27 class SettingsManager:
28 """
29 Base settings manager class. get_setting and set_setting methods should
30 be overriden.
31 """
32 def __init__(self):
33 pass
34
35 def get_theme(self):
36 theme = self.get_setting(SETTING_THEME)
37 if not theme:
38 theme = DEFAULT_THEME
39 self.set_setting(SETTING_THEME, theme)
40
41 return theme
42
43 def set_theme(self, theme):
44 self.set_setting(SETTING_THEME, theme)
45
46 def has_permission(self, permission):
47 permissions = self.get_setting(SETTING_PERMISSIONS)
48 if permissions:
49 return permission in permissions
50 else:
51 return False
52
53 def get_setting(self, setting):
54 pass
55
56 def set_setting(self, setting, value):
57 pass
58
59 def add_permission(self, permission):
60 permissions = self.get_setting(SETTING_PERMISSIONS)
61 if not permissions:
62 permissions = [permission]
63 else:
64 permissions.append(permission)
65 self.set_setting(SETTING_PERMISSIONS, permissions)
66
67 def del_permission(self, permission):
68 permissions = self.get_setting(SETTING_PERMISSIONS)
69 if not permissions:
70 permissions = []
71 else:
72 permissions.remove(permission)
73 self.set_setting(SETTING_PERMISSIONS, permissions)
74
75 def get_fav_tags(self):
76 tag_names = self.get_setting(SETTING_FAVORITE_TAGS)
77 tags = []
78 if tag_names:
79 for tag_name in tag_names:
80 tag = get_object_or_404(Tag, name=tag_name)
81 tags.append(tag)
82
83 return tags
84
85 def add_fav_tag(self, tag):
86 tags = self.get_setting(SETTING_FAVORITE_TAGS)
87 if not tags:
88 tags = [tag.name]
89 else:
90 if not tag.name in tags:
91 tags.append(tag.name)
92 self.set_setting(SETTING_FAVORITE_TAGS, tags)
93
94 def del_fav_tag(self, tag):
95 tags = self.get_setting(SETTING_FAVORITE_TAGS)
96 if tag.name in tags:
97 tags.remove(tag.name)
98 self.set_setting(SETTING_FAVORITE_TAGS, tags)
99
100 def get_hidden_tags(self):
101 tag_names = self.get_setting(SETTING_HIDDEN_TAGS)
102 tags = []
103 if tag_names:
104 for tag_name in tag_names:
105 tag = get_object_or_404(Tag, name=tag_name)
106 tags.append(tag)
107
108 return tags
109
110 def add_hidden_tag(self, tag):
111 tags = self.get_setting(SETTING_HIDDEN_TAGS)
112 if not tags:
113 tags = [tag.name]
114 else:
115 if not tag.name in tags:
116 tags.append(tag.name)
117 self.set_setting(SETTING_HIDDEN_TAGS, tags)
118
119 def del_hidden_tag(self, tag):
120 tags = self.get_setting(SETTING_HIDDEN_TAGS)
121 if tag.name in tags:
122 tags.remove(tag.name)
123 self.set_setting(SETTING_HIDDEN_TAGS, tags)
124
125
126 class SessionSettingsManager(SettingsManager):
127 """
128 Session-based settings manager. All settings are saved to the user's
129 session.
130 """
131 def __init__(self, session):
132 SettingsManager.__init__(self)
133 self.session = session
134
135 def get_setting(self, setting):
136 if setting in self.session:
137 return self.session[setting]
138 else:
139 return None
140
141 def set_setting(self, setting, value):
142 self.session[setting] = value
143
@@ -0,0 +1,146 b''
1 # -*- coding: utf-8 -*-
2 from south.utils import datetime_utils as datetime
3 from south.db import db
4 from south.v2 import SchemaMigration
5 from django.db import models
6
7
8 class Migration(SchemaMigration):
9
10 def forwards(self, orm):
11 # Deleting model 'Setting'
12 db.delete_table(u'boards_setting')
13
14 # Deleting model 'User'
15 db.delete_table(u'boards_user')
16
17 # Removing M2M table for field hidden_threads on 'User'
18 db.delete_table(db.shorten_name(u'boards_user_hidden_threads'))
19
20 # Removing M2M table for field fav_threads on 'User'
21 db.delete_table(db.shorten_name(u'boards_user_fav_threads'))
22
23 # Removing M2M table for field fav_tags on 'User'
24 db.delete_table(db.shorten_name(u'boards_user_fav_tags'))
25
26 # Removing M2M table for field hidden_tags on 'User'
27 db.delete_table(db.shorten_name(u'boards_user_hidden_tags'))
28
29 # Deleting field 'Post.user'
30 db.delete_column(u'boards_post', 'user_id')
31
32
33 def backwards(self, orm):
34 # Adding model 'Setting'
35 db.create_table(u'boards_setting', (
36 ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['boards.User'])),
37 (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
38 ('value', self.gf('django.db.models.fields.CharField')(max_length=50)),
39 ('name', self.gf('django.db.models.fields.CharField')(max_length=50)),
40 ))
41 db.send_create_signal('boards', ['Setting'])
42
43 # Adding model 'User'
44 db.create_table(u'boards_user', (
45 ('registration_time', self.gf('django.db.models.fields.DateTimeField')()),
46 (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
47 ('user_id', self.gf('django.db.models.fields.CharField')(max_length=50)),
48 ('rank', self.gf('django.db.models.fields.IntegerField')()),
49 ))
50 db.send_create_signal('boards', ['User'])
51
52 # Adding M2M table for field hidden_threads on 'User'
53 m2m_table_name = db.shorten_name(u'boards_user_hidden_threads')
54 db.create_table(m2m_table_name, (
55 ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
56 ('user', models.ForeignKey(orm['boards.user'], null=False)),
57 ('post', models.ForeignKey(orm['boards.post'], null=False))
58 ))
59 db.create_unique(m2m_table_name, ['user_id', 'post_id'])
60
61 # Adding M2M table for field fav_threads on 'User'
62 m2m_table_name = db.shorten_name(u'boards_user_fav_threads')
63 db.create_table(m2m_table_name, (
64 ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
65 ('user', models.ForeignKey(orm['boards.user'], null=False)),
66 ('post', models.ForeignKey(orm['boards.post'], null=False))
67 ))
68 db.create_unique(m2m_table_name, ['user_id', 'post_id'])
69
70 # Adding M2M table for field fav_tags on 'User'
71 m2m_table_name = db.shorten_name(u'boards_user_fav_tags')
72 db.create_table(m2m_table_name, (
73 ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
74 ('user', models.ForeignKey(orm['boards.user'], null=False)),
75 ('tag', models.ForeignKey(orm['boards.tag'], null=False))
76 ))
77 db.create_unique(m2m_table_name, ['user_id', 'tag_id'])
78
79 # Adding M2M table for field hidden_tags on 'User'
80 m2m_table_name = db.shorten_name(u'boards_user_hidden_tags')
81 db.create_table(m2m_table_name, (
82 ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
83 ('user', models.ForeignKey(orm['boards.user'], null=False)),
84 ('tag', models.ForeignKey(orm['boards.tag'], null=False))
85 ))
86 db.create_unique(m2m_table_name, ['user_id', 'tag_id'])
87
88 # Adding field 'Post.user'
89 db.add_column(u'boards_post', 'user',
90 self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['boards.User'], null=True),
91 keep_default=False)
92
93
94 models = {
95 'boards.ban': {
96 'Meta': {'object_name': 'Ban'},
97 'can_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
98 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
99 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
100 'reason': ('django.db.models.fields.CharField', [], {'default': "'Auto'", 'max_length': '200'})
101 },
102 'boards.post': {
103 'Meta': {'ordering': "('id',)", 'object_name': 'Post'},
104 '_text_rendered': ('django.db.models.fields.TextField', [], {}),
105 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
106 'images': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ip+'", 'to': "orm['boards.PostImage']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True', 'db_index': 'True'}),
107 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
108 'poster_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
109 'poster_user_agent': ('django.db.models.fields.TextField', [], {}),
110 'pub_time': ('django.db.models.fields.DateTimeField', [], {}),
111 'referenced_posts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'rfp+'", 'to': "orm['boards.Post']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True', 'db_index': 'True'}),
112 'refmap': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
113 'text': ('markupfield.fields.MarkupField', [], {'rendered_field': 'True'}),
114 'text_markup_type': ('django.db.models.fields.CharField', [], {'default': "'markdown'", 'max_length': '30'}),
115 'thread_new': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Thread']", 'null': 'True'}),
116 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'})
117 },
118 'boards.postimage': {
119 'Meta': {'ordering': "('id',)", 'object_name': 'PostImage'},
120 'hash': ('django.db.models.fields.CharField', [], {'max_length': '36'}),
121 'height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
122 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
123 'image': ('boards.thumbs.ImageWithThumbsField', [], {'max_length': '100', 'blank': 'True'}),
124 'pre_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
125 'pre_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
126 'width': ('django.db.models.fields.IntegerField', [], {'default': '0'})
127 },
128 'boards.tag': {
129 'Meta': {'ordering': "('name',)", 'object_name': 'Tag'},
130 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
131 'linked': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}),
132 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'db_index': 'True'}),
133 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tag+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Thread']"})
134 },
135 'boards.thread': {
136 'Meta': {'object_name': 'Thread'},
137 'archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
138 'bump_time': ('django.db.models.fields.DateTimeField', [], {}),
139 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
140 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
141 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tre+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}),
142 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['boards.Tag']", 'symmetrical': 'False'})
143 }
144 }
145
146 complete_apps = ['boards'] No newline at end of file
@@ -0,0 +1,73 b''
1 # -*- coding: utf-8 -*-
2 from south.utils import datetime_utils as datetime
3 from south.db import db
4 from south.v2 import SchemaMigration
5 from django.db import models
6
7
8 class Migration(SchemaMigration):
9
10 def forwards(self, orm):
11 # Deleting field 'Tag.linked'
12 db.delete_column(u'boards_tag', 'linked_id')
13
14
15 def backwards(self, orm):
16 # Adding field 'Tag.linked'
17 db.add_column(u'boards_tag', 'linked',
18 self.gf('django.db.models.fields.related.ForeignKey')(to=orm['boards.Tag'], null=True, blank=True),
19 keep_default=False)
20
21
22 models = {
23 'boards.ban': {
24 'Meta': {'object_name': 'Ban'},
25 'can_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
26 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
27 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
28 'reason': ('django.db.models.fields.CharField', [], {'default': "'Auto'", 'max_length': '200'})
29 },
30 'boards.post': {
31 'Meta': {'ordering': "('id',)", 'object_name': 'Post'},
32 '_text_rendered': ('django.db.models.fields.TextField', [], {}),
33 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34 'images': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ip+'", 'to': "orm['boards.PostImage']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True', 'db_index': 'True'}),
35 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
36 'poster_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
37 'poster_user_agent': ('django.db.models.fields.TextField', [], {}),
38 'pub_time': ('django.db.models.fields.DateTimeField', [], {}),
39 'referenced_posts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'rfp+'", 'to': "orm['boards.Post']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True', 'db_index': 'True'}),
40 'refmap': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
41 'text': ('markupfield.fields.MarkupField', [], {'rendered_field': 'True'}),
42 'text_markup_type': ('django.db.models.fields.CharField', [], {'default': "'bbcode'", 'max_length': '30'}),
43 'thread_new': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Thread']", 'null': 'True'}),
44 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'})
45 },
46 'boards.postimage': {
47 'Meta': {'ordering': "('id',)", 'object_name': 'PostImage'},
48 'hash': ('django.db.models.fields.CharField', [], {'max_length': '36'}),
49 'height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
50 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
51 'image': ('boards.thumbs.ImageWithThumbsField', [], {'max_length': '100', 'blank': 'True'}),
52 'pre_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
53 'pre_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
54 'width': ('django.db.models.fields.IntegerField', [], {'default': '0'})
55 },
56 'boards.tag': {
57 'Meta': {'ordering': "('name',)", 'object_name': 'Tag'},
58 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
59 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'db_index': 'True'}),
60 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tag+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Thread']"})
61 },
62 'boards.thread': {
63 'Meta': {'object_name': 'Thread'},
64 'archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
65 'bump_time': ('django.db.models.fields.DateTimeField', [], {}),
66 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
67 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
68 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tre+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}),
69 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['boards.Tag']", 'symmetrical': 'False'})
70 }
71 }
72
73 complete_apps = ['boards'] No newline at end of file
@@ -0,0 +1,24 b''
1 from django.shortcuts import render
2
3 from boards.abstracts.settingsmanager import PERMISSION_MODERATE,\
4 get_settings_manager
5 from boards.forms import LoginForm
6 from boards.views.base import BaseBoardView, CONTEXT_FORM
7
8
9 __author__ = 'neko259'
10
11
12 class LogoutView(BaseBoardView):
13
14 def get(self, request, form=None):
15 settings_manager = get_settings_manager(request)
16 settings_manager.del_permission(PERMISSION_MODERATE)
17
18 context = self.get_context_data(request=request)
19
20 if not form:
21 form = LoginForm()
22 context[CONTEXT_FORM] = form
23
24 return render(request, 'boards/login.html', context) No newline at end of file
@@ -1,5 +1,5 b''
1 1 from django.contrib import admin
2 from boards.models import Post, Tag, User, Ban, Thread
2 from boards.models import Post, Tag, Ban, Thread
3 3
4 4
5 5 class PostAdmin(admin.ModelAdmin):
@@ -11,15 +11,7 b' class PostAdmin(admin.ModelAdmin):'
11 11
12 12 class TagAdmin(admin.ModelAdmin):
13 13
14 list_display = ('name', 'linked')
15 list_filter = ('linked',)
16
17
18 class UserAdmin(admin.ModelAdmin):
19
20 list_display = ('user_id', 'rank')
21 search_fields = ('user_id',)
22
14 list_display = ('name',)
23 15
24 16 class ThreadAdmin(admin.ModelAdmin):
25 17
@@ -35,6 +27,5 b' class ThreadAdmin(admin.ModelAdmin):'
35 27
36 28 admin.site.register(Post, PostAdmin)
37 29 admin.site.register(Tag, TagAdmin)
38 admin.site.register(User, UserAdmin)
39 30 admin.site.register(Ban)
40 31 admin.site.register(Thread, ThreadAdmin)
@@ -1,8 +1,10 b''
1 from boards.abstracts.settingsmanager import PERMISSION_MODERATE, \
2 get_settings_manager
3
1 4 __author__ = 'neko259'
2 5
3 from boards import utils, settings
6 from boards import settings
4 7 from boards.models import Post
5 from boards.models.post import SETTING_MODERATE
6 8
7 9 CONTEXT_SITE_NAME = 'site_name'
8 10 CONTEXT_VERSION = 'version'
@@ -17,21 +19,17 b" CONTEXT_USER = 'user'"
17 19 def user_and_ui_processor(request):
18 20 context = {}
19 21
20 user = utils.get_user(request)
21 context[CONTEXT_USER] = user
22 context[CONTEXT_TAGS] = user.fav_tags.all()
23 22 context[CONTEXT_PPD] = float(Post.objects.get_posts_per_day())
24 23
25 theme = utils.get_theme(request, user)
24 settings_manager = get_settings_manager(request)
25 context[CONTEXT_TAGS] = settings_manager.get_fav_tags()
26 theme = settings_manager.get_theme()
26 27 context[CONTEXT_THEME] = theme
27 28 context[CONTEXT_THEME_CSS] = 'css/' + theme + '/base_page.css'
28 29
29 30 # This shows the moderator panel
30 moderate = user.get_setting(SETTING_MODERATE)
31 if moderate == 'True':
32 context[CONTEXT_MODERATOR] = user.is_moderator()
33 else:
34 context[CONTEXT_MODERATOR] = False
31 moderate = settings_manager.has_permission(PERMISSION_MODERATE)
32 context[CONTEXT_MODERATOR] = moderate
35 33
36 34 context[CONTEXT_VERSION] = settings.VERSION
37 35 context[CONTEXT_SITE_NAME] = settings.SITE_NAME
@@ -9,7 +9,7 b' from django.utils.translation import uge'
9 9
10 10 from boards.mdx_neboard import formatters
11 11 from boards.models.post import TITLE_MAX_LENGTH
12 from boards.models import User, PostImage
12 from boards.models import PostImage
13 13 from neboard import settings
14 14 from boards import utils
15 15 import boards.settings as board_settings
@@ -20,8 +20,7 b" ATTRIBUTE_PLACEHOLDER = 'placeholder'"
20 20
21 21 LAST_POST_TIME = 'last_post_time'
22 22 LAST_LOGIN_TIME = 'last_login_time'
23 TEXT_PLACEHOLDER = _('''Type message here. You can reply to message >>123 like
24 this. 2 new lines are required to start new paragraph.''')
23 TEXT_PLACEHOLDER = _('''Type message here. Use formatting panel for more advanced usage.''')
25 24 TAGS_PLACEHOLDER = _('tag1 several_words_tag')
26 25
27 26 ERROR_IMAGE_DUPLICATE = _('Such image was already posted')
@@ -187,10 +186,6 b' class PostForm(NeboardForm):'
187 186 if not 'user_id' in self.session:
188 187 return
189 188
190 user = User.objects.get(id=self.session['user_id'])
191 if user.is_veteran():
192 posting_delay = VETERAN_POSTING_DELAY
193 else:
194 189 posting_delay = settings.POSTING_DELAY
195 190
196 191 if board_settings.LIMIT_POSTING_SPEED and LAST_POST_TIME in \
@@ -282,57 +277,6 b' class SettingsForm(NeboardForm):'
282 277 label=_('Theme'))
283 278
284 279
285 class ModeratorSettingsForm(SettingsForm):
286
287 moderate = forms.BooleanField(required=False, label=_('Enable moderation '
288 'panel'))
289
290
291 class LoginForm(NeboardForm):
292
293 user_id = forms.CharField()
294
295 session = None
296
297 def clean_user_id(self):
298 user_id = self.cleaned_data['user_id']
299 if user_id:
300 users = User.objects.filter(user_id=user_id)
301 if len(users) == 0:
302 raise forms.ValidationError(_('No such user found'))
303
304 return user_id
305
306 def _validate_login_speed(self):
307 can_post = True
308
309 if LAST_LOGIN_TIME in self.session:
310 now = time.time()
311 last_login_time = self.session[LAST_LOGIN_TIME]
312
313 current_delay = int(now - last_login_time)
314
315 if current_delay < board_settings.LOGIN_TIMEOUT:
316 error_message = _('Wait %s minutes after last login') % str(
317 (board_settings.LOGIN_TIMEOUT - current_delay) / 60)
318 self._errors['user_id'] = self.error_class([error_message])
319
320 can_post = False
321
322 if can_post:
323 self.session[LAST_LOGIN_TIME] = time.time()
324
325 def clean(self):
326 if not self.session:
327 raise forms.ValidationError('Humans have sessions')
328
329 self._validate_login_speed()
330
331 cleaned_data = super(LoginForm, self).clean()
332
333 return cleaned_data
334
335
336 280 class AddTagForm(NeboardForm):
337 281
338 282 tag = forms.CharField(max_length=TAG_MAX_LENGTH, label=LABEL_TAG)
@@ -354,4 +298,44 b' class AddTagForm(NeboardForm):'
354 298
355 299
356 300 class SearchForm(NeboardForm):
357 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False) No newline at end of file
301 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False)
302
303
304 class LoginForm(NeboardForm):
305
306 password = forms.CharField()
307
308 session = None
309
310 def clean_password(self):
311 password = self.cleaned_data['password']
312 if board_settings.MASTER_PASSWORD != password:
313 raise forms.ValidationError(_('Invalid master password'))
314
315 return password
316
317 def _validate_login_speed(self):
318 can_post = True
319
320 if LAST_LOGIN_TIME in self.session:
321 now = time.time()
322 last_login_time = self.session[LAST_LOGIN_TIME]
323
324 current_delay = int(now - last_login_time)
325
326 if current_delay < board_settings.LOGIN_TIMEOUT:
327 error_message = _('Wait %s minutes after last login') % str(
328 (board_settings.LOGIN_TIMEOUT - current_delay) / 60)
329 self._errors['password'] = self.error_class([error_message])
330
331 can_post = False
332
333 if can_post:
334 self.session[LAST_LOGIN_TIME] = time.time()
335
336 def clean(self):
337 self._validate_login_speed()
338
339 cleaned_data = super(LoginForm, self).clean()
340
341 return cleaned_data
1 NO CONTENT: modified file, binary diff hidden
@@ -7,7 +7,7 b' msgid ""'
7 7 msgstr ""
8 8 "Project-Id-Version: PACKAGE VERSION\n"
9 9 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2014-06-29 13:46+0300\n"
10 "POT-Creation-Date: 2014-07-20 20:11+0300\n"
11 11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 13 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,56 +18,53 b' msgstr ""'
18 18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
19 19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
20 20
21 #: authors.py:5
21 #: authors.py:9
22 22 msgid "author"
23 23 msgstr "автор"
24 24
25 #: authors.py:6
25 #: authors.py:10
26 26 msgid "developer"
27 27 msgstr "разработчик"
28 28
29 #: authors.py:7
29 #: authors.py:11
30 30 msgid "javascript developer"
31 31 msgstr "разработчик javascript"
32 32
33 #: authors.py:8
33 #: authors.py:12
34 34 msgid "designer"
35 35 msgstr "дизайнер"
36 36
37 37 #: forms.py:23
38 msgid ""
39 "Type message here. You can reply to message >>123 like\n"
40 " this. 2 new lines are required to start new paragraph."
38 msgid "Type message here. Use formatting panel for more advanced usage."
41 39 msgstr ""
42 "Введите сообщение здесь. Вы можете ответить на сообщение >>123 вот так. 2 "
43 "переноса строки обязательны для создания нового абзаца."
40 "Вводите сообщение сюда. Используйте панель для более сложного форматирования."
44 41
45 #: forms.py:25
42 #: forms.py:24
46 43 msgid "tag1 several_words_tag"
47 44 msgstr "тег1 тег_из_нескольких_слов"
48 45
49 #: forms.py:27
46 #: forms.py:26
50 47 msgid "Such image was already posted"
51 48 msgstr "Такое изображение уже было загружено"
52 49
53 #: forms.py:29
50 #: forms.py:28
54 51 msgid "Title"
55 52 msgstr "Заголовок"
56 53
57 #: forms.py:30
54 #: forms.py:29
58 55 msgid "Text"
59 56 msgstr "Текст"
60 57
61 #: forms.py:31
58 #: forms.py:30
62 59 msgid "Tag"
63 60 msgstr "Тег"
64 61
65 #: forms.py:32 templates/boards/base.html:50 templates/search/search.html:9
62 #: forms.py:31 templates/boards/base.html:54 templates/search/search.html:9
66 63 #: templates/search/search.html.py:13
67 64 msgid "Search"
68 65 msgstr "Поиск"
69 66
70 #: forms.py:109
67 #: forms.py:108
71 68 msgid "Image"
72 69 msgstr "Изображение"
73 70
@@ -94,36 +91,32 b' msgstr "\xd0\x98\xd0\xb7\xd0\xbe\xd0\xb1\xd1\x80\xd0\xb0\xd0\xb6\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb5 \xd0\xb4\xd0\xbe\xd0\xbb\xd0\xb6\xd0\xbd\xd0\xbe \xd0\xb1\xd1\x8b\xd1\x82\xd1\x8c \xd0\xbc\xd0\xb5\xd0\xbd\xd0\xb5\xd0\xb5 %s \xd0\xb1\xd0\xb0\xd0\xb9\xd1\x82"'
94 91 msgid "Either text or image must be entered."
95 92 msgstr "Текст или картинка должны быть введены."
96 93
97 #: forms.py:202
94 #: forms.py:199
98 95 #, python-format
99 96 msgid "Wait %s seconds after last posting"
100 97 msgstr "Подождите %s секунд после последнего постинга"
101 98
102 #: forms.py:218 templates/boards/tags.html:7 templates/boards/rss/post.html:10
99 #: forms.py:215 templates/boards/tags.html:7 templates/boards/rss/post.html:10
103 100 msgid "Tags"
104 101 msgstr "Теги"
105 102
106 #: forms.py:225 forms.py:344
103 #: forms.py:222 forms.py:290
107 104 msgid "Inappropriate characters in tags."
108 105 msgstr "Недопустимые символы в тегах."
109 106
110 #: forms.py:253 forms.py:274
107 #: forms.py:250 forms.py:271
111 108 msgid "Captcha validation failed"
112 109 msgstr "Проверка капчи провалена"
113 110
114 #: forms.py:280
111 #: forms.py:277
115 112 msgid "Theme"
116 113 msgstr "Тема"
117 114
118 #: forms.py:285
119 msgid "Enable moderation panel"
120 msgstr "Включить панель модерации"
115 #: forms.py:313
116 msgid "Invalid master password"
117 msgstr "Неверный мастер-пароль"
121 118
122 #: forms.py:300
123 msgid "No such user found"
124 msgstr "Данный пользователь не найден"
125
126 #: forms.py:314
119 #: forms.py:327
127 120 #, python-format
128 121 msgid "Wait %s minutes after last login"
129 122 msgstr "Подождите %s минут после последнего входа"
@@ -168,17 +161,21 b' msgstr "\xd0\xa3\xd0\xbf\xd1\x80\xd0\xb0\xd0\xb2\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb5 \xd1\x82\xd0\xb5\xd0\xb3\xd0\xb0\xd0\xbc\xd0\xb8"'
168 161 msgid "Settings"
169 162 msgstr "Настройки"
170 163
171 #: templates/boards/base.html:49 templates/boards/login.html:6
164 #: templates/boards/base.html:50
165 msgid "Logout"
166 msgstr "Выход"
167
168 #: templates/boards/base.html:52 templates/boards/login.html:6
172 169 #: templates/boards/login.html.py:16
173 170 msgid "Login"
174 171 msgstr "Вход"
175 172
176 #: templates/boards/base.html:52
173 #: templates/boards/base.html:56
177 174 #, python-format
178 175 msgid "Speed: %(ppd)s posts per day"
179 176 msgstr "Скорость: %(ppd)s сообщений в день"
180 177
181 #: templates/boards/base.html:54
178 #: templates/boards/base.html:58
182 179 msgid "Up"
183 180 msgstr "Вверх"
184 181
@@ -186,7 +183,7 b' msgstr "\xd0\x92\xd0\xb2\xd0\xb5\xd1\x80\xd1\x85"'
186 183 msgid "Insert your user id above"
187 184 msgstr "Вставьте свой ID пользователя выше"
188 185
189 #: templates/boards/post.html:21 templates/boards/staticpages/help.html:19
186 #: templates/boards/post.html:21 templates/boards/staticpages/help.html:17
190 187 msgid "Quote"
191 188 msgstr "Цитата"
192 189
@@ -216,8 +213,8 b' msgstr "\xd0\x9e\xd1\x82\xd0\xb2\xd0\xb5\xd1\x82\xd1\x8b"'
216 213
217 214 #: templates/boards/post.html:86 templates/boards/thread.html:88
218 215 #: templates/boards/thread_gallery.html:61
219 msgid "replies"
220 msgstr "ответов"
216 msgid "messages"
217 msgstr "сообщений"
221 218
222 219 #: templates/boards/post.html:87 templates/boards/thread.html:89
223 220 #: templates/boards/thread_gallery.html:62
@@ -249,7 +246,7 b' msgstr "\xd0\x9f\xd1\x80\xd0\xb5\xd0\xb4\xd1\x8b\xd0\xb4\xd1\x83\xd1\x89\xd0\xb0\xd1\x8f \xd1\x81\xd1\x82\xd1\x80\xd0\xb0\xd0\xbd\xd0\xb8\xd1\x86\xd0\xb0"'
249 246 msgid "Skipped %(count)s replies. Open thread to see all replies."
250 247 msgstr "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы."
251 248
252 #: templates/boards/posting_general.html:121 templates/search/search.html:35
249 #: templates/boards/posting_general.html:121 templates/search/search.html:33
253 250 msgid "Next page"
254 251 msgstr "Следующая страница"
255 252
@@ -278,35 +275,19 b' msgstr "\xd0\xa1\xd0\xb8\xd0\xbd\xd1\x82\xd0\xb0\xd0\xba\xd1\x81\xd0\xb8\xd1\x81 \xd1\x82\xd0\xb5\xd0\xba\xd1\x81\xd1\x82\xd0\xb0"'
278 275 msgid "Pages:"
279 276 msgstr "Страницы: "
280 277
281 #: templates/boards/settings.html:14
282 msgid "User:"
283 msgstr "Пользователь:"
284
285 #: templates/boards/settings.html:16
278 #: templates/boards/settings.html:15
286 279 msgid "You are moderator."
287 280 msgstr "Вы модератор."
288 281
289 282 #: templates/boards/settings.html:19
290 msgid "You are veteran."
291 msgstr "Вы ветеран."
292
293 #: templates/boards/settings.html:22
294 msgid "Posts:"
295 msgstr "Сообщений:"
296
297 #: templates/boards/settings.html:23
298 msgid "First access:"
299 msgstr "Первый доступ:"
300
301 #: templates/boards/settings.html:25
302 msgid "Last access:"
303 msgstr "Последний доступ: "
304
305 #: templates/boards/settings.html:29
306 283 msgid "Hidden tags:"
307 284 msgstr "Скрытые теги:"
308 285
309 #: templates/boards/settings.html:44
286 #: templates/boards/settings.html:26
287 msgid "No hidden tags."
288 msgstr "Нет скрытых тегов."
289
290 #: templates/boards/settings.html:35
310 291 msgid "Save"
311 292 msgstr "Сохранить"
312 293
@@ -356,37 +337,25 b' msgid "Syntax"'
356 337 msgstr "Синтаксис"
357 338
358 339 #: templates/boards/staticpages/help.html:11
359 msgid "2 line breaks for a new line."
360 msgstr "2 перевода строки создают новый абзац."
361
362 #: templates/boards/staticpages/help.html:12
363 340 msgid "Italic text"
364 341 msgstr "Курсивный текст"
365 342
366 #: templates/boards/staticpages/help.html:13
343 #: templates/boards/staticpages/help.html:12
367 344 msgid "Bold text"
368 345 msgstr "Полужирный текст"
369 346
370 #: templates/boards/staticpages/help.html:14
347 #: templates/boards/staticpages/help.html:13
371 348 msgid "Spoiler"
372 349 msgstr "Спойлер"
373 350
374 #: templates/boards/staticpages/help.html:15
351 #: templates/boards/staticpages/help.html:14
375 352 msgid "Link to a post"
376 353 msgstr "Ссылка на сообщение"
377 354
378 #: templates/boards/staticpages/help.html:16
355 #: templates/boards/staticpages/help.html:15
379 356 msgid "Strikethrough text"
380 357 msgstr "Зачеркнутый текст"
381 358
382 #: templates/boards/staticpages/help.html:17
383 msgid "You need to new line before:"
384 msgstr "Перед этими тегами нужна новая строка:"
385
386 #: templates/boards/staticpages/help.html:18
359 #: templates/boards/staticpages/help.html:16
387 360 msgid "Comment"
388 361 msgstr "Комментарий"
389
390 #: templates/search/search.html:30
391 msgid "No results found."
392 msgstr "Результаты не найдены."
@@ -13,4 +13,4 b' class Command(BaseCommand):'
13 13
14 14 @transaction.atomic
15 15 def handle(self, *args, **options):
16 Post.objects.all().update(poster_ip=NO_IP, user=None) No newline at end of file
16 Post.objects.all().update(poster_ip=NO_IP) No newline at end of file
@@ -1,8 +1,7 b''
1 1 # coding=utf-8
2 2
3 import markdown
4 from markdown.inlinepatterns import Pattern
5 from markdown.util import etree
3 import re
4 import bbcode
6 5
7 6 import boards
8 7
@@ -10,13 +9,7 b' import boards'
10 9 __author__ = 'neko259'
11 10
12 11
13 AUTOLINK_PATTERN = r'(https?://\S+)'
14 QUOTE_PATTERN = r'^(?<!>)(>[^>].*)$'
15 REFLINK_PATTERN = r'((>>)(\d+))'
16 SPOILER_PATTERN = r'%%([^(%%)]+)%%'
17 COMMENT_PATTERN = r'^(//(.+))'
18 STRIKETHROUGH_PATTERN = r'~(.+)~'
19 DASH_PATTERN = r'--'
12 REFLINK_PATTERN = re.compile(r'\d+')
20 13
21 14
22 15 class TextFormatter():
@@ -38,7 +31,7 b' class TextFormatter():'
38 31 format_right = ''
39 32
40 33
41 class AutolinkPattern(Pattern):
34 class AutolinkPattern():
42 35 def handleMatch(self, m):
43 36 link_element = etree.Element('a')
44 37 href = m.group(2)
@@ -48,44 +41,22 b' class AutolinkPattern(Pattern):'
48 41 return link_element
49 42
50 43
51 class QuotePattern(Pattern, TextFormatter):
52 name = ''
53 preview_left = '<span class="quote">&gt; '
44 class QuotePattern(TextFormatter):
45 name = 'q'
46 preview_left = '<span class="multiquote">'
54 47 preview_right = '</span>'
55 48
56 format_left = '&gt;'
57
58 def handleMatch(self, m):
59 quote_element = etree.Element('span')
60 quote_element.set('class', 'quote')
61 quote_element.text = m.group(2)
62
63 return quote_element
49 format_left = '[quote]'
50 format_right = '[/quote]'
64 51
65 52
66 class ReflinkPattern(Pattern):
67 def handleMatch(self, m):
68 post_id = m.group(4)
69
70 posts = boards.models.Post.objects.filter(id=post_id)
71 if posts.count() > 0:
72 ref_element = etree.Element('a')
73
74 post = posts[0]
75
76 ref_element.set('href', post.get_url())
77 ref_element.text = m.group(2)
78
79 return ref_element
80
81
82 class SpoilerPattern(Pattern, TextFormatter):
83 name = 's'
53 class SpoilerPattern(TextFormatter):
54 name = 'spoiler'
84 55 preview_left = '<span class="spoiler">'
85 56 preview_right = '</span>'
86 57
87 format_left = '%%'
88 format_right = '%%'
58 format_left = '[spoiler]'
59 format_right = '[/spoiler]'
89 60
90 61 def handleMatch(self, m):
91 62 quote_element = etree.Element('span')
@@ -95,35 +66,22 b' class SpoilerPattern(Pattern, TextFormat'
95 66 return quote_element
96 67
97 68
98 class CommentPattern(Pattern, TextFormatter):
69 class CommentPattern(TextFormatter):
99 70 name = ''
100 71 preview_left = '<span class="comment">// '
101 72 preview_right = '</span>'
102 73
103 format_left = '//'
104
105 def handleMatch(self, m):
106 quote_element = etree.Element('span')
107 quote_element.set('class', 'comment')
108 quote_element.text = '//' + m.group(3)
109
110 return quote_element
74 format_left = '[comment]'
75 format_right = '[/comment]'
111 76
112 77
113 class StrikeThroughPattern(Pattern, TextFormatter):
78 class StrikeThroughPattern(TextFormatter):
114 79 name = 's'
115 80 preview_left = '<span class="strikethrough">'
116 81 preview_right = '</span>'
117 82
118 format_left = '~'
119 format_right = '~'
120
121 def handleMatch(self, m):
122 quote_element = etree.Element('span')
123 quote_element.set('class', 'strikethrough')
124 quote_element.text = m.group(2)
125
126 return quote_element
83 format_left = '[s]'
84 format_right = '[/s]'
127 85
128 86
129 87 class ItalicPattern(TextFormatter):
@@ -131,8 +89,8 b' class ItalicPattern(TextFormatter):'
131 89 preview_left = '<i>'
132 90 preview_right = '</i>'
133 91
134 format_left = '_'
135 format_right = '_'
92 format_left = '[i]'
93 format_right = '[/i]'
136 94
137 95
138 96 class BoldPattern(TextFormatter):
@@ -140,8 +98,8 b' class BoldPattern(TextFormatter):'
140 98 preview_left = '<b>'
141 99 preview_right = '</b>'
142 100
143 format_left = '__'
144 format_right = '__'
101 format_left = '[b]'
102 format_right = '[/b]'
145 103
146 104
147 105 class CodePattern(TextFormatter):
@@ -149,52 +107,39 b' class CodePattern(TextFormatter):'
149 107 preview_left = '<code>'
150 108 preview_right = '</code>'
151 109
152 format_left = ' '
153
154
155 class DashPattern(Pattern):
156 def handleMatch(self, m):
157 return u'—'
110 format_left = '[code]'
111 format_right = '[/code]'
158 112
159 113
160 class NeboardMarkdown(markdown.Extension):
161 def extendMarkdown(self, md, md_globals):
162 self._add_neboard_patterns(md)
163 self._delete_patterns(md)
114 def render_reflink(tag_name, value, options, parent, context):
115 if not REFLINK_PATTERN.match(value):
116 return u'>>%s' % value
164 117
165 def _delete_patterns(self, md):
166 del md.parser.blockprocessors['quote']
167
168 del md.inlinePatterns['image_link']
169 del md.inlinePatterns['image_reference']
118 post_id = int(value)
170 119
171 def _add_neboard_patterns(self, md):
172 autolink = AutolinkPattern(AUTOLINK_PATTERN, md)
173 quote = QuotePattern(QUOTE_PATTERN, md)
174 reflink = ReflinkPattern(REFLINK_PATTERN, md)
175 spoiler = SpoilerPattern(SPOILER_PATTERN, md)
176 comment = CommentPattern(COMMENT_PATTERN, md)
177 strikethrough = StrikeThroughPattern(STRIKETHROUGH_PATTERN, md)
178 dash = DashPattern(DASH_PATTERN, md)
120 posts = boards.models.Post.objects.filter(id=post_id)
121 if posts.exists():
122 post = posts[0]
179 123
180 md.inlinePatterns[u'autolink_ext'] = autolink
181 md.inlinePatterns[u'spoiler'] = spoiler
182 md.inlinePatterns[u'strikethrough'] = strikethrough
183 md.inlinePatterns[u'comment'] = comment
184 md.inlinePatterns[u'reflink'] = reflink
185 md.inlinePatterns[u'quote'] = quote
186 md.inlinePatterns[u'dash'] = dash
124 return u'<a href=%s>&gt;&gt;%s</a>' % (post.get_url(), post_id)
125 else:
126 return u'>>%s' % value
187 127
188 128
189 def make_extension(configs=None):
190 return NeboardMarkdown(configs=configs)
191
192 neboard_extension = make_extension()
193
194
195 def markdown_extended(markup):
196 return markdown.markdown(markup, [neboard_extension, 'nl2br'],
197 safe_mode='escape')
129 def bbcode_extended(markup):
130 parser = bbcode.Parser(newline='</p><p>')
131 parser.add_formatter('post', render_reflink, strip=True)
132 parser.add_simple_formatter('quote',
133 u'<span class="multiquote">%(value)s</span>')
134 parser.add_simple_formatter('comment',
135 u'<span class="comment">//%(value)s</span>')
136 parser.add_simple_formatter('spoiler',
137 u'<span class="spoiler">%(value)s</span>')
138 parser.add_simple_formatter('s',
139 u'<span class="strikethrough">%(value)s</span>')
140 parser.add_simple_formatter('code',
141 u'<pre><code>%(value)s</pre></code>')
142 return '<p>%s</p>' % parser.format(markup)
198 143
199 144 formatters = [
200 145 QuotePattern,
@@ -5,5 +5,3 b' from boards.models.thread import Thread'
5 5 from boards.models.post import Post
6 6 from boards.models.tag import Tag
7 7 from boards.models.user import Ban
8 from boards.models.user import Setting
9 from boards.models.user import User
@@ -20,7 +20,7 b" APP_LABEL_BOARDS = 'boards'"
20 20 CACHE_KEY_PPD = 'ppd'
21 21 CACHE_KEY_POST_URL = 'post_url'
22 22
23 POSTS_PER_DAY_RANGE = range(7)
23 POSTS_PER_DAY_RANGE = 7
24 24
25 25 BAN_REASON_AUTO = 'Auto'
26 26
@@ -28,7 +28,7 b' IMAGE_THUMB_SIZE = (200, 150)'
28 28
29 29 TITLE_MAX_LENGTH = 200
30 30
31 DEFAULT_MARKUP_TYPE = 'markdown'
31 DEFAULT_MARKUP_TYPE = 'bbcode'
32 32
33 33 # TODO This should be removed
34 34 NO_IP = '0.0.0.0'
@@ -36,17 +36,14 b" NO_IP = '0.0.0.0'"
36 36 # TODO Real user agent should be saved instead of this
37 37 UNKNOWN_UA = ''
38 38
39 SETTING_MODERATE = "moderate"
40
41 REGEX_REPLY = re.compile('>>(\d+)')
39 REGEX_REPLY = re.compile(r'&gt;&gt;(\d+)')
42 40
43 41 logger = logging.getLogger(__name__)
44 42
45 43
46 44 class PostManager(models.Manager):
47
48 def create_post(self, title, text, image=None, thread=None,
49 ip=NO_IP, tags=None, user=None):
45 def create_post(self, title, text, image=None, thread=None, ip=NO_IP,
46 tags=None):
50 47 """
51 48 Creates new post
52 49 """
@@ -69,8 +66,7 b' class PostManager(models.Manager):'
69 66 poster_ip=ip,
70 67 poster_user_agent=UNKNOWN_UA, # TODO Get UA at
71 68 # last!
72 last_edit_time=posting_time,
73 user=user)
69 last_edit_time=posting_time)
74 70
75 71 if image:
76 72 post_image = PostImage.objects.create(image=image)
@@ -80,20 +76,14 b' class PostManager(models.Manager):'
80 76
81 77 thread.replies.add(post)
82 78 if tags:
83 linked_tags = []
84 for tag in tags:
85 tag_linked_tags = tag.get_linked_tags()
86 if len(tag_linked_tags) > 0:
87 linked_tags.extend(tag_linked_tags)
88
89 tags.extend(linked_tags)
90 79 map(thread.add_tag, tags)
91 80
92 81 if new_thread:
93 82 Thread.objects.process_oldest_threads()
94 83 self.connect_replies(post)
95 84
96 logger.info('Created post #%d' % post.id)
85 logger.info('Created post #%d with title %s' % (post.id,
86 post.get_title()))
97 87
98 88 return post
99 89
@@ -114,7 +104,7 b' class PostManager(models.Manager):'
114 104
115 105 post.delete()
116 106
117 logger.info('Deleted post #%d' % post_id)
107 logger.info('Deleted post #%d (%s)' % (post_id, post.get_title()))
118 108
119 109 def delete_posts_by_ip(self, ip):
120 110 """
@@ -129,7 +119,7 b' class PostManager(models.Manager):'
129 119 Connects replies to a post to show them as a reflink map
130 120 """
131 121
132 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
122 for reply_number in re.finditer(REGEX_REPLY, post.text.rendered):
133 123 post_id = reply_number.group(1)
134 124 ref_post = self.filter(id=post_id)
135 125 if ref_post.count() > 0:
@@ -148,28 +138,26 b' class PostManager(models.Manager):'
148 138 Gets average count of posts per day for the last 7 days
149 139 """
150 140
151 today = date.today()
152 ppd = cache.get(CACHE_KEY_PPD + str(today))
141 day_end = date.today()
142 day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
143
144 cache_key = CACHE_KEY_PPD + str(day_end)
145 ppd = cache.get(cache_key)
153 146 if ppd:
154 147 return ppd
155 148
156 posts_per_days = []
157 for i in POSTS_PER_DAY_RANGE:
158 day_end = today - timedelta(i + 1)
159 day_start = today - timedelta(i + 2)
160
161 149 day_time_start = timezone.make_aware(datetime.combine(
162 150 day_start, dtime()), timezone.get_current_timezone())
163 151 day_time_end = timezone.make_aware(datetime.combine(
164 152 day_end, dtime()), timezone.get_current_timezone())
165 153
166 posts_per_days.append(float(self.filter(
154 posts_per_period = float(self.filter(
167 155 pub_time__lte=day_time_end,
168 pub_time__gte=day_time_start).count()))
156 pub_time__gte=day_time_start).count())
169 157
170 ppd = (sum(posts_per_day for posts_per_day in posts_per_days) /
171 len(posts_per_days))
172 cache.set(CACHE_KEY_PPD + str(today), ppd)
158 ppd = posts_per_period / POSTS_PER_DAY_RANGE
159
160 cache.set(cache_key, ppd)
173 161 return ppd
174 162
175 163
@@ -196,7 +184,6 b' class Post(models.Model, Viewable):'
196 184 thread_new = models.ForeignKey('Thread', null=True, default=None,
197 185 db_index=True)
198 186 last_edit_time = models.DateTimeField()
199 user = models.ForeignKey('User', null=True, default=None, db_index=True)
200 187
201 188 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
202 189 null=True,
@@ -220,13 +207,18 b' class Post(models.Model, Viewable):'
220 207 return title
221 208
222 209 def build_refmap(self):
210 """
211 Builds a replies map string from replies list. This is a cache to stop
212 the server from recalculating the map on every post show.
213 """
223 214 map_string = ''
224 215
225 216 first = True
226 217 for refpost in self.referenced_posts.all():
227 218 if not first:
228 219 map_string += ', '
229 map_string += '<a href="%s">&gt;&gt;%s</a>' % (refpost.get_url(), refpost.id)
220 map_string += '<a href="%s">&gt;&gt;%s</a>' % (refpost.get_url(),
221 refpost.id)
230 222 first = False
231 223
232 224 self.refmap = map_string
@@ -251,10 +243,10 b' class Post(models.Model, Viewable):'
251 243 thread = self.get_thread()
252 244 thread.add_tag(tag)
253 245 self.last_edit_time = edit_time
254 self.save()
246 self.save(update_fields=['last_edit_time'])
255 247
256 248 thread.last_edit_time = edit_time
257 thread.save()
249 thread.save(update_fields=['last_edit_time'])
258 250
259 251 @transaction.atomic
260 252 def remove_tag(self, tag):
@@ -263,10 +255,10 b' class Post(models.Model, Viewable):'
263 255 thread = self.get_thread()
264 256 thread.remove_tag(tag)
265 257 self.last_edit_time = edit_time
266 self.save()
258 self.save(update_fields=['last_edit_time'])
267 259
268 260 thread.last_edit_time = edit_time
269 thread.save()
261 thread.save(update_fields=['last_edit_time'])
270 262
271 263 def get_url(self, thread=None):
272 264 """
@@ -343,9 +335,9 b' class Post(models.Model, Viewable):'
343 335
344 336 def delete(self, using=None):
345 337 """
346 Delete all post images and the post itself.
338 Deletes all post images and the post itself.
347 339 """
348 340
349 341 self.images.all().delete()
350 342
351 super(Post, self).delete(using) No newline at end of file
343 super(Post, self).delete(using)
@@ -1,10 +1,12 b''
1 1 from django.template.loader import render_to_string
2 from boards.models import Thread, Post
3 2 from django.db import models
4 3 from django.db.models import Count, Sum
5 4 from django.core.urlresolvers import reverse
5
6 from boards.models import Thread
6 7 from boards.models.base import Viewable
7 8
9
8 10 __author__ = 'neko259'
9 11
10 12
@@ -36,7 +38,6 b' class Tag(models.Model, Viewable):'
36 38 name = models.CharField(max_length=100, db_index=True)
37 39 threads = models.ManyToManyField(Thread, null=True,
38 40 blank=True, related_name='tag+')
39 linked = models.ForeignKey('Tag', null=True, blank=True)
40 41
41 42 def __unicode__(self):
42 43 return self.name
@@ -51,29 +52,6 b' class Tag(models.Model, Viewable):'
51 52 def get_thread_count(self):
52 53 return self.threads.count()
53 54
54 def get_linked_tags(self):
55 """
56 Gets tags linked to the current one.
57 """
58
59 tag_list = []
60 self.get_linked_tags_list(tag_list)
61
62 return tag_list
63
64 def get_linked_tags_list(self, tag_list=[]):
65 """
66 Returns the list of tags linked to current. The list can be got
67 through returned value or tag_list parameter
68 """
69
70 linked_tag = self.linked
71
72 if linked_tag and not (linked_tag in tag_list):
73 tag_list.append(linked_tag)
74
75 linked_tag.get_linked_tags_list(tag_list)
76
77 55 def get_post_count(self, archived=False):
78 56 """
79 57 Gets posts count for the tag's threads.
@@ -1,122 +1,10 b''
1 1 from django.db import models
2 from django.db.models import Count
3 from boards import settings
4 from boards.models import Post
5 from django.core.cache import cache
6 2
7 3 __author__ = 'neko259'
8 4
9 RANK_ADMIN = 0
10 RANK_MODERATOR = 10
11 RANK_USER = 100
12
13 5 BAN_REASON_AUTO = 'Auto'
14 6 BAN_REASON_MAX_LENGTH = 200
15 7
16 VETERAN_POSTS = 1000
17
18
19 class User(models.Model):
20
21 class Meta:
22 app_label = 'boards'
23
24 user_id = models.CharField(max_length=50)
25 rank = models.IntegerField()
26
27 registration_time = models.DateTimeField()
28
29 fav_tags = models.ManyToManyField('Tag', null=True, blank=True)
30 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
31 blank=True)
32
33 hidden_tags = models.ManyToManyField('Tag', null=True, blank=True,
34 related_name='ht+')
35 hidden_threads = models.ManyToManyField('Post', null=True, blank=True,
36 related_name='hth+')
37
38 def save_setting(self, name, value):
39 setting, created = Setting.objects.get_or_create(name=name, user=self)
40 setting.value = str(value)
41 setting.save()
42
43 return setting
44
45 def get_setting(self, name):
46 if Setting.objects.filter(name=name, user=self).exists():
47 setting = Setting.objects.get(name=name, user=self)
48 setting_value = setting.value
49 else:
50 setting_value = None
51
52 return setting_value
53
54 def is_moderator(self):
55 return RANK_MODERATOR >= self.rank
56
57 def get_sorted_fav_tags(self):
58 cache_key = self._get_tag_cache_key()
59 fav_tags = cache.get(cache_key)
60 if fav_tags:
61 return fav_tags
62
63 tags = self.fav_tags.annotate(Count('threads')) \
64 .filter(threads__count__gt=0).order_by('name')
65
66 if tags:
67 cache.set(cache_key, tags)
68
69 return tags
70
71 def get_post_count(self):
72 return Post.objects.filter(user=self).count()
73
74 def __unicode__(self):
75 return self.user_id + '(' + str(self.rank) + ')'
76
77 def get_last_access_time(self):
78 """
79 Gets user's last post time.
80 """
81
82 posts = Post.objects.filter(user=self)
83 if posts.exists() > 0:
84 return posts.latest('pub_time').pub_time
85
86 def add_tag(self, tag):
87 self.fav_tags.add(tag)
88 cache.delete(self._get_tag_cache_key())
89
90 def remove_tag(self, tag):
91 self.fav_tags.remove(tag)
92 cache.delete(self._get_tag_cache_key())
93
94 def hide_tag(self, tag):
95 self.hidden_tags.add(tag)
96
97 def unhide_tag(self, tag):
98 self.hidden_tags.remove(tag)
99
100 def is_veteran(self):
101 """
102 Returns if a user is old (veteran).
103 """
104
105 return self.get_post_count() >= VETERAN_POSTS
106
107 def _get_tag_cache_key(self):
108 return self.user_id + '_tags'
109
110
111 class Setting(models.Model):
112
113 class Meta:
114 app_label = 'boards'
115
116 name = models.CharField(max_length=50)
117 value = models.CharField(max_length=50)
118 user = models.ForeignKey(User)
119
120 8
121 9 class Ban(models.Model):
122 10
@@ -17,4 +17,7 b' LAST_REPLIES_COUNT = 3'
17 17 # Enable archiving threads instead of deletion when the thread limit is reached
18 18 ARCHIVE_THREADS = True
19 19 # Limit posting speed
20 LIMIT_POSTING_SPEED = False No newline at end of file
20 LIMIT_POSTING_SPEED = False
21
22 # This password is used to add admin permissions to the user
23 MASTER_PASSWORD = u'password' No newline at end of file
@@ -217,6 +217,16 b' blockquote {'
217 217 font-style: italic;
218 218 }
219 219
220 .multiquote {
221 border-left: solid 4px #ccc;
222 padding: 3px;
223 display: inline-block;
224 background: #222;
225 border-right: solid 1px #ccc;
226 border-top: solid 1px #ccc;
227 border-bottom: solid 1px #ccc;
228 }
229
220 230 .spoiler {
221 231 background: white;
222 232 color: white;
@@ -35,10 +35,10 b' function moveCaretToEnd(el) {'
35 35 }
36 36
37 37 function addQuickReply(postId) {
38 var textToAdd = '>>' + postId + '\n\n';
38 var textToAdd = '[post]' + postId + '[/post]\n';
39 39 var selection = window.getSelection().toString();
40 40 if (selection.length > 0) {
41 textToAdd += '> ' + selection + '\n\n';
41 textToAdd += '[quote]' + selection + '[/quote]\n';
42 42 }
43 43
44 44 var textAreaId = 'textarea';
@@ -46,7 +46,11 b''
46 46
47 47 <div class="navigation_panel">
48 48 {% block metapanel %}{% endblock %}
49 {% if moderator %}
50 [<a href="{% url "logout" %}">{% trans 'Logout' %}</a>]
51 {% else %}
49 52 [<a href="{% url "login" %}">{% trans 'Login' %}</a>]
53 {% endif %}
50 54 [<a href="{% url "search" %}">{% trans 'Search' %}</a>]
51 55 {% with ppd=posts_per_day|floatformat:2 %}
52 56 {% blocktrans %}Speed: {{ ppd }} posts per day{% endblocktrans %}
@@ -83,7 +83,7 b''
83 83 {% cache 600 post_thread thread.id thread.last_edit_time LANGUAGE_CODE need_open_link %}
84 84 <div class="metadata">
85 85 {% if is_opening and need_open_link %}
86 {{ thread.get_reply_count }} {% trans 'replies' %},
86 {{ thread.get_reply_count }} {% trans 'messages' %},
87 87 {{ thread.get_images_count }} {% trans 'images' %}.
88 88 {% endif %}
89 89 <span class="tags">
@@ -44,14 +44,14 b''
44 44 {% if tag %}
45 45 <div class="tag_info">
46 46 <h2>
47 {% if tag in user.fav_tags.all %}
47 {% if tag in fav_tags %}
48 48 <a href="{% url 'tag' tag.name %}?method=unsubscribe&next={{ request.path }}"
49 49 class="fav"></a>
50 50 {% else %}
51 51 <a href="{% url 'tag' tag.name %}?method=subscribe&next={{ request.path }}"
52 52 class="not_fav"></a>
53 53 {% endif %}
54 {% if tag in user.hidden_tags.all %}
54 {% if tag in hidden_tags %}
55 55 <a href="{% url 'tag' tag.name %}?method=unhide&next={{ request.path }}"
56 56 title="{% trans 'Show tag' %}"
57 57 class="fav">H</a>
@@ -11,20 +11,10 b''
11 11
12 12 <div class="post">
13 13 <p>
14 {% trans 'User:' %} <b>{{ user.user_id }}</b>.
15 {% if user.is_moderator %}
14 {% if moderator %}
16 15 {% trans 'You are moderator.' %}
17 16 {% endif %}
18 {% if user.is_veteran %}
19 {% trans 'You are veteran.' %}
20 {% endif %}
21 17 </p>
22 <p>{% trans 'Posts:' %} {{ user.get_post_count }}</p>
23 <p>{% trans 'First access:' %} {{ user.registration_time|naturaltime }}</p>
24 {% if user.get_last_access_time %}
25 <p>{% trans 'Last access:' %} {{ user.get_last_access_time|naturaltime }}</p>
26 {% endif %}
27 {% with hidden_tags=user.hidden_tags.all %}
28 18 {% if hidden_tags %}
29 19 <p>{% trans 'Hidden tags:' %}
30 20 {% for tag in hidden_tags %}
@@ -32,8 +22,9 b''
32 22 #{{ tag.name }}</a>{% if not forloop.last %},{% endif %}
33 23 {% endfor %}
34 24 </p>
25 {% else %}
26 <p>{% trans 'No hidden tags.' %}</p>
35 27 {% endif %}
36 {% endwith %}
37 28 </div>
38 29
39 30 <div class="post-form-w">
@@ -8,13 +8,11 b''
8 8
9 9 {% block staticcontent %}
10 10 <h2>{% trans 'Syntax' %}</h2>
11 <p>{% trans '2 line breaks for a new line.' %}</p>
12 <p>_<i>{% trans 'Italic text' %}</i>_</p>
13 <p>__<b>{% trans 'Bold text' %}</b>__</p>
14 <p>%%<span class="spoiler">{% trans 'Spoiler' %}</span>%%</p>
15 <p><a>>>123</a> -- {% trans 'Link to a post' %}</p>
16 <p>~<span class="strikethrough">{% trans 'Strikethrough text' %}</span>~</p>
17 <p>{% trans 'You need to new line before:' %}</p>
18 <p><span class="comment">//{% trans 'Comment' %}</span></p>
19 <p><span class="quote">> {% trans 'Quote' %}</span></p>
11 <p>[i]<i>{% trans 'Italic text' %}</i>[/i]</p>
12 <p>[b]<b>{% trans 'Bold text' %}</b>[/b]</p>
13 <p>[spoiler]<span class="spoiler">{% trans 'Spoiler' %}</span>[/spoiler]</p>
14 <p>[post]123[/post] -- {% trans 'Link to a post' %}</p>
15 <p>[s]<span class="strikethrough">{% trans 'Strikethrough text' %}</span>[/s]</p>
16 <p>[comment]<span class="comment">{% trans 'Comment' %}</span>[/comment]</p>
17 <p>[quote]<span class="multiquote">{% trans 'Quote' %}</span>[/quote]</p>
20 18 {% endblock %}
@@ -85,7 +85,7 b''
85 85
86 86 <span class="metapanel" data-last-update="{{ last_update }}">
87 87 {% cache 600 thread_meta thread.last_edit_time moderator LANGUAGE_CODE %}
88 <span id="reply-count">{{ thread.get_reply_count }}</span>/{{ max_replies }} {% trans 'replies' %},
88 <span id="reply-count">{{ thread.get_reply_count }}</span>/{{ max_replies }} {% trans 'messages' %},
89 89 <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}.
90 90 {% trans 'Last update: ' %}<span id="last-update">{{ thread.last_edit_time }}</span>
91 91 [<a href="rss/">RSS</a>]
@@ -58,7 +58,7 b''
58 58 <span class="metapanel" data-last-update="{{ last_update }}">
59 59 {% cache 600 thread_meta thread.last_edit_time moderator LANGUAGE_CODE %}
60 60 <span id="reply-count">{{ thread.get_reply_count }}</span>/{{ max_replies }}
61 {% trans 'replies' %},
61 {% trans 'messages' %},
62 62 <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}.
63 63 {% trans 'Last update: ' %}{{ thread.last_edit_time }}
64 64 [<a href="rss/">RSS</a>]
@@ -6,12 +6,15 b' from django.core.paginator import Pagina'
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 from boards.abstracts.settingsmanager import get_settings_manager
9 10
10 11 from boards.models import Post, Tag, Thread
11 12 from boards import urls
12 13 from boards import settings
13 14 import neboard
14 15
16 TEST_TAG = 'test_tag'
17
15 18 PAGE_404 = 'boards/404.html'
16 19
17 20 TEST_TEXT = 'test text'
@@ -30,15 +33,18 b' logger = logging.getLogger(__name__)'
30 33 class PostTests(TestCase):
31 34
32 35 def _create_post(self):
33 return Post.objects.create_post(title='title',
34 text='text')
36 tag = Tag.objects.create(name=TEST_TAG)
37 return Post.objects.create_post(title='title', text='text',
38 tags=[tag])
35 39
36 40 def test_post_add(self):
37 41 """Test adding post"""
38 42
39 43 post = self._create_post()
40 44
41 self.assertIsNotNone(post, 'No post was created')
45 self.assertIsNotNone(post, 'No post was created.')
46 self.assertEqual(TEST_TAG, post.get_thread().tags.all()[0].name,
47 'No tags were added to the post.')
42 48
43 49 def test_delete_post(self):
44 50 """Test post deletion"""
@@ -131,17 +137,6 b' class PostTests(TestCase):'
131 137 self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id,
132 138 first_post.id)
133 139
134 def test_linked_tag(self):
135 """Test adding a linked tag"""
136
137 linked_tag = Tag.objects.create(name=u'tag1')
138 tag = Tag.objects.create(name=u'tag2', linked=linked_tag)
139
140 post = Post.objects.create_post("", "", tags=[tag])
141
142 self.assertTrue(linked_tag in post.get_thread().tags.all(),
143 'Linked tag was not added')
144
145 140
146 141 class PagesTest(TestCase):
147 142
@@ -234,10 +229,10 b' class FormTest(TestCase):'
234 229 class ViewTest(TestCase):
235 230
236 231 def test_all_views(self):
237 '''
232 """
238 233 Try opening all views defined in ulrs.py that don't need additional
239 234 parameters
240 '''
235 """
241 236
242 237 client = Client()
243 238 for url in urls.urlpatterns:
@@ -258,3 +253,18 b' class ViewTest(TestCase):'
258 253 except AttributeError:
259 254 # This is normal, some views do not have names
260 255 pass
256
257
258 class AbstractTest(TestCase):
259 def test_settings_manager(self):
260 request = MockRequest()
261 settings_manager = get_settings_manager(request)
262
263 settings_manager.set_setting('test_setting', 'test_value')
264 self.assertEqual('test_value', settings_manager.get_setting(
265 'test_setting'), u'Setting update failed.')
266
267
268 class MockRequest:
269 def __init__(self):
270 self.session = dict()
@@ -2,7 +2,7 b' from django.conf.urls import patterns, u'
2 2 from boards import views
3 3 from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed
4 4 from boards.views import api, tag_threads, all_threads, \
5 login, settings, all_tags
5 login, settings, all_tags, logout
6 6 from boards.views.authors import AuthorsView
7 7 from boards.views.delete_post import DeletePostView
8 8 from boards.views.ban import BanUserView
@@ -24,6 +24,7 b" urlpatterns = patterns('',"
24 24
25 25 # login page
26 26 url(r'^login/$', login.LoginView.as_view(), name='login'),
27 url(r'^logout/$', logout.LogoutView.as_view(), name='logout'),
27 28
28 29 # /boards/tag/tag_name/
29 30 url(r'^tag/(?P<tag_name>\w+)/$', tag_threads.TagView.as_view(),
@@ -5,11 +5,8 b' import hashlib'
5 5 import time
6 6
7 7 from django.utils import timezone
8 import boards
9 8
10 9 from neboard import settings
11 from boards.models import User
12 from boards.models.user import RANK_USER
13 10
14 11
15 12 KEY_CAPTCHA_FAILS = 'key_captcha_fails'
@@ -82,48 +79,4 b' def get_client_ip(request):'
82 79 def datetime_to_epoch(datetime):
83 80 return int(time.mktime(timezone.localtime(
84 81 datetime,timezone.get_current_timezone()).timetuple())
85 * 1000000 + datetime.microsecond)
86
87
88 def get_user(request):
89 """
90 Get current user from the session. If the user does not exist, create
91 a new one.
92 """
93
94 session = request.session
95 if not 'user_id' in session:
96 request.session.save()
97
98 md5 = hashlib.md5()
99 md5.update(session.session_key)
100 new_id = md5.hexdigest()
101
102 while User.objects.filter(user_id=new_id).exists():
103 md5.update(str(timezone.now()))
104 new_id = md5.hexdigest()
105
106 time_now = timezone.now()
107 user = User.objects.create(user_id=new_id, rank=RANK_USER,
108 registration_time=time_now)
109
110 session['user_id'] = user.id
111 else:
112 user = User.objects.select_related('fav_tags').get(
113 id=session['user_id'])
114
115 return user
116
117
118 def get_theme(request, user=None):
119 """
120 Get user's CSS theme
121 """
122
123 if not user:
124 user = get_user(request)
125 theme = user.get_setting('theme')
126 if not theme:
127 theme = boards.settings.DEFAULT_THEME
128
129 return theme No newline at end of file
82 * 1000000 + datetime.microsecond) No newline at end of file
@@ -5,6 +5,7 b' from django.shortcuts import render, red'
5 5
6 6 from boards import utils, settings
7 7 from boards.abstracts.paginator import get_paginator
8 from boards.abstracts.settingsmanager import get_settings_manager
8 9 from boards.forms import ThreadForm, PlainErrorList
9 10 from boards.models import Post, Thread, Ban, Tag
10 11 from boards.views.banned import BannedView
@@ -28,16 +29,17 b' DEFAULT_PAGE = 1'
28 29
29 30 class AllThreadsView(PostMixin, BaseBoardView):
30 31
31 user = None
32 def __init__(self):
33 self.settings_manager = None
34 super(AllThreadsView, self).__init__()
32 35
33 36 def get(self, request, page=DEFAULT_PAGE, form=None):
34 37 context = self.get_context_data(request=request)
35 38
36 self.user = utils.get_user(request)
37
38 39 if not form:
39 40 form = ThreadForm(error_class=PlainErrorList)
40 41
42 self.settings_manager = get_settings_manager(request)
41 43 paginator = get_paginator(self.get_threads(),
42 44 settings.THREADS_PER_PAGE)
43 45 paginator.current_page = int(page)
@@ -122,9 +124,8 b' class AllThreadsView(PostMixin, BaseBoar'
122 124
123 125 tags = self.parse_tags_string(tag_strings)
124 126
125 post = Post.objects.create_post(title=title, text=text, ip=ip,
126 image=image, tags=tags,
127 user=utils.get_user(request))
127 post = Post.objects.create_post(title=title, text=text, image=image,
128 ip=ip, tags=tags)
128 129
129 130 if html_response:
130 131 return redirect(post.get_url())
@@ -135,4 +136,4 b' class AllThreadsView(PostMixin, BaseBoar'
135 136 """
136 137
137 138 return Thread.objects.all().order_by('-bump_time')\
138 .exclude(tags__in=self.user.hidden_tags.all())
139 .exclude(tags__in=self.settings_manager.get_hidden_tags())
@@ -1,7 +1,8 b''
1 1 from django.db import transaction
2 2 from django.shortcuts import get_object_or_404
3 from boards import utils
4 3
4 from boards.abstracts.settingsmanager import PERMISSION_MODERATE, \
5 get_settings_manager
5 6 from boards.views.base import BaseBoardView
6 7 from boards.models import Post, Ban
7 8 from boards.views.mixins import RedirectNextMixin
@@ -11,10 +12,11 b' class BanUserView(BaseBoardView, Redirec'
11 12
12 13 @transaction.atomic
13 14 def get(self, request, post_id):
14 user = utils.get_user(request)
15 15 post = get_object_or_404(Post, id=post_id)
16 16
17 if user.is_moderator():
17 settings_manager = get_settings_manager(request)
18
19 if settings_manager.has_permission(PERMISSION_MODERATE):
18 20 # TODO Show confirmation page before ban
19 21 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
20 22 if created:
@@ -1,7 +1,8 b''
1 1 from django.shortcuts import redirect, get_object_or_404
2 2 from django.db import transaction
3 from boards import utils
4 3
4 from boards.abstracts.settingsmanager import PERMISSION_MODERATE,\
5 get_settings_manager
5 6 from boards.views.base import BaseBoardView
6 7 from boards.views.mixins import RedirectNextMixin
7 8 from boards.models import Post
@@ -11,12 +12,12 b' class DeletePostView(BaseBoardView, Redi'
11 12
12 13 @transaction.atomic
13 14 def get(self, request, post_id):
14 user = utils.get_user(request)
15 15 post = get_object_or_404(Post, id=post_id)
16 16
17 17 opening_post = post.is_opening()
18 18
19 if user.is_moderator():
19 settings_manager = get_settings_manager(request)
20 if settings_manager.has_permission(PERMISSION_MODERATE):
20 21 # TODO Show confirmation page before deletion
21 22 Post.objects.delete_post(post)
22 23
@@ -1,8 +1,11 b''
1 1 from django.shortcuts import render, redirect
2
3 from boards.abstracts.settingsmanager import PERMISSION_MODERATE, \
4 get_settings_manager
2 5 from boards.forms import LoginForm, PlainErrorList
3 from boards.models import User
4 6 from boards.views.base import BaseBoardView, CONTEXT_FORM
5 7
8
6 9 __author__ = 'neko259'
7 10
8 11
@@ -23,8 +26,8 b' class LoginView(BaseBoardView):'
23 26 form.session = request.session
24 27
25 28 if form.is_valid():
26 user = User.objects.get(user_id=form.cleaned_data['user_id'])
27 request.session['user_id'] = user.id
29 settings_manager = get_settings_manager(request)
30 settings_manager.add_permission(PERMISSION_MODERATE)
28 31 return redirect('index')
29 32 else:
30 33 return self.get(request, form)
@@ -1,16 +1,19 b''
1 1 from django.shortcuts import render, get_object_or_404, redirect
2 2
3 from boards.abstracts.settingsmanager import PERMISSION_MODERATE,\
4 get_settings_manager
3 5 from boards.views.base import BaseBoardView
4 6 from boards.views.mixins import DispatcherMixin
5 7 from boards.models.post import Post
6 8 from boards.models.tag import Tag
7 9 from boards.forms import AddTagForm, PlainErrorList
8 10
11
9 12 class PostAdminView(BaseBoardView, DispatcherMixin):
10 13
11 14 def get(self, request, post_id, form=None):
12 user = utils.get_user(request)
13 if not user.is_moderator:
15 settings_manager = get_settings_manager(request)
16 if not settings_manager.has_permission(PERMISSION_MODERATE):
14 17 redirect('index')
15 18
16 19 post = get_object_or_404(Post, id=post_id)
@@ -30,8 +33,8 b' class PostAdminView(BaseBoardView, Dispa'
30 33 return render(request, 'boards/post_admin.html', context)
31 34
32 35 def post(self, request, post_id):
33 user = utils.get_user(request)
34 if not user.is_moderator:
36 settings_manager = get_settings_manager(request)
37 if not settings_manager.has_permission(PERMISSION_MODERATE):
35 38 redirect('index')
36 39
37 40 post = get_object_or_404(Post, id=post_id)
@@ -27,8 +27,7 b' class BoardSearchView(View):'
27 27 if form.is_valid():
28 28 query = form.cleaned_data[FORM_QUERY]
29 29 if len(query) >= 3:
30 results = SearchQuerySet().auto_query(query).order_by('-id') \
31 .highlight()
30 results = SearchQuerySet().auto_query(query).order_by('-id')
32 31 paginator = get_paginator(results, 10)
33 32
34 33 if REQUEST_PAGE in request.GET:
@@ -1,53 +1,38 b''
1 1 from django.db import transaction
2 2 from django.shortcuts import render, redirect
3 from boards import utils
4 3
4 from boards.abstracts.settingsmanager import get_settings_manager
5 5 from boards.views.base import BaseBoardView, CONTEXT_FORM
6 from boards.forms import SettingsForm, ModeratorSettingsForm, PlainErrorList
7 from boards.models.post import SETTING_MODERATE
6 from boards.forms import SettingsForm, PlainErrorList
7
8 CONTEXT_HIDDEN_TAGS = 'hidden_tags'
8 9
9 10
10 11 class SettingsView(BaseBoardView):
11 12
12 13 def get(self, request):
13 14 context = self.get_context_data(request=request)
14 user = utils.get_user(request)
15 is_moderator = user.is_moderator()
16
17 selected_theme = utils.get_theme(request, user)
15 settings_manager = get_settings_manager(request)
18 16
19 if is_moderator:
20 form = ModeratorSettingsForm(initial={
21 'theme': selected_theme,
22 'moderate': user.get_setting(SETTING_MODERATE) and \
23 user.is_moderator()
24 }, error_class=PlainErrorList)
25 else:
17 selected_theme = settings_manager.get_theme()
18
26 19 form = SettingsForm(initial={'theme': selected_theme},
27 20 error_class=PlainErrorList)
28 21
29 22 context[CONTEXT_FORM] = form
23 context[CONTEXT_HIDDEN_TAGS] = settings_manager.get_hidden_tags()
30 24
31 25 return render(request, 'boards/settings.html', context)
32 26
33 27 def post(self, request):
34 user = utils.get_user(request)
35 is_moderator = user.is_moderator()
28 settings_manager = get_settings_manager(request)
36 29
37 30 with transaction.atomic():
38 if is_moderator:
39 form = ModeratorSettingsForm(request.POST,
40 error_class=PlainErrorList)
41 else:
42 31 form = SettingsForm(request.POST, error_class=PlainErrorList)
43 32
44 33 if form.is_valid():
45 34 selected_theme = form.cleaned_data['theme']
46 35
47 user.save_setting('theme', selected_theme)
48
49 if is_moderator:
50 moderate = form.cleaned_data['moderate']
51 user.save_setting(SETTING_MODERATE, moderate)
36 settings_manager.set_theme(selected_theme)
52 37
53 38 return redirect('settings')
@@ -1,10 +1,12 b''
1 1 from django.shortcuts import get_object_or_404
2 from boards import utils
3 from boards.models import Tag, Post
2
3 from boards.abstracts.settingsmanager import get_settings_manager
4 from boards.models import Tag
4 5 from boards.views.all_threads import AllThreadsView, DEFAULT_PAGE
5 6 from boards.views.mixins import DispatcherMixin, RedirectNextMixin
6 7 from boards.forms import ThreadForm, PlainErrorList
7 8
9
8 10 __author__ = 'neko259'
9 11
10 12
@@ -20,9 +22,14 b' class TagView(AllThreadsView, Dispatcher'
20 22 def get_context_data(self, **kwargs):
21 23 context = super(TagView, self).get_context_data(**kwargs)
22 24
25 settings_manager = get_settings_manager(kwargs['request'])
26
23 27 tag = get_object_or_404(Tag, name=self.tag_name)
24 28 context['tag'] = tag
25 29
30 context['fav_tags'] = settings_manager.get_fav_tags()
31 context['hidden_tags'] = settings_manager.get_hidden_tags()
32
26 33 return context
27 34
28 35 def get(self, request, tag_name, page=DEFAULT_PAGE, form=None):
@@ -48,20 +55,18 b' class TagView(AllThreadsView, Dispatcher'
48 55 return self.get(request, tag_name, page, form)
49 56
50 57 def subscribe(self, request):
51 user = utils.get_user(request)
52 58 tag = get_object_or_404(Tag, name=self.tag_name)
53 59
54 if not tag in user.fav_tags.all():
55 user.add_tag(tag)
60 settings_manager = get_settings_manager(request)
61 settings_manager.add_fav_tag(tag)
56 62
57 63 return self.redirect_to_next(request)
58 64
59 65 def unsubscribe(self, request):
60 user = utils.get_user(request)
61 66 tag = get_object_or_404(Tag, name=self.tag_name)
62 67
63 if tag in user.fav_tags.all():
64 user.remove_tag(tag)
68 settings_manager = get_settings_manager(request)
69 settings_manager.del_fav_tag(tag)
65 70
66 71 return self.redirect_to_next(request)
67 72
@@ -71,17 +76,17 b' class TagView(AllThreadsView, Dispatcher'
71 76 shown.
72 77 """
73 78
74 user = utils.get_user(request)
75 79 tag = get_object_or_404(Tag, name=self.tag_name)
76 80
77 user.hide_tag(tag)
81 settings_manager = get_settings_manager(request)
82 settings_manager.add_hidden_tag(tag)
78 83
79 84 def unhide(self, request):
80 85 """
81 86 Removed tag from user's hidden tags.
82 87 """
83 88
84 user = utils.get_user(request)
85 89 tag = get_object_or_404(Tag, name=self.tag_name)
86 90
87 user.unhide_tag(tag)
91 settings_manager = get_settings_manager(request)
92 settings_manager.del_hidden_tag(tag)
@@ -128,10 +128,8 b' class ThreadView(BaseBoardView, PostMixi'
128 128
129 129 post_thread = opening_post.get_thread()
130 130
131 post = Post.objects.create_post(title=title, text=text, ip=ip,
132 thread=post_thread, image=image,
133 tags=tags,
134 user=utils.get_user(request))
131 post = Post.objects.create_post(title=title, text=text, image=image,
132 thread=post_thread, ip=ip, tags=tags)
135 133
136 134 thread_to_show = (opening_post.id if opening_post else post.id)
137 135
@@ -35,3 +35,9 b' elements instead of swapping them'
35 35 images to a post
36 36 * Added search over posts and tags
37 37 * [ADMIN] Command to remove empty users
38
39 # 2.0 D'Anna
40 * Removed users. Now settings are stored in sessions
41 * Changed markdown to bbcode
42 * Removed linked tags
43 * [ADMIN] Added title to the post logs to make them more informative
@@ -1,6 +1,6 b''
1 1 # Django settings for neboard project.
2 2 import os
3 from boards.mdx_neboard import markdown_extended
3 from boards.mdx_neboard import bbcode_extended
4 4
5 5 DEBUG = True
6 6 TEMPLATE_DEBUG = DEBUG
@@ -217,7 +217,7 b' HAYSTACK_CONNECTIONS = {'
217 217 }
218 218
219 219 MARKUP_FIELD_TYPES = (
220 ('markdown', markdown_extended),
220 ('bbcode', bbcode_extended),
221 221 )
222 222
223 223 THEMES = [
@@ -1,10 +1,10 b''
1 south>=0.8.4
1 2 line_profiler
2 3 haystack
3 4 pillow
4 5 django>=1.6
5 6 django_cleanup
6 7 django-markupfield
7 markdown
8 python-markdown
9 8 django-simple-captcha
10 9 line-profiler
10 bbcode
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now