* 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
* Translated modals
* Added translation endpoint for Javascript
* Translated Javascript messages
* Improved dashboard javascript by removing duplicates
projects
Domeniko Gentner 3 years ago
parent 1e3ce054a9
commit 5ac50ed667
  1. 36
      i18n/de-DE.json
  2. 35
      i18n/en-US.json
  3. 1
      labertasche/blueprints/bp_jsconnector/__init__.py
  4. 21
      labertasche/blueprints/bp_jsconnector/language.py
  5. 177
      static/js/dashboard.js
  6. 2
      templates/login.html
  7. 17
      templates/modals/comments-export-all.html
  8. 18
      templates/modals/project-delete.html
  9. 55
      templates/modals/project_edit.html
  10. 17
      templates/modals/project_not_found.html

@ -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.<br>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!"
}

@ -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."
}

@ -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

@ -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)

@ -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,
{
mode: "cors",
headers: {
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,
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,95 +214,31 @@ async function save_project_settings(id_name)
"addon_smileys": document.getElementById('edit-project-addons-smileys').checked
}
if (modal.dataset.mode === "edit"){
let old_name = modal.dataset.name;
await post('/api/project/edit/' + old_name, JSON.stringify(json_data), function(result){
// Get field for errors and reset it
let error = document.getElementById('modal-edit-error-messages')
error.innerText = ''
let has_error = false;
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'] === '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;
if (modal.dataset.mode === "edit"){
let old_name = modal.dataset.name;
await post('/api/project/edit/' + old_name, JSON.stringify(json_data), function(result){
if (result['status'] !== 'ok') {
has_error = resolve_error_status(error, result);
}
// 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'] !== 'ok') {
has_error = resolve_error_status(error, result);
}
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'] === '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');
if (has_error === false){
window.location.reload(true);
})
}
}
@ -320,8 +271,8 @@ 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');
}
@ -329,6 +280,48 @@ async function export_all_comments(btn)
// 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;
}

@ -9,7 +9,7 @@
<link rel="stylesheet" href="/static/css/Chart.min.css" media="screen">
<title>Labertasche {{ i18n['dashboard'] | capitalize }}</title>
</head>
<body>
<body class="is-family-sans-serif" data-language="{{ i18n['browser_language'] }}">
<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"

@ -2,20 +2,19 @@
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head has-background-warning">
<p class="modal-card-title has-text-black is-uppercase">Export all comments</p>
<p class="modal-card-title has-text-black is-uppercase">{{ i18n['export_all_comments'] }}</p>
<button onclick="hide_modal('modal-comments-export')" class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body has-text-white bg-deepmatte">
<p class="block">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.</p>
<p class="block has-text-danger">Do you wish to proceed?</p>
<section class="modal-card-body bg-deepmatte">
<div class="block has-text-white">{{ i18n['export_warning_text'] }}</div>
<div class="block has-text-danger">{{ i18n['wish_to_proceed'] }}</div>
</section>
<footer class="modal-card-foot bg-deepmatte">
<button id="modal-ok" onclick="export_all_comments(this);" class="button is-success">
OK
<button id="modal-ok" onclick="export_all_comments(this);" class="button is-warning is-capitalized">
{{ i18n['ok'] }}
</button>
<button id="modal-cancel" onclick="hide_modal('modal-comments-export')" class="button is-info">
CANCEL
<button id="modal-cancel" onclick="hide_modal('modal-comments-export')" class="button is-success is-capitalized">
{{ i18n['cancel'] }}
</button>
</footer>
</div>

@ -2,19 +2,19 @@
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head has-background-warning">
<p class="modal-card-title has-text-black">WARNING!</p>
<p class="modal-card-title has-text-black is-capitalized">{{ i18n['warning'] }}!</p>
<button onclick="hide_modal('modal-project-delete')" class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body has-text-black">
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.
<section class="modal-card-body has-text-black bg-deepmatte">
<div class="block has-text-white">{{ i18n['delete_project_warning'] }}</div>
<div class="block has-text-danger">{{ i18n['wish_to_proceed'] }}</div>
</section>
<footer class="modal-card-foot">
<button id="modal-delete-ok" onclick="project_delete()" class="button is-danger">
OK
<footer class="modal-card-foot bg-deepmatte has-text-white">
<button id="modal-delete-ok" onclick="project_delete()" class="button is-danger is-capitalized">
{{ i18n['ok'] }}
</button>
<button id="modal-delete-cancel" onclick="hide_modal('modal-project-delete')" class="button is-success">
CANCEL
<button id="modal-delete-cancel" onclick="hide_modal('modal-project-delete')" class="button is-success is-capitalized">
{{ i18n['cancel'] }}
</button>
</footer>
</div>

@ -2,94 +2,97 @@
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head has-background-warning">
<p id="modal-title" class="modal-card-title has-text-black">Edit Project</p>
<p id="modal-title" class="modal-card-title has-text-black">{# filled by javascript #}</p>
<button onclick="hide_modal('modal-project-edit')" class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body has-text-black bg-deepmatte">
<form>
<div class="field">
<div class="control has-icons-left"
data-tippy-content="Please select an unique name for your project.">
data-tippy-content="{{ i18n['tooltip_project_name'] }}">
<input class="input" id="edit-project-name"
name="edit-project-name" type="text">
<span class="icon is-small is-left">
<i class="mdi mdi-24px mdi-help-rhombus-outline"></i>
<i class="mdi mdi-24px mdi-ab-testing"></i>
</span>
</div>
<label class="label help has-text-white" for="edit-project-name">
Project Name
{{ i18n['project_name'] }}
</label>
</div>
<div class="field">
<div class="control has-icons-left"
data-tippy-content="URL of your hugo site, e.g. https://example.com">
data-tippy-content="{{ i18n['tooltip_hugo_url'] }}">
<input class="input" id="edit-project-blog-url" name="edit-project-blog-url" type="text">
<span class="icon is-small is-left">
<i class="mdi mdi-24px mdi-help-rhombus-outline"></i>
<i class="mdi mdi-24px mdi-web"></i>
</span>
</div>
<label class="label help has-text-white" for="edit-project-blog-url">
URL of your Hugo site for this project
{{ i18n['description_hugo_url'] }}
</label>
</div>
<div class="field">
<div class="control has-icons-left"
data-tippy-content="The path to the data directory of your Hugo installation.">
data-tippy-content="{{ i18n['tooltip_output_path'] }}">
<input class="input" id="edit-project-output" name="edit-project-output" type="text">
<span class="icon is-small is-left">
<i class="mdi mdi-24px mdi-help-rhombus-outline"></i>
<i class="mdi mdi-24px mdi-folder-outline"></i>
</span>
</div>
<label class="label help has-text-white" for="edit-project-output">Output Directory</label>
<label class="label help has-text-white" for="edit-project-output">{{ i18n['description_output_path'] }}</label>
</div>
<div class="field">
<div class="control">
<div class="control"
data-tippy-content="{{ i18n['tooltip_gravatar_cache'] }}">
<label class="checkbox help has-text-white" for="edit-project-gravatar-cache">
<input id="edit-project-gravatar-cache" class="checkbox" type="checkbox"
name="edit-project-gravatar-cache" checked onclick="toggle_gravatar_settings(this);">
Cache Gravatar images?
{{ i18n['description_gravatar_cache'] }}
</label>
</div>
</div>
<div class="field">
<div class="control has-icons-left"
data-tippy-content="The directory where to save the gravatar images. Should be a full path.">
data-tippy-content="{{ i18n['tooltip_gravatar_dir'] }}">
<input class="input" id="edit-project-gravatar-cache-dir" name="edit-project-gravatar-cache-dir" type="text">
<span class="icon is-small is-left">
<i class="mdi mdi-24px mdi-help-rhombus-outline"></i>
<i class="mdi mdi-24px mdi-folder-outline"></i>
</span>
</div>
<label class="label help has-text-white" for="edit-project-gravatar-cache-dir">
Gravatar Cache Directory
{{ i18n['description_gravatar_dir'] }}
</label>
</div>
<div class="field">
<div class="control has-icons-left"
data-tippy-content="The numeric size of the images to download. Must be a power of 2.">
data-tippy-content="{{ i18n['tooltip_gravatar_size'] }}">
<input class="input" id="edit-project-gravatar-size" name="edit-project-gravatar-size" type="text" value="256">
<span class="icon is-small is-left">
<i class="mdi mdi-24px mdi-help-rhombus-outline"></i>
<i class="mdi mdi-24px mdi-numeric-0-box"></i>
</span>
</div>
<label class="label help has-text-white" for="edit-project-gravatar-size">
Gravatar image size
{{ i18n['description_gravatar_size'] }}
</label>
</div>
<div class="field">
<div class="control">
<div class="control"
data-tippy-content="{{ i18n['tooltip_send_otp'] }}">
<label class="checkbox help has-text-white" for="edit-project-send-otp">
<input id="edit-project-send-otp" class="checkbox" type="checkbox"
name="edit-project-send-otp" checked>
Send OTP to publish?
{{ i18n['description_send_otp'] }}
</label>
</div>
</div>
<div class="field">
<div class="control">
<div class="control"
data-tippy-content="{{ i18n['tooltip_enable_smileys'] }}">
<label class="checkbox help has-text-white" for="edit-project-addons-smileys">
<input id="edit-project-addons-smileys" class="checkbox" type="checkbox"
name="edit-project-addons-smileys" checked>
Enable Smiley Addon?
{{ i18n['description_enable_smileys'] }}
</label>
</div>
</div>
@ -98,11 +101,11 @@
</form>
</section>
<footer class="modal-card-foot bg-deepmatte">
<button id="modal-save-ok" onclick="save_project_settings('modal-project-edit');" class="button is-success">
OK
<button id="modal-save-ok" onclick="save_project_settings('modal-project-edit');" class="button is-success is-capitalized">
{{ i18n['ok'] }}
</button>
<button onclick="hide_modal('modal-project-edit')" class="button is-info">
CANCEL
<button onclick="hide_modal('modal-project-edit')" class="button is-info is-capitalized">
{{ i18n['cancel'] }}
</button>
</footer>
</div>

@ -2,22 +2,23 @@
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head has-background-danger">
<p class="modal-card-title has-text-white">ERROR</p>
<p class="modal-card-title has-text-white is-capitalized">{{ i18n['error'] }}</p>
<button onclick="hide_modal('modal-project-not-found')" class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body has-text-black">
The specified project was not found! Did you delete it? Try refreshing the page.<br>
If you believe this to be a bug, please report it
<a class="has-text-info"
<div class="block">{{ i18n['message_project_404'] }}</div>
<div class="block">
<a class="has-text-info is-uppercase"
href="https://github.com/domeniko-gentner/labertasche/issues"
target="_blank" rel="nofollow noopener norefferer">
here
</a>.
{{ i18n['link'] }}
</a>
</div>
</section>
<footer class="modal-card-foot">
<button id="modal-ok" onclick="hide_modal('modal-project-not-found', {{ url_for('bp_dashboard.dashboard_project_list') }})"
class="button is-success">
OK
class="button is-success is-capitalized">
{{ i18n['ok'] }}
</button>
</footer>
</div>

Loading…
Cancel
Save