##// END OF EJS Templates
Merged 1.1 into default branch.
neko259 -
r146:60af707d merge default
parent child Browse files
Show More
@@ -0,0 +1,130 b''
1 # -*- coding: utf-8 -*-
2 import datetime
3 from south.db import db
4 from south.v2 import SchemaMigration
5 from django.db import models
6
7
8 class Migration(SchemaMigration):
9
10 def forwards(self, orm):
11 # Deleting model 'Admin'
12 db.delete_table(u'boards_admin')
13
14 # Adding model 'User'
15 db.create_table(u'boards_user', (
16 (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
17 ('user_id', self.gf('django.db.models.fields.CharField')(max_length=50)),
18 ('rank', self.gf('django.db.models.fields.IntegerField')()),
19 ('registration_time', self.gf('django.db.models.fields.DateTimeField')()),
20 ('last_access_time', self.gf('django.db.models.fields.DateTimeField')()),
21 ))
22 db.send_create_signal(u'boards', ['User'])
23
24 # Adding M2M table for field fav_tags on 'User'
25 m2m_table_name = db.shorten_name(u'boards_user_fav_tags')
26 db.create_table(m2m_table_name, (
27 ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
28 ('user', models.ForeignKey(orm[u'boards.user'], null=False)),
29 ('tag', models.ForeignKey(orm[u'boards.tag'], null=False))
30 ))
31 db.create_unique(m2m_table_name, ['user_id', 'tag_id'])
32
33 # Adding M2M table for field fav_threads on 'User'
34 m2m_table_name = db.shorten_name(u'boards_user_fav_threads')
35 db.create_table(m2m_table_name, (
36 ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
37 ('user', models.ForeignKey(orm[u'boards.user'], null=False)),
38 ('post', models.ForeignKey(orm[u'boards.post'], null=False))
39 ))
40 db.create_unique(m2m_table_name, ['user_id', 'post_id'])
41
42 # Adding model 'Setting'
43 db.create_table(u'boards_setting', (
44 (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
45 ('name', self.gf('django.db.models.fields.CharField')(max_length=50)),
46 ('value', self.gf('django.db.models.fields.CharField')(max_length=50)),
47 ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['boards.User'])),
48 ))
49 db.send_create_signal(u'boards', ['Setting'])
50
51 # Adding field 'Post.user'
52 db.add_column(u'boards_post', 'user',
53 self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['boards.User'], null=True),
54 keep_default=False)
55
56
57 def backwards(self, orm):
58 # Adding model 'Admin'
59 db.create_table(u'boards_admin', (
60 ('password', self.gf('django.db.models.fields.CharField')(max_length=100)),
61 (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
62 ('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
63 ))
64 db.send_create_signal(u'boards', ['Admin'])
65
66 # Deleting model 'User'
67 db.delete_table(u'boards_user')
68
69 # Removing M2M table for field fav_tags on 'User'
70 db.delete_table(db.shorten_name(u'boards_user_fav_tags'))
71
72 # Removing M2M table for field fav_threads on 'User'
73 db.delete_table(db.shorten_name(u'boards_user_fav_threads'))
74
75 # Deleting model 'Setting'
76 db.delete_table(u'boards_setting')
77
78 # Deleting field 'Post.user'
79 db.delete_column(u'boards_post', 'user_id')
80
81
82 models = {
83 u'boards.ban': {
84 'Meta': {'object_name': 'Ban'},
85 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
86 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
87 },
88 u'boards.post': {
89 'Meta': {'object_name': 'Post'},
90 '_text_rendered': ('django.db.models.fields.TextField', [], {}),
91 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
92 'image': ('boards.thumbs.ImageWithThumbsField', [], {'max_length': '100', 'blank': 'True'}),
93 'image_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
94 'image_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
95 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
96 'parent': ('django.db.models.fields.BigIntegerField', [], {}),
97 'poster_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
98 'poster_user_agent': ('django.db.models.fields.TextField', [], {}),
99 'pub_time': ('django.db.models.fields.DateTimeField', [], {}),
100 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['boards.Tag']", 'symmetrical': 'False'}),
101 'text': ('markupfield.fields.MarkupField', [], {'rendered_field': 'True'}),
102 'text_markup_type': ('django.db.models.fields.CharField', [], {'default': "'markdown'", 'max_length': '30'}),
103 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
104 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['boards.User']", 'null': 'True'})
105 },
106 u'boards.setting': {
107 'Meta': {'object_name': 'Setting'},
108 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
109 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
110 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['boards.User']"}),
111 'value': ('django.db.models.fields.CharField', [], {'max_length': '50'})
112 },
113 u'boards.tag': {
114 'Meta': {'object_name': 'Tag'},
115 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
116 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
117 },
118 u'boards.user': {
119 'Meta': {'object_name': 'User'},
120 'fav_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['boards.Tag']", 'symmetrical': 'False'}),
121 'fav_threads': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'+'", 'symmetrical': 'False', 'to': u"orm['boards.Post']"}),
122 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
123 'last_access_time': ('django.db.models.fields.DateTimeField', [], {}),
124 'rank': ('django.db.models.fields.IntegerField', [], {}),
125 'registration_time': ('django.db.models.fields.DateTimeField', [], {}),
126 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '50'})
127 }
128 }
129
130 complete_apps = ['boards'] No newline at end of file
@@ -1,7 +1,7 b''
1 from django.contrib import admin
1 from django.contrib import admin
2 from boards.models import Post, Tag, Admin, Ban
2 from boards.models import Post, Tag, User, Ban
3
3
4 admin.site.register(Post)
4 admin.site.register(Post)
5 admin.site.register(Tag)
5 admin.site.register(Tag)
6 admin.site.register(Admin)
6 admin.site.register(User)
7 admin.site.register(Ban) No newline at end of file
7 admin.site.register(Ban)
@@ -2,7 +2,7 b' import re'
2 from captcha.fields import CaptchaField
2 from captcha.fields import CaptchaField
3 from django import forms
3 from django import forms
4 from django.forms.util import ErrorList
4 from django.forms.util import ErrorList
5 from boards.models import TITLE_MAX_LENGTH
5 from boards.models import TITLE_MAX_LENGTH, User
6 from neboard import settings
6 from neboard import settings
7 from boards import utils
7 from boards import utils
8
8
@@ -67,7 +67,6 b' class PostForm(forms.Form):'
67 self._errors['image'] = self.error_class([error_message])
67 self._errors['image'] = self.error_class([error_message])
68
68
69
69
70
71 class ThreadForm(PostForm):
70 class ThreadForm(PostForm):
72 regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE)
71 regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE)
73 tags = forms.CharField(max_length=100)
72 tags = forms.CharField(max_length=100)
@@ -132,3 +131,21 b' class ThreadCaptchaForm(ThreadForm):'
132
131
133 class SettingsForm(forms.Form):
132 class SettingsForm(forms.Form):
134 theme = forms.ChoiceField(choices=settings.THEMES, widget=forms.RadioSelect)
133 theme = forms.ChoiceField(choices=settings.THEMES, widget=forms.RadioSelect)
134
135
136 class LoginForm(forms.Form):
137 user_id = forms.CharField()
138
139 def clean_user_id(self):
140 user_id = self.cleaned_data['user_id']
141 if user_id:
142 users = User.objects.filter(user_id=user_id)
143 if len(users) == 0:
144 raise forms.ValidationError('No such user found')
145
146 return user_id
147
148 def clean(self):
149 cleaned_data = super(LoginForm, self).clean()
150
151 return cleaned_data No newline at end of file
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -7,7 +7,7 b' msgid ""'
7 msgstr ""
7 msgstr ""
8 "Project-Id-Version: PACKAGE VERSION\n"
8 "Project-Id-Version: PACKAGE VERSION\n"
9 "Report-Msgid-Bugs-To: \n"
9 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2013-08-30 18:54+0300\n"
10 "POT-Creation-Date: 2013-09-07 19:43+0300\n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -54,24 +54,37 b' msgstr "\xd0\x92\xd0\xb0\xd1\x88 IP \xd0\xb0\xd0\xb4\xd1\x80\xd0\xb5\xd1\x81 \xd0\xb1\xd1\x8b\xd0\xbb \xd0\xb7\xd0\xb0\xd0\xb1\xd0\xbb\xd0\xbe\xd0\xba\xd0\xb8\xd1\x80\xd0\xbe\xd0\xb2\xd0\xb0\xd0\xbd. \xd0\xa1\xd0\xb2\xd1\x8f\xd0\xb6\xd0\xb8\xd1\x82\xd0\xb5\xd1\x81\xd1\x8c \xd1\x81 \xd0\xb0\xd0\xb4\xd0\xbc\xd0\xb8\xd0\xbd\xd0\xb8\xd1\x81\xd1\x82\xd1\x80\xd0\xb0\xd1\x82\xd0\xbe\xd1\x80\xd0\xbe\xd0\xbc"'
54 msgid "Feed"
54 msgid "Feed"
55 msgstr "Лента"
55 msgstr "Лента"
56
56
57 #: templates/boards/base.html:36
57 #: templates/boards/base.html:29
58 msgid "All threads"
58 msgid "All threads"
59 msgstr "Все темы"
59 msgstr "Все темы"
60
60
61 #: templates/boards/base.html:42
61 #: templates/boards/base.html:35
62 msgid "Settings"
62 msgid "Settings"
63 msgstr "Настройки"
63 msgstr "Настройки"
64
64
65 #: templates/boards/base.html:50
65 #: templates/boards/base.html:42 templates/boards/login.html:6
66 #: templates/boards/login.html.py:21
67 msgid "Login"
68 msgstr "Вход"
69
70 #: templates/boards/base.html:43
66 msgid "Up"
71 msgid "Up"
67 msgstr "Вверх"
72 msgstr "Вверх"
68
73
74 #: templates/boards/login.html:15
75 msgid "User ID"
76 msgstr "ID пользователя"
77
78 #: templates/boards/login.html:24
79 msgid "Insert your user id above"
80 msgstr "Вставьте свой ID пользователя выше"
81
69 #: templates/boards/posting_general.html:18
82 #: templates/boards/posting_general.html:18
70 msgid "Tag: "
83 msgid "Tag: "
71 msgstr "Тег: "
84 msgstr "Тег: "
72
85
73 #: templates/boards/posting_general.html:35
86 #: templates/boards/posting_general.html:35
74 #: templates/boards/posting_general.html:81 templates/boards/thread.html:27
87 #: templates/boards/posting_general.html:89 templates/boards/thread.html:27
75 #: templates/boards/rss/post.html:5
88 #: templates/boards/rss/post.html:5
76 msgid "Post image"
89 msgid "Post image"
77 msgstr "Изображение сообщения"
90 msgstr "Изображение сообщения"
@@ -80,93 +93,124 b' msgstr "\xd0\x98\xd0\xb7\xd0\xbe\xd0\xb1\xd1\x80\xd0\xb0\xd0\xb6\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb5 \xd1\x81\xd0\xbe\xd0\xbe\xd0\xb1\xd1\x89\xd0\xb5\xd0\xbd\xd0\xb8\xd1\x8f"'
80 msgid "Reply"
93 msgid "Reply"
81 msgstr "Ответ"
94 msgstr "Ответ"
82
95
83 #: templates/boards/posting_general.html:55 templates/boards/thread.html:108
96 #: templates/boards/posting_general.html:54 templates/boards/thread.html:46
97 msgid "Delete"
98 msgstr "Удалить"
99
100 #: templates/boards/posting_general.html:63 templates/boards/thread.html:113
84 msgid "replies"
101 msgid "replies"
85 msgstr "ответов"
102 msgstr "ответов"
86
103
87 #: templates/boards/posting_general.html:56 templates/boards/thread.html:109
104 #: templates/boards/posting_general.html:64 templates/boards/thread.html:114
88 msgid "images"
105 msgid "images"
89 msgstr "изображений"
106 msgstr "изображений"
90
107
91 #: templates/boards/posting_general.html:58
108 #: templates/boards/posting_general.html:66
92 #: templates/boards/posting_general.html:131 templates/boards/thread.html:48
109 #: templates/boards/posting_general.html:139 templates/boards/tags.html:7
93 #: templates/boards/rss/post.html:10
110 #: templates/boards/thread.html:56 templates/boards/rss/post.html:10
94 msgid "Tags"
111 msgid "Tags"
95 msgstr "Теги"
112 msgstr "Теги"
96
113
97 #: templates/boards/posting_general.html:113
114 #: templates/boards/posting_general.html:115
115 msgid "No threads exist. Create the first one!"
116 msgstr "Нет тем. Создайте первую!"
117
118 #: templates/boards/posting_general.html:121
98 msgid "Create new thread"
119 msgid "Create new thread"
99 msgstr "Создать новую тему"
120 msgstr "Создать новую тему"
100
121
101 #: templates/boards/posting_general.html:116 templates/boards/thread.html:70
122 #: templates/boards/posting_general.html:124 templates/boards/thread.html:75
102 msgid "Title"
123 msgid "Title"
103 msgstr "Заголовок"
124 msgstr "Заголовок"
104
125
105 #: templates/boards/posting_general.html:121 templates/boards/thread.html:75
126 #: templates/boards/posting_general.html:129 templates/boards/thread.html:80
106 msgid "Text"
127 msgid "Text"
107 msgstr "Текст"
128 msgstr "Текст"
108
129
109 #: templates/boards/posting_general.html:126 templates/boards/thread.html:80
130 #: templates/boards/posting_general.html:134 templates/boards/thread.html:85
110 msgid "Image"
131 msgid "Image"
111 msgstr "Изображение"
132 msgstr "Изображение"
112
133
113 #: templates/boards/posting_general.html:141 templates/boards/thread.html:91
134 #: templates/boards/posting_general.html:149 templates/boards/thread.html:96
114 msgid "Post"
135 msgid "Post"
115 msgstr "Отправить"
136 msgstr "Отправить"
116
137
117 #: templates/boards/posting_general.html:143
138 #: templates/boards/posting_general.html:151
118 msgid "Tags must be delimited by spaces. Text or image is required."
139 msgid "Tags must be delimited by spaces. Text or image is required."
119 msgstr ""
140 msgstr ""
120 "Теги должны быть разделены пробелами. Текст или изображение обязательны."
141 "Теги должны быть разделены пробелами. Текст или изображение обязательны."
121
142
122 #: templates/boards/posting_general.html:146 templates/boards/thread.html:93
143 #: templates/boards/posting_general.html:154 templates/boards/thread.html:98
123 msgid "Basic markdown syntax."
144 msgid "Basic markdown syntax."
124 msgstr "Базовый синтаксис markdown."
145 msgstr "Базовый синтаксис markdown."
125
146
126 #: templates/boards/posting_general.html:156
147 #: templates/boards/posting_general.html:164
127 msgid "Pages:"
148 msgid "Pages:"
128 msgstr "Страницы: "
149 msgstr "Страницы: "
129
150
130 #: templates/boards/settings.html:13
151 #: templates/boards/settings.html:12
152 msgid "User:"
153 msgstr "Пользователь:"
154
155 #: templates/boards/settings.html:14
156 msgid "You are moderator."
157 msgstr "Вы модератор."
158
159 #: templates/boards/settings.html:20
131 msgid "Theme"
160 msgid "Theme"
132 msgstr "Тема"
161 msgstr "Тема"
133
162
134 #: templates/boards/settings.html:29
163 #: templates/boards/settings.html:36
135 msgid "Save"
164 msgid "Save"
136 msgstr "Сохранить"
165 msgstr "Сохранить"
137
166
138 #: templates/boards/tags.html:7
167 #: templates/boards/tags.html:17
139 msgid "tags"
168 msgid "threads"
140 msgstr "тегов"
169 msgstr "тем"
170
171 #: templates/boards/tags.html:20
172 msgid "Remove"
173 msgstr "Удалить"
141
174
142 #: templates/boards/thread.html:67
175 #: templates/boards/tags.html:23
176 msgid "Add"
177 msgstr "Добавить"
178
179 #: templates/boards/tags.html:28
180 msgid "No tags found."
181 msgstr "Теги не найдены."
182
183 #: templates/boards/thread.html:72
143 msgid "Reply to thread"
184 msgid "Reply to thread"
144 msgstr "Ответить в тему"
185 msgstr "Ответить в тему"
145
186
146 #: templates/boards/thread.html:94
187 #: templates/boards/thread.html:99
147 msgid "Example: "
188 msgid "Example: "
148 msgstr "Пример: "
189 msgstr "Пример: "
149
190
150 #: templates/boards/thread.html:94
191 #: templates/boards/thread.html:99
151 msgid "italic"
192 msgid "italic"
152 msgstr "курсив"
193 msgstr "курсив"
153
194
154 #: templates/boards/thread.html:95
195 #: templates/boards/thread.html:100
155 msgid "bold"
196 msgid "bold"
156 msgstr "полужирный"
197 msgstr "полужирный"
157
198
158 #: templates/boards/thread.html:96
199 #: templates/boards/thread.html:101
159 msgid "Quotes can be inserted with"
200 msgid "Quotes can be inserted with"
160 msgstr "Цитаты могут быть вставлены при помощи"
201 msgstr "Цитаты могут быть вставлены при помощи"
161
202
162 #: templates/boards/thread.html:97
203 #: templates/boards/thread.html:102
163 msgid "Links to answers can be inserted with"
204 msgid "Links to answers can be inserted with"
164 msgstr "Ссылки на ответы могут быть вставлены с помощью"
205 msgstr "Ссылки на ответы могут быть вставлены с помощью"
165
206
166 #: templates/boards/thread.html:110
207 #: templates/boards/thread.html:115
167 msgid "Last update: "
208 msgid "Last update: "
168 msgstr "Последнее обновление: "
209 msgstr "Последнее обновление: "
169
210
211 #~ msgid "tags"
212 #~ msgstr "тегов"
213
170 #~ msgid "Get!"
214 #~ msgid "Get!"
171 #~ msgstr "Гет!"
215 #~ msgstr "Гет!"
172
216
@@ -27,10 +27,14 b' OPENING_POST_POPULARITY_WEIGHT = 2'
27 IMAGES_DIRECTORY = 'images/'
27 IMAGES_DIRECTORY = 'images/'
28 FILE_EXTENSION_DELIMITER = '.'
28 FILE_EXTENSION_DELIMITER = '.'
29
29
30 RANK_ADMIN = 0
31 RANK_MODERATOR = 10
32 RANK_USER = 100
33
30
34
31 class PostManager(models.Manager):
35 class PostManager(models.Manager):
32 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
36 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
33 ip=NO_IP, tags=None):
37 ip=NO_IP, tags=None, user=None):
34 post = self.create(title=title,
38 post = self.create(title=title,
35 text=text,
39 text=text,
36 pub_time=timezone.now(),
40 pub_time=timezone.now(),
@@ -38,7 +42,8 b' class PostManager(models.Manager):'
38 image=image,
42 image=image,
39 poster_ip=ip,
43 poster_ip=ip,
40 poster_user_agent=UNKNOWN_UA,
44 poster_user_agent=UNKNOWN_UA,
41 last_edit_time=timezone.now())
45 last_edit_time=timezone.now(),
46 user=user)
42
47
43 if tags:
48 if tags:
44 map(post.tags.add, tags)
49 map(post.tags.add, tags)
@@ -224,6 +229,7 b' class Post(models.Model):'
224 parent = models.BigIntegerField()
229 parent = models.BigIntegerField()
225 tags = models.ManyToManyField(Tag)
230 tags = models.ManyToManyField(Tag)
226 last_edit_time = models.DateTimeField()
231 last_edit_time = models.DateTimeField()
232 user = models.ForeignKey('User', null=True, default=None)
227
233
228 def __unicode__(self):
234 def __unicode__(self):
229 return '#' + str(self.id) + ' ' + self.title + ' (' + \
235 return '#' + str(self.id) + ' ' + self.title + ' (' + \
@@ -271,19 +277,54 b' class Post(models.Model):'
271 return last_replies
277 return last_replies
272
278
273
279
274 class Admin(models.Model):
280 class User(models.Model):
275 """
281
276 Model for admin users
282 user_id = models.CharField(max_length=50)
277 """
283 rank = models.IntegerField()
278 name = models.CharField(max_length=100)
284
279 password = models.CharField(max_length=100)
285 registration_time = models.DateTimeField()
286 last_access_time = models.DateTimeField()
287
288 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
289 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
290 blank=True)
291
292 def save_setting(self, name, value):
293 setting, created = Setting.objects.get_or_create(name=name, user=self)
294 setting.value = value
295 setting.save()
296
297 return setting
298
299 def get_setting(self, name):
300 settings = Setting.objects.filter(name=name, user=self)
301 if len(settings) > 0:
302 setting = settings[0]
303 else:
304 setting = None
305
306 if setting:
307 setting_value = setting.value
308 else:
309 setting_value = None
310
311 return setting_value
312
313 def is_moderator(self):
314 return RANK_MODERATOR >= self.rank
280
315
281 def __unicode__(self):
316 def __unicode__(self):
282 return self.name + '/' + '*' * len(self.password)
317 return self.user_id
318
319
320 class Setting(models.Model):
321
322 name = models.CharField(max_length=50)
323 value = models.CharField(max_length=50)
324 user = models.ForeignKey(User)
283
325
284
326
285 class Ban(models.Model):
327 class Ban(models.Model):
286
287 ip = models.GenericIPAddressField()
328 ip = models.GenericIPAddressField()
288
329
289 def __unicode__(self):
330 def __unicode__(self):
@@ -128,7 +128,7 b' p {'
128 .form-errors {
128 .form-errors {
129 padding-left: 1ex;
129 padding-left: 1ex;
130 font-weight: bold;
130 font-weight: bold;
131 vertical-align: top;
131 vertical-align: middle;
132 }
132 }
133
133
134 .post-form input, .post-form textarea {
134 .post-form input, .post-form textarea {
@@ -274,6 +274,11 b' li {'
274 color: #ddd;
274 color: #ddd;
275 }
275 }
276
276
277 .moderator_info {
278 color: #e99d41;
279 float: right;
280 }
281
277 .refmap {
282 .refmap {
278 font-size: 0.9em;
283 font-size: 0.9em;
279 color: #ccc;
284 color: #ccc;
@@ -263,8 +263,13 b' li {'
263 margin-left: 1ex;
263 margin-left: 1ex;
264 }
264 }
265
265
266 .moderator_info {
267 font-weight: bold;
268 float: right;
269 }
270
266 .refmap {
271 .refmap {
267 border: 1px dashed #aaa;
272 border: 1px dashed #aaa;
268 padding: 0.5em;
273 padding: 0.5em;
269 display: table;
274 display: table;
270 } No newline at end of file
275 }
@@ -24,19 +24,12 b''
24 <script src="{% url 'django.views.i18n.javascript_catalog' %}"></script>
24 <script src="{% url 'django.views.i18n.javascript_catalog' %}"></script>
25 <script src="{{ STATIC_URL }}js/refmaps.js"></script>
25 <script src="{{ STATIC_URL }}js/refmaps.js"></script>
26 <script src="{{ STATIC_URL }}js/main.js"></script>
26 <script src="{{ STATIC_URL }}js/main.js"></script>
27 <div id="admin_panel">
28
29 {% if request.session.admin == True %}
30 Admin panel TODO: Need to implement <BR />
31 {% endif %}
32
33 </div>
34
27
35 <div class="navigation_panel">
28 <div class="navigation_panel">
36 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
29 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
37 {% for tag in tags %}
30 {% for tag in tags %}
38 <a class="tag" href=" {% url 'tag' tag_name=tag.name %}">
31 <a class="tag" href="{% url 'tag' tag_name=tag.name %}">
39 {{ tag.name }}</a>({{ tag.get_post_count }})
32 {{ tag.name }}</a>
40 {% endfor %}
33 {% endfor %}
41 <a class="tag" href="{% url 'tags' %}">[...]</a>
34 <a class="tag" href="{% url 'tags' %}">[...]</a>
42 <a class="link" href="{% url 'settings' %}">{% trans 'Settings' %}</a>
35 <a class="link" href="{% url 'settings' %}">{% trans 'Settings' %}</a>
@@ -46,7 +39,7 b''
46
39
47 <div class="navigation_panel">
40 <div class="navigation_panel">
48 {% block metapanel %}{% endblock %}
41 {% block metapanel %}{% endblock %}
49 [<a href="rss/">RSS</a>]
42 [<a href="{% url "login" %}">{% trans 'Login' %}</a>]
50 <a class="link" href="#top">{% trans 'Up' %}</a>
43 <a class="link" href="#top">{% trans 'Up' %}</a>
51 </div>
44 </div>
52
45
@@ -1,22 +1,29 b''
1 <!DOCTYPE html>
1 {% extends "boards/base.html" %}
2 <html>
2
3 <head>
3 {% load i18n %}
4 <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/md/login.css" media="all"/>
4
5 <title>Login page</title>
5 {% block head %}
6 </head>
6 <title>{% trans 'Login' %}</title>
7 {% endblock %}
8
9 {% block content %}
7
10
8 <body>
11 <form enctype="multipart/form-data" method="post">
9 {% if error != none%}
12 <div class="post-form-w">
10 <span id="error_message">
13 <div class="post-form">
11 {{ error }}
14 <div class="form-row">
12 </span>
15 <div class="form-label">{% trans 'User ID' %}</div>
13 {% endif %}
16 <div class="form-input">{{ form.user_id }}</div>
17 <div class="form-errors">{{ form.user_id.errors }}</div>
18 </div>
19 </div>
20 <div class="form-submit">
21 <input type="submit" value="{% trans "Login" %}"/>
22 </div>
23 <div>
24 {% trans 'Insert your user id above' %}
25 </div>
26 </div>
27 </form>
14
28
15 <form action="login" method="POST">{% csrf_token %}
29 {% endblock %} No newline at end of file
16
17 Login: <input type="text" name="name"><br />
18 Password: <input type="password" name="password"><br />
19 <input type="submit">
20 </form>
21 </body>
22 </html> No newline at end of file
@@ -46,6 +46,14 b''
46 [{{ thread.pub_time }}]
46 [{{ thread.pub_time }}]
47 [<a class="link" href="{% url 'thread' thread.id %}#form"
47 [<a class="link" href="{% url 'thread' thread.id %}#form"
48 >{% trans "Reply" %}</a>]
48 >{% trans "Reply" %}</a>]
49
50 {% if user.is_moderator %}
51 <span class="moderator_info">
52 ({{ thread.poster_ip }})
53 [<a href="{% url 'delete' post_id=thread.id %}"
54 >{% trans 'Delete' %}</a>]
55 </span>
56 {% endif %}
49 </div>
57 </div>
50 {% autoescape off %}
58 {% autoescape off %}
51 {{ thread.text.rendered|truncatewords_html:50 }}
59 {{ thread.text.rendered|truncatewords_html:50 }}
@@ -103,8 +111,8 b''
103 </div>
111 </div>
104 {% endfor %}
112 {% endfor %}
105 {% else %}
113 {% else %}
106 No threads found.
114 <div class="post">
107 <hr />
115 {% trans 'No threads exist. Create the first one!' %}</div>
108 {% endif %}
116 {% endif %}
109
117
110 <form enctype="multipart/form-data" method="post">{% csrf_token %}
118 <form enctype="multipart/form-data" method="post">{% csrf_token %}
@@ -163,6 +171,7 b''
163 {% endif %}
171 {% endif %}
164 ">{{ page }}</a>]
172 ">{{ page }}</a>]
165 {% endfor %}
173 {% endfor %}
174 [<a href="rss/">RSS</a>]
166 </span>
175 </span>
167
176
168 {% endblock %}
177 {% endblock %}
@@ -8,6 +8,13 b''
8
8
9 {% block content %}
9 {% block content %}
10
10
11 <div class="post">
12 {% trans 'User:' %} <b>{{ user.user_id }}</b>.
13 {% if user.is_moderator %}
14 {% trans 'You are moderator.' %}
15 {% endif %}
16 </div>
17
11 <div class="post-form-w">
18 <div class="post-form-w">
12 <div class="post-form">
19 <div class="post-form">
13 <span class="form-title">{% trans "Theme" %}</span>
20 <span class="form-title">{% trans "Theme" %}</span>
@@ -4,20 +4,28 b''
4 {% load markup %}
4 {% load markup %}
5
5
6 {% block head %}
6 {% block head %}
7 <title>Neboard - {% trans "tags" %}</title>
7 <title>Neboard - {% trans "Tags" %}</title>
8 {% endblock %}
8 {% endblock %}
9
9
10 {% block content %}
10 {% block content %}
11
11
12 <div class="post">
12 <div class="post">
13 {% if tags %}
13 {% if all_tags %}
14 {% for tag in all_tags %}
14 {% for tag in all_tags %}
15 <a class="tag" href="{% url 'tag' tag.name %}">
15 <a class="tag" href="{% url 'tag' tag.name %}">
16 {{ tag.name }}</a><br />
16 {{ tag.name }}</a>
17 ({{ tag.get_post_count }} {% trans 'threads' %})
18 {% if tag in user.fav_tags.all %}
19 [<a href="{% url 'tag_unsubscribe' tag.name %}"
20 >{% trans 'Remove' %}</a>]
21 {% else %}
22 [<a href="{% url 'tag_subscribe' tag.name %}"
23 >{% trans 'Add' %}</a>]
24 {% endif %}
25 <br />
17 {% endfor %}
26 {% endfor %}
18 {% else %}
27 {% else %}
19 No tags found.
28 {% trans 'No tags found.' %}
20 <hr />
21 {% endif %}
29 {% endif %}
22 </div>
30 </div>
23
31
@@ -38,6 +38,14 b''
38 [{{ post.pub_time }}]
38 [{{ post.pub_time }}]
39 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
39 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
40 ; return false;">&gt;&gt;</a>]
40 ; return false;">&gt;&gt;</a>]
41
42 {% if user.is_moderator %}
43 <span class="moderator_info">
44 ({{ post.poster_ip }})
45 [<a href="{% url 'delete' post_id=post.id %}"
46 >{% trans 'Delete' %}</a>]
47 </span>
48 {% endif %}
41 </div>
49 </div>
42 {% autoescape off %}
50 {% autoescape off %}
43 {{ post.text.rendered }}
51 {{ post.text.rendered }}
@@ -56,9 +64,6 b''
56 </div>
64 </div>
57 {% endfor %}
65 {% endfor %}
58 </div>
66 </div>
59 {% else %}
60 No thread found.
61 <hr />
62 {% endif %}
67 {% endif %}
63
68
64 <form id="form" enctype="multipart/form-data" method="post"
69 <form id="form" enctype="multipart/form-data" method="post"
@@ -108,6 +113,7 b''
108 {{ posts.0.get_reply_count }} {% trans 'replies' %},
113 {{ posts.0.get_reply_count }} {% trans 'replies' %},
109 {{ posts.0.get_images_count }} {% trans 'images' %}.
114 {{ posts.0.get_images_count }} {% trans 'images' %}.
110 {% trans 'Last update: ' %}{{ posts.0.last_edit_time }}
115 {% trans 'Last update: ' %}{{ posts.0.last_edit_time }}
116 [<a href="rss/">RSS</a>]
111 </span>
117 </span>
112
118
113 {% endblock %}
119 {% endblock %}
@@ -4,7 +4,7 b' from django.test.client import Client'
4
4
5 import boards
5 import boards
6
6
7 from boards.models import Post, Admin, Tag
7 from boards.models import Post, Tag
8 from neboard import settings
8 from neboard import settings
9
9
10 TEST_TEXT = 'test text'
10 TEST_TEXT = 'test text'
@@ -54,48 +54,6 b' class BoardTests(TestCase):'
54 admin.save()
54 admin.save()
55 return admin
55 return admin
56
56
57 def test_admin_login(self):
58 client = Client()
59
60 self.assertFalse('admin' in client.session)
61
62 admin = self._create_test_user()
63
64 response = client.post('/login',
65 {'name': admin.name, 'password': admin.password})
66
67 # it means that login passed and user are redirected to another page
68 self.assertEqual(302, response.status_code)
69
70 self.assertTrue('admin' in client.session)
71 self.assertTrue(client.session['admin'])
72
73 admin.delete()
74
75 wrong_name = 'sd2f1s3d21fs3d21f'
76 wrong_password = 'sd2f1s3d21fs3d21fsdfsd'
77
78 client.post('/login', {'name': wrong_name, 'password': wrong_password})
79 self.assertFalse(client.session['admin'])
80
81 def test_admin_logout(self):
82 client = Client()
83
84 self.assertFalse('admin' in client.session)
85
86 admin = self._create_test_user()
87
88 client.post('/login',
89 {'name': admin.name, 'password': admin.password})
90
91 self.assertTrue(client.session['admin'])
92
93 client.get('/logout')
94
95 self.assertFalse(client.session['admin'])
96
97 admin.delete()
98
99 def test_get_thread(self):
57 def test_get_thread(self):
100 opening_post = self._create_post()
58 opening_post = self._create_post()
101 op_id = opening_post.id
59 op_id = opening_post.id
@@ -120,13 +78,6 b' class BoardTests(TestCase):'
120 self.assertEqual(settings.MAX_THREAD_COUNT,
78 self.assertEqual(settings.MAX_THREAD_COUNT,
121 len(Post.objects.get_threads()))
79 len(Post.objects.get_threads()))
122
80
123 def test_get(self):
124 """Test if the get computes properly"""
125
126 post = self._create_post()
127
128 self.assertTrue(post.is_get())
129
130 def test_pages(self):
81 def test_pages(self):
131 """Test that the thread list is properly split into pages"""
82 """Test that the thread list is properly split into pages"""
132
83
@@ -202,6 +153,7 b' class BoardTests(TestCase):'
202
153
203 response_not_existing = client.get(THREAD_PAGE + str(
154 response_not_existing = client.get(THREAD_PAGE + str(
204 existing_post_id + 1) + '/')
155 existing_post_id + 1) + '/')
156 response_not_existing.get_full_path()
205 self.assertEqual(HTTP_CODE_NOT_FOUND,
157 self.assertEqual(HTTP_CODE_NOT_FOUND,
206 response_not_existing.status_code,
158 response_not_existing.status_code,
207 u'Not existing thread is opened')
159 u'Not existing thread is opened')
@@ -9,6 +9,7 b' from PIL import Image'
9 from django.core.files.base import ContentFile
9 from django.core.files.base import ContentFile
10 import cStringIO
10 import cStringIO
11
11
12
12 def generate_thumb(img, thumb_size, format):
13 def generate_thumb(img, thumb_size, format):
13 """
14 """
14 Generates a thumbnail image and returns a ContentFile object with the thumbnail
15 Generates a thumbnail image and returns a ContentFile object with the thumbnail
@@ -22,10 +23,10 b' def generate_thumb(img, thumb_size, form'
22 format format of the original image ('jpeg','gif','png',...)
23 format format of the original image ('jpeg','gif','png',...)
23 (this format will be used for the generated thumbnail, too)
24 (this format will be used for the generated thumbnail, too)
24 """
25 """
25
26
26 img.seek(0) # see http://code.djangoproject.com/ticket/8222 for details
27 img.seek(0) # see http://code.djangoproject.com/ticket/8222 for details
27 image = Image.open(img)
28 image = Image.open(img)
28
29
29 # get size
30 # get size
30 thumb_w, thumb_h = thumb_size
31 thumb_w, thumb_h = thumb_size
31 # If you want to generate a square thumbnail
32 # If you want to generate a square thumbnail
@@ -33,12 +34,13 b' def generate_thumb(img, thumb_size, form'
33 # quad
34 # quad
34 xsize, ysize = image.size
35 xsize, ysize = image.size
35 # get minimum size
36 # get minimum size
36 minsize = min(xsize,ysize)
37 minsize = min(xsize, ysize)
37 # largest square possible in the image
38 # largest square possible in the image
38 xnewsize = (xsize-minsize)/2
39 xnewsize = (xsize - minsize) / 2
39 ynewsize = (ysize-minsize)/2
40 ynewsize = (ysize - minsize) / 2
40 # crop it
41 # crop it
41 image2 = image.crop((xnewsize, ynewsize, xsize-xnewsize, ysize-ynewsize))
42 image2 = image.crop(
43 (xnewsize, ynewsize, xsize - xnewsize, ysize - ynewsize))
42 # load is necessary after crop
44 # load is necessary after crop
43 image2.load()
45 image2.load()
44 # thumbnail of the cropped image (with ANTIALIAS to make it look better)
46 # thumbnail of the cropped image (with ANTIALIAS to make it look better)
@@ -47,66 +49,70 b' def generate_thumb(img, thumb_size, form'
47 # not quad
49 # not quad
48 image2 = image
50 image2 = image
49 image2.thumbnail(thumb_size, Image.ANTIALIAS)
51 image2.thumbnail(thumb_size, Image.ANTIALIAS)
50
52
51 io = cStringIO.StringIO()
53 io = cStringIO.StringIO()
52 # PNG and GIF are the same, JPG is JPEG
54 # PNG and GIF are the same, JPG is JPEG
53 if format.upper()=='JPG':
55 if format.upper() == 'JPG':
54 format = 'JPEG'
56 format = 'JPEG'
55
57
56 image2.save(io, format)
58 image2.save(io, format)
57 return ContentFile(io.getvalue())
59 return ContentFile(io.getvalue())
60
58
61
59 class ImageWithThumbsFieldFile(ImageFieldFile):
62 class ImageWithThumbsFieldFile(ImageFieldFile):
60 """
63 """
61 See ImageWithThumbsField for usage example
64 See ImageWithThumbsField for usage example
62 """
65 """
66
63 def __init__(self, *args, **kwargs):
67 def __init__(self, *args, **kwargs):
64 super(ImageWithThumbsFieldFile, self).__init__(*args, **kwargs)
68 super(ImageWithThumbsFieldFile, self).__init__(*args, **kwargs)
65 self.sizes = self.field.sizes
69 self.sizes = self.field.sizes
66
70
67 if self.sizes:
71 if self.sizes:
68 def get_size(self, size):
72 def get_size(self, size):
69 if not self:
73 if not self:
70 return ''
74 return ''
71 else:
75 else:
72 split = self.url.rsplit('.',1)
76 split = self.url.rsplit('.', 1)
73 thumb_url = '%s.%sx%s.%s' % (split[0],w,h,split[1])
77 thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
74 return thumb_url
78 return thumb_url
75
79
76 for size in self.sizes:
80 for size in self.sizes:
77 (w,h) = size
81 (w, h) = size
78 setattr(self, 'url_%sx%s' % (w,h), get_size(self, size))
82 setattr(self, 'url_%sx%s' % (w, h), get_size(self, size))
79
83
80 def save(self, name, content, save=True):
84 def save(self, name, content, save=True):
81 super(ImageWithThumbsFieldFile, self).save(name, content, save)
85 super(ImageWithThumbsFieldFile, self).save(name, content, save)
82
86
83 if self.sizes:
87 if self.sizes:
84 for size in self.sizes:
88 for size in self.sizes:
85 (w,h) = size
89 (w, h) = size
86 split = self.name.rsplit('.',1)
90 split = self.name.rsplit('.', 1)
87 thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
91 thumb_name = '%s.%sx%s.%s' % (split[0], w, h, split[1])
88
92
89 # you can use another thumbnailing function if you like
93 # you can use another thumbnailing function if you like
90 thumb_content = generate_thumb(content, size, split[1])
94 thumb_content = generate_thumb(content, size, split[1])
91
95
92 thumb_name_ = self.storage.save(thumb_name, thumb_content)
96 thumb_name_ = self.storage.save(thumb_name, thumb_content)
93
97
94 if not thumb_name == thumb_name_:
98 if not thumb_name == thumb_name_:
95 raise ValueError('There is already a file named %s' % thumb_name)
99 raise ValueError(
96
100 'There is already a file named %s' % thumb_name)
101
97 def delete(self, save=True):
102 def delete(self, save=True):
98 name=self.name
103 name = self.name
99 super(ImageWithThumbsFieldFile, self).delete(save)
104 super(ImageWithThumbsFieldFile, self).delete(save)
100 if self.sizes:
105 if self.sizes:
101 for size in self.sizes:
106 for size in self.sizes:
102 (w,h) = size
107 (w, h) = size
103 split = name.rsplit('.',1)
108 split = name.rsplit('.', 1)
104 thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
109 thumb_name = '%s.%sx%s.%s' % (split[0], w, h, split[1])
105 try:
110 try:
106 self.storage.delete(thumb_name)
111 self.storage.delete(thumb_name)
107 except:
112 except:
108 pass
113 pass
109
114
115
110 class ImageWithThumbsField(ImageField):
116 class ImageWithThumbsField(ImageField):
111 attr_class = ImageWithThumbsFieldFile
117 attr_class = ImageWithThumbsFieldFile
112 """
118 """
@@ -152,14 +158,16 b' class ImageWithThumbsField(ImageField):'
152
158
153
159
154 """
160 """
155 def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, sizes=None, **kwargs):
161
156 self.verbose_name=verbose_name
162 def __init__(self, verbose_name=None, name=None, width_field=None,
157 self.name=name
163 height_field=None, sizes=None, **kwargs):
158 self.width_field=width_field
164 self.verbose_name = verbose_name
159 self.height_field=height_field
165 self.name = name
166 self.width_field = width_field
167 self.height_field = height_field
160 self.sizes = sizes
168 self.sizes = sizes
161 super(ImageField, self).__init__(**kwargs)
169 super(ImageField, self).__init__(**kwargs)
162
170
163
171
164 from south.modelsinspector import add_introspection_rules
172 from south.modelsinspector import add_introspection_rules
165 add_introspection_rules([], ["^boards\.thumbs\.ImageWithThumbsField"]) No newline at end of file
173 add_introspection_rules([], ["^boards\.thumbs\.ImageWithThumbsField"])
@@ -14,22 +14,29 b" urlpatterns = patterns('',"
14 url(r'^page/(?P<page>\w+)/$', views.index, name='index'),
14 url(r'^page/(?P<page>\w+)/$', views.index, name='index'),
15
15
16 # login page
16 # login page
17 url(r'^login$', views.login, name='login'),
17 url(r'^login/$', views.login, name='login'),
18 # logout page
19 url(r'^logout$', views.logout, name='logout'),
20
18
21 # /boards/tag/tag_name/
19 # /boards/tag/tag_name/
22 url(r'^tag/(?P<tag_name>\w+)/$', views.tag, name='tag'),
20 url(r'^tag/(?P<tag_name>\w+)/$', views.tag, name='tag'),
23 # /boards/tag/tag_id/page/
21 # /boards/tag/tag_id/page/
24 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/$', views.tag, name='tag'),
22 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/$', views.tag, name='tag'),
23
24 # /boards/tag/tag_name/unsubscribe/
25 url(r'^tag/(?P<tag_name>\w+)/subscribe/$', views.tag_subscribe,
26 name='tag_subscribe'),
27 # /boards/tag/tag_name/unsubscribe/
28 url(r'^tag/(?P<tag_name>\w+)/unsubscribe/$', views.tag_unsubscribe,
29 name='tag_unsubscribe'),
30
25 # /boards/thread/
31 # /boards/thread/
26 url(r'^thread/(?P<post_id>\w+)/$', views.thread, name='thread'),
32 url(r'^thread/(?P<post_id>\w+)/$', views.thread, name='thread'),
27 # /boards/theme/theme_name/
33 # /boards/theme/theme_name/
28 url(r'^settings$', views.settings, name='settings'),
34 url(r'^settings/$', views.settings, name='settings'),
29 url(r'^tags$', views.all_tags, name='tags'),
35 url(r'^tags/$', views.all_tags, name='tags'),
30 url(r'^captcha/', include('captcha.urls')),
36 url(r'^captcha/', include('captcha.urls')),
31 url(r'^jump/(?P<post_id>\w+)/$', views.jump_to_post, name='jumper'),
37 url(r'^jump/(?P<post_id>\w+)/$', views.jump_to_post, name='jumper'),
32 url(r'^authors/$', views.authors, name='authors'),
38 url(r'^authors/$', views.authors, name='authors'),
39 url(r'^delete/(?P<post_id>\w+)/$', views.delete, name='delete'),
33 url(r'^banned/$', views.you_are_banned, name='banned'),
40 url(r'^banned/$', views.you_are_banned, name='banned'),
34
41
35 # RSS feeds
42 # RSS feeds
@@ -40,4 +47,4 b" urlpatterns = patterns('',"
40 url(r'^thread/(?P<post_id>\w+)/rss/$', ThreadPostsFeed()),
47 url(r'^thread/(?P<post_id>\w+)/rss/$', ThreadPostsFeed()),
41
48
42 url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
49 url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
43 ) No newline at end of file
50 )
@@ -1,15 +1,17 b''
1 import hashlib
1 from django.core.urlresolvers import reverse
2 from django.core.urlresolvers import reverse
2 from django.template import RequestContext
3 from django.template import RequestContext
3 from django.shortcuts import render, redirect, get_object_or_404
4 from django.shortcuts import render, redirect, get_object_or_404
4 from django.http import HttpResponseRedirect
5 from django.http import HttpResponseRedirect
6 from django.utils import timezone
5
7
6 from boards import forms
8 from boards import forms
7 import boards
9 import boards
8 from boards import utils
10 from boards import utils
9 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
11 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
10 ThreadCaptchaForm, PostCaptchaForm
12 ThreadCaptchaForm, PostCaptchaForm, LoginForm
11
13
12 from boards.models import Post, Admin, Tag, Ban
14 from boards.models import Post, Tag, Ban, User, RANK_USER, RANK_MODERATOR, NO_PARENT
13 from boards import authors
15 from boards import authors
14 import neboard
16 import neboard
15
17
@@ -148,51 +150,43 b' def thread(request, post_id):'
148
150
149
151
150 def login(request):
152 def login(request):
151 """Log in as admin"""
153 """Log in with user id"""
152
153 if 'name' in request.POST and 'password' in request.POST:
154 request.session['admin'] = False
155
154
156 isAdmin = len(Admin.objects.filter(name=request.POST['name'],
155 context = _init_default_context(request)
157 password=request.POST[
158 'password'])) > 0
159
160 if isAdmin:
161 request.session['admin'] = True
162
156
163 response = HttpResponseRedirect('/')
157 if request.method == 'POST':
164
158 form = LoginForm(request.POST, request.FILES, error_class=PlainErrorList)
165 else:
159 if form.is_valid():
166 response = render(request, 'boards/login.html', {'error': 'Login error'})
160 user = User.objects.get(user_id=form.cleaned_data['user_id'])
167 else:
161 request.session['user_id'] = user.id
168 response = render(request, 'boards/login.html', {})
162 return redirect(index)
169
163
170 return response
164 else:
171
165 form = LoginForm()
172
166
173 def logout(request):
167 context['form'] = form
174 request.session['admin'] = False
168
175 return HttpResponseRedirect('/')
169 return render(request, 'boards/login.html', context)
176
170
177
171
178 def settings(request):
172 def settings(request):
179 """User's settings"""
173 """User's settings"""
180
174
181 context = RequestContext(request)
175 context = _init_default_context(request)
182
176
183 if request.method == 'POST':
177 if request.method == 'POST':
184 form = SettingsForm(request.POST)
178 form = SettingsForm(request.POST)
185 if form.is_valid():
179 if form.is_valid():
186 selected_theme = form.cleaned_data['theme']
180 selected_theme = form.cleaned_data['theme']
187 request.session['theme'] = selected_theme
181
182 user = _get_user(request)
183 user.save_setting('theme', selected_theme)
188
184
189 return redirect(settings)
185 return redirect(settings)
190 else:
186 else:
191 selected_theme = _get_theme(request)
187 selected_theme = _get_theme(request)
192 form = SettingsForm(initial={'theme': selected_theme})
188 form = SettingsForm(initial={'theme': selected_theme})
193 context['form'] = form
189 context['form'] = form
194 context['tags'] = Tag.objects.get_popular_tags()
195 context['theme'] = _get_theme(request)
196
190
197 return render(request, 'boards/settings.html', context)
191 return render(request, 'boards/settings.html', context)
198
192
@@ -226,6 +220,19 b' def authors(request):'
226 return render(request, 'boards/authors.html', context)
220 return render(request, 'boards/authors.html', context)
227
221
228
222
223 def delete(request, post_id):
224 user = _get_user(request)
225 post = get_object_or_404(Post, id=post_id)
226
227 if user.is_moderator():
228 Post.objects.delete_post(post)
229
230 if NO_PARENT == post.parent:
231 return redirect(index)
232 else:
233 return redirect(thread, post_id=post.parent)
234
235
229 def you_are_banned(request):
236 def you_are_banned(request):
230 context = _init_default_context(request)
237 context = _init_default_context(request)
231 return render(request, 'boards/banned.html', context)
238 return render(request, 'boards/banned.html', context)
@@ -236,10 +243,35 b' def page_404(request):'
236 return render(request, 'boards/404.html', context)
243 return render(request, 'boards/404.html', context)
237
244
238
245
246 def tag_subscribe(request, tag_name):
247 user = _get_user(request)
248 tag = get_object_or_404(Tag, name=tag_name)
249
250 if not tag in user.fav_tags.all():
251 user.fav_tags.add(tag)
252
253 return redirect(all_tags)
254
255
256 def tag_unsubscribe(request, tag_name):
257 user = _get_user(request)
258 tag = get_object_or_404(Tag, name=tag_name)
259
260 if tag in user.fav_tags.all():
261 user.fav_tags.remove(tag)
262
263 return redirect(all_tags)
264
265
239 def _get_theme(request):
266 def _get_theme(request):
240 """Get user's CSS theme"""
267 """Get user's CSS theme"""
241
268
242 return request.session.get('theme', neboard.settings.DEFAULT_THEME)
269 user = _get_user(request)
270 theme = user.get_setting('theme')
271 if not theme:
272 theme = neboard.settings.DEFAULT_THEME
273
274 return theme
243
275
244
276
245 def _get_client_ip(request):
277 def _get_client_ip(request):
@@ -255,7 +287,34 b' def _init_default_context(request):'
255 """Create context with default values that are used in most views"""
287 """Create context with default values that are used in most views"""
256
288
257 context = RequestContext(request)
289 context = RequestContext(request)
258 context['tags'] = Tag.objects.get_popular_tags()
290 context['user'] = _get_user(request)
291 context['tags'] = _get_user(request).fav_tags.all()
259 context['theme'] = _get_theme(request)
292 context['theme'] = _get_theme(request)
260
293
261 return context
294 return context
295
296
297 def _get_user(request):
298 """Get current user from the session"""
299
300 session = request.session
301 if not 'user_id' in session:
302 request.session.save()
303
304 md5 = hashlib.md5()
305 md5.update(session.session_key)
306 new_id = md5.hexdigest()
307
308 time_now = timezone.now()
309 user = User.objects.create(user_id=new_id, rank=RANK_USER,
310 registration_time=time_now,
311 last_access_time=time_now)
312
313 session['user_id'] = user.id
314 else:
315 user = User.objects.get(id=session['user_id'])
316 user.save()
317
318 user.last_access_time = timezone.now()
319
320 return user
@@ -188,13 +188,14 b" SITE_NAME = 'Neboard'"
188
188
189 THEMES = [
189 THEMES = [
190 ('md', 'Mystic Dark'),
190 ('md', 'Mystic Dark'),
191 ('sw', 'Snow White') ]
191 ('sw', 'Snow White')
192 ]
192
193
193 DEFAULT_THEME = 'md'
194 DEFAULT_THEME = 'md'
194
195
195 POPULAR_TAGS = 10
196 POPULAR_TAGS = 10
196 LAST_REPLIES_COUNT = 3
197 LAST_REPLIES_COUNT = 3
197
198
198 ENABLE_CAPTCHA = True
199 ENABLE_CAPTCHA = False
199 # if user tries to post before CAPTCHA_DEFAULT_SAFE_TIME. Captcha will be shown
200 # if user tries to post before CAPTCHA_DEFAULT_SAFE_TIME. Captcha will be shown
200 CAPTCHA_DEFAULT_SAFE_TIME = 30 # seconds
201 CAPTCHA_DEFAULT_SAFE_TIME = 30 # seconds
General Comments 0
You need to be logged in to leave comments. Login now