From 6fc604f832e41fbe744e8768fd2ed240b5c5571d Mon Sep 17 00:00:00 2001 From: nylar Date: Tue, 26 May 2015 23:14:31 +0100 Subject: [PATCH] Added login and logout views, more UI changes, added thread and post count to forum and thread respectively --- fora/mixins.py | 9 +++++ fora/settings.py | 7 +++- fora/static/css/main.scss | 41 +++++++++++++++++++- fora/tests/base.py | 12 ++++++ forums/models.py | 3 ++ forums/templates/forums/forum_detail.html | 6 ++- forums/templates/forums/forum_list.html | 2 + forums/tests/test_urls.py | 12 ++++++ forums/tests/test_views.py | 8 ++++ forums/views.py | 7 ++-- threads/models.py | 3 ++ threads/templates/threads/thread_detail.html | 2 +- threads/tests/test_models.py | 8 ++++ threads/tests/test_urls.py | 6 +++ threads/tests/test_views.py | 2 + threads/views.py | 9 ++++- users/templates/users/user_detail.html | 6 ++- users/templates/users/user_login.html | 10 +++++ users/tests/test_models.py | 6 +-- users/tests/test_urls.py | 16 ++++++-- users/tests/test_views.py | 29 +++++++++++++- users/urls.py | 14 ++++++- users/views.py | 30 +++++++++++++- 23 files changed, 226 insertions(+), 22 deletions(-) create mode 100644 fora/mixins.py create mode 100644 users/templates/users/user_login.html diff --git a/fora/mixins.py b/fora/mixins.py new file mode 100644 index 0000000..bde2335 --- /dev/null +++ b/fora/mixins.py @@ -0,0 +1,9 @@ +from django.contrib.auth.decorators import login_required +from django.utils.decorators import method_decorator + + +class LoginRequiredMixin(object): + + @method_decorator(login_required) + def dispatch(self, *args, **kwargs): + return super(LoginRequiredMixin, self).dispatch(*args, **kwargs) diff --git a/fora/settings.py b/fora/settings.py index d42095b..9dd885d 100644 --- a/fora/settings.py +++ b/fora/settings.py @@ -13,6 +13,9 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os +from django.core.urlresolvers import reverse_lazy + + BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -29,6 +32,8 @@ AUTH_USER_MODEL = "users.User" +LOGIN_URL = reverse_lazy('users:login') + # Application definition INSTALLED_APPS = ( @@ -41,10 +46,10 @@ 'compressor', + 'users', 'forums', 'threads', 'posts', - 'users', ) MIDDLEWARE_CLASSES = ( diff --git a/fora/static/css/main.scss b/fora/static/css/main.scss index b7736a8..5062ba8 100644 --- a/fora/static/css/main.scss +++ b/fora/static/css/main.scss @@ -9,9 +9,46 @@ input { line-height: 1.5; } +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +.pull-left { + float: left; +} + +.pull-right { + float: right; +} + +nav { + background: #546A76; + margin-bottom: 50px; + + ul { + li { + display: inline-block; + a { + color: #BFD4E7; + display: block; + padding: 21px 20px; + text-decoration: none; + + &:hover { + background: #3F535D; + } + } + } + } + @include clearfix; +} + .wrap { margin: 0 auto; - padding-top: 100px; width: 80%; } @@ -158,7 +195,7 @@ table { border: 1px solid #BFBFBF; a { - color: #007FFB; + color: #006BFA; font-weight: 600; text-decoration: none; diff --git a/fora/tests/base.py b/fora/tests/base.py index 2401674..057b9dd 100644 --- a/fora/tests/base.py +++ b/fora/tests/base.py @@ -7,9 +7,21 @@ class BaseTestCase(TestCase): + _password = 'secret' + + def setUp(self): + self.user = get_user_model().objects.create_user( + username='user', password=self._password) + def tearDown(self): get_user_model().objects.all().delete() Post.objects.all() Thread.objects.all().delete() Forum.objects.all().delete() super(BaseTestCase, self).tearDown() + + def _add_session_to_request(self, request): + from django.contrib.sessions.middleware import SessionMiddleware + middleware = SessionMiddleware() + middleware.process_request(request) + request.session.save() diff --git a/forums/models.py b/forums/models.py index f518daa..9242d13 100644 --- a/forums/models.py +++ b/forums/models.py @@ -33,5 +33,8 @@ def save(self, *args, **kwargs): def threads(self): return self.thread_set.all() + def thread_count(self): + return self.thread_set.count() + def moderator_list(self): return self.moderators.all() diff --git a/forums/templates/forums/forum_detail.html b/forums/templates/forums/forum_detail.html index 025ec41..5023de3 100644 --- a/forums/templates/forums/forum_detail.html +++ b/forums/templates/forums/forum_detail.html @@ -13,13 +13,17 @@

{{ forum.name }}

- + + + {% for thread in forum.threads %} + + {% empty %} diff --git a/forums/templates/forums/forum_list.html b/forums/templates/forums/forum_list.html index 6888623..b0d8ef7 100644 --- a/forums/templates/forums/forum_list.html +++ b/forums/templates/forums/forum_list.html @@ -9,6 +9,7 @@ + @@ -26,6 +27,7 @@ {% endif %} + {% empty %} diff --git a/forums/tests/test_urls.py b/forums/tests/test_urls.py index 20c0431..00c13e5 100644 --- a/forums/tests/test_urls.py +++ b/forums/tests/test_urls.py @@ -10,15 +10,23 @@ def setUp(self): super(ForumUrlsTestCase, self).setUp() self.client = Client() + def tearDown(self): + self.client.logout() + super(ForumUrlsTestCase, self).tearDown() + def test_index_url(self): response = self.client.get(reverse('forums:index')) self.assertEqual(response.status_code, 200) def test_new_url(self): + self.client.login( + username=self.user.username, password=self._password) response = self.client.get(reverse('forums:new')) self.assertEqual(response.status_code, 200) def test_update_url(self): + self.client.login( + username=self.user.username, password=self._password) f = Forum.objects.create(name='Test', description='Test', active=True) response = self.client.get( reverse('forums:update', kwargs={'slug': f.slug}) @@ -26,6 +34,8 @@ def test_update_url(self): self.assertEqual(response.status_code, 200) def test_update_url_not_found(self): + self.client.login( + username=self.user.username, password=self._password) f = Forum.objects.create(name='Test', description='Test', active=False) response = self.client.get( reverse('forums:update', kwargs={'slug': f.slug}) @@ -33,6 +43,8 @@ def test_update_url_not_found(self): self.assertEqual(response.status_code, 404) def test_visibility_url(self): + self.client.login( + username=self.user.username, password=self._password) f = Forum.objects.create( name='Visible', description='I am visible', diff --git a/forums/tests/test_views.py b/forums/tests/test_views.py index aff7c07..0f39a76 100644 --- a/forums/tests/test_views.py +++ b/forums/tests/test_views.py @@ -46,6 +46,7 @@ def setUp(self): def test_get_new_view(self): request = self.factory.get('/') + request.user = self.user response = NewForumView.as_view()(request) response.render() @@ -65,6 +66,7 @@ def test_post_form(self): 'name': 'My Forum', 'description': 'My forum containing my threads' }) + request.user = self.user response = NewForumView.as_view()(request) self.assertEqual(response.status_code, 302) @@ -72,6 +74,7 @@ def test_post_form(self): def test_post_form_invalid(self): request = self.factory.post(reverse('forums:new'), data={}) + request.user = self.user response = NewForumView.as_view()(request) response.render() @@ -94,6 +97,7 @@ def setUp(self): def test_get_update_view(self): request = self.factory.get('/') + request.user = self.user response = UpdateForumView.as_view()(request, slug=self.forum.slug) response.render() @@ -112,6 +116,7 @@ def test_post_form(self): 'name': 'Updated Forum', 'description': self.forum.description }) + request.user = self.user response = UpdateForumView.as_view()(request, slug=self.forum.slug) self.assertEqual(response.status_code, 302) @@ -119,6 +124,7 @@ def test_post_form(self): def test_post_form_invalid(self): request = self.factory.post('/', data={}) + request.user = self.user response = UpdateForumView.as_view()(request, slug=self.forum.slug) response.render() @@ -141,6 +147,7 @@ def setUp(self): def test_get_visibility_view(self): request = self.factory.get('/') + request.user = self.user response = ChangeForumVisibilityView.as_view()( request, slug=self.forum.slug @@ -157,6 +164,7 @@ def test_post_form(self): request = self.factory.post('/', data={ 'active': False, }) + request.user = self.user response = ChangeForumVisibilityView.as_view()( request, slug=self.forum.slug diff --git a/forums/views.py b/forums/views.py index cfa1ddd..8c1510f 100644 --- a/forums/views.py +++ b/forums/views.py @@ -3,6 +3,7 @@ from django.views.generic.detail import DetailView from django.views.generic.edit import CreateView, UpdateView from django.views.generic.list import ListView +from fora.mixins import LoginRequiredMixin from forums.models import Forum @@ -11,7 +12,7 @@ class ForumIndexView(ListView): model = Forum -class NewForumView(CreateView): +class NewForumView(LoginRequiredMixin, CreateView): model = Forum fields = ['name', 'description', 'active'] @@ -29,7 +30,7 @@ def get_object(self): raise Http404('No Forum matches the given query.') -class UpdateForumView(ForumMixin, UpdateView): +class UpdateForumView(LoginRequiredMixin, ForumMixin, UpdateView): context_object_name = 'forum' model = Forum fields = ['name', 'description'] @@ -38,7 +39,7 @@ def get_success_url(self): return reverse('forums:index') -class ChangeForumVisibilityView(UpdateView): +class ChangeForumVisibilityView(LoginRequiredMixin, UpdateView): context_object_name = 'forum' model = Forum fields = ['active'] diff --git a/threads/models.py b/threads/models.py index 9c806d3..1b24a84 100644 --- a/threads/models.py +++ b/threads/models.py @@ -30,6 +30,9 @@ def __unicode__(self): def posts(self): return self.post_set.all() + def post_count(self): + return self.post_set.count() + @receiver(post_save, sender=Thread) def generate_slug(sender, instance, created, **kwargs): diff --git a/threads/templates/threads/thread_detail.html b/threads/templates/threads/thread_detail.html index 5ba9b38..dc51e00 100644 --- a/threads/templates/threads/thread_detail.html +++ b/threads/templates/threads/thread_detail.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %} -

{{ thread.subject }}

+

{{ thread.subject }}

Forum: {{ thread.forum.name }}

{{ thread.created }}

{% endblock content %} \ No newline at end of file diff --git a/threads/tests/test_models.py b/threads/tests/test_models.py index 4cc2d29..94c1339 100644 --- a/threads/tests/test_models.py +++ b/threads/tests/test_models.py @@ -45,3 +45,11 @@ def test_posts(self): posts = self.thread.posts() self.assertEqual(len(posts), 2) self.assertEqual(list(posts), [p1, p2]) + + def test_post_count(self): + post_count = self.thread.post_count() + self.assertEqual(post_count, 0) + + Post.objects.create(message='p1', thread=self.thread) + post_count = self.thread.post_count() + self.assertEqual(post_count, 1) diff --git a/threads/tests/test_urls.py b/threads/tests/test_urls.py index a164709..2744154 100644 --- a/threads/tests/test_urls.py +++ b/threads/tests/test_urls.py @@ -11,7 +11,13 @@ def setUp(self): super(ThreadUrlsTestCase, self).setUp() self.client = Client() + def tearDown(self): + self.client.logout() + super(ThreadUrlsTestCase, self).tearDown() + def test_new_url(self): + self.client.login( + username=self.user.username, password=self._password) response = self.client.get(reverse('threads:new')) self.assertEqual(response.status_code, 200) diff --git a/threads/tests/test_views.py b/threads/tests/test_views.py index c0278ff..9b19751 100644 --- a/threads/tests/test_views.py +++ b/threads/tests/test_views.py @@ -14,6 +14,7 @@ def setUp(self): def test_get_new_view(self): request = self.factory.get('/') + request.user = self.user response = NewThreadView.as_view()(request) response.render() @@ -34,6 +35,7 @@ def test_post_form(self): 'subject': 'My thread', 'forum': forum.pk }) + request.user = self.user response = NewThreadView.as_view()(request) thread = Thread.objects.get(pk=1) diff --git a/threads/views.py b/threads/views.py index c6d41af..9cddd50 100644 --- a/threads/views.py +++ b/threads/views.py @@ -2,6 +2,7 @@ from django.http import Http404 from django.views.generic.detail import DetailView from django.views.generic.edit import CreateView +from fora.mixins import LoginRequiredMixin from threads.models import Thread @@ -15,11 +16,17 @@ def get_object(self): raise Http404('No Thread matches the given query.') -class NewThreadView(CreateView): +class NewThreadView(LoginRequiredMixin, CreateView): context_object_name = 'thread' model = Thread fields = ['subject', 'forum'] + def form_valid(self, form): + f = form.save(commit=False) + f.author = self.request.user + f.save() + return super(NewThreadView, self).form_valid(form) + def get_success_url(self): return reverse('threads:show', kwargs={'slug': self.object.slug}) diff --git a/users/templates/users/user_detail.html b/users/templates/users/user_detail.html index 6dd5606..9940134 100644 --- a/users/templates/users/user_detail.html +++ b/users/templates/users/user_detail.html @@ -1 +1,5 @@ -{{ user.username }} \ No newline at end of file +{% extends "base.html" %} + +{% block content %} +

{{ user.username }}

+{% endblock content %} diff --git a/users/templates/users/user_login.html b/users/templates/users/user_login.html new file mode 100644 index 0000000..dc38d14 --- /dev/null +++ b/users/templates/users/user_login.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} + +{% block content %} +

Sign In

+ + {% csrf_token %} + {{ form.as_p }} + + +{% endblock content %} \ No newline at end of file diff --git a/users/tests/test_models.py b/users/tests/test_models.py index 89d4cbc..99fea90 100644 --- a/users/tests/test_models.py +++ b/users/tests/test_models.py @@ -5,8 +5,6 @@ class UserModelTestCase(BaseTestCase): def setUp(self): - self.user = get_user_model().objects.create( - username='admin', password='') super(UserModelTestCase, self).setUp() def test_new_user(self): @@ -14,7 +12,7 @@ def test_new_user(self): self.assertEqual(get_user_model().objects.get(pk=1), self.user) def test_user_to_string(self): - self.assertEqual(str(self.user), "admin") + self.assertEqual(str(self.user), "user") def test_user_to_unicode(self): - self.assertEqual(unicode(self.user), u'admin') + self.assertEqual(unicode(self.user), u'user') diff --git a/users/tests/test_urls.py b/users/tests/test_urls.py index aa48f44..01aa1b6 100644 --- a/users/tests/test_urls.py +++ b/users/tests/test_urls.py @@ -1,4 +1,3 @@ -from django.contrib.auth import get_user_model from django.core.urlresolvers import reverse from django.test import Client from fora.tests.base import BaseTestCase @@ -9,13 +8,24 @@ class UserUrlsTestCase(BaseTestCase): def setUp(self): super(UserUrlsTestCase, self).setUp() self.client = Client() + self.client.login( + username=self.user.username, password=self.user.password) def test_user_profile_url(self): - u = get_user_model().objects.create(username='u', password='x') response = self.client.get( - reverse('users:profile', kwargs={'username': u.username})) + reverse('users:profile', kwargs={'username': self.user.username})) self.assertEqual(response.status_code, 200) def test_register_url(self): response = self.client.get(reverse('users:register')) self.assertEqual(response.status_code, 200) + + def test_login_url(self): + response = self.client.get(reverse('users:login')) + self.assertEqual(response.status_code, 200) + + def test_logout_url(self): + self.client.login( + username=self.user.username, password=self._password) + response = self.client.get(reverse('users:logout')) + self.assertEqual(response.status_code, 301) diff --git a/users/tests/test_views.py b/users/tests/test_views.py index 1bd8ffc..8a6636d 100644 --- a/users/tests/test_views.py +++ b/users/tests/test_views.py @@ -2,14 +2,13 @@ from django.contrib.auth import get_user_model from django.test import RequestFactory from fora.tests.base import BaseTestCase -from users.views import UserProfileView, UserRegisterView +from users.views import UserProfileView, UserRegisterView, UserLoginView class UserProfileViewTestCase(BaseTestCase): def setUp(self): self.factory = RequestFactory() - self.user = get_user_model().objects.create(username='a', password='a') super(UserProfileViewTestCase, self).setUp() def test_get_user_profile_view(self): @@ -51,3 +50,29 @@ def test_post_user_register_view(self): self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse( 'users:profile', kwargs={'username': u.username})) + + +class UserLoginViewTestCase(BaseTestCase): + + def setUp(self): + self.factory = RequestFactory() + super(UserLoginViewTestCase, self).setUp() + + def test_get_user_login_view(self): + request = self.factory.get('/') + response = UserLoginView.as_view()(request) + response.render() + + self.assertEqual(response.status_code, 200) + self.assertIn('
ThreadSubjectAuthorPosts
{{ thread.subject }}{{ thread.author.username }}{{ thread.post_count }}
Name DescriptionThreads
{{ forum.description }}{{ forum.thread_count }}