##// END OF EJS Templates
Fixed some tests and views. Added migration for admin model removal.
neko259 -
r141:3edd554a 1.1
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,183 +1,177 b''
1 1 # coding=utf-8
2 2 from django.utils.unittest import TestCase
3 3 from django.test.client import Client
4 4
5 5 import boards
6 6
7 from boards.models import Post, Admin, Tag
7 from boards.models import Post, Tag
8 8 from neboard import settings
9 9
10 10 TEST_TEXT = 'test text'
11 11
12 12 NEW_THREAD_PAGE = '/'
13 13 THREAD_PAGE_ONE = '/thread/1/'
14 14 THREAD_PAGE = '/thread/'
15 15 TAG_PAGE = '/tag/'
16 16 HTTP_CODE_REDIRECT = 302
17 17 HTTP_CODE_OK = 200
18 18 HTTP_CODE_NOT_FOUND = 404
19 19
20 20
21 21 class BoardTests(TestCase):
22 22 def _create_post(self):
23 23 return Post.objects.create_post(title='title',
24 24 text='text')
25 25
26 26 def test_post_add(self):
27 27 post = self._create_post()
28 28
29 29 self.assertIsNotNone(post)
30 30 self.assertEqual(boards.models.NO_PARENT, post.parent)
31 31
32 32 def test_delete_post(self):
33 33 post = self._create_post()
34 34 post_id = post.id
35 35
36 36 Post.objects.delete_post(post)
37 37
38 38 self.assertFalse(Post.objects.exists(post_id))
39 39
40 40 def test_delete_posts_by_ip(self):
41 41 post = self._create_post()
42 42 post_id = post.id
43 43
44 44 Post.objects.delete_posts_by_ip('0.0.0.0')
45 45
46 46 self.assertFalse(Post.objects.exists(post_id))
47 47
48 48 # Authentication tests
49 49
50 50 def _create_test_user(self):
51 51 admin = Admin(name='test_username12313584353165',
52 52 password='test_userpassword135135512')
53 53
54 54 admin.save()
55 55 return admin
56 56
57 57 def test_get_thread(self):
58 58 opening_post = self._create_post()
59 59 op_id = opening_post.id
60 60
61 61 for i in range(0, 2):
62 62 Post.objects.create_post('title', 'text',
63 63 parent_id=op_id)
64 64
65 65 thread = Post.objects.get_thread(op_id)
66 66
67 67 self.assertEqual(3, len(thread))
68 68
69 69 def test_create_post_with_tag(self):
70 70 tag = Tag.objects.create(name='test_tag')
71 71 post = Post.objects.create_post(title='title', text='text', tags=[tag])
72 72 self.assertIsNotNone(post)
73 73
74 74 def test_thread_max_count(self):
75 75 for i in range(settings.MAX_THREAD_COUNT + 1):
76 76 self._create_post()
77 77
78 78 self.assertEqual(settings.MAX_THREAD_COUNT,
79 79 len(Post.objects.get_threads()))
80 80
81 def test_get(self):
82 """Test if the get computes properly"""
83
84 post = self._create_post()
85
86 self.assertTrue(post.is_get())
87
88 81 def test_pages(self):
89 82 """Test that the thread list is properly split into pages"""
90 83
91 84 for i in range(settings.MAX_THREAD_COUNT):
92 85 self._create_post()
93 86
94 87 all_threads = Post.objects.get_threads()
95 88
96 89 posts_in_second_page = Post.objects.get_threads(page=1)
97 90 first_post = posts_in_second_page[0]
98 91
99 92 self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id,
100 93 first_post.id)
101 94
102 95 def test_post_validation(self):
103 96 """Test the validation of the post form"""
104 97
105 98 # Disable captcha for the test
106 99 captcha_enabled = settings.ENABLE_CAPTCHA
107 100 settings.ENABLE_CAPTCHA = False
108 101
109 102 Post.objects.all().delete()
110 103
111 104 client = Client()
112 105
113 106 valid_tags = u'tag1 tag_2 Ρ‚Π΅Π³_3'
114 107 invalid_tags = u'$%_356 ---'
115 108
116 109 response = client.post(NEW_THREAD_PAGE, {'title': 'test title',
117 110 'text': TEST_TEXT,
118 111 'tags': valid_tags})
119 112 self.assertEqual(response.status_code, HTTP_CODE_REDIRECT,
120 113 msg='Posting new message failed: got code ' +
121 114 str(response.status_code))
122 115
123 116 self.assertEqual(1, Post.objects.count(),
124 117 msg='No posts were created')
125 118
126 119 client.post(NEW_THREAD_PAGE, {'text': TEST_TEXT,
127 120 'tags': invalid_tags})
128 121 self.assertEqual(1, Post.objects.count(), msg='The validation passed '
129 122 'where it should fail')
130 123
131 124 response = client.post(THREAD_PAGE_ONE, {'text': TEST_TEXT,
132 125 'tags': valid_tags})
133 126 self.assertEqual(HTTP_CODE_REDIRECT, response.status_code,
134 127 msg=u'Posting new message failed: got code ' +
135 128 str(response.status_code))
136 129
137 130 self.assertEqual(2, Post.objects.count(),
138 131 msg=u'No posts were created')
139 132
140 133 # Restore captcha setting
141 134 settings.ENABLE_CAPTCHA = captcha_enabled
142 135
143 136 def test_404(self):
144 137 """Test receiving error 404 when opening a non-existent page"""
145 138
146 139 Post.objects.all().delete()
147 140 Tag.objects.all().delete()
148 141
149 142 tag_name = u'test_tag'
150 143 tags, = [Tag.objects.get_or_create(name=tag_name)]
151 144 client = Client()
152 145
153 146 Post.objects.create_post('title', TEST_TEXT, tags=tags)
154 147
155 148 existing_post_id = Post.objects.all()[0].id
156 149 response_existing = client.get(THREAD_PAGE + str(existing_post_id) +
157 150 '/')
158 151 self.assertEqual(HTTP_CODE_OK, response_existing.status_code,
159 152 u'Cannot open existing thread')
160 153
161 154 response_not_existing = client.get(THREAD_PAGE + str(
162 155 existing_post_id + 1) + '/')
156 response_not_existing.get_full_path()
163 157 self.assertEqual(HTTP_CODE_NOT_FOUND,
164 158 response_not_existing.status_code,
165 159 u'Not existing thread is opened')
166 160
167 161 response_existing = client.get(TAG_PAGE + tag_name + '/')
168 162 self.assertEqual(HTTP_CODE_OK,
169 163 response_existing.status_code,
170 164 u'Cannot open existing tag')
171 165
172 166 response_not_existing = client.get(TAG_PAGE + u'not_tag' + '/')
173 167 self.assertEqual(HTTP_CODE_NOT_FOUND,
174 168 response_not_existing.status_code,
175 169 u'Not existing tag is opened')
176 170
177 171 reply_id = Post.objects.create_post('', TEST_TEXT,
178 172 parent_id=existing_post_id)
179 173 response_not_existing = client.get(THREAD_PAGE + str(
180 174 reply_id) + '/')
181 175 self.assertEqual(HTTP_CODE_NOT_FOUND,
182 176 response_not_existing.status_code,
183 177 u'Not existing thread is opened')
@@ -1,305 +1,307 b''
1 1 import hashlib
2 2 from django.core.urlresolvers import reverse
3 3 from django.template import RequestContext
4 4 from django.shortcuts import render, redirect, get_object_or_404
5 5 from django.http import HttpResponseRedirect
6 6 from django.utils import timezone
7 7
8 8 from boards import forms
9 9 import boards
10 10 from boards import utils
11 11 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
12 12 ThreadCaptchaForm, PostCaptchaForm
13 13
14 from boards.models import Post, Admin, Tag, Ban, User, RANK_USER, RANK_MODERATOR, NO_PARENT
14 from boards.models import Post, Tag, Ban, User, RANK_USER, RANK_MODERATOR, NO_PARENT
15 15 from boards import authors
16 16 import neboard
17 17
18 18
19 19 def index(request, page=0):
20 20 context = _init_default_context(request)
21 21
22 22 if utils.need_include_captcha(request):
23 23 threadFormClass = ThreadCaptchaForm
24 24 kwargs = {'request': request}
25 25 else:
26 26 threadFormClass = ThreadForm
27 27 kwargs = {}
28 28
29 29 if request.method == 'POST':
30 30 form = threadFormClass(request.POST, request.FILES,
31 31 error_class=PlainErrorList, **kwargs)
32 32
33 33 if form.is_valid():
34 34 return _new_post(request, form)
35 35 else:
36 36 form = threadFormClass(error_class=PlainErrorList, **kwargs)
37 37
38 38 threads = Post.objects.get_threads(page=int(page))
39 39
40 40 context['threads'] = None if len(threads) == 0 else threads
41 41 context['form'] = form
42 42 context['pages'] = range(Post.objects.get_thread_page_count())
43 43
44 44 return render(request, 'boards/posting_general.html',
45 45 context)
46 46
47 47
48 48 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
49 49 """Add a new post (in thread or as a reply)."""
50 50
51 51 ip = _get_client_ip(request)
52 52 is_banned = Ban.objects.filter(ip=ip).count() > 0
53 53
54 54 if is_banned:
55 55 return redirect(you_are_banned)
56 56
57 57 data = form.cleaned_data
58 58
59 59 title = data['title']
60 60 text = data['text']
61 61
62 62 if 'image' in data.keys():
63 63 image = data['image']
64 64 else:
65 65 image = None
66 66
67 67 tags = []
68 68
69 69 new_thread = thread_id == boards.models.NO_PARENT
70 70 if new_thread:
71 71 tag_strings = data['tags']
72 72
73 73 if tag_strings:
74 74 tag_strings = tag_strings.split(' ')
75 75 for tag_name in tag_strings:
76 76 tag_name = tag_name.strip()
77 77 if len(tag_name) > 0:
78 78 tag, created = Tag.objects.get_or_create(name=tag_name)
79 79 tags.append(tag)
80 80
81 81 # TODO Add a possibility to define a link image instead of an image file.
82 82 # If a link is given, download the image automatically.
83 83
84 84 post = Post.objects.create_post(title=title, text=text, ip=ip,
85 85 parent_id=thread_id, image=image,
86 86 tags=tags)
87 87
88 88 thread_to_show = (post.id if new_thread else thread_id)
89 89
90 90 if new_thread:
91 91 return redirect(thread, post_id=thread_to_show)
92 92 else:
93 93 return redirect(reverse(thread,
94 94 kwargs={'post_id': thread_to_show}) + '#'
95 95 + str(post.id))
96 96
97 97
98 98 def tag(request, tag_name, page=0):
99 99 """Get all tag threads (posts without a parent)."""
100 100
101 101 tag = get_object_or_404(Tag, name=tag_name)
102 102 threads = Post.objects.get_threads(tag=tag, page=int(page))
103 103
104 104 if request.method == 'POST':
105 105 form = ThreadForm(request.POST, request.FILES,
106 106 error_class=PlainErrorList)
107 107 if form.is_valid():
108 108 return _new_post(request, form)
109 109 else:
110 110 form = forms.ThreadForm(initial={'tags': tag_name},
111 111 error_class=PlainErrorList)
112 112
113 113 context = _init_default_context(request)
114 114 context['threads'] = None if len(threads) == 0 else threads
115 115 context['tag'] = tag_name
116 116 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
117 117
118 118 context['form'] = form
119 119
120 120 return render(request, 'boards/posting_general.html',
121 121 context)
122 122
123 123
124 124 def thread(request, post_id):
125 125 """Get all thread posts"""
126 126
127 127 if utils.need_include_captcha(request):
128 128 postFormClass = PostCaptchaForm
129 129 kwargs = {'request': request}
130 130 else:
131 131 postFormClass = PostForm
132 132 kwargs = {}
133 133
134 134 if request.method == 'POST':
135 135 form = postFormClass(request.POST, request.FILES,
136 136 error_class=PlainErrorList, **kwargs)
137 137 if form.is_valid():
138 138 return _new_post(request, form, post_id)
139 139 else:
140 140 form = postFormClass(error_class=PlainErrorList, **kwargs)
141 141
142 142 posts = Post.objects.get_thread(post_id)
143 143
144 144 context = _init_default_context(request)
145 145
146 146 context['posts'] = posts
147 147 context['form'] = form
148 148
149 149 return render(request, 'boards/thread.html', context)
150 150
151 151
152 152 def login(request):
153 153 """Log in as admin"""
154 154
155 155 if 'name' in request.POST and 'password' in request.POST:
156 156 request.session['admin'] = False
157 157
158 158 isAdmin = len(Admin.objects.filter(name=request.POST['name'],
159 159 password=request.POST[
160 160 'password'])) > 0
161 161
162 162 if isAdmin:
163 163 request.session['admin'] = True
164 164
165 165 response = HttpResponseRedirect('/')
166 166
167 167 else:
168 168 response = render(request, 'boards/login.html', {'error': 'Login error'})
169 169 else:
170 170 response = render(request, 'boards/login.html', {})
171 171
172 172 return response
173 173
174 174
175 175 def logout(request):
176 176 request.session['admin'] = False
177 177 return HttpResponseRedirect('/')
178 178
179 179
180 180 def settings(request):
181 181 """User's settings"""
182 182
183 183 context = _init_default_context(request)
184 184
185 185 if request.method == 'POST':
186 186 form = SettingsForm(request.POST)
187 187 if form.is_valid():
188 188 selected_theme = form.cleaned_data['theme']
189 189
190 190 user = _get_user(request)
191 191 user.save_setting('theme', selected_theme)
192 192
193 193 return redirect(settings)
194 194 else:
195 195 selected_theme = _get_theme(request)
196 196 form = SettingsForm(initial={'theme': selected_theme})
197 197 context['form'] = form
198 198
199 199 return render(request, 'boards/settings.html', context)
200 200
201 201
202 202 def all_tags(request):
203 203 """All tags list"""
204 204
205 205 context = _init_default_context(request)
206 206 context['all_tags'] = Tag.objects.get_not_empty_tags()
207 207
208 208 return render(request, 'boards/tags.html', context)
209 209
210 210
211 211 def jump_to_post(request, post_id):
212 212 """Determine thread in which the requested post is and open it's page"""
213 213
214 214 post = get_object_or_404(Post, id=post_id)
215 215
216 216 if boards.models.NO_PARENT == post.parent:
217 217 return redirect(thread, post_id=post.id)
218 218 else:
219 219 parent_thread = get_object_or_404(Post, id=post.parent)
220 220 return redirect(reverse(thread, kwargs={'post_id': parent_thread.id})
221 221 + '#' + str(post.id))
222 222
223 223
224 224 def authors(request):
225 225 context = _init_default_context(request)
226 226 context['authors'] = boards.authors.authors
227 227
228 228 return render(request, 'boards/authors.html', context)
229 229
230 230
231 231 def delete(request, post_id):
232 232 user = _get_user(request)
233 233 post = get_object_or_404(Post, id=post_id)
234 234
235 235 if user.is_moderator():
236 236 Post.objects.delete_post(post)
237 237
238 238 if NO_PARENT == post.parent:
239 239 return redirect(index)
240 240 else:
241 241 return redirect(thread, post_id=post.parent)
242 242
243 243 def you_are_banned(request):
244 244 context = _init_default_context(request)
245 245 return render(request, 'boards/banned.html', context)
246 246
247 247
248 248 def page_404(request):
249 249 context = _init_default_context(request)
250 250 return render(request, 'boards/404.html', context)
251 251
252 252
253 253 def _get_theme(request):
254 254 """Get user's CSS theme"""
255 255
256 256 user = _get_user(request)
257 257 theme = user.get_setting('theme')
258 258 if not theme:
259 259 theme = neboard.settings.DEFAULT_THEME
260 260
261 261 return theme
262 262
263 263
264 264 def _get_client_ip(request):
265 265 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
266 266 if x_forwarded_for:
267 267 ip = x_forwarded_for.split(',')[-1].strip()
268 268 else:
269 269 ip = request.META.get('REMOTE_ADDR')
270 270 return ip
271 271
272 272
273 273 def _init_default_context(request):
274 274 """Create context with default values that are used in most views"""
275 275
276 276 context = RequestContext(request)
277 277 context['tags'] = Tag.objects.get_popular_tags()
278 278 context['theme'] = _get_theme(request)
279 279 context['user'] = _get_user(request)
280 280
281 281 return context
282 282
283 283
284 284 def _get_user(request):
285 285 """Get current user from the session"""
286 286
287 287 session = request.session
288 288 if not 'user_id' in session:
289 289 request.session.save()
290 290
291 291 md5 = hashlib.md5()
292 292 md5.update(session.session_key)
293 293 new_id = md5.hexdigest()
294 294
295 time_now = timezone.now()
295 296 user = User.objects.create(user_id=new_id, rank=RANK_USER,
296 registration_time=timezone.now())
297 registration_time=time_now,
298 last_access_time=time_now)
297 299
298 300 session['user_id'] = user.id
299 301 else:
300 302 user = User.objects.get(id=session['user_id'])
301 303 user.save()
302 304
303 305 user.last_access_time = timezone.now()
304 306
305 307 return user
General Comments 0
You need to be logged in to leave comments. Login now