forked from AFPy/afpy.org
Formulaire d'édition et de création des articles
This commit is contained in:
parent
78fb6875e6
commit
2590a7c995
89
afpy.py
89
afpy.py
|
@ -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 d’emploi',
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
53
templates/edit_post.html
Normal 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 %}
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue
Block a user