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):
|
if is_login_ok(username, password):
|
||||||
session["user"] = username
|
session["user"] = username
|
||||||
return redirect("/web/admin")
|
return redirect("/web/admin")
|
||||||
# TODO localization
|
flash(app.config["MESSAGES"].get("login.failure.username"))
|
||||||
flash("Identifiant ou mot de passe incorrect")
|
|
||||||
return redirect("/web/login")
|
return redirect("/web/login")
|
||||||
# GET
|
# GET
|
||||||
return render_template(
|
return render_template(
|
||||||
|
@ -49,6 +48,7 @@ def login():
|
||||||
@app.route("/web/logout", methods=["GET"])
|
@app.route("/web/logout", methods=["GET"])
|
||||||
def logout():
|
def logout():
|
||||||
session.pop("user")
|
session.pop("user")
|
||||||
|
flash(app.config["MESSAGES"].get("logout.flash"))
|
||||||
return redirect("/web/admin")
|
return redirect("/web/admin")
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,8 +58,6 @@ def admin_homepage():
|
||||||
"user" in session
|
"user" in session
|
||||||
and session["user"] == app.config["CONFIG"].get(ConfigParameter.WEB_USERNAME)
|
and session["user"] == app.config["CONFIG"].get(ConfigParameter.WEB_USERNAME)
|
||||||
):
|
):
|
||||||
# TODO localization
|
|
||||||
flash("Vous avez été déconnecté.")
|
|
||||||
return redirect("/web/login")
|
return redirect("/web/login")
|
||||||
|
|
||||||
comments = dao.find_not_published_comments()
|
comments = dao.find_not_published_comments()
|
||||||
|
@ -74,15 +72,12 @@ def admin_homepage():
|
||||||
def admin_action():
|
def admin_action():
|
||||||
comment = dao.find_comment_by_id(request.form.get("comment"))
|
comment = dao.find_comment_by_id(request.form.get("comment"))
|
||||||
if comment is None:
|
if comment is None:
|
||||||
# TODO localization
|
flash(app.config["MESSAGES"].get("admin.comment.notfound"))
|
||||||
flash("Commentaire introuvable")
|
|
||||||
elif request.form.get("action") == "APPROVE":
|
elif request.form.get("action") == "APPROVE":
|
||||||
dao.publish_comment(comment)
|
dao.publish_comment(comment)
|
||||||
app.config["RSS"].generate()
|
app.config["RSS"].generate()
|
||||||
# TODO localization
|
flash(app.config["MESSAGES"].get("admin.comment.approved"))
|
||||||
flash("Commentaire publié")
|
|
||||||
else:
|
else:
|
||||||
dao.delete_comment(comment)
|
dao.delete_comment(comment)
|
||||||
# TODO localization
|
flash(app.config["MESSAGES"].get("admin.comment.deleted"))
|
||||||
flash("Commentaire supprimé")
|
|
||||||
return redirect("/web/admin")
|
return redirect("/web/admin")
|
||||||
|
|
|
@ -7,6 +7,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from stacosys.db import database
|
from stacosys.db import database
|
||||||
|
from stacosys.i18n.messages import Messages
|
||||||
from stacosys.interface import api, app, form
|
from stacosys.interface import api, app, form
|
||||||
from stacosys.interface.web import admin
|
from stacosys.interface.web import admin
|
||||||
from stacosys.service.configuration import Config, ConfigParameter
|
from stacosys.service.configuration import Config, ConfigParameter
|
||||||
|
@ -64,6 +65,12 @@ def configure_rss(config):
|
||||||
return rss
|
return rss
|
||||||
|
|
||||||
|
|
||||||
|
def configure_localization(config):
|
||||||
|
messages = Messages()
|
||||||
|
messages.load_messages(config.get(ConfigParameter.LANG))
|
||||||
|
return messages
|
||||||
|
|
||||||
|
|
||||||
def main(config_pathname):
|
def main(config_pathname):
|
||||||
logger = configure_logging()
|
logger = configure_logging()
|
||||||
config = load_and_validate_config(config_pathname, logger)
|
config = load_and_validate_config(config_pathname, logger)
|
||||||
|
@ -72,11 +79,13 @@ def main(config_pathname):
|
||||||
logger.info("Start Stacosys application")
|
logger.info("Start Stacosys application")
|
||||||
rss = configure_rss(config)
|
rss = configure_rss(config)
|
||||||
mailer = configure_and_validate_mailer(config, logger)
|
mailer = configure_and_validate_mailer(config, logger)
|
||||||
|
messages = configure_localization(config)
|
||||||
|
|
||||||
logger.info("start interfaces %s %s %s", api, form, admin)
|
logger.info("start interfaces %s %s %s", api, form, admin)
|
||||||
app.config["CONFIG"] = config
|
app.config["CONFIG"] = config
|
||||||
app.config["MAILER"] = mailer
|
app.config["MAILER"] = mailer
|
||||||
app.config["RSS"] = rss
|
app.config["RSS"] = rss
|
||||||
|
app.config["MESSAGES"] = messages
|
||||||
app.run(
|
app.run(
|
||||||
host=config.get(ConfigParameter.HTTP_HOST),
|
host=config.get(ConfigParameter.HTTP_HOST),
|
||||||
port=config.get_int(ConfigParameter.HTTP_PORT),
|
port=config.get_int(ConfigParameter.HTTP_PORT),
|
||||||
|
|
|
@ -38,21 +38,22 @@ class Mailer:
|
||||||
|
|
||||||
def send(self, subject: str, message: str) -> bool:
|
def send(self, subject: str, message: str) -> bool:
|
||||||
sender = self._smtp_login
|
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:
|
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:
|
with SMTP_SSL(self._smtp_host, self._smtp_port) as server:
|
||||||
server.login(self._smtp_login, self._smtp_password)
|
try:
|
||||||
server.send_message(msg, sender, receivers)
|
server.login(self._smtp_login, self._smtp_password)
|
||||||
|
except SMTPAuthenticationError:
|
||||||
|
logger.exception("Invalid credentials")
|
||||||
|
return False
|
||||||
|
|
||||||
|
server.send_message(msg)
|
||||||
return True
|
return True
|
||||||
except SMTPAuthenticationError:
|
except Exception:
|
||||||
logger.exception("Invalid credentials")
|
logger.error("Error sending email", exc_info=True)
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
|
||||||
logger.exception(f"Error sending email: {e}")
|
|
||||||
return False
|
|
||||||
|
|
|
@ -52,5 +52,5 @@ class Rss:
|
||||||
lastBuildDate=datetime.now(),
|
lastBuildDate=datetime.now(),
|
||||||
items=items,
|
items=items,
|
||||||
)
|
)
|
||||||
# pylint: disable=consider-using-with
|
with open(self._rss_file, "w", encoding="utf-8") as outfile:
|
||||||
rss.write_xml(open(self._rss_file, "w", encoding="utf-8"), encoding="utf-8")
|
rss.write_xml(outfile, encoding="utf-8")
|
||||||
|
|
Loading…
Add table
Reference in a new issue