2018-10-04 13:34:38 +00:00
|
|
|
|
import email
|
|
|
|
|
from pathlib import Path
|
2020-01-06 22:01:16 +00:00
|
|
|
|
import time
|
2018-10-04 13:34:38 +00:00
|
|
|
|
from xml.etree import ElementTree
|
|
|
|
|
|
2020-01-06 22:01:16 +00:00
|
|
|
|
from werkzeug.utils import secure_filename
|
2018-10-04 13:34:38 +00:00
|
|
|
|
|
|
|
|
|
POST_ACTUALITIES = 'actualites'
|
|
|
|
|
POST_JOBS = 'emplois'
|
|
|
|
|
POSTS = {POST_ACTUALITIES: "Actualités", POST_JOBS: "Offres d’emploi"}
|
|
|
|
|
|
|
|
|
|
STATE_WAITING = 'waiting'
|
|
|
|
|
STATE_PUBLISHED = 'published'
|
2018-10-04 15:48:45 +00:00
|
|
|
|
STATE_TRASHED = 'trashed'
|
2018-10-04 15:53:55 +00:00
|
|
|
|
STATES = {
|
|
|
|
|
STATE_WAITING: "En attente",
|
|
|
|
|
STATE_PUBLISHED: "Publié",
|
|
|
|
|
STATE_TRASHED: "À la corbeille"
|
|
|
|
|
}
|
2018-10-04 13:34:38 +00:00
|
|
|
|
|
|
|
|
|
ACTION_PUBLISH = 'publish'
|
|
|
|
|
ACTION_UNPUBLISH = 'unpublish'
|
2018-10-04 15:53:55 +00:00
|
|
|
|
ACTION_REPUBLISH = 'republish'
|
|
|
|
|
ACTION_TRASH = 'trash'
|
2018-10-05 10:27:46 +00:00
|
|
|
|
ACTION_EDIT = 'edit'
|
2018-10-05 14:04:33 +00:00
|
|
|
|
ACTION_DELETE_IMAGE = 'delete_image'
|
2018-10-04 15:53:55 +00:00
|
|
|
|
ACTIONS = {
|
|
|
|
|
ACTION_PUBLISH: "Publier",
|
|
|
|
|
ACTION_UNPUBLISH: "Dépublier",
|
|
|
|
|
ACTION_REPUBLISH: "Republier",
|
|
|
|
|
ACTION_TRASH: "Supprimer",
|
2018-10-05 10:27:46 +00:00
|
|
|
|
ACTION_EDIT: 'Editer',
|
2018-10-05 14:04:33 +00:00
|
|
|
|
ACTION_DELETE_IMAGE: "Supprimer l'image"
|
2018-10-04 15:53:55 +00:00
|
|
|
|
}
|
2018-10-04 13:34:38 +00:00
|
|
|
|
|
2018-10-04 15:44:18 +00:00
|
|
|
|
IMAGE = '_image'
|
2018-10-04 13:34:38 +00:00
|
|
|
|
TIMESTAMP = '_timestamp'
|
|
|
|
|
STATE = '_state'
|
|
|
|
|
PATH = '_path'
|
|
|
|
|
DIR = '_dir'
|
|
|
|
|
|
|
|
|
|
BASE_DIR = 'posts'
|
|
|
|
|
BASE_FILE = 'post.xml'
|
2018-10-04 15:44:18 +00:00
|
|
|
|
BASE_IMAGE = 'post.jpg'
|
2018-10-04 13:34:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DataException(Exception):
|
|
|
|
|
def __init__(self, *args, http_code=None, **kwargs):
|
|
|
|
|
self.http_code = http_code
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
root = Path(__file__).parent / BASE_DIR
|
|
|
|
|
for category in POSTS:
|
|
|
|
|
for state in STATES:
|
|
|
|
|
(root / category / state).mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_path(category, state, timestamp, *args, create_dir=False):
|
|
|
|
|
path = root / category / state / timestamp
|
|
|
|
|
if create_dir:
|
|
|
|
|
path.mkdir(exist_ok=True)
|
|
|
|
|
for arg in args:
|
|
|
|
|
path /= arg
|
|
|
|
|
return path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def count_posts(category, state=STATE_PUBLISHED):
|
|
|
|
|
return len(tuple((root / category / state).iterdir()))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_posts(category, state=STATE_PUBLISHED, start=0, end=None):
|
|
|
|
|
path = root / category / state
|
|
|
|
|
timestamps = sorted(path.iterdir(), reverse=True)
|
|
|
|
|
timestamps = timestamps[start:end] if end else timestamps[start:]
|
|
|
|
|
for timestamp in timestamps:
|
2020-01-06 21:09:24 +00:00
|
|
|
|
post = get_post(category, timestamp.name, state)
|
|
|
|
|
if post:
|
|
|
|
|
yield post
|
2018-10-04 13:34:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_post(category, timestamp, states=None):
|
|
|
|
|
states = (
|
|
|
|
|
states
|
|
|
|
|
if isinstance(states, (tuple, list))
|
|
|
|
|
else [states]
|
|
|
|
|
if isinstance(states, str)
|
|
|
|
|
else STATES
|
|
|
|
|
)
|
|
|
|
|
for state in states:
|
|
|
|
|
dir = root / category / state / timestamp
|
|
|
|
|
path = dir / BASE_FILE
|
|
|
|
|
if path.is_file():
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
return None
|
|
|
|
|
tree = ElementTree.parse(path)
|
|
|
|
|
post = {item.tag: (item.text or '').strip() for item in tree.iter()}
|
2018-10-04 15:44:18 +00:00
|
|
|
|
|
|
|
|
|
# Calculated fields
|
|
|
|
|
image = post.get('image') or BASE_IMAGE
|
|
|
|
|
if (dir / image).is_file():
|
|
|
|
|
post[IMAGE] = '/'.join((category, state, timestamp, image))
|
|
|
|
|
post[TIMESTAMP] = timestamp
|
2018-10-04 13:34:38 +00:00
|
|
|
|
post[STATE] = state
|
|
|
|
|
post[DIR] = dir
|
|
|
|
|
post[PATH] = path
|
|
|
|
|
return post
|
|
|
|
|
|
|
|
|
|
|
2018-10-04 15:34:58 +00:00
|
|
|
|
def save_post(category, timestamp, admin, form, files):
|
2018-10-04 13:34:38 +00:00
|
|
|
|
if timestamp is None:
|
|
|
|
|
status = STATE_WAITING
|
|
|
|
|
timestamp = str(int(time.time()))
|
|
|
|
|
elif get_path(category, STATE_WAITING, timestamp, BASE_FILE).is_file():
|
|
|
|
|
status = STATE_WAITING
|
|
|
|
|
elif get_path(category, STATE_PUBLISHED, timestamp, BASE_FILE).is_file():
|
|
|
|
|
status = STATE_PUBLISHED
|
2018-10-04 15:53:55 +00:00
|
|
|
|
elif get_path(category, STATE_TRASHED, timestamp, BASE_FILE).is_file():
|
|
|
|
|
status = STATE_TRASHED
|
2018-10-04 13:34:38 +00:00
|
|
|
|
else:
|
|
|
|
|
raise DataException(http_code=404)
|
2018-10-05 10:27:46 +00:00
|
|
|
|
if status == STATE_TRASHED and not admin:
|
2018-10-04 13:34:38 +00:00
|
|
|
|
raise DataException(http_code=401)
|
|
|
|
|
|
|
|
|
|
post = get_path(category, status, timestamp, BASE_FILE, create_dir=True)
|
|
|
|
|
tree = ElementTree.Element('entry')
|
2018-10-04 15:35:16 +00:00
|
|
|
|
|
2018-10-04 13:34:38 +00:00
|
|
|
|
for key, value in form.items():
|
|
|
|
|
if key.startswith('_'):
|
|
|
|
|
continue
|
|
|
|
|
element = ElementTree.SubElement(tree, key)
|
|
|
|
|
element.text = value
|
2018-10-05 10:38:25 +00:00
|
|
|
|
|
2018-10-05 14:44:39 +00:00
|
|
|
|
if '_image_path' in form:
|
2018-10-05 14:36:51 +00:00
|
|
|
|
image_path = root / form['_image_path']
|
2020-02-19 09:11:32 +00:00
|
|
|
|
if ACTION_DELETE_IMAGE in form and image_path.exists():
|
2018-10-05 14:36:51 +00:00
|
|
|
|
image_path.unlink()
|
2020-02-19 09:11:32 +00:00
|
|
|
|
else:
|
|
|
|
|
if 'image' in files and files['image'].filename:
|
|
|
|
|
post_image = files['image']
|
|
|
|
|
filename = secure_filename(post_image.filename)
|
|
|
|
|
post_image.save(str(post.parent / filename))
|
|
|
|
|
element = ElementTree.SubElement(tree, 'image')
|
|
|
|
|
element.text = filename
|
|
|
|
|
elif '_image_path' in form:
|
2018-10-05 14:44:39 +00:00
|
|
|
|
element = ElementTree.SubElement(tree, 'image')
|
|
|
|
|
element.text = image_path.name
|
2018-10-05 14:04:33 +00:00
|
|
|
|
|
2018-10-04 13:34:38 +00:00
|
|
|
|
element = ElementTree.SubElement(tree, STATE_PUBLISHED)
|
|
|
|
|
element.text = email.utils.formatdate(
|
|
|
|
|
int(timestamp) if timestamp else time.time()
|
|
|
|
|
)
|
|
|
|
|
ElementTree.ElementTree(tree).write(post)
|
|
|
|
|
|
2018-10-04 16:16:23 +00:00
|
|
|
|
if ACTION_TRASH in form and status == STATE_PUBLISHED:
|
|
|
|
|
(root / category / STATE_PUBLISHED / timestamp).rename(
|
|
|
|
|
root / category / STATE_TRASHED / timestamp
|
|
|
|
|
)
|
2018-10-05 10:27:46 +00:00
|
|
|
|
if not admin and ACTION_EDIT in form and status == STATE_PUBLISHED:
|
|
|
|
|
(root / category / STATE_PUBLISHED / timestamp).rename(
|
|
|
|
|
root / category / STATE_WAITING / timestamp
|
|
|
|
|
)
|
2018-10-04 16:16:23 +00:00
|
|
|
|
|
2018-10-04 13:34:38 +00:00
|
|
|
|
if admin:
|
|
|
|
|
if ACTION_PUBLISH in form and status == STATE_WAITING:
|
|
|
|
|
(root / category / STATE_WAITING / timestamp).rename(
|
|
|
|
|
root / category / STATE_PUBLISHED / timestamp
|
|
|
|
|
)
|
|
|
|
|
elif ACTION_UNPUBLISH in form and status == STATE_PUBLISHED:
|
|
|
|
|
(root / category / STATE_PUBLISHED / timestamp).rename(
|
|
|
|
|
root / category / STATE_WAITING / timestamp
|
|
|
|
|
)
|
2018-10-04 15:53:55 +00:00
|
|
|
|
elif ACTION_REPUBLISH in form and status == STATE_TRASHED:
|
|
|
|
|
(root / category / STATE_TRASHED / timestamp).rename(
|
|
|
|
|
root / category / STATE_PUBLISHED / timestamp
|
|
|
|
|
)
|
2018-10-04 13:34:38 +00:00
|
|
|
|
|
|
|
|
|
return get_post(category, timestamp)
|