mirror of
https://github.com/ae-utbm/sith.git
synced 2024-12-22 15:51:19 +00:00
Add news system, still miss nices templates and moderation tools
This commit is contained in:
parent
f79ffbee7d
commit
1ca6bf7c62
@ -3,6 +3,7 @@ from django.contrib import admin
|
||||
from com.models import *
|
||||
|
||||
admin.site.register(Sith)
|
||||
admin.site.register(News)
|
||||
|
||||
|
||||
|
||||
|
40
com/migrations/0002_news_newsdate.py
Normal file
40
com/migrations/0002_news_newsdate.py
Normal file
@ -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')),
|
||||
],
|
||||
),
|
||||
]
|
@ -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)
|
||||
|
25
com/templates/com/news_admin_list.jinja
Normal file
25
com/templates/com/news_admin_list.jinja
Normal file
@ -0,0 +1,25 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
|
||||
{% block title %}
|
||||
{% trans %}News admin{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% 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>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
15
com/templates/com/news_detail.jinja
Normal file
15
com/templates/com/news_detail.jinja
Normal file
@ -0,0 +1,15 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
|
||||
{% block title %}
|
||||
{% trans %}News{% endtrans %} -
|
||||
{{ object.title }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{% trans %}News{% endtrans %}</h3>
|
||||
{{ object }}
|
||||
{{ object.dates.all() }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
59
com/templates/com/news_edit.jinja
Normal file
59
com/templates/com/news_edit.jinja
Normal file
@ -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 %}
|
||||
<h2>{% trans %}Edit news{% endtrans %}</h2>
|
||||
{% else %}
|
||||
<h2>{% trans %}Create news{% endtrans %}</h2>
|
||||
{% endif %}
|
||||
<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>
|
||||
<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>
|
||||
<p>{{ form.title.errors }}<label for="{{ form.title.name }}">{{ form.title.label }}</label> {{ form.title }}</p>
|
||||
<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>
|
||||
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
$( function() {
|
||||
var type = $('input[name=type]');
|
||||
var dates = $('.date');
|
||||
var until = $('.until');
|
||||
function update_targets () {
|
||||
type_checked = $('input[name=type]:checked');
|
||||
if (type_checked.val() == "EVENT" || type_checked.val() == "CALL") {
|
||||
dates.show();
|
||||
until.hide();
|
||||
} else if (type_checked.val() == "WEEKLY") {
|
||||
dates.show();
|
||||
until.show();
|
||||
} else {
|
||||
dates.hide();
|
||||
until.hide();
|
||||
}
|
||||
}
|
||||
update_targets();
|
||||
type.change(update_targets);
|
||||
} );
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
86
com/templates/com/news_list.jinja
Normal file
86
com/templates/com/news_list.jinja
Normal file
@ -0,0 +1,86 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
|
||||
{% block title %}
|
||||
{% trans %}News{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<style type="text/css" media="all">
|
||||
section {
|
||||
padding: 5px;
|
||||
}
|
||||
section.news_call {
|
||||
background: lightgrey;
|
||||
}
|
||||
section.news_event:nth-child(even) {
|
||||
background: lightblue;
|
||||
}
|
||||
.date {
|
||||
font-size: small;
|
||||
color: grey;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{% trans %}News{% endtrans %}</h3>
|
||||
<hr>
|
||||
<h4>Notice</h4>
|
||||
{% for news in object_list.filter(type="NOTICE") %}
|
||||
<section class="news_notice">
|
||||
<h4>{{ news.title }}</h4>
|
||||
<p>{{ news.summary }}</p>
|
||||
</section>
|
||||
{% endfor %}
|
||||
<hr>
|
||||
<h4>Calls</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>
|
||||
<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>
|
||||
</section>
|
||||
{% endfor %}
|
||||
<hr>
|
||||
<h4>Events</h4>
|
||||
{% for news in object_list.filter(dates__end_date__gte=timezone.now(), type="EVENT") %}
|
||||
<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>
|
||||
</section>
|
||||
{% endfor %}
|
||||
<hr>
|
||||
<h4>Weekly</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 -->
|
||||
{% for d in news.dates.all() %}
|
||||
<section class="news_weekly">
|
||||
<h4>{{ news.title }}</h4>
|
||||
<p class="date">
|
||||
<span>{{ d.start_date|localtime|date(DATETIME_FORMAT) }}
|
||||
{{ d.start_date|localtime|time(DATETIME_FORMAT) }}</span> -
|
||||
<span>{{ d.end_date|localtime|date(DATETIME_FORMAT) }}
|
||||
{{ d.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 %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
11
com/urls.py
11
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<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'),
|
||||
]
|
||||
|
||||
|
98
com/views.py
98
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'
|
||||
|
Loading…
Reference in New Issue
Block a user