Add news moderation tool

This commit is contained in:
Skia
2016-12-23 18:40:12 +01:00
parent c95e7565e7
commit 1dc1a0a42c
12 changed files with 386 additions and 100 deletions

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('com', '0002_news_newsdate'),
]
operations = [
migrations.RenameField(
model_name='news',
old_name='owner',
new_name='author',
),
migrations.AlterField(
model_name='news',
name='moderator',
field=models.ForeignKey(null=True, to=settings.AUTH_USER_MODEL, related_name='moderated_news', verbose_name='moderator'),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('com', '0003_auto_20161223_1548'),
]
operations = [
migrations.AlterField(
model_name='news',
name='author',
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='owned_news', verbose_name='author'),
),
]

View File

@ -32,9 +32,18 @@ class News(models.Model):
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"))
author = models.ForeignKey(User, related_name="owned_news", verbose_name=_("author"))
is_moderated = models.BooleanField(_("is moderated"), default=False)
moderator = models.ForeignKey(User, related_name="moderated_news", verbose_name=_("owner"), null=True)
moderator = models.ForeignKey(User, related_name="moderated_news", verbose_name=_("moderator"), null=True)
def is_owned_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or user == self.author
def can_be_edited_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
def can_be_viewed_by(self, user):
return self.is_moderated or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
def get_absolute_url(self):
return reverse('com:news_detail', kwargs={'news_id': self.id})

View File

@ -1,4 +1,5 @@
{% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import user_profile_link %}
{% block title %}
{% trans %}News admin{% endtrans %}
@ -6,19 +7,73 @@
{% block content %}
<h3>{% trans %}News{% endtrans %}</h3>
<ul>
{% for news in object_list %}
<li>
<p>{{ news.get_type_display() }} - {{ news.title }}:
<span>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span> -
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
</p>
</li>
{% endfor %}
</ul>
<h4>{% trans %}Displayed news{% endtrans %}</h4>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Moderator{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in object_list.filter(is_moderated=True) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ user_profile_link(news.moderator) }}</td>
<td>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a> </td>
</tr>
{% endfor %}
</tbody>
</table>
<h4>{% trans %}News to moderate{% endtrans %}</h4>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in object_list.filter(is_moderated=False) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -1,4 +1,5 @@
{% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import user_profile_link %}
{% block title %}
{% trans %}News{% endtrans %} -
@ -6,9 +7,30 @@
{% endblock %}
{% block content %}
<p><a href="{{ url('com:news_list') }}">{% trans %}Back to news{% endtrans %}</a></p>
<h3>{% trans %}News{% endtrans %}</h3>
{{ object }}
{{ object.dates.all() }}
{% if user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or user.is_root %}
{% endif %}
<section class="news_event">
<h4>{{ news.title }}</h4>
<p class="date">
<span>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</p>
<p><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></p>
<p>{{ news.summary|markdown }}</p>
<p>{% trans %}Author: {% endtrans %}{{ user_profile_link(news.author) }}</p>
{% if news.moderator %}
<p>{% trans %}Moderator: {% endtrans %}{{ user_profile_link(news.moderator) }}</p>
{% elif user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) %}
<p> <a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a></p>
{% endif %}
{% if user.can_edit(news) %}
<p> <a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit (will be remoderated){% endtrans %}</a></p>
{% endif %}
</section>
{% endblock %}

View File

@ -17,8 +17,15 @@
<form action="" method="post">
{% csrf_token %}
{{ form.non_field_errors() }}
{{ form.owner }}
<p>{{ form.type.errors }}<label for="{{ form.type.name }}">{{ form.type.label }}</label> {{ form.type }}</p>
{{ form.author }}
<p>{{ form.type.errors }}<label for="{{ form.type.name }}">{{ form.type.label }}</label>
<ul>
<li>Notice: Information, election result - no date</li>
<li>Evenement: punctual event, associated with one date</li>
<li>Weekly: recurrent event, associated with many dates (specify the first one, and a deadline)</li>
<li>Call: long time event, associated with a long date (election appliance, ...)</li>
</ul>
{{ form.type }}</p>
<p class="date">{{ form.start_date.errors }}<label for="{{ form.start_date.name }}">{{ form.start_date.label }}</label> {{ form.start_date }}</p>
<p class="date">{{ form.end_date.errors }}<label for="{{ form.end_date.name }}">{{ form.end_date.label }}</label> {{ form.end_date }}</p>
<p class="until">{{ form.until.errors }}<label for="{{ form.until.name }}">{{ form.until.label }}</label> {{ form.until }}</p>
@ -26,6 +33,10 @@
<p>{{ form.club.errors }}<label for="{{ form.club.name }}">{{ form.club.label }}</label> {{ form.club }}</p>
<p>{{ form.summary.errors }}<label for="{{ form.summary.name }}">{{ form.summary.label }}</label> {{ form.summary }}</p>
<p>{{ form.content.errors }}<label for="{{ form.content.name }}">{{ form.content.label }}</label> {{ form.content }}</p>
{% if user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) %}
<p>{{ form.automoderation.errors }}<label for="{{ form.automoderation.name }}">{{ form.automoderation.label }}</label>
{{ form.automoderation }}</p>
{% endif %}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
{% endblock %}

View File

@ -10,10 +10,11 @@
section {
padding: 5px;
}
section.news_call {
section.news_call, section.news_notice {
background: lightgrey;
margin: 2px;
}
section.news_event:nth-child(even) {
section.news_event:nth-of-type(even) {
background: lightblue;
}
.date {
@ -25,50 +26,62 @@ section.news_event:nth-child(even) {
{% block content %}
<h3>{% trans %}News{% endtrans %}</h3>
<hr>
<h4>{% trans %}Notice{% endtrans %}</h4>
{% for news in object_list.filter(type="NOTICE") %}
<section class="news_notice">
<h4>{{ news.title }}</h4>
<p>{{ news.summary }}</p>
<h4> <a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
<p>{{ news.summary|markdown }}</p>
</section>
{% endfor %}
<hr>
<h4>{% trans %}Calls{% endtrans %}</h4>
{% for news in object_list.filter(dates__start_date__lte=timezone.now(), dates__end_date__gte=timezone.now(), type="CALL") %}
<section class="news_call">
<h4>{{ news.title }}</h4>
<h4> <a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
<p class="date">
<span>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</p>
<p>{{ news.summary }}</p>
<p>{{ news.summary|markdown }}</p>
</section>
{% endfor %}
<hr>
<h4>{% trans %}Events{% endtrans %}</h4>
{% for news in object_list.filter(dates__end_date__gte=timezone.now(), type="EVENT") %}
<h4>{% trans %}Events today and the next few days{% endtrans %}</h4>
{% for d in NewsDate.objects.filter(end_date__gte=timezone.now(), start_date__lte=timezone.now()+timedelta(days=5),
news__type="EVENT", news__is_moderated=True).datetimes('start_date', 'day') %}
<h5 class="date">{{ d|localtime|date(DATETIME_FORMAT) }}</h5>
{% for news in object_list.filter(dates__start_date__gte=d, dates__start_date__lte=d+timedelta(days=1),
type="EVENT").exclude(dates__end_date__lt=timezone.now()) %}
<section class="news_event">
<h4>{{ news.title }}</h4>
<h4> <a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
<p class="date">
<span>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
<span>{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</p>
<p><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></p>
<p>{{ news.summary|markdown }}</p>
</section>
{% endfor %}
<hr>
<h4>{% trans %}Coming soon... don't miss!{% endtrans %}</h4>
{% endfor %}
{% for news in object_list.filter(dates__start_date__gte=timezone.now()+timedelta(days=5), type="EVENT") %}
<section>
<h4> <a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a>
<span class="date">{{ 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) }}</span>
</h4>
</section>
{% endfor %}
<!--
<hr>
<h4>{% trans %}Weekly{% endtrans %}</h4>
{% for news in object_list.filter(dates__end_date__gte=timezone.now(), type="WEEKLY").distinct() %}
<!-- buggy when more than one news, anyway, we won't use it this way -->
buggy when more than one news, anyway, we won't use it this way
{% for d in news.dates.all() %}
<section class="news_weekly">
<h4>{{ news.title }}</h4>
<h4> <a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
<p class="date">
<span>{{ d.start_date|localtime|date(DATETIME_FORMAT) }}
{{ d.start_date|localtime|time(DATETIME_FORMAT) }}</span> -
@ -80,6 +93,7 @@ section.news_event:nth-child(even) {
</section>
{% endfor %}
{% endfor %}
-->
{% endblock %}

View File

@ -9,6 +9,7 @@ urlpatterns = [
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<news_id>[0-9]+)/moderate$', NewsModerateView.as_view(), name='news_moderate'),
url(r'^news/(?P<news_id>[0-9]+)/edit$', NewsEditView.as_view(), name='news_edit'),
url(r'^news/(?P<news_id>[0-9]+)$', NewsDetailView.as_view(), name='news_detail'),
]

View File

@ -1,6 +1,7 @@
from django.shortcuts import render
from django.shortcuts import render, redirect
from django.views.generic import ListView, DetailView, RedirectView
from django.views.generic.edit import UpdateView, CreateView
from django.views.generic.detail import SingleObjectMixin
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse, reverse_lazy
from django.core.exceptions import ValidationError
@ -10,7 +11,7 @@ from django import forms
from datetime import timedelta
from com.models import Sith, News, NewsDate
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin
from core.views.forms import SelectDateTime
from club.models import Club
@ -69,14 +70,15 @@ class IndexEditView(ComEditView):
class NewsForm(forms.ModelForm):
class Meta:
model = News
fields = ['type', 'title', 'club', 'summary', 'content', 'owner']
fields = ['type', 'title', 'club', 'summary', 'content', 'author']
widgets = {
'owner': forms.HiddenInput,
'author': 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)
automoderation = forms.BooleanField(label=_("Automoderation"), required=False)
def clean(self):
self.cleaned_data = super(NewsForm, self).clean()
@ -107,7 +109,7 @@ class NewsForm(forms.ModelForm):
end_date += timedelta(days=7)
return ret
class NewsEditView(UpdateView):
class NewsEditView(CanEditMixin, UpdateView):
model = News
form_class = NewsForm
template_name = 'com/news_edit.jinja'
@ -123,27 +125,70 @@ class NewsEditView(UpdateView):
except: pass
return init
class NewsCreateView(CreateView):
def form_valid(self, form):
self.object = form.save()
if form.cleaned_data['automoderation'] and self.request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID):
self.object.moderator = self.request.user
self.object.is_moderated = True
self.object.save()
else:
self.object.is_moderated = False
self.object.save()
return super(NewsEditView, self).form_valid(form)
class NewsCreateView(CanCreateMixin, CreateView):
model = News
form_class = NewsForm
template_name = 'com/news_edit.jinja'
def get_initial(self):
init = {'owner': self.request.user}
init = {'author': self.request.user}
try:
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
except: pass
return init
class NewsAdminListView(ListView):
def form_valid(self, form):
self.object = form.save()
print(form.cleaned_data)
if form.cleaned_data['automoderation'] and self.request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID):
print("GUY")
self.object.moderator = self.request.user
self.object.is_moderated = True
self.object.save()
return super(NewsCreateView, self).form_valid(form)
class NewsModerateView(CanEditMixin, SingleObjectMixin):
model = News
pk_url_kwarg = 'news_id'
def get(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.is_moderated = True
self.object.moderator = request.user
self.object.save()
if 'next' in self.request.GET.keys():
return redirect(self.request.GET['next'])
return redirect('com:news_admin_list')
class NewsAdminListView(CanEditMixin, ListView):
model = News
template_name = 'com/news_admin_list.jinja'
queryset = News.objects.filter(dates__end_date__gte=timezone.now()).distinct().order_by('id')
class NewsListView(ListView):
class NewsListView(CanViewMixin, ListView):
model = News
template_name = 'com/news_list.jinja'
class NewsDetailView(DetailView):
def get_context_data(self, **kwargs):
kwargs = super(NewsListView, self).get_context_data(**kwargs)
kwargs['NewsDate'] = NewsDate
kwargs['timedelta'] = timedelta
return kwargs
class NewsDetailView(CanViewMixin, DetailView):
model = News
template_name = 'com/news_detail.jinja'
pk_url_kwarg = 'news_id'