Formulaire d'édition et de création des articles

This commit is contained in:
Guillaume Ayoub 2018-04-30 14:10:54 +02:00
parent 78fb6875e6
commit 2590a7c995
7 changed files with 189 additions and 19 deletions

89
afpy.py
View File

@ -8,7 +8,7 @@ from xml.etree import ElementTree
import docutils.core
import docutils.writers.html5_polyglot
import feedparser
from flask import Flask, abort, redirect, render_template
from flask import Flask, abort, redirect, render_template, request, url_for
from flask_cache import Cache
from jinja2 import TemplateNotFound
@ -41,7 +41,7 @@ MEETUPS = {
POSTS = {
'actualites': 'Actualités',
'emplois': 'Emplois',
'emplois': 'Offres demploi',
}
@ -57,18 +57,23 @@ def page_not_found(e):
@app.route('/')
@app.route('/<name>')
def pages(name='index'):
def index():
posts = {}
path = root / 'actualites' / 'published'
timestamps = sorted(path.iterdir(), reverse=True)[:4]
for timestamp in timestamps:
tree = ElementTree.parse(timestamp / 'post.xml')
posts[timestamp.name] = {item.tag: item.text for item in tree.iter()}
return render_template(
'index.html', body_id='index', name='actualites', posts=posts)
@app.route('/<name>')
def pages(name):
if name == 'index':
path = root / 'actualites' / 'published'
timestamps = sorted(path.iterdir(), reverse=True)[:4]
for timestamp in timestamps:
tree = ElementTree.parse(timestamp / 'post.xml')
posts[timestamp] = {item.tag: item.text for item in tree.iter()}
return redirect(url_for('index'))
try:
return render_template(
f'{name}.html', body_id=name, meetups=MEETUPS, posts=posts)
return render_template(f'{name}.html', body_id=name, meetups=MEETUPS)
except TemplateNotFound:
abort(404)
@ -87,6 +92,63 @@ def rest(name):
'rst.html', body_id=name, html=parts['body'], title=parts['title'])
@app.route('/post/edit/<name>')
@app.route('/post/edit/<name>/<timestamp>')
def edit_post(name, timestamp=None):
if name not in POSTS:
abort(404)
if timestamp is None:
state = 'waiting'
post = {}
else:
for state in ('published', 'waiting'):
if (root / name / state / timestamp / 'post.xml').is_file():
path = (root / name / state / timestamp / 'post.xml')
break
else:
abort(404)
tree = ElementTree.parse(path)
post = {item.tag: (item.text or '').strip() for item in tree.iter()}
return render_template(
'edit_post.html', body_id='edit-post', post=post, name=name,
state=state)
@app.route('/post/edit/<name>', methods=['post'])
@app.route('/post/edit/<name>/<timestamp>', methods=['post'])
def save_post(name, timestamp=None):
if name not in POSTS:
abort(404)
if timestamp is None:
timestamp = str(int(time.time()))
status = 'waiting'
folder = root / name / 'waiting' / timestamp
folder.mkdir()
post = folder / 'post.xml'
elif (root / name / 'waiting' / timestamp / 'post.xml').is_file():
status = 'waiting'
elif (root / name / 'published' / timestamp / 'post.xml').is_file():
status = 'published'
else:
abort(404)
post = root / name / status / timestamp / 'post.xml'
tree = ElementTree.Element('item')
for key in ('title', 'description', 'content', 'email'):
element = ElementTree.SubElement(tree, key)
element.text = request.form[key]
element = ElementTree.SubElement(tree, 'pubDate')
element.text = email.utils.formatdate(
int(timestamp) if timestamp else time.time())
ElementTree.ElementTree(tree).write(post)
if 'publish' in request.form and status == 'waiting':
(root / name / 'waiting' / timestamp).rename(
root / name / 'published' / timestamp)
elif 'unpublish' in request.form and status == 'published':
(root / name / 'published' / timestamp).rename(
root / name / 'waiting' / timestamp)
return redirect('/')
@app.route('/posts/<name>')
def posts(name):
if name not in POSTS:
@ -96,7 +158,7 @@ def posts(name):
posts = {}
for timestamp in timestamps:
tree = ElementTree.parse(timestamp / 'post.xml')
posts[timestamp] = {item.tag: item.text for item in tree.iter()}
posts[timestamp.name] = {item.tag: item.text for item in tree.iter()}
return render_template(
'posts.html', body_id=name, posts=posts, title=POSTS[name], name=name)
@ -144,8 +206,7 @@ def format_datetime(time_struct, format_):
@app.template_filter('rfc822_datetime')
def format_rfc822_datetime(datetime_tuple):
timestamp = time.mktime(datetime_tuple)
return email.utils.formatdate(timestamp)
return email.utils.formatdate(time.mktime(datetime_tuple))
if __name__ == '__main__': # pragma: no cover

View File

@ -16,20 +16,47 @@ a
&:hover
color: lighten($action-secondary, 10%)
label
display: block
margin: 1em 0
max-width: 40em
width: 80%
input, select, textarea
background: $bkg
border: 1px solid
color: $text
display: block
padding: 0.2em
width: 100%
&:focus
border-color: $action-secondary
textarea
height: 5em
input[type="submit"]
background: $action
border: 0
color: #fff
color: $text
cursor: pointer
font-family: 'Hind', sans-serif
outline: transparent
padding: 1em 2em
text-transform: uppercase
transition: background 250ms
width: auto
&:hover
background: lighten($action, 5%)
.nicEdit-panelContain, .nicEdit-pane
color: black
input[type="submit"]
color: black
code
background: $header
border-bottom: 1px solid $action-secondary

View File

@ -10,19 +10,43 @@ a {
a:hover {
color: #ffd738; }
label {
display: block;
margin: 1em 0;
max-width: 40em;
width: 80%; }
label input, label select, label textarea {
background: #25252d;
border: 1px solid;
color: #eaeaea;
display: block;
padding: 0.2em;
width: 100%; }
label input:focus, label select:focus, label textarea:focus {
border-color: #ffcd05; }
textarea {
height: 5em; }
input[type="submit"] {
background: #2e5cfd;
border: 0;
color: #fff;
color: #eaeaea;
cursor: pointer;
font-family: 'Hind', sans-serif;
outline: transparent;
padding: 1em 2em;
text-transform: uppercase;
transition: background 250ms; }
transition: background 250ms;
width: auto; }
input[type="submit"]:hover {
background: #4770fd; }
.nicEdit-panelContain, .nicEdit-pane {
color: black; }
.nicEdit-panelContain input[type="submit"], .nicEdit-pane input[type="submit"] {
color: black; }
code {
background: #1d1e23;
border-bottom: 1px solid #ffcd05;

View File

@ -5,6 +5,7 @@
<link rel="icon" href="{{ url_for('static', filename='images/favicon.ico') }}" />
<title>AFPY - Le site web de l'Association Francophone de Python</title>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/style.sass.css') }}" />
{% block script %}{% endblock script %}
</head>
<body id="{{ body_id }}">
<header>
@ -13,7 +14,7 @@
<nav>
<ul>
<li{% if body_id == 'index' %} class="active"{% endif %}>
<a href="{{ url_for('pages') }}">Accueil</a>
<a href="{{ url_for('index') }}">Accueil</a>
</li>
<li{% if body_id == 'a-propos' %} class="active"{% endif %}>
<a href="{{ url_for('rest', name='a-propos') }}">Qui sommes-nous ?</a>

53
templates/edit_post.html Normal file
View File

@ -0,0 +1,53 @@
{% extends '_layout.jinja2' %}
{% block script %}
<script type="text/javascript" src="http://js.nicedit.com/nicEdit-latest.js"></script>
<script type="text/javascript">
bkLib.onDomLoaded(function() {
new nicEditor({
buttonList : ['fontFormat','bold','italic','underline','strikeThrough','subscript','superscript','link','unlink','xhtml']
}).panelInstance('content');
});
</script>
{% endblock script %}
{% block header %}
<h1>
{% if post %}
Modification d'un article
{% else %}
Création d'un article
{% endif %}
</h1>
{% endblock header %}
{% block main %}
<article>
<form method="post">
<label>Titre
<input name="title" value="{{ post.title }}" />
</label>
<label>Description
<textarea name="description">{{ post.description }}</textarea>
</label>
<label>Contenu de l'article
<textarea name="content" id="content">{{ post.content }}</textarea>
</label>
<label>Adresse e-mail
<input name="email" type="email" value="{{ post.email }}" />
</label>
<input type="submit" value="Enregistrer" />
{% if post %}
{% if state == 'waiting' %}
<input type="submit" name="publish" value="Publier" />
{% else %}
<input type="submit" name="unpublish" value="Dépublier" />
{% endif %}
{% endif %}
</form>
<p>
L'adresse e-mail n'est pas rendue publique, elle est uniquement utilisée
par les modérateurs pour vous contacter si nécessaire.
</p>
</article>
{% endblock main %}

View File

@ -13,7 +13,7 @@
<h2>Adhérer</h2>
<p>
Il est possible de soutenir le développement de l'AFPy en cotisant ou en effectuant un don. Une cotisation = un panda roux sauvé.
Il est possible de soutenir le développement de l'AFPy en cotisant ou en effectuant un don.
</p>
<form action="{{ url_for('pages', name='adhesions') }}">
<input type="submit" value="S'inscrire" />

View File

@ -5,6 +5,10 @@
{% endblock header %}
{% block main %}
<aside>
Vous pouvez <a href="{{ url_for('edit_post', name=name) }}">créer un article</a> qui
sera mis en ligne après acceptation de l'un des modérateurs.
</aside>
{% for timestamp, post in posts.items() %}
<article>
<h2>{{ post.title }}</h2>