diff --git a/i18n/de-DE.json b/i18n/de-DE.json
index 619566d..b2b717c 100644
--- a/i18n/de-DE.json
+++ b/i18n/de-DE.json
@@ -2,12 +2,15 @@
"html_language": "de",
"browser_language": "de-DE",
"ok": "ok",
+ "link": "link",
"cancel": "abbrechen",
"dashboard": "dashboard",
"username": "benutzername",
"password": "passwort",
"login": "login",
"logout": "abmelden",
+ "error": "fehler",
+ "warning": "warnung",
"comments": "kommentare",
"unpublished": "unveröffentlicht",
"published": "veröffentlicht",
@@ -18,7 +21,8 @@
"delete": "löschen",
"new": "neu",
"project": "projekt",
- "new_project": "neues projekt",
+ "new_project": "Neues Projekt",
+ "project_name": "Projekt Name",
"statistics": "statistiken",
"address": "adresse",
"status": "status",
@@ -44,7 +48,35 @@
"tooltip_export_all_comments": "Alle Kommentare nach Hugo exportieren.
Wird normalerweise nicht benötigt.",
"tooltip_manage_this_project": "Dieses Projekt verwalten",
"placeholder_search_mail": "Mail Adressen durchsuchen",
+ "export_all_comments": "Alle Kommentare exportieren",
+ "export_warning_text": "Dies wird alle Kommentare neu exportieren. Normalerweise wird das nicht benötigt, kann aber in gewissen Situatonen hilfreich sein.",
+ "delete_project_warning": "Du bist dabei ein Projekt zu löschen. Dies wird alle dazugehörigen Daten auch löschen. Bitte führe einen manuellen SQL Dump durch, falls du die Daten behalten willst.",
+ "wish_to_proceed": "Möchtest du fortfahren?",
"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."
+ "tooltip_delete_email": "Eintrag löschen. Email unterliegt wieder den normalen Regeln.",
+ "javascript_required_field_empty": "Ein Pflichtfeld wurde leer gelassen!",
+ "javascript_invalid_project_name": "Der Projektname ist nicht gültig, bitte nur Buchstaben und Zahlen verwenden!",
+ "javascript_project_duplicate": "Ein Projekt mit diesem name existiert bereits!",
+ "javascript_blogurl_invalid": "Die blog-url ist ungültig!",
+ "javascript_output_nonexistent": "Der Ausgabe Pfad existiert nicht!",
+ "javascript_gravatar_cache_nonexistent": "Der caching Pfad existiert nicht!",
+ "javascript_exception": "Es gab eine unerwartet Ausnahme. Bitte eröffne ein issue auf Github!",
+ "javascript_edit_project_modal_title": "Projekt %name% editieren",
+ "description_hugo_url": "Die URL der Hugo Seite für dieses Projekt.",
+ "tooltip_hugo_url": "Die URL sollte so aussehen: https://beispiel.de",
+ "tooltip_project_name": "Bitte wähle einen einzigartigen Namen für das Projekt.",
+ "description_output_path": "Hugo Datenverzeichnis",
+ "tooltip_output_path": "Der Pfad zum Datenverzeichnis der Hugo Installation. Kann relativ sein.",
+ "description_gravatar_cache": "Gravatar Bilder lokal speichern?",
+ "tooltip_gravatar_cache": "Wenn dies aktiviert ist, wird Labertasche Gravatare herunterladen und hier speichern.",
+ "tooltip_gravatar_dir": "Der Pfad zum Zwischenspeicher für Gravatare. Kann relativ sein.",
+ "description_gravatar_dir": "Lokales Verzeichnis für Gravatar",
+ "tooltip_gravatar_size": "Die Größe der Gravatare. Sollte ein vielfaches von 2 sein, bswp. 64, 128 oder 256.",
+ "description_gravatar_size": "Gravatar Bildgröße",
+ "description_send_otp": "OTP zur Veröffentlichung senden?",
+ "tooltip_send_otp": "Wenn aktiviert, bekommt der User ein Einmalpasswort zugesendet, mit dem der Kommentar veröffentlich wird (empfohlen). Wenn deaktiviert, wird der Kommentar immer veröffentlicht (ausser Spam).",
+ "description_enable_smileys": "Smiley Addon aktivieren?",
+ "tooltip_enable_smileys": "Wenn aktiviert, werden simple Text Smileys mit Emojis ersetzt. In /etc/labertasche/smileys.yaml findest du die aktivierten Smileys.",
+ "message_project_404": "Das angegebene Projekt wurde nicht gefunden. Wurde es vielleicht gelöscht? Wenn du denkst, dass dies ein Bug ist, melde es bitte auf Github!"
}
diff --git a/i18n/en-US.json b/i18n/en-US.json
index bceb0fe..9167e01 100644
--- a/i18n/en-US.json
+++ b/i18n/en-US.json
@@ -3,11 +3,14 @@
"browser_language": "en-US",
"ok": "ok",
"cancel": "cancel",
+ "link": "link",
"dashboard": "dashboard",
"username": "username",
"password": "password",
"login": "login",
"logout": "logout",
+ "error": "error",
+ "warning": "warning",
"comments": "comments",
"unpublished": "unpublished",
"published": "published",
@@ -22,6 +25,7 @@
"statistics": "statistics",
"address": "address",
"status": "status",
+ "project_name": "Projekt Name",
"manage_mail": "manage mail addresses",
"stats_label_regular_comments": "regular comments",
"stats_label_unpublished_comments": "unpublished comments",
@@ -45,5 +49,34 @@
"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."
+ "tooltip_delete_email": "Delete entry, Email has to follow the regular rules.",
+ "export_all_comments": "Export all comments",
+ "export_warning_text": "This will export all comments of this project to all locations. Usually this is not needed, but can be helpful, if you have imported backups or similar.",
+ "wish_to_proceed": "Do you wish to proceed?",
+ "delete_project_warning": "You are about to delete a project. All associated data will be unrecoverably lost! Please perform a manual sql dump if you would like to retain that data.",
+ "javascript_required_field_empty": "A required field has been left empty!",
+ "javascript_invalid_project_name": "The project name is not valid. Please only use alphanumeric characters!",
+ "javascript_project_duplicate": "A project with this name already exists!",
+ "javascript_blogurl_invalid": "The blog-url is invalid!",
+ "javascript_output_nonexistent": "This output path does not exist!",
+ "javascript_gravatar_cache_nonexistent": "The cache path does not exist!",
+ "javascript_exception": "There was an unexpected exception. Please open an issue on Github!",
+ "javascript_edit_project_modal_title": "Edit project %name%",
+ "javascript_new_project_modal_title": "New Project",
+ "description_hugo_url": "URL of your Hugo site for this project",
+ "tooltip_hugo_url": "An URL is formed like this: https://example.com",
+ "tooltip_project_name": "Please select an unique name for your project.",
+ "description_output_path": "Hugo Data dir",
+ "tooltip_output_path": "The path to the data directory of your Hugo installation. Path can be relative.",
+ "description_gravatar_cache": "Cache Gravatar images locally?",
+ "tooltip_gravatar_cache": "If enabled, Labertasche will download gravatars to this location",
+ "tooltip_gravatar_dir": "The directory where to save the Gravatar images. Path can be relative.",
+ "description_gravatar_dir": "Gravatar caching directory.",
+ "tooltip_gravatar_size": "The numeric size of the images to download. Must be a power of 2, e.g 64, 128, 256",
+ "description_gravatar_size": "Gravatar image size",
+ "description_send_otp": "Send OTP to publish?",
+ "tooltip_send_otp": "If enabled, the user will be mailed a one time password to publish the comment (recommended). If disabled, it will be published by default (except spam).",
+ "description_enable_smileys": "Enable Smiley Addon?",
+ "tooltip_enable_smileys": "If enabled, simple text Smileys will be replaced with Emojis. Please see /etc/labertasche/smileys.yaml for more.",
+ "message_project_404": "The specified project was not found! Did you delete it? If you believe this to be a bug, please report it."
}
diff --git a/labertasche/blueprints/bp_jsconnector/__init__.py b/labertasche/blueprints/bp_jsconnector/__init__.py
index ec3ba38..f982610 100644
--- a/labertasche/blueprints/bp_jsconnector/__init__.py
+++ b/labertasche/blueprints/bp_jsconnector/__init__.py
@@ -13,5 +13,6 @@ bp_jsconnector = Blueprint("bp_jsconnector", __name__, url_prefix='/api/')
from .projects import api_create_project, api_delete_project, api_edit_project_name
from .mail import api_toggle_email_reputation, api_reset_mail_reputation
+from .language import api_translation
from .comments import api_comment_allow_user, api_comment_allow_comment, \
api_comment_block_mail, api_comments_delete_comment
diff --git a/labertasche/blueprints/bp_jsconnector/language.py b/labertasche/blueprints/bp_jsconnector/language.py
new file mode 100644
index 0000000..9b143f2
--- /dev/null
+++ b/labertasche/blueprints/bp_jsconnector/language.py
@@ -0,0 +1,21 @@
+#!/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 . import bp_jsconnector
+from flask import make_response, jsonify, request
+from flask_cors import cross_origin
+from flask_login import login_required
+from labertasche.language import Language
+
+
+@cross_origin
+@bp_jsconnector.route('/language/')
+@login_required
+def api_translation():
+ lang = Language(request=request)
+ return make_response(jsonify(lang.i18n), 200)
diff --git a/static/js/dashboard.js b/static/js/dashboard.js
index 7cc14a2..396e950 100644
--- a/static/js/dashboard.js
+++ b/static/js/dashboard.js
@@ -5,20 +5,27 @@
// # * _license : This project is under MIT License
// # *********************************************************************************/
-async function get(partial, callback) {
- await fetch(window.location.protocol + "//" + window.location.host + partial,
+async function get(partial, callback = null, accept_lang = null) {
+
+ let headers = {
+ 'Access-Control-Allow-Origin': window.location.host,
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ }
+ if (accept_lang){
+ headers = Object.assign(headers, {'Accept-Language': accept_lang})
+ }
+
+ return await fetch(window.location.protocol + "//" + window.location.host + partial,
{
mode: "cors",
- headers: {
- 'Access-Control-Allow-Origin': window.location.host,
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
+ headers,
method: "GET"
})
.then(async function (response) {
- let result = await response.json();
- callback(result);
+ const result = await response.json();
+ if (callback){ callback(result); }
+ return result;
})
.catch(function (exc) {
console.log(exc);
@@ -26,8 +33,8 @@ async function get(partial, callback) {
})
}
-async function post(partial, stringified_json, callback) {
- await fetch(window.location.protocol + "//" + window.location.host + partial,
+async function post(partial, stringified_json, callback = null) {
+ return await fetch(window.location.protocol + "//" + window.location.host + partial,
{
mode: "cors",
headers: {
@@ -39,11 +46,13 @@ async function post(partial, stringified_json, callback) {
body: stringified_json
})
.then(async function (response) {
- let result = await response.json();
- callback(result);
+ const result = await response.json();
+ if (callback){ callback(result); }
+ return result;
})
.catch(function (exc) {
console.log(exc);
+ return null;
})
}
@@ -59,7 +68,10 @@ function dashboard_mailsearch(search_txt)
children[i].style.display = "none";
let iTxt = children[i].innerText.replace(/(\r\n|\n|\r)/gm, "").trim();
- if ( search_txt.value === iTxt.slice(0, search_txt.value.length)){
+ if (iTxt.includes(search_txt.value)){
+ children[i].style.display = "table-row";
+ }
+ if(search_txt.value === ''){
children[i].style.display = "table-row";
}
}
@@ -129,6 +141,9 @@ async function show_modal_with_project(id_name, proj_name)
// Get Dialog
let modal = document.getElementById(id_name);
+ // Load i18n
+ let i18n = await get('/api/language', null, document.body.dataset.language);
+
if (proj_name){
// Get Data
await get('/api/project/get/' + proj_name,
@@ -147,7 +162,7 @@ async function show_modal_with_project(id_name, proj_name)
proj_el.value = proj_name
// Set project name
- title.innerText = "Edit project '" + proj_name + "'";
+ title.innerText =i18n['javascript_edit_project_modal_title'].replace('%name%', proj_name);
// Make active
modal.classList.add("is-active");
@@ -158,7 +173,7 @@ async function show_modal_with_project(id_name, proj_name)
}
if (proj_name == null){
// Set project name
- title.innerText = "New Project";
+ title.innerText = i18n['new_project'];
// Reset fields, needed when user pressed cancel on edit modal
document.getElementById('edit-project-name').value = "";
@@ -199,96 +214,32 @@ async function save_project_settings(id_name)
"addon_smileys": document.getElementById('edit-project-addons-smileys').checked
}
+ // Get field for errors and reset it
+ let error = document.getElementById('modal-edit-error-messages')
+ error.innerText = ''
+ let has_error = false;
+
if (modal.dataset.mode === "edit"){
let old_name = modal.dataset.name;
-
await post('/api/project/edit/' + old_name, JSON.stringify(json_data), function(result){
- let error = document.getElementById('modal-edit-error-messages')
- error.innerText = ''
-
- if (result['status'] === 'too-short'){
- error.innerText = "A required field has been left empty!"
- return;
- }
- if (result['status'] === 'invalid-project-name') {
- error.innerText = "The project name is not valid. Please only use alphanumeric characters!"
- return;
- }
- if (result['status'] === 'project-exists') {
- error.innerText = "A project with this name already exists!"
- return;
- }
- if (result['status'] === 'invalid-blog-url') {
- error.innerText = "The blog-url is invalid!"
- return;
- }
- if (result['status'] === 'invalid-path-output') {
- error.innerText = "This output path does not exist!"
- return;
+ if (result['status'] !== 'ok') {
+ has_error = resolve_error_status(error, result);
}
- if (result['status'] === 'invalid-path-cache') {
- error.innerText = "The cache path does not exist!"
- return;
- }
- if (result['status'] === 'exception') {
- error.innerText = "There was an unexpected exception. Please report this to contact@tuxstash.de:"
- error.innerText += result['msg']
- return;
- }
-
- // Reset button
- btn.classList.remove('is-loading');
- window.location.reload(true);
})
}
if (modal.dataset.mode === 'new'){
await post('/api/project/new', JSON.stringify(json_data), function(result){
- let error = document.getElementById('modal-edit-error-messages')
- error.innerText = '';
-
- console.log(result['status']);
- if (result['status'] === 'too-short'){
- error.innerText = "A required field has been left empty!";
- btn.classList.remove('is-loading');
- return;
- }
- if (result['status'] === 'invalid-project-name') {
- error.innerText = "The project name is not valid. Please only use alphanumeric characters!";
- btn.classList.remove('is-loading');
- return;
- }
- if (result['status'] === 'project-exists') {
- error.innerText = "A project with this name already exists!";
- btn.classList.remove('is-loading');
- return;
- }
- if (result['status'] === 'invalid-blog-url') {
- error.innerText = "The blog-url is invalid!";
- btn.classList.remove('is-loading');
- return;
+ if (result['status'] !== 'ok') {
+ has_error = resolve_error_status(error, result);
}
- if (result['status'] === 'invalid-path-output') {
- error.innerText = "This output path does not exist!";
- btn.classList.remove('is-loading');
- return;
- }
- if (result['status'] === 'invalid-path-cache') {
- error.innerText = "The cache path does not exist!";
- btn.classList.remove('is-loading');
- return;
- }
- if (result['status'] === 'exception') {
- error.innerText = "There was an unexpected exception. Please report this to contact@tuxstash.de:";
- error.innerText += result['msg'];
- btn.classList.remove('is-loading');
- return;
- }
-
- // Reset button
- btn.classList.remove('is-loading');
- window.location.reload(true);
})
}
+
+ // Reset button
+ btn.classList.remove('is-loading');
+ if (has_error === false){
+ window.location.reload(true);
+ }
}
// ------------------------------------------------------
@@ -320,15 +271,57 @@ async function export_all_comments(btn)
{
btn.classList.add('is-loading');
let proj_name = document.getElementById('modal-comments-export').dataset.name;
+ let result = await get('/api/comment-export-all/' + proj_name);
- await get('/api/comment-export-all/' + proj_name, function(result){
- if (result['status'] === 'ok'){
- hide_modal('modal-comments-export');
- }
- if (result['status'] === 'not-found'){
- // Redirect to error
- hide_modal('modal-comments-export', '?error=404');
- }
- btn.classList.remove('is-loading');
- })
+ if (result['status'] === 'ok'){
+ hide_modal('modal-comments-export');
+ }
+ if (result['status'] === 'not-found'){
+ // Redirect to error
+ hide_modal('modal-comments-export', '?error=404');
+ }
+
+ // Reset button
+ btn.classList.remove('is-loading');
+ console.log(result);
+}
+
+
+async function resolve_error_status(field, result)
+{
+ // Load i18n
+ let i18n = await get('/api/language', null, document.body.dataset.language);
+
+ let has_error = false;
+
+ if (result['status'] === 'too-short'){
+ field.innerText = i18n['javascript_required_field_empty'];
+ has_error = true;
+ }
+ if (result['status'] === 'invalid-project-name') {
+ field.innerText = i18n['javascript_invalid_project_name'];
+ has_error = true;
+ }
+ if (result['status'] === 'project-exists') {
+ field.innerText = i18n['javascript_project_duplicate'];
+ has_error = true;
+ }
+ if (result['status'] === 'invalid-blog-url') {
+ field.innerText = i18n['javascript_blogurl_invalid'];
+ has_error = true;
+ }
+ if (result['status'] === 'invalid-path-output') {
+ field.innerText = i18n['javascript_output_nonexistent'];
+ has_error = true;
+ }
+ if (result['status'] === 'invalid-path-cache') {
+ field.innerText = "The cache path does not exist!";
+ has_error = true;
+ }
+ if (result['status'] === 'exception') {
+ field.innerText = i18n['javascript_exception'];
+ field.innerText += "\n" + result['msg'];
+ has_error = true;
+ }
+ return has_error;
}
diff --git a/templates/login.html b/templates/login.html
index 0f63287..2187e12 100644
--- a/templates/login.html
+++ b/templates/login.html
@@ -9,7 +9,7 @@
Export all comments
+{{ i18n['export_all_comments'] }}
This will export all comments of this project to all locations. - Usually this is not needed, but can be helpful, if you have imported backups or similar.
-Do you wish to proceed?
+WARNING!
+{{ i18n['warning'] }}!
Edit Project
+{# filled by javascript #}