From 606891bcb9ce2cf85cdf129f5ecbd9a60cd7be76 Mon Sep 17 00:00:00 2001 From: Pierre Beaujean Date: Thu, 10 Aug 2017 20:07:16 +0200 Subject: [PATCH] Corrige la gestion des unes (#4450) * Corrige la gestion des unes (fix #4444) * CBV that! * Ajoute un input type="datetime-local" --- templates/featured/index.html | 2 + zds/featured/forms.py | 41 ++++++--- zds/featured/tests.py | 102 +++++++++++++++------- zds/featured/views.py | 154 ++++++++++++++++------------------ 4 files changed, 175 insertions(+), 124 deletions(-) diff --git a/templates/featured/index.html b/templates/featured/index.html index 94902e083c..7c3cc59472 100644 --- a/templates/featured/index.html +++ b/templates/featured/index.html @@ -1,6 +1,7 @@ {% extends "featured/base.html" %} {% load i18n %} {% load remove_url_protocole %} +{% load date %} {% block title %} {% trans "Liste des unes" %} @@ -45,6 +46,7 @@ class="topic-image" > {{ featured_resource.title }} + {% trans 'Publiée' %} {{ featured_resource.pubdate|format_date:True|lower }} diff --git a/zds/featured/forms.py b/zds/featured/forms.py index 1ed95bbbf3..d11f5ab672 100644 --- a/zds/featured/forms.py +++ b/zds/featured/forms.py @@ -13,7 +13,7 @@ class FeaturedResourceForm(forms.ModelForm): class Meta: model = FeaturedResource - fields = ['title', 'type', 'authors', 'image_url', 'url', 'pubdate'] + fields = ['title', 'type', 'authors', 'image_url', 'url'] widgets = { 'title': forms.TextInput( @@ -44,12 +44,6 @@ class Meta: attrs={ 'placeholder': _(u'Lien vers la ressource.') } - ), - - 'pubdate': forms.DateTimeInput( - attrs={ - 'placeholder': _(u'Exemple : 2016-12-25 00:00:00') - } ) } @@ -59,25 +53,48 @@ class Meta: required=False ) + pubdate = forms.DateTimeField( + label=_(u'Date de publication (exemple: 25/12/2015 15:00 ou 2015-12-25T15:00)'), + input_formats=[ + '%d/%m/%Y %H:%M:%S', '%Y-%m-%d %H:%M:%S', # full format with second + '%Y-%m-%dT%H:%M', # datetime field format + '%Y-%m-%d %H:%M', '%d/%m/%Y %H:%M', # without second + '%Y-%m-%d', '%d/%m/%Y' # day only + ], + widget=forms.DateTimeInput( + attrs={'placeholder': _(u'Exemple : 25/12/2016 10:00'), 'type': 'datetime-local'}, + format='%Y-%m-%dT%H:%M' # datetime field format + ) + ) + def __init__(self, *args, **kwargs): + hide_major_update_field = kwargs.pop('hide_major_update_field', False) + super(FeaturedResourceForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_class = 'content-wrapper' self.helper.form_method = 'post' self.helper.form_action = reverse('featured-resource-create') - self.helper.layout = Layout( + fields = [ Field('title'), Field('type'), Field('authors'), Field('image_url'), - Field('url'), - Field('major_update'), + Field('url') + ] + + if not hide_major_update_field: + fields.append(Field('major_update')) + + fields.extend([ Field('pubdate'), ButtonHolder( StrictButton(_(u'Enregistrer'), type='submit'), - ), - ) + ) + ]) + + self.helper.layout = Layout(*fields) class FeaturedMessageForm(forms.ModelForm): diff --git a/zds/featured/tests.py b/zds/featured/tests.py index c2b0fe64b5..a2ffc4edeb 100644 --- a/zds/featured/tests.py +++ b/zds/featured/tests.py @@ -6,6 +6,7 @@ from zds.member.factories import StaffProfileFactory, ProfileFactory from zds.featured.factories import FeaturedResourceFactory from zds.featured.models import FeaturedResource, FeaturedMessage +from datetime import datetime, date stringof2001chars = 'http://url.com/' @@ -52,25 +53,44 @@ def test_success_create_featured(self): username=staff.user.username, password='hostel77' ) - self.assertTrue(login_check) + self.assertTrue(login_check) self.assertEqual(0, FeaturedResource.objects.all().count()) - response = self.client.post( - reverse('featured-resource-create'), - { - 'title': 'title', - 'type': 'type', - 'image_url': 'image_url', - 'url': 'url', - 'authors': staff.user.username, - 'pubdate': '2016-12-25 00:00:00' - }, - follow=True - ) + + pubdate = date(2016, 1, 1).strftime('%d/%m/%Y %H:%M:%S') + + fields = { + 'title': 'title', + 'type': 'type', + 'image_url': 'http://test.com/image.png', + 'url': 'http://test.com', + 'authors': staff.user.username, + 'pubdate': pubdate + } + + response = self.client.post(reverse('featured-resource-create'), fields, follow=True) self.assertEqual(200, response.status_code) self.assertEqual(1, FeaturedResource.objects.all().count()) + featured = FeaturedResource.objects.first() + + for field, value in fields.items(): + if field != 'pubdate': + self.assertEqual(value, getattr(featured, field), msg='Error on {}'.format(field)) + else: + self.assertEqual(value, featured.pubdate.strftime('%d/%m/%Y %H:%M:%S')) + + # now with major_update + fields['major_update'] = 'on' + + response = self.client.post(reverse('featured-resource-create'), fields, follow=True) + self.assertEqual(200, response.status_code) + self.assertEqual(2, FeaturedResource.objects.all().count()) + + featured = FeaturedResource.objects.last() + self.assertTrue((datetime.now() - featured.pubdate).total_seconds() < 10) + def test_failure_create_featured_with_unauthenticated_user(self): response = self.client.get(reverse('featured-resource-create')) @@ -103,7 +123,7 @@ def test_failure_too_long_url(self): 'title': 'title', 'type': 'type', 'image_url': stringof2001chars, - 'url': 'url', + 'url': 'http://test.com', 'authors': staff.user.username }, follow=True @@ -117,7 +137,7 @@ def test_failure_too_long_url(self): { 'title': 'title', 'type': 'type', - 'image_url': 'url', + 'image_url': 'http://test.com/image.png', 'url': stringof2001chars, 'authors': staff.user.username }, @@ -139,21 +159,45 @@ def test_success_update_featured(self): news = FeaturedResourceFactory() self.assertEqual(1, FeaturedResource.objects.all().count()) - response = self.client.post( - reverse('featured-resource-update', args=[news.pk]), - { - 'title': 'title', - 'type': 'type', - 'image_url': 'image_url', - 'url': 'url', - 'authors': staff.user.username - }, - follow=True - ) + + old_featured = FeaturedResource.objects.first() + + pubdate = date(2016, 1, 1).strftime('%d/%m/%Y %H:%M:%S') + + fields = { + 'title': 'title', + 'type': 'type', + 'image_url': 'http://test.com/image.png', + 'url': 'http://test.com', + 'authors': staff.user.username, + 'pubdate': pubdate + } + + response = self.client.post(reverse('featured-resource-update', args=[news.pk]), fields, follow=True) self.assertEqual(200, response.status_code) self.assertEqual(1, FeaturedResource.objects.all().count()) + featured = FeaturedResource.objects.first() + + for field, value in fields.items(): + self.assertNotEqual(getattr(featured, field), getattr(old_featured, field)) + + if field != 'pubdate': + self.assertEqual(value, getattr(featured, field), msg='Error on {}'.format(field)) + else: + self.assertEqual(value, featured.pubdate.strftime('%d/%m/%Y %H:%M:%S')) + + # now with major_update + self.assertFalse((datetime.now() - featured.pubdate).total_seconds() < 10) + + fields['major_update'] = 'on' + + response = self.client.post(reverse('featured-resource-update', args=[news.pk]), fields, follow=True) + self.assertEqual(200, response.status_code) + featured = FeaturedResource.objects.first() + self.assertTrue((datetime.now() - featured.pubdate).total_seconds() < 10) + def test_failure_create_featured_with_unauthenticated_user(self): response = self.client.get(reverse('featured-resource-update', args=[42])) @@ -266,7 +310,7 @@ def test_success_list_create_message(self): reverse('featured-message-create'), { 'message': 'message', - 'url': 'url', + 'url': 'http://test.com', }, follow=True ) @@ -286,7 +330,7 @@ def test_create_only_one_message_in_system(self): reverse('featured-message-create'), { 'message': 'message', - 'url': 'url', + 'url': 'http://test.com', }, follow=True ) @@ -298,7 +342,7 @@ def test_create_only_one_message_in_system(self): reverse('featured-message-create'), { 'message': 'message', - 'url': 'url', + 'url': 'http://test.com', }, follow=True ) diff --git a/zds/featured/views.py b/zds/featured/views.py index 94ab9150e5..3462c931b1 100644 --- a/zds/featured/views.py +++ b/zds/featured/views.py @@ -5,11 +5,10 @@ from django.contrib.auth.decorators import login_required, permission_required from django.core.urlresolvers import reverse from django.db import transaction -from django.shortcuts import render, redirect +from django.shortcuts import redirect from django.utils.decorators import method_decorator from django.utils.translation import ugettext as _ -from django.views.generic import CreateView, RedirectView, UpdateView -from django.views.generic.detail import SingleObjectMixin +from django.views.generic import CreateView, RedirectView, UpdateView, FormView, DeleteView from django.views.generic.list import MultipleObjectMixin from django.conf import settings @@ -41,31 +40,34 @@ class FeaturedResourceCreate(CreateView): form_class = FeaturedResourceForm template_name = 'featured/resource/create.html' + context_object_name = 'featured_resource' @method_decorator(login_required) @method_decorator(permission_required('featured.change_featuredresource', raise_exception=True)) def dispatch(self, request, *args, **kwargs): return super(FeaturedResourceCreate, self).dispatch(request, *args, **kwargs) - def post(self, request, *args, **kwargs): - form = self.form_class(request.POST) - - if form.is_valid(): - return self.form_valid(form) - - return render(request, self.template_name, {'form': form}) + def get_form_kwargs(self): + kw = super(FeaturedResourceCreate, self).get_form_kwargs() + kw['hide_major_update_field'] = True + return kw def form_valid(self, form): - featured_resource = FeaturedResource() - featured_resource.title = form.data.get('title') - featured_resource.type = form.data.get('type') - featured_resource.authors = form.data.get('authors') - featured_resource.image_url = form.data.get('image_url') - featured_resource.url = form.data.get('url') - featured_resource.pubdate = form.data.get('pubdate') + featured_resource.title = form.cleaned_data.get('title') + featured_resource.type = form.cleaned_data.get('type') + featured_resource.authors = form.cleaned_data.get('authors') + featured_resource.image_url = form.cleaned_data.get('image_url') + featured_resource.url = form.cleaned_data.get('url') + + if form.cleaned_data.get('major_update', False): + featured_resource.pubdate = datetime.now() + else: + featured_resource.pubdate = form.cleaned_data.get('pubdate') + featured_resource.save() + messages.success(self.request, _(u'La une a été créée.')) return redirect(reverse('featured-resource-list')) @@ -77,74 +79,66 @@ class FeaturedResourceUpdate(UpdateView): form_class = FeaturedResourceForm template_name = 'featured/resource/update.html' queryset = FeaturedResource.objects.all() - featured_resource = None + context_object_name = 'featured_resource' @method_decorator(login_required) @method_decorator(permission_required('featured.change_featuredresource', raise_exception=True)) def dispatch(self, request, *args, **kwargs): return super(FeaturedResourceUpdate, self).dispatch(request, *args, **kwargs) - def get(self, request, *args, **kwargs): - self.featured_resource = self.get_object() - form = self.form_class(initial={ - 'title': self.featured_resource.title, - 'type': self.featured_resource.type, - 'authors': self.featured_resource.authors, - 'image_url': self.featured_resource.image_url, - 'url': self.featured_resource.url, - 'pubdate': self.featured_resource.pubdate, + def get_initial(self): + initial = super(FeaturedResourceUpdate, self).get_initial() + initial.update({ + 'title': self.object.title, + 'type': self.object.type, + 'authors': self.object.authors, + 'image_url': self.object.image_url, + 'url': self.object.url, + 'pubdate': self.object.pubdate, }) - form.helper.form_action = reverse('featured-resource-update', args=[self.featured_resource.pk]) - return render(request, self.template_name, {'form': form, 'featured_resource': self.featured_resource}) - def post(self, request, *args, **kwargs): - self.featured_resource = self.get_object() - form = self.form_class(request.POST) - - if form.is_valid(): - return self.form_valid(form) - - return render(request, self.template_name, {'form': form, 'featured_resource': self.featured_resource}) + return initial def form_valid(self, form): - self.featured_resource.title = form.data.get('title') - self.featured_resource.type = form.data.get('type') - self.featured_resource.authors = form.data.get('authors') - self.featured_resource.image_url = form.data.get('image_url') - self.featured_resource.url = form.data.get('url') - if form.data.get('major_update') is not None: - self.featured_resource.pubdate = datetime.now() - - self.featured_resource.save() - return redirect(reverse('homepage')) - - def get_form(self, form_class=FeaturedResourceForm): - form = self.form_class(self.request.POST) - form.helper.form_action = reverse('featured-resource-update', args=[self.featured_resource.pk]) + self.object.title = form.cleaned_data.get('title') + self.object.type = form.cleaned_data.get('type') + self.object.authors = form.cleaned_data.get('authors') + self.object.image_url = form.cleaned_data.get('image_url') + self.object.url = form.cleaned_data.get('url') + if form.cleaned_data.get('major_update', False): + self.object.pubdate = datetime.now() + else: + self.object.pubdate = form.cleaned_data.get('pubdate') + + messages.success(self.request, _(u'La une a été mise à jour.')) + self.success_url = reverse('featured-resource-list') + return super(FeaturedResourceUpdate, self).form_valid(form) + + def get_form(self, form_class=None): + form = super(FeaturedResourceUpdate, self).get_form(form_class) + form.helper.form_action = reverse('featured-resource-update', args=[self.object.pk]) return form -class FeaturedResourceDeleteDetail(SingleObjectMixin, RedirectView): +class FeaturedResourceDeleteDetail(DeleteView): """ Deletes a featured resource. """ - queryset = FeaturedResource.objects.all() - permanent = False + + model = FeaturedResource @method_decorator(login_required) @method_decorator(transaction.atomic) @method_decorator(permission_required('featured.change_featuredresource', raise_exception=True)) def dispatch(self, request, *args, **kwargs): + self.success_url = reverse('featured-resource-list') return super(FeaturedResourceDeleteDetail, self).dispatch(request, *args, **kwargs) def post(self, request, *args, **kwargs): - featured_resource = self.get_object() - featured_resource.delete() - + r = super(FeaturedResourceDeleteDetail, self).post(request, *args, **kwargs) messages.success(request, _(u'La une a été supprimée avec succès.')) - - return redirect(reverse('featured-resource-list')) + return r class FeaturedResourceDeleteList(MultipleObjectMixin, RedirectView): @@ -171,48 +165,42 @@ def post(self, request, *args, **kwargs): return redirect(reverse('featured-resource-list')) -class FeaturedMessageCreateUpdate(CreateView): +class FeaturedMessageCreateUpdate(FormView): """ Creates or updates the featured message. """ form_class = FeaturedMessageForm template_name = 'featured/message/create.html' + last_message = None @method_decorator(login_required) @method_decorator(permission_required('featured.change_featuredmessage', raise_exception=True)) def dispatch(self, request, *args, **kwargs): + self.last_message = FeaturedMessage.objects.get_last_message() return super(FeaturedMessageCreateUpdate, self).dispatch(request, *args, **kwargs) - def get(self, request, *args, **kwargs): - last_message = FeaturedMessage.objects.get_last_message() - init = {} - if last_message is not None: - init = { - 'hook': last_message.hook, - 'message': last_message.message, - 'url': last_message.url, - } + def get_initial(self): + init = super(FeaturedMessageCreateUpdate, self).get_initial() - form = self.form_class(initial=init) - form.helper.form_action = reverse('featured-message-create') - return render(request, self.template_name, {'form': form}) + if self.last_message is not None: + init.update({ + 'hook': self.last_message.hook, + 'message': self.last_message.message, + 'url': self.last_message.url, + }) - def post(self, request, *args, **kwargs): - form = self.form_class(request.POST) - - if form.is_valid(): - return self.form_valid(form) - - return render(request, self.template_name, {'form': form}) + return init def form_valid(self, form): - last_message = FeaturedMessage.objects.get_last_message() - if last_message: - last_message.delete() + if self.last_message: + self.last_message.delete() + featured_message = FeaturedMessage() featured_message.hook = form.data.get('hook') featured_message.message = form.data.get('message') featured_message.url = form.data.get('url') featured_message.save() - return redirect(reverse('homepage')) + + messages.success(self.request, _(u'Le message a été changé')) + return redirect(reverse('featured-resource-list'))