From 37839526ec0006e6886c3b3e460063d00a735a94 Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 4 Jun 2014 14:07:31 +0200 Subject: [PATCH 001/270] ajout de tests pour les formulaires de mp --- zds/mp/tests/__init__.py | 0 zds/mp/{tests.py => tests/tests.py.old} | 0 zds/mp/tests/tests_forms.py | 150 ++++++++++++++++++++++++ zds/mp/tests/tests_views.py | 0 4 files changed, 150 insertions(+) create mode 100644 zds/mp/tests/__init__.py rename zds/mp/{tests.py => tests/tests.py.old} (100%) create mode 100644 zds/mp/tests/tests_forms.py create mode 100644 zds/mp/tests/tests_views.py diff --git a/zds/mp/tests/__init__.py b/zds/mp/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/zds/mp/tests.py b/zds/mp/tests/tests.py.old similarity index 100% rename from zds/mp/tests.py rename to zds/mp/tests/tests.py.old diff --git a/zds/mp/tests/tests_forms.py b/zds/mp/tests/tests_forms.py new file mode 100644 index 0000000000..81a39db2c2 --- /dev/null +++ b/zds/mp/tests/tests_forms.py @@ -0,0 +1,150 @@ +# coding: utf-8 + +from django.test import TestCase + +from zds.member.factories import ProfileFactory, StaffFactory +from zds.mp.forms import PrivateTopicForm, PrivatePostForm +from zds.mp.factories import PrivateTopicFactory + + +class PrivateTopicFormTest(TestCase): + + def setUp(self): + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + self.staff1 = StaffFactory() + + def test_valid_topic_form(self): + data = { + 'participants': + self.profile1.user.username + + ',' + self.staff1.username, + 'title': 'Test title', + 'subtitle': 'Test subtitle', + 'text': 'blabla' + } + + form = PrivateTopicForm(data=data) + + self.assertTrue(form.is_valid()) + + def test_invalid_topic_form_user_notexist(self): + + data = { + 'participants': self.profile1.user.username + ', toto, tata', + 'title': 'Test title', + 'subtitle': 'Test subtitle', + 'text': 'blabla' + } + + form = PrivateTopicForm(data=data) + + self.assertFalse(form.is_valid()) + + def test_invalid_topic_form_no_participants(self): + + data = { + 'title': 'Test title', + 'subtitle': 'Test subtitle', + 'text': 'blabla' + } + + form = PrivateTopicForm(data=data) + + self.assertFalse(form.is_valid()) + + def test_invalid_topic_form_empty_participants(self): + + data = { + 'participants': ' ', + 'title': 'Test title', + 'subtitle': 'Test subtitle', + 'text': 'blabla' + } + + form = PrivateTopicForm(data=data) + + self.assertFalse(form.is_valid()) + + def test_invalid_topic_form_no_title(self): + + data = { + 'participants': self.profile1.user.username, + 'subtitle': 'Test subtitle', + 'text': 'blabla' + } + + form = PrivateTopicForm(data=data) + + self.assertFalse(form.is_valid()) + + def test_invalid_topic_form_empty_title(self): + + data = { + 'participants': self.profile1.user.username, + 'title': ' ', + 'subtitle': 'Test subtitle', + 'text': 'blabla' + } + + form = PrivateTopicForm(data=data) + + self.assertFalse(form.is_valid()) + + def test_invalid_topic_form_no_text(self): + + data = { + 'participants': self.profile1.user.username, + 'title': 'Test title', + 'subtitle': 'Test subtitle', + } + + form = PrivateTopicForm(data=data) + + self.assertFalse(form.is_valid()) + + def test_invalid_topic_form_empty_text(self): + + data = { + 'participants': self.profile1.user.username, + 'title': 'Test title', + 'subtitle': 'Test subtitle', + 'text': ' ' + } + + form = PrivateTopicForm(data=data) + + self.assertFalse(form.is_valid()) + + +class PrivatePostFormTest(TestCase): + + def setUp(self): + self.profile = ProfileFactory() + self.topic = PrivateTopicFactory(author=self.profile.user) + + def test_valid_form_post(self): + data = { + 'text': 'blabla' + } + + form = PrivatePostForm(self.topic, self.profile.user, data=data) + + self.assertTrue(form.is_valid()) + + def test_invalid_form_post_empty_text(self): + data = { + 'text': ' ' + } + + form = PrivatePostForm(self.topic, self.profile.user, data=data) + + self.assertFalse(form.is_valid()) + + def test_invalid_form_post_no_text(self): + data = { + } + + form = PrivatePostForm(self.topic, self.profile.user, data=data) + + self.assertFalse(form.is_valid()) diff --git a/zds/mp/tests/tests_views.py b/zds/mp/tests/tests_views.py new file mode 100644 index 0000000000..e69de29bb2 From f17a94ed392dc8bdd12fa99adcf9e1a3036c3ce4 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 5 Jun 2014 13:42:38 +0200 Subject: [PATCH 002/270] =?UTF-8?q?fonction=20get=5Fcurrent=5Fuser=20mise?= =?UTF-8?q?=20en=20param=C3=A8tre=20optionnel=20dans=20les=20mod=C3=A8les?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit car non utilisable dans les tests et ne change pas le reste du code --- zds/mp/models.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/zds/mp/models.py b/zds/mp/models.py index bf7b76e5b8..2f7e1c494d 100644 --- a/zds/mp/models.py +++ b/zds/mp/models.py @@ -62,12 +62,12 @@ def first_post(self): .order_by('pubdate')\ .first() - def last_read_post(self): + def last_read_post(self, user=get_current_user()): """Return the last private post the user has read.""" try: post = PrivateTopicRead.objects\ .select_related()\ - .filter(privatetopic=self, user=get_current_user()) + .filter(privatetopic=self, user=user) if len(post) == 0: return self.first_post() else: @@ -76,12 +76,12 @@ def last_read_post(self): except PrivatePost.DoesNotExist: return self.first_post() - def first_unread_post(self): + def first_unread_post(self, user=get_current_user()): """Return the first post the user has unread.""" try: last_post = PrivateTopicRead.objects\ .select_related()\ - .filter(privatetopic=self, user=get_current_user())\ + .filter(privatetopic=self, user=user)\ .latest('post__pubdate').privatepost next_post = PrivatePost.objects.filter( @@ -156,11 +156,9 @@ def __unicode__(self): self.privatepost.pk) -def never_privateread(privatetopic, user=None): +def never_privateread(privatetopic, user=get_current_user()): """Check if a private topic has been read by an user since it last post was added.""" - if user is None: - user = get_current_user() return PrivateTopicRead.objects\ .filter(privatepost=privatetopic.last_message, @@ -168,15 +166,15 @@ def never_privateread(privatetopic, user=None): .count() == 0 -def mark_read(privatetopic): +def mark_read(privatetopic, user=get_current_user()): """Mark a private topic as read for the user.""" PrivateTopicRead.objects.filter( privatetopic=privatetopic, - user=get_current_user()).delete() + user=user).delete() t = PrivateTopicRead( privatepost=privatetopic.last_message, privatetopic=privatetopic, - user=get_current_user()) + user=user) t.save() From 12b19809b6ee546423c64921ecb5b470e25fe044 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 5 Jun 2014 15:17:40 +0200 Subject: [PATCH 003/270] =?UTF-8?q?verification=20du=20user=20en=20param?= =?UTF-8?q?=C3=A8tre=20optionnel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit si None alors get_current_user --- zds/mp/models.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/zds/mp/models.py b/zds/mp/models.py index 2f7e1c494d..a668962323 100644 --- a/zds/mp/models.py +++ b/zds/mp/models.py @@ -62,8 +62,11 @@ def first_post(self): .order_by('pubdate')\ .first() - def last_read_post(self, user=get_current_user()): + def last_read_post(self, user=None): """Return the last private post the user has read.""" + if user is None: + user = get_current_user() + try: post = PrivateTopicRead.objects\ .select_related()\ @@ -76,8 +79,11 @@ def last_read_post(self, user=get_current_user()): except PrivatePost.DoesNotExist: return self.first_post() - def first_unread_post(self, user=get_current_user()): + def first_unread_post(self, user=None): """Return the first post the user has unread.""" + if user is None: + user = get_current_user() + try: last_post = PrivateTopicRead.objects\ .select_related()\ @@ -96,8 +102,11 @@ def alone(self): """Check if there just one participant in the conversation.""" return self.participants.count() == 0 - def never_read(self): - return never_privateread(self) + def never_read(self, user=None): + if user is None: + user = get_current_user() + + return never_privateread(self, user) class PrivatePost(models.Model): @@ -156,18 +165,25 @@ def __unicode__(self): self.privatepost.pk) -def never_privateread(privatetopic, user=get_current_user()): +def never_privateread(privatetopic, user=None): """Check if a private topic has been read by an user since it last post was added.""" + if user is None: + user = get_current_user() + return PrivateTopicRead.objects\ .filter(privatepost=privatetopic.last_message, privatetopic=privatetopic, user=user)\ .count() == 0 -def mark_read(privatetopic, user=get_current_user()): +def mark_read(privatetopic, user=None): """Mark a private topic as read for the user.""" + + if user is None: + user = get_current_user() + PrivateTopicRead.objects.filter( privatetopic=privatetopic, user=user).delete() From a73b995d6b53087320feceff2f4bee861c5c301f Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 5 Jun 2014 15:21:26 +0200 Subject: [PATCH 004/270] =?UTF-8?q?la=20requ=C3=AAte=20pour=20r=C3=A9cuper?= =?UTF-8?q?er=20le=20dernier=20message=20non=20lu=20renvoie=20une=20except?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit du coup la fonction qui devait renvoyer le premier message non lu, renvoyait le premier message du topic --- zds/mp/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zds/mp/models.py b/zds/mp/models.py index a668962323..bfda8979f7 100644 --- a/zds/mp/models.py +++ b/zds/mp/models.py @@ -88,7 +88,7 @@ def first_unread_post(self, user=None): last_post = PrivateTopicRead.objects\ .select_related()\ .filter(privatetopic=self, user=user)\ - .latest('post__pubdate').privatepost + .latest('privatepost__pubdate').privatepost next_post = PrivatePost.objects.filter( privatetopic__pk=self.pk, From 02a8cba8cced6198d53a316ba1237f8aae8eb106 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 5 Jun 2014 16:02:28 +0200 Subject: [PATCH 005/270] =?UTF-8?q?ajout=20des=20tests=20pour=20les=20mod?= =?UTF-8?q?=C3=A8les=20des=20mp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/mp/tests/tests_models.py | 231 +++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 zds/mp/tests/tests_models.py diff --git a/zds/mp/tests/tests_models.py b/zds/mp/tests/tests_models.py new file mode 100644 index 0000000000..5954ab8a74 --- /dev/null +++ b/zds/mp/tests/tests_models.py @@ -0,0 +1,231 @@ +# coding: utf-8 + +from django.test import TestCase +from django.core.urlresolvers import reverse +from math import ceil + +from zds.member.factories import ProfileFactory +from zds.mp.factories import PrivateTopicFactory, PrivatePostFactory +from zds.mp.models import mark_read, never_privateread +from zds.utils import slugify +from zds import settings + +# by moment, i wrote the scenario to be simpler + + +class PrivateTopicTest(TestCase): + + def setUp(self): + # scenario - topic1 : + # post1 - user1 - unread + # post2 - user2 - unread + + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + self.topic1 = PrivateTopicFactory(author=self.profile1.user) + self.topic1.participants.add(self.profile2.user) + self.post1 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile1.user, + position_in_topic=1) + + self.post2 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=2) + + def test_unicode(self): + self.assertEqual(self.topic1.__unicode__(), self.topic1.title) + + def test_absolute_url(self): + url = reverse( + 'zds.mp.views.topic', + args=[self.topic1.pk, slugify(self.topic1.title)]) + + self.assertEqual(self.topic1.get_absolute_url(), url) + + def test_post_count(self): + self.assertEqual(2, self.topic1.get_post_count()) + + def test_get_last_answer(self): + topic = PrivateTopicFactory(author=self.profile2.user) + PrivatePostFactory( + privatetopic=topic, + author=self.profile2.user, + position_in_topic=1) + + self.assertEqual(self.post2, self.topic1.get_last_answer()) + self.assertNotEqual(self.post1, self.topic1.get_last_answer()) + + self.assertIsNone(topic.get_last_answer()) + + def test_first_post(self): + topic = PrivateTopicFactory(author=self.profile2.user) + self.assertEqual(self.post1, self.topic1.first_post()) + self.assertIsNone(topic.first_post()) + + def test_last_read_post(self): + # scenario - topic1 : + # post1 - user1 - unread + # post2 - user2 - unread + self.assertEqual( + self.post1, + self.topic1.last_read_post(self.profile1.user)) + + # scenario - topic1 : + # post1 - user1 - read + # post2 - user2 - read + mark_read(self.topic1, user=self.profile1.user) + self.assertEqual( + self.post2, + self.topic1.last_read_post(self.profile1.user)) + + # scenario - topic1 : + # post1 - user1 - read + # post2 - user2 - read + # post3 - user2 - unread + PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=3) + self.assertEqual( + self.post2, + self.topic1.last_read_post(self.profile1.user)) + + def test_first_unread_post(self): + # scenario - topic1 : + # post1 - user1 - unread + # post2 - user2 - unread + self.assertEqual( + self.post1, + self.topic1.first_unread_post(self.profile1.user)) + + # scenario - topic1 : + # post1 - user1 - read + # post2 - user2 - read + # post3 - user2 - unread + mark_read(self.topic1, self.profile1.user) + post3 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=3) + + self.assertEqual( + post3, + self.topic1.first_unread_post(self.profile1.user)) + + def test_alone(self): + topic2 = PrivateTopicFactory(author=self.profile1.user) + self.assertFalse(self.topic1.alone()) + self.assertTrue(topic2.alone()) + + def test_never_read(self): + # scenario - topic1 : + # post1 - user1 - unread + # post2 - user2 - unread + self.assertTrue(self.topic1.never_read(self.profile1.user)) + + # scenario - topic1 : + # post1 - user1 - read + # post2 - user2 - read + mark_read(self.topic1, self.profile1.user) + self.assertFalse(self.topic1.never_read(self.profile1.user)) + + # scenario - topic1 : + # post1 - user1 - read + # post2 - user2 - read + # post3 - user2 - unread + PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=3) + + self.assertTrue(self.topic1.never_read(self.profile1.user)) + + +class PrivatePostTest(TestCase): + + def setUp(self): + # scenario - topic1 : + # post1 - user1 - unread + # post2 - user2 - unread + + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + self.topic1 = PrivateTopicFactory(author=self.profile1.user) + self.topic1.participants.add(self.profile2.user) + self.post1 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile1.user, + position_in_topic=1) + + self.post2 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=2) + + def test_unicode(self): + title = u''.format( + self.post1.privatetopic, + self.post1.pk) + self.assertEqual(title, self.post1.__unicode__()) + + def test_absolute_url(self): + page = int( + ceil( + float( + self.post1.position_in_topic) / + settings.POSTS_PER_PAGE)) + + url = '{0}?page={1}#p{2}'.format( + self.post1.privatetopic.get_absolute_url(), + page, + self.post1.pk) + + self.assertEqual(url, self.post1.get_absolute_url()) + + +class FunctionTest(TestCase): + + def setUp(self): + # scenario - topic1 : + # post1 - user1 - unread + # post2 - user2 - unread + + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + self.topic1 = PrivateTopicFactory(author=self.profile1.user) + self.topic1.participants.add(self.profile2.user) + self.post1 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile1.user, + position_in_topic=1) + + self.post2 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=2) + + def test_never_privateread(self): + self.assertTrue(never_privateread(self.topic1, self.profile1.user)) + mark_read(self.topic1, self.profile1.user) + self.assertFalse(never_privateread(self.topic1, self.profile1.user)) + + def test_mark_read(self): + self.assertTrue(self.topic1.never_read(self.profile1.user)) + + # scenario - topic1 : + # post1 - user1 - read + # post2 - user2 - read + mark_read(self.topic1, self.profile1.user) + self.assertFalse(self.topic1.never_read(self.profile1.user)) + + # scenario - topic1 : + # post1 - user1 - read + # post2 - user2 - read + # post3 - user2 - unread + PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=3) + self.assertTrue(self.topic1.never_read(self.profile1.user)) From 6ab8aee439d4becc21ba5d790b6af1b238a00931 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 6 Jun 2014 13:26:45 +0200 Subject: [PATCH 006/270] =?UTF-8?q?v=C3=A9rification=20qu'un=20utilisateur?= =?UTF-8?q?=20appartient=20bien=20=C3=A0=20un=20topic=20avant=20de=20suppr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/mp/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zds/mp/views.py b/zds/mp/views.py index 1eddd9fc52..c8fca118e9 100644 --- a/zds/mp/views.py +++ b/zds/mp/views.py @@ -37,7 +37,11 @@ def index(request): if request.method == 'POST': if 'delete' in request.POST: liste = request.POST.getlist('items') - topics = PrivateTopic.objects.filter(pk__in=liste).all() + topics = PrivateTopic.objects.filter(pk__in=liste)\ + .filter( + Q(participants__in=[request.user]) + | Q(author=request.user)) + for topic in topics: if topic.participants.all().count() == 0: topic.delete() From 6ea0fad5f29dbe8e07d4e34c40203f21612c2a7f Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 6 Jun 2014 16:37:50 +0200 Subject: [PATCH 007/270] ajout de tests pour les views mp --- zds/mp/tests/tests_views.py | 205 ++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/zds/mp/tests/tests_views.py b/zds/mp/tests/tests_views.py index e69de29bb2..5d3125dd84 100644 --- a/zds/mp/tests/tests_views.py +++ b/zds/mp/tests/tests_views.py @@ -0,0 +1,205 @@ +# coding: utf-8 + +import urllib + +from django.test import TestCase +from django.core.urlresolvers import reverse + +from zds.member.factories import ProfileFactory +from zds.mp.factories import PrivateTopicFactory, PrivatePostFactory +from zds.mp.models import PrivateTopic +from zds.utils import slugify + + +class IndexViewTest(TestCase): + + def setUp(self): + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + self.topic1 = PrivateTopicFactory(author=self.profile1.user) + self.topic1.participants.add(self.profile2.user) + self.post1 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile1.user, + position_in_topic=1) + + self.post2 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=2) + + def test_denies_anonymous(self): + response = self.client.get(reverse('zds.mp.views.index'), follow=True) + self.assertRedirects( + response, + reverse('zds.member.views.login_view') + + '?next=' + urllib.quote(reverse('zds.mp.views.index'), '')) + + def test_success_delete_topic_no_participants(self): + topic = PrivateTopicFactory(author=self.profile1.user) + login_check = self.client.login( + username=self.profile1.user.username, + password='hostel77' + ) + self.assertTrue(login_check) + self.assertEqual(1, PrivateTopic.objects.filter(pk=topic.pk).count()) + + response = self.client.post( + reverse('zds.mp.views.index'), + { + 'delete': '', + 'items': [topic.pk] + } + ) + + self.assertEqual(200, response.status_code) + self.assertEqual(0, PrivateTopic.objects.filter(pk=topic.pk).count()) + + def test_success_delete_topic_as_author(self): + + login_check = self.client.login( + username=self.profile1.user.username, + password='hostel77' + ) + self.assertTrue(login_check) + + response = self.client.post( + reverse('zds.mp.views.index'), + { + 'delete': '', + 'items': [self.topic1.pk] + } + ) + + self.assertEqual(200, response.status_code) + topic = PrivateTopic.objects.get(pk=self.topic1.pk) + self.assertEqual(self.profile2.user, topic.author) + self.assertNotIn(self.profile1.user, topic.participants.all()) + self.assertNotIn(self.profile2.user, topic.participants.all()) + + def test_success_delete_topic_as_participant(self): + + login_check = self.client.login( + username=self.profile2.user.username, + password='hostel77' + ) + self.assertTrue(login_check) + + response = self.client.post( + reverse('zds.mp.views.index'), + { + 'delete': '', + 'items': [self.topic1.pk] + } + ) + + self.assertEqual(200, response.status_code) + + topic = PrivateTopic.objects.get(pk=self.topic1.pk) + self.assertNotEqual(self.profile2.user, topic.author) + self.assertNotIn(self.profile1.user, topic.participants.all()) + self.assertNotIn(self.profile2.user, topic.participants.all()) + + def test_fail_delete_topic_not_belong_to_user(self): + topic = PrivateTopicFactory(author=self.profile1.user) + + self.assertEqual(1, PrivateTopic.objects.filter(pk=topic.pk).count()) + + login_check = self.client.login( + username=self.profile2.user.username, + password='hostel77' + ) + self.assertTrue(login_check) + + self.client.post( + reverse('zds.mp.views.index'), + { + 'delete': '', + 'items': [topic.pk] + } + ) + + self.assertEqual(1, PrivateTopic.objects.filter(pk=topic.pk).count()) + + +class TopicViewTest(TestCase): + + def setUp(self): + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + self.topic1 = PrivateTopicFactory(author=self.profile1.user) + self.topic1.participants.add(self.profile2.user) + self.post1 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile1.user, + position_in_topic=1) + + self.post2 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=2) + + def test_denies_anonymous(self): + response = self.client.get( + reverse( + 'zds.mp.views.topic', + args=[self.topic1.pk, slugify(self.topic1.title)]), + follow=True) + self.assertRedirects( + response, + reverse('zds.member.views.login_view') + + '?next=' + urllib.quote(reverse( + 'zds.mp.views.topic', + args=[self.topic1.pk, slugify(self.topic1.title)]), '')) + + def test_fail_topic_no_exist(self): + + login_check = self.client.login( + username=self.profile1.user.username, + password='hostel77' + ) + self.assertTrue(login_check) + + response = self.client.get(reverse( + 'zds.mp.views.topic', + args=[12, 'test'])) + self.assertEqual(404, response.status_code) + + def test_fail_topic_no_permission(self): + topic = PrivateTopicFactory(author=self.profile1.user) + + login_check = self.client.login( + username=self.profile2.user.username, + password='hostel77' + ) + self.assertTrue(login_check) + + response = self.client.get(reverse( + 'zds.mp.views.topic', + args=[topic.pk, 'test']), + follow=True + ) + + self.assertEqual(403, response.status_code) + + def test_fail_topic_slug(self): + login_check = self.client.login( + username=self.profile1.user.username, + password='hostel77' + ) + self.assertTrue(login_check) + + self.client.get(reverse( + 'zds.mp.views.topic', + args=[self.topic1.pk, 'test']), + follow=True + ) + + self.assertRedirects( + reverse( + 'zds.mp.views.topic', + args=[self.topic1.pk, 'test']), + reverse( + 'zds.mp.views.topic', + args=[self.topic1.pk, slugify(self.topic1.title)]), + ) From a388e664ec6d6151165991d03d4bb4cea8ca5fda Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 13 Jun 2014 14:31:19 +0200 Subject: [PATCH 008/270] empeche un utilisateur de s'envoyer un mp tout seul --- zds/mp/views.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/zds/mp/views.py b/zds/mp/views.py index c8fca118e9..1e5690a9e0 100644 --- a/zds/mp/views.py +++ b/zds/mp/views.py @@ -17,6 +17,7 @@ from django.template import Context from django.template.loader import get_template from django.views.decorators.http import require_POST +from django.forms.util import ErrorList from zds.utils import render_template, slugify from zds.utils.mps import send_mp @@ -177,6 +178,16 @@ def new(request): continue ctrl.append(p) + # user add only himself + if (len(ctrl) < 1 + and len(list_part) == 1 + and list_part[0] == request.user.username): + errors = form._errors.setdefault("participants", ErrorList()) + errors.append(u'Vous êtes déjà auteur du message') + return render_template('mp/topic/new.html', { + 'form': form, + }) + p_topic = send_mp(request.user, ctrl, data['title'], From 0b54f624e43187b1628e26e15a3acc4659621a44 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 13 Jun 2014 16:30:18 +0200 Subject: [PATCH 009/270] ajout de tests pour les views --- zds/mp/tests/tests_views.py | 257 +++++++++++++++++++++++++++++++++++- 1 file changed, 253 insertions(+), 4 deletions(-) diff --git a/zds/mp/tests/tests_views.py b/zds/mp/tests/tests_views.py index 5d3125dd84..ff238b50e1 100644 --- a/zds/mp/tests/tests_views.py +++ b/zds/mp/tests/tests_views.py @@ -189,17 +189,266 @@ def test_fail_topic_slug(self): ) self.assertTrue(login_check) - self.client.get(reverse( + response = self.client.get(reverse( 'zds.mp.views.topic', args=[self.topic1.pk, 'test']), follow=True ) self.assertRedirects( - reverse( - 'zds.mp.views.topic', - args=[self.topic1.pk, 'test']), + response, reverse( 'zds.mp.views.topic', args=[self.topic1.pk, slugify(self.topic1.title)]), ) + + +class NewTopicViewTest(TestCase): + + def setUp(self): + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + + login_check = self.client.login( + username=self.profile1.user.username, + password='hostel77' + ) + self.assertTrue(login_check) + + def test_denies_anonymous(self): + + self.client.logout() + response = self.client.get(reverse('zds.mp.views.new'), follow=True) + + self.assertRedirects( + response, + reverse('zds.member.views.login_view') + + '?next=' + urllib.quote(reverse('zds.mp.views.new'), '')) + + def test_success_get_with_and_without_username(self): + + response = self.client.get(reverse('zds.mp.views.new')) + + self.assertEqual(200, response.status_code) + self.assertIsNone( + response.context['form'].initial['participants']) + + response2 = self.client.get( + reverse('zds.mp.views.new') + + '?username=' + self.profile2.user.username) + + self.assertEqual(200, response2.status_code) + self.assertEqual( + self.profile2.user.username, + response2.context['form'].initial['participants']) + + def test_fail_get_with_username_not_exist(self): + + response2 = self.client.get( + reverse('zds.mp.views.new') + + '?username=wrongusername') + + self.assertEqual(200, response2.status_code) + self.assertIsNone( + response2.context['form'].initial['participants']) + + def test_success_preview(self): + + self.assertEqual(0, PrivateTopic.objects.all().count()) + response = self.client.post( + reverse('zds.mp.views.new'), + { + 'preview': '', + 'participants': self.profile2.user.username, + 'title': 'title', + 'subtitle': 'subtitle', + 'text': 'text' + } + ) + + self.assertEqual(200, response.status_code) + self.assertEqual(0, PrivateTopic.objects.all().count()) + + def test_fail_new_topic_user_no_exist(self): + + self.assertEqual(0, PrivateTopic.objects.all().count()) + response = self.client.post( + reverse('zds.mp.views.new'), + { + 'participants': 'wronguser', + 'title': 'title', + 'subtitle': 'subtitle', + 'text': 'text' + } + ) + + self.assertEqual(200, response.status_code) + self.assertEqual(0, PrivateTopic.objects.all().count()) + + def test_success_new_topic(self): + + self.assertEqual(0, PrivateTopic.objects.all().count()) + response = self.client.post( + reverse('zds.mp.views.new'), + { + 'participants': self.profile2.user.username, + 'title': 'title', + 'subtitle': 'subtitle', + 'text': 'text' + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + self.assertEqual(1, PrivateTopic.objects.all().count()) + + def test_fail_new_topic_user_add_only_himself(self): + + self.assertEqual(0, PrivateTopic.objects.all().count()) + response = self.client.post( + reverse('zds.mp.views.new'), + { + 'participants': self.profile1.user.username, + 'title': 'title', + 'subtitle': 'subtitle', + 'text': 'text' + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + self.assertEqual(0, PrivateTopic.objects.all().count()) + + def test_fail_new_topic_user_add_himself_and_others(self): + + self.assertEqual(0, PrivateTopic.objects.all().count()) + + participants = self.profile1.user.username\ + + ',' + self.profile2.user.username + + response = self.client.post( + reverse('zds.mp.views.new'), + { + 'participants': participants, + 'title': 'title', + 'subtitle': 'subtitle', + 'text': 'text' + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + self.assertEqual(1, PrivateTopic.objects.all().count()) + self.assertNotIn( + self.profile1.user, + PrivateTopic.objects.all()[0].participants.all() + ) + + +class EditViewTest(TestCase): + + def setUp(self): + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + self.profile3 = ProfileFactory() + + self.topic1 = PrivateTopicFactory(author=self.profile1.user) + self.topic1.participants.add(self.profile2.user) + self.post1 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile1.user, + position_in_topic=1) + + self.post2 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=2) + + login_check = self.client.login( + username=self.profile1.user.username, + password='hostel77' + ) + self.assertTrue(login_check) + + def test_denies_anonymous(self): + + self.client.logout() + response = self.client.get(reverse('zds.mp.views.edit'), follow=True) + + self.assertRedirects( + response, + reverse('zds.member.views.login_view') + + '?next=' + urllib.quote(reverse('zds.mp.views.edit'), '')) + + def test_fail_edit_topic_not_sending_topic_pk(self): + + response = self.client.post(reverse('zds.mp.views.edit')) + + self.assertEqual(404, response.status_code) + + def test_fail_edit_topic_no_exist(self): + + response = self.client.post( + reverse('zds.mp.views.edit'), + { + 'privatetopic': 156 + } + ) + + self.assertEqual(404, response.status_code) + + def test_fail_edit_topic_add_no_exist_user(self): + + response = self.client.post( + reverse('zds.mp.views.edit'), + { + 'privatetopic': self.topic1.pk, + 'username': 'wrongusername' + } + ) + + self.assertEqual(404, response.status_code) + + def test_success_edit_topic_add_participant(self): + + response = self.client.post( + reverse('zds.mp.views.edit'), + { + 'privatetopic': self.topic1.pk, + 'username': self.profile3.user.username + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + topic = PrivateTopic.objects.get(pk=self.topic1.pk) + self.assertIn( + self.profile3.user, + topic.participants.all() + ) + + def test_fail_user_add_himself_to_private_topic_with_no_right(self): + + self.client.logout() + self.assertTrue( + self.client.login( + username=self.profile3.user.username, + password='hostel77' + ) + ) + + response = self.client.post( + reverse('zds.mp.views.edit'), + { + 'privatetopic': self.topic1.pk, + 'username': self.profile3.user.username + }, + follow=True + ) + + #self.assertEqual(403, response.status_code) + topic = PrivateTopic.objects.get(pk=self.topic1.pk) + self.assertNotIn( + self.profile3.user, + topic.participants.all() + ) From a2970b271f7d3c2ceb0d05a0722cffff5d3aaf33 Mon Sep 17 00:00:00 2001 From: Geoffrey ROYER Date: Mon, 7 Jul 2014 15:33:13 +0200 Subject: [PATCH 010/270] More information into confirmation mails --- templates/email/register/confirm.html | 15 +++++++++------ templates/email/register/confirm.txt | 7 ++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/templates/email/register/confirm.html b/templates/email/register/confirm.html index 957d5fab25..aa71ea6afd 100644 --- a/templates/email/register/confirm.html +++ b/templates/email/register/confirm.html @@ -7,12 +7,15 @@

- Merci de votre inscription sur Zeste de Savoir. Pour activer votre profil, cliquez sur le lien ci-dessous : + + Vous vous êtes inscrit sur Zeste de Savoir et nous vous en remercions. + En étant membre de notre communauté, vous aurez la possibilité de rédiger des tutoriels et des articles + que vous pourrez ensuite publier et ainsi rendre vos connaissances accessibles au plus grand nombre. + Vous pourrez également échanger avec les autres membres sur nos forums !
+ Il ne vous reste plus qu'à activer votre profil ! Pour ce faire, visitez le lien ci-dessous : {{ url }} +
+ L'équipe Zeste de Savoir

-
- Cordialement, -
- L'équipe Zeste de Savoir - \ No newline at end of file + diff --git a/templates/email/register/confirm.txt b/templates/email/register/confirm.txt index d5a52a1a1b..40ca7478c9 100644 --- a/templates/email/register/confirm.txt +++ b/templates/email/register/confirm.txt @@ -1,8 +1,9 @@ Bonjour {{ username }}, -Merci de votre inscription sur Zeste de Savoir. Pour activer votre profil, cliquez ou recopiez le lien ci-dessous : +Vous vous êtes inscrit sur Zeste de Savoir et nous vous en remercions. En étant membre de notre communauté, vous aurez la possibilité de rédiger des tutoriels et des articles que vous pourrez ensuite publier et ainsi rendre votre savoir accessible au plus grand nombre. Vous pourrez également échanger avec les autres membres sur nos forums ! + +Il ne vous reste plus qu'à activer votre profil ! Pour ce faire, visitez le lien ci-dessous : {{ url }} -Cordialement, -L'équipe Zeste de Savoir \ No newline at end of file +L'équipe Zeste de Savoir From 37e6dd64bbe0107b5707274614e0ab5665fca057 Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 7 Jul 2014 16:57:28 +0200 Subject: [PATCH 011/270] ajout de tests pour les vues answer, edit_post, leave et add_participants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit plus correction des bugs trouvés --- zds/mp/tests/tests_views.py | 510 +++++++++++++++++++++++++++++++++++- zds/mp/views.py | 25 +- 2 files changed, 521 insertions(+), 14 deletions(-) diff --git a/zds/mp/tests/tests_views.py b/zds/mp/tests/tests_views.py index ff238b50e1..eb1a2a36dc 100644 --- a/zds/mp/tests/tests_views.py +++ b/zds/mp/tests/tests_views.py @@ -7,7 +7,7 @@ from zds.member.factories import ProfileFactory from zds.mp.factories import PrivateTopicFactory, PrivatePostFactory -from zds.mp.models import PrivateTopic +from zds.mp.models import PrivateTopic, PrivatePost from zds.utils import slugify @@ -437,7 +437,7 @@ def test_fail_user_add_himself_to_private_topic_with_no_right(self): ) ) - response = self.client.post( + self.client.post( reverse('zds.mp.views.edit'), { 'privatetopic': self.topic1.pk, @@ -446,9 +446,513 @@ def test_fail_user_add_himself_to_private_topic_with_no_right(self): follow=True ) - #self.assertEqual(403, response.status_code) + # self.assertEqual(403, response.status_code) topic = PrivateTopic.objects.get(pk=self.topic1.pk) self.assertNotIn( self.profile3.user, topic.participants.all() ) + + +class AnswerViewTest(TestCase): + + def setUp(self): + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + self.profile3 = ProfileFactory() + + self.topic1 = PrivateTopicFactory(author=self.profile1.user) + self.topic1.participants.add(self.profile2.user) + self.post1 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile1.user, + position_in_topic=1) + + self.post2 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=2) + + self.assertTrue( + self.client.login( + username=self.profile1.user.username, + password='hostel77' + ) + ) + + def test_denies_anonymous(self): + + self.client.logout() + response = self.client.get(reverse('zds.mp.views.answer'), follow=True) + + self.assertRedirects( + response, + reverse('zds.member.views.login_view') + + '?next=' + urllib.quote(reverse('zds.mp.views.answer'), '')) + + def test_fail_answer_not_send_topic_pk(self): + + response = self.client.post( + reverse('zds.mp.views.answer'), + {} + ) + + self.assertEqual(404, response.status_code) + + def test_fail_answer_topic_no_exist(self): + + response = self.client.post( + reverse('zds.mp.views.answer') + '?sujet=156', + {} + ) + + self.assertEqual(404, response.status_code) + + def test_fail_cite_post_no_exist(self): + + response = self.client.get( + reverse('zds.mp.views.answer') + + '?sujet='+str(self.topic1.pk) + + '&cite=4864', + {} + ) + + self.assertEqual(404, response.status_code) + + def test_success_cite_post(self): + + response = self.client.get( + reverse('zds.mp.views.answer') + + '?sujet='+str(self.topic1.pk) + + '&cite='+str(self.post1.pk), + {} + ) + + self.assertEqual(200, response.status_code) + + def test_success_preview_answer(self): + + response = self.client.post( + reverse('zds.mp.views.answer') + + '?sujet='+str(self.topic1.pk), + { + 'text': 'answer', + 'preview': '', + 'last_post': self.topic1.get_last_answer().pk + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + + def test_success_answer(self): + + response = self.client.post( + reverse('zds.mp.views.answer') + + '?sujet='+str(self.topic1.pk), + { + 'text': 'answer', + 'last_post': self.topic1.get_last_answer().pk + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + self.assertEqual(3, PrivatePost.objects.all().count()) + + # TODO test mail notification + + def test_fail_answer_with_no_right(self): + + self.client.logout() + self.assertTrue( + self.client.login( + username=self.profile3.user.username, + password='hostel77' + ) + ) + + response = self.client.post( + reverse('zds.mp.views.answer') + + '?sujet='+str(self.topic1.pk), + { + 'text': 'answer', + 'last_post': self.topic1.get_last_answer().pk + }, + follow=True + ) + + self.assertEqual(403, response.status_code) + self.assertEqual(2, PrivatePost.objects.all().count()) + + +class EditPostViewTest(TestCase): + + def setUp(self): + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + + self.topic1 = PrivateTopicFactory(author=self.profile1.user) + self.topic1.participants.add(self.profile2.user) + self.post1 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile1.user, + position_in_topic=1) + + self.post2 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=2) + + self.assertTrue( + self.client.login( + username=self.profile1.user.username, + password='hostel77' + ) + ) + + def test_denies_anonymous(self): + + self.client.logout() + response = self.client.get(reverse('zds.mp.views.edit_post'), follow=True) + + self.assertRedirects( + response, + reverse('zds.member.views.login_view') + + '?next=' + urllib.quote(reverse('zds.mp.views.edit_post'), '')) + + def test_succes_get_edit_post_page(self): + + self.client.logout() + self.assertTrue( + self.client.login( + username=self.profile2.user.username, + password='hostel77' + ) + ) + + response = self.client.get( + reverse('zds.mp.views.edit_post') + + '?message='+str(self.post2.pk) + ) + + self.assertEqual(200, response.status_code) + + def test_fail_edit_post_no_exist(self): + + response = self.client.get( + reverse('zds.mp.views.edit_post') + + '?message=154' + ) + + self.assertEqual(404, response.status_code) + + def test_fail_edit_post_not_last(self): + + response = self.client.get( + reverse('zds.mp.views.edit_post') + + '?message='+str(self.post1.pk) + ) + + self.assertEqual(403, response.status_code) + + def test_fail_edit_post_with_no_right(self): + + response = self.client.get( + reverse('zds.mp.views.edit_post') + + '?message='+str(self.post2.pk) + ) + + self.assertEqual(403, response.status_code) + + def test_success_edit_post_preview(self): + + self.client.logout() + self.assertTrue( + self.client.login( + username=self.profile2.user.username, + password='hostel77' + ) + ) + + response = self.client.post( + reverse('zds.mp.views.edit_post') + + '?message='+str(self.post2.pk), + { + 'text': 'update post', + 'preview': '' + } + ) + + self.assertEqual(200, response.status_code) + self.assertEqual( + 'update post', + response.context['form'].initial['text'] + ) + + def test_success_edit_post(self): + + self.client.logout() + self.assertTrue( + self.client.login( + username=self.profile2.user.username, + password='hostel77' + ) + ) + + response = self.client.post( + reverse('zds.mp.views.edit_post') + + '?message='+str(self.post2.pk), + { + 'text': 'update post', + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + self.assertEqual( + 'update post', + PrivatePost.objects.get(pk=self.post2.pk).text + ) + + +class LeaveViewTest(TestCase): + + def setUp(self): + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + + self.topic1 = PrivateTopicFactory(author=self.profile1.user) + self.topic1.participants.add(self.profile2.user) + self.post1 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile1.user, + position_in_topic=1) + + self.post2 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=2) + + self.assertTrue( + self.client.login( + username=self.profile1.user.username, + password='hostel77' + ) + ) + + def test_denies_anonymous(self): + + self.client.logout() + response = self.client.get(reverse('zds.mp.views.leave'), follow=True) + + self.assertRedirects( + response, + reverse('zds.member.views.login_view') + + '?next=' + urllib.quote(reverse('zds.mp.views.leave'), '')) + + def test_fail_leave_topic_no_exist(self): + + response = self.client.post( + reverse('zds.mp.views.leave'), + { + 'leave': '', + 'topic_pk': '154' + } + ) + + self.assertEqual(404, response.status_code) + + def test_success_leave_topic_as_author_no_participants(self): + + self.topic1.participants.remove(self.profile2) + self.topic1.save() + + response = self.client.post( + reverse('zds.mp.views.leave'), + { + 'leave': '', + 'topic_pk': self.topic1.pk + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + self.assertEqual( + 0, + PrivateTopic.objects.all().count() + ) + + def test_success_leave_topic_as_author(self): + + response = self.client.post( + reverse('zds.mp.views.leave'), + { + 'leave': '', + 'topic_pk': self.topic1.pk + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + self.assertEqual( + 1, + PrivateTopic.objects.all().count() + ) + + self.assertEqual( + self.profile2.user, + PrivateTopic.objects.get(pk=self.topic1.pk).author + ) + + def test_success_leave_topic_as_participant(self): + + self.client.logout() + self.assertTrue( + self.client.login( + username=self.profile2.user.username, + password='hostel77' + ) + ) + + response = self.client.post( + reverse('zds.mp.views.leave'), + { + 'leave': '', + 'topic_pk': self.topic1.pk + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + + self.assertNotIn( + self.profile2.user, + PrivateTopic.objects.get(pk=self.topic1.pk).participants.all() + ) + + self.assertNotEqual( + self.profile2.user, + PrivateTopic.objects.get(pk=self.topic1.pk).author + ) + + +class AddParticipantViewTest(TestCase): + + def setUp(self): + self.profile1 = ProfileFactory() + self.profile2 = ProfileFactory() + + self.topic1 = PrivateTopicFactory(author=self.profile1.user) + self.topic1.participants.add(self.profile2.user) + self.post1 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile1.user, + position_in_topic=1) + + self.post2 = PrivatePostFactory( + privatetopic=self.topic1, + author=self.profile2.user, + position_in_topic=2) + + self.assertTrue( + self.client.login( + username=self.profile1.user.username, + password='hostel77' + ) + ) + + def test_denies_anonymous(self): + + self.client.logout() + response = self.client.get(reverse('zds.mp.views.add_participant'), follow=True) + + self.assertRedirects( + response, + reverse('zds.member.views.login_view') + + '?next=' + urllib.quote(reverse('zds.mp.views.add_participant'), '')) + + def test_fail_add_participant_topic_no_exist(self): + + response = self.client.post( + reverse('zds.mp.views.add_participant'), + { + 'topic_pk': '451' + }, + follow=True + ) + + self.assertEqual(404, response.status_code) + + def test_fail_add_participant_who_no_exist(self): + + response = self.client.post( + reverse('zds.mp.views.add_participant'), + { + 'topic_pk': self.topic1.pk, + 'user_pk': '178548' + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + self.assertEqual(1, len(response.context['messages'])) + + def test_fail_add_participant_with_no_right(self): + profile3 = ProfileFactory() + + self.client.logout() + self.assertTrue( + self.client.login( + username=profile3.user.username, + password='hostel77' + ) + ) + + response = self.client.post( + reverse('zds.mp.views.add_participant'), + { + 'topic_pk': self.topic1.pk, + 'user_pk': profile3.user.username + } + ) + + self.assertEqual(403, response.status_code) + self.assertNotIn( + profile3.user, + PrivateTopic.objects.get(pk=self.topic1.pk).participants.all() + ) + + def test_fail_add_participant_already_in(self): + + response = self.client.post( + reverse('zds.mp.views.add_participant'), + { + 'topic_pk': self.topic1.pk, + 'user_pk': self.profile2.user.username + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + self.assertEqual(1, len(response.context['messages'])) + + def test_success_add_participant(self): + + profile3 = ProfileFactory() + + response = self.client.post( + reverse('zds.mp.views.add_participant'), + { + 'topic_pk': self.topic1.pk, + 'user_pk': profile3.user.username + }, + follow=True + ) + + self.assertEqual(200, response.status_code) + self.assertEqual(1, len(response.context['messages'])) + self.assertIn( + profile3.user, + PrivateTopic.objects.get(pk=self.topic1.pk).participants.all() + ) diff --git a/zds/mp/views.py b/zds/mp/views.py index 1e5690a9e0..f6d5b6e9b9 100644 --- a/zds/mp/views.py +++ b/zds/mp/views.py @@ -29,7 +29,6 @@ never_privateread, mark_read, PrivateTopicRead - @login_required def index(request): """Display the all private topics.""" @@ -78,7 +77,6 @@ def index(request): }) - @login_required def topic(request, topic_pk, topic_slug): """Display a thread and its posts using a pager.""" @@ -142,7 +140,6 @@ def topic(request, topic_pk, topic_slug): }) - @login_required def new(request): """Creates a new private topic.""" @@ -221,7 +218,6 @@ def new(request): }) - @login_required @require_POST def edit(request): @@ -249,7 +245,6 @@ def edit(request): return redirect(u'{}?page={}'.format(g_topic.get_absolute_url(), page)) - @login_required def answer(request): """Adds an answer from an user to a topic.""" @@ -261,6 +256,11 @@ def answer(request): # Retrieve current topic. g_topic = get_object_or_404(PrivateTopic, pk=topic_pk) + # check if user has right to answer + if not g_topic.author == request.user \ + and request.user not in list(g_topic.participants.all()): + raise PermissionDenied + # Retrieve 3 last posts of the currenta topic. posts = PrivatePost.objects\ .filter(privatetopic=g_topic)\ @@ -356,7 +356,7 @@ def answer(request): # Using the quote button if 'cite' in request.GET: post_cite_pk = request.GET['cite'] - post_cite = PrivatePost.objects.get(pk=post_cite_pk) + post_cite = get_object_or_404(PrivatePost, pk=post_cite_pk) for line in post_cite.text.splitlines(): text = text + '> ' + line + '\n' @@ -377,7 +377,6 @@ def answer(request): }) - @login_required def edit_post(request): """Edit the given user's post.""" @@ -451,13 +450,12 @@ def edit_post(request): }) - @login_required @require_POST @transaction.atomic def leave(request): if 'leave' in request.POST: - ptopic = PrivateTopic.objects.get(pk=request.POST['topic_pk']) + ptopic = get_object_or_404(PrivateTopic, pk=request.POST['topic_pk']) if ptopic.participants.count() == 0: ptopic.delete() elif request.user.pk == ptopic.author.pk: @@ -475,13 +473,18 @@ def leave(request): return redirect(reverse('zds.mp.views.index')) - @login_required @require_POST @transaction.atomic def add_participant(request): - ptopic = PrivateTopic.objects.get(pk=request.POST['topic_pk']) + ptopic = get_object_or_404(PrivateTopic, pk=request.POST['topic_pk']) + + # check if user is the author of topic + if not ptopic.author == request.user: + raise PermissionDenied + try: + # user_pk or user_username ? part = User.objects.get(username=request.POST['user_pk']) if part.pk == ptopic.author.pk or part in ptopic.participants.all(): messages.warning( From 95a16b82654d9d076d8224b2fd9861b12b61f34b Mon Sep 17 00:00:00 2001 From: poulp Date: Mon, 7 Jul 2014 19:30:58 +0200 Subject: [PATCH 012/270] clean code, pep8 --- zds/mp/tests/tests.py.old | 296 ------------------------------------ zds/mp/tests/tests_forms.py | 5 +- zds/mp/tests/tests_views.py | 24 ++- zds/mp/views.py | 3 +- 4 files changed, 20 insertions(+), 308 deletions(-) delete mode 100644 zds/mp/tests/tests.py.old diff --git a/zds/mp/tests/tests.py.old b/zds/mp/tests/tests.py.old deleted file mode 100644 index 7b5a718df2..0000000000 --- a/zds/mp/tests/tests.py.old +++ /dev/null @@ -1,296 +0,0 @@ -# coding: utf-8 - -from django.core import mail -from django.core.urlresolvers import reverse -from django.test import TestCase - -from zds import settings -from zds.member.factories import ProfileFactory, StaffProfileFactory -from zds.mp.factories import PrivateTopicFactory, PrivatePostFactory -from zds.mp.models import PrivateTopic, PrivatePost -from zds.utils import slugify - - -class MPTests(TestCase): - - def setUp(self): - self.user1 = ProfileFactory().user - self.staff = StaffProfileFactory().user - log = self.client.login( - username=self.user1.username, - password='hostel77') - self.assertEqual(log, True) - - settings.EMAIL_BACKEND = \ - 'django.core.mail.backends.locmem.EmailBackend' - - def test_mp_from_profile(self): - """Test: Send a MP from a user profile.""" - # User to send the MP - user2 = ProfileFactory().user - - # Test if user is correctly added to the MP - result = self.client.get( - reverse('zds.mp.views.new') + - '?username={0}'.format( - user2.username), - ) - - # Check username in new MP page - self.assertContains(result, user2.username) - - def test_view_mp(self): - """check mp is readable.""" - ptopic1 = PrivateTopicFactory(author=self.user1) - PrivatePostFactory( - privatetopic=ptopic1, - author=self.user1, - position_in_topic=1) - PrivatePostFactory( - privatetopic=ptopic1, - author=self.staff, - position_in_topic=2) - PrivatePostFactory( - privatetopic=ptopic1, - author=self.user1, - position_in_topic=3) - - result = self.client.get( - reverse( - 'zds.mp.views.topic', - args=[ - ptopic1.pk, - slugify(ptopic1.title)]), - follow=True) - self.assertEqual(result.status_code, 200) - - def test_create_mp(self): - """To test all aspects of mp's creation by member.""" - # Another User - user2 = ProfileFactory().user - user3 = ProfileFactory().user - - result = self.client.post( - reverse('zds.mp.views.new'), - { - 'participants': '{0}, {1}'.format(user2.username, - user3.username), - 'title': u'Un autre MP', - 'subtitle': u'Encore ces lombards en plein été', - 'text': u'C\'est tout simplement l\'histoire de la ville de Paris que je voudrais vous conter ' - }, - follow=False) - self.assertEqual(result.status_code, 302) - - # check topic's number - self.assertEqual(PrivateTopic.objects.all().count(), 1) - ptopic = PrivateTopic.objects.get(pk=1) - # check post's number - self.assertEqual(PrivatePost.objects.all().count(), 1) - ppost = PrivatePost.objects.get(pk=1) - - # check topic and post - self.assertEqual(ppost.privatetopic, ptopic) - - # check position - self.assertEqual(ppost.position_in_topic, 1) - - self.assertEqual(ppost.author, self.user1) - - # check last message - self.assertEqual(ptopic.last_message, ppost) - - # check email has been sent - self.assertEquals(len(mail.outbox), 2) - - # check view authorisations - user4 = ProfileFactory().user - staff1 = StaffProfileFactory().user - - # user2 and user3 can view mp - self.client.login(username=user2.username, password='hostel77') - result = self.client.get( - reverse( - 'zds.mp.views.topic', - args=[ - ptopic.pk, - ptopic.pk]), - follow=True) - self.assertEqual(result.status_code, 200) - self.client.login(username=user3.username, password='hostel77') - result = self.client.get( - reverse( - 'zds.mp.views.topic', - args=[ - ptopic.pk, - ptopic.pk]), - follow=True) - self.assertEqual(result.status_code, 200) - - # user4 and staff1 can't view mp - self.client.login(username=user4.username, password='hostel77') - result = self.client.get( - reverse( - 'zds.mp.views.topic', - args=[ - ptopic.pk, - ptopic.pk]), - follow=True) - self.assertNotEqual(result.status_code, 200) - self.client.login(username=staff1.username, password='hostel77') - result = self.client.get( - reverse( - 'zds.mp.views.topic', - args=[ - ptopic.pk, - ptopic.pk]), - follow=True) - self.assertNotEqual(result.status_code, 200) - - def test_edit_mp_post(self): - """To test all aspects of the edition of simple mp post by member.""" - - ptopic1 = PrivateTopicFactory(author=self.user1) - ppost1 = PrivatePostFactory( - privatetopic=ptopic1, - author=self.user1, - position_in_topic=1) - ppost2 = PrivatePostFactory( - privatetopic=ptopic1, - author=self.user1, - position_in_topic=2) - ppost3 = PrivatePostFactory( - privatetopic=ptopic1, - author=self.user1, - position_in_topic=3) - - result = self.client.post( - reverse('zds.mp.views.edit_post') + '?message={0}' - .format(ppost3.pk), - { - 'text': u'C\'est tout simplement l\'histoire de la ville de Paris que je voudrais vous conter ' - }, - follow=False) - - self.assertEqual(result.status_code, 302) - - # check topic's number - self.assertEqual(PrivateTopic.objects.all().count(), 1) - - # check post's number - self.assertEqual(PrivatePost.objects.all().count(), 3) - - # check topic and post - self.assertEqual(ppost1.privatetopic, ptopic1) - self.assertEqual(ppost2.privatetopic, ptopic1) - self.assertEqual(ppost3.privatetopic, ptopic1) - - # check values - self.assertEqual( - PrivatePost.objects.get( - pk=ppost3.pk).text, - u"C\'est tout simplement l\'histoire de la ville de Paris que je voudrais vous conter ") - - # check no email has been sent - self.assertEquals(len(mail.outbox), 0) - - # i can edit a mp if it's not last - result = self.client.post( - reverse('zds.mp.views.edit_post') + '?message={0}' - .format(ppost2.pk), - { - 'text': u"C\'est tout simplement l\'histoire de la ville de Paris que je voudrais vous conter " - }, - follow=False) - - self.assertEqual(result.status_code, 403) - - # staff can't edit mp if he's not author - staff = StaffProfileFactory().user - log = self.client.login(username=staff.username, password='hostel77') - self.assertEqual(log, True) - - result = self.client.post( - reverse('zds.mp.views.edit_post') + '?message={0}' - .format(ppost3.pk), - { - 'text': u'C\'est tout simplement l\'histoire de la ville de Paris que je voudrais vous conter ' - }, - follow=False) - - self.assertEqual(result.status_code, 403) - - def test_delete_mp_post(self): - """To test all aspects of the deletion of simple mp post by member.""" - - # Another User - user2 = ProfileFactory().user - user3 = ProfileFactory().user - - result = self.client.post( - reverse('zds.mp.views.new'), - { - 'participants': '{0}, {1}'.format(user2.username, - user3.username), - 'title': u'Un autre MP', - 'subtitle': u'Encore ces lombards en plein été', - 'text': u'C\'est tout simplement l\'histoire de la ville de Paris que je voudrais vous conter ' - }, - follow=False) - self.assertEqual(result.status_code, 302) - - # check topic's number - self.assertEqual(PrivateTopic.objects.all().count(), 1) - ptopic = PrivateTopic.objects.get(pk=1) - - # check post's number - self.assertEqual(PrivatePost.objects.all().count(), 1) - ppost = PrivatePost.objects.get(pk=1) - - # check author of the MP - self.assertEqual(ppost.author, self.user1) - - # User3 would like leave the MP. He isn't the author - # and there will be still users in the MP. - self.client.login(username=user3.username, password='hostel77') - result = self.client.post( - reverse('zds.mp.views.leave'), - { - 'leave': 'leave', - 'topic_pk': ptopic.pk, - }, - follow=False) - self.assertEqual(result.status_code, 302) - - # check there are still 2 participants in MP. - self.assertEqual(ptopic.participants.count(), 1) - - # User1 would like leave the MP. He is the author so User2 - # will become the new author of the MP. - self.client.login(username=self.user1.username, password='hostel77') - result = self.client.post( - reverse('zds.mp.views.leave'), - { - 'leave': 'leave', - 'topic_pk': ptopic.pk, - }, - follow=False) - self.assertEqual(result.status_code, 302) - - # check there is still 1 participant in MP. - self.assertEqual(ptopic.participants.count(), 0) - - # User2 would like leave the MP. He is the author but there - # isn't other participants, the MP must to be delete. - self.client.login(username=user2.username, password='hostel77') - result = self.client.post( - reverse('zds.mp.views.leave'), - { - 'leave': 'leave', - 'topic_pk': ptopic.pk, - }, - follow=False) - self.assertEqual(result.status_code, 302) - - # check there is no more MP. - self.assertEqual(PrivateTopic.objects.all().count(), 0) diff --git a/zds/mp/tests/tests_forms.py b/zds/mp/tests/tests_forms.py index 81a39db2c2..9ee5caf24a 100644 --- a/zds/mp/tests/tests_forms.py +++ b/zds/mp/tests/tests_forms.py @@ -142,9 +142,6 @@ def test_invalid_form_post_empty_text(self): self.assertFalse(form.is_valid()) def test_invalid_form_post_no_text(self): - data = { - } - - form = PrivatePostForm(self.topic, self.profile.user, data=data) + form = PrivatePostForm(self.topic, self.profile.user, data={}) self.assertFalse(form.is_valid()) diff --git a/zds/mp/tests/tests_views.py b/zds/mp/tests/tests_views.py index eb1a2a36dc..aa851d3fda 100644 --- a/zds/mp/tests/tests_views.py +++ b/zds/mp/tests/tests_views.py @@ -437,7 +437,7 @@ def test_fail_user_add_himself_to_private_topic_with_no_right(self): ) ) - self.client.post( + response = self.client.post( reverse('zds.mp.views.edit'), { 'privatetopic': self.topic1.pk, @@ -446,7 +446,7 @@ def test_fail_user_add_himself_to_private_topic_with_no_right(self): follow=True ) - # self.assertEqual(403, response.status_code) + self.assertEqual(403, response.status_code) topic = PrivateTopic.objects.get(pk=self.topic1.pk) self.assertNotIn( self.profile3.user, @@ -560,8 +560,6 @@ def test_success_answer(self): self.assertEqual(200, response.status_code) self.assertEqual(3, PrivatePost.objects.all().count()) - # TODO test mail notification - def test_fail_answer_with_no_right(self): self.client.logout() @@ -614,13 +612,24 @@ def setUp(self): def test_denies_anonymous(self): self.client.logout() - response = self.client.get(reverse('zds.mp.views.edit_post'), follow=True) + response = self.client.get( + reverse('zds.mp.views.edit_post'), + follow=True + ) self.assertRedirects( response, reverse('zds.member.views.login_view') + '?next=' + urllib.quote(reverse('zds.mp.views.edit_post'), '')) + def test_fail_edit_post_no_get_parameter(self): + + response = self.client.get( + reverse('zds.mp.views.edit_post') + ) + + self.assertEqual(404, response.status_code) + def test_succes_get_edit_post_page(self): self.client.logout() @@ -865,7 +874,10 @@ def setUp(self): def test_denies_anonymous(self): self.client.logout() - response = self.client.get(reverse('zds.mp.views.add_participant'), follow=True) + response = self.client.get( + reverse('zds.mp.views.add_participant'), + follow=True + ) self.assertRedirects( response, diff --git a/zds/mp/views.py b/zds/mp/views.py index f6d5b6e9b9..f62aff6e70 100644 --- a/zds/mp/views.py +++ b/zds/mp/views.py @@ -222,7 +222,6 @@ def new(request): @require_POST def edit(request): """Edit the given topic.""" - authenticated_user = request.user try: topic_pk = request.POST['privatetopic'] @@ -238,7 +237,7 @@ def edit(request): if request.POST['username']: u = get_object_or_404(User, username=request.POST['username']) - if not authenticated_user == u: + if not request.user == u: g_topic.participants.add(u) g_topic.save() From 6b4dbee5f0a4cb9ccc78364b648fea16c74d65c8 Mon Sep 17 00:00:00 2001 From: Alexandre Broudin Date: Mon, 7 Jul 2014 22:25:11 +0200 Subject: [PATCH 013/270] Suppression du lien vers Google Chrome Frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Google Chrome Frame n’existe plus, il n’a donc plus raison d’être present sur le site. https://www.google.com/chromeframe/about/ Aussi je change le test lt IE 7 vers lt IE 9, IE 8 étant en fin de vie http://theie8countdown.com/ --- templates/base.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/base.html b/templates/base.html index d1064c560e..29d7c04a36 100644 --- a/templates/base.html +++ b/templates/base.html @@ -77,8 +77,8 @@ -
From f4497a07c5ce573923ad2c1675b1d2d1367db174 Mon Sep 17 00:00:00 2001 From: Geoffrey ROYER Date: Tue, 8 Jul 2014 17:50:49 +0200 Subject: [PATCH 014/270] Fixup confirmation mail --- templates/email/register/confirm.html | 6 ++++-- templates/email/register/confirm.txt | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/templates/email/register/confirm.html b/templates/email/register/confirm.html index aa71ea6afd..731e105ce5 100644 --- a/templates/email/register/confirm.html +++ b/templates/email/register/confirm.html @@ -7,15 +7,17 @@

- Vous vous êtes inscrit sur Zeste de Savoir et nous vous en remercions. En étant membre de notre communauté, vous aurez la possibilité de rédiger des tutoriels et des articles que vous pourrez ensuite publier et ainsi rendre vos connaissances accessibles au plus grand nombre. Vous pourrez également échanger avec les autres membres sur nos forums !
Il ne vous reste plus qu'à activer votre profil ! Pour ce faire, visitez le lien ci-dessous : +
{{ url }}
- L'équipe Zeste de Savoir + A très bientôt ! +
+ L'équipe Zeste de Savoir

diff --git a/templates/email/register/confirm.txt b/templates/email/register/confirm.txt index 40ca7478c9..82a99fef3d 100644 --- a/templates/email/register/confirm.txt +++ b/templates/email/register/confirm.txt @@ -6,4 +6,5 @@ Il ne vous reste plus qu'à activer votre profil ! Pour ce faire, visitez le lie {{ url }} +A très bientôt ! L'équipe Zeste de Savoir From bd96508d33f4fdc1638482ceddb17b56aabe229d Mon Sep 17 00:00:00 2001 From: Geoffrey ROYER Date: Wed, 9 Jul 2014 07:21:19 +0200 Subject: [PATCH 015/270] Fixup html template --- templates/email/register/confirm.html | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/templates/email/register/confirm.html b/templates/email/register/confirm.html index 731e105ce5..c14fbacb85 100644 --- a/templates/email/register/confirm.html +++ b/templates/email/register/confirm.html @@ -9,15 +9,16 @@

Vous vous êtes inscrit sur Zeste de Savoir et nous vous en remercions. En étant membre de notre communauté, vous aurez la possibilité de rédiger des tutoriels et des articles - que vous pourrez ensuite publier et ainsi rendre vos connaissances accessibles au plus grand nombre. + que vous pourrez ensuite publier et ainsi de rendre vos connaissances accessibles au plus grand nombre. Vous pourrez également échanger avec les autres membres sur nos forums !
Il ne vous reste plus qu'à activer votre profil ! Pour ce faire, visitez le lien ci-dessous :
{{ url }} -
- A très bientôt ! -
- L'équipe Zeste de Savoir +
+
+ A très bientôt ! +
+ L'équipe Zeste de Savoir

From 404ffc38fbf99c26f5ab17a3e0d001be2fc297fb Mon Sep 17 00:00:00 2001 From: coma94 Date: Thu, 10 Jul 2014 20:18:23 +0200 Subject: [PATCH 016/270] fix formatage MP refus tuto (#1142) --- zds/tutorial/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index b010edfc18..424b3a61c8 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -289,7 +289,7 @@ def reject_tutorial(request): u'pas passé l’étape de validation. Mais ne désespère pas, ' u'certaines corrections peuvent surement être faite pour ' u'l’améliorer et repasser la validation plus tard. ' - u'Voici le message que [{2}]({3}), ton validateur t\'a laissé `{4}`' + u'Voici le message que [{2}]({3}), ton validateur t\'a laissé:\n\n`{4}`\n\n' u'N\'hésite pas a lui envoyer un petit message pour discuter ' u'de la décision ou demander plus de détail si tout cela te ' u'semble injuste ou manque de clarté.' From b15ec01e0d2f63a5bfbb5108aba797c84c9adea3 Mon Sep 17 00:00:00 2001 From: Florianboux Date: Tue, 15 Jul 2014 12:16:25 +0200 Subject: [PATCH 017/270] =?UTF-8?q?R=C3=A8gle=20le=20probl=C3=A8me=20des?= =?UTF-8?q?=20blocs=20oranges=20intro/conclu=20lorsque=20le=20tuto=20est?= =?UTF-8?q?=20en=20b=C3=AAta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/tutorial/part/view.html | 4 +- templates/tutorial/part/view.html~ | 177 ++++++++++++ templates/tutorial/tutorial/view.html | 4 +- templates/tutorial/tutorial/view.html~ | 370 +++++++++++++++++++++++++ 4 files changed, 551 insertions(+), 4 deletions(-) create mode 100644 templates/tutorial/part/view.html~ create mode 100644 templates/tutorial/tutorial/view.html~ diff --git a/templates/tutorial/part/view.html b/templates/tutorial/part/view.html index 37c1e8bc92..b4aaf403b8 100644 --- a/templates/tutorial/part/view.html +++ b/templates/tutorial/part/view.html @@ -37,7 +37,7 @@

{% block content %} {% if part.intro and part.intro != "None" %} {{ part.intro|emarkdown }} - {% else %} + {% elif not tutorial.in_beta or tutorial.sha_beta != version %}

Il n'y a pas d'introduction.

@@ -84,7 +84,7 @@

{% if part.conclu and part.conclu != "None" %} {{ part.conclu|emarkdown }} - {% else %} + {% elif not tutorial.in_beta or tutorial.sha_beta != version %}

Il n'y a pas de conclusion.

diff --git a/templates/tutorial/part/view.html~ b/templates/tutorial/part/view.html~ new file mode 100644 index 0000000000..37c1e8bc92 --- /dev/null +++ b/templates/tutorial/part/view.html~ @@ -0,0 +1,177 @@ +{% extends "tutorial/base.html" %} +{% load emarkdown %} + + + +{% block title %} + {{ part.title }} - {{ part.tutorial.title }} +{% endblock %} + + + +{% block breadcrumb %} +
  • {{ part.tutorial.title }}
  • +
  • {{ part.title }}
  • +{% endblock %} + + + +{% block headline %} +

    + {{ part.title }} +

    + + {% include 'tutorial/includes/tags_authors.part.html' with tutorial=part.tutorial %} + + {% if part.tutorial.in_beta and part.tutorial.sha_beta == version %} +
    +
    + Attention, cette version du tutoriel est en BETA ! +
    +
    + {% endif %} +{% endblock %} + + + +{% block content %} + {% if part.intro and part.intro != "None" %} + {{ part.intro|emarkdown }} + {% else %} +

    + Il n'y a pas d'introduction. +

    + {% endif %} + +
    + + {% if part.chapters %} +
      + {% for chapter in part.chapters %} +
    • +

      + + {{ chapter.title }} + +

      + +
    • + {% empty %} +

      + Il n'y a actuellement aucune partie dans ce tutoriel. +

      + {% endfor %} +
    + {% endif %} + +
    + + {% if part.conclu and part.conclu != "None" %} + {{ part.conclu|emarkdown }} + {% else %} +

    + Il n'y a pas de conclusion. +

    + {% endif %} +{% endblock %} + + + +{% block sidebar_new %} + {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} + + Ajouter un chapitre + + + Éditer la partie + + {% endif %} +{% endblock %} + + +{% block sidebar_actions %} + {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} + {% if part %} +
  • + + Déplacer la partie + + +
  • + {% endif %} + {% endif %} +{% endblock %} + +{% block sidebar_blocks %} + {% include "tutorial/includes/summary.part.html" with tutorial=part.tutorial parts=part.tutorial.get_parts %} +{% endblock %} diff --git a/templates/tutorial/tutorial/view.html b/templates/tutorial/tutorial/view.html index 48a2277cb1..3d79fe199e 100644 --- a/templates/tutorial/tutorial/view.html +++ b/templates/tutorial/tutorial/view.html @@ -52,7 +52,7 @@

    {% with tuto_version=tutorial|repo_tuto:version %} {% if tuto_version.introduction and tuto_version.introduction != "None" %} {{ tuto_version.introduction|emarkdown }} - {% else %} + {% elif not tutorial.in_beta or tutorial.sha_beta != version %}

    Il n'y a pas d'introduction.

    @@ -86,7 +86,7 @@

    {% if tuto_version.conclusion and tuto_version.conclusion != "None" %} {{ tuto_version.conclusion|emarkdown }} - {% else %} + {% elif not tutorial.in_beta or tutorial.sha_beta != version %}

    Il n'y a pas de conclusion.

    diff --git a/templates/tutorial/tutorial/view.html~ b/templates/tutorial/tutorial/view.html~ new file mode 100644 index 0000000000..48a2277cb1 --- /dev/null +++ b/templates/tutorial/tutorial/view.html~ @@ -0,0 +1,370 @@ +{% extends "tutorial/base.html" %} +{% load emarkdown %} +{% load repo_reader %} +{% load crispy_forms_tags %} +{% load thumbnail %} + + +{% block title %} + {{ tutorial.title }} +{% endblock %} + + + +{% block breadcrumb %} +
  • {{ tutorial.title }}
  • +{% endblock %} + + + +{% block headline %} +

    + {% if tutorial.image %} + + {% endif %} + {{ tutorial.title }} - {{ tutorial.image }} +

    + + {% if tutorial.description %} +

    + {{ tutorial.description }} +

    + {% endif %} + + {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} + {% include 'tutorial/includes/tags_authors.part.html' with tutorial=tutorial add_author=True %} + {% else %} + {% include 'tutorial/includes/tags_authors.part.html' with tutorial=tutorial %} + {% endif %} + + {% if tutorial.in_beta and tutorial.sha_beta == version %} +
    +
    + Attention, cette version du tutoriel est en BÊTA ! +
    +
    + {% endif %} +{% endblock %} + + + +{% block content %} + {% with tuto_version=tutorial|repo_tuto:version %} + {% if tuto_version.introduction and tuto_version.introduction != "None" %} + {{ tuto_version.introduction|emarkdown }} + {% else %} +

    + Il n'y a pas d'introduction. +

    + {% endif %} + + {% if tutorial.is_mini %} + {# Small tutorial #} + + {% include "tutorial/includes/chapter.part.html" with authors=tutorial.authors.all %} + {% else %} + {# Large tutorial #} + +
    + + {% for part in parts %} +

    + + Partie {{ part.position_in_tutorial }} : {{ part.title }} + +

    + {% include "tutorial/includes/part.part.html" %} + {% empty %} +

    + Il n'y a actuellement aucune partie dans ce tutoriel. +

    + {% endfor %} + +
    + + {% endif %} + + {% if tuto_version.conclusion and tuto_version.conclusion != "None" %} + {{ tuto_version.conclusion|emarkdown }} + {% else %} +

    + Il n'y a pas de conclusion. +

    + {% endif %} + {% endwith %} +{% endblock %} + + + +{% block sidebar_new %} + {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} + {% if not tutorial.is_mini %} + + Ajouter une partie + + {% else %} + + Ajouter un extrait + + {% endif %} + + {% if tutorial.sha_draft = version %} + + Éditer + + {% endif %} + {% endif %} +{% endblock %} + + + +{% block sidebar_actions %} + {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} +
  • + + Ajouter un auteur + + +
  • +
  • + + Gérer les auteurs + + +
  • + + {% if tutorial.sha_public %} +
  • + + Voir la version en ligne + +
  • + {% endif %} + + {% if not tutorial.in_beta %} +
  • + + Activer la bêta + + +
  • + {% else %} + {% if tutorial.sha_beta != version %} +
  • + + Mettre à jour la bêta + + +
  • + {% endif %} +
  • + + Désactiver la bêta + + +
  • + {% endif %} + +
  • + + Historique des versions + +
  • + + {% if not tutorial.in_validation %} +
  • + + Demander la validation + + +
  • + {% else %} +
  • + En attente de validation +
  • + {% endif %} + {% endif %} +{% endblock %} + + + +{% block sidebar_blocks %} + {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} + + {% endif %} + + {% include "tutorial/includes/summary.part.html" %} + + {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} + + {% endif %} +{% endblock %} From 3ad3738a643ade948eacc8032b6bc70ab7536b6a Mon Sep 17 00:00:00 2001 From: Florianboux Date: Tue, 15 Jul 2014 14:39:34 +0200 Subject: [PATCH 018/270] =?UTF-8?q?Enl=C3=A8ve=20les=20fichier=20~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/tutorial/part/view.html~ | 177 ------------ templates/tutorial/tutorial/view.html~ | 370 ------------------------- 2 files changed, 547 deletions(-) delete mode 100644 templates/tutorial/part/view.html~ delete mode 100644 templates/tutorial/tutorial/view.html~ diff --git a/templates/tutorial/part/view.html~ b/templates/tutorial/part/view.html~ deleted file mode 100644 index 37c1e8bc92..0000000000 --- a/templates/tutorial/part/view.html~ +++ /dev/null @@ -1,177 +0,0 @@ -{% extends "tutorial/base.html" %} -{% load emarkdown %} - - - -{% block title %} - {{ part.title }} - {{ part.tutorial.title }} -{% endblock %} - - - -{% block breadcrumb %} -
  • {{ part.tutorial.title }}
  • -
  • {{ part.title }}
  • -{% endblock %} - - - -{% block headline %} -

    - {{ part.title }} -

    - - {% include 'tutorial/includes/tags_authors.part.html' with tutorial=part.tutorial %} - - {% if part.tutorial.in_beta and part.tutorial.sha_beta == version %} -
    -
    - Attention, cette version du tutoriel est en BETA ! -
    -
    - {% endif %} -{% endblock %} - - - -{% block content %} - {% if part.intro and part.intro != "None" %} - {{ part.intro|emarkdown }} - {% else %} -

    - Il n'y a pas d'introduction. -

    - {% endif %} - -
    - - {% if part.chapters %} -
      - {% for chapter in part.chapters %} -
    • -

      - - {{ chapter.title }} - -

      - -
    • - {% empty %} -

      - Il n'y a actuellement aucune partie dans ce tutoriel. -

      - {% endfor %} -
    - {% endif %} - -
    - - {% if part.conclu and part.conclu != "None" %} - {{ part.conclu|emarkdown }} - {% else %} -

    - Il n'y a pas de conclusion. -

    - {% endif %} -{% endblock %} - - - -{% block sidebar_new %} - {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} - - Ajouter un chapitre - - - Éditer la partie - - {% endif %} -{% endblock %} - - -{% block sidebar_actions %} - {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} - {% if part %} -
  • - - Déplacer la partie - - -
  • - {% endif %} - {% endif %} -{% endblock %} - -{% block sidebar_blocks %} - {% include "tutorial/includes/summary.part.html" with tutorial=part.tutorial parts=part.tutorial.get_parts %} -{% endblock %} diff --git a/templates/tutorial/tutorial/view.html~ b/templates/tutorial/tutorial/view.html~ deleted file mode 100644 index 48a2277cb1..0000000000 --- a/templates/tutorial/tutorial/view.html~ +++ /dev/null @@ -1,370 +0,0 @@ -{% extends "tutorial/base.html" %} -{% load emarkdown %} -{% load repo_reader %} -{% load crispy_forms_tags %} -{% load thumbnail %} - - -{% block title %} - {{ tutorial.title }} -{% endblock %} - - - -{% block breadcrumb %} -
  • {{ tutorial.title }}
  • -{% endblock %} - - - -{% block headline %} -

    - {% if tutorial.image %} - - {% endif %} - {{ tutorial.title }} - {{ tutorial.image }} -

    - - {% if tutorial.description %} -

    - {{ tutorial.description }} -

    - {% endif %} - - {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} - {% include 'tutorial/includes/tags_authors.part.html' with tutorial=tutorial add_author=True %} - {% else %} - {% include 'tutorial/includes/tags_authors.part.html' with tutorial=tutorial %} - {% endif %} - - {% if tutorial.in_beta and tutorial.sha_beta == version %} -
    -
    - Attention, cette version du tutoriel est en BÊTA ! -
    -
    - {% endif %} -{% endblock %} - - - -{% block content %} - {% with tuto_version=tutorial|repo_tuto:version %} - {% if tuto_version.introduction and tuto_version.introduction != "None" %} - {{ tuto_version.introduction|emarkdown }} - {% else %} -

    - Il n'y a pas d'introduction. -

    - {% endif %} - - {% if tutorial.is_mini %} - {# Small tutorial #} - - {% include "tutorial/includes/chapter.part.html" with authors=tutorial.authors.all %} - {% else %} - {# Large tutorial #} - -
    - - {% for part in parts %} -

    - - Partie {{ part.position_in_tutorial }} : {{ part.title }} - -

    - {% include "tutorial/includes/part.part.html" %} - {% empty %} -

    - Il n'y a actuellement aucune partie dans ce tutoriel. -

    - {% endfor %} - -
    - - {% endif %} - - {% if tuto_version.conclusion and tuto_version.conclusion != "None" %} - {{ tuto_version.conclusion|emarkdown }} - {% else %} -

    - Il n'y a pas de conclusion. -

    - {% endif %} - {% endwith %} -{% endblock %} - - - -{% block sidebar_new %} - {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} - {% if not tutorial.is_mini %} - - Ajouter une partie - - {% else %} - - Ajouter un extrait - - {% endif %} - - {% if tutorial.sha_draft = version %} - - Éditer - - {% endif %} - {% endif %} -{% endblock %} - - - -{% block sidebar_actions %} - {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} -
  • - - Ajouter un auteur - - -
  • -
  • - - Gérer les auteurs - - -
  • - - {% if tutorial.sha_public %} -
  • - - Voir la version en ligne - -
  • - {% endif %} - - {% if not tutorial.in_beta %} -
  • - - Activer la bêta - - -
  • - {% else %} - {% if tutorial.sha_beta != version %} -
  • - - Mettre à jour la bêta - - -
  • - {% endif %} -
  • - - Désactiver la bêta - - -
  • - {% endif %} - -
  • - - Historique des versions - -
  • - - {% if not tutorial.in_validation %} -
  • - - Demander la validation - - -
  • - {% else %} -
  • - En attente de validation -
  • - {% endif %} - {% endif %} -{% endblock %} - - - -{% block sidebar_blocks %} - {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} - - {% endif %} - - {% include "tutorial/includes/summary.part.html" %} - - {% if user in tutorial.authors.all or perms.tutorial.change_tutorial %} - - {% endif %} -{% endblock %} From b58016198c17c0ece87be41933c66e3f50106990 Mon Sep 17 00:00:00 2001 From: coma94 Date: Wed, 16 Jul 2014 16:24:24 +0200 Subject: [PATCH 019/270] Auto-remplissage de l'url d'origine d'un tuto #1201 --- zds/tutorial/forms.py | 5 ++--- zds/tutorial/views.py | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zds/tutorial/forms.py b/zds/tutorial/forms.py index 86f30a3788..b5a7ea2ff2 100644 --- a/zds/tutorial/forms.py +++ b/zds/tutorial/forms.py @@ -387,9 +387,8 @@ def __init__(self, *args, **kwargs): StrictButton( 'Confirmer', type='submit'), - Hidden( - 'tutorial', '{{ tutorial.pk }}'), Hidden( - 'version', '{{ version }}'), ) + Hidden('tutorial', '{{ tutorial.pk }}'), + Hidden('version', '{{ version }}'), ) class ValidForm(forms.Form): diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 424b3a61c8..cfcc56338b 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -701,10 +701,11 @@ def view_tutorial(request, tutorial_pk, tutorial_slug): version=sha)\ .order_by("-date_proposition")\ .first() - formAskValidation = AskValidationForm() if tutorial.source: + formAskValidation = AskValidationForm(initial={"source": tutorial.source}) formValid = ValidForm(initial={"source": tutorial.source}) else: + formAskValidation = AskValidationForm() formValid = ValidForm() formReject = RejectForm() return render_template("tutorial/tutorial/view.html", { From 2e3fddb078a05ac8bfabd9d667f553568f410f7b Mon Sep 17 00:00:00 2001 From: firm1 Date: Thu, 24 Jul 2014 01:52:39 +0200 Subject: [PATCH 020/270] creation auto de sujet pour les tuto beta --- .../0006_auto__add_field_topic_key.py | 136 ++++++++++++++++++ zds/forum/models.py | 2 + zds/settings.py | 4 + zds/tutorial/tests.py | 9 +- zds/tutorial/views.py | 78 +++++++++- zds/utils/forums.py | 73 ++++++++++ 6 files changed, 299 insertions(+), 3 deletions(-) create mode 100644 zds/forum/migrations/0006_auto__add_field_topic_key.py create mode 100644 zds/utils/forums.py diff --git a/zds/forum/migrations/0006_auto__add_field_topic_key.py b/zds/forum/migrations/0006_auto__add_field_topic_key.py new file mode 100644 index 0000000000..538c5bf883 --- /dev/null +++ b/zds/forum/migrations/0006_auto__add_field_topic_key.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Topic.key' + db.add_column(u'forum_topic', 'key', + self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Topic.key' + db.delete_column(u'forum_topic', 'key') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'forum.category': { + 'Meta': {'object_name': 'Category'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'position': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '80'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}) + }, + u'forum.forum': { + 'Meta': {'object_name': 'Forum'}, + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['forum.Category']"}), + 'group': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['auth.Group']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), + 'position_in_category': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '80'}), + 'subtitle': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}) + }, + u'forum.post': { + 'Meta': {'object_name': 'Post', '_ormbases': [u'utils.Comment']}, + u'comment_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['utils.Comment']", 'unique': 'True', 'primary_key': 'True'}), + 'is_useful': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'topic': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['forum.Topic']"}) + }, + u'forum.topic': { + 'Meta': {'object_name': 'Topic'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'topics'", 'to': u"orm['auth.User']"}), + 'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['forum.Forum']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_solved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'is_sticky': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'key': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'last_message': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'last_message'", 'null': 'True', 'to': u"orm['forum.Post']"}), + 'pubdate': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'subtitle': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['utils.Tag']", 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}) + }, + u'forum.topicfollowed': { + 'Meta': {'object_name': 'TopicFollowed'}, + 'email': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'topic': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['forum.Topic']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'topics_followed'", 'to': u"orm['auth.User']"}) + }, + u'forum.topicread': { + 'Meta': {'object_name': 'TopicRead'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['forum.Post']"}), + 'topic': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['forum.Topic']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'topics_read'", 'to': u"orm['auth.User']"}) + }, + u'utils.comment': { + 'Meta': {'object_name': 'Comment'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': u"orm['auth.User']"}), + 'dislike': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments-editor'", 'null': 'True', 'to': u"orm['auth.User']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.CharField', [], {'max_length': '39'}), + 'is_visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'like': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'position': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'pubdate': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'text_hidden': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '80'}), + 'text_html': ('django.db.models.fields.TextField', [], {}), + 'update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'utils.tag': { + 'Meta': {'object_name': 'Tag'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '20'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '20'}) + } + } + + complete_apps = ['forum'] \ No newline at end of file diff --git a/zds/forum/models.py b/zds/forum/models.py index 654289cdf3..f614afc87d 100644 --- a/zds/forum/models.py +++ b/zds/forum/models.py @@ -157,6 +157,8 @@ class Meta: null=True, blank=True, db_index=True) + + key = models.IntegerField('cle', null=True, blank=True) def __unicode__(self): """Textual form of a thread.""" diff --git a/zds/settings.py b/zds/settings.py index b78ecd6cb2..da8e75d7e1 100644 --- a/zds/settings.py +++ b/zds/settings.py @@ -272,8 +272,12 @@ SPAM_LIMIT_PARTICIPANT = 2 FOLLOWED_TOPICS_PER_PAGE = 21 +#username of the bot who send MP BOT_ACCOUNT = 'admin' +#primary key of beta forum +BETA_FORUM_ID = 1 + PANDOC_LOC = '' # LOG PATH FOR PANDOC LOGGING PANDOC_LOG = './pandoc.log' diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py index a909dbf77c..ef5e4dbb39 100644 --- a/zds/tutorial/tests.py +++ b/zds/tutorial/tests.py @@ -9,6 +9,7 @@ from django.test import TestCase from django.test.utils import override_settings +from zds.forum.factories import CategoryFactory, ForumFactory from zds.member.factories import ProfileFactory, StaffProfileFactory from zds.gallery.factories import GalleryFactory, UserGalleryFactory, ImageFactory from zds.mp.models import PrivateTopic @@ -1362,7 +1363,9 @@ def test_delete_image_tutorial(self): def test_workflow_beta_tuto(self) : "Ensure the behavior of the beta version of tutorials" - + forum = ForumFactory( + category=CategoryFactory(position=1), + position_in_category=1) # logout before self.client.logout() # first, login with author : @@ -2313,6 +2316,10 @@ def test_reorder_tuto(self): def test_workflow_beta_tuto(self) : "Ensure the behavior of the beta version of tutorials" + forum = ForumFactory( + category=CategoryFactory(position=1), + position_in_category=1) + # logout before self.client.logout() # first, login with author : diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index bb0a52d5cf..9c72547294 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -5,6 +5,7 @@ from operator import attrgetter from urllib import urlretrieve from urlparse import urlparse +from django.contrib.humanize.templatetags.humanize import naturalday, naturaltime try: import ujson as json_reader except: @@ -45,12 +46,14 @@ from zds.member.decorator import can_write_and_read_now from zds.member.models import get_info_old_tuto, Profile from zds.member.views import get_client_ip +from zds.forum.models import Forum, Topic from zds.utils import render_template from zds.utils import slugify from zds.utils.models import Alert from zds.utils.models import Category, Licence, CommentLike, CommentDislike, \ SubCategory from zds.utils.mps import send_mp +from zds.utils.forums import create_topic, send_post, lock_topic, unlock_topic from zds.utils.paginator import paginator_range from zds.utils.templatetags.emarkdown import emarkdown from zds.utils.tutorials import get_blob, export_tutorial_to_md, move @@ -599,14 +602,78 @@ def modify_tutorial(request): if "version" in request.POST and tutorial.sha_draft == request.POST['version'] : tutorial.sha_beta = tutorial.sha_draft tutorial.save() + + topic = Topic.objects.filter(key=tutorial.pk, forum__pk=settings.BETA_FORUM_ID).first() + if topic is None: + msg = \ + (u'Bonjour à tous,\n\n' + u'J\'ai commencé ({0}) la rédaction d\'un tutoriel dont l\'intitulé est **{1}**.\n\n' + u'J\'aimerai obtenir un maximum de retour sur celui-ci, sur le fond ainsi que ' + u'sur la forme, afin de proposer en validation un texte de qualité.' + u'\n\nSi vous êtes interessé, cliquez ci-dessous ' + u'\n\n-> [Lien de la beta du tutoriel : {1}]({2}) <-\n\n' + u'\n\nMerci d\'avance pour votre aide'.format( + naturaltime(tutorial.create_at), + tutorial.title, + settings.SITE_URL + tutorial.get_absolute_url_beta())) + forum = get_object_or_404(Forum, pk=settings.BETA_FORUM_ID) + create_topic(author = request.user, + forum = forum, + title = u"[beta][tutoriel]{0}".format(tutorial.title), + subtitle = u"{}".format(tutorial.description), + text = msg, + key = tutorial.pk + ) + else: + msg = \ + (u'Bonjour, !\n\n' + u'La beta du tutoriel est de nouveau active.' + u'\n\n-> [Lien de la beta du tutoriel : {0}]({1}) <-\n\n' + u'\n\nMerci pour vos relectures'.format(tutorial.title, + settings.SITE_URL + tutorial.get_absolute_url_beta())) + unlock_topic(topic) + send_post(topic, msg) + messages.success(request, u"La BETA sur ce tutoriel est bien activée.") else: messages.error(request, u"Impossible d'activer la BETA sur ce tutoriel.") return redirect(tutorial.get_absolute_url_beta()) elif "update_beta" in request.POST: - if "version" in request.POST and tutorial.sha_draft == request.POST['version'] : - tutorial.sha_beta = tutorial.sha_draft + if "version" in request.POST: + tutorial.sha_beta = request.POST['version'] tutorial.save() + + topic = Topic.objects.filter(key=tutorial.pk, forum__pk=settings.BETA_FORUM_ID).first() + if topic is None: + msg = \ + (u'Bonjour à tous,\n\n' + u'J\'ai commencé ({0}) la rédaction d\'un tutoriel dont l\'intitulé est **{1}**.\n\n' + u'J\'aimerai obtenir un maximum de retour sur celui-ci, sur le fond ainsi que ' + u'sur la forme, afin de proposer en validation un texte de qualité.' + u'\n\nSi vous êtes interessé, cliquez ci-dessous ' + u'\n\n-> [Lien de la beta du tutoriel : {1}]({2}) <-\n\n' + u'\n\nMerci d\'avance pour votre aide'.format( + naturaltime(tutorial.create_at), + tutorial.title, + settings.SITE_URL + tutorial.get_absolute_url_beta())) + forum = get_object_or_404(Forum, pk=settings.BETA_FORUM_ID) + create_topic(author = request.user, + forum = forum, + title = u"[beta][tutoriel]{0}".format(tutorial.title), + subtitle = u"{}".format(tutorial.description), + text = msg, + key = tutorial.pk + ) + else: + msg = \ + (u'Bonjour, !\n\n' + u'La beta du tutoriel a été mise à jour.' + u'\n\n-> [Lien de la beta du tutoriel : {0}]({1}) <-\n\n' + u'\n\nMerci pour vos relectures'.format(tutorial.title, + settings.SITE_URL + tutorial.get_absolute_url_beta())) + unlock_topic(topic) + send_post(topic, msg) + messages.success(request, u"La BETA sur ce tutoriel a bien été mise à jour.") else: messages.error(request, u"Impossible de mettre à jour la BETA sur ce tutoriel.") @@ -614,6 +681,13 @@ def modify_tutorial(request): elif "desactiv_beta" in request.POST: tutorial.sha_beta = None tutorial.save() + topic = Topic.objects.filter(key=tutorial.pk, forum__pk=settings.BETA_FORUM_ID).first() + if topic is not None: + msg = \ + (u'Désactivation de la beta du tutoriel {}' + u'\n\nPour plus d\'information envoyez moi un MP'.format(tutorial.title)) + lock_topic(topic) + send_post(topic, msg) messages.info(request, u"La BETA sur ce tutoriel a été désactivée.") return redirect(tutorial.get_absolute_url()) diff --git a/zds/utils/forums.py b/zds/utils/forums.py new file mode 100644 index 0000000000..ed1334c366 --- /dev/null +++ b/zds/utils/forums.py @@ -0,0 +1,73 @@ +# coding: utf-8 + +from datetime import datetime +from zds.forum.models import Topic, Post, follow +from zds.forum.views import get_tag_by_title +from django.core.mail import EmailMultiAlternatives +from django.conf import settings +from django.template import Context +from django.template.loader import get_template +from zds.utils.templatetags.emarkdown import emarkdown + +def create_topic( + author, + forum, + title, + subtitle, + text, + key): + """create topic in forum""" + + (tags, title_only) = get_tag_by_title(title[:80]) + + # Creating the thread + n_topic = Topic() + n_topic.forum = forum + n_topic.title = title_only + n_topic.subtitle = subtitle + n_topic.pubdate = datetime.now() + n_topic.author = author + n_topic.key = key + n_topic.save() + n_topic.add_tags(tags) + n_topic.save() + + # Add the first message + post = Post() + post.topic = n_topic + post.author = author + post.text = text + post.text_html = emarkdown(text) + post.pubdate = datetime.now() + post.position = 1 + post.save() + + n_topic.last_message = post + n_topic.save() + + follow(n_topic, user=author) + + return n_topic + +def send_post(topic, text): + + post = Post() + post.topic = topic + post.author = topic.author + post.text = text + post.text_html = emarkdown(text) + post.pubdate = datetime.now() + post.position = topic.last_message.position+1 + post.save() + + topic.last_message = post + topic.save() + +def lock_topic(topic): + topic.is_locked = True + topic.save() + +def unlock_topic(topic): + topic.is_locked = False + topic.save() + From 0f6552e8f0ab99d71557f65c05c5b772f1a57ab1 Mon Sep 17 00:00:00 2001 From: Alex-D Date: Tue, 29 Jul 2014 05:45:08 +0200 Subject: [PATCH 021/270] =?UTF-8?q?Corrige=20un=20petit=20d=C3=A9tail=20da?= =?UTF-8?q?ns=20les=20ML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/mp/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zds/mp/views.py b/zds/mp/views.py index cbe971777b..ffc7a19537 100644 --- a/zds/mp/views.py +++ b/zds/mp/views.py @@ -199,7 +199,8 @@ def new(request): destList.append(User.objects.get(username=username).username) except: pass - dest = ', '.join(destList) + if len(destList) > 0: + dest = ', '.join(destList) form = PrivateTopicForm(initial={ 'participants': dest, From 641c5de4d9b00cd6d5aa42dfac559ca4614991db Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 30 Jul 2014 16:34:17 +0200 Subject: [PATCH 022/270] nouvelle quote --- quotes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/quotes.txt b/quotes.txt index ef0ca12bbb..06336a9185 100644 --- a/quotes.txt +++ b/quotes.txt @@ -7,3 +7,4 @@ Un concentré de connaissances, sans ajout ni conservateur, sans zeste déplacé Faites un zeste envers votre prochain Ici, vous êtes libres de vos faits et zestes ZdS c'est la classe, tu peux pas zeste +Alea Jacta Zeste ! From d8fe87fb54bf1c84efe0b02944427660250c025e Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 30 Jul 2014 16:46:29 +0200 Subject: [PATCH 023/270] =?UTF-8?q?mise=20=C3=A0=20jour=20d'une=20citation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- quotes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/quotes.txt b/quotes.txt index 06336a9185..1563c3ecc7 100644 --- a/quotes.txt +++ b/quotes.txt @@ -8,3 +8,4 @@ Faites un zeste envers votre prochain Ici, vous êtes libres de vos faits et zestes ZdS c'est la classe, tu peux pas zeste Alea Jacta Zeste ! +Partagez vos connaissances pour la beauté du zeste From 45c2ac347fc391255830bb176d2c95625dd03650 Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 4 Aug 2014 22:18:58 +0200 Subject: [PATCH 024/270] ajout de l'import d'une archive d'un tutoriel --- templates/tutorial/tutorial/import.html | 9 +- zds/tutorial/forms.py | 29 ++++- zds/tutorial/tests.py | 2 +- zds/tutorial/views.py | 61 +++++++---- zds/utils/tutorials.py | 137 +++++++++++++++++++++++- 5 files changed, 210 insertions(+), 28 deletions(-) diff --git a/templates/tutorial/tutorial/import.html b/templates/tutorial/tutorial/import.html index ccf831b52f..46c606c516 100644 --- a/templates/tutorial/tutorial/import.html +++ b/templates/tutorial/tutorial/import.html @@ -24,12 +24,17 @@

    {% block content %} +

    Envoi d'une archive

    - Vous êtes l'auteur d'un cours sur le SdZ ? Nous l'avons récupéré pour vous ! Il est disponible hors-ligne et n'attend plus que votre accord pour être publié. Il vous suffit d'en faire la demande à un membre du Staff via le forum ou directement par MP. + Vous avez commencé a rédiger votre tutoriel sur l'éditeur en ligne de Zeste de Savoir, vous avez téléchargé l'archive correspondate et vous avez fait des modifications sur les fichiers en hors-ligne, et vous souhaitez maintenant importer ces modifications sur le site ? Faites une archive zip (seul format supporté actuellement) du répertoire dans lequel se trouve les fichiers de votre tutoriel et renseignez les deux champs ci-dessous, puis cliquez sur importer.

    - + {% crispy form_archive %}

    Envoi de fichiers .tuto

    +

    + Vous êtes l'auteur d'un cours sur le SdZ ? Nous l'avons récupéré pour vous ! Il est disponible en hors-ligne et n'attend plus que votre accord pour être publié. Il vous suffit d'en faire la demande à un membre du Staff via le forum ou directement par MP. +

    +

    Attention, le fichier attendu n'est pas un .tuto du Site du Zéro, mais un format intermédiaire qui contient du Markdown et non du zCode. Contactez un membre du Staff pour demander la conversion de votre fichier.

    diff --git a/zds/tutorial/forms.py b/zds/tutorial/forms.py index 884ec0ebd8..eaa313047c 100644 --- a/zds/tutorial/forms.py +++ b/zds/tutorial/forms.py @@ -293,10 +293,37 @@ def __init__(self, *args, **kwargs): self.helper.layout = Layout( Field('file'), Field('images'), - Submit('submit', 'Importer'), + Submit('import-tuto', 'Importer le .tuto'), ) super(ImportForm, self).__init__(*args, **kwargs) +class ImportArchiveForm(forms.Form): + + file = forms.FileField( + label='Sélectionnez l\'archive de votre tutoriel', + required=True + ) + + tutorial = forms.ModelChoiceField( + label="Tutoriel vers lequel vous souhaitez importer votre archive", + queryset=Tutorial.objects.none(), + required=True + ) + + def __init__(self, user, *args, **kwargs): + super(ImportArchiveForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_class = 'content-wrapper' + self.helper.form_method = 'post' + self.fields['tutorial'].queryset=Tutorial.objects.filter(authors__in=[user]) + + self.helper.layout = Layout( + Field('file'), + Field('tutorial'), + Submit('import-archive', 'Importer l\'archive'), + ) + + # Notes diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py index b54efefd4c..17eb656d0e 100644 --- a/zds/tutorial/tests.py +++ b/zds/tutorial/tests.py @@ -474,7 +474,7 @@ def test_url_for_guest(self): self.chapter2_1.slug]), follow=False) self.assertEqual(result.status_code, 302) - + def test_workflow_tuto(self): """Test workflow of tutorial.""" diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 0ad79dc18e..26c614dae9 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -38,7 +38,7 @@ from lxml import etree from forms import TutorialForm, PartForm, ChapterForm, EmbdedChapterForm, \ - ExtractForm, ImportForm, NoteForm, AskValidationForm, ValidForm, RejectForm + ExtractForm, ImportForm, ImportArchiveForm, NoteForm, AskValidationForm, ValidForm, RejectForm from models import Tutorial, Part, Chapter, Extract, Validation, never_read, \ mark_read, Note from zds.gallery.models import Gallery, UserGallery, Image @@ -53,7 +53,7 @@ from zds.utils.mps import send_mp from zds.utils.paginator import paginator_range from zds.utils.templatetags.emarkdown import emarkdown -from zds.utils.tutorials import get_blob, export_tutorial_to_md, move +from zds.utils.tutorials import get_blob, export_tutorial_to_md, move, import_archive from zds.utils.misc import compute_hash, content_has_changed def render_chapter_form(chapter): @@ -2405,31 +2405,46 @@ def local_import(request): @login_required def import_tuto(request): if request.method == "POST": - form = ImportForm(request.POST, request.FILES) - - # check extension - - if "file" in request.FILES: - filename = str(request.FILES["file"]) - ext = filename.split(".")[-1] - if ext == "tuto": - import_content(request, request.FILES["file"], - request.FILES["images"], "") + # for import tuto + if "import-tuto" in request.POST: + form = ImportForm(request.POST, request.FILES) + form_archive = ImportArchiveForm(user=request.user) + if "file" in request.FILES: + filename = str(request.FILES["file"]) + ext = filename.split(".")[-1] + if ext == "tuto": + import_content(request, request.FILES["file"], + request.FILES["images"], "") + else: + raise Http404 + return redirect(reverse("zds.member.views.tutorials")) + elif "import-archive" in request.POST: + form = ImportForm() + form_archive = ImportArchiveForm(request.user, request.POST, request.FILES) + (check, reason) = import_archive(request) + if not check: + messages.error(request, reason) else: - raise Http404 - return redirect(reverse("zds.member.views.tutorials")) + messages.success(request, reason) + return redirect(reverse("zds.member.views.tutorials")) + + else: form = ImportForm() - profile = get_object_or_404(Profile, user=request.user) - oldtutos = [] - if profile.sdz_tutorial: - olds = profile.sdz_tutorial.strip().split(":") - else: - olds = [] - for old in olds: - oldtutos.append(get_info_old_tuto(old)) + form_archive = ImportArchiveForm(user=request.user) + + profile = get_object_or_404(Profile, user=request.user) + oldtutos = [] + if profile.sdz_tutorial: + olds = profile.sdz_tutorial.strip().split(":") + else: + olds = [] + for old in olds: + oldtutos.append(get_info_old_tuto(old)) return render_template( - "tutorial/tutorial/import.html", {"form": form, "old_tutos": oldtutos}) + "tutorial/tutorial/import.html", {"form": form, + "form_archive":form_archive, + "old_tutos": oldtutos}) # Handling repo diff --git a/zds/utils/tutorials.py b/zds/utils/tutorials.py index fa53615985..357704acac 100644 --- a/zds/utils/tutorials.py +++ b/zds/utils/tutorials.py @@ -6,8 +6,11 @@ from django.template import Context from django.template.loader import get_template from git import * +import tarfile from zds.utils import slugify +from zds.utils.models import Licence + # Export-to-dict functions def export_chapter(chapter, export_all=True): @@ -289,4 +292,136 @@ def move(obj, new_pos, position_f, parent_f, children_fn): # we can do it now setattr(obj, position_f, new_pos) - +def check_json(data, tutorial): + from zds.tutorial.models import Tutorial, Part, Chapter, Extract + if "title" not in data: + return (False, u"Le tutoriel que vous souhaitez importer manque de titre") + if "type" not in data: + return (False, u"Les métadonnées du tutoriel à importer ne nous permettent pas de connaître son type") + elif tutorial.is_mini(): + if data["type"] == "BIG": + return (False, u"Vous essayez d'importer un big tutoriel dans un mini tutoriel") + elif "chapter" not in data: + return (False, u"La structure de vos métadonnées est incohérente") + elif "extracts" not in data["chapter"]: + return (False, u"La structure de vos extraits est incohérente") + else: + for extract in data["chapter"]["extracts"]: + if "pk" not in extract or "title" not in extract or "text" not in extract: + return (False, u"Un de vos extraits est mal renseigné") + elif not Extract.objects.filter(pk=extract["pk"]).exists(): + return (False, u"L'extrait « {} » n'existe pas dans notre base".format(extract["title"])) + elif not Extract.objects.filter(pk=extract["pk"], chapter__tutorial__pk=tutorial.pk).exists(): + return (False, u"Vous n'êtes pas autorisé à modifier l'extrait « {} »".format(extract["title"])) + elif tutorial.is_big(): + if data["type"] == "MINI": + return (False, u"Vous essayez d'importer un mini tutoriel dans un big tutoriel") + elif "parts" not in data: + return (False, u"La structure de vos métadonnées est incohérente") + else: + for part in data["parts"]: + if "pk" not in part or "title" not in part: + return (False, u"La structure de vos parties est incohérente") + elif not Part.objects.filter(pk=part["pk"]).exists(): + return (False, u"La partie « {} » n'existe pas dans notre base".format(part["title"])) + elif not Part.objects.filter(pk=part["pk"], tutorial__pk=tutorial.pk).exists(): + return (False, u"La partie « {} » n'est pas dans le tutoriel à modifier ".format(part["title"])) + if "chapters" in part: + for chapter in part["chapters"]: + if "pk" not in chapter or "title" not in chapter: + return (False, u"La structure de vos chapitres est incohérente") + elif not Chapter.objects.filter(pk=chapter["pk"]).exists(): + return (False, u"Le chapitre « {} » n'existe pas dans notre base".format(chapter["title"])) + elif not Chapter.objects.filter(pk=chapter["pk"], part__tutorial__pk=tutorial.pk).exists(): + return (False, u"Le chapitre « {} » n'est pas dans le tutoriel a modifier".format(chapter["title"])) + elif "extracts" in chapter: + for extract in chapter["extracts"]: + if "pk" not in extract or "title" not in extract or "text" not in extract: + return (False, u"Un de vos extraits est mal renseigné") + elif not Extract.objects.filter(pk=extract["pk"]).exists(): + return (False, u"L'extrait « {} » n'existe pas dans notre base".format(extract["title"])) + elif not Extract.objects.filter(pk=extract["pk"], chapter__part__tutorial__pk=tutorial.pk).exists(): + return (False, u"Vous n'êtes pas autorisé à modifier l'extrait « {} » ".format(extract["title"])) + return (True, None) + +def import_archive(request): + from django.core.files import File + from zds.tutorial.models import Tutorial + import zipfile + import shutil + import os + try: + import ujson as json_reader + except: + try: + import simplejson as json_reader + except: + import json as json_reader + + archive = request.FILES["file"] + tutorial = Tutorial.objects.get(pk=request.POST["tutorial"]) + ext = str(archive).split(".")[-1] + if ext=="zip": + zfile = zipfile.ZipFile(archive, "a") + for i in zfile.namelist(): + ph = i[i.index(os.sep)+1:] + if ph=="manifest.json": + json_data = zfile.read(i) + mandata = json_reader.loads(json_data) + (check, reason) = check_json(mandata, tutorial) + if not check: + return (check, reason) + tutorial.title = mandata['title'] + if "description" in mandata: + tutorial.description = mandata['description'] + if "introduction" in mandata: + tutorial.introduction = mandata['introduction'] + if "conclusion" in mandata: + tutorial.conclusion = mandata['conclusion'] + if "licence" in mandata: + tutorial.licence = Licence.objects.filter(code=json["licence"]).all()[0] + old_path = tutorial.get_path() + tutorial.save() + new_path = tutorial.get_path() + shutil.move(old_path, new_path) + + break + #delete old file + for filename in os.listdir(tutorial.get_path()) : + if not filename.startswith('.'): + mf = os.path.join(tutorial.get_path(), filename) + if os.path.isfile(mf): + os.remove(mf) + elif os.path.isdir(mf): + shutil.rmtree(mf) + #copy new file + for i in zfile.namelist(): + ph = i[i.index(os.sep)+1:] + if ph!="": + ph_dest = os.path.join(tutorial.get_path(), ph) + try: + data = zfile.read(i) + fp = open(ph_dest, "wb") + fp.write(data) + fp.close() + except IOError: + try: + os.makedirs(ph_dest) + except: + pass + zfile.close() + # save in git + repo = Repo(tutorial.get_path()) + index = repo.index + index.add(["."]) + msg = "Import du tutoriel" + + aut_user = str(request.user.pk) + aut_email = str(request.user.email) + if aut_email is None or aut_email.strip() == "": aut_email = "inconnu@zestedesavoir.com" + com = index.commit( msg.encode("utf-8"), + author=Actor(aut_user, aut_email), + committer=Actor(aut_user, aut_email)) + tutorial.sha_draft = com.hexsha + tutorial.save() + return (True, u"Le tutoriel {} a été importé avec succès".format(tutorial.title)) \ No newline at end of file From cb0b51a4912c0e78b047a93e7932e889d5ad82da Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 5 Aug 2014 08:14:09 +0200 Subject: [PATCH 025/270] correction des tests --- zds/tutorial/tests.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py index 17eb656d0e..8a7eb269c9 100644 --- a/zds/tutorial/tests.py +++ b/zds/tutorial/tests.py @@ -399,7 +399,8 @@ def test_import_tuto(self): 'tuto', 'temps-reel-avec-irrlicht', 'images.zip'), - 'r')}, + 'r'), + 'import-tuto': "importer"}, follow=False) self.assertEqual(result.status_code, 302) @@ -2139,7 +2140,8 @@ def test_import_tuto(self): 'tuto', 'temps-reel-avec-irrlicht', 'images.zip'), - 'r')}, + 'r'), + 'import-tuto': "importer"}, follow=False) self.assertEqual(result.status_code, 302) From 9e7a8c83dfc1070e93c421430c1ed59f82c8df3d Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 5 Aug 2014 09:43:05 +0200 Subject: [PATCH 026/270] correction d'un test --- zds/tutorial/tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py index 8a7eb269c9..c6253e6ac4 100644 --- a/zds/tutorial/tests.py +++ b/zds/tutorial/tests.py @@ -2121,7 +2121,7 @@ def test_dislike_note(self): self.assertEqual(Note.objects.get(pk=note3.pk).dislike, 0) def test_import_tuto(self): - """Test import of big tuto.""" + """Test import of mini tuto.""" result = self.client.post( reverse('zds.tutorial.views.import_tuto'), { @@ -2130,15 +2130,15 @@ def test_import_tuto(self): settings.SITE_ROOT, 'fixtures', 'tuto', - 'temps-reel-avec-irrlicht', - 'temps-reel-avec-irrlicht.tuto'), + 'securisez-vos-mots-de-passe-avec-lastpass', + 'securisez-vos-mots-de-passe-avec-lastpass.tuto'), 'r'), 'images': open( os.path.join( settings.SITE_ROOT, 'fixtures', 'tuto', - 'temps-reel-avec-irrlicht', + 'securisez-vos-mots-de-passe-avec-lastpass', 'images.zip'), 'r'), 'import-tuto': "importer"}, From 8ab2351270f103460813a2a13b616b12697c5521 Mon Sep 17 00:00:00 2001 From: Akulen Date: Thu, 7 Aug 2014 14:19:02 +0200 Subject: [PATCH 027/270] Correction du mode preview de l'edition d'articles --- templates/article/member/edit.html | 4 ++++ zds/article/views.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/templates/article/member/edit.html b/templates/article/member/edit.html index f98ed703be..50511bf3aa 100644 --- a/templates/article/member/edit.html +++ b/templates/article/member/edit.html @@ -54,4 +54,8 @@

    Éditer l'article : {{ article.title }}

    {% endif %} {% crispy form %} + + {% if form.text.value %} + {% include "misc/previsualization.part.html" with text=form.text.value %} + {% endif %} {% endblock %} diff --git a/zds/article/views.py b/zds/article/views.py index 44c74b5575..c97aea0b33 100644 --- a/zds/article/views.py +++ b/zds/article/views.py @@ -288,6 +288,25 @@ def edit(request): json = article.load_json() if request.method == 'POST': + # Using the "preview button" + if 'preview' in request.POST: + image = None + if 'image' in request.FILES: + image = request.FILES['image'] + form = ArticleForm(initial={ + 'title': request.POST['title'], + 'description': request.POST['description'], + 'text': request.POST['text'], + 'image': image, + 'subcategory': request.POST.getlist('subcategory'), + 'licence': request.POST['licence'] + }) + return render_template('article/member/edit.html', { + 'article': article, + 'text': request.POST['text'], + 'form': form + }) + form = ArticleForm(request.POST, request.FILES) if form.is_valid(): # Update article with data. From 3a6d01364e1b38785250d788de19d5b6f3d51145 Mon Sep 17 00:00:00 2001 From: Akulen Date: Sat, 9 Aug 2014 14:33:59 +0200 Subject: [PATCH 028/270] Fix : licence optionnelle --- zds/article/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zds/article/views.py b/zds/article/views.py index c97aea0b33..8f9b5dfd41 100644 --- a/zds/article/views.py +++ b/zds/article/views.py @@ -291,15 +291,18 @@ def edit(request): # Using the "preview button" if 'preview' in request.POST: image = None + licence = None if 'image' in request.FILES: image = request.FILES['image'] + if 'licence' in request.POST: + licence = request.POST['licence'] form = ArticleForm(initial={ 'title': request.POST['title'], 'description': request.POST['description'], 'text': request.POST['text'], 'image': image, 'subcategory': request.POST.getlist('subcategory'), - 'licence': request.POST['licence'] + 'licence': licence }) return render_template('article/member/edit.html', { 'article': article, From e82596d427c96fa212b85a8a8f21103630504eb9 Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 12 Aug 2014 03:02:33 +0200 Subject: [PATCH 029/270] fichier inutile depuis un moment --- zds/utils/templatetags/cssstyles.py | 152 ---------------------------- 1 file changed, 152 deletions(-) delete mode 100644 zds/utils/templatetags/cssstyles.py diff --git a/zds/utils/templatetags/cssstyles.py b/zds/utils/templatetags/cssstyles.py deleted file mode 100644 index d090454ff3..0000000000 --- a/zds/utils/templatetags/cssstyles.py +++ /dev/null @@ -1,152 +0,0 @@ -# coding: utf-8 - -import random -import re - -from markdown.extensions import Extension -from markdown.postprocessors import Postprocessor - - -HTMLELEMENTS = ['p', 'a', 'div', 'blockquote', 'hr'] -HTMLATTRIBUTES = ['style', 'id', 'href', 'class'] -STYLE_REG = reg = re.compile(r'\[([a-z]+)\]\{(.+?)\r?\n\}', re.DOTALL) - -getStyle = lambda tag, blockcontent, blockid: { - 'secret': {'1hr': {}, - '7div': - {'class': - ['hidden-block'], - 'id': - ['show-' + str(blockid)], - 'text': - {'3p_': - {'text': - blockcontent - }, - '1a': - {'href': - '#show-' + str(blockid), - 'id': - 'display-' + str(blockid), - 'class': - ['hidden-link-show'], - 'text': - u'Contenu masqué (cliquez pour afficher)', - }, - '2a': - {'href': - '#hide-' + str(blockid), - 'id': - 'hide-' + str(blockid), - 'class': - ['hidden-link-hide'], - 'text': - 'Masquer le contenu', - }, - }, - }, - '4hr': {} - - - }, -}[tag] - - -class Element(): - __tag = None - __attrs = {} - __text = "" - - def __init__(self, tag): - self.__tag = tag - self.__attrs = {} - self.__text = "" - - def setattr(self, name, value): - self.__attrs[name] = unicode(value) - - def settext(self, text): - self.__text = unicode(text) - - def completeWith(self, elem): - for attr in elem.__attrs: - self.setattr(attr, elem.__attrs[attr]) - self.__text = elem.__text - - def __repr__(self): - def formatAttr(name, value): - return unicode(name) + '="' + unicode(value) + '"' - if len(self.__text) > 0: - return '<' + unicode(self.__tag) + ' ' + \ - ' '.join([formatAttr(attr, self.__attrs[attr]) for attr in self.__attrs.keys()] - ) + '>' + unicode(self.__text) + '' - else: - return '<' + unicode(self.__tag) + ' ' + ' '.join( - [formatAttr(attr, self.__attrs[attr]) for attr in self.__attrs.keys()]) + ' />' - - -def cleanElement(element_name): - return re.sub(r'(?:[0-9]*)([a-z]+)_*', r'\1', element_name) - - -def localProcess(elements, parent=None): - for elem in elements.keys(): - value = elements[elem] - if cleanElement(elem) in HTMLELEMENTS: - el = Element(cleanElement(elem)) - for e in localProcess(value, el): - el = e - yield el - elif elem in HTMLATTRIBUTES: - if isinstance(value, str) or isinstance(value, unicode): - parent.setattr(elem, value) - elif isinstance(value, list): - parent.setattr(elem, ' '.join(value)) - elif isinstance(value, dict): - parent.setattr(elem, ';'.join( - [':'.join([n, value[n]]) for n in value])) - else: - parent.setattr(elem, value) - yield parent - elif elem == 'text': - if isinstance(value, dict): - parent.settext( - ''.join([unicode(a) for a in localProcess(value)])) - elif isinstance(value, str) or isinstance(value, unicode): - parent.settext(unicode(value)) - elif isinstance(value, list): - parent.settext(' '.join(value)) - else: - parent.settext("") - yield parent - else: - yield parent - - -def processStyle(styles): - return ''.join([unicode(a) for a in localProcess(styles)]) - - -def process(match): - try: - return processStyle( - getStyle( - match.group(1), - match.group(2), - random.randint( - 1000, - 9999))) - except KeyError: - return "Style non-valide" - - -class CSSPostprocessor(Postprocessor): - - def run(self, text): - return re.sub(STYLE_REG, process, text, re.DOTALL) - - -class StyleExtension(Extension): - - def extendMarkdown(self, md, md_globals): - md.postprocessors.add('cssstyle', CSSPostprocessor(md), '_begin') From 1ca661f6edcc719352e5f4689a271aa6e7df26cb Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 19 Aug 2014 00:35:24 +0200 Subject: [PATCH 030/270] correction du bug du dossier d'import --- zds/tutorial/views.py | 10 +++++----- zds/utils/tutorials.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 26c614dae9..5d41816291 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -2705,7 +2705,7 @@ def download(request): repo = Repo(ph) repo.archive(open(ph + ".tar", "w")) response = HttpResponse(open(ph + ".tar", "rb").read(), - mimetype="application/tar") + content_type="application/tar") response["Content-Disposition"] = \ "attachment; filename={0}.tar".format(tutorial.slug) return response @@ -2723,7 +2723,7 @@ def download_markdown(request): ".md") response = HttpResponse( open(phy_path, "rb").read(), - mimetype="application/txt") + content_type="application/txt") response["Content-Disposition"] = \ "attachment; filename={0}.md".format(tutorial.slug) return response @@ -2742,7 +2742,7 @@ def download_html(request): raise Http404 response = HttpResponse( open(phy_path, "rb").read(), - mimetype="text/html") + content_type="text/html") response["Content-Disposition"] = \ "attachment; filename={0}.html".format(tutorial.slug) return response @@ -2761,7 +2761,7 @@ def download_pdf(request): raise Http404 response = HttpResponse( open(phy_path, "rb").read(), - mimetype="application/pdf") + content_type="application/pdf") response["Content-Disposition"] = \ "attachment; filename={0}.pdf".format(tutorial.slug) return response @@ -2780,7 +2780,7 @@ def download_epub(request): raise Http404 response = HttpResponse( open(phy_path, "rb").read(), - mimetype="application/epub") + content_type="application/epub") response["Content-Disposition"] = \ "attachment; filename={0}.epub".format(tutorial.slug) return response diff --git a/zds/utils/tutorials.py b/zds/utils/tutorials.py index 357704acac..5705dee203 100644 --- a/zds/utils/tutorials.py +++ b/zds/utils/tutorials.py @@ -364,7 +364,7 @@ def import_archive(request): if ext=="zip": zfile = zipfile.ZipFile(archive, "a") for i in zfile.namelist(): - ph = i[i.index(os.sep)+1:] + ph = i if ph=="manifest.json": json_data = zfile.read(i) mandata = json_reader.loads(json_data) @@ -396,7 +396,7 @@ def import_archive(request): shutil.rmtree(mf) #copy new file for i in zfile.namelist(): - ph = i[i.index(os.sep)+1:] + ph = i if ph!="": ph_dest = os.path.join(tutorial.get_path(), ph) try: From 7ba8d04a12d9053908e572e80472d2f3d9b84164 Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 19 Aug 2014 00:35:51 +0200 Subject: [PATCH 031/270] ajout des tests de validation de la feature --- zds/tutorial/tests.py | 128 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py index c6253e6ac4..35edf51f49 100644 --- a/zds/tutorial/tests.py +++ b/zds/tutorial/tests.py @@ -2,6 +2,9 @@ import os import shutil +import tarfile +import zipfile +from urllib import urlretrieve import HTMLParser from django.conf import settings from django.core import mail @@ -20,6 +23,8 @@ from zds.tutorial.models import Note, Tutorial, Validation, Extract, Part, Chapter from zds.utils.models import SubCategory, Licence, Alert from zds.utils.misc import compute_hash +from django.conf.global_settings import MEDIA_ROOT +from django.test.client import RequestFactory @override_settings(MEDIA_ROOT=os.path.join(SITE_ROOT, 'media-test')) @override_settings(REPO_PATH=os.path.join(SITE_ROOT, 'tutoriels-private-test')) @@ -37,6 +42,7 @@ def setUp(self): settings.EMAIL_BACKEND = \ 'django.core.mail.backends.locmem.EmailBackend' + self.factory = RequestFactory() self.mas = ProfileFactory().user settings.BOT_ACCOUNT = self.mas.username @@ -120,6 +126,75 @@ def setUp(self): mail.outbox = [] + def test_import_archive(self): + #create temporary data directory + temp = os.path.join(SITE_ROOT, "temp") + + # download tar + if not os.path.isdir(temp): + os.makedirs(temp, mode=0777) + tarpath = os.path.join(MEDIA_ROOT, "temp", self.bigtuto.get_phy_slug()+".tar") + result = self.client.get( + reverse('zds.tutorial.views.download')+"?tutoriel={}".format(str(self.bigtuto.pk)), + follow=False) + + save_path=os.path.join(settings.REPO_PATH, self.bigtuto.get_phy_slug()+".tar") + shutil.copy(save_path, tarpath) + tar = tarfile.open(tarpath) + tar.extractall(path=os.path.join(temp, self.bigtuto.get_phy_slug())) + tar.close() + + self.assertTrue(os.path.isdir(os.path.join(temp, self.bigtuto.get_phy_slug()))) + + # update markdown files + up_intro_tfile = open(os.path.join(temp, self.bigtuto.get_phy_slug(), self.bigtuto.introduction), "a") + up_intro_tfile.write(u"preuve de modification de l'introduction") + up_intro_tfile.close() + up_conclu_tfile = open(os.path.join(temp, self.bigtuto.get_phy_slug(), self.bigtuto.conclusion), "a") + up_conclu_tfile.write(u"preuve de modification de la conclusion") + up_conclu_tfile.close() + parts = Part.objects.filter(tutorial__pk=self.bigtuto.pk) + for part in parts: + up_intro_pfile = open(os.path.join(temp, self.bigtuto.get_phy_slug(), part.introduction), "a") + up_intro_pfile.write(u"preuve de modification de l'introduction") + up_intro_pfile.close() + up_conclu_pfile = open(os.path.join(temp, self.bigtuto.get_phy_slug(), part.conclusion), "a") + up_conclu_pfile.write(u"preuve de modification de la conclusion") + up_conclu_pfile.close() + chapters = Chapter.objects.filter(part__pk=part.pk) + for chapter in chapters: + up_intro_cfile = open(os.path.join(temp, self.bigtuto.get_phy_slug(), chapter.introduction), "a") + up_intro_cfile.write(u"preuve de modification de l'introduction") + up_intro_cfile.close() + up_conclu_cfile = open(os.path.join(temp, self.bigtuto.get_phy_slug(), chapter.conclusion), "a") + up_conclu_cfile.write(u"preuve de modification de la conclusion") + up_conclu_cfile.close() + + # zip directory + shutil.make_archive(os.path.join(temp, self.bigtuto.get_phy_slug()), + "zip", + os.path.join(temp, self.bigtuto.get_phy_slug())) + + self.assertTrue(os.path.isfile(os.path.join(temp, self.bigtuto.get_phy_slug()+".zip"))) + + # import zip archive + result = self.client.post( + reverse('zds.tutorial.views.import_tuto'), + { + 'file': open( + os.path.join( + temp, + os.path.join(temp, self.bigtuto.get_phy_slug()+".zip")), + 'r'), + 'tutorial': self.bigtuto.pk, + 'import-archive': "importer"}, + follow=False) + self.assertEqual(result.status_code, 302) + self.assertEqual(Tutorial.objects.all().count(), 1) + + #delete temporary data directory + shutil.rmtree(temp) + def test_add_note(self): """To test add note for tutorial.""" user1 = ProfileFactory().user @@ -1826,6 +1901,59 @@ def setUp(self): mail.outbox = [] + def test_import_archive(self): + #create temporary data directory + temp = os.path.join(SITE_ROOT, "temp") + + # download tar + if not os.path.isdir(temp): + os.makedirs(temp, mode=0777) + tarpath = os.path.join(MEDIA_ROOT, "temp", self.minituto.get_phy_slug()+".tar") + result = self.client.get( + reverse('zds.tutorial.views.download')+"?tutoriel={}".format(str(self.minituto.pk)), + follow=False) + + save_path=os.path.join(settings.REPO_PATH, self.minituto.get_phy_slug()+".tar") + shutil.copy(save_path, tarpath) + tar = tarfile.open(tarpath) + tar.extractall(path=os.path.join(temp, self.minituto.get_phy_slug())) + tar.close() + + self.assertTrue(os.path.isdir(os.path.join(temp, self.minituto.get_phy_slug()))) + + # update markdown files + up_intro_tfile = open(os.path.join(temp, self.minituto.get_phy_slug(), self.minituto.introduction), "a") + up_intro_tfile.write(u"preuve de modification de l'introduction") + up_intro_tfile.close() + up_conclu_tfile = open(os.path.join(temp, self.minituto.get_phy_slug(), self.minituto.conclusion), "a") + up_conclu_tfile.write(u"preuve de modification de la conclusion") + up_conclu_tfile.close() + + # zip directory + shutil.make_archive(os.path.join(temp, self.minituto.get_phy_slug()), + "zip", + os.path.join(temp, self.minituto.get_phy_slug())) + + self.assertTrue(os.path.isfile(os.path.join(temp, self.minituto.get_phy_slug()+".zip"))) + + # import zip archive + result = self.client.post( + reverse('zds.tutorial.views.import_tuto'), + { + 'file': open( + os.path.join( + temp, + os.path.join(temp, self.minituto.get_phy_slug()+".zip")), + 'r'), + 'tutorial': self.minituto.pk, + 'import-archive': "importer"}, + follow=False) + self.assertEqual(result.status_code, 302) + self.assertEqual(Tutorial.objects.all().count(), 1) + + #delete temporary data directory + shutil.rmtree(temp) + def add_test_extract_named_introduction(self): """test the use of an extract named introduction""" From 15d4f57f8ec90f93b38efdceb1417815bd31ac7c Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 19 Aug 2014 00:45:57 +0200 Subject: [PATCH 032/270] ajout d'un message d'erreur en cas d'archive non zip --- zds/utils/tutorials.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/zds/utils/tutorials.py b/zds/utils/tutorials.py index 5705dee203..b573a29f47 100644 --- a/zds/utils/tutorials.py +++ b/zds/utils/tutorials.py @@ -363,6 +363,7 @@ def import_archive(request): ext = str(archive).split(".")[-1] if ext=="zip": zfile = zipfile.ZipFile(archive, "a") + json_here = False for i in zfile.namelist(): ph = i if ph=="manifest.json": @@ -384,8 +385,10 @@ def import_archive(request): tutorial.save() new_path = tutorial.get_path() shutil.move(old_path, new_path) - + json_here = True break + if not json_here: + return (False, u"L'archive n'a pas pu être importée car le fichier manifest.json (fichier de métadonnées est introuvable).") #delete old file for filename in os.listdir(tutorial.get_path()) : if not filename.startswith('.'): @@ -424,4 +427,7 @@ def import_archive(request): committer=Actor(aut_user, aut_email)) tutorial.sha_draft = com.hexsha tutorial.save() - return (True, u"Le tutoriel {} a été importé avec succès".format(tutorial.title)) \ No newline at end of file + + return (True, u"Le tutoriel {} a été importé avec succès".format(tutorial.title)) + else: + return (False, u"L'archive n'a pas pu être importée car elle n'est pas au format zip") \ No newline at end of file From f50d4429711122e185d3d2c30f6fb5e7e551226b Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 20 Aug 2014 01:20:41 +0200 Subject: [PATCH 033/270] ajout des tops tags dans la top bar --- templates/base.html | 16 ++++++++++++++-- zds/settings.py | 3 +++ zds/utils/models.py | 5 +++++ zds/utils/templatetags/topbar.py | 33 ++++++++++++++++++++++++++++---- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/templates/base.html b/templates/base.html index d508b40840..e87f39e7cd 100644 --- a/templates/base.html +++ b/templates/base.html @@ -215,8 +215,8 @@ diff --git a/zds/settings.py b/zds/settings.py index a185b70643..1d20795a3c 100644 --- a/zds/settings.py +++ b/zds/settings.py @@ -261,6 +261,9 @@ REPO_PATH_PROD = os.path.join(SITE_ROOT, 'tutoriels-public') REPO_ARTICLE_PATH = os.path.join(SITE_ROOT, 'articles-data') +# Constant for tags +TOP_TAG_MAX = 2 + # Constants for pagination POSTS_PER_PAGE = 21 TOPICS_PER_PAGE = 21 diff --git a/zds/utils/models.py b/zds/utils/models.py index d713e82ee9..923003848f 100644 --- a/zds/utils/models.py +++ b/zds/utils/models.py @@ -275,6 +275,11 @@ class Meta: def __unicode__(self): """Textual Link Form.""" return u"{0}".format(self.title) + + def get_absolute_url(self): + return reverse('zds.forum.views.find_topic_by_tag', + kwargs={'tag_pk': self.pk, + 'tag_slug': self.slug}) def save(self, *args, **kwargs): self.title = smart_text(self.title).lower() diff --git a/zds/utils/templatetags/topbar.py b/zds/utils/templatetags/topbar.py index 79f89a9587..7ce7d2a9a4 100644 --- a/zds/utils/templatetags/topbar.py +++ b/zds/utils/templatetags/topbar.py @@ -1,10 +1,12 @@ # coding: utf-8 from django import template - -from zds.forum.models import Category as fCategory, Forum +from django.conf import settings +from django.db.models import Count +import itertools +from zds.forum.models import Category as fCategory, Forum, Topic from zds.tutorial.models import Tutorial -from zds.utils.models import Category, SubCategory, CategorySubCategory +from zds.utils.models import Category, SubCategory, CategorySubCategory, Tag register = template.Library() @@ -31,8 +33,31 @@ def top_categories(user): else: cats[key] = [forum] - return cats + topics = Topic.objects.filter(forum__in=forums) + tgs = Topic.objects\ + .values('tags', 'pk')\ + .distinct()\ + .filter(forum__in=forums, tags__isnull=False) + + #for tg in tgs: + # print tg + cts = {} + for key, group in itertools.groupby(tgs, lambda item: item["tags"]): + for thing in group: + if key in cts: cts[key]+=1 + else: cts[key]=1 + cpt=0 + top_tag =[] + sort_list = reversed(sorted(cts.iteritems(), key=lambda (k,v): (v,k))) + for key, value in sort_list: + top_tag.append(key) + cpt+=1 + if cpt >=settings.TOP_TAG_MAX : break + + tags=Tag.objects.filter(pk__in=top_tag) + + return {"tags":tags, "categories":cats} @register.filter('top_categories_tuto') def top_categories_tuto(user): From 05acdc9c04bafa86a69477d264b2191e99636c0b Mon Sep 17 00:00:00 2001 From: firm1 Date: Sun, 24 Aug 2014 20:58:32 +0200 Subject: [PATCH 034/270] correction de la page des forums --- templates/forum/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/forum/index.html b/templates/forum/index.html index 42e3538b52..1c3e992f9b 100644 --- a/templates/forum/index.html +++ b/templates/forum/index.html @@ -37,7 +37,7 @@ {% endblock %} \ No newline at end of file diff --git a/zds/forum/feeds.py b/zds/forum/feeds.py index e3fda2ddeb..d5fb6056e4 100644 --- a/zds/forum/feeds.py +++ b/zds/forum/feeds.py @@ -3,6 +3,7 @@ from django.contrib.syndication.views import Feed from django.utils.feedgenerator import Atom1Feed +from django.conf import settings from zds.utils.templatetags.emarkdown import emarkdown @@ -14,11 +15,34 @@ class LastPostsFeedRSS(Feed): link = '/forums/' description = (u'Les derniers messages ' u'parus sur le forum de Zeste de Savoir.') - - def items(self): - posts = Post.objects.filter(topic__forum__group__isnull=True)\ + + def get_object(self, request): + obj = {} + if "forum" in request.GET: + obj['forum']=request.GET["forum"] + if "tag" in request.GET: + obj['tag']=request.GET["tag"] + return obj + + def items(self, obj): + if "forum" in obj and "tag" in obj: + posts = Post.objects.filter(topic__forum__group__isnull=True, + topic__forum__pk=obj['forum'], + topic__tags__pk__in=[obj['tag']])\ + .order_by('-pubdate') + elif "forum" in obj and "tag" not in obj: + posts = Post.objects.filter(topic__forum__group__isnull=True, + topic__forum__pk=obj['forum'])\ .order_by('-pubdate') - return posts[:5] + elif "forum" not in obj and "tag" in obj: + posts = Post.objects.filter(topic__forum__group__isnull=True, + topic__tags__pk__in=[obj['tag']])\ + .order_by('-pubdate') + if "forum" not in obj and "tag" not in obj: + posts = Post.objects.filter(topic__forum__group__isnull=True)\ + .order_by('-pubdate') + + return posts[:settings.POSTS_PER_PAGE] def item_title(self, item): return u'{}, message #{}'.format(item.topic.title, item.pk) @@ -49,11 +73,34 @@ class LastTopicsFeedRSS(Feed): title = u'Derniers sujets sur Zeste de Savoir' link = '/forums/' description = u'Les derniers sujets créés sur le forum de Zeste de Savoir.' - - def items(self): - topics = Topic.objects.filter(forum__group__isnull=True)\ + + def get_object(self, request): + obj = {} + if "forum" in request.GET: + obj['forum']=request.GET["forum"] + if "tag" in request.GET: + obj['tag']=request.GET["tag"] + return obj + + def items(self, obj): + if "forum" in obj and "tag" in obj: + topics = Topic.objects.filter(forum__group__isnull=True, + forum__pk=obj['forum'], + tags__pk__in=[obj['tag']])\ + .order_by('-pubdate') + elif "forum" in obj and "tag" not in obj: + topics = Topic.objects.filter(forum__group__isnull=True, + forum__pk=obj['forum'])\ .order_by('-pubdate') - return topics[:5] + elif "forum" not in obj and "tag" in obj: + topics = Topic.objects.filter(forum__group__isnull=True, + tags__pk__in=[obj['tag']])\ + .order_by('-pubdate') + if "forum" not in obj and "tag" not in obj: + topics = Topic.objects.filter(forum__group__isnull=True)\ + .order_by('-pubdate') + + return topics[:settings.POSTS_PER_PAGE] def item_pubdate(self, item): return item.pubdate diff --git a/zds/forum/tests.py b/zds/forum/tests.py index 886b14c743..763a466afc 100644 --- a/zds/forum/tests.py +++ b/zds/forum/tests.py @@ -9,12 +9,12 @@ from zds.forum.factories import CategoryFactory, ForumFactory, \ TopicFactory, PostFactory, TagFactory from zds.member.factories import ProfileFactory, StaffProfileFactory -from zds.utils.models import CommentLike, CommentDislike, Alert +from zds.utils.models import CommentLike, CommentDislike, Alert, Tag from django.core import mail from .models import Post, Topic, TopicFollowed, TopicRead from zds.forum.views import get_tag_by_title -from zds.forum.models import get_topics +from zds.forum.models import get_topics, Forum class ForumMemberTests(TestCase): @@ -48,6 +48,25 @@ def setUp(self): password='hostel77') self.assertEqual(log, True) + def feed_rss_display(self): + """Test each rss feed feed""" + response = self.client.get(reverse('post-feed-rss'), follow=False) + self.assertEqual(response.status_code, 200) + + for forum in Forum.objects.all(): + response = self.client.get(reverse('post-feed-rss')+"?forum={}".format(forum.pk), follow=False) + self.assertEqual(response.status_code, 200) + + for tag in Tag.objects.all(): + response = self.client.get(reverse('post-feed-rss')+"?tag={}".format(tag.pk), follow=False) + self.assertEqual(response.status_code, 200) + + for forum in Forum.objects.all(): + for tag in Tag.objects.all(): + response = self.client.get(reverse('post-feed-rss')+"?tag={}&forum={}".format(tag.pk, forum.pk), follow=False) + self.assertEqual(response.status_code, 200) + + def test_display(self): """Test forum display (full: root, category, forum) Topic display test is in creation topic test.""" @@ -675,6 +694,24 @@ def setUp(self): position_in_category=2) self.user = ProfileFactory().user + def feed_rss_display(self): + """Test each rss feed feed""" + response = self.client.get(reverse('post-feed-rss'), follow=False) + self.assertEqual(response.status_code, 200) + + for forum in Forum.objects.all(): + response = self.client.get(reverse('post-feed-rss')+"?forum={}".format(forum.pk), follow=False) + self.assertEqual(response.status_code, 200) + + for tag in Tag.objects.all(): + response = self.client.get(reverse('post-feed-rss')+"?tag={}".format(tag.pk), follow=False) + self.assertEqual(response.status_code, 200) + + for forum in Forum.objects.all(): + for tag in Tag.objects.all(): + response = self.client.get(reverse('post-feed-rss')+"?tag={}&forum={}".format(tag.pk, forum.pk), follow=False) + self.assertEqual(response.status_code, 200) + def test_display(self): """Test forum display (full: root, category, forum) Topic display test is in creation topic test.""" diff --git a/zds/forum/urls.py b/zds/forum/urls.py index 909f1467f8..73a42e3453 100644 --- a/zds/forum/urls.py +++ b/zds/forum/urls.py @@ -9,13 +9,9 @@ urlpatterns = patterns('', # Feeds - url(r'^flux/messages/rss/$', - feeds.LastPostsFeedRSS(), - name='post-feed-rss'), - url(r'^flux/messages/atom/$', - feeds.LastPostsFeedATOM(), - name='post-feed-atom'), - + url(r'^flux/messages/rss/$', feeds.LastPostsFeedRSS(), name='post-feed-rss'), + url(r'^flux/messages/atom/$', feeds.LastPostsFeedATOM(), name='post-feed-atom'), + url(r'^flux/sujets/rss/$', feeds.LastTopicsFeedRSS(), name='topic-feed-rss'), diff --git a/zds/tutorial/feeds.py b/zds/tutorial/feeds.py new file mode 100644 index 0000000000..8005ff8540 --- /dev/null +++ b/zds/tutorial/feeds.py @@ -0,0 +1,43 @@ +# coding: utf-8 + +from django.contrib.syndication.views import Feed + +from django.utils.feedgenerator import Atom1Feed + +from .models import Tutorial + + +class LastTutorialsFeedRSS(Feed): + title = "Tutoriels sur Zeste de Savoir" + link = "/tutoriels/" + description = "Les derniers tutoriels parus sur Zeste de Savoir." + + def items(self): + return Tutorial.objects\ + .filter(sha_public__isnull=False)\ + .order_by('-pubdate')[:5] + + def item_title(self, item): + return item.title + + def item_pubdate(self, item): + return item.pubdate + + def item_description(self, item): + return item.description + + def item_author_name(self, item): + authors_list = item.authors.all() + authors = [] + for authors_obj in authors_list: + authors.append(authors_obj.username) + authors = ", ".join(authors) + return authors + + def item_link(self, item): + return item.get_absolute_url() + + +class LastTutorialsFeedATOM(LastTutorialsFeedRSS): + feed_type = Atom1Feed + subtitle = LastTutorialsFeedRSS.description \ No newline at end of file diff --git a/zds/tutorial/urls.py b/zds/tutorial/urls.py index 049e4ebd68..c1b6736880 100644 --- a/zds/tutorial/urls.py +++ b/zds/tutorial/urls.py @@ -3,10 +3,12 @@ from django.conf.urls import patterns, url from . import views - +from . import feeds urlpatterns = patterns('', # Viewing + url(r'^flux/rss/$', feeds.LastTutorialsFeedRSS(), name='tutorial-feed-rss'), + url(r'^flux/atom/$', feeds.LastTutorialsFeedATOM(), name='tutorial-feed-atom'), # Current URLs url(r'^recherche/(?P\d+)/$', From 8a41a39c5de35c631bf3e0f4467e0d5b23812ba9 Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 1 Sep 2014 23:05:53 +0200 Subject: [PATCH 056/270] typo --- zds/gallery/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zds/gallery/views.py b/zds/gallery/views.py index 2fd88ce626..1d3a5851a6 100644 --- a/zds/gallery/views.py +++ b/zds/gallery/views.py @@ -342,7 +342,7 @@ def import_image(request, gal_pk): title = os.path.basename(i) # if size is too large don't save if os.stat(ph_temp).st_size > settings.IMAGE_MAX_SIZE: - messages.error(request, u"L'image {} n'a pas pu être importée dans la galérie car elle est beaucoup trop lourde".format(title)) + messages.error(request, u"L'image {} n'a pas pu être importée dans la galerie car elle est beaucoup trop lourde".format(title)) continue f = File(open(ph_temp, "rb")) @@ -370,4 +370,4 @@ def import_image(request, gal_pk): else: form = ArchiveImageForm(initial={"new_image": True}) # A empty, unbound form return render_template("gallery/image/new.html", {"form": form, - "gallery": gal}) \ No newline at end of file + "gallery": gal}) From c846ed4d1cdc49ab78d1d6d481f0e1c11eb41fe0 Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 1 Sep 2014 23:16:17 +0200 Subject: [PATCH 057/270] controle de l'extention du fichier --- zds/gallery/forms.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/zds/gallery/forms.py b/zds/gallery/forms.py index ae0696ef51..77a0759cf1 100644 --- a/zds/gallery/forms.py +++ b/zds/gallery/forms.py @@ -171,6 +171,20 @@ def __init__(self, *args, **kwargs): u'href="{{ gallery.get_absolute_url }}">Annuler'), ), ) + + def clean(self): + cleaned_data = super(ArchiveImageForm, self).clean() + + file = cleaned_data.get('file') + extension=file.name.split('.')[-1] + + if extension != "zip": + self._errors['file'] = self.error_class( + [u"Le champ n'accepte que les fichiers zip"]) + if 'file' in cleaned_data: + del cleaned_data['file'] + + return cleaned_data class UpdateImageForm(ImageForm): From 36a73bc1696c502fcb69904ff73ea526ccac2ebb Mon Sep 17 00:00:00 2001 From: SpaceFox Date: Mon, 1 Sep 2014 23:27:31 +0200 Subject: [PATCH 058/270] =?UTF-8?q?Issue=20#1416:=20Tests=20cass=C3=A9s=20?= =?UTF-8?q?lors=20du=20merge=20dans=20dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/mp/tests/tests_forms.py | 16 ++++++++-------- zds/mp/tests/tests_views.py | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/zds/mp/tests/tests_forms.py b/zds/mp/tests/tests_forms.py index 9ee5caf24a..1640203d49 100644 --- a/zds/mp/tests/tests_forms.py +++ b/zds/mp/tests/tests_forms.py @@ -24,7 +24,7 @@ def test_valid_topic_form(self): 'text': 'blabla' } - form = PrivateTopicForm(data=data) + form = PrivateTopicForm(self.profile2.user.username, data=data) self.assertTrue(form.is_valid()) @@ -37,7 +37,7 @@ def test_invalid_topic_form_user_notexist(self): 'text': 'blabla' } - form = PrivateTopicForm(data=data) + form = PrivateTopicForm(self.profile1.user.username, data=data) self.assertFalse(form.is_valid()) @@ -49,7 +49,7 @@ def test_invalid_topic_form_no_participants(self): 'text': 'blabla' } - form = PrivateTopicForm(data=data) + form = PrivateTopicForm('', data=data) self.assertFalse(form.is_valid()) @@ -62,7 +62,7 @@ def test_invalid_topic_form_empty_participants(self): 'text': 'blabla' } - form = PrivateTopicForm(data=data) + form = PrivateTopicForm(' ', data=data) self.assertFalse(form.is_valid()) @@ -74,7 +74,7 @@ def test_invalid_topic_form_no_title(self): 'text': 'blabla' } - form = PrivateTopicForm(data=data) + form = PrivateTopicForm(self.profile1.user.username, data=data) self.assertFalse(form.is_valid()) @@ -87,7 +87,7 @@ def test_invalid_topic_form_empty_title(self): 'text': 'blabla' } - form = PrivateTopicForm(data=data) + form = PrivateTopicForm(self.profile1.user.username, data=data) self.assertFalse(form.is_valid()) @@ -99,7 +99,7 @@ def test_invalid_topic_form_no_text(self): 'subtitle': 'Test subtitle', } - form = PrivateTopicForm(data=data) + form = PrivateTopicForm(self.profile1.user.username, data=data) self.assertFalse(form.is_valid()) @@ -112,7 +112,7 @@ def test_invalid_topic_form_empty_text(self): 'text': ' ' } - form = PrivateTopicForm(data=data) + form = PrivateTopicForm(self.profile1.user.username, data=data) self.assertFalse(form.is_valid()) diff --git a/zds/mp/tests/tests_views.py b/zds/mp/tests/tests_views.py index aa851d3fda..765d685a9d 100644 --- a/zds/mp/tests/tests_views.py +++ b/zds/mp/tests/tests_views.py @@ -323,8 +323,7 @@ def test_fail_new_topic_user_add_himself_and_others(self): self.assertEqual(0, PrivateTopic.objects.all().count()) - participants = self.profile1.user.username\ - + ',' + self.profile2.user.username + participants = self.profile2.user.username response = self.client.post( reverse('zds.mp.views.new'), From 1a75d77ec956984790e1fc1365e1d635cd975ead Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 2 Sep 2014 00:43:50 +0200 Subject: [PATCH 059/270] =?UTF-8?q?ajout=20des=20messages=20pr=C3=A9c?= =?UTF-8?q?=C3=A9dents=20lors=20de=20la=20preview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/article/reaction/new.html | 27 +++++++++++++++++++++++++++ templates/mp/post/new.html | 16 ++++++++++++++++ templates/tutorial/comment/new.html | 28 ++++++++++++++++++++++++++++ zds/article/views.py | 13 ++++++++----- zds/forum/views.py | 14 +++++--------- zds/mp/views.py | 12 +++++++----- zds/tutorial/models.py | 2 +- zds/tutorial/views.py | 8 +++++++- 8 files changed, 99 insertions(+), 21 deletions(-) diff --git a/templates/article/reaction/new.html b/templates/article/reaction/new.html index 15e2048d9a..35efcf9882 100644 --- a/templates/article/reaction/new.html +++ b/templates/article/reaction/new.html @@ -1,5 +1,6 @@ {% extends "article/base.html" %} {% load crispy_forms_tags %} +{% load captureas %} @@ -35,4 +36,30 @@ {% if form.text.value %} {% include "misc/previsualization.part.html" with text=form.text.value %} {% endif %} + +
    + {% for message in reactions %} + {% captureas edit_link %} + {% url "zds.article.views.edit_note" %}?message={{ message.pk }} + {% endcaptureas %} + + {% captureas cite_link %} + {% url "zds.article.views.answer" %}?article={{ topic.pk }}&cite={{ message.pk }} + {% endcaptureas %} + + {% captureas upvote_link %} + {% url "zds.article.views.like_note" %}?message={{ message.pk }} + {% endcaptureas %} + + {% captureas downvote_link %} + {% url "zds.article.views.dislike_note" %}?message={{ message.pk }} + {% endcaptureas %} + + {% captureas alert_solve_link %} + {% url "zds.article.views.solve_alert" %} + {% endcaptureas %} + + {% include "misc/message.part.html" with perms_change=perms.tutorial.change_article %} + {% endfor %} +
    {% endblock %} \ No newline at end of file diff --git a/templates/mp/post/new.html b/templates/mp/post/new.html index 5ebe91c149..19cf91e5e5 100644 --- a/templates/mp/post/new.html +++ b/templates/mp/post/new.html @@ -46,4 +46,20 @@ {% if form.text.value %} {% include "misc/previsualization.part.html" with text=form.text.value %} {% endif %} + +
    + +
    + {% for message in posts %} + {% captureas edit_link %} + {% url "zds.mp.views.edit_post" %}?message={{ message.pk }} + {% endcaptureas %} + + {% captureas cite_link %} + {% url "zds.mp.views.answer" %}?sujet={{ topic.pk }}&cite={{ message.pk }} + {% endcaptureas %} + + {% include "misc/message.part.html" %} + {% endfor %} +
    {% endblock %} diff --git a/templates/tutorial/comment/new.html b/templates/tutorial/comment/new.html index 307d5dc2b7..47ccca35c1 100644 --- a/templates/tutorial/comment/new.html +++ b/templates/tutorial/comment/new.html @@ -1,6 +1,7 @@ {% extends "tutorial/base_online.html" %} {% load profile %} {% load crispy_forms_tags %} +{% load captureas %} @@ -42,4 +43,31 @@

    {{ tutorial.description }}

    {% if form.text.value %} {% include "misc/previsualization.part.html" with text=form.text.value %} {% endif %} + +
    + {% for message in notes %} + {% captureas edit_link %} + {% url "zds.tutorial.views.edit_note" %}?message={{ message.pk }} + {% endcaptureas %} + + {% captureas cite_link %} + {% url "zds.tutorial.views.answer" %}?tutorial={{ topic.pk }}&cite={{ message.pk }} + {% endcaptureas %} + + {% captureas upvote_link %} + {% url "zds.tutorial.views.like_note" %}?message={{ message.pk }} + {% endcaptureas %} + + {% captureas downvote_link %} + {% url "zds.tutorial.views.dislike_note" %}?message={{ message.pk }} + {% endcaptureas %} + + {% captureas alert_solve_link %} + {% url "zds.tutorial.views.solve_alert" %} + {% endcaptureas %} + + {% include "misc/message.part.html" with perms_change=perms.tutorial.change_tutorial %} + {% endfor %} +
    + {% endblock %} \ No newline at end of file diff --git a/zds/article/views.py b/zds/article/views.py index b42065b5d9..9f1cbc5c66 100644 --- a/zds/article/views.py +++ b/zds/article/views.py @@ -891,17 +891,18 @@ def answer(request): if article.antispam(request.user): raise PermissionDenied - # Retrieve 3 last reactions of the currenta article. - reactions = Reaction.objects\ - .filter(article=article)\ - .order_by('-pubdate')[:3] - # If there is a last reaction for the article, we save his pk. # Otherwise, we save 0. if article.last_reaction: last_reaction_pk = article.last_reaction.pk else: last_reaction_pk = 0 + + # Retrieve lasts reactions of the current topic. + reactions = Reaction.objects.filter(article=article) \ + .prefetch_related() \ + .order_by("-pubdate")[:settings.POSTS_PER_PAGE] + # User would like preview his post or post a new reaction on the article. if request.method == 'POST': @@ -917,6 +918,7 @@ def answer(request): 'article': article, 'last_reaction_pk': last_reaction_pk, 'newreaction': newreaction, + 'reactions': reactions, 'form': form }) @@ -945,6 +947,7 @@ def answer(request): 'article': article, 'last_reaction_pk': last_reaction_pk, 'newreaction': newreaction, + 'reactions': reactions, 'form': form }) diff --git a/zds/forum/views.py b/zds/forum/views.py index 4615d1eadf..f1d70023b6 100644 --- a/zds/forum/views.py +++ b/zds/forum/views.py @@ -466,13 +466,10 @@ def answer(request): raise PermissionDenied last_post_pk = g_topic.last_message.pk - # Retrieve 10 last posts of the current topic. - - posts = \ - Post.objects.filter(topic=g_topic) \ - .prefetch_related() \ - .order_by("-pubdate" - )[:10] + # Retrieve last posts of the current topic. + posts = Post.objects.filter(topic=g_topic) \ + .prefetch_related() \ + .order_by("-pubdate")[:settings.POSTS_PER_PAGE] # User would like preview his post or post a new post on the topic. @@ -483,8 +480,7 @@ def answer(request): # Using the « preview button », the « more » button or new post if "preview" in data or newpost: - form = PostForm(g_topic, request.user, initial={"text": data["text" - ]}) + form = PostForm(g_topic, request.user, initial={"text": data["text"]}) form.helper.form_action = reverse("zds.forum.views.answer") \ + "?sujet=" + str(g_topic.pk) return render_template("forum/post/new.html", { diff --git a/zds/mp/views.py b/zds/mp/views.py index 8399f09b35..20d4381e55 100644 --- a/zds/mp/views.py +++ b/zds/mp/views.py @@ -265,12 +265,12 @@ def answer(request): if not g_topic.author == request.user \ and request.user not in list(g_topic.participants.all()): raise PermissionDenied - - # Retrieve 3 last posts of the currenta topic. - posts = PrivatePost.objects\ - .filter(privatetopic=g_topic)\ - .order_by('-pubdate')[:3] + last_post_pk = g_topic.last_message.pk + # Retrieve last posts of the current private topic. + posts = PrivatePost.objects.filter(privatetopic=g_topic) \ + .prefetch_related() \ + .order_by("-pubdate")[:settings.POSTS_PER_PAGE] # User would like preview his post or post a new post on the topic. if request.method == 'POST': @@ -285,6 +285,7 @@ def answer(request): return render_template('mp/post/new.html', { 'topic': g_topic, 'last_post_pk': last_post_pk, + 'posts': posts, 'newpost': newpost, 'form': form, }) @@ -352,6 +353,7 @@ def answer(request): 'topic': g_topic, 'last_post_pk': last_post_pk, 'newpost': newpost, + 'posts': posts, 'form': form, }) diff --git a/zds/tutorial/models.py b/zds/tutorial/models.py index 742ff802f8..eb5352e344 100644 --- a/zds/tutorial/models.py +++ b/zds/tutorial/models.py @@ -403,7 +403,7 @@ def antispam(self, user=None): .filter(author=user.pk)\ .order_by('-pubdate') - if last_user_notes and last_user_notes[0] == self.get_last_note(): + if last_user_notes and last_user_notes[0] == self.last_note: last_user_note = last_user_notes[0] t = timezone.now() - last_user_note.pubdate if t.total_seconds() < settings.SPAM_LIMIT_SECONDS: diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 3b3cc4b3d0..7e0954cc06 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -3044,6 +3044,11 @@ def answer(request): last_note_pk = 0 if tutorial.last_note: last_note_pk = tutorial.last_note.pk + + # Retrieve lasts notes of the current tutorial. + notes = Note.objects.filter(tutorial=tutorial) \ + .prefetch_related() \ + .order_by("-pubdate")[:settings.POSTS_PER_PAGE] # User would like preview his post or post a new note on the tutorial. @@ -3060,6 +3065,7 @@ def answer(request): "tutorial": tutorial, "last_note_pk": last_note_pk, "newnote": newnote, + "notes": notes, "form": form, }) else: @@ -3086,12 +3092,12 @@ def answer(request): "tutorial": tutorial, "last_note_pk": last_note_pk, "newnote": newnote, + "notes": notes, "form": form, }) else: # Actions from the editor render to answer.html. - text = "" # Using the quote button From 07ad8dab221cebc954d24ab69bea08a19411ffe7 Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 2 Sep 2014 01:05:47 +0200 Subject: [PATCH 060/270] correction des tests --- zds/tutorial/tests.py | 8 ++++++++ zds/tutorial/views.py | 5 +---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py index efc971c287..2c74730d55 100644 --- a/zds/tutorial/tests.py +++ b/zds/tutorial/tests.py @@ -482,6 +482,10 @@ def test_url_for_guest(self): def test_workflow_tuto(self): """Test workflow of tutorial.""" + forum = ForumFactory( + category=CategoryFactory(position=1), + position_in_category=1) + # logout before self.client.logout() # login with simple member @@ -3037,6 +3041,10 @@ def test_workflow_beta_tuto(self) : def test_workflow_tuto(self): """Test workflow of mini tutorial.""" + + forum = ForumFactory( + category=CategoryFactory(position=1), + position_in_category=1) # logout before self.client.logout() diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index cb99e0ac39..9140c93f47 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -547,7 +547,6 @@ def modify_tutorial(request): raise Http404 tutorial_pk = request.POST["tutorial"] tutorial = get_object_or_404(Tutorial, pk=tutorial_pk) - # User actions if request.user in tutorial.authors.all() or request.user.has_perm("tutorial.change_tutorial"): @@ -607,7 +606,6 @@ def modify_tutorial(request): if "version" in request.POST: tutorial.sha_beta = request.POST['version'] tutorial.save() - topic = Topic.objects.filter(key=tutorial.pk, forum__pk=settings.BETA_FORUM_ID).first() if topic is None: msg = \ @@ -622,6 +620,7 @@ def modify_tutorial(request): tutorial.title, settings.SITE_URL + tutorial.get_absolute_url_beta())) forum = get_object_or_404(Forum, pk=settings.BETA_FORUM_ID) + create_topic(author = request.user, forum = forum, title = u"[beta][tutoriel]{0}".format(tutorial.title), @@ -742,8 +741,6 @@ def view_tutorial(request, tutorial_pk, tutorial_slug): mandata = json_reader.loads(manifest) tutorial.load_dic(mandata, sha) tutorial.load_introduction_and_conclusion(mandata, sha) - - #print mandata # If it's a small tutorial, fetch its chapter From 7fc07ce954f6415f7c2720a9d3839f6bdefe0d3d Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 2 Sep 2014 01:37:27 +0200 Subject: [PATCH 061/270] mp et reservation lors d'une maj de contenu en validation --- zds/article/views.py | 27 +++++++++++++++++++++++++++ zds/tutorial/views.py | 27 ++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/zds/article/views.py b/zds/article/views.py index b42065b5d9..7116b063b3 100644 --- a/zds/article/views.py +++ b/zds/article/views.py @@ -633,6 +633,14 @@ def modify(request): # User would like to validate his article. So we must save the # current sha (version) of the article to his sha_validation. elif 'pending' in request.POST: + old_validation = Validation.objects.filter(tutorial__pk=tutorial_pk, + status__in=['PENDING_V']).first() + if old_validation is not None: + old_validator = old_validation.validator + old_reserve_date = old_validation.date_reserve + else: + old_validator = None + old_reserve_date = None # Delete old pending validation Validation.objects.filter(article__pk=article_pk, status__in=['PENDING','PENDING_V'])\ @@ -645,6 +653,25 @@ def modify(request): validation.date_proposition = datetime.now() validation.comment_authors = request.POST['comment'] validation.version = request.POST['version'] + + if old_validator is not None: + validation.validator = old_validator + validation.date_reserve + bot = get_object_or_404(User, username=settings.BOT_ACCOUNT) + msg = \ + (u'Bonjour {0},' + u'L\'article *{1}* que tu as réservé a été mis à jour en zone de validation, ' + u'Pour retrouver les modifications qui ont été faites, je t\'invite à' + u'consulter l\'historique des versions' + u'\n\n> Merci'.format(old_validator.username, article.title)) + send_mp( + bot, + [old_validator], + u"Mise à jour d'article : {0}".format(article.title), + "En validation", + msg, + False, + ) validation.save() diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 3b3cc4b3d0..adcb2ad70f 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -459,18 +459,43 @@ def ask_validation(request): if not request.user.has_perm("tutorial.change_tutorial"): raise PermissionDenied + old_validation = Validation.objects.filter(tutorial__pk=tutorial_pk, + status__in=['PENDING_V']).first() + if old_validation is not None: + old_validator = old_validation.validator + old_reserve_date = old_validation.date_reserve + else: + old_validator = None + old_reserve_date = None #delete old pending validation Validation.objects.filter(tutorial__pk=tutorial_pk, status__in=['PENDING','PENDING_V'])\ .delete() # We create and save validation object of the tutorial. - validation = Validation() validation.tutorial = tutorial validation.date_proposition = datetime.now() validation.comment_authors = request.POST["text"] validation.version = request.POST["version"] + if old_validator is not None: + validation.validator = old_validator + validation.date_reserve + bot = get_object_or_404(User, username=settings.BOT_ACCOUNT) + msg = \ + (u'Bonjour {0},' + u'Le tutoriel *{1}* que tu as réservé a été mis à jour en zone de validation, ' + u'Pour retrouver les modifications qui ont été faites, je t\'invite à' + u'consulter l\'historique des versions' + u'\n\n> Merci'.format(old_validator.username, tutorial.title)) + send_mp( + bot, + [old_validator], + u"Mise à jour de tuto : {0}".format(tutorial.title), + "En validation", + msg, + False, + ) validation.save() validation.tutorial.source=request.POST["source"] validation.tutorial.sha_validation = request.POST["version"] From f3721985e58f40effb49c48fc8379850e188cafe Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 2 Sep 2014 02:30:41 +0200 Subject: [PATCH 062/270] correction --- zds/article/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zds/article/views.py b/zds/article/views.py index 7116b063b3..c2c1cf5333 100644 --- a/zds/article/views.py +++ b/zds/article/views.py @@ -633,7 +633,7 @@ def modify(request): # User would like to validate his article. So we must save the # current sha (version) of the article to his sha_validation. elif 'pending' in request.POST: - old_validation = Validation.objects.filter(tutorial__pk=tutorial_pk, + old_validation = Validation.objects.filter(article__pk=article_pk, status__in=['PENDING_V']).first() if old_validation is not None: old_validator = old_validation.validator From cce7f5bdeeed38638e839e150053aafcddd4053a Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 2 Sep 2014 11:41:43 +0200 Subject: [PATCH 063/270] =?UTF-8?q?mise=20=C3=A0=20jour=20des=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/tutorial/tests.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py index 711173e98b..0206453221 100644 --- a/zds/tutorial/tests.py +++ b/zds/tutorial/tests.py @@ -106,6 +106,7 @@ def setUp(self): reverse('zds.tutorial.views.reservation', args=[validation.pk]), follow=False) self.assertEqual(pub.status_code, 302) + self.first_validator = self.staff # publish tutorial pub = self.client.post( @@ -970,6 +971,7 @@ def test_workflow_tuto(self): # ask public tutorial tuto = Tutorial.objects.get(pk=tuto.pk) + pub = self.client.post( reverse('zds.tutorial.views.ask_validation'), { @@ -980,7 +982,11 @@ def test_workflow_tuto(self): }, follow=False) self.assertEqual(pub.status_code, 302) - + + # old validator stay + validation = Validation.objects.filter(tutorial__pk=tuto.pk).last() + self.assertEqual(self.first_validator, validation.validator) + # logout before self.client.logout() # login with staff member From 123966cda80f67f0354558a0a128f0cbe1bb7ddb Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 2 Sep 2014 13:36:19 +0200 Subject: [PATCH 064/270] correction du test de validation --- zds/tutorial/tests.py | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py index 0206453221..9d66b5276b 100644 --- a/zds/tutorial/tests.py +++ b/zds/tutorial/tests.py @@ -982,10 +982,6 @@ def test_workflow_tuto(self): }, follow=False) self.assertEqual(pub.status_code, 302) - - # old validator stay - validation = Validation.objects.filter(tutorial__pk=tuto.pk).last() - self.assertEqual(self.first_validator, validation.validator) # logout before self.client.logout() @@ -1003,6 +999,41 @@ def test_workflow_tuto(self): reverse('zds.tutorial.views.reservation', args=[validation.pk]), follow=False) self.assertEqual(pub.status_code, 302) + old_validator = self.staff + + # logout staff before + self.staff.logout() + # login with simple member + self.assertEqual( + self.client.login( + username=self.client.username, + password='hostel77'), + True) + + # ask public tutorial again + pub = self.client.post( + reverse('zds.tutorial.views.ask_validation'), + { + 'tutorial': tuto.pk, + 'text': u'Nouvelle demande de publication', + 'version': tuto.sha_draft, + 'source': 'www.zestedesavoir.com', + }, + follow=False) + self.assertEqual(pub.status_code, 302) + + # old validator stay + validation = Validation.objects.filter(tutorial__pk=tuto.pk).last() + self.assertEqual(old_validator, validation.validator) + + # logout before + self.client.logout() + # login with staff member + self.assertEqual( + self.client.login( + username=self.staff.username, + password='hostel77'), + True) # publish tutorial pub = self.client.post( From fe6725328cdb5908e741721bce3bd47687f6c4ac Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 2 Sep 2014 14:06:41 +0200 Subject: [PATCH 065/270] =?UTF-8?q?mise=20=C3=A0=20jours=20des=20v=C3=A9ri?= =?UTF-8?q?fications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/tutorial/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py index 9d66b5276b..b612af7d09 100644 --- a/zds/tutorial/tests.py +++ b/zds/tutorial/tests.py @@ -1002,11 +1002,11 @@ def test_workflow_tuto(self): old_validator = self.staff # logout staff before - self.staff.logout() + self.client.logout() # login with simple member self.assertEqual( self.client.login( - username=self.client.username, + username=self.user.username, password='hostel77'), True) From 60dfb32a7372b87523f6fccea7691d90f63ed41e Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 2 Sep 2014 14:57:49 +0200 Subject: [PATCH 066/270] =?UTF-8?q?test=20de=20l'arriv=C3=A9e=20d'un=20MP?= =?UTF-8?q?=20au=20staff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/tutorial/tests.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py index b612af7d09..65d40e74e3 100644 --- a/zds/tutorial/tests.py +++ b/zds/tutorial/tests.py @@ -3,6 +3,7 @@ import os import shutil import HTMLParser +from django.db.models import Q from django.conf import settings from django.core import mail from django.core.urlresolvers import reverse @@ -1000,6 +1001,9 @@ def test_workflow_tuto(self): follow=False) self.assertEqual(pub.status_code, 302) old_validator = self.staff + old_mps_count = PrivateTopic.objects\ + .filter(Q(author=old_validator)|Q(participants__in=[old_validator]))\ + .count() # logout staff before self.client.logout() @@ -1025,7 +1029,12 @@ def test_workflow_tuto(self): # old validator stay validation = Validation.objects.filter(tutorial__pk=tuto.pk).last() self.assertEqual(old_validator, validation.validator) - + + #new MP for staff + new_mps_count = PrivateTopic.objects\ + .filter(Q(author=old_validator)|Q(participants__in=[old_validator]))\ + .count() + self.assertEqual((new_mps_count-old_mps_count), 1) # logout before self.client.logout() # login with staff member From c3c7624bbe09ff6ae41fb29929178de76faa1d7f Mon Sep 17 00:00:00 2001 From: Eskimon Date: Tue, 2 Sep 2014 15:08:22 +0200 Subject: [PATCH 067/270] Suppression d'un lien de connexion redondant --- templates/member/register/token_success.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/member/register/token_success.html b/templates/member/register/token_success.html index af1a632d65..30be099208 100644 --- a/templates/member/register/token_success.html +++ b/templates/member/register/token_success.html @@ -26,9 +26,9 @@

    Confirmation d'inscription réussie !

    Vous avez confirmé votre courriel sur le site.

    - Vous pouvez maintenant vous connecter et partager avec tous les membres. + Vous pouvez maintenant vous connecter et partager avec tous les membres. {% crispy form %} Mot de passe déjà oublié ?

    -{% endblock %} \ No newline at end of file +{% endblock %} From b7402469f700a14e3a7719ec495172b23cacea45 Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 3 Sep 2014 01:30:03 +0200 Subject: [PATCH 068/270] police Merriweather dans le contenu des pdfs --- .travis.yml | 7 +++++++ zds/tutorial/views.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d13ecc88e9..83ecf4fafe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,11 +21,18 @@ install: - sudo apt-get update - sudo echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | sudo debconf-set-selections - sudo apt-get install ttf-mscorefonts-installer + - sudo apt-get install unzip - sudo apt-get install texlive - sudo apt-get install texlive-xetex - sudo apt-get install texlive-lang-french - sudo apt-get install texlive-latex-extra + # Add fonts + - sudo wget -P /usr/share/fonts/truetype https://www.dropbox.com/s/ema28tjn52960mq/Merriweather.zip + - unzip /usr/share/fonts/truetype/Merriweather.zip -d /usr/share/fonts/truetype/Merriweather/ + - sudo chmod a+r /usr/share/fonts/truetype/Merriweather/*.ttf + - sudo fc-cache -f -v + # Cabal + Pandoc stuff - sudo mkdir -p ~/cabal/bin - sudo mkdir -p ~/.pandoc diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 0ad79dc18e..aa2d4cc5e4 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -2942,7 +2942,7 @@ def MEP(tutorial, sha): os.system(settings.PANDOC_LOC + "pandoc " + "--latex-engine=xelatex " + "--template=../../assets/tex/template.tex " + "-s " + "-S " + "-N " + "--toc " + "-V documentclass=scrbook " - + "-V lang=francais " + "-V mainfont=Verdana " + + "-V lang=francais " + "-V mainfont=Merriweather " + "-V monofont=\"Andale Mono\" " + "-V fontsize=12pt " + "-V geometry:margin=1in " + os.path.join(tutorial.get_prod_path(), tutorial.slug) + ".md " From 686204448078199dd0f1c944ac4ae0f96f9fb60b Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 3 Sep 2014 01:47:38 +0200 Subject: [PATCH 069/270] permissions dans travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 83ecf4fafe..363fb35aa3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ install: # Add fonts - sudo wget -P /usr/share/fonts/truetype https://www.dropbox.com/s/ema28tjn52960mq/Merriweather.zip - - unzip /usr/share/fonts/truetype/Merriweather.zip -d /usr/share/fonts/truetype/Merriweather/ + - sudo unzip /usr/share/fonts/truetype/Merriweather.zip -d /usr/share/fonts/truetype/Merriweather/ - sudo chmod a+r /usr/share/fonts/truetype/Merriweather/*.ttf - sudo fc-cache -f -v From f294c2ed29a00aea22c1bbeb7d18bfd5a322f71b Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 3 Sep 2014 10:24:50 +0200 Subject: [PATCH 070/270] =?UTF-8?q?corrige=20les=20probl=C3=A8me=20d'encod?= =?UTF-8?q?age=20de=20formulaire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/member/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zds/member/forms.py b/zds/member/forms.py index 4eff50eacf..bdc4a46848 100644 --- a/zds/member/forms.py +++ b/zds/member/forms.py @@ -257,7 +257,7 @@ def __init__(self, *args, **kwargs): Field('avatar_url'), Field('sign'), ButtonHolder( - StrictButton('Éditer le profil', type='submit'), + StrictButton(u'Éditer le profil', type='submit'), HTML('Annuler'), )) @@ -311,7 +311,7 @@ def __init__(self, *args, **kwargs): Field('sign'), Field('options'), ButtonHolder( - StrictButton('Editer mon profil', type='submit'), + StrictButton(u'Éditer mon profil', type='submit'), HTML('Annuler'), )) From 0170ce91224bef5f3c37827279b2da5eb5329a83 Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 3 Sep 2014 19:53:28 +0200 Subject: [PATCH 071/270] correction de la detection trop souple --- zds/mp/forms.py | 2 +- zds/mp/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zds/mp/forms.py b/zds/mp/forms.py index a6174579ff..fec9bb6ff1 100644 --- a/zds/mp/forms.py +++ b/zds/mp/forms.py @@ -75,7 +75,7 @@ def clean(self): if participants is not None and participants.strip() != '': receivers = participants.strip().split(',') for receiver in receivers: - if User.objects.filter(username=receiver.strip()).count() == 0 and receiver.strip() != '': + if User.objects.filter(username__exact=receiver.strip()).count() == 0 and receiver.strip() != '': self._errors['participants'] = self.error_class( [u'Un des participants saisi est introuvable']) elif receiver.strip() == self.username: diff --git a/zds/mp/views.py b/zds/mp/views.py index 8399f09b35..49394d7a71 100644 --- a/zds/mp/views.py +++ b/zds/mp/views.py @@ -491,7 +491,7 @@ def add_participant(request): try: # user_pk or user_username ? - part = User.objects.get(username=request.POST['user_pk']) + part = User.objects.get(username__exact=request.POST['user_pk']) if part.pk == ptopic.author.pk or part in ptopic.participants.all(): messages.warning( request, From 443a3a545ca38eedf1ebfe3019ec210dee94ccb6 Mon Sep 17 00:00:00 2001 From: Alex-D Date: Wed, 3 Sep 2014 22:19:56 +0200 Subject: [PATCH 072/270] Rend le test travis sur les assets plus complet --- Gulpfile.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Gulpfile.js b/Gulpfile.js index 2949f788a9..fe035ca787 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -25,6 +25,8 @@ var paths = { sprite: "assets/images/sprite@2x/*.png" }; + + gulp.task("clean", function() { return gulp.src(["dist/*"], { read: false }) .pipe($.rimraf()); @@ -144,7 +146,7 @@ gulp.task("watch", function(cb) { gulp.watch(paths.images, ["images"]); gulp.watch(paths.styles_path, ["stylesheet"]); gulp.watch(paths.errors_path, ["errors"]); - gulp.watch(paths.sprite, ["sprite", "stylesheet"]); + gulp.watch(paths.sprite, ["stylesheet"]); // stylesheet task already lauch sprite gulp.watch("dist/*/**", function(file) { var filePath = path.join("static/", path.relative(path.join(__dirname, "dist/"), file.path)); // Pour que le chemin ressemble à static/.../... @@ -172,8 +174,8 @@ gulp.task("pack", ["build"], function() { .pipe(gulp.dest("dist/")); }); -gulp.task("travis", ["test"]); +gulp.task("travis", ["pack"]); gulp.task("build", ["smileys", "images", "sprite", "stylesheet", "vendors", "script", "merge-scripts"]); From 733e007a6d5e6c6871243de22909e640c215385e Mon Sep 17 00:00:00 2001 From: Alex-D Date: Wed, 3 Sep 2014 22:26:06 +0200 Subject: [PATCH 073/270] Change les sources des images pour minitures --- templates/gallery/gallery/details.html | 2 +- templates/gallery/image/edit.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/gallery/gallery/details.html b/templates/gallery/gallery/details.html index c335a76b0b..3de81a6621 100644 --- a/templates/gallery/gallery/details.html +++ b/templates/gallery/gallery/details.html @@ -80,7 +80,7 @@
    - {{ img.title }}{{ image.legend }}

    Miniature :

    - {{ image.legend }} + {{ image.legend }}
    From f82d5db72ee8b1e99c3fb8856c93fe42d9ec0764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Thu, 28 Aug 2014 20:51:11 +0200 Subject: [PATCH 151/270] Bouge btn de desinscript + tests articles et tutos --- templates/base.html | 10 +- templates/member/settings/base.html | 9 ++ zds/member/tests/tests_views.py | 136 +++++++++++++++++++++++++++- zds/member/views.py | 4 +- 4 files changed, 146 insertions(+), 13 deletions(-) diff --git a/templates/base.html b/templates/base.html index 9d2b2ac742..39f90de218 100644 --- a/templates/base.html +++ b/templates/base.html @@ -442,15 +442,7 @@ -
  • -
    - {% csrf_token %} - -
    -
  • + diff --git a/templates/member/settings/base.html b/templates/member/settings/base.html index fe73cdce38..4d0190fb71 100644 --- a/templates/member/settings/base.html +++ b/templates/member/settings/base.html @@ -22,6 +22,15 @@

    Navigation

  • Mon profil
  • Compte
  • Pseudo et courriel
  • +
  • +
    + {% csrf_token %} + +
    +
  • diff --git a/zds/member/tests/tests_views.py b/zds/member/tests/tests_views.py index 7dba0ff547..a43653f754 100644 --- a/zds/member/tests/tests_views.py +++ b/zds/member/tests/tests_views.py @@ -13,11 +13,12 @@ from zds.member.models import TokenRegister, Ban from zds.tutorial.factories import MiniTutorialFactory -from zds.tutorial.models import Tutorial +from zds.tutorial.models import Tutorial, Validation from zds.article.factories import ArticleFactory from zds.article.models import Article from zds.forum.factories import CategoryFactory, ForumFactory, TopicFactory, PostFactory from zds.forum.models import Topic, Post +from zds.article.models import Validation as ArticleValidation class MemberTests(TestCase): @@ -32,6 +33,7 @@ def setUp(self): self.forum11 = ForumFactory( category=self.category1, position_in_category=1) + self.staff = StaffProfileFactory().user def test_login(self): """To test user login.""" @@ -120,6 +122,61 @@ def test_unregister(self): writingTutorial2.authors.add(user.user) writingTutorial2.authors.add(user2.user) writingTutorial2.save() + self.client.login(username=self.staff.username, password="hostel77") + pub = self.client.post( + reverse('zds.tutorial.views.ask_validation'), + { + 'tutorial': publishedTutorialAlone.pk, + 'text': u'Ce tuto est excellent', + 'version': publishedTutorialAlone.sha_draft, + 'source': 'http://zestedesavoir.com', + }, + follow=False) + self.assertEqual(pub.status_code, 302) + # reserve tutorial + validation = Validation.objects.get( + tutorial__pk=publishedTutorialAlone.pk) + pub = self.client.post( + reverse('zds.tutorial.views.reservation', args=[validation.pk]), + follow=False) + self.assertEqual(pub.status_code, 302) + # publish tutorial + pub = self.client.post( + reverse('zds.tutorial.views.valid_tutorial'), + { + 'tutorial': publishedTutorialAlone.pk, + 'text': u'Ce tuto est excellent', + 'is_major': True, + 'source': 'http://zestedesavoir.com', + }, + follow=False) + pub = self.client.post( + reverse('zds.tutorial.views.ask_validation'), + { + 'tutorial': publishedTutorial2.pk, + 'text': u'Ce tuto est excellent', + 'version': publishedTutorial2.sha_draft, + 'source': 'http://zestedesavoir.com', + }, + follow=False) + self.assertEqual(pub.status_code, 302) + # reserve tutorial + validation = Validation.objects.get( + tutorial__pk=publishedTutorial2.pk) + pub = self.client.post( + reverse('zds.tutorial.views.reservation', args=[validation.pk]), + follow=False) + self.assertEqual(pub.status_code, 302) + # publish tutorial + pub = self.client.post( + reverse('zds.tutorial.views.valid_tutorial'), + { + 'tutorial': publishedTutorial2.pk, + 'text': u'Ce tuto est excellent', + 'is_major': True, + 'source': 'http://zestedesavoir.com', + }, + follow=False) # same thing for articles publishedArticleAlone = ArticleFactory() publishedArticleAlone.authors.add(user.user) @@ -128,8 +185,8 @@ def test_unregister(self): publishedArticle2 = ArticleFactory() publishedArticle2.authors.add(user.user) publishedArticle2.authors.add(user2.user) - publishedArticle2.pubdate = datetime.now() publishedArticle2.save() + writingArticleAlone = ArticleFactory() writingArticleAlone.authors.add(user.user) writingArticleAlone.save() @@ -137,6 +194,81 @@ def test_unregister(self): writingArticle2.authors.add(user.user) writingArticle2.authors.add(user2.user) writingArticle2.save() + # ask public article + pub = self.client.post( + reverse('zds.article.views.modify'), + { + 'article': publishedArticleAlone.pk, + 'comment': u'Valides moi ce bébé', + 'pending': 'Demander validation', + 'version': publishedArticleAlone.sha_draft, + 'is_major': True + }, + follow=False) + self.assertEqual(pub.status_code, 302) + + + login_check = self.client.login( + username=self.staff.username, + password='hostel77') + self.assertEqual(login_check, True) + + # reserve tutorial + validation = ArticleValidation.objects.get( + article__pk=publishedArticleAlone.pk) + pub = self.client.post( + reverse('zds.article.views.reservation', args=[validation.pk]), + follow=False) + self.assertEqual(pub.status_code, 302) + + # publish article + pub = self.client.post( + reverse('zds.article.views.modify'), + { + 'article': publishedArticleAlone.pk, + 'comment-v': u'Cet article est excellent', + 'valid-article': 'Demander validation', + 'is_major': True + }, + follow=False) + self.assertEqual(pub.status_code, 302) + # ask public article + pub = self.client.post( + reverse('zds.article.views.modify'), + { + 'article': publishedArticle2.pk, + 'comment': u'Valides moi ce bébé', + 'pending': 'Demander validation', + 'version': publishedArticle2.sha_draft, + 'is_major': True + }, + follow=False) + self.assertEqual(pub.status_code, 302) + + login_check = self.client.login( + username=self.staff.username, + password='hostel77') + self.assertEqual(login_check, True) + + # reserve tutorial + validation = ArticleValidation.objects.get( + article__pk=publishedArticle2.pk) + pub = self.client.post( + reverse('zds.article.views.reservation', args=[validation.pk]), + follow=False) + self.assertEqual(pub.status_code, 302) + + # publish article + pub = self.client.post( + reverse('zds.article.views.modify'), + { + 'article': publishedArticle2.pk, + 'comment-v': u'Cet article est excellent', + 'valid-article': 'Demander validation', + 'is_major': True + }, + follow=False) + self.assertEqual(pub.status_code, 302) # about posts and topics authoredTopic = TopicFactory(author=user.user, forum=self.forum11) answeredTopic = TopicFactory(author=user2.user, forum=self.forum11) diff --git a/zds/member/views.py b/zds/member/views.py index d2c71b6625..1b04bc30ff 100644 --- a/zds/member/views.py +++ b/zds/member/views.py @@ -99,7 +99,7 @@ def unregister(request): current = request.user for tuto in request.user.profile.get_tutos(): # we delete article only if not published with only one author - if tuto.pubdate is None and tuto.authors.count() == 1: + if not tuto.on_line() and tuto.authors.count() == 1: Tutorial.objects.filter(pk = tuto.pk).delete() else: if tuto.authors.count() == 1: @@ -108,7 +108,7 @@ def unregister(request): tuto.save() for article in request.user.profile.get_articles(): # we delete article only if not published with only one author - if article.pubdate is None and article.authors.count() == 1: + if not article.on_line() and article.authors.count() == 1: Article.objects.filter(pk = article.pk).delete() else: if article.authors.count() == 1: From 5bacf59353939c3bab2a1f030914b7fb1d806439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Sat, 30 Aug 2014 11:27:17 +0200 Subject: [PATCH 152/270] =?UTF-8?q?Ne=20change=20l'=C3=A9diteur=20que=20s'?= =?UTF-8?q?il=20y=20a=20eu=20edition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/member/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zds/member/views.py b/zds/member/views.py index 1b04bc30ff..227d036e5d 100644 --- a/zds/member/views.py +++ b/zds/member/views.py @@ -118,7 +118,8 @@ def unregister(request): # all messages anonymisation (forum, article and tutorial posts) for message in Comment.objects.filter(author = current): message.author = anonymous - message.editor = anonymous + if message.editor is not None: + message.editor = anonymous message.save() for message in PrivatePost.objects.filter(author = current): message.author = anonymous From 7d5a769364ddda29a3bf84adf5e204cf9606e4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Sat, 30 Aug 2014 18:11:09 +0200 Subject: [PATCH 153/270] Nettoyage des tests --- zds/member/tests/tests_views.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/zds/member/tests/tests_views.py b/zds/member/tests/tests_views.py index a43653f754..9a411e8c23 100644 --- a/zds/member/tests/tests_views.py +++ b/zds/member/tests/tests_views.py @@ -6,6 +6,7 @@ from django.core.urlresolvers import reverse from django.test import TestCase from datetime import datetime +from zds.settings import ANONYMOUS_USER, EXTERNAL_USER from zds.member.factories import ProfileFactory, StaffProfileFactory, NonAsciiProfileFactory, UserFactory from zds.member.forms import RegisterForm, ChangeUserForm, ChangePasswordForm @@ -27,8 +28,8 @@ def setUp(self): 'django.core.mail.backends.locmem.EmailBackend' self.mas = ProfileFactory() settings.BOT_ACCOUNT = self.mas.user.username - self.anonymous = UserFactory(username="anonymous", password="anything") - self.external = UserFactory(username="Auteur externe", password="anything") + self.anonymous = UserFactory(username=ANONYMOUS_USER, password="anything") + self.external = UserFactory(username=EXTERNAL_USER, password="anything") self.category1 = CategoryFactory(position=1) self.forum11 = ForumFactory( category=self.category1, @@ -285,19 +286,19 @@ def test_unregister(self): follow=False) self.assertEqual(result.status_code, 302) self.assertEqual(publishedTutorialAlone.authors.count(), 1) - self.assertEqual(publishedTutorialAlone.authors.first().username, "Auteur externe") + self.assertEqual(publishedTutorialAlone.authors.first().username, EXTERNAL_USER) self.assertEqual(publishedTutorial2.authors.count(), 1) - self.assertEqual(publishedTutorial2.authors.filter(username="Auteur externe").count(), 0) + self.assertEqual(publishedTutorial2.authors.filter(username=EXTERNAL_USER).count(), 0) self.assertEqual(Tutorial.objects.filter(pk=writingTutorialAlone.pk).count(), 0) self.assertEqual(writingTutorial2.authors.count(), 1) - self.assertEqual(writingTutorial2.authors.filter(username="Auteur externe").count(), 0) + self.assertEqual(writingTutorial2.authors.filter(username=EXTERNAL_USER).count(), 0) self.assertEqual(publishedArticleAlone.authors.count(), 1) - self.assertEqual(publishedArticleAlone.authors.first().username, "Auteur externe") + self.assertEqual(publishedArticleAlone.authors.first().username, EXTERNAL_USER) self.assertEqual(publishedArticle2.authors.count(), 1) - self.assertEqual(publishedArticle2.authors.filter(username="Auteur externe").count(), 0) + self.assertEqual(publishedArticle2.authors.filter(username=EXTERNAL_USER).count(), 0) self.assertEqual(Article.objects.filter(pk=writingArticleAlone.pk).count(), 0) self.assertEqual(writingArticle2.authors.count(), 1) - self.assertEqual(writingArticle2.authors.filter(username="Auteur externe").count(), 0) + self.assertEqual(writingArticle2.authors.filter(username=EXTERNAL_USER).count(), 0) self.assertEqual(Topic.objects.filter(author__username=user.user.username).count(), 0) self.assertEqual(Post.objects.filter(author__username=user.user.username).count(), 0) self.assertEqual(Post.objects.filter(editor__username=user.user.username).count(), 0) From f3b7d967bea18a743d000d88fa70630a16df84c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Sun, 31 Aug 2014 12:30:21 +0200 Subject: [PATCH 154/270] =?UTF-8?q?D=C3=A9sinscrit=20participants=20d'une?= =?UTF-8?q?=20discussion=20priv=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/base.html | 8 ++++---- templates/member/settings/base.html | 7 ++++--- zds/member/views.py | 8 ++++++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/templates/base.html b/templates/base.html index 39f90de218..158729fb0f 100644 --- a/templates/base.html +++ b/templates/base.html @@ -436,12 +436,12 @@
  • {% csrf_token %} - -
    -
  • + + + diff --git a/templates/member/settings/base.html b/templates/member/settings/base.html index 4d0190fb71..2a62aecb66 100644 --- a/templates/member/settings/base.html +++ b/templates/member/settings/base.html @@ -22,15 +22,16 @@

    Navigation

  • Mon profil
  • Compte
  • Pseudo et courriel
  • -
  • +
  • {% csrf_token %} - +
  • + diff --git a/zds/member/views.py b/zds/member/views.py index 227d036e5d..f084eb6a79 100644 --- a/zds/member/views.py +++ b/zds/member/views.py @@ -35,7 +35,7 @@ get_info_old_tuto, logout_user from zds.gallery.forms import ImageAsAvatarForm from zds.article.models import Article -from zds.forum.models import Topic, follow +from zds.forum.models import Topic, follow, TopicFollowed from zds.member.decorator import can_write_and_read_now from zds.tutorial.models import Tutorial from zds.utils import render_template @@ -118,7 +118,7 @@ def unregister(request): # all messages anonymisation (forum, article and tutorial posts) for message in Comment.objects.filter(author = current): message.author = anonymous - if message.editor is not None: + if message.editor is not None and message.editor.username == current.username: message.editor = anonymous message.save() for message in PrivatePost.objects.filter(author = current): @@ -127,9 +127,13 @@ def unregister(request): for topic in PrivateTopic.objects.filter(author = current): topic.author = anonymous topic.save() + for topic in PrivateTopic.objects.filter(participants__in =[current]): + topic.participants.remove(current) + topic.save() for topic in Topic.objects.filter(author = current): topic.author = anonymous topic.save() + TopicFollowed.objects.filter(user = current).delete() for gallery in UserGallery.objects.filter(user = current): gallery.user = anonymous gallery.save() From 412dfbb3c3445277d1df5727f3550557ba2f588b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Sun, 31 Aug 2014 21:45:14 +0200 Subject: [PATCH 155/270] =?UTF-8?q?Corrige=20les=20galeries=20pour=20d?= =?UTF-8?q?=C3=A9sinscription?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/member/tests/tests_views.py | 6 ++---- zds/member/views.py | 10 ++++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/zds/member/tests/tests_views.py b/zds/member/tests/tests_views.py index 9a411e8c23..39f5ea1f8e 100644 --- a/zds/member/tests/tests_views.py +++ b/zds/member/tests/tests_views.py @@ -106,13 +106,11 @@ def test_unregister(self): # first case : a published tutorial with only one author publishedTutorialAlone = MiniTutorialFactory() publishedTutorialAlone.authors.add(user.user) - publishedTutorialAlone.pubdate = datetime.now()#must see how to publish more properly publishedTutorialAlone.save() # second case : a published tutorial with two authors publishedTutorial2 = MiniTutorialFactory() publishedTutorial2.authors.add(user.user) publishedTutorial2.authors.add(user2.user) - publishedTutorial2.pubdate = datetime.now() publishedTutorial2.save() # third case : a private tutorial with only one author writingTutorialAlone = MiniTutorialFactory() @@ -214,7 +212,7 @@ def test_unregister(self): password='hostel77') self.assertEqual(login_check, True) - # reserve tutorial + # reserve article validation = ArticleValidation.objects.get( article__pk=publishedArticleAlone.pk) pub = self.client.post( @@ -251,7 +249,7 @@ def test_unregister(self): password='hostel77') self.assertEqual(login_check, True) - # reserve tutorial + # reserve article validation = ArticleValidation.objects.get( article__pk=publishedArticle2.pk) pub = self.client.post( diff --git a/zds/member/views.py b/zds/member/views.py index f084eb6a79..73deede9b0 100644 --- a/zds/member/views.py +++ b/zds/member/views.py @@ -135,8 +135,14 @@ def unregister(request): topic.save() TopicFollowed.objects.filter(user = current).delete() for gallery in UserGallery.objects.filter(user = current): - gallery.user = anonymous - gallery.save() + if gallery.gallery.get_users().count() == 1: + anonymousGallery = UserGallery() + anonymousGallery.user = external + anonymousGallery.mode = "w" + anonymousGallery.gallery = gallery.gallery + anonymousGallery.save() + else: + gallery.delete() logout(request) User.objects.filter(pk = current.pk).delete() From 303c2f2481c9b75ec612829331237c463b005ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Wed, 3 Sep 2014 14:37:52 +0200 Subject: [PATCH 156/270] Page d'avertissement + fixtures --- fixtures/users.yaml | 15 +++++++++++++++ templates/member/settings/base.html | 11 +---------- templates/member/unregister.html | 27 +++++++++++++++++++++++++++ zds/member/urls.py | 1 + zds/member/views.py | 4 ++++ 5 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 templates/member/unregister.html diff --git a/fixtures/users.yaml b/fixtures/users.yaml index 291e43cc83..5b97cb36bf 100644 --- a/fixtures/users.yaml +++ b/fixtures/users.yaml @@ -88,3 +88,18 @@ fields: user: 4 last_ip_address: 192.168.0.1 +- model: member.Profile + pk: 4 + fields: + user: 4 + last_ip_address: 192.168.0.1 +- model: member.Profile + pk: 5 + fields: + user: 5 + last_ip_address: 192.168.0.1 +- model: member.Profile + pk: 6 + fields: + user: 6 + last_ip_address: 192.168.0.1 \ No newline at end of file diff --git a/templates/member/settings/base.html b/templates/member/settings/base.html index 2a62aecb66..e537f32a21 100644 --- a/templates/member/settings/base.html +++ b/templates/member/settings/base.html @@ -22,16 +22,7 @@

    Navigation

  • Mon profil
  • Compte
  • Pseudo et courriel
  • -
  • -
    - {% csrf_token %} - -
    -
  • - +
  • Se désinscrire
  • diff --git a/templates/member/unregister.html b/templates/member/unregister.html new file mode 100644 index 0000000000..94ffc9ffea --- /dev/null +++ b/templates/member/unregister.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} +{% block breadcrumb %} +
  • Supression de compte
  • +{% endblock %} +{% block content_out %} +

    Attention {{user.username}}, vous êtes sur le point de vous désinscrire de Zeste de Savoir. Voici ce qu'implique cette action qui ne peut-être annulée :

    +
      +
    • Tous vos MP seront perdus
    • +
    • Tous vos messages de forums seront anonymisés
    • +
    • Votre compte et tout ses détails seront supprimés. Cela signifie que l'adresse email ({{user.email}}) sera effacée du système et votre pseudo ({{user.username}}) sera libéré et utilisable par un autre membre.
    • +
    • Vos tutoriels et articles non publiés (brouillon ou beta) seront supprimés sans possibilité de recuperation (sauf si d'autres auteurs sont présents lors de la rédaction)
    • +
    • Vos tutoriels et articles publies seront : +
        +
      • Si vous étiez seul, placés sous la gouvernance du compte "Auteur Externe" (sauf demande contraire de votre part a un membre du staff AVANT votre désinscription)
      • +
      • Si vous étiez plusieurs auteurs, vous quitterez le groupe de redacteur. Les autres pourront continuer alors sans vous.
      • +
      +
    • +
    +

    Si vous souhaitez poursuivre votre désinscription, cliquez sur le bouton "Se désinscrire" ci-dessous. Sinon il n'est pas encore trop tard…

    +
    + {% csrf_token %} + +
    +{% endblock %} \ No newline at end of file diff --git a/zds/member/urls.py b/zds/member/urls.py index 2108b5c942..86ac2e10d2 100644 --- a/zds/member/urls.py +++ b/zds/member/urls.py @@ -8,6 +8,7 @@ urlpatterns = patterns('', url(r'^$', 'zds.member.views.index'), url(r'^desinscrire/$', 'zds.member.views.unregister'), + url(r'^desinscrire/attention$', 'zds.member.views.warning_unregister'), url(r'^voir/(?P.+)/$', 'zds.member.views.details'), url(r'^profil/modifier/(?P\d+)/$', diff --git a/zds/member/views.py b/zds/member/views.py index 73deede9b0..1fd503629a 100644 --- a/zds/member/views.py +++ b/zds/member/views.py @@ -87,6 +87,10 @@ def index(request): "nb": page, }) +@login_required +def warning_unregister(request): + """displays a warning page showing what will happen when user unregisters""" + return render_template("member/unregister.html",{"user":request.user}) @login_required @require_POST From c7454d7d1be6f97db2d650d583479ff7435b0133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Wed, 3 Sep 2014 20:50:05 +0200 Subject: [PATCH 157/270] Documentation des fixtures --- README.md | 4 +++- fixtures/users.yaml | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8548da7794..7175c12142 100644 --- a/README.md +++ b/README.md @@ -70,10 +70,12 @@ python manage.py loaddata fixtures/*.yaml Cela va créer plusieurs entitées : -* 3 utilisateurs (utilisateur/mot de passe) : +* 5 utilisateurs (utilisateur/mot de passe) : * user/user : Utilisateur normal * staff/staff : Utilisateur avec les droits d'un staff * admin/admin : Utilisateur avec les droits d'un staff et d'un admin + * anonymous/anonymous : Utilisateur qui permet l'anonymisation des messages sur les forums + * Auteur externe/external : Utilisateur qui permet de récupérer les tutoriels d'anciens membres et/ou de publier des tutoriels externes. * 3 catégories * 11 forums * 3 sujets avec une réponse diff --git a/fixtures/users.yaml b/fixtures/users.yaml index 5b97cb36bf..d9708af767 100644 --- a/fixtures/users.yaml +++ b/fixtures/users.yaml @@ -58,7 +58,7 @@ first_name: Anonymous last_name: User username: anonymous - password: pbkdf2_sha256$12000$FssznaRHkjFK$8GvNM2oqOIHRNNr4G+8s+cbk6LIYXaxGOsI1Hh6cswI= + password: pbkdf2_sha256$12000$DWHNLpO3jXIv$MrnMZblGu1Q+xemP4XK2keLtbyfkRXxczPTLhSJ0AAM= is_superuser: False - model: auth.user pk: 6 @@ -66,7 +66,7 @@ first_name: Auteur externe last_name: User username: Auteur externe - password: pbkdf2_sha256$12000$FssznaRHkjFK$8GvNM2oqOIHRNNr4G+8s+cbk6LIYXaxGOsI1Hh6cswI= + password: pbkdf2_sha256$12000$DWHNLpO3jXIv$MrnMZblGu1Q+xemP4XK2keLtbyfkRXxczPTLhSJ0AAM= is_superuser: False - model: member.Profile pk: 1 From 3e8324a25f0c70478966ee5e703765f5e6954c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Wed, 3 Sep 2014 21:59:47 +0200 Subject: [PATCH 158/270] =?UTF-8?q?Suppresion=20des=20MP=20d=C3=A9sinscrit?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/member/views.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/zds/member/views.py b/zds/member/views.py index 1fd503629a..37a3829b5e 100644 --- a/zds/member/views.py +++ b/zds/member/views.py @@ -125,12 +125,8 @@ def unregister(request): if message.editor is not None and message.editor.username == current.username: message.editor = anonymous message.save() - for message in PrivatePost.objects.filter(author = current): - message.author = anonymous - message.save() - for topic in PrivateTopic.objects.filter(author = current): - topic.author = anonymous - topic.save() + PrivatePost.objects.filter(author = current).delete() + PrivateTopic.objects.filter(author = current).delete() for topic in PrivateTopic.objects.filter(participants__in =[current]): topic.participants.remove(current) topic.save() From aa0e58c03a605d435d49694d740bbe5ab0d7b061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Thu, 4 Sep 2014 17:27:52 +0200 Subject: [PATCH 159/270] =?UTF-8?q?Suppr=20tuto=20complet=20lorsque=20le?= =?UTF-8?q?=20membre=20se=20d=C3=A9sinscrit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/article/models.py | 8 ++++++++ zds/member/views.py | 4 ++-- zds/tutorial/models.py | 9 ++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/zds/article/models.py b/zds/article/models.py index 11a502b0cc..8b6e224b35 100644 --- a/zds/article/models.py +++ b/zds/article/models.py @@ -7,6 +7,8 @@ import os import string import uuid +import shutil + from easy_thumbnails.fields import ThumbnailerImageField try: @@ -90,6 +92,12 @@ class Meta: def __unicode__(self): return self.title + def delete_entity_and_tree(self, using=None): + shutil.rmtree(self.get_path(),0) + if self.on_line(): + shutil.rmtree(self.get_prod_path()) + super.delete() + def get_absolute_url(self): return reverse('zds.article.views.view', kwargs={'article_pk': self.pk, diff --git a/zds/member/views.py b/zds/member/views.py index 37a3829b5e..cdf8c18e88 100644 --- a/zds/member/views.py +++ b/zds/member/views.py @@ -104,7 +104,7 @@ def unregister(request): for tuto in request.user.profile.get_tutos(): # we delete article only if not published with only one author if not tuto.on_line() and tuto.authors.count() == 1: - Tutorial.objects.filter(pk = tuto.pk).delete() + tuto.delete_entity_and_tree() else: if tuto.authors.count() == 1: tuto.authors.add(external) @@ -113,7 +113,7 @@ def unregister(request): for article in request.user.profile.get_articles(): # we delete article only if not published with only one author if not article.on_line() and article.authors.count() == 1: - Article.objects.filter(pk = article.pk).delete() + article.delete_entity_and_tree() else: if article.authors.count() == 1: article.authors.add(external) diff --git a/zds/tutorial/models.py b/zds/tutorial/models.py index a963aa72b7..42e0e5afcd 100644 --- a/zds/tutorial/models.py +++ b/zds/tutorial/models.py @@ -1,6 +1,7 @@ # coding: utf-8 from math import ceil +import shutil try: import ujson as json_reader except: @@ -284,7 +285,7 @@ def get_introduction(self, sha=None): if sha is None: sha = self.sha_draft repo = Repo(self.get_path()) - + manifest = get_blob(repo.commit(sha).tree, "manifest.json") tutorial_version = json_reader.loads(manifest) if "introduction" in tutorial_version: @@ -333,6 +334,12 @@ def get_conclusion_online(self): return conclu_contenu.decode('utf-8') + def delete_entity_and_tree(self, using=None): + shutil.rmtree(self.get_path(),0) + if self.on_line(): + shutil.rmtree(self.get_prod_path()) + super.delete() + def save(self, *args, **kwargs): self.slug = slugify(self.title) From 3127ccb83e98fc807eaf9b60828c5203e283eef9 Mon Sep 17 00:00:00 2001 From: Alex-D Date: Wed, 3 Sep 2014 22:53:31 +0200 Subject: [PATCH 160/270] =?UTF-8?q?Am=C3=A9lioration=20du=20front=20de=20l?= =?UTF-8?q?a=20d=C3=A9sinscription?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/member/unregister.html | 65 +++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/templates/member/unregister.html b/templates/member/unregister.html index 94ffc9ffea..0bb2836875 100644 --- a/templates/member/unregister.html +++ b/templates/member/unregister.html @@ -1,27 +1,66 @@ -{% extends "base.html" %} +{% extends "member/base.html" %} + + + {% block breadcrumb %}
  • Supression de compte
  • {% endblock %} -{% block content_out %} -

    Attention {{user.username}}, vous êtes sur le point de vous désinscrire de Zeste de Savoir. Voici ce qu'implique cette action qui ne peut-être annulée :

    + + + +{% block title %} + Désinscription +{% endblock %} + + + +{% block content %} +

    + Attention {{ user.username }}, vous êtes sur le point de vous désinscrire de Zeste de Savoir. Voici ce qu'implique cette action qui ne peut-être annulée : +

    +
      -
    • Tous vos MP seront perdus
    • -
    • Tous vos messages de forums seront anonymisés
    • -
    • Votre compte et tout ses détails seront supprimés. Cela signifie que l'adresse email ({{user.email}}) sera effacée du système et votre pseudo ({{user.username}}) sera libéré et utilisable par un autre membre.
    • -
    • Vos tutoriels et articles non publiés (brouillon ou beta) seront supprimés sans possibilité de recuperation (sauf si d'autres auteurs sont présents lors de la rédaction)
    • -
    • Vos tutoriels et articles publies seront : +
    • + Toutes vos conversation privées (MP) seront perdues ; +
    • +
    • + Tous vos messages de forums seront anonymisés ; +
    • +
    • + Votre compte et tout ses détails seront supprimés, cela signifie que l'adresse courriel ({{ user.email }}) sera effacée du système et votre pseudo ({{ user.username }}) sera libéré et utilisable par un autre membre ; +
    • +
    • + Vos tutoriels et articles non publiés (brouillon ou bêta) seront supprimés sans possibilité de récuperation (sauf si d'autres auteurs sont présents lors de la rédaction) ; +
    • +
    • + Vos tutoriels et articles publiés subiront les modifications suivantes :
        -
      • Si vous étiez seul, placés sous la gouvernance du compte "Auteur Externe" (sauf demande contraire de votre part a un membre du staff AVANT votre désinscription)
      • -
      • Si vous étiez plusieurs auteurs, vous quitterez le groupe de redacteur. Les autres pourront continuer alors sans vous.
      • +
      • + si vous étiez seul, ils seront placés sous la gouvernance du compte "Auteur Externe", sauf demande contraire de votre part à un membre du staff avant votre désinscription ; +
      • +
      • + si vous étiez plusieurs auteurs, vous quitterez le groupe de rédacteurs. Les autres pourront dès lors continuer sans vous. +
    -

    Si vous souhaitez poursuivre votre désinscription, cliquez sur le bouton "Se désinscrire" ci-dessous. Sinon il n'est pas encore trop tard…

    -
    + Si vous souhaitez poursuivre votre désinscription, cliquez sur le bouton "Me désinscrire" ci-dessous. Sinon il n'est pas encore trop tard... + + Me désinscrire +

    + +

    + C'est votre dernière chance de rester parmis nous... +

    + {% csrf_token %} - +
    {% endblock %} \ No newline at end of file From 43205c8f7ac3452d7df0c7846332a265761e300d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Thu, 4 Sep 2014 21:59:05 +0200 Subject: [PATCH 161/270] =?UTF-8?q?Suppr=20tuto=20complet=20lorsque=20le?= =?UTF-8?q?=20membre=20se=20d=C3=A9sinscrit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/article/models.py | 2 +- zds/member/tests/tests_views.py | 5 ++++- zds/tutorial/models.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/zds/article/models.py b/zds/article/models.py index 8b6e224b35..bb4e757ef7 100644 --- a/zds/article/models.py +++ b/zds/article/models.py @@ -96,7 +96,7 @@ def delete_entity_and_tree(self, using=None): shutil.rmtree(self.get_path(),0) if self.on_line(): shutil.rmtree(self.get_prod_path()) - super.delete() + self.delete() def get_absolute_url(self): return reverse('zds.article.views.view', diff --git a/zds/member/tests/tests_views.py b/zds/member/tests/tests_views.py index 39f5ea1f8e..0b4ab085b5 100644 --- a/zds/member/tests/tests_views.py +++ b/zds/member/tests/tests_views.py @@ -1,5 +1,6 @@ # coding: utf-8 +import os from django.conf import settings from django.contrib.auth.models import User from django.core import mail @@ -116,6 +117,7 @@ def test_unregister(self): writingTutorialAlone = MiniTutorialFactory() writingTutorialAlone.authors.add(user.user) writingTutorialAlone.save() + writingTutorialAlonePath = writingTutorialAlone.get_path() # fourth case : a private tutorial with at least two authors writingTutorial2 = MiniTutorialFactory() writingTutorial2.authors.add(user.user) @@ -300,7 +302,8 @@ def test_unregister(self): self.assertEqual(Topic.objects.filter(author__username=user.user.username).count(), 0) self.assertEqual(Post.objects.filter(author__username=user.user.username).count(), 0) self.assertEqual(Post.objects.filter(editor__username=user.user.username).count(), 0) - + self.assertFalse(os.path.exists(writingTutorialAlonePath)) + def test_sanctions(self): """Test various sanctions.""" diff --git a/zds/tutorial/models.py b/zds/tutorial/models.py index 42e0e5afcd..6a0cf5a7fd 100644 --- a/zds/tutorial/models.py +++ b/zds/tutorial/models.py @@ -338,7 +338,7 @@ def delete_entity_and_tree(self, using=None): shutil.rmtree(self.get_path(),0) if self.on_line(): shutil.rmtree(self.get_prod_path()) - super.delete() + self.delete() def save(self, *args, **kwargs): self.slug = slugify(self.title) From df086451b4aa5b1b7b0e75f586464f2a700b0efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fran=C3=A7ois=20dambrine?= Date: Fri, 5 Sep 2014 08:22:27 +0200 Subject: [PATCH 162/270] Override des settings dans le test --- fixtures/users.yaml | 5 ----- templates/member/unregister.html | 4 ++-- zds/member/tests/tests_views.py | 13 ++++++++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/fixtures/users.yaml b/fixtures/users.yaml index d9708af767..cb659b80c3 100644 --- a/fixtures/users.yaml +++ b/fixtures/users.yaml @@ -88,11 +88,6 @@ fields: user: 4 last_ip_address: 192.168.0.1 -- model: member.Profile - pk: 4 - fields: - user: 4 - last_ip_address: 192.168.0.1 - model: member.Profile pk: 5 fields: diff --git a/templates/member/unregister.html b/templates/member/unregister.html index 0bb2836875..6473be7b57 100644 --- a/templates/member/unregister.html +++ b/templates/member/unregister.html @@ -21,7 +21,7 @@