Commit cafdb48d authored by cynddl's avatar cynddl

Add edit and preview views for articles

parent 2f289bad
...@@ -12,24 +12,38 @@ STATUS_CHOICES = ( ...@@ -12,24 +12,38 @@ STATUS_CHOICES = (
("REJECTED", _("Rejected")) ("REJECTED", _("Rejected"))
) )
URL_HELP_TEXT = """The URL should not contain any marketing tags. We
automatically strip the most known tags."""
TITLE_HELP_TEXT = """Please remove non-necessary parts such as newspapers'
names and leave only the article title."""
EXTRACTS_HELP_TEXT = """Please select short and helpful extracts from the
article content. You should aim at around 500 characters. Use bracket ellipsis
[…] to cut parts not required to understand the context."""
class Article(VoteMixin): class Article(VoteMixin):
url = models.URLField("URL") url = models.URLField("URL", help_text=URL_HELP_TEXT)
lang = models.CharField(_("Language"), max_length=50, null=True) lang = models.CharField(_("Language"), max_length=50, null=True)
metadata = models.TextField(_("Opengraph metadata"), blank=True, null=True) metadata = models.TextField(
screenshot = models.ImageField(_("Article screenshot"), _("Opengraph metadata"), blank=True, null=True)
blank=True, null=True) screenshot = models.ImageField(
title = models.CharField(_("Article title"), max_length=255, default="") _("Article screenshot"), blank=True, null=True)
title = models.CharField(
_("Article title"), max_length=255, default="",
help_text=TITLE_HELP_TEXT)
website = models.CharField(_("Website"), max_length=255, default="") website = models.CharField(_("Website"), max_length=255, default="")
extracts = models.TextField(_("Content extracts"), blank=True, null=True) extracts = models.TextField(
_("Content extracts"), blank=True, null=True,
help_text=EXTRACTS_HELP_TEXT)
created_at = models.DateTimeField(_("Creation date"), auto_now_add=True) created_at = models.DateTimeField(_("Creation date"), auto_now_add=True)
updated_at = models.DateTimeField(_("Last update"), auto_now=True) updated_at = models.DateTimeField(_("Last update"), auto_now=True)
published_at = models.DateTimeField(_("Publication date"), published_at = models.DateTimeField(
blank=True, null=True) _("Publication date"), blank=True, null=True)
status = models.CharField( status = models.CharField(
_("Status"), choices=STATUS_CHOICES, _("Status"), choices=STATUS_CHOICES, default="PENDING", max_length=20)
default="PENDING", max_length=20)
#: priority: True if article have priority #: priority: True if article have priority
priority = models.BooleanField(default=False) priority = models.BooleanField(default=False)
......
{% extends "base.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% load crispy_forms_field %}
{% block content-banner %}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12 white-bg">
<div class="well">
<div class="d-flex justify-content-start align-items-baseline mb-4">
<p class="lead">
<a class="btn btn-outline-primary" href="{% url 'rp:article-list' filter_view='draft' %}">
<i class="fa fa-chevron-left" aria-hidden="true"></i> Pending articles
</a>
<span class="ml-2"><strong>Article ID #{{object.id}}</strong></span>
</p>
<div class="ml-auto">
<span class="btn-group" role="group" aria-label="Actions">
<a class="btn btn-outline-primary" href="{% url 'rp:article-edit' object.id %}">Éditer</a>
<input type="submit" class="btn btn-outline-warning" name="publish" value="Publier">
</span>
</div>
</div>
<div class="row">
<div class="col-sm-3">
{% if object.screenshot %}
<img class="img-responsive mb-4"
src="/media/{{ object.screenshot }}">
{% endif %}
</div>
<div class="col-sm-9 lead">
<h4>{{object.title}}</h4>
<p>{{article.created_at.date}}
{% for t in article.tags.all %}
<span class="badge badge-default ml-1">{{t}}</span>
{% endfor %}
</p>
<p>{{object.extracts}}</p>
</div>
</div>
</div>
</div>
{% endblock %}
{% extends "base.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% load crispy_forms_field %}
{% block content-banner %}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12 white-bg">
<div class="well">
<form class="" action="#" method="post">
<div class="d-flex justify-content-start align-items-baseline mb-4">
<p class="lead">
<a class="btn btn-outline-primary" href="{{request.META.HTTP_REFERER}}"><i class="fa fa-chevron-left" aria-hidden="true"></i> Go back</a>
<span class="ml-2"><strong>Article ID #{{object.id}}</strong> – proposé par XXX</span></span>
</p>
<div class="ml-auto">
<span>Save and&nbsp;</span>
<span class="btn-group" role="group" aria-label="Actions">
<input type="submit" class="btn btn-outline-primary" name="preview" value="Prévisualiser">
<input type="submit" class="btn btn-outline-warning" name="publish" value="Publier">
</span>
</div>
</div>
{% crispy form %}
</form>
</div>
</div>
{% endblock %}
{% extends "base.html" %} {% extends "base.html" %}
{% block content %}
{% block content-header %}
<div class="row justify-content-left">
<div class="col-md-6 offset-md-1">
<h4>Participez à l'aventure !</h4>
<p>En tant que padawan
<img class="inline-image" role="img" src="{% static 'img/padawan.svg' %}" />,
aidez-nous à sélectionner les prochains articles à intégrer dans la
revue de presse en votant pour les meilleurs. Les jedis
<img class="inline-image" role="img" src="{% static 'img/jedi.svg' %}" />
s'occupent ensuite de la relecture et de la mise en page des articles.
</p>
<p>Faites-vous connaître sur le
<a target="_blank" href="https://www.laquadrature.net/wiki/IRC">salon IRC #lqdn-rp</a>
où il vous sera fait bon accueil, ou en nous
<a target="_blank" href="https://www.laquadrature.net/fr/contact">contactant par email</a>.
</p>
</div>
<div class="col-md-4">
<h4>Comment sélectionner un article ?</h4>
<ol>
<li>Il aborde les sujets sur lesquels La Quadrature est engagée (paquet télécom, ACTA, <i>etc.</i>).</li>
<li>Il reflète ou appuie l'avis de La Quadrature.</li>
<li>Il provient prioritairement de médias importants (presse nationale, régionale ou internationale).</li>
</ol>
</div>
</div>
{% endblock %}
{% block content %}
<div class="row"> <div class="row">
<div class="col-md-12 white-bg"> <div class="col-md-12 white-bg">
<ul class="nav nav-integrated"> <ul class="nav nav-integrated">
...@@ -88,7 +118,7 @@ ...@@ -88,7 +118,7 @@
<i class="fa fa-times" aria-hidden="true"></i> Rejeter <i class="fa fa-times" aria-hidden="true"></i> Rejeter
</li> </li>
{% else %} {% else %}
<li class="actions-item actions-item-edit"><a href="#"> <li class="actions-item actions-item-edit"><a href="{% url 'rp:article-edit' article.id %}">
<i class="fa fa-pencil" aria-hidden="true"></i> Éditer</a> <i class="fa fa-pencil" aria-hidden="true"></i> Éditer</a>
</li> </li>
<li class="actions-item actions-item-publish" onclick="javascript:call_publish({{article.id}})"> <li class="actions-item actions-item-publish" onclick="javascript:call_publish({{article.id}})">
......
from django.conf.urls import url from django.conf.urls import url
from rp.views.articles import ArticleListFlux from rp.views.articles import ArticleListFlux, ArticleEdit, ArticleDetailView
urlpatterns = [ urlpatterns = [
url( url(
...@@ -12,4 +12,19 @@ urlpatterns = [ ...@@ -12,4 +12,19 @@ urlpatterns = [
ArticleListFlux.as_view(), ArticleListFlux.as_view(),
name="article-list" name="article-list"
), ),
url(
r"^article/edit/(?P<pk>\d+)",
ArticleEdit.as_view(),
name="article-edit"
),
url(
r"^article/view/(?P<pk>\d+)",
ArticleDetailView.as_view(),
name="article-view"
),
url(
r"^article/preview/(?P<pk>\d+)",
ArticleDetailView.as_view(preview=True),
name="article-preview"
)
] ]
from django.http import HttpResponseRedirect
from django.views.generic.detail import DetailView
from django.views.generic.edit import UpdateView
from django.urls import reverse
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout, Field, Div, HTML
from crispy_forms.bootstrap import AppendedText
from rp.models import Article from rp.models import Article
from .votes import UDList from .votes import UDList
...@@ -31,3 +42,69 @@ class ArticleListFlux(UDList): ...@@ -31,3 +42,69 @@ class ArticleListFlux(UDList):
"status='PENDING'" "status='PENDING'"
]).count() ]).count()
return context return context
class ArticleDetailView(DetailView):
model = Article
preview = False
def get_context_data(self, **kwargs):
context = super(ArticleDetailView, self).get_context_data(**kwargs)
context['is_preview'] = self.preview
return context
class ArticleEdit(UpdateView):
model = Article
fields = ['screenshot', 'url', 'lang', 'title', 'tags', 'extracts']
success_url = 'article-list'
def form_valid(self, form):
self.object = form.save()
if "preview" in self.request.POST:
self.success_url = reverse("rp:article-preview", args=[self.object.id])
elif "publish" in self.request.POST:
self.object.status = "PUBLISHED"
self.object.save()
self.success_url = reverse("rp:article-list")
return HttpResponseRedirect(self.get_success_url())
def get_form(self, form_class=None):
form = super().get_form(form_class)
form.fields["screenshot"].widget = forms.widgets.FileInput()
form.helper = FormHelper()
form.helper.form_tag = False
left_layout = Div(
Div(
Field('title', wrapper_class='col-sm-10'),
Field('lang', wrapper_class='col-sm-2'),
css_class="row"),
AppendedText('url',
'<a href="%s">Go to link</a>' % form.initial['url']),
'tags',
'extracts',
css_class="col-sm-8")
right_layout = Div(
'screenshot',
HTML(
"""
{% if form.screenshot.value %}
<img class="img-responsive mb-4"
src="/media/{{ form.screenshot.value }}">
{% endif %}
"""
),
css_class="col-sm-4")
form.helper.layout = Layout(
Div(left_layout, right_layout, css_class="row"))
form.helper.add_input(
Submit('submit', 'Update the article', css_class='btn-primary'))
return form
...@@ -20,6 +20,7 @@ CONTRIB_APPS = [ ...@@ -20,6 +20,7 @@ CONTRIB_APPS = [
# https://github.com/philipn/django-rest-framework-filters # https://github.com/philipn/django-rest-framework-filters
# "rest_framework_filters", # "rest_framework_filters",
"taggit", "taggit",
"crispy_forms"
] ]
if DEBUG: if DEBUG:
...@@ -38,3 +39,5 @@ INSTALLED_APPS = DJANGO_APPS + CONTRIB_APPS + PROJECT_APPS ...@@ -38,3 +39,5 @@ INSTALLED_APPS = DJANGO_APPS + CONTRIB_APPS + PROJECT_APPS
# Use username for upvote instead of user object # Use username for upvote instead of user object
UND_USE_USERNAME = True UND_USE_USERNAME = True
CRISPY_TEMPLATE_PACK = "bootstrap4"
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
rp_new URL Configuration rp_new URL Configuration
""" """
from django.conf import settings
from django.conf.urls import url, include from django.conf.urls import url, include
from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
# Django rest framework router # Django rest framework router
...@@ -17,3 +19,10 @@ urlpatterns = [ ...@@ -17,3 +19,10 @@ urlpatterns = [
url(r"^api/", include(router.urls, namespace="api")), url(r"^api/", include(router.urls, namespace="api")),
url(r"^rp/", include("rp.urls", namespace="rp")) url(r"^rp/", include("rp.urls", namespace="rp"))
] ]
if settings.DEBUG:
# Serve static files in DEBUG mode
urlpatterns += static(
settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT
)
...@@ -7,3 +7,4 @@ django-und ...@@ -7,3 +7,4 @@ django-und
Pillow==4.1.0 Pillow==4.1.0
selenium selenium
newspaper3k newspaper3k
django-crispy-forms
...@@ -120,39 +120,16 @@ ...@@ -120,39 +120,16 @@
</div> </div>
<section class="container-fluid light-grey-bg"> <section class="container-fluid light-grey-bg">
<div id="subtitle" class="text-center row justify-content-center"> {% block content-banner %}
<div id="main-banner" class="text-center row justify-content-center">
<div class="page-header col-md-10"> <div class="page-header col-md-10">
<h1><span>Bienvenue sur la revue de presse de La Quadrature</span></h1> <h1><span>Bienvenue sur la revue de presse de La Quadrature</span></h1>
</div> </div>
</div> </div>
{% endblock %}
<div class="row justify-content-left"> {% block content-header %}
<div class="col-md-6 offset-md-1"> {% endblock %}
<h4>Participez à l'aventure !</h4>
<p>En tant que padawan
<img class="inline-image" role="img" src="{% static 'img/padawan.svg' %}" />,
aidez-nous à sélectionner les prochains articles à intégrer dans la
revue de presse en votant pour les meilleurs. Les jedis
<img class="inline-image" role="img" src="{% static 'img/jedi.svg' %}" />
s'occupent ensuite de la relecture et de la mise en page des articles.
</p>
<p>Faites-vous connaître sur le
<a target="_blank" href="https://www.laquadrature.net/wiki/IRC">salon IRC #lqdn-rp</a>
où il vous sera fait bon accueil, ou en nous
<a target="_blank" href="https://www.laquadrature.net/fr/contact">contactant par email</a>.
</p>
</div>
<div class="col-md-4">
<h4>Comment sélectionner un article ?</h4>
<ol>
<li>Il aborde les sujets sur lesquels La Quadrature est engagée (paquet télécom, ACTA, <i>etc.</i>).</li>
<li>Il reflète ou appuie l'avis de La Quadrature.</li>
<li>Il provient prioritairement de médias importants (presse nationale, régionale ou internationale).</li>
</ol>
</div>
</div>
</section> </section>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment