SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit 2508fe96 authored by Jean-Sébastien Caux's avatar Jean-Sébastien Caux
Browse files

Work on NewsItems and NewsLetters

parent 6ba55176
No related branches found
No related tags found
No related merge requests found
Showing
with 560 additions and 12 deletions
......@@ -4,7 +4,18 @@ __license__ = "AGPL v3"
from django.contrib import admin
from .models import NewsItem
from .models import NewsLetter, NewsItem, NewsLetterNewsItemsTable
class NewsLetterNewsItemsTableInline(admin.TabularInline):
model = NewsLetterNewsItemsTable
class NewsLetterAdmin(admin.ModelAdmin):
search_fields = ['intro', 'closing']
list_display = ['__str__', 'published']
inlines = [NewsLetterNewsItemsTableInline]
admin.site.register(NewsLetter, NewsLetterAdmin)
class NewsItemAdmin(admin.ModelAdmin):
......
__copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"
from django import forms
from .models import NewsLetter, NewsItem, NewsLetterNewsItemsTable
class NewsLetterForm(forms.ModelForm):
class Meta:
model = NewsLetter
fields = ['date', 'intro', 'closing', 'published']
class NewsItemForm(forms.ModelForm):
class Meta:
model = NewsItem
fields = ['date', 'headline', 'blurb_short', 'blurb',
'image', 'css_class',
'followup_link', 'followup_link_text',
'published', 'on_homepage']
class NewsLetterNewsItemsTableForm(forms.ModelForm):
class Meta:
model = NewsLetterNewsItemsTable
fields = ['newsitem']
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2018-07-06 12:59
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('news', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='NewsLetter',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField()),
('intro', models.TextField()),
('closing', models.TextField()),
('published', models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='NewsLetterNewsItemsTable',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('order', models.PositiveSmallIntegerField()),
],
),
migrations.AddField(
model_name='newsitem',
name='blurb_short',
field=models.TextField(default='', help_text='Short version for use in Newsletter/emails etc'),
),
migrations.AddField(
model_name='newsitem',
name='css_class',
field=models.CharField(blank=True, max_length=256, verbose_name='Additional image CSS class'),
),
migrations.AddField(
model_name='newsitem',
name='image',
field=models.ImageField(blank=True, upload_to='news/newsitems/%Y/'),
),
migrations.AddField(
model_name='newsitem',
name='published',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='newsletternewsitemstable',
name='newsitem',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='news.NewsItem'),
),
migrations.AddField(
model_name='newsletternewsitemstable',
name='newsletter',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='news.NewsLetter'),
),
]
......@@ -8,12 +8,39 @@ from django.db import models
from .managers import NewsManager
class NewsLetter(models.Model):
"""
Container of NewsItems.
Which NewsItems (and their order) are handled via the auxiliary
model NewsLetterNewsItemsTable.
"""
date = models.DateField()
intro = models.TextField()
closing = models.TextField()
published = models.BooleanField(default=False)
def __str__(self):
return 'SciPost Newsletter %s' % self.date.strftime('%Y-%m-%d')
def get_absolute_url(self):
return reverse('news:newsletter_detail',
kwargs={'year': self.date.strftime('%Y'),
'month': self.date.strftime('%m'),
'day': self.date.strftime('%d')})
class NewsItem(models.Model):
date = models.DateField()
headline = models.CharField(max_length=300)
blurb_short = models.TextField(default='',
help_text='Short version for use in Newsletter/emails etc')
blurb = models.TextField()
image = models.ImageField(upload_to='news/newsitems/%Y/', blank=True)
css_class = models.CharField(max_length=256, blank=True,
verbose_name='Additional image CSS class')
followup_link = models.URLField(blank=True)
followup_link_text = models.CharField(max_length=300, blank=True)
published = models.BooleanField(default=False)
on_homepage = models.BooleanField(default=True)
objects = NewsManager()
......@@ -27,3 +54,13 @@ class NewsItem(models.Model):
def get_absolute_url(self):
return reverse('news:news') + '#news_' + str(self.id)
class NewsLetterNewsItemsTable(models.Model):
"""
Carries the specification of which NewsItem sits in which NewsLetter,
and in which order.
"""
newsletter = models.ForeignKey('news.NewsLetter', on_delete=models.CASCADE)
newsitem = models.ForeignKey('news.NewsItem', on_delete=models.CASCADE)
order = models.PositiveSmallIntegerField()
<div class="row">
<div class="col-12">
<div class="card card-grey card-news">
<h1>SciPost Newsletter {{ nl.date|date:'Y-m-d' }}</h1>
<p>{{ nl.intro|safe }}</p>
</div>
{% for nt in nl.newsletternewsitemstable_set.all|dictsort:'order' %}
<div class="card card-grey card-news">
{% include 'news/news_card_content.html' with news=nt.newsitem %}
</div>
{% endfor %}
<div class="card card-grey card-news">
<p>{{ nl.closing|safe }}</p>
</div>
</div>
</div>
<div class="card-body news-item" id="news_{{news.id}}">
<h2 class="card-title">{{news.headline}}</h2>
<div>
<div class="text-muted date">{{news.date|date:'j F Y'}}</div>
<div class="pb-3">{{news.blurb|safe}}</div>
{% if news.image %}
<div class="row">
{% if news.followup_link %}
<a href="{{news.followup_link}}">{{news.followup_link_text}}</a>
{% endif %}
<div class="col-3">
<img class="d-flex mr-3 {{ news.image.css_class }}" src="{{ news.image.url }}" alt="image"/>
</div>
<div class="col-9">
{% endif %}
<div>
<h2 class="card-title">{{news.headline}}</h2>
<div class="text-muted date">{{news.date|date:'j F Y'}}</div>
<div class="pb-3">{{news.blurb|safe}}</div>
{% if news.followup_link %}
<a href="{{news.followup_link}}">{{news.followup_link_text}}</a>
{% endif %}
</div>
{% if news.image %}
</div>
</div>
{% endif %}
</div>
{% extends 'scipost/base.html' %}
{% load bootstrap %}
{% block pagetitle %}: News Management{% endblock pagetitle %}
{% block content %}
<div class="row">
<div class="col-12">
<h1>NewsLetters</h1>
<a href="{% url 'news:newsletter_create' %}">Add a NewsLetter</a>
<br/>
<ul>
{% for nl in newsletters %}
<li>
<div><a href="{{ nl.get_absolute_url }}">{{ nl }}</a>&nbsp;<strong>Actions:</strong>&nbsp;<a href="{% url 'news:newsletter_update' pk=nl.id %}">Update</a>&nbsp;<a href="{% url 'news:newsletter_delete' pk=nl.id %}">Delete</a>
<h3>Add a News Item to this Newsletter:</h3>
<form class="d-block mt-2 mb-3" action="{% url 'news:add_newsitem_to_newsletter' nlpk=nl.id %}" method="post">
{% csrf_token %}
{{ add_ni_to_nl_form|bootstrap }}
<input type="submit" name="submit" value="Add" class="btn btn-outline-secondary">
</form>
</div>
<ul>
{% for nt in nl.newsletternewsitemstable_set.all %}
<li>{{ nt.newsitem }}</li>
{% empty %}
<li>No associated NewsItems found</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="row">
<div class="col-12">
<h1>NewsItems</h1>
<a href="{% url 'news:newsitem_create' %}">Add a NewsItem</a>
<br/>
<ul>
{% for ni in newsitems %}
<li>{{ ni }} <a href="{% url 'news:newsitem_update' pk=ni.id %}">Update</a>&nbsp;<a href="{% url 'news:newsitem_delete' pk=ni.id %}">Delete</a></li>
{% endfor %}
</ul>
</div>
</div>
{% endblock content %}
{% extends 'scipost/base.html' %}
{% load bootstrap %}
{% block pagetitle %}: Delete NewsItem{% endblock pagetitle %}
{% block content %}
<div class="row">
<div class="col-12">
<h1 class="highlight">Delete NewsItem</h1>
{{ object }}
</div>
</div>
<div class="row">
<div class="col-12">
<form method="post">
{% csrf_token %}
<h3 class="mb-2">Are you sure you want to delete this NewsItem?</h3>
<input type="submit" class="btn btn-danger" value="Yes, delete it" />
</form>
</ul>
</div>
</div>
{% endblock content %}
{% extends 'scipost/base.html' %}
{% load bootstrap %}
{% block pagetitle %}: NewsItems{% endblock pagetitle %}
{% block content %}
<div class="row">
<div class="col-12">
<form action="{% url 'news:newsitem_create' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" value="Submit" class="btn btn-primary">
</div>
</div>
{% endblock content %}
......@@ -9,7 +9,10 @@
<div class="row">
<div class="col-12">
<h1 class="highlight">SciPost News</h1>
<h1 class="highlight">SciPost News</h1>
{% if perms.scipost.can_manage_news %}
Go to the <a href="{% url 'news:manage' %}">News management page</a>
{% endif %}
</div>
</div>
......@@ -29,9 +32,19 @@
{% endif %}
{% for item in object_list %}
<div class="card card-grey card-news">
{% if item.image %}
<div class="row">
<div class="col-3">
<img class="d-flex mr-3 {{ item.image.css_class }}" src="{{ item.image.url }}" alt="image"/>
</div>
<div class="col-9">
{% endif %}
<div class="card card-grey card-news">
{% include 'news/news_card_content.html' with news=item %}
</div>
</div>
{% if item.image %}
</div>
{% endif %}
{% empty %}
<div>No news found.</div>
{% endfor %}
......
{% extends 'scipost/base.html' %}
{% load bootstrap %}
{% block pagetitle %}: NewsItems{% endblock pagetitle %}
{% block breadcrumb_items %}
{{ block.super }}
<a href="{% url 'news:news' %}" class="breadcrumb-item">News</a>
<a href="{% url 'news:manage' %}" class="breadcrumb-item">Manage</a>
<span class="breadcrumb-item">Update NewsItem</span>
{% endblock breadcrumb_items %}
{% block content %}
<div class="row">
<div class="col-12">
<h1>News Item to update:</h1>
<div class="card card-grey card-news">
{% include 'news/news_card_content.html' with news=object %}
</div>
</div>
<hr/>
<div class="row">
<div class="col-12">
<h1>Edit it here:</h1>
<form action="{% url 'news:newsitem_update' pk=object.id %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" value="Submit" class="btn btn-primary">
</div>
</div>
{% endblock content %}
{% extends 'scipost/base.html' %}
{% load bootstrap %}
{% block pagetitle %}: Delete NewsLetter{% endblock pagetitle %}
{% block content %}
<div class="row">
<div class="col-12">
<h1 class="highlight">Delete NewsLetter</h1>
{{ object }}
</div>
</div>
<div class="row">
<div class="col-12">
<form method="post">
{% csrf_token %}
<h3 class="mb-2">Are you sure you want to delete this NewsLetter?</h3>
<input type="submit" class="btn btn-danger" value="Yes, delete it" />
</form>
</ul>
</div>
</div>
{% endblock content %}
{% extends 'scipost/base.html' %}
{% load bootstrap %}
{% block pagetitle %}: NewsLetters{% endblock pagetitle %}
{% block content %}
<div class="row">
<div class="col-12">
<form action="{% url 'news:newsletter_create' %}" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" value="Submit" class="btn btn-primary">
</div>
</div>
{% endblock content %}
{% extends 'scipost/base.html' %}
{% load bootstrap %}
{% block pagetitle %}: NewsLetter{% endblock pagetitle %}
{% block content %}
<div class="row">
<div class="col-12">
{% include 'news/_newsletter_contents.html' with nl=nl %}
</div>
</div>
{% endblock content %}
{% extends 'scipost/base.html' %}
{% load bootstrap %}
{% block pagetitle %}: NewsLetters{% endblock pagetitle %}
{% block breadcrumb_items %}
{{ block.super }}
<a href="{% url 'news:news' %}" class="breadcrumb-item">News</a>
<a href="{% url 'news:manage' %}" class="breadcrumb-item">Manage</a>
<span class="breadcrumb-item">Update NewsLetter</span>
{% endblock breadcrumb_items %}
{% block content %}
<div class="row">
<div class="col-12">
<h1>Newsletter to update:</h1>
{{ object }}
</div>
</div>
<hr/>
<div class="row">
<div class="col-12">
<h1>Edit it here:</h1>
<form action="{% url 'news:newsletter_update' pk=object.id %}" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" value="Submit" class="btn btn-primary">
</div>
</div>
{% endblock content %}
......@@ -7,5 +7,32 @@ from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^manage/$',
views.NewsManageView.as_view(),
name='manage'),
url(r'^newsletter/(?P<year>[0-9]{4,})-(?P<month>[0-9]{2,})-(?P<day>[0-9]{2,})/$',
views.NewsLetterView.as_view(),
name='newsletter_detail'),
url(r'^newsletter/add/$',
views.NewsLetterCreateView.as_view(),
name='newsletter_create'),
url(r'^newsletter/(?P<pk>[0-9]+)/update/$',
views.NewsLetterUpdateView.as_view(),
name='newsletter_update'),
url(r'^newsletter/(?P<pk>[0-9]+)/delete/$',
views.NewsLetterDeleteView.as_view(),
name='newsletter_delete'),
url(r'^newsitem/add/$',
views.NewsItemCreateView.as_view(),
name='newsitem_create'),
url(r'^newsitem/(?P<pk>[0-9]+)/update/$',
views.NewsItemUpdateView.as_view(),
name='newsitem_update'),
url(r'^newsitem/(?P<pk>[0-9]+)/delete/$',
views.NewsItemDeleteView.as_view(),
name='newsitem_delete'),
url(r'^add_newsitem_to_newsletter/(?P<nlpk>[0-9]+)/$',
views.NewsLetterNewsItemsTableCreateView.as_view(),
name='add_newsitem_to_newsletter'),
url(r'^$', views.NewsListView.as_view(), name='news'),
]
......@@ -2,9 +2,124 @@ __copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"
from django.contrib import messages
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import get_object_or_404
from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from .models import NewsItem
from .models import NewsLetter, NewsItem
from .forms import NewsLetterForm, NewsItemForm, NewsLetterNewsItemsTableForm
from scipost.mixins import PermissionsMixin
class NewsManageView(PermissionsMixin, TemplateView):
"""
General management of News.
"""
permission_required = 'scipost.can_manage_news'
template_name = 'news/news_manage.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['newsletters'] = NewsLetter.objects.all()
context['newsitems'] = NewsItem.objects.all()
context['add_ni_to_nl_form'] = NewsLetterNewsItemsTableForm()
return context
class NewsLetterView(TemplateView):
"""
Newsletter, for public consumption online.
"""
template_name = 'news/newsletter_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['nl'] = get_object_or_404(NewsLetter,
date__year=self.kwargs['year'],
date__month=self.kwargs['month'],
date__day=self.kwargs['day'])
return context
class NewsLetterCreateView(PermissionsMixin, CreateView):
"""
Create a NewsLetter.
"""
permission_required = 'scipost.can_manage_news'
form_class = NewsLetterForm
template_name = 'news/newsletter_create.html'
success_url = reverse_lazy('news:manage')
class NewsLetterUpdateView(PermissionsMixin, UpdateView):
"""
Update a NewsLetter.
"""
permission_required = 'scipost.can_manage_news'
model = NewsLetter
form_class = NewsLetterForm
template_name = 'news/newsletter_update.html'
success_url = reverse_lazy('news:news')
class NewsLetterDeleteView(PermissionsMixin, DeleteView):
"""
Delete a NewsLetter.
"""
permission_required = 'scipost.can_manage_news'
model = NewsLetter
success_url = reverse_lazy('news:news')
class NewsItemCreateView(PermissionsMixin, CreateView):
"""
Create a NewsItem.
"""
permission_required = 'scipost.can_manage_news'
form_class = NewsItemForm
template_name = 'news/newsitem_create.html'
success_url = reverse_lazy('news:news')
class NewsItemUpdateView(PermissionsMixin, UpdateView):
"""
Update a NewsItem.
"""
permission_required = 'scipost.can_manage_news'
model = NewsItem
form_class = NewsItemForm
template_name = 'news/newsitem_update.html'
success_url = reverse_lazy('news:news')
class NewsItemDeleteView(PermissionsMixin, DeleteView):
"""
Delete a NewsItem.
"""
permission_required = 'scipost.can_manage_news'
model = NewsItem
success_url = reverse_lazy('news:news')
class NewsLetterNewsItemsTableCreateView(PermissionsMixin, CreateView):
"""
Add a NewsItem to a NewsLetter.
"""
permission_required = 'scipost.can_manage_news'
form_class = NewsLetterNewsItemsTableForm
success_url = reverse_lazy('news:manage')
def form_valid(self, form):
nl = get_object_or_404(NewsLetter, id=self.kwargs['nlpk'])
form.instance.newsletter = nl
form.instance.order = nl.newsletternewsitemstable_set.all().count() + 1
messages.success(self.request, 'Successfully added News Item to Newsletter')
return super().form_valid(form)
class NewsListView(ListView):
......
......@@ -28,6 +28,7 @@ class Command(BaseCommand):
name='Registered Contributors')
Developers, created = Group.objects.get_or_create(name='Developers')
Testers, created = Group.objects.get_or_create(name='Testers')
NewsAdmin, created = Group.objects.get_or_create(name='News Administrators')
Ambassadors, created = Group.objects.get_or_create(name='Ambassadors')
JuniorAmbassadors, created = Group.objects.get_or_create(name='Junior Ambassadors')
ProductionSupervisors, created = Group.objects.get_or_create(name='Production Supervisor')
......@@ -280,6 +281,12 @@ class Command(BaseCommand):
name='Can manage affiliations',
content_type=content_type)
# News administration
can_manage_news, created = Permission.objects.get_or_create(
codename='can_manage_news',
name='Can manage News',
content_type=content_type)
# Mailchimp
can_manage_mailchimp, created = Permission.objects.get_or_create(
codename='can_manage_mailchimp',
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment