Merge pull request #19 from kianby/feature-improvements
Feature improvements
This commit is contained in:
commit
8d663c7ee7
9 changed files with 166 additions and 26 deletions
17
src/stacosys/i18n/messages.py
Normal file
17
src/stacosys/i18n/messages.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
import configparser
|
||||
import os
|
||||
|
||||
|
||||
class Messages:
|
||||
def __init__(self):
|
||||
self.property_dict = {}
|
||||
|
||||
def load_messages(self, lang):
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(os.path.dirname(__file__), 'messages_' + lang + '.properties'))
|
||||
|
||||
for key, value in config.items('messages'):
|
||||
self.property_dict[key] = value
|
||||
|
||||
def get(self, key):
|
||||
return self.property_dict.get(key)
|
6
src/stacosys/i18n/messages_en.properties
Normal file
6
src/stacosys/i18n/messages_en.properties
Normal file
|
@ -0,0 +1,6 @@
|
|||
[messages]
|
||||
login.failure.username=Username or password incorrect
|
||||
logout.flash=You have been logged out.
|
||||
admin.comment.notfound=Comment not found.
|
||||
admin.comment.approved=Comment published.
|
||||
admin.comment.deleted=Comment deleted.
|
6
src/stacosys/i18n/messages_fr.properties
Normal file
6
src/stacosys/i18n/messages_fr.properties
Normal file
|
@ -0,0 +1,6 @@
|
|||
[messages]
|
||||
login.failure.username=Identifiant ou mot de passe incorrect
|
||||
logout.flash=Vous avez été déconnecté.
|
||||
admin.comment.notfound=Commentaire introuvable
|
||||
admin.comment.approved=Commentaire publié
|
||||
admin.comment.deleted=Commentaire supprimé
|
64
src/stacosys/interface/templates/admin_en.html
Normal file
64
src/stacosys/interface/templates/admin_en.html
Normal file
|
@ -0,0 +1,64 @@
|
|||
<!doctype html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Stacosys Comment Moderation</title>
|
||||
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h2>Comment Moderation</h2>
|
||||
<nav>
|
||||
<a href="/web/logout">Log out</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<blockquote>
|
||||
{% for message in messages %}
|
||||
<p>{{ message }}</p>
|
||||
{% endfor %}
|
||||
</blockquote>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Author</th>
|
||||
<th>Comment</th>
|
||||
<th>Article</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for comment in comments %}
|
||||
<tr>
|
||||
<td>{{ comment.created }}</td>
|
||||
<td>{{ comment.author_name }}</td>
|
||||
<td>{{ comment.content }}</td>
|
||||
<td><a href="{{ baseurl + comment.url }}">{{ comment.url }}</a></td>
|
||||
<td>
|
||||
<form action="/web/admin" method="post">
|
||||
<input type="hidden" name="comment" value="{{comment.id}}">
|
||||
<input type="hidden" name="action" value="APPROVE">
|
||||
<button type="submit">Approve</button>
|
||||
</form>
|
||||
<form action="/web/admin" method="post">
|
||||
<input type="hidden" name="comment" value="{{comment.id}}">
|
||||
<input type="hidden" name="action" value="REJECT">
|
||||
<button type="submit">Reject</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
<footer>
|
||||
<p>This page was designed by Yax with <a href="https://simplecss.org">Simple.css</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
42
src/stacosys/interface/templates/login_en.html
Normal file
42
src/stacosys/interface/templates/login_en.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
<!doctype html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Stacosys</title>
|
||||
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
|
||||
<style>
|
||||
form {
|
||||
width: 350px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h2>Comment Moderation Login</h2>
|
||||
</header>
|
||||
<main>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<blockquote>
|
||||
{% for message in messages %}
|
||||
<p>{{ message }}</p>
|
||||
{% endfor %}
|
||||
</blockquote>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<form action="/web/login" method="POST">
|
||||
<p><label>Username:</label></p>
|
||||
<p><input type="text" name="username" /></p>
|
||||
<p><label>Password:</label></p>
|
||||
<p><input type="password" name="password" /></p>
|
||||
<input type="submit" value="Log in" />
|
||||
</form>
|
||||
</main>
|
||||
<footer>
|
||||
<p>This page was designed with <a href="https://simplecss.org">Simple.css</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
|
@ -37,8 +37,7 @@ def login():
|
|||
if is_login_ok(username, password):
|
||||
session["user"] = username
|
||||
return redirect("/web/admin")
|
||||
# TODO localization
|
||||
flash("Identifiant ou mot de passe incorrect")
|
||||
flash(app.config["MESSAGES"].get("login.failure.username"))
|
||||
return redirect("/web/login")
|
||||
# GET
|
||||
return render_template(
|
||||
|
@ -49,6 +48,7 @@ def login():
|
|||
@app.route("/web/logout", methods=["GET"])
|
||||
def logout():
|
||||
session.pop("user")
|
||||
flash(app.config["MESSAGES"].get("logout.flash"))
|
||||
return redirect("/web/admin")
|
||||
|
||||
|
||||
|
@ -58,8 +58,6 @@ def admin_homepage():
|
|||
"user" in session
|
||||
and session["user"] == app.config["CONFIG"].get(ConfigParameter.WEB_USERNAME)
|
||||
):
|
||||
# TODO localization
|
||||
flash("Vous avez été déconnecté.")
|
||||
return redirect("/web/login")
|
||||
|
||||
comments = dao.find_not_published_comments()
|
||||
|
@ -74,15 +72,12 @@ def admin_homepage():
|
|||
def admin_action():
|
||||
comment = dao.find_comment_by_id(request.form.get("comment"))
|
||||
if comment is None:
|
||||
# TODO localization
|
||||
flash("Commentaire introuvable")
|
||||
flash(app.config["MESSAGES"].get("admin.comment.notfound"))
|
||||
elif request.form.get("action") == "APPROVE":
|
||||
dao.publish_comment(comment)
|
||||
app.config["RSS"].generate()
|
||||
# TODO localization
|
||||
flash("Commentaire publié")
|
||||
flash(app.config["MESSAGES"].get("admin.comment.approved"))
|
||||
else:
|
||||
dao.delete_comment(comment)
|
||||
# TODO localization
|
||||
flash("Commentaire supprimé")
|
||||
flash(app.config["MESSAGES"].get("admin.comment.deleted"))
|
||||
return redirect("/web/admin")
|
||||
|
|
|
@ -7,6 +7,7 @@ import os
|
|||
import sys
|
||||
|
||||
from stacosys.db import database
|
||||
from stacosys.i18n.messages import Messages
|
||||
from stacosys.interface import api, app, form
|
||||
from stacosys.interface.web import admin
|
||||
from stacosys.service.configuration import Config, ConfigParameter
|
||||
|
@ -64,6 +65,12 @@ def configure_rss(config):
|
|||
return rss
|
||||
|
||||
|
||||
def configure_localization(config):
|
||||
messages = Messages()
|
||||
messages.load_messages(config.get(ConfigParameter.LANG))
|
||||
return messages
|
||||
|
||||
|
||||
def main(config_pathname):
|
||||
logger = configure_logging()
|
||||
config = load_and_validate_config(config_pathname, logger)
|
||||
|
@ -72,11 +79,13 @@ def main(config_pathname):
|
|||
logger.info("Start Stacosys application")
|
||||
rss = configure_rss(config)
|
||||
mailer = configure_and_validate_mailer(config, logger)
|
||||
messages = configure_localization(config)
|
||||
|
||||
logger.info("start interfaces %s %s %s", api, form, admin)
|
||||
app.config["CONFIG"] = config
|
||||
app.config["MAILER"] = mailer
|
||||
app.config["RSS"] = rss
|
||||
app.config["MESSAGES"] = messages
|
||||
app.run(
|
||||
host=config.get(ConfigParameter.HTTP_HOST),
|
||||
port=config.get_int(ConfigParameter.HTTP_PORT),
|
||||
|
|
|
@ -38,21 +38,22 @@ class Mailer:
|
|||
|
||||
def send(self, subject: str, message: str) -> bool:
|
||||
sender = self._smtp_login
|
||||
receivers = [self._site_admin_email]
|
||||
|
||||
msg = MIMEText(message)
|
||||
msg["Subject"] = subject
|
||||
msg["To"] = self._site_admin_email
|
||||
msg["From"] = sender
|
||||
|
||||
try:
|
||||
msg = MIMEText(message)
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = sender
|
||||
msg["To"] = self._site_admin_email
|
||||
|
||||
with SMTP_SSL(self._smtp_host, self._smtp_port) as server:
|
||||
server.login(self._smtp_login, self._smtp_password)
|
||||
server.send_message(msg, sender, receivers)
|
||||
try:
|
||||
server.login(self._smtp_login, self._smtp_password)
|
||||
except SMTPAuthenticationError:
|
||||
logger.exception("Invalid credentials")
|
||||
return False
|
||||
|
||||
server.send_message(msg)
|
||||
return True
|
||||
except SMTPAuthenticationError:
|
||||
logger.exception("Invalid credentials")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.exception(f"Error sending email: {e}")
|
||||
return False
|
||||
except Exception:
|
||||
logger.error("Error sending email", exc_info=True)
|
||||
return False
|
||||
|
|
|
@ -52,5 +52,5 @@ class Rss:
|
|||
lastBuildDate=datetime.now(),
|
||||
items=items,
|
||||
)
|
||||
# pylint: disable=consider-using-with
|
||||
rss.write_xml(open(self._rss_file, "w", encoding="utf-8"), encoding="utf-8")
|
||||
with open(self._rss_file, "w", encoding="utf-8") as outfile:
|
||||
rss.write_xml(outfile, encoding="utf-8")
|
||||
|
|
Loading…
Add table
Reference in a new issue