From 1ca6bf7c6212bc153092bd5a45f917565377552a Mon Sep 17 00:00:00 2001 From: Skia Date: Fri, 23 Dec 2016 03:02:46 +0100 Subject: [PATCH] Add news system, still miss nices templates and moderation tools --- com/admin.py | 1 + com/migrations/0002_news_newsdate.py | 40 ++++++++++ com/models.py | 42 +++++++++++ com/templates/com/news_admin_list.jinja | 25 +++++++ com/templates/com/news_detail.jinja | 15 ++++ com/templates/com/news_edit.jinja | 59 +++++++++++++++ com/templates/com/news_list.jinja | 86 ++++++++++++++++++++++ com/urls.py | 11 ++- com/views.py | 98 ++++++++++++++++++++++++- 9 files changed, 372 insertions(+), 5 deletions(-) create mode 100644 com/migrations/0002_news_newsdate.py create mode 100644 com/templates/com/news_admin_list.jinja create mode 100644 com/templates/com/news_detail.jinja create mode 100644 com/templates/com/news_edit.jinja create mode 100644 com/templates/com/news_list.jinja diff --git a/com/admin.py b/com/admin.py index 00a75764..b74b7a48 100644 --- a/com/admin.py +++ b/com/admin.py @@ -3,6 +3,7 @@ from django.contrib import admin from com.models import * admin.site.register(Sith) +admin.site.register(News) diff --git a/com/migrations/0002_news_newsdate.py b/com/migrations/0002_news_newsdate.py new file mode 100644 index 00000000..0a57e7db --- /dev/null +++ b/com/migrations/0002_news_newsdate.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('club', '0005_auto_20161120_1149'), + ('com', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='News', + fields=[ + ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')), + ('title', models.CharField(max_length=64, verbose_name='title')), + ('summary', models.TextField(verbose_name='summary')), + ('content', models.TextField(verbose_name='content')), + ('type', models.CharField(max_length=16, choices=[('NOTICE', 'Notice'), ('EVENT', 'Event'), ('WEEKLY', 'Weekly'), ('CALL', 'Call')], default='EVENT', verbose_name='type')), + ('is_moderated', models.BooleanField(default=False, verbose_name='is moderated')), + ('club', models.ForeignKey(to='club.Club', verbose_name='club', related_name='news')), + ('moderator', models.ForeignKey(verbose_name='owner', to=settings.AUTH_USER_MODEL, related_name='moderated_news', null=True)), + ('owner', models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='owner', related_name='owned_news')), + ], + ), + migrations.CreateModel( + name='NewsDate', + fields=[ + ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')), + ('start_date', models.DateTimeField(null=True, blank=True, verbose_name='start_date')), + ('end_date', models.DateTimeField(null=True, blank=True, verbose_name='end_date')), + ('news', models.ForeignKey(to='com.News', verbose_name='news_date', related_name='dates')), + ], + ), + ] diff --git a/com/models.py b/com/models.py index ac8a0b8b..da4d6964 100644 --- a/com/models.py +++ b/com/models.py @@ -1,8 +1,13 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse_lazy, reverse from django.conf import settings +from core.models import User +from club.models import Club + class Sith(models.Model): + """A one instance class storing all the modifiable infos""" alert_msg = models.TextField(_("alert message"), default="", blank=True) info_msg = models.TextField(_("info message"), default="", blank=True) index_page = models.TextField(_("index page"), default="", blank=True) @@ -13,3 +18,40 @@ class Sith(models.Model): def __str__(self): return "⛩ Sith ⛩" +NEWS_TYPES = [ + ('NOTICE', _('Notice')), + ('EVENT', _('Event')), + ('WEEKLY', _('Weekly')), + ('CALL', _('Call')), + ] + +class News(models.Model): + """The news class""" + title = models.CharField(_("title"), max_length=64) + summary = models.TextField(_("summary")) + content = models.TextField(_("content")) + type = models.CharField(_("type"), max_length=16, choices=NEWS_TYPES, default="EVENT") + club = models.ForeignKey(Club, related_name="news", verbose_name=_("club")) + owner = models.ForeignKey(User, related_name="owned_news", verbose_name=_("owner")) + is_moderated = models.BooleanField(_("is moderated"), default=False) + moderator = models.ForeignKey(User, related_name="moderated_news", verbose_name=_("owner"), null=True) + + def get_absolute_url(self): + return reverse('com:news_detail', kwargs={'news_id': self.id}) + + def __str__(self): + return "%s: %s" % (self.type, self.title) + +class NewsDate(models.Model): + """ + A date class, useful for weekly events, or for events that just have no date + + This class allows more flexibilty managing the dates related to a news, particularly when this news is weekly, since + we don't have to make copies + """ + news = models.ForeignKey(News, related_name="dates", verbose_name=_("news_date")) + start_date = models.DateTimeField(_('start_date'), null=True, blank=True) + end_date = models.DateTimeField(_('end_date'), null=True, blank=True) + + def __str__(self): + return "%s: %s - %s" % (self.news.title, self.start_date, self.end_date) diff --git a/com/templates/com/news_admin_list.jinja b/com/templates/com/news_admin_list.jinja new file mode 100644 index 00000000..fd880f2e --- /dev/null +++ b/com/templates/com/news_admin_list.jinja @@ -0,0 +1,25 @@ +{% extends "core/base.jinja" %} + +{% block title %} +{% trans %}News admin{% endtrans %} +{% endblock %} + +{% block content %} +

{% trans %}News{% endtrans %}

+ +{% endblock %} + + + diff --git a/com/templates/com/news_detail.jinja b/com/templates/com/news_detail.jinja new file mode 100644 index 00000000..e29b32df --- /dev/null +++ b/com/templates/com/news_detail.jinja @@ -0,0 +1,15 @@ +{% extends "core/base.jinja" %} + +{% block title %} +{% trans %}News{% endtrans %} - +{{ object.title }} +{% endblock %} + +{% block content %} +

{% trans %}News{% endtrans %}

+ {{ object }} + {{ object.dates.all() }} +{% endblock %} + + + diff --git a/com/templates/com/news_edit.jinja b/com/templates/com/news_edit.jinja new file mode 100644 index 00000000..e117d062 --- /dev/null +++ b/com/templates/com/news_edit.jinja @@ -0,0 +1,59 @@ +{% extends "core/base.jinja" %} + +{% block title %} +{% if object %} +{% trans %}Edit news{% endtrans %} +{% else %} +{% trans %}Create news{% endtrans %} +{% endif %} +{% endblock %} + +{% block content %} +{% if object %} +

{% trans %}Edit news{% endtrans %}

+{% else %} +

{% trans %}Create news{% endtrans %}

+{% endif %} +
+ {% csrf_token %} + {{ form.non_field_errors() }} + {{ form.owner }} +

{{ form.type.errors }} {{ form.type }}

+

{{ form.start_date.errors }} {{ form.start_date }}

+

{{ form.end_date.errors }} {{ form.end_date }}

+

{{ form.until.errors }} {{ form.until }}

+

{{ form.title.errors }} {{ form.title }}

+

{{ form.club.errors }} {{ form.club }}

+

{{ form.summary.errors }} {{ form.summary }}

+

{{ form.content.errors }} {{ form.content }}

+

+
+{% endblock %} + +{% block script %} + {{ super() }} + +{% endblock %} + + diff --git a/com/templates/com/news_list.jinja b/com/templates/com/news_list.jinja new file mode 100644 index 00000000..6f335b9b --- /dev/null +++ b/com/templates/com/news_list.jinja @@ -0,0 +1,86 @@ +{% extends "core/base.jinja" %} + +{% block title %} +{% trans %}News{% endtrans %} +{% endblock %} + +{% block head %} +{{ super() }} + +{% endblock %} + +{% block content %} +

{% trans %}News{% endtrans %}

+
+

Notice

+ {% for news in object_list.filter(type="NOTICE") %} +
+

{{ news.title }}

+

{{ news.summary }}

+
+ {% endfor %} +
+

Calls

+ {% for news in object_list.filter(dates__start_date__lte=timezone.now(), dates__end_date__gte=timezone.now(), type="CALL") %} +
+

{{ news.title }}

+

+ {{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }} + {{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }} - + {{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }} + {{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }} +

+

{{ news.summary }}

+
+ {% endfor %} +
+

Events

+ {% for news in object_list.filter(dates__end_date__gte=timezone.now(), type="EVENT") %} +
+

{{ news.title }}

+

+ {{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }} + {{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }} - + {{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }} + {{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }} +

+

{{ news.club }}

+

{{ news.summary|markdown }}

+
+ {% endfor %} +
+

Weekly

+ {% for news in object_list.filter(dates__end_date__gte=timezone.now(), type="WEEKLY").distinct() %} + + {% for d in news.dates.all() %} +
+

{{ news.title }}

+

+ {{ d.start_date|localtime|date(DATETIME_FORMAT) }} + {{ d.start_date|localtime|time(DATETIME_FORMAT) }} - + {{ d.end_date|localtime|date(DATETIME_FORMAT) }} + {{ d.end_date|localtime|time(DATETIME_FORMAT) }} +

+

{{ news.club }}

+

{{ news.summary|markdown }}

+
+ {% endfor %} + {% endfor %} +{% endblock %} + + + diff --git a/com/urls.py b/com/urls.py index 693a2697..abe10d97 100644 --- a/com/urls.py +++ b/com/urls.py @@ -3,8 +3,13 @@ from django.conf.urls import url, include from com.views import * urlpatterns = [ - url(r'^edit/alert$', AlertMsgEditView.as_view(), name='alert_edit'), - url(r'^edit/info$', InfoMsgEditView.as_view(), name='info_edit'), - url(r'^edit/index$', IndexEditView.as_view(), name='index_edit'), + url(r'^sith/edit/alert$', AlertMsgEditView.as_view(), name='alert_edit'), + url(r'^sith/edit/info$', InfoMsgEditView.as_view(), name='info_edit'), + url(r'^sith/edit/index$', IndexEditView.as_view(), name='index_edit'), + url(r'^news$', NewsListView.as_view(), name='news_list'), + url(r'^news/admin$', NewsAdminListView.as_view(), name='news_admin_list'), + url(r'^news/create$', NewsCreateView.as_view(), name='news_new'), + url(r'^news/(?P[0-9]+)/edit$', NewsEditView.as_view(), name='news_edit'), + url(r'^news/(?P[0-9]+)$', NewsDetailView.as_view(), name='news_detail'), ] diff --git a/com/views.py b/com/views.py index 2209d9ca..88f94b1d 100644 --- a/com/views.py +++ b/com/views.py @@ -1,12 +1,22 @@ from django.shortcuts import render -from django.views.generic.edit import UpdateView +from django.views.generic import ListView, DetailView, RedirectView +from django.views.generic.edit import UpdateView, CreateView from django.utils.translation import ugettext as _ from django.core.urlresolvers import reverse, reverse_lazy +from django.core.exceptions import ValidationError +from django.utils import timezone +from django import forms -from com.models import Sith +from datetime import timedelta + +from com.models import Sith, News, NewsDate from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin +from core.views.forms import SelectDateTime +from club.models import Club +# Sith object + sith = Sith.objects.first class ComTabsMixin(TabedViewMixin): @@ -53,3 +63,87 @@ class IndexEditView(ComEditView): fields = ['index_page'] current_tab = "index" success_url = reverse_lazy('com:index_edit') + +# News + +class NewsForm(forms.ModelForm): + class Meta: + model = News + fields = ['type', 'title', 'club', 'summary', 'content', 'owner'] + widgets = { + 'owner': forms.HiddenInput, + 'type': forms.RadioSelect, + } + start_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Start date"), widget=SelectDateTime, required=False) + end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), widget=SelectDateTime, required=False) + until = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Until"), widget=SelectDateTime, required=False) + + def clean(self): + self.cleaned_data = super(NewsForm, self).clean() + if self.cleaned_data['type'] != "NOTICE": + if not self.cleaned_data['start_date']: + self.add_error('start_date', ValidationError(_("This field is required."))) + if not self.cleaned_data['end_date']: + self.add_error('end_date', ValidationError(_("This field is required."))) + if self.cleaned_data['type'] == "WEEKLY" and not self.cleaned_data['until']: + self.add_error('until', ValidationError(_("This field is required."))) + return self.cleaned_data + + def save(self): + ret = super(NewsForm, self).save() + self.instance.dates.all().delete() + if self.instance.type == "EVENT" or self.instance.type == "CALL": + NewsDate(start_date=self.cleaned_data['start_date'], + end_date=self.cleaned_data['end_date'], + news=self.instance).save() + elif self.instance.type == "WEEKLY": + start_date = self.cleaned_data['start_date'] + end_date = self.cleaned_data['end_date'] + while start_date <= self.cleaned_data['until']: + NewsDate(start_date=start_date, + end_date=end_date, + news=self.instance).save() + start_date += timedelta(days=7) + end_date += timedelta(days=7) + return ret + +class NewsEditView(UpdateView): + model = News + form_class = NewsForm + template_name = 'com/news_edit.jinja' + pk_url_kwarg = 'news_id' + + def get_initial(self): + init = {} + try: + init['start_date'] = self.object.dates.order_by('id').first().start_date.strftime('%Y-%m-%d %H:%M:%S') + except: pass + try: + init['end_date'] = self.object.dates.order_by('id').first().end_date.strftime('%Y-%m-%d %H:%M:%S') + except: pass + return init + +class NewsCreateView(CreateView): + model = News + form_class = NewsForm + template_name = 'com/news_edit.jinja' + + def get_initial(self): + init = {'owner': self.request.user} + try: + init['club'] = Club.objects.filter(id=self.request.GET['club']).first() + except: pass + return init + +class NewsAdminListView(ListView): + model = News + template_name = 'com/news_admin_list.jinja' + +class NewsListView(ListView): + model = News + template_name = 'com/news_list.jinja' + +class NewsDetailView(DetailView): + model = News + template_name = 'com/news_detail.jinja' + pk_url_kwarg = 'news_id'