* Added class for internationalization to the project
* Added English and German language files
* Moved all strings to the language files
* Translated to German
* Added context processor to inject language file into every template
projects
Domeniko Gentner 3 years ago
parent ad8f68cdd8
commit 1e3ce054a9
  1. 50
      i18n/de-DE.json
  2. 49
      i18n/en-US.json
  3. 6
      labertasche/__init__.py
  4. 47
      labertasche/language/__init__.py
  5. 20
      server.py
  6. 55
      templates/base.html
  7. 16
      templates/login.html
  8. 47
      templates/manage-comments.html
  9. 15
      templates/manage-mail.html
  10. 51
      templates/project-list.html
  11. 68
      templates/project-stats.html

@ -0,0 +1,50 @@
{
"html_language": "de",
"browser_language": "de-DE",
"ok": "ok",
"cancel": "abbrechen",
"dashboard": "dashboard",
"username": "benutzername",
"password": "passwort",
"login": "login",
"logout": "abmelden",
"comments": "kommentare",
"unpublished": "unveröffentlicht",
"published": "veröffentlicht",
"spam": "spam",
"edit": "editieren",
"manage": "verwalten",
"export": "export",
"delete": "löschen",
"new": "neu",
"project": "projekt",
"new_project": "neues projekt",
"statistics": "statistiken",
"address": "adresse",
"status": "status",
"manage_mail": "Mail Addressen verwalten",
"manage_comments": "Kommentare verwalten",
"manage_comments_delete_comment": "Diesen Kommentar löschen",
"manage_comments_delete_and_block": "Diesen Kommentar löschen und EMail Adresse blocken.",
"manage_comments_allow_comment": "Diesen Kommentar erlauben, aber Email Adresse nicht freischalten.",
"manage_comments_allow_and_approve": "Diesen Kommentar erlauben und Email Adresse freischalten.",
"manage_spam": "Spam verwalten",
"select_article": "Artikel auswählen",
"hooray_no_spam": "Hurra, kein Spam!",
"spam_score": "Spamerkennung",
"tooltip_spam_score": "Je höher der Spam Wert ist, desto höher die Chance, dass es sich um Spam handelt.",
"stats_label_regular_comments": "reguläre Kommentare",
"stats_label_unpublished_comments": "unveröffentlichte Kommentare",
"stats_last_7_days": "Aktivität der letzten 7 Tage",
"stats_total_percentage": "Verhältnis von Kommentaren zu Spam",
"select_project_to_manage": "Projekte:",
"tooltip_create_new_project": "Neues Projekt erzeugen",
"tooltip_delete_project": "Projekt und dazugehörige Daten löschen",
"tooltip_edit_project": "Einstellungen und Daten des Projekts ändern",
"tooltip_export_all_comments": "Alle Kommentare nach Hugo exportieren.<br>Wird normalerweise nicht benötigt.",
"tooltip_manage_this_project": "Dieses Projekt verwalten",
"placeholder_search_mail": "Mail Adressen durchsuchen",
"tooltip_email_blocked": "Email ist momentan gesperrt. Zum entsperren klicken.",
"tooltip_email_allowed": "Email darf momentan ohne Bestätigung posten. Zum Sperren klicken.",
"tooltip_delete_email": "Eintrag löschen. Email unterliegt wieder den normalen Regeln."
}

@ -0,0 +1,49 @@
{
"html_language": "en",
"browser_language": "en-US",
"ok": "ok",
"cancel": "cancel",
"dashboard": "dashboard",
"username": "username",
"password": "password",
"login": "login",
"logout": "logout",
"comments": "comments",
"unpublished": "unpublished",
"published": "published",
"spam": "spam",
"edit": "edit",
"manage": "manage",
"export": "export",
"delete": "delete",
"new": "new",
"project": "project",
"new_project": "new project",
"statistics": "statistics",
"address": "address",
"status": "status",
"manage_mail": "manage mail addresses",
"stats_label_regular_comments": "regular comments",
"stats_label_unpublished_comments": "unpublished comments",
"stats_last_7_days": "Activity last 7 days",
"stats_total_percentage": "Total comment - spam ratio",
"manage_comments": "manage comments",
"manage_comments_delete_comment": "Delete this comment",
"manage_comments_delete_and_block": "Delete this comment and block mail address",
"manage_comments_allow_comment": "Approve this comment, don't approve mail",
"manage_comments_allow_and_approve": "Approve this comment and approve mail",
"select_article": "Select article",
"select_project_to_manage": "Projects",
"hooray_no_spam": "Hooray, no Spam!",
"spam_score": "score",
"tooltip_spam_score": "The higher the spam score is, the more likely it is spam",
"tooltip_create_new_project": "Create a new project",
"tooltip_delete_project": "Delete the project and all of its content",
"tooltip_edit_project": "Edit the name of the project and it's properties",
"tooltip_export_all_comments": "Export all comments to Hugo.<br>This is normally not needed.",
"tooltip_manage_this_project": "Manage this project",
"placeholder_search_mail": "Search mail",
"tooltip_email_blocked": "Email is currently blocked. Click to unblock.",
"tooltip_email_allowed": "Email is currently excempt from spam detection. Click to block.",
"tooltip_delete_email": "Delete entry, Email has to follow the regular rules."
}

@ -13,7 +13,8 @@ from labertasche import (
blueprints,
helper,
mail,
settings
settings,
language
)
_all_ = [
@ -22,5 +23,6 @@ _all_ = [
blueprints,
helper,
mail,
settings
settings,
language
]

@ -0,0 +1,47 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# /**********************************************************************************
# * _author : Domeniko Gentner
# * _mail : code@tuxstash.de
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
# * _license : This project is under MIT License
# *********************************************************************************/
from flask import Request
from pathlib import Path
from json import load
class Language:
def __init__(self, request: Request):
# Define data
self.i18n = dict()
self.languages = list()
# Directory where translations live
i18n_dir = Path('./i18n').absolute()
# Looks for translations
for filename in i18n_dir.glob("*.json"):
if filename.is_file():
self.languages.append(filename.stem)
# Check the browser language in the headers
self.browser_language = request.accept_languages.best_match(self.languages, default="en-US")
# Try to Load language accepted by browser
try:
file = i18n_dir / self.browser_language
with file.with_suffix(".json").absolute().open('r', encoding='utf-8') as fp:
foreign = load(fp)
except FileNotFoundError:
pass
# Always load english
file = i18n_dir / "en-US"
with file.with_suffix(".json").absolute().open('r', encoding='utf-8') as fp:
self.i18n = load(fp)
# Merge dicts, so missing keys are replaced with English
self.i18n.update(**foreign)

@ -9,11 +9,12 @@
# noinspection PyProtectedMember
from sqlalchemy.engine import Engine
from logging import getLogger, ERROR as LOGGING_ERROR
from flask import Flask, redirect, url_for
from flask import Flask, redirect, url_for, request
from flask_cors import CORS
from sqlalchemy import event, inspect
from labertasche.settings import Settings, Secret
from labertasche.database import labertasche_db
from labertasche.language import Language
from labertasche.blueprints import bp_comments, bp_login, bp_dashboard, bp_jsconnector, bp_dbupgrades
from labertasche.helper import User
from flask_login import LoginManager
@ -36,15 +37,13 @@ laberflask.config.update(dict(
SECRET_KEY=secret.key,
TEMPLATES_AUTO_RELOAD=settings.debug,
SQLALCHEMY_DATABASE_URI=settings.database_uri,
SQLALCHEMY_TRACK_MODIFICATIONS=False
SQLALCHEMY_TRACK_MODIFICATIONS=False,
JSON_AS_ASCII=False
))
# Mark secret for deletion
del secret
# CORS
cors = CORS(laberflask)
# Import blueprints
laberflask.register_blueprint(bp_comments)
laberflask.register_blueprint(bp_dashboard)
@ -70,6 +69,10 @@ with laberflask.app_context():
labertasche_db.create_all()
# CORS
cors = CORS(laberflask)
# There is only one user
@loginmgr.user_loader
def user_loader(user_id):
@ -92,3 +95,10 @@ def set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA journal_mode=WAL;")
cursor.close()
# Inject i18n dictionaries into all templates
@laberflask.context_processor
def inject_language():
lang = Language(request)
return {"i18n": lang.i18n}

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{{ i18n['html_language'] }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes">
@ -8,33 +8,47 @@
<link rel="stylesheet" href="/static/css/materialdesignicons.min.css" media="screen">
<link rel="stylesheet" href="/static/css/Chart.min.css" media="screen">
<title>labertasche Dashboard</title>
<title>Labertasche&nbsp;{{ i18n['dashboard'] | capitalize }}</title>
</head>
<body class="is-family-sans-serif">
<body class="is-family-sans-serif" data-language="{{ i18n['browser_language'] }}">
<nav class="navbar" role="navigation" aria-label="main navigation">
<a class="navbar-item is-size-4" href="/">
<span class="icon"><span class="mdi mdi-24px mdi-chart-bar"></span></span>
&nbsp;Labertasche Dashboard
<span class="icon"><span class="mdi mdi-24px mdi-home-group"></span></span>
<span class="is-capitalized">&nbsp;Labertasche&nbsp;{{ i18n['dashboard'] }}</span>
</a>
<div class="navbar-start"></div>
<div class="navbar-start">
</div>
<div class="navbar-end">
{% if project is defined %}
<a class="navbar-item" href="/dashboard/{{ project }}/manage-spam/">
<span class="icon"><i class="mdi mdi-24px mdi-space-invaders"></i></span>
&nbsp;MANAGE SPAM
</a>
<a class="navbar-item" href="/dashboard/{{ project }}/manage-comments/">
<span class="icon"><i class="mdi mdi-24px mdi-comment"></i></span>
&nbsp;MANAGE COMMENTS
</a>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link is-uppercase">
<span class="icon"><i class="mdi mdi-24px mdi-comment-edit-outline"></i></span>
<span class="is-uppercase">&nbsp;{{ project }}</span>
</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="/dashboard/{{ project }}/">
<span class="icon"><i class="mdi mdi-24px mdi-chart-box-outline"></i></span>
<span class="is-uppercase">&nbsp;{{ i18n['statistics'] }}</span>
</a>
<a class="navbar-item" href="/dashboard/{{ project }}/manage-comments/">
<span class="icon"><i class="mdi mdi-24px mdi-comment-check-outline"></i></span>
<span class="is-uppercase">&nbsp;{{ i18n['comments'] }}</span>
</a>
<a class="navbar-item" href="/dashboard/{{ project }}/manage-spam/">
<span class="icon"><i class="mdi mdi-24px mdi-comment-alert-outline"></i></span>
<span class="is-uppercase">&nbsp;{{ i18n['spam'] }}</span>
</a>
</div>
</div>
{% endif %}
<a class="navbar-item" href="/dashboard/{{ project }}/manage-mail">
<span class="icon"><i class="mdi mdi-24px mdi-email"></i></span>
&nbsp;MANAGE EMAIL ADDRESSES
<span class="icon"><i class="mdi mdi-24px mdi-email-outline"></i></span>
<span class="is-uppercase">&nbsp;{{ i18n['manage_mail'] }}</span>
</a>
{% endif %}
<!--suppress HtmlUnknownTarget -->
<a class="navbar-item" href="/logout/">
<span class="icon"><i class="mdi mdi-24px mdi-account-cancel"></i></span>
&nbsp;LOGOUT
<span class="is-uppercase">&nbsp;{{ i18n['logout'] }}</span>
</a>
</div>
</nav>
@ -55,6 +69,11 @@
if (urlParams.get("error") === "404"){
show_modal('modal-project-not-found');
}
tippy('[data-tippy-content]', {
allowHTML: true,
delay: 500
});
{% block javascript %}
{% endblock %}
});

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{{ i18n.browser_language }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes">
@ -7,29 +7,28 @@
<link rel="stylesheet" href="/static/css/labertasche.css" media="screen">
<link rel="stylesheet" href="/static/css/materialdesignicons.min.css" media="screen">
<link rel="stylesheet" href="/static/css/Chart.min.css" media="screen">
<title>labertasche Dashboard</title>
<title>Labertasche {{ i18n['dashboard'] | capitalize }}</title>
</head>
<body>
<section class="hero bg-yayellow is-fullheight">
<div class="hero-head">
<a target="_blank" href="https://github.com/domeniko-gentner/labertasche" rel="noopener nofollow noreferrer"
class="button is-info is-inverted is-medium">
class="button is-info is-inverted is-medium ml-1 mt-1 my-shadow-subtle">
<span class="icon mr-2">
<i class="mdi mdi-36px mdi-github"></i>
</span>
<span>Download</span>
<span>Github</span>
</a>
</div>
<div class="hero-body has-text-black">
<div class="container has-text-centered">
<p class="title">Labertasche Login</p>
<p class="title">Labertasche {{ i18n['login'] | capitalize }}</p>
<!--suppress HtmlUnknownTarget -->
<form method="POST" action="/login">
<div class="field">
<div class="control is-expanded has-icons-left">
<label for="username" class="help has-text-left has-text-black">
<input class="input" name="username" type="text" placeholder="username">
<input class="input" name="username" type="text" placeholder="{{ i18n['username'] | capitalize }}">
</label>
<span class="icon is-small is-left">
<span class="mdi mdi-24px mdi-shield-account"></span>
@ -39,7 +38,7 @@
<div class="field">
<div class="control is-expanded has-icons-left">
<label for="password">
<input class="input" name="password" type="password" placeholder="password">
<input class="input" name="password" type="password" placeholder="{{ i18n['password'] | capitalize }}">
</label>
<span class="icon is-small is-left">
<span class="mdi mdi-24px mdi-shield-key"></span>
@ -52,6 +51,5 @@
</div>
<div class="hero-foot"></div>
</section>
</body>
</html>

@ -1,14 +1,18 @@
{% extends "base.html" %}
{% block main %}
<div style="min-height: 100vh;" class="container bg-deepmatte p-6 brdr-yayellow">
<h1 class="title has-text-white has-text-centered">{{ title }}</h1>
{% if action == "spam" %}
<h1 class="title has-text-white has-text-centered">{{ i18n['manage_spam'] }}</h1>
{% else %}
<h1 class="title has-text-white has-text-centered">{{ i18n['manage_comments'] }}</h1>
{% endif %}
<div class="field">
<form method="GET" action="/dashboard/{{ project }}/manage-{{action}}/">
<div class="control">
<div class="select">
<label for="location">
<select name="location" onchange="this.form.submit();">
<option value="-1">Select the article</option>
<option value="-1">{{ i18n['select_article'] }}</option>
{% for each in locations %}
{% if selected is defined %}
{% if selected | string() == each.id_location | string() %}
@ -27,6 +31,13 @@
</form>
</div>
<div>
{% if locations | length == 0 %}
{% if action == "spam" %}
<div class="block">
<p class="has-text-centered mt-5 is-size-2">{{ i18n['hooray_no_spam'] }}</p>
</div>
{% endif %}
{% endif %}
{% if spam_comments is defined %}
{% for comment in spam_comments %}
<article>
@ -38,35 +49,43 @@
</figure>
<div class="media-content">
<div class="content mr-5 mt-2">
<a title="comment ID" id="comment_{{comment.comments_id}}" href="#comment_{{comment.comments_id}}">#{{comment.comments_id}}</a>
Posted by <span class="fg-yellow">{{comment.email}}</span>
on <small class="fg-yellow">{{comment.created_on}}</small>
spam score: <span title="The higher this is, the less likely it is spam" class="fg-yellow">{{ comment.spam_score }}</span>
<a data-tippy-content="comment ID"
id="comment_{{comment.comments_id}}"
href="#comment_{{comment.comments_id}}">
#{{comment.comments_id}}
</a>
&nbsp;<span class="fg-yellow">{{comment.email}}</span>
&nbsp;<span class="fg-yellow">{{comment.created_on}}</span>
&nbsp;{{ i18n['spam_score'] }}:&nbsp;
<span data-tippy-content="{{ i18n['tooltip_spam_score'] }}"
class="fg-yellow">
{{ comment.spam_score | round(5) }}</span>
&nbsp;{{ i18n['published'] }}:&nbsp;<span class="fg-yellow">{{ comment.is_published }}</span>
<br><br>
<span class="mt-5">
{{comment.content}}
</span>
</div>
<nav class="level is-mobile">
<a title="Delete this comment"
<a data-tippy-content="{{ i18n['manage_comments_delete_comment'] }}"
class="level-item"
href="/api/comment-delete/{{ comment.comments_id }}?location={{ selected }}">
<span class="icon is-medium"><i class="mdi mdi-24px mdi-trash-can"></i></span>
<span class="icon is-medium"><i class="mdi mdi-24px mdi-trash-can"></i></span>
</a>
<a title="Delete comment and block mail address"
<a data-tippy-content="{{ i18n['manage_comments_delete_and_block'] }}"
class="level-item"
href="/api/comment-block-mail/{{ comment.comments_id }}?location={{ selected }}">
<span class="icon is-medium"><i class="mdi mdi-24px mdi-shield-lock"></i></span>
<span class="icon is-medium"><i class="mdi mdi-24px mdi-close-box"></i></span>
</a>
<a title="Publish this comment, don't allow mail"
<a data-tippy-content="{{ i18n['manage_comments_allow_comment'] }}"
class="level-item"
href="/api/comment-allow/{{ comment.comments_id }}?location={{ selected }}">
<span class="icon is-medium"><i class="mdi mdi-24px mdi-check"></i></span>
<span class="icon is-medium"><i class="mdi mdi-24px mdi-check"></i></span>
</a>
<a title="Allow email to bypass spam detection and allow comment"
<a data-tippy-content="{{ i18n['manage_comments_allow_and_approve'] }}"
class="level-item"
href="/api/comment-allow-user/{{ comment.comments_id }}?location={{ selected }}">
<span class="icon is-medium"><i class="mdi mdi-24px mdi-check-all"></i></span>
<span class="icon is-medium"><i class="mdi mdi-24px mdi-check-all"></i></span>
</a>
</nav>
</div>

@ -1,15 +1,16 @@
{% extends "base.html" %}
{% block main %}
<div style="min-height: 100vh;" class="container bg-deepmatte p-6 brdr-yayellow">
<h1 class="title has-text-white has-text-centered">Manage mail addresses</h1>
<h1 class="title has-text-white has-text-centered">{{ i18n['manage_mail'] }}</h1>
<div class="field">
<div class="control has-icons-left">
<input class="input"
type="text"
id="mail-search"
oninput="dashboard_mailsearch(this);"
placeholder="Search mail..."
placeholder="{{ i18n['placeholder_search_mail'] }}..."
aria-placeholder="type to search mail addresses">
<label for="mail-search"></label>
<span class="icon is-small is-left">
<span class="mdi mdi-24px mdi-account-search"></span>
</span>
@ -19,8 +20,8 @@
<table class="table is-fullwidth">
<thead>
<tr>
<th class="has-text-centered">Address</th>
<th class="has-text-centered">Status</th>
<th class="has-text-centered is-uppercase">{{ i18n['address'] }}</th>
<th class="has-text-centered is-uppercase">{{ i18n['status'] }}</th>
<th class="has-text-centered"></th>
</tr>
</thead>
@ -31,13 +32,13 @@
<td>
<p class="has-text-centered">
{% if each.is_blocked %}
<a title="Email is currently blocked. Click to unblock."
<a title="{{ i18n['tooltip_email_blocked'] }}"
class="has-text-black"
href="/api/mail-toggle-status/{{ each.id_email }}">
<i class="mdi mdi-24px mdi-check"></i>
</a>
{% else %}
<a title="Email is currently excempt from spam detection. Click to block."
<a data-tippy-content="{{ i18n['tooltip_email_allowed'] }}"
class="has-text-danger"
href="/dashboard/toggle-mail-allowed/{{ each.id_email }}">
<i class="mdi mdi-24px mdi-block-helper"></i>
@ -47,7 +48,7 @@
</td>
<td>
<p class="has-text-centered">
<a title="Delete entry, this resets the reputation"
<a data-tippy-content="{{ i18n['tooltip_delete_email'] }}"
class="has-text-danger-dark"
href="/api/mail-reset-reputation/{{ each.id_email }}">
<i class="mdi mdi-24px mdi-trash-can"></i>

@ -1,14 +1,14 @@
{% extends "base.html" %}
{% block main %}
<div style="min-height: 100vh;" class="container bg-deepmatte p-6 brdr-yayellow">
<h1 class="is-size-2 mb-3 is-uppercase">Select project</h1>
<h1 class="is-size-2 mb-3 is-uppercase">{{ i18n['select_project_to_manage']}}</h1>
<div class="columns is-multiline">
<div class="column is-4">
<div class="card my-shadow-subtle brdr-darkslate">
<div class="card-header">
<div class="card-header-title">
<p class="is-size-4 is-uppercase">
NEW PROJECT
{{ i18n['new_project']}}
</p>
</div>
</div>
@ -16,19 +16,19 @@
<div class="level is-mobile">
<div class="level-item has-text-centered">
<div>
<p class="heading is-capitalized">Comments</p>
<p class="heading is-capitalized">{{ i18n['comments']}}</p>
<p class="is-size-4 has-text-weight-bold has-text-white">n/a</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading is-capitalized">Unpublished</p>
<p class="heading is-capitalized">{{ i18n['unpublished']}}</p>
<p class="is-size-4 has-text-weight-bold has-text-white">n/a</p>
</div>
</div>
<div class="level-item has-text-centered">
<div class="ml-4">
<p class="heading is-capitalized">Spam</p>
<p class="heading is-capitalized">{{ i18n['spam']}}</p>
<p class="is-size-4 has-text-weight-bold has-text-white">n/a</p>
</div>
</div>
@ -39,8 +39,8 @@
<!-- TODO: onclick -->
<a class="has-text-weight-bold has-text-black is-uppercase"
onclick="show_modal_with_project('modal-project-edit', null)"
data-tippy-content="Create a new project" href="#"
>NEW</a>
data-tippy-content="{{ i18n['tooltip_create_new_project'] }}" href="#"
>{{ i18n['new']}}</a>
</div>
</div>
</div>
@ -59,19 +59,19 @@
<div class="level is-mobile">
<div class="level-item has-text-centered">
<div>
<p class="heading is-capitalized">Comments</p>
<p class="heading is-capitalized">{{ i18n['comments']}}</p>
<p class="is-size-4 has-text-weight-bold has-text-white">{{ each['total_comments'] }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading is-capitalized">Unpublished</p>
<p class="heading is-capitalized">{{ i18n['unpublished']}}</p>
<p class="is-size-4 has-text-weight-bold has-text-white">{{ each['total_unpublished'] }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div class="ml-4">
<p class="heading is-capitalized">Spam</p>
<p class="heading is-capitalized">{{ i18n['spam']}}</p>
<p class="is-size-4 has-text-weight-bold has-text-white">{{ each['total_spam'] }}</p>
</div>
</div>
@ -80,25 +80,34 @@
<div class="card-footer">
<div class="card-footer-item has-background-danger-dark">
<a class="has-text-weight-bold has-text-white is-uppercase"
data-tippy-content="Delete the project and all of its content"
onclick="show_modal('modal-project-delete', '{{ each['name'] }}');">DELETE</a>
data-tippy-content="{{ i18n['tooltip_delete_project'] }}"
onclick="show_modal('modal-project-delete', '{{ each['name'] }}');">
{# {{ i18n['delete']}}#}
<span class="icon"><span class="mdi mdi-24px mdi-delete"></span></span>
</a>
</div>
<div class="card-footer-item">
<a class="has-text-weight-bold has-text-black is-uppercase"
onclick="show_modal_with_project('modal-project-edit', '{{ each['name'] }}');"
data-tippy-content="Edit the name of the project and it's properties"
href="#">EDIT</a>
data-tippy-content="{{ i18n['tooltip_edit_project'] }}"
href="#">
<span class="icon"><span class="mdi mdi-24px mdi-database-edit"></span></span>
</a>
</div>
<div class="card-footer-item">
<a class="has-text-weight-bold has-text-black is-uppercase"
onclick="show_modal('modal-comments-export', '{{ each['name'] }}')"
data-tippy-content="Export all comments to Hugo.<br>This is normally not needed."
href="#">EXPORT</a>
data-tippy-content="{{ i18n['tooltip_export_all_comments'] }}"
href="#">
<span class="icon"><span class="mdi mdi-24px mdi-webhook"></span></span>
</a>
</div>
<div class="card-footer-item">
<a class="has-text-weight-bold has-text-black is-uppercase"
data-tippy-content="Manage this project"
href="{{ url_for('bp_dashboard.dashboard_project_stats', project=each['name']) }}">manage</a>
data-tippy-content="{{ i18n['tooltip_manage_this_project'] }}"
href="{{ url_for('bp_dashboard.dashboard_project_stats', project=each['name']) }}">
<span class="icon"><span class="mdi mdi-24px mdi-view-comfy"></span></span>
</a>
</div>
</div>
</div>
@ -111,9 +120,3 @@
{% include "modals/project_edit.html" %}
{% include "modals/comments-export-all.html"%}
{% endblock %}
{% block javascript %}
tippy('[data-tippy-content]', {
allowHTML: true,
delay: 500
});
{% endblock %}

@ -1,37 +1,91 @@
{% extends "base.html" %}
{% block main %}
<div style="min-height: 100vh;" class="container bg-deepmatte p-6 brdr-yayellow">
<h1 class="title has-text-white has-text-centered">Overview</h1>
<h1 class="title is-size-2 has-text-white has-text-centered is-uppercase">{{ i18n['statistics'] }}</h1>
<div class="columns">
<div class="column is-full">
<p class="is-size-4 mb-4 mt-4">Last 7 days</p>
<canvas id="chart-7d"></canvas>
<p class="is-size-4 mb-4 mt-4">{{ i18n['stats_total_percentage'] }}</p>
<canvas id="chart-total"
data-spam="{{ total_spam }}"
data-comments="{{ total_comments }}"
data-unpublished="{{ total_unpublished }}">
Your Browser does not support a HTML5 canvas :(.
</canvas>
</div>
</div>
<div class="columns">
<div class="column is-full">
<p class="is-size-4 mb-4 mt-4">{{ i18n['stats_last_7_days'] }}</p>
<canvas id="chart-7d">Your Browser does not support a HTML5 canvas :(.</canvas>
</div>
</div>
</div>
<!--suppress JSValidateTypes -->
<script>
window.addEventListener('DOMContentLoaded', () => {
let chart_total = document.getElementById('chart-total');
let total_spam = parseInt(chart_total.dataset.spam);
let total_comments = parseInt(chart_total.dataset.comments);
let total_unpublished = parseInt(chart_total.dataset.unpublished);
let total = (total_spam + total_comments + total_unpublished);
let spam_perc = (total_spam/total) * 100;
let comm_perc = (total_comments/total) * 100;
let unpub_perc = (total_unpublished/total) * 100;
console.log(total);
console.log(spam_perc);
console.log(comm_perc);
console.log(unpub_perc);
new Chart(document.getElementById('chart-total'), {
type: "pie",
data: {
datasets: [{
data: [spam_perc, comm_perc, unpub_perc],
backgroundColor: [
"rgba(182, 106, 254, 0.8)",
"rgba(254, 218, 106, 0.8)",
"rgba(108, 106, 254, 0.8)"
],
}
],
labels:[
"{{ i18n['spam'] }}",
"{{ i18n['stats_label_regular_comments'] }}",
"{{ i18n['stats_label_unpublished_comments'] }}"
]
},
options:{
responsive: true,
legend: {
labels: {
fontColor: 'white'
}
}
}
});
new Chart( document.getElementById('chart-7d'), {
type: "line",
data: {
labels:[{% for days in dates %}"{{ days }}"{% if not loop.last %},{% endif %}{% endfor %}],
datasets: [
{
label: "spam",
label: "{{ i18n['spam'] }}",
borderColor: "rgba(182, 106, 254, 1)",
backgroundColor: "rgba(182, 106, 254, 0.1)",
data: {{ spam }}
},
{
label: "published comments",
label: "{{ i18n['stats_label_regular_comments'] }}",
borderColor: "rgba(254, 218, 106, 1)",
backgroundColor: "rgba(254, 218, 106, 0.1)",
data: {{published}}
},
{
label: "unpublished comments",
label: "{{ i18n['stats_label_unpublished_comments'] }}",
borderColor: "rgba(108, 106, 254, 1)",
backgroundColor: "rgba(108, 106, 254, 0.1)",
data: {{ unpublished }}

Loading…
Cancel
Save