One monster truck commit for

add translation API, add fi_FI and en_EN translations, add C and
.properties file support, check if file is binary before sending and
clean up ajax handler
This commit is contained in:
luukas 2021-03-26 15:57:50 +02:00
parent 85cb28b9ad
commit 0b3d20ffbe
27 changed files with 1088 additions and 468 deletions

View File

@ -0,0 +1,57 @@
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
import os
import json
import logging
logger = logging.getLogger(__name__)
class Translation():
def __init__(self):
self.translations_path = os.path.join(helper.root_dir, 'app', 'translations')
def translate(self, page, word):
translated_word = None
lang = helper.get_setting('language')
fallback_lang = 'en_EN'
translated_word = \
self.translate_inner(page, word, lang) or \
self.translate_inner(page, word, fallback_lang)
if translated_word:
if isinstance(translated_word, dict): return json.dumps(translated_word)
elif iter(translated_word) and not isinstance(translated_word, str): return '\n'.join(translated_word)
return translated_word
return 'Error while getting translation'
def translate_inner(self, page, word, lang):
lang_file = os.path.join(
self.translations_path,
lang + '.json'
)
try:
with open(lang_file, 'r') as f:
data = json.load(f)
try:
translated_page = data[page]
except KeyError:
logger.error('Translation File Error: page {} does not exist for lang {}'.format(page, lang))
console.error('Translation File Error: page {} does not exist for lang {}'.format(page, lang))
return None
try:
translated_word = translated_page[word]
return translated_word
except KeyError:
logger.error('Translation File Error: word {} does not exist on page {} for lang {}'.format(word, page, lang))
console.error('Translation File Error: word {} does not exist on page {} for lang {}'.format(word, page, lang))
return None
except Exception as e:
logger.critical('Translation File Error: Unable to read {} due to {}'.format(lang_file, e))
console.critical('Translation File Error: Unable to read {} due to {}'.format(lang_file, e))
return None
translation = Translation()

View File

@ -20,7 +20,8 @@ class AjaxHandler(BaseHandler):
def render_page(self, template, page_data):
self.render(
template,
data=page_data
data=page_data,
translate=self.translator.translate,
)
@tornado.web.authenticated
@ -84,47 +85,36 @@ class AjaxHandler(BaseHandler):
file_path = self.get_argument('file_path', None)
server_id = self.get_argument('id', None)
if server_id is None:
logger.warning("Server ID not found in get_file ajax call")
console.warning("Server ID not found in get_file ajax call")
return False
else:
server_id = bleach.clean(server_id)
# does this server id exist?
if not db_helper.server_id_exists(server_id):
logger.warning("Server ID not found in get_file ajax call ({})".format(server_id))
console.warning("Server ID not found in get_file ajax call ({})".format(server_id))
return False
if not self.check_server_id(server_id, 'get_file'): return False
else: server_id = bleach.clean(server_id)
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], file_path)\
or not helper.check_file_exists(os.path.abspath(file_path)):
or not helper.check_file_exists(os.path.abspath(file_path)):
logger.warning("Invalid path in get_file ajax call ({})".format(file_path))
console.warning("Invalid path in get_file ajax call ({})".format(file_path))
return False
error = None
try:
with open(file_path) as file:
file_contents = file.read()
except UnicodeDecodeError:
file_contents = ''
error = 'UnicodeDecodeError'
file = open(file_path)
file_contents = file.read()
file.close()
self.write(file_contents)
self.write({
'content': file_contents,
'error': error
})
self.finish()
elif page == "get_tree":
server_id = self.get_argument('id', None)
if server_id is None:
logger.warning("Server ID not found in get_file ajax call")
console.warning("Server ID not found in get_file ajax call")
return False
else:
server_id = bleach.clean(server_id)
# does this server id exist?
if not db_helper.server_id_exists(server_id):
logger.warning("Server ID not found in get_file ajax call ({})".format(server_id))
console.warning("Server ID not found in get_file ajax call ({})".format(server_id))
return False
if not self.check_server_id(server_id, 'get_tree'): return False
else: server_id = bleach.clean(server_id)
self.write(db_helper.get_server_data_by_id(server_id)['path'] + '\n' +
helper.generate_tree(db_helper.get_server_data_by_id(server_id)['path']))
@ -161,21 +151,11 @@ class AjaxHandler(BaseHandler):
server_id = self.get_argument('id', None)
print(server_id)
if server_id is None:
logger.warning("Server ID not found in create_file ajax call")
console.warning("Server ID not found in create_file ajax call")
return False
else:
server_id = bleach.clean(server_id)
# does this server id exist?
if not db_helper.server_id_exists(server_id):
logger.warning("Server ID not found in create_file ajax call ({})".format(server_id))
console.warning("Server ID not found in create_file ajax call ({})".format(server_id))
return False
if not self.check_server_id(server_id, 'create_file'): return False
else: server_id = bleach.clean(server_id)
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], file_path) \
or helper.check_file_exists(os.path.abspath(file_path)):
or helper.check_file_exists(os.path.abspath(file_path)):
logger.warning("Invalid path in create_file ajax call ({})".format(file_path))
console.warning("Invalid path in create_file ajax call ({})".format(file_path))
return False
@ -191,21 +171,11 @@ class AjaxHandler(BaseHandler):
server_id = self.get_argument('id', None)
print(server_id)
if server_id is None:
logger.warning("Server ID not found in create_dir ajax call")
console.warning("Server ID not found in create_dir ajax call")
return False
else:
server_id = bleach.clean(server_id)
# does this server id exist?
if not db_helper.server_id_exists(server_id):
logger.warning("Server ID not found in create_dir ajax call ({})".format(server_id))
console.warning("Server ID not found in create_dir ajax call ({})".format(server_id))
return False
if not self.check_server_id(server_id, 'create_dir'): return False
else: server_id = bleach.clean(server_id)
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], dir_path) \
or helper.check_path_exists(os.path.abspath(dir_path)):
or helper.check_path_exists(os.path.abspath(dir_path)):
logger.warning("Invalid path in create_dir ajax call ({})".format(dir_path))
console.warning("Invalid path in create_dir ajax call ({})".format(dir_path))
return False
@ -219,23 +189,15 @@ class AjaxHandler(BaseHandler):
file_path = self.get_body_argument('file_path', default=None, strip=True)
server_id = self.get_argument('id', None)
if server_id is None:
logger.warning("Server ID not found in del_file ajax call")
console.warning("Server ID not found in del_file ajax call")
return False
else:
server_id = bleach.clean(server_id)
console.warning("delete {} for server {}".format(file_path, server_id))
# does this server id exist?
if not db_helper.server_id_exists(server_id):
logger.warning("Server ID not found in del_file ajax call ({})".format(server_id))
console.warning("Server ID not found in del_file ajax call ({})".format(server_id))
return False
if not self.check_server_id(server_id, 'del_file'): return False
else: server_id = bleach.clean(server_id)
server_info = db_helper.get_server_data_by_id(server_id)
if not helper.in_path(server_info['path'], file_path) \
or not helper.in_path(server_info['backup_path'], file_path) \
or not helper.check_file_exists(os.path.abspath(file_path)):
if not (helper.in_path(server_info['path'], file_path) \
or helper.in_path(server_info['backup_path'], file_path)) \
or not helper.check_file_exists(os.path.abspath(file_path)):
logger.warning("Invalid path in del_file ajax call ({})".format(file_path))
console.warning("Invalid path in del_file ajax call ({})".format(file_path))
return False
@ -248,29 +210,20 @@ class AjaxHandler(BaseHandler):
server_id = self.get_argument('id', None)
print(server_id)
if server_id is None:
logger.warning("Server ID not found in del_file ajax call")
console.warning("Server ID not found in del_file ajax call")
return False
else:
server_id = bleach.clean(server_id)
console.warning("delete {} for server {}".format(file_path, server_id))
# does this server id exist?
if not db_helper.server_id_exists(server_id):
logger.warning("Server ID not found in del_file ajax call ({})".format(server_id))
console.warning("Server ID not found in del_file ajax call ({})".format(server_id))
return False
if not self.check_server_id(server_id, 'del_dir'): return False
else: server_id = bleach.clean(server_id)
server_info = db_helper.get_server_data_by_id(server_id)
if not helper.in_path(server_info['path'], dir_path) \
or not helper.in_path(server_info['backup_path'], dir_path) \
or not helper.check_path_exists(os.path.abspath(dir_path)):
or not helper.check_path_exists(os.path.abspath(dir_path)):
logger.warning("Invalid path in del_file ajax call ({})".format(dir_path))
console.warning("Invalid path in del_file ajax call ({})".format(dir_path))
return False
# Delete the file
# os.rmdir(dir_path)
# Delete the directory
# os.rmdir(dir_path) # Would only remove empty directories
shutil.rmtree(dir_path) # Removes also when there are contents
@tornado.web.authenticated
@ -283,21 +236,11 @@ class AjaxHandler(BaseHandler):
print(file_path)
print(server_id)
if server_id is None:
logger.warning("Server ID not found in save_file ajax call")
console.warning("Server ID not found in save_file ajax call")
return False
else:
server_id = bleach.clean(server_id)
# does this server id exist?
if not db_helper.server_id_exists(server_id):
logger.warning("Server ID not found in save_file ajax call ({})".format(server_id))
console.warning("Server ID not found in save_file ajax call ({})".format(server_id))
return False
if not self.check_server_id(server_id, 'save_file'): return False
else: server_id = bleach.clean(server_id)
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], file_path)\
or not helper.check_file_exists(os.path.abspath(file_path)):
or not helper.check_file_exists(os.path.abspath(file_path)):
logger.warning("Invalid path in save_file ajax call ({})".format(file_path))
console.warning("Invalid path in save_file ajax call ({})".format(file_path))
return False
@ -312,37 +255,41 @@ class AjaxHandler(BaseHandler):
server_id = self.get_argument('id', None)
print(server_id)
if server_id is None:
logger.warning("Server ID not found in rename_item ajax call ({})".format(server_id))
console.warning("Server ID not found in rename_item ajax call ({})".format(server_id))
return False
else:
server_id = bleach.clean(server_id)
# does this server id exist?
if not db_helper.server_id_exists(server_id):
logger.warning("Server ID not found in rename_item ajax call ({})".format(server_id))
console.warning("Server ID not found in rename_item ajax call ({})".format(server_id))
return False
if not self.check_server_id(server_id, 'rename_item'): return False
else: server_id = bleach.clean(server_id)
if item_path is None or new_item_name is None:
logger.warning("Invalid path in rename_item ajax call")
console.warning("Invalid path in rename_item ajax call")
logger.warning("Invalid path(s) in rename_item ajax call")
console.warning("Invalid path(s) in rename_item ajax call")
return False
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], item_path) \
or not helper.check_path_exists(os.path.abspath(item_path)):
logger.warning("Invalid path in rename_item ajax call ({})".format(server_id))
console.warning("Invalid path in rename_item ajax call ({})".format(server_id))
or not helper.check_path_exists(os.path.abspath(item_path)):
logger.warning("Invalid old name path in rename_item ajax call ({})".format(server_id))
console.warning("Invalid old name path in rename_item ajax call ({})".format(server_id))
return False
new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name)
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], new_item_path) \
or helper.check_path_exists(os.path.abspath(new_item_path)):
logger.warning("Invalid path 2 in rename_item ajax call ({})".format(server_id))
console.warning("Invalid path 2 in rename_item ajax call ({})".format(server_id))
or helper.check_path_exists(os.path.abspath(new_item_path)):
logger.warning("Invalid new name path in rename_item ajax call ({})".format(server_id))
console.warning("Invalid new name path in rename_item ajax call ({})".format(server_id))
return False
# RENAME
os.rename(item_path, new_item_path)
def check_server_id(self, server_id, page_name):
if server_id is None:
logger.warning("Server ID not defined in {} ajax call ({})".format(page_name, server_id))
console.warning("Server ID not defined in {} ajax call ({})".format(page_name, server_id))
return False
else:
server_id = bleach.clean(server_id)
# does this server id exist?
if not db_helper.server_id_exists(server_id):
logger.warning("Server ID not found in {} ajax call ({})".format(page_name, server_id))
console.warning("Server ID not found in {} ajax call ({})".format(page_name, server_id))
return False
return True

View File

@ -12,9 +12,10 @@ logger = logging.getLogger(__name__)
class BaseHandler(tornado.web.RequestHandler):
def initialize(self, controller=None, tasks_manager=None):
def initialize(self, controller=None, tasks_manager=None, translator=None):
self.controller = controller
self.tasks_manager = tasks_manager
self.translator = translator
def get_remote_ip(self):
remote_ip = self.request.headers.get("X-Real-IP") or \

View File

@ -11,7 +11,13 @@ class DefaultHandler(BaseHandler):
def prepare(self, page=None):
if page is not None:
self.set_status(404)
self.render("public/404.html")
self.render(
"public/404.html",
translate=self.translator.translate,
)
else:
self.redirect("/public/login")
self.redirect(
"/public/login",
translate=self.translator.translate,
)

View File

@ -399,6 +399,7 @@ class PanelHandler(BaseHandler):
data=page_data,
time=time,
utc_offset=(time.timezone * -1 / 60 / 60),
translate=self.translator.translate,
)
@tornado.web.authenticated
@ -705,4 +706,7 @@ class PanelHandler(BaseHandler):
else:
self.set_status(404)
self.render("public/404.html")
self.render(
"public/404.html",
translate=self.translator.translate,
)

View File

@ -63,7 +63,11 @@ class PublicHandler(BaseHandler):
else:
self.redirect('/public/login')
self.render(template, data=page_data)
self.render(
template,
data=page_data,
translate=self.translator.translate,
)
def post(self, page=None):

View File

@ -69,7 +69,8 @@ class ServerHandler(BaseHandler):
self.render(
template,
data=page_data
data=page_data,
translate=self.translator.translate,
)
@tornado.web.authenticated
@ -197,5 +198,6 @@ class ServerHandler(BaseHandler):
self.render(
template,
data=page_data
data=page_data,
translate=self.translator.translate,
)

View File

@ -25,6 +25,7 @@ try:
from app.classes.web.ajax_handler import AjaxHandler
from app.classes.web.api_handler import ServersStats, NodeStats
from app.classes.web.websocket_handler import SocketHandler
from app.classes.shared.translation import translation
except ModuleNotFoundError as e:
logger.critical("Import Error: Unable to load {} module".format(e, e.name))
@ -120,7 +121,7 @@ class Webserver:
tornado.locale.set_default_locale(lang)
handler_args = {"controller": self.controller, "tasks_manager": self.tasks_manager}
handler_args = {"controller": self.controller, "tasks_manager": self.tasks_manager, "translator": translation}
handlers = [
(r'/', DefaultHandler, handler_args),
(r'/public/(.*)', PublicHandler, handler_args),

View File

@ -8,9 +8,10 @@ from app.classes.web.websocket_helper import websocket_helper
class SocketHandler(tornado.websocket.WebSocketHandler):
def initialize(self, controller=None, tasks_manager=None):
def initialize(self, controller=None, tasks_manager=None, translator=None):
self.controller = controller
self.tasks_manager = tasks_manager
self.translator = translator
def get_remote_ip(self):
remote_ip = self.request.headers.get("X-Real-IP") or \

View File

@ -59,9 +59,9 @@
<div class="main-panel">
<div class="warnings">
<div class="noscript-warning" style="padding: 20px; background-color: rgb(247, 151, 15);">
<div><strong>Warning: </strong>Crafty doesn't work properly when JavaScript isn't enabled!</div>
</div>
<noscript class="noscript-warning" style="padding: 20px; background-color: rgb(247, 151, 15);">
<div>{% raw translate('base', 'doesNotWorkWithoutJavascript') %}</div>
</noscript>
</div>
{% block content %}
@ -138,7 +138,9 @@
<script>
$('.noscript-warning').toggle();
$.extend($.fn.dataTable.defaults, {
language: {% raw translate('datatables', 'i18n') %}
})
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
function getCookie(name) {
@ -146,12 +148,12 @@
return r ? r[1] : undefined;
}
<!-- tool tips -->
// tool tips
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
<!-- Notify-->
// Notify
$(document).ready(function(){
$("#notificationDropdown").click(function(){
$.get("/ajax/announcements", function(data){
@ -165,7 +167,7 @@
});
});
{% if request.protocol == 'https' %}
// {% if request.protocol == 'https' %}
let usingWebSockets = true;
let listenEvents = [];
@ -211,11 +213,11 @@
console.error('Error while making websocket helpers', error);
usingWebSockets = false;
}
{% else %}
// {% else %}
let usingWebSockets = false;
warn('WebSockets are not supported in Crafty if not using the https protocol')
var webSocket;
{% end%}
// {% end%}
function warn(message) {
var closeEl = document.createElement('span');

View File

@ -2,8 +2,8 @@
<footer class="footer">
<div class="container-fluid ">
<span class="text-muted d-block text-center text-sm-left d-sm-inline-block">Copyright © 2021 <a href="http://www.craftycontrol.com/" target="_blank">Crafty Controller</a>. All rights reserved.</span>
<span class="float-none float-sm-right d-block mt-1 mt-sm-0 text-center">Version: {{ data['version_data'] }}
<span class="text-muted d-block text-center text-sm-left d-sm-inline-block">{{ translate('footer', 'copyright') }} © 2021 <a href="http://www.craftycontrol.com/" target="_blank">Crafty Controller</a>. {{ translate('footer', 'allRightsReserved') }}.</span>
<span class="float-none float-sm-right d-block mt-1 mt-sm-0 text-center">{{ translate('footer', 'version') }}: {{ data['version_data'] }}
</span>
</div>

View File

@ -4,25 +4,25 @@
<nav class="sidebar sidebar-offcanvas" id="sidebar">
<ul class="nav">
<li class="nav-item nav-category" style="margin-top:10px;">Main Menu</li>
<li class="nav-item nav-category" style="margin-top:10px;">{{ translate('sidebar', 'navigation') }}</li>
<li class="nav-item">
<a class="nav-link" href="/panel/dashboard">
<i class="fas fa-chart-network"></i>&nbsp;
<span class="menu-title">Dashboard</span>
<span class="menu-title">{{ translate('sidebar', 'dashboard') }}</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="collapse" href="#page-layouts" aria-expanded="false" aria-controls="page-layouts">
<i class="fas fa-server"></i> &nbsp;
<span class="menu-title">Servers</span>
<span class="menu-title">{{ translate('sidebar', 'servers') }}</span>
<i class="menu-arrow"></i>
</a>
<div class="collapse" id="page-layouts">
<ul class="nav flex-column sub-menu">
<li class="nav-item">
<a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> &nbsp; Add New Server</a>
<a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> &nbsp; {{ translate('sidebar', 'newServer') }}</a>
</li>
{% for s in data['menu_servers'] %}
<li class="nav-item">
@ -37,7 +37,7 @@
<li class="nav-item">
<a class="nav-link" href="https://gitlab.com/crafty-controller/crafty-web/-/wikis/home" target="_blank">
<i class="fas fa-book"></i> &nbsp;
<span class="menu-title">Documentation</span>
<span class="menu-title">{{ translate('sidebar', 'documentation') }}</span>
</a>
</li>
@ -51,7 +51,7 @@
<li class="nav-item">
<a class="nav-link" href="/panel/credits">
<i class="fas fa-heart"></i> &nbsp;
<span class="menu-title">Credits</span>
<span class="menu-title">{{ translate('sidebar', 'credits') }}</span>
</a>
</li>
@ -59,20 +59,11 @@
<li class="nav-item">
<a class="nav-link" href="/panel/contribute">
<i class="fas fa-donate"></i> &nbsp;
<span class="menu-title">Contribute</span>
<span class="menu-title">{{ translate('sidebar', 'contribute') }}</span>
</a>
</li>
{% end %}
<!--
<li class="nav-item">
<a class="nav-link" href="/panel/files?id=1">
<i class="fas fa-copy"></i> &nbsp;
<span class="menu-title">Files Test</span>
</a>
</li>-->
</ul>
</nav>
<!-- partial -->

View File

@ -4,7 +4,7 @@
<meta http-equiv="refresh" content="60">
{% end %}
{% block title %}Crafty Controller - Dashboard{% end %}
{% block title %}Crafty Controller - {{ translate('dashboard', 'dashboard') }}{% end %}
{% block content %}
@ -14,7 +14,7 @@
<div class="row page-title-header">
<div class="col-12">
<div class="page-header">
<h4 class="page-title">Dashboard</h4>
<h4 class="page-title">{{ translate('dashboard', 'dashboard') }}</h4>
</div>
</div>
@ -29,16 +29,16 @@
<div class="col-lg-3 col-md-6">
<div class="d-flex">
<div class="wrapper">
<h5 class="mb-1 font-weight-medium text-primary"> Host</h5>
<h5 class="mb-1 font-weight-medium text-primary"> {{ translate('dashboard', 'host') }}</h5>
<h3 class="mb-0 font-weight-semibold"> <i class="fas fa-chart-line"></i></h3>
</div>
<div class="wrapper my-auto ml-auto ml-lg-4">
<p id="cpu_data" class="mb-0 text-success" data-toggle="tooltip" data-placement="top" data-html="true" title="CPU Cores: {{ data.get('hosts_data').get('cpu_cores') }} <br /> CPU Cur Freq: {{ data.get('hosts_data').get('cpu_cur_freq') }} <br /> CPU Max Freq: {{ data.get('hosts_data').get('cpu_max_freq') }}" >
<span id="cpu_usage">{{ data.get('hosts_data').get('cpu_usage') }}</span> {{ _('CPU Usage') }}
<p id="cpu_data" class="mb-0 text-success" data-toggle="tooltip" data-placement="top" data-html="true" title="{% raw translate('dashboard', 'cpuCores') %}: {{ data.get('hosts_data').get('cpu_cores') }} <br /> {% raw translate('dashboard', 'cpuCurFreq') %}: {{ data.get('hosts_data').get('cpu_cur_freq') }} <br /> {% raw translate('dashboard', 'cpuMaxFreq') %}: {{ data.get('hosts_data').get('cpu_max_freq') }}" >
{{ translate('dashboard', 'cpuUsage') }}: <span id="cpu_usage">{{ data.get('hosts_data').get('cpu_usage') }}</span>
</p>
<p id="mem_usage" class="mb-0 text-danger" data-toggle="tooltip" data-placement="top" title="Memory Usage: {{ data.get('hosts_data').get('mem_usage') }}" >
<span id="mem_percent">{{ data.get('hosts_data').get('mem_percent') }}%</span> {{ _('Memory Usage') }}
<p id="mem_usage" class="mb-0 text-danger" data-toggle="tooltip" data-placement="top" title="{{ translate('dashboard', 'memUsage') }}: {{ data.get('hosts_data').get('mem_usage') }}" >
{{ translate('dashboard', 'memUsage') }}: <span id="mem_percent">{{ data.get('hosts_data').get('mem_percent') }}%</span>
</p>
</div>
</div>
@ -46,26 +46,26 @@
<div class="col-lg-3 col-md-6 mt-md-0 mt-4">
<div class="d-flex">
<div class="wrapper">
<h5 class="mb-1 font-weight-medium text-primary">Servers</h5>
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'servers') }}</h5>
<h3 class="mb-0 font-weight-semibold">{{ data['server_stats']['total'] }}</h3>
</div>
<div class="wrapper my-auto ml-auto ml-lg-4">
<p class="mb-0 text-success">{{ data['server_stats']['running'] }} {{_('Online')}}</p>
<p class="mb-0 text-warning"> {{ data['server_stats']['stopped'] }} {{_('Shutdown')}}</p>
<p class="mb-0 text-success">{{ data['server_stats']['running'] }} {{ translate('dashboard', 'online').lower() }}</p>
<p class="mb-0 text-warning"> {{ data['server_stats']['stopped'] }} {{ translate('dashboard', 'offline').lower() }}</p>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6 mt-md-0 mt-4">
<div class="d-flex">
<div class="wrapper">
<h5 class="mb-1 font-weight-medium text-primary">Players</h5>
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'players') }}</h5>
<h3 class="mb-0 font-weight-semibold">18</h3>
</div>
<div class="wrapper my-auto ml-auto ml-lg-4">
<p class="mb-0 text-success">35 Max</p>
<p class="mb-0 text-warning">10 Avg</p>
<p class="mb-0 text-success">35 {{ translate('dashboard', 'max') }}</p>
<p class="mb-0 text-warning">10 {{ translate('dashboard', 'avg') }}</p>
</div>
</div>
</div>
@ -73,12 +73,12 @@
<div class="d-flex">
<div class="wrapper">
<h5 class="mb-1 font-weight-medium text-primary">Backups</h5>
<h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'backups') }}</h5>
<h3 class="mb-0 font-weight-semibold">7</h3>
</div>
<div class="wrapper my-auto ml-auto ml-lg-4">
<p class="mb-0 text-success">Last: 11-31-2020</p>
<p class="mb-0 text-success">Next: 12-05-2020</p>
<p class="mb-0 text-success">{{ translate('dashboard', 'lastBackup') }} 11-31-2020</p>
<p class="mb-0 text-success">{{ translate('dashboard', 'nextBackup') }} 12-05-2020</p>
</div>
</div>
</div>
@ -92,11 +92,11 @@
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-server"></i> &nbsp;All Servers</h4>
<h4 class="card-title"><i class="fas fa-server"></i> &nbsp;{{ translate('dashboard', 'allServers') }}</h4>
<div class="d-md-none">
<small>Can't see everything on mobile?<br /> Try scrolling the table sideways.</small>
<small>{{ translate('dashboard', 'cannotSeeOnMobile') }}<br /> {{ translate('dashboard', 'cannotSeeOnMobile2') }}</small>
</div>
<div><a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> &nbsp; Add New Server</a></div>
<div><a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> &nbsp; {{ translate('dashboard', 'newServer') }}</a></div>
</div>
<div class="card-body">
@ -104,13 +104,13 @@
<table class="table">
<thead>
<tr class="rounded">
<th>Server</th>
<th>Actions</th>
<th>CPU</th>
<th>Memory</th>
<th>World</th>
<th>Players</th>
<th>Status</th>
<th>{{ translate('dashboard', 'server') }}</th>
<th>{{ translate('dashboard', 'actions') }}</th>
<th>{{ translate('dashboard', 'cpuUsage') }}</th>
<th>{{ translate('dashboard', 'memUsage') }}</th>
<th>{{ translate('dashboard', 'world') }}</th>
<th>{{ translate('dashboard', 'players') }}</th>
<th>{{ translate('dashboard', 'status') }}</th>
</tr>
</thead>
<tbody>
@ -175,7 +175,7 @@
</td>
<td>
{% if server['stats']['int_ping_results'] %}
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} Max<br />
{{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', 'max') }}<br />
{% if server['stats']['desc'] != 'False' %}
{{ server['stats']['desc'] }} <br />
@ -189,9 +189,9 @@
</td>
<td>
{% if server['stats']['running'] %}
<i class="fas fa-thumbs-up"></i> <span class="text-success">Online</span>
<i class="fas fa-thumbs-up"></i> <span class="text-success">{{ translate('dashboard', 'online') }}</span>
{% else %}
<i class="fas fa-thumbs-down"></i> <span class="text-danger">Offline</span>
<i class="fas fa-thumbs-down"></i> <span class="text-danger">{{ translate('dashboard', 'offline') }}</span>
{% end %}
</td>
</tr>
@ -242,8 +242,8 @@ $( document ).ready(function() {
send_command(server_id, 'start_server');
bootbox.alert({
backdrop: true,
title: "Sending your command",
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; Please be patient while we start the server<br /> This screen will refresh in a moment </div>'
title: '{% raw translate("dashboard", "sendingCommand") %}',
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; {% raw translate("dashboard", "bePatientStart") %} </div>'
});
});
@ -253,8 +253,8 @@ $( document ).ready(function() {
send_command(server_id, 'stop_server');
bootbox.alert({
backdrop: true,
title: "Sending your command",
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; Please be patient while we stop the server<br /> This screen will refresh in a moment </div>'
title: '{% raw translate("dashboard", "sendingCommand") %}',
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; {% raw translate("dashboard", "bePatientStop") %} </div>'
});
});
@ -263,8 +263,8 @@ $( document ).ready(function() {
send_command(server_id, 'restart_server');
bootbox.alert({
backdrop: true,
title: "Sending your command",
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; Please be patient while we restart the server<br /> This screen will refresh in a moment </div>'
title: '{% raw translate("dashboard", "sendingCommand") %}',
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; {% raw translate("dashboard", "bePatientRestart") %} </div>'
});
});
if (webSocket) {
@ -274,10 +274,10 @@ $( document ).ready(function() {
mem_percent = document.getElementById('mem_percent');
webSocket.on('update_host_stats', function (hostStats) {
var cpuDataTitle = `CPU Cores: ${hostStats.cpu_cores} <br /> CPU Cur Freq: ${hostStats.cpu_cur_freq} <br /> CPU Max Freq: ${hostStats.cpu_max_freq}`;
var cpuDataTitle = `{% raw translate('dashboard', 'cpuCores') %}: ${hostStats.cpu_cores} <br /> {% raw translate("dashboard", "cpuCurFreq") %}: ${hostStats.cpu_cur_freq} <br /> {% raw translate("dashboard", "cpuMaxFreq") %}: ${hostStats.cpu_max_freq}`;
cpu_data.setAttribute('data-original-title', cpuDataTitle);
cpu_usage.textContent = hostStats.cpu_usage;
mem_usage.setAttribute('data-original-title', `Memory Usage: ${hostStats.mem_usage}`);
mem_usage.setAttribute('data-original-title', `{% raw translate("dashboard", "memUsage") %}: ${hostStats.mem_usage}`);
mem_percent.textContent = hostStats.mem_percent + '%';
});
}
@ -287,8 +287,8 @@ $( document ).ready(function() {
send_command(server_id, 'clone_server');
bootbox.alert({
backdrop: true,
title: "Sending your command",
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; Please be patient while we clone the server<br /> This screen will refresh in a moment </div>'
title: '{% raw translate("dashboard", "sendingCommand") %}',
message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> &nbsp; {% raw translate("dashboard", "bePatientClone") %} </div>'
});
});

View File

@ -32,12 +32,11 @@
<div class="col-sm-12 grid-margin stretch-card">
<div class="card card-statistics social-card google-card card-colored">
<div class="card-body">
<h4 class="platform-name mb-3 mt-4 font-weight-semibold user-name">Access Denied</h4>
<h5 class="headline font-weight-medium">You do not have access to this resource</h5>
<h4 class="platform-name mb-3 mt-4 font-weight-semibold user-name">{{ translate('accessDenied', 'accessDenied') }}</h4>
<h5 class="headline font-weight-medium">{{ translate('accessDenied', 'noAccess') }}</h5>
<p class="mb-2 comment font-weight-light">
Contact your server administrator for access to this resource, or if you think you should have access
to this resource, contact support.<br /><br />
<a class="d-inline font-weight-medium" href="https://discord.gg/9VJPhCE"> Contact Crafty Control Support via Discord</a>
{{ translate('accessDenied', 'contactAdmin') }}<br /><br />
<a class="d-inline font-weight-medium" href="https://discord.gg/9VJPhCE"> {{ translate('accessDenied', 'contact') }}</a>
</p>
</div>
</div>

View File

@ -5,33 +5,33 @@
<div class="row">
<div class="col-sm-3 mr-2">
{% if data['server_stats']['running'] %}
<b>Server Status:</b> <span class="text-success">Online</span><br />
<b>Server Started:</b> <span id="started">{{ data['server_stats']['started'] }} (Server Time)</span><br />
<b>Server Uptime:</b> <span id="uptime">Error Calculating</span>
<b>{{ translate('serverStats', 'serverStatus') }}:</b> <span class="text-success">{{ translate('serverStats', 'online') }}</span><br />
<b>{{ translate('serverStats', 'serverStarted') }}:</b> <span id="started">{{ data['server_stats']['started'] }} ({{ translate('serverStats', 'serverTime') }})</span><br />
<b>{{ translate('serverStats', 'serverUptime') }}:</b> <span id="uptime">{{ translate('serverStats', 'errorCalculatingUptime') }}</span>
{% else %}
<b>Server Status:</b> <span class="text-danger">Offline</span><br />
<b>Server Started:</b> <span class="text-danger">Offline</span><br />
<b>Server Uptime:</b> <span class="text-danger">Offline</span>
<b>{{ translate('serverStats', 'serverStatus') }}:</b> <span class="text-danger">{{ translate('serverStats', 'offline') }}</span><br />
<b>{{ translate('serverStats', 'serverStarted') }}:</b> <span class="text-danger">{{ translate('serverStats', 'offline') }}</span><br />
<b>{{ translate('serverStats', 'serverUptime') }}:</b> <span class="text-danger">{{ translate('serverStats', 'offline') }}</span>
{% end %}
</div>
<div class="col-sm-3 mr-2">
<b>CPU:</b> {{ data['server_stats']['cpu'] }}% <br />
<b>Mem:</b> {{ data['server_stats']['mem'] }} <br />
<b>{{ translate('serverStats', 'cpuUsage') }}:</b> {{ data['server_stats']['cpu'] }}% <br />
<b>{{ translate('serverStats', 'memUsage') }}:</b> {{ data['server_stats']['mem'] }} <br />
{% if data['server_stats']['int_ping_results'] %}
<b>Players:</b> {{ data['server_stats']['online'] }} / {{ data['server_stats']['max'] }}<br />
<b>{{ translate('serverStats', 'players') }}:</b> {{ data['server_stats']['online'] }} / {{ data['server_stats']['max'] }}<br />
{% else %}
<b>Players:</b> 0/0<br />
<b>{{ translate('serverStats', 'players') }}:</b> 0/0<br />
{% end %}
</div>
<div class="col-sm-3 mr-2">
{% if data['server_stats']['version'] != 'False' %}
<b>Server:</b> {{ data['server_stats']['version'] }} <br />
<b>Desc:</b> {{ data['server_stats']['desc'] }} <br />
<b>{{ translate('serverStats', 'version') }}:</b> {{ data['server_stats']['version'] }} <br />
<b>{{ translate('serverStats', 'description') }}:</b> {{ data['server_stats']['desc'] }} <br />
{% else %}
<b>Server:</b> Unable To Connect <br />
<b>Desc:</b> Unable To Connect <br />
<b>{{ translate('serverStats', 'version') }}:</b> {{ translate('serverStats', 'unableToConnect') }} <br />
<b>{{ translate('serverStats', 'description') }}:</b> {{ translate('serverStats', 'unableToConnect') }} <br />
{% end %}
</div>

View File

@ -4,7 +4,7 @@
<!-- <meta http-equiv="refresh" content="60">-->
{% end %}
{% block title %}Crafty Controller - Server Details{% end %}
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails') }}{% end %}
{% block content %}
@ -15,7 +15,7 @@
<div class="col-12">
<div class="page-header">
<h4 class="page-title">
Server Details - {{ data['server_stats']['server_id']['server_name'] }}
{{ translate('serverDetails', 'serverDetails') }} - {{ data['server_stats']['server_id']['server_name'] }}
<br />
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4>
@ -35,31 +35,31 @@
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Terminal</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'terminal') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Logs</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'logs') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a>
<i class="fas fa-clock"></i>{{ translate('serverDetails', 'schedule') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
<i class="fas fa-save"></i>Backup</a>
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a>
<i class="fas fa-folder-tree"></i>{{ translate('serverDetails', 'files') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true">
<i class="fas fa-cogs"></i>Config</a>
<i class="fas fa-cogs"></i>{{ translate('serverDetails', 'config') }}</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true">
<i class="fas fa-users"></i>Player Controls</a>
<i class="fas fa-users"></i>{{ translate('serverDetails', 'playerControls') }}</a>
</li>
</ul>
@ -95,7 +95,7 @@
font-size: 1.1rem;
}
</style>
<h2>Players:</h2>
<h2>{{ translate('serverPlayerManagement', 'players') }}:</h2>
<ul style="list-style: none;padding: 0px;margin: 0px; margin-bottom: 1rem;gap: 1rem;">
{% for player in data['get_players']() %}
<li class="playerItem">
@ -111,10 +111,10 @@
</ul>
</div>
<div class="col-md-6 col-sm-12">
<h2>Banned Players:</h2>
<h2>{{ translate('serverPlayerManagement', 'bannedPlayers') }}:</h2>
<ul id="bannedPlayers" style="list-style: none;padding: 0px;margin: 0px; margin-bottom: 1rem;gap: 1rem;">
<li class="playerItem banned">
<h3>Loading Banned Players</h3>
<h3>{{ translate('serverPlayerManagement', 'loadingBannedPlayers') }}</h3>
</li>
</ul>

View File

@ -4,7 +4,7 @@
<!-- <meta http-equiv="refresh" content="60">-->
{% end %}
{% block title %}Crafty Controller - Server Details{% end %}
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails') }}{% end %}
{% block content %}
@ -15,7 +15,7 @@
<div class="col-12">
<div class="page-header">
<h4 class="page-title">
Server Details - {{ data['server_stats']['server_id']['server_name'] }}
{{ translate('serverDetails', 'serverDetails') }} - {{ data['server_stats']['server_id']['server_name'] }}
<br />
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4>
@ -35,31 +35,31 @@
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Terminal</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'terminal') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Logs</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'logs') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a>
<i class="fas fa-clock"></i>{{ translate('serverDetails', 'schedule') }}</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="true">
<i class="fas fa-save"></i>Backup</a>
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a>
<i class="fas fa-folder-tree"></i>{{ translate('serverDetails', 'files') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="false">
<i class="fas fa-cogs"></i>Config</a>
<i class="fas fa-cogs"></i>{{ translate('serverDetails', 'config') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="false">
<i class="fas fa-users"></i>Player Controls</a>
<i class="fas fa-users"></i>{{ translate('serverDetails', 'playerControls') }}</a>
</li>
</ul>
@ -72,37 +72,37 @@
<input type="hidden" name="subpage" value="backup">
<div class="form-group">
<a href="/panel/backup_now?id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary" onclick="backup_started()">Backup Now!</a>
<a href="/panel/backup_now?id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary" onclick="backup_started()">{{ translate('serverBackups', 'backupNow') }}</a>
</div>
<div class="form-group">
<label for="server_name">Storage Location <small class="text-muted ml-1"> - Where do you want to store backups?</small> </label>
<input type="text" class="form-control" name="backup_path" id="backup_path" value="{{ data['server_stats']['server_id']['backup_path'] }}" placeholder="Backup Path" >
<label for="server_name">{{ translate('serverBackups', 'storageLocation') }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'storageLocationDesc') }}</small> </label>
<input type="text" class="form-control" name="backup_path" id="backup_path" value="{{ data['server_stats']['server_id']['backup_path'] }}" placeholder="{{ translate('serverBackups', 'storageLocation') }}" >
</div>
<div class="form-group">
<label for="server_path">Max Backups <small class="text-muted ml-1"> - Crafty will not store more than n backups, deleting the oldest (enter 0 to keep all)</small> </label>
<input type="text" class="form-control" name="max_backups" id="max_backups" value="{{ data['backup_config']['max_backups'] }}" placeholder="Max Backups" >
<label for="server_path">{{ translate('serverBackups', 'maxBackups') }} <small class="text-muted ml-1"> - {{ translate('serverBackups', 'maxBackupsDesc') }}</small> </label>
<input type="text" class="form-control" name="max_backups" id="max_backups" value="{{ data['backup_config']['max_backups'] }}" placeholder="{{ translate('serverBackups', 'maxBackups') }}" >
</div>
<div class="form-group">
<label for="superuser" class="form-check-label ml-4 mb-4">
{% if data['backup_config']['auto_enabled'] %}
<input type="checkbox" class="form-check-input" id="auto_enabled" name="auto_enabled" checked="" value="1" >Auto-backup at 12:00 AM?
<input type="checkbox" class="form-check-input" id="auto_enabled" name="auto_enabled" checked="" value="1" >{{ translate('serverBackups', 'backupAtMidnight') }}
{% else %}
<input type="checkbox" class="form-check-input" id="auto_enabled" name="auto_enabled" value="1" >Auto-backup at 12:00 AM?
<input type="checkbox" class="form-check-input" id="auto_enabled" name="auto_enabled" value="1" >{{ translate('serverBackups', 'backupAtMidnight') }}
{% end %}
</label>
</div>
<button type="submit" class="btn btn-success mr-2">Save</button>
<button type="reset" class="btn btn-light">Cancel</button>
<button type="submit" class="btn btn-success mr-2">{{ translate('serverBackups', 'save') }}</button>
<button type="reset" class="btn btn-light">{{ translate('serverBackups', 'cancel') }}</button>
</form>
</div>
<div class="col-md-6 col-sm-12">
<div class="card">
<div class="card-body">
<h4 class="card-title">Current Backups</h4>
<h4 class="card-title">{{ translate('serverBackups', 'currentBackups') }}</h4>
</div>
</div>
@ -111,10 +111,10 @@
<table class="table table-responsive dataTable" id="backup_table">
<thead>
<tr>
<th width="10%">Download</th>
<th>Path</th>
<th width="20%">Size</th>
<th width="10%">Delete</th>
<th width="10%">{{ translate('serverBackups', 'download') }}</th>
<th>{{ translate('serverBackups', 'path') }}</th>
<th width="20%">{{ translate('serverBackups', 'size') }}</th>
<th width="10%">{{ translate('serverBackups', 'delete') }}</th>
</tr>
</thead>
<tbody>
@ -123,7 +123,7 @@
<td>
<a href="/panel/download_backup?file={{ backup['path'] }}&id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-primary">
<i class="fas fa-download" aria-hidden="true"></i>
Download
{{ translate('serverBackups', 'download') }}
</a>
</td>
<td>{{ backup['path'] }}</td>
@ -131,7 +131,7 @@
<td>
<button data-file="{{ backup['path'] }}" class="btn btn-danger del_button">
<i class="fas fa-trash" aria-hidden="true"></i>
Delete
{{ translate('serverBackups', 'delete') }}
</button>
</td>
@ -169,7 +169,7 @@
function backup_started(time='5-10') {
bootbox.alert({
message: "A backup task has been started.",
message: "{{ translate('serverBackups', 'backupTask') }}",
backdrop: true
});
}
@ -179,13 +179,13 @@
data_to_send = { file_name :filename}
console.log('Sending Command to delete file: ' + filename)
console.log('Sending Command to delete backup: ' + filename)
$.ajax({
type: "POST",
headers: {'X-XSRFToken': token},
url: '/ajax/del_file?server_id='+id,
data: data_to_send,
success: function(data){
success: function(data) {
location.reload();
},
});
@ -220,14 +220,14 @@
console.log("file to delete is" + file_to_del);
bootbox.confirm({
title: "Destroy backup " + file_to_del + "?",
message: "Do you want to delete this file? This cannot be undone.",
title: "{% raw translate('serverBackups', 'destroyBackup') %}",
message: "{{ translate('serverBackups', 'confirmDelete') }}",
buttons: {
cancel: {
label: '<i class="fas fa-times"></i> Cancel'
label: '<i class="fas fa-times"></i> {{ translate("serverBackups", "cancel") }}'
},
confirm: {
label: '<i class="fas fa-check"></i> Confirm'
label: '<i class="fas fa-check"></i> {{ translate("serverBackups", "confirm") }}'
}
},
callback: function (result) {

View File

@ -4,7 +4,7 @@
<!-- <meta http-equiv="refresh" content="60">-->
{% end %}
{% block title %}Crafty Controller - Server Details{% end %}
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails') }}{% end %}
{% block content %}
@ -15,7 +15,7 @@
<div class="col-12">
<div class="page-header">
<h4 class="page-title">
Server Details - {{ data['server_stats']['server_id']['server_name'] }}
{{ translate('serverDetails', 'serverDetails') }} - {{ data['server_stats']['server_id']['server_name'] }}
<br />
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4>
@ -35,31 +35,31 @@
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Terminal</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'terminal') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Logs</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'logs') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a>
<i class="fas fa-clock"></i>{{ translate('serverDetails', 'schedule') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
<i class="fas fa-save"></i>Backup</a>
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a>
<i class="fas fa-folder-tree"></i>{{ translate('serverDetails', 'files') }}</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true">
<i class="fas fa-cogs"></i>Config</a>
<i class="fas fa-cogs"></i>{{ translate('serverDetails', 'config') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true">
<i class="fas fa-users"></i>Player Controls</a>
<i class="fas fa-users"></i>{{ translate('serverDetails', 'playerControls') }}</a>
</li>
</ul>
@ -72,109 +72,97 @@
<input type="hidden" name="subpage" value="config">
<div class="form-group">
<label for="server_name">Server Name <small class="text-muted ml-1"> - What you wish to call this server</small> </label>
<input type="text" class="form-control" name="server_name" id="server_name" value="{{ data['server_stats']['server_id']['server_name'] }}" placeholder="Server Name" >
<label for="server_name">{{ translate('serverConfig', 'serverName') }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverNameDesc') }}</small> </label>
<input type="text" class="form-control" name="server_name" id="server_name" value="{{ data['server_stats']['server_id']['server_name'] }}" placeholder="{{ translate('serverConfig', 'serverName') }}" >
</div>
<div class="form-group">
<label for="server_path">Server Path <small class="text-muted ml-1"> - Absolute full path (not including executable)</small> </label>
<input type="text" class="form-control" name="server_path" id="server_path" value="{{ data['server_stats']['server_id']['path'] }}" placeholder="Server Path" >
<label for="server_path">{{ translate('serverConfig', 'serverPath') }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPathDesc') }}</small> </label>
<input type="text" class="form-control" name="server_path" id="server_path" value="{{ data['server_stats']['server_id']['path'] }}" placeholder="{{ translate('serverConfig', 'serverPath') }}" >
</div>
<div class="form-group">
<label for="log_path">Server Log Location <small class="text-muted ml-1"> - Absolute full path to the log file</small> </label>
<input type="text" class="form-control" name="log_path" id="log_path" value="{{ data['server_stats']['server_id']['log_path'] }}" placeholder="Server Log" >
<label for="log_path">{{ translate('serverConfig', 'serverLogLocation') }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverLogLocationDesc') }}</small> </label>
<input type="text" class="form-control" name="log_path" id="log_path" value="{{ data['server_stats']['server_id']['log_path'] }}" placeholder="{{ translate('serverConfig', 'serverLogLocation') }}" >
</div>
<div class="form-group">
<label for="executable">Server Executable <small class="text-muted ml-1"> - Just the executable file</small> </label>
<input type="text" class="form-control" name="executable" id="executable" value="{{ data['server_stats']['server_id']['executable'] }}" placeholder="Server Executable" >
<label for="executable">{{ translate('serverConfig', 'serverExecutable') }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverExecutableDesc') }}</small> </label>
<input type="text" class="form-control" name="executable" id="executable" value="{{ data['server_stats']['server_id']['executable'] }}" placeholder="{{ translate('serverConfig', 'serverExecutable') }}" >
</div>
<div class="form-group">
<label for="execution_command">Server Execution Command <small class="text-muted ml-1"> - What will be launched in a hidden terminal</small> </label>
<input type="text" class="form-control" name="execution_command" id="execution_command" value="{{ data['server_stats']['server_id']['execution_command'] }}" placeholder="Server Execution Command" >
<label for="execution_command">{{ translate('serverConfig', 'serverExecutionCommand') }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverExecutionCommandDesc') }}</small> </label>
<input type="text" class="form-control" name="execution_command" id="execution_command" value="{{ data['server_stats']['server_id']['execution_command'] }}" placeholder="{{ translate('serverConfig', 'serverExecutionCommand') }}" >
</div>
<div class="form-group">
<label for="stop_command">Server Stop Command <small class="text-muted ml-1"> - Command to send the program to stop it</small> </label>
<input type="text" class="form-control" name="stop_command" id="stop_command" value="{{ data['server_stats']['server_id']['stop_command'] }}" placeholder="Server Stop Command" >
<label for="stop_command">{{ translate('serverConfig', 'serverStopCommand') }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverStopCommandDesc') }}</small> </label>
<input type="text" class="form-control" name="stop_command" id="stop_command" value="{{ data['server_stats']['server_id']['stop_command'] }}" placeholder="{{ translate('serverConfig', 'serverStopCommand') }}" >
</div>
<div class="form-group">
<label for="auto_start_delay">Server Autostart Delay <small class="text-muted ml-1"> - Delay before auto starting (if enabled below)</small> </label>
<label for="auto_start_delay">{{ translate('serverConfig', 'serverAutostartDelay') }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverAutostartDelayDesc') }}</small> </label>
<input type="number" class="form-control" name="auto_start_delay" id="auto_start_delay" value="{{ data['server_stats']['server_id']['auto_start_delay'] }}" step="1" max="999" min="10" >
</div>
<div class="form-group">
<label for="server_ip">Server IP <small class="text-muted ml-1"> - IP Crafty should connect to for stats (Try a real ip instead of 127.0.0.1 if you have issues)</small> </label>
<label for="server_ip">{{ translate('serverConfig', 'serverPort') }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverPortDesc') }}</small> </label>
<input type="text" class="form-control" name="server_ip" id="server_ip" value="{{ data['server_stats']['server_id']['server_ip'] }}">
</div>
<div class="form-group">
<label for="server_port">Server Port <small class="text-muted ml-1"> - Port Crafty should connect to for stats</small> </label>
<label for="server_port">{{ translate('serverConfig', 'serverIP') }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'serverIPDesc') }}</small> </label>
<input type="number" class="form-control" name="server_port" id="server_port" value="{{ data['server_stats']['server_id']['server_port'] }}" step="1" max="65566" min="1" >
</div>
<div class="form-group">
<label for="logs_delete_after">Remove Old Logs After <small class="text-muted ml-1"> - How many days will a log file has to be old to get deleted (0 is off)</small> </label>
<label for="logs_delete_after">{{ translate('serverConfig', 'removeOldLogsAfter') }} <small class="text-muted ml-1"> - {{ translate('serverConfig', 'removeOldLogsAfterDesc') }}</small> </label>
<input type="number" class="form-control" name="logs_delete_after" id="logs_delete_after" value="{{ data['server_stats']['server_id']['logs_delete_after'] }}" step="1" max="365" min="0" >
</div>
<div class="form-check-flat">
<label for="auto_start" class="form-check-label ml-4 mb-4">
{% if data['server_stats']['server_id']['auto_start'] %}
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" checked="" value="1">Server Auto Start
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" checked="" value="1">{{ translate('serverConfig', 'serverAutoStart') }}
{% else %}
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" value="1">Server Auto Start
<input type="checkbox" class="form-check-input" id="auto_start" name="auto_start" value="1">{{ translate('serverConfig', 'serverAutoStart') }}
{% end %}
</label>
<label for="crash_detection" class="form-check-label ml-4 mb-4">
{% if data['server_stats']['server_id']['crash_detection'] %}
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection" checked="" value="1">Server Crash Detection
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection" checked="" value="1">{{ translate('serverConfig', 'serverCrashDetection') }}
{% else %}
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection" value="1" >Server Crash Detection
<input type="checkbox" class="form-check-input" id="crash_detection" name="crash_detection" value="1" >{{ translate('serverConfig', 'serverCrashDetection') }}
{% end %}
</label>
</div>
<button type="submit" class="btn btn-success mr-2">Save</button>
<button type="reset" class="btn btn-light">Cancel</button>
<button type="submit" class="btn btn-success mr-2">{{ translate('serverConfig', 'save') }}</button>
<button type="reset" class="btn btn-light">{{ translate('serverConfig', 'cancel') }}</button>
</form>
</div>
<div class="col-md-6 col-sm-12">
<div class="card">
<div class="card-body">
<h4 class="card-title">Server Config Area</h4>
<p class="card-description"> Here is where you can change the configuration of your server</p>
<h4 class="card-title">{{ translate('serverConfigHelp', 'title') }}</h4>
<p class="card-description"> {{ translate('serverConfigHelp', 'desc') }}</p>
<blockquote class="blockquote">
<p class="mb-0">
It is recommended to <code>NOT</code> change the paths of a server managed by Crafty.
Changing paths <code>CAN</code> break things, especially on Linux type operating systems where
file permissions are more locked down.
<br /><br/>
If you feel you have to change a where a server is located
you may do so as long as you give the "Crafty" user permission to read / write to the server path.
<br />
<br />
On Linux this is best done by executing the following:<br />
<code>
sudo chown crafty:crafty /path/to/your/server -R<br />
sudo chmod 2775 /path/to/your/server -R<br />
</code>
{% raw translate('serverConfigHelp', 'perms') %}
</p>
</blockquote>
</div>
</div>
<div class="text-center">
{% if data['server_stats']['running'] %}
<a class="btn btn-sm btn-danger disabled">Delete Server</a><br />
<small>Please stop the server before deleting it</small>
<a class="btn btn-sm btn-danger disabled">{{ translate('serverConfig', 'deleteServer') }}</a><br />
<small>{{ translate('serverConfig', 'stopBeforeDeleting') }}</small>
{% else %}
<a href="/panel/remove_server?id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-sm btn-danger">Delete Server</a>
<a href="/panel/remove_server?id={{ data['server_stats']['server_id']['server_id'] }}" class="btn btn-sm btn-danger">{{ translate('serverConfig', 'deleteServer') }}</a>
{% end %}
</div>

View File

@ -4,7 +4,7 @@
<!-- <meta http-equiv="refresh" content="60">-->
{% end %}
{% block title %}Crafty Controller - Server Details{% end %}
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails') }}{% end %}
{% block content %}
@ -15,7 +15,7 @@
<div class="col-12">
<div class="page-header">
<h4 class="page-title">
Server Details - {{ data['server_stats']['server_id']['server_name'] }}
{{ translate('serverDetails', 'serverDetails') }} - {{ data['server_stats']['server_id']['server_name'] }}
<br />
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4>
@ -35,31 +35,31 @@
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Terminal</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'terminal') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Logs</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'logs') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a>
<i class="fas fa-clock"></i>{{ translate('serverDetails', 'schedule') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
<i class="fas fa-save"></i>Backup</a>
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup') }}</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a>
<i class="fas fa-folder-tree"></i>{{ translate('serverDetails', 'files') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true">
<i class="fas fa-cogs"></i>Config</a>
<i class="fas fa-cogs"></i>{{ translate('serverDetails', 'config') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true">
<i class="fas fa-users"></i>Player Controls</a>
<i class="fas fa-users"></i>{{ translate('serverDetails', 'playerControls') }}</a>
</li>
</ul>
@ -67,7 +67,7 @@
<div class="row">
<div class="col-md-6 col-sm-12">
<noscript>
The file manager does not work without JavaScript
{{ translate('serverFiles', 'noscript') }}
</noscript>
<div id="files-tree-nav" class="overlay">
@ -76,11 +76,11 @@
<!-- Overlay content -->
<div id="files-tree-nav-content" class="overlay-content">
<a onclick="createFileE(event)" href="javascript:void(0)" id="createFile" href="#">Create file</a>
<a onclick="createDirE(event)" href="javascript:void(0)" id="createDir" href="#">Create directory</a>
<a onclick="renameItemE(event)" href="javascript:void(0)" id="renameItem" href="#">Rename</a>
<a onclick="deleteFileE(event)" href="javascript:void(0)" id="deleteFile" href="#">Delete</a>
<a onclick="deleteDirE(event)" href="javascript:void(0)" id="deleteDir" href="#">Delete</a>
<a onclick="createFileE(event)" href="javascript:void(0)" id="createFile" href="#">{{ translate('serverFiles', 'createFile') }}</a>
<a onclick="createDirE(event)" href="javascript:void(0)" id="createDir" href="#">{{ translate('serverFiles', 'createDir') }}</a>
<a onclick="renameItemE(event)" href="javascript:void(0)" id="renameItem" href="#">{{ translate('serverFiles', 'rename') }}</a>
<a onclick="deleteFileE(event)" href="javascript:void(0)" id="deleteFile" href="#">{{ translate('serverFiles', 'delete') }}</a>
<a onclick="deleteDirE(event)" href="javascript:void(0)" id="deleteDir" href="#">{{ translate('serverFiles', 'delete') }}</a>
</div>
</div>
@ -147,10 +147,10 @@
<div class="tree-caret tree-ctx-item files-tree-title">
<i class="far fa-folder"></i>
<i class="far fa-folder-open"></i>
Files
{{ translate('serverFiles', 'files') }}
</div>
<ul class="tree-nested" id="files-tree">
<li>Error while getting files</li>
<li>{{ translate('serverFiles', 'error') }}</li>
</ul>
</li>
@ -194,16 +194,20 @@
}
</style>
<div class="col-md-6 col-sm-12">
Editing file <span id="editingFile"></span>
<div id="editor" style="resize: both;">file_contents</div>
<div id="editorParent">
{{ translate('serverFiles', 'editingFile') }} <span id="editingFile"></span>
<div id="editor" onresize="editor.resize()" style="resize: both;width: 100%;">file_contents</div>
<br/>
</div>
{{ translate('serverFiles', 'keybindings') }}:
<div class="btn-group" role="group">
<button onclick="setKeyboard(event.target)" class="btn btn-primary" data-handler-name="null">Default</button>
<button onclick="setKeyboard(event.target)" class="btn btn-primary" data-handler-name="null">{{ translate('serverFiles', 'default') }}</button>
<button onclick="setKeyboard(event.target)" class="btn btn-secondary" data-handler-name="ace/keyboard/vim">Vim</button>
<button onclick="setKeyboard(event.target)" class="btn btn-secondary" data-handler-name="ace/keyboard/emacs">Emacs</button>
<button onclick="setKeyboard(event.target)" class="btn btn-secondary" data-handler-name="ace/keyboard/sublime">Sublime</button>
</div>
<h3 id="file_warn"></h3>
<button class="btn btn-success" onclick="save()">Save</button>
<button class="btn btn-success" onclick="save()">{{ translate('serverFiles', 'save') }}</button>
</div>
</div>
@ -278,6 +282,10 @@
regex: /^cpp$/,
replaceWith: 'ace/mode/c_cpp'
},
{
regex: /^c$/,
replaceWith: 'ace/mode/c_cpp'
},
{
regex: /^css$/,
replaceWith: 'ace/mode/css'
@ -317,23 +325,32 @@
{
regex: /^ts$/,
replaceWith: 'ace/mode/typescript'
}
},
{
regex: /^properties$/,
replaceWith: 'ace/mode/properties'
},
];
var editorEnabled = false;
var filePath = '';
function clickOnFile(event) {
editorEnabled = true;
filePath = event.target.getAttribute('data-path');
setFileName(event.target.innerText);
$.ajax({
type: 'GET',
url: '/ajax/get_file?id={{ data['server_stats']['server_id']['server_id'] }}&file_path=' + encodeURIComponent(filePath),
dataType: 'text',
success: function (data) {
console.log('Got File Contents From Server');
editor.session.setValue(data);
json = JSON.parse(data)
if (json.error) {
$('#editorParent').toggle(false)
editor.blur()
} else {
$('#editorParent').toggle(true)
setFileName(event.target.innerText);
editor.session.setValue(json.content);
}
},
});
}
@ -354,11 +371,13 @@
setMode('txt');
document
.querySelector('#file_warn')
.innerText = 'Warning: This is not a supported language';
.innerText = "{% raw translate('serverFiles', 'unsupportedLanguage') %}";
}
}
setFileName();
$('#editorParent').toggle(false)
editor.blur()
function setMode (extension) {
// if the extension matches with the RegEx it will return the replaceWith
@ -373,7 +392,7 @@
if (!aceMode.startsWith('ace/mode/')) {
document
.querySelector('#file_warn')
.innerText = 'Warning: This is not a supported language';
.innerText = "{% raw translate('serverFiles', 'unsupportedLanguage') %}";
} else {
document
.querySelector('#file_warn')
@ -574,7 +593,7 @@
function createFileE(event) {
bootbox.prompt('What name do you want for the new file?', function(result) {
bootbox.prompt("{% raw translate('serverFiles', 'createFileQuestion') %}", function(result) {
path = event.target.parentElement.getAttribute('data-path');
name = event.target.parentElement.getAttribute('data-name');
if (!result) return;
@ -587,7 +606,7 @@
}
function createDirE(event) {
bootbox.prompt('What name do you want for the new directory?', function(result) {
bootbox.prompt("{% raw translate('serverFiles', 'createDirQuestion') %}", function(result) {
path = event.target.parentElement.getAttribute('data-path');
name = event.target.parentElement.getAttribute('data-name');
if (!result) return;
@ -600,7 +619,7 @@
}
function renameItemE(event) {
bootbox.prompt('What should the new name be?', function(result) {
bootbox.prompt("{% raw translate('serverFiles', 'renameItemQuestion') %}", function(result) {
path = event.target.parentElement.getAttribute('data-path');
name = event.target.parentElement.getAttribute('data-name');
if (!result) return;
@ -617,16 +636,16 @@
name = event.target.parentElement.getAttribute('data-name');
bootbox.confirm({
size: "",
title: "Are you sure you want to delete " + name + "?",
title: "{% raw translate('serverFiles', 'deleteItemQuestion') %}",
closeButton: false,
message: "You are deleting \"" + path + "\"!<br/><br/>This action will be irreversible and it'll be lost forever!",
message: "{% raw translate('serverFiles', 'deleteItemQuestionMessage') %}",
buttons: {
confirm: {
label: 'Yes, I understand the consequences',
label: "{{ translate('serverFiles', 'yesDelete') }}",
className: 'btn-danger'
},
cancel: {
label: 'No',
label: "{{ translate('serverFiles', 'noDelete') }}",
className: 'btn-link'
}
},
@ -645,16 +664,16 @@
name = event.target.parentElement.getAttribute('data-name');
bootbox.confirm({
size: "",
title: "Are you sure you want to delete " + name + "?",
title: "{% raw translate('serverFiles', 'deleteItemQuestion') %}",
closeButton: false,
message: "You are deleting \"" + path + "\"!<br/><br/>This action will be irreversible and it'll be lost forever!",
message: "{% raw translate('serverFiles', 'deleteItemQuestionMessage') %}",
buttons: {
confirm: {
label: 'Yes, I understand the consequences',
label: "{{ translate('serverFiles', 'yesDelete') }}",
className: 'btn-danger'
},
cancel: {
label: 'No',
label: "{{ translate('serverFiles', 'noDelete') }}",
className: 'btn-link'
}
},

View File

@ -4,7 +4,7 @@
<!-- <meta http-equiv="refresh" content="60">-->
{% end %}
{% block title %}Crafty Controller - Server Details{% end %}
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails') }}{% end %}
{% block content %}
@ -15,7 +15,7 @@
<div class="col-12">
<div class="page-header">
<h4 class="page-title">
Server Details - {{ data['server_stats']['server_id']['server_name'] }}
{{ translate('serverDetails', 'serverDetails') }} - {{ data['server_stats']['server_id']['server_name'] }}
<br />
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4>
@ -36,31 +36,31 @@
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
<i class="fas fa-terminal"></i>Terminal</a>
<i class="fas fa-terminal"></i>{{ translate('serverDetails', 'terminal') }}</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="true">
<i class="fas fa-file-signature"></i>Logs</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'logs') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a>
<i class="fas fa-clock"></i>{{ translate('serverDetails', 'schedule') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
<i class="fas fa-save"></i>Backup</a>
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a>
<i class="fas fa-folder-tree"></i>{{ translate('serverDetails', 'files') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="false">
<i class="fas fa-cogs"></i>Config</a>
<i class="fas fa-cogs"></i>{{ translate('serverDetails', 'config') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true">
<i class="fas fa-users"></i>Player Controls</a>
<i class="fas fa-users"></i>{{ translate('serverDetails', 'playerControls') }}</a>
</li>
</ul>

View File

@ -4,7 +4,7 @@
<!-- <meta http-equiv="refresh" content="60">-->
{% end %}
{% block title %}Crafty Controller - Server Details{% end %}
{% block title %}Crafty Controller - {{ translate('serverDetails', 'serverDetails') }}{% end %}
{% block content %}
@ -15,7 +15,7 @@
<div class="col-12">
<div class="page-header">
<h4 class="page-title">
Server Details - {{ data['server_stats']['server_id']['server_name'] }}
{{ translate('serverDetails', 'serverDetails') }} - {{ data['server_stats']['server_id']['server_name'] }}
<br />
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4>
@ -36,36 +36,36 @@
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item term-nav-item">
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="true">
<i class="fas fa-file-signature"></i>Terminal</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'terminal') }}</a>
</li>
<li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
<i class="fas fa-file-signature"></i>Logs</a>
<i class="fas fa-file-signature"></i>{{ translate('serverDetails', 'logs') }}</a>
</li>
<li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
<i class="fas fa-clock"></i>Schedule</a>
<i class="fas fa-clock"></i>{{ translate('serverDetails', 'schedule') }}</a>
</li>
<li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
<i class="fas fa-save"></i>Backup</a>
<i class="fas fa-save"></i>{{ translate('serverDetails', 'backup') }}</a>
</li>
<li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Files</a>
<i class="fas fa-folder-tree"></i>{{ translate('serverDetails', 'files') }}</a>
</li>
<li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="false">
<i class="fas fa-cogs"></i>Config</a>
<i class="fas fa-cogs"></i>{{ translate('serverDetails', 'config') }}</a>
</li>
<li class="nav-item term-nav-item">
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats']['server_id']['server_id'] }}&subpage=admin_controls" role="tab" aria-selected="true">
<i class="fas fa-users"></i>Player Controls</a>
<i class="fas fa-users"></i>{{ translate('serverDetails', 'playerControls') }}</a>
</li>
<li class="nav-item term-nav-item">
<label class="p-0 m-0">
<input type="checkbox" name="stop_scroll" id="stop_scroll" />
{{ _('Stop Refresh') }}
{{ translate('serverTerm', 'stopRefresh') }}
</label>
</li>
</ul>
@ -77,17 +77,17 @@
<br />
<div style="gap: 0.5rem;" class="input-group flex-wrap">
<input style="min-width: 10rem;" type="text" class="form-control" id="server_command" name="server_command" placeholder="Enter your server command" autofocus="">
<input style="min-width: 10rem;" type="text" class="form-control" id="server_command" name="server_command" placeholder="{{ translate('serverTerm', 'commandInput') }}" autofocus="">
<span class="input-group-btn ml-5">
<input type="hidden" value="" id="last_command"/>
<button id="submit" class="btn btn-sm btn-info" type="button">Send Command</button>
<button id="submit" class="btn btn-sm btn-info" type="button">{{ translate('serverTerm', 'sendCommand') }}</button>
</span>
</div>
<div class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0">
<button onclick="send_command(server_id, 'start_server');" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">Start</button>
<button onclick="send_command(server_id, 'restart_server');" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1">Restart</button>
<button onclick="send_command(server_id, 'stop_server');" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1">Stop</button>
<button onclick="send_command(server_id, 'start_server');" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">{{ translate('serverTerm', 'start') }}</button>
<button onclick="send_command(server_id, 'restart_server');" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1">{% raw translate('serverTerm', 'restart') %}</button>
<button onclick="send_command(server_id, 'stop_server');" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1">{{ translate('serverTerm', 'stop') }}</button>
</div>

View File

@ -33,11 +33,11 @@
<div class="card card-statistics social-card facebook-card card-colored">
<div class="card-body">
<h4 class="platform-name mb-3 mt-4 font-weight-semibold user-name">404</h4>
<h5 class="headline font-weight-medium">Page Not Found</h5>
<h5 class="headline font-weight-medium">{{ translate('404', 'notFound') }}</h5>
<p class="mb-2 comment font-weight-light">
We were unable to find the page you are looking for. Please try again, or go back and refresh.
{{ translate('404', 'unableToFind') }}
<br /><br />
<a class="d-inline font-weight-medium" href="https://discord.gg/9VJPhCE"> Contact Crafty Control Support via Discord</a>
<a class="d-inline font-weight-medium" href="https://discord.gg/9VJPhCE"> {{ translate('404', 'contact') }}</a>
</p>
</div>
</div>

View File

@ -32,16 +32,15 @@
<div class="col-sm-12 grid-margin stretch-card">
<div class="card card-statistics social-card google-card card-colored">
<div class="card-body">
<h4 class="platform-name mb-3 mt-4 font-weight-semibold user-name">WTF Error!</h4>
<h5 class="headline font-weight-medium">What a Terrible Failure!</h5>
<h4 class="platform-name mb-3 mt-4 font-weight-semibold user-name">{{ translate('error', 'error') }}</h4>
<h5 class="headline font-weight-medium">{{ translate('error', 'terribleFailure') }}</h5>
<p class="mb-2 comment font-weight-light">
Oh my, well, this is embarrassing.<br />
Something has broken so bad, we didn't make a special page for it.<br />
{{ translate('error', 'embarassing') }}<br />
<br />
<b>Here is the error: {{data['error']}}</b><br /><br />
<b>{{ translate('error', 'hereIsTheError') }}: {{data['error']}}</b><br /><br />
That's all the help I can give you - Godspeed
<br /><br />
<a class="d-inline font-weight-medium" href="https://discord.gg/9VJPhCE"> Contact Crafty Control Support via Discord</a>
<a class="d-inline font-weight-medium" href="https://discord.gg/9VJPhCE"> {{ translate('error', 'contact') }}</a>
</p>
</div>
</div>

View File

@ -57,25 +57,25 @@
<form action="/public/login" method="post">
{% raw xsrf_form_html() %}
<div class="form-group">
<label class="label">Username</label>
<label class="label">{{ translate('login', 'username') }}</label>
<div class="input-group">
<input type="text" class="form-control login-text-input login-input" placeholder="username" name="username" id="username" required="true">
<input type="text" class="form-control login-text-input login-input" placeholder="{{ translate('login', 'username') }}" name="username" id="username" required="true">
</div>
</div>
<div class="form-group">
<label class="label">Password</label>
<label class="label">{{ translate('login', 'password') }}</label>
<div class="input-group">
<input type="password" class="form-control login-text-input login-input" placeholder="" name="password" id="password" required="true">
<input type="password" class="form-control login-text-input login-input" placeholder="{{ translate('login', 'password') }}" name="password" id="password" required="true">
</div>
</div>
<div class="form-group">
<button class="login-input btn btn-primary submit-btn btn-block">Login</button>
<button class="login-input btn btn-primary submit-btn btn-block">{{ translate('login', 'login') }}</button>
</div>
<div class="form-group d-flex justify-content-between">
<div class="form-check form-check-flat mt-0">
&nbsp;
</div>
<a href="#" class="text-small forgot-password ">Forgot Password</a>
<a href="#" class="text-small forgot-password ">{{ translate('login', 'forgotPassword') }}</a>
</div>
<div class="text-block text-center my-3">

View File

@ -1,6 +1,6 @@
{% extends ../base.html %}
{% block title %}Crafty Controller - New Server{% end %}
{% block title %}Crafty Controller - {{ translate('serverWizard', 'newServer') }}{% end %}
{% block content %}
@ -10,7 +10,7 @@
<div class="card">
<div class="card-body">
<h4>Create a New Server</h4>
<h4>{{ translate('serverWizard', 'newServer') }}</h4>
<br />
<p class="card-description">
@ -19,7 +19,7 @@
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<h4 class="card-title" style="margin-bottom:20px;">Server Type</h4>
<h4 class="card-title" style="margin-bottom:20px;">{{ translate('serverWizard', 'serverType') }}</h4>
<select class="form-control form-control-lg select-css" id="server" name="server">
{% for s in data['server_types'] %}
@ -34,41 +34,41 @@
<div class="col-sm-12">
<div class="form-group">
<label for="server_name">Server Name</label>
<input type="text" class="form-control" id="server_name" name="server_name" value="My New Server">
<label for="server_name">{{ translate('serverWizard', 'serverName') }}</label>
<input type="text" class="form-control" id="server_name" name="server_name" placeholder="{{ translate('serverWizard', 'myNewServer') }}">
</div>
</div>
</div>
<br />
<h4 class="card-title">Quick Settings <small> - Don't worry, you can change these later</small></h4>
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings') }} <small style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription') }}</small></h4>
<hr>
<div class="row">
<div class="col-sm-3">
<div class="form-group">
<label for="min_memory">Min Memory <small> - Size in GB</small></label>
<label for="min_memory">{{ translate('serverWizard', 'minMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
<input type="number" class="form-control" id="min_memory" name="min_memory" value="1" step="0.5" min="0.5">
</div>
</div>
<div class="col-sm-3 offset-1">
<div class="form-group">
<label for="max_memory">Max Memory <small> - Size in GB</small></label>
<label for="max_memory">{{ translate('serverWizard', 'maxMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
<input type="number" class="form-control" id="max_memory" name="max_memory" value="2" step="0.5" min="0.5">
</div>
</div>
<div class="col-sm-3 offset-1">
<div class="form-group">
<label for="port">Server Port <small> - 25565 default</small></label>
<label for="port">{{ translate('serverWizard', 'serverPort') }} <small> - {{ translate('serverWizard', 'defaultPort') }}</small></label>
<input type="number" class="form-control" id="port" name="port" value="25565" step="1" min="1">
</div>
</div>
</div>
<button type="submit" class="btn btn-primary mr-2" onclick="wait_msg()">Build My Server!</button>
<button type="reset" class="btn btn-danger mr-2">Reset Form</button>
<button type="submit" class="btn btn-primary mr-2" onclick="wait_msg()">{{ translate('serverWizard', 'buildServer') }}</button>
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm') }}</button>
</form>
</p>
@ -80,7 +80,7 @@
<div class="card">
<div class="card-body">
<h4>Import an Existing Server</h4>
<h4>{{ translate('serverWizard', 'importServer') }}</h4>
<br />
<p class="card-description">
@ -91,21 +91,21 @@
<div class="col-sm-12">
<div class="form-group">
<label for="server_name">Server Name</label>
<input type="text" class="form-control" id="server_name" name="server_name" value="" placeholder="My Imported Server">
<label for="server_name">{{ translate('serverWizard', 'serverName') }}</label>
<input type="text" class="form-control" id="server_name" name="server_name" value="" placeholder="{{ translate('serverWizard', 'myNewServer') }}">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="server">Server Path <small>Absolute path to your server</small></label>
<label for="server">{{ translate('serverWizard', 'serverPath') }} <small>{{ translate('serverWizard', 'absoluteServerPath') }}</small></label>
<input type="text" class="form-control" id="server_path" name="server_path" placeholder="/var/opt/server">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="server_jar">Server Jar Name</label>
<label for="server_jar">{{ translate('serverWizard', 'serverJar') }}</label>
<input type="text" class="form-control" id="server_jar" name="server_jar" value="" placeholder="paper.jar">
</div>
</div>
@ -114,34 +114,34 @@
</div>
<br />
<h4 class="card-title">Quick Settings <small> - Don't worry, you can change these later</small></h4>
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings') }} <small style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription') }}</small></h4>
<hr>
<div class="row">
<div class="col-sm-3">
<div class="form-group">
<label for="min_memory">Min Memory <small> - Size in GB</small></label>
<label for="min_memory">{{ translate('serverWizard', 'minMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
<input type="number" class="form-control" id="min_memory" name="min_memory" value="1" step="0.5" min="0.5">
</div>
</div>
<div class="col-sm-3 offset-1">
<div class="form-group">
<label for="max_memory">Max Memory <small> - Size in GB</small></label>
<label for="max_memory">{{ translate('serverWizard', 'maxMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
<input type="number" class="form-control" id="max_memory" name="max_memory" value="2" step="0.5" min="0.5">
</div>
</div>
<div class="col-sm-3 offset-1">
<div class="form-group">
<label for="port">Server Port <small> - 25565 default</small></label>
<label for="port">{{ translate('serverWizard', 'serverPort') }} <small> - {{ translate('serverWizard', 'defaultPort') }}</small></label>
<input type="number" class="form-control" id="port" name="port" value="25565" step="1" min="1">
</div>
</div>
</div>
<button type="submit" class="btn btn-primary mr-2" onclick="wait_msg(true)">Import My Server!</button>
<button type="reset" class="btn btn-danger mr-2">Reset Form</button>
<button type="submit" class="btn btn-primary mr-2" onclick="wait_msg(true)">{{ translate('serverWizard', 'importServerButton') }}</button>
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm') }}</button>
</form>
</p>
@ -153,7 +153,7 @@
<div class="card">
<div class="card-body">
<h4>Import From Zip File</h4>
<h4>{{ translate('serverWizard', 'importZip') }}</h4>
<br />
<p class="card-description">
@ -162,64 +162,62 @@
<input type="hidden" value="import_zip" name="create_type">
<div class="row">
<div class="col-sm-9">
<h4 class="card-title">Server Details</h4>
<div class="col-sm-9">
<div class="col-sm-12">
<div class="form-group">
<label for="server_name">{{ translate('serverWizard', 'serverName') }}</label>
<input type="text" class="form-control" id="server_name" name="server_name" value="" placeholder="{{ translate('serverWizard', 'myNewServer') }}">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="server">{{ translate('serverWizard', 'zipPath') }} <small>{{ translate('serverWizard', 'absoluteZipPath') }}</small></label>
<input type="text" class="form-control" id="server_path" name="server_path" placeholder="/var/opt/server.zip">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="server_jar">{{ translate('serverWizard', 'serverJar') }}</label>
<input type="text" class="form-control" id="server_jar" name="server_jar" value="" placeholder="paper.jar">
</div>
</div>
</div>
<div class="col-sm-3">
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings') }} <small style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription') }}</small></h4>
<hr>
<div class="col-sm-12">
<div class="form-group">
<label for="server_name">Server Name</label>
<input type="text" class="form-control" id="server_name" name="server_name" value="" placeholder="My Imported Server">
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label for="server">Zip Path <small>Absolute path to your zip file</small></label>
<input type="text" class="form-control" id="server_path" name="server_path" placeholder="/var/opt/server.zip">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="min_memory">{{ translate('serverWizard', 'minMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
<input type="number" class="form-control" id="min_memory" name="min_memory" value="1" step="0.5" min="0.5">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="max_memory">{{ translate('serverWizard', 'maxMem') }} <small> - {{ translate('serverWizard', 'sizeInGB') }}</small></label>
<input type="number" class="form-control" id="max_memory" name="max_memory" value="2" step="0.5" min="0.5">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="port">{{ translate('serverWizard', 'serverPort') }} <small> - {{ translate('serverWizard', 'defaultPort') }}</small></label>
<input type="number" class="form-control" id="port" name="port" value="25565" step="1" min="1">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="server_jar">Server Jar Name</label>
<input type="text" class="form-control" id="server_jar" name="server_jar" value="" placeholder="paper.jar">
</div>
<button type="submit" class="btn btn-primary mr-2" onclick="wait_msg(true)">{{ translate('serverWizard', 'importServerButton') }}</button>
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm') }}</button>
</div>
</div>
<div class="col-sm-3">
<h4 class="card-title">Quick Settings <small> - Don't worry, you can change these later</small></h4>
<hr>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label for="min_memory">Min Memory <small> - Size in GB</small></label>
<input type="number" class="form-control" id="min_memory" name="min_memory" value="1" step="0.5" min="0.5">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="max_memory">Max Memory <small> - Size in GB</small></label>
<input type="number" class="form-control" id="max_memory" name="max_memory" value="2" step="0.5" min="0.5">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="port">Server Port <small> - 25565 default</small></label>
<input type="number" class="form-control" id="port" name="port" value="25565" step="1" min="1">
</div>
</div>
</div>
<button type="submit" class="btn btn-primary mr-2" onclick="wait_msg(true)">Import My Server!</button>
<button type="reset" class="btn btn-danger mr-2">Reset Form</button>
</div>
</div>
</form>
</p>
</div>
@ -235,25 +233,28 @@
$( document ).ready(function() {
console.log('ready');
$("#min_memory").change(function(){
check_sizes();
check_sizes('min');
});
$("#max_memory").change(function(){
check_sizes('max');
});
});
function wait_msg(importing){
bootbox.alert({
title: importing ? 'Importing Server...' : 'Downloading Server...',
message: '<i class="fas fa-cloud-download"></i> Please be patient as we ' + (importing ? 'import' : 'download') + ' the server'
title: importing ? '{% raw translate("serverWizard", "importing") %}' : '{% raw translate("serverWizard", "downloading") %}',
message: '<i class="fas fa-cloud-download"></i> {% raw translate("serverWizard", "bePatient") %}'
});
}
function check_sizes(){
function check_sizes(changed){
max_mem = parseFloat($('#max_memory').val());
min_mem = parseFloat($('#min_memory').val());
if (max_mem < min_mem){
bootbox.alert({
title: 'Memory Warning',
message: 'Min Memory is Higher than Max Memory'
});
if (max_mem < min_mem && changed === 'min'){
$('#max_memory').val(min_mem)
}
if (max_mem < min_mem && changed === 'max'){
$('#min_memory').val(max_mem)
}
}

287
app/translations/en_EN.json Normal file
View File

@ -0,0 +1,287 @@
{
"login": {
"forgotPassword": "Forgot Password",
"login": "Log In",
"password": "Password",
"username": "Username"
},
"error": {
"hereIsTheError": "Here is the error",
"contact": "Contact Crafty Control Support via Discord",
"terribleFailure": "What a Terrble Failure!",
"embarassing": "Oh my, well, this is embarrassing.",
"error": "Error!"
},
"404": {
"contact": "Contact Crafty Control Support via Discord",
"unableToFind": "We were unable to find the page you are looking for. Please try again, or go back and refresh.",
"notFound": "Page Not Found"
},
"footer": {
"version": "Version",
"copyright": "Copyright",
"allRightsReserved": "All rights reserved"
},
"sidebar": {
"dashboard": "Dashboard",
"servers": "Servers",
"documentation": "Documentation",
"credits": "Credits",
"contribute": "Contribute",
"newServer": "Create New Server",
"navigation": "Navigation"
},
"serverWizard": {
"newServer": "Create New Server",
"importServer": "Import an Existing Server",
"importZip": "Import from a Zip File",
"serverName": "Server Name",
"serverPath": "Server Path",
"serverType": "Server Type",
"absoluteServerPath": "Absolute path to your server",
"serverJar": "Server Jarfile",
"minMem": "Minimum Memory",
"maxMem": "Maximum Memory",
"serverPort": "Server Port",
"defaultPort": "25565 default",
"sizeInGB": "Size in GB",
"zipPath": "Server Path",
"absoluteZipPath": "Absolute path to your server",
"resetForm": "Reset Form",
"importServerButton": "Import Server!",
"buildServer": "Build Server!",
"quickSettings": "Quick Settings",
"quickSettingsDescription": "Don't worry, you can change these later",
"myNewServer": "My New Server",
"bePatient": "Please be patient as we ' + (importing ? 'import' : 'download') + ' the server",
"importing": "Importing Server...",
"downloading": "Downloading Server..."
},
"dashboard": {
"dashboard": "Dashboard",
"memUsage": "Memory Usage",
"cpuUsage": "CPU Usage",
"host": "Host",
"players": "Players",
"backups": "Backups",
"newServer": "Create New Server",
"allServers": "All Servers",
"server": "Server",
"actions": "Actions",
"world": "World",
"status": "Status",
"online": "Online",
"offline": "Offline",
"lastBackup": "Last:",
"nextBackup": "Next:",
"servers": "Servers",
"cannotSeeOnMobile": "Can't see everything on mobile?",
"cannotSeeOnMobile2": "Try scrolling the table sideways.",
"max": "Max",
"avg": "Avg",
"bePatientStart": "Please be patient while we start the server.<br /> This screen will refresh in a moment",
"bePatientStop": "Please be patient while we stop the server.<br /> This screen will refresh in a moment",
"bePatientRestart": "Please be patient while we restart the server.<br /> This screen will refresh in a moment",
"bePatientClone": "Please be patient while we clone the server.<br /> This screen will refresh in a moment",
"sendingCommand": "Sending your command",
"cpuCurFreq": "CPU Current Clock",
"cpuMaxFreq": "CPU Maximum Clock",
"cpuCores": "CPU Cores"
},
"accessDenied": {
"accessDenied": "Access Denied",
"noAccess": "You do not have access to this resource",
"contactAdmin": "Contact your server administrator for access to this resource, or if you think you should have access to this resource, contact support.",
"contact": "Contact Crafty Control Support via Discord"
},
"serverStats": {
"online": "Online",
"offline": "Offline",
"serverStatus": "Server Status",
"serverStarted": "Server Started",
"serverUptime": "Server Uptime",
"players": "Players",
"memUsage": "Memory Usage",
"cpuUsage": "CPU Usage",
"version": "Version",
"description": "Description",
"errorCalculatingUptime": "Error Calculating Uptime",
"serverTime": "Server Time",
"unableToConnect": "Unable To Connect"
},
"serverDetails": {
"serverDetails": "Server Details",
"terminal": "Terminal",
"logs": "Logs",
"schedule": "Schedule",
"backup": "Backup",
"files": "Files",
"config": "Config",
"playerControls": "Player Management"
},
"serverTerm": {
"stopRefresh": "Stop Refresh",
"commandInput": "Enter your command",
"sendCommand": "Send command",
"start": "Start",
"restart": "Restart",
"stop": "Stop"
},
"serverPlayerManagement": {
"players": "Players",
"bannedPlayers": "Banned Players",
"loadingBannedPlayers": "Loading Banned Players"
},
"serverBackups": {
"backupNow": "Backup Now!",
"backupAtMidnight": "Auto-backup at midnight?",
"storageLocation": "Storage Location",
"storageLocationDesc": "Where do you want to store backups?",
"maxBackups": "Max Backups",
"maxBackupsDesc": "Crafty will not store more than N backups, deleting the oldest (enter 0 to keep all)",
"save": "Save",
"cancel": "Cancel",
"currentBackups": "Current Backups",
"download": "Download",
"path": "Path",
"size": "Size",
"delete": "Delete",
"backupTask": "A backup task has been started.",
"destroyBackup": "Destroy backup \" + file_to_del + \"?",
"confirmDelete": "Do you want to delete this backup? This cannot be undone.",
"confirm": "Confirm"
},
"serverFiles": {
"noscript": "The file manager does not work without JavaScript",
"error": "Error while getting files",
"files": "Files",
"default": "Default",
"save": "Save",
"editingFile": "Editing file",
"delete": "Delete",
"createFile": "Create file",
"createDir": "Create directory",
"rename": "Rename",
"createFileQuestion": "What name do you want for the new file?",
"createDirQuestion": "What name do you want for the new directory?",
"renameItemQuestion": "What should the new name be?",
"deleteItemQuestion": "Are you sure you want to delete \" + name + \"?",
"deleteItemQuestionMessage": "You are deleting \\\"\" + path + \"\\\"!<br/><br/>This action will be irreversible and it'll be lost forever!",
"yesDelete": "Yes, I understand the consequences",
"noDelete": "No",
"unsupportedLanguage": "Warning: This is not a supported file type",
"keybindings": "Keybindings"
},
"serverConfig": {
"serverName": "Server Name",
"serverNameDesc": "What you wish to call this server",
"serverPath": "Server Working Directory",
"serverPathDesc": "Absolute full path (not including executable)",
"serverLogLocation": "Server Log Location",
"serverLogLocationDesc": "Absolute full path to the log file",
"serverExecutable": "Server Executable",
"serverExecutableDesc": "The server's executable file",
"serverExecutionCommand": "Server Execution Command",
"serverExecutionCommandDesc": "What will be launched in a hidden terminal",
"serverStopCommand": "Server Stop Command",
"serverStopCommandDesc": "Command to send the program to stop it",
"serverAutostartDelay": "Server Autostart Delay",
"serverAutostartDelayDesc": "Delay before auto starting (If enabled below)",
"serverIP": "Server IP",
"serverIPDesc": "IP Crafty should connect to for stats (Try a real ip instead of 127.0.0.1 if you have issues)",
"serverPort": "Server Port",
"serverPortDesc": "Port Crafty should connect to for stats",
"removeOldLogsAfter": "Remove Old Logs After",
"removeOldLogsAfterDesc": "How many days will a log file has to be old to get deleted (0 is off)",
"serverAutoStart": "Server Auto Start",
"serverCrashDetection": "Server Crash Detection",
"save": "Save",
"cancel": "Cancel",
"deleteServer": "Delete Server",
"stopBeforeDeleting": "Please stop the server before deleting it"
},
"serverConfigHelp": {
"title": "Server Config Area",
"desc": "Here is where you can change the configuration of your server",
"perms": [
"It is recommended to <code>NOT</code> change the paths of a server managed by Crafty.",
"Changing paths <code>CAN</code> break things, especially on Linux type operating systems where file permissions are more locked down.",
"<br /><br/>",
"If you feel you have to change a where a server is located you may do so as long as you give the \"crafty\" user permission to read / write to the server path.",
"<br />",
"<br />",
"On Linux this is best done by executing the following:<br />",
"<code>",
" sudo chown crafty:crafty /path/to/your/server -R<br />",
" sudo chmod 2775 /path/to/your/server -R<br />",
"</code>"
]
},
"datatables": {
"i18n": {
"decimal": "",
"emptyTable": "No data available in table",
"info": "Showing _START_ to _END_ of _TOTAL_ entries",
"infoEmpty": "Showing 0 to 0 of 0 entries",
"infoFiltered": "(filtered from _MAX_ total entries)",
"infoPostFix": "",
"thousands": ",",
"lengthMenu": "Show _MENU_ entries",
"loadingRecords": "Loading...",
"processing": "Processing...",
"search": "Search:",
"zeroRecords": "No matching records found",
"paginate": {
"first": "First",
"last": "Last",
"next": "Next",
"previous": "Previous"
},
"aria": {
"sortAscending": ": activate to sort column ascending",
"sortDescending": ": activate to sort column descending"
},
"buttons": {
"collection": "Collection <span class='ui-button-icon-primary ui-icon ui-icon-triangle-1-s'\/>",
"colvis": "Column Visibility",
"colvisRestore": "Restore visibility",
"copy": "Copy",
"copyKeys": "Press ctrl or u2318 + C to copy the table data to your system clipboard.<br><br>To cancel, click this message or press escape.",
"copySuccess": {
"1": "Copied 1 row to clipboard",
"_": "Copied %d rows to clipboard"
},
"copyTitle": "Copy to Clipboard",
"csv": "CSV",
"excel": "Excel",
"pageLength": {
"-1": "Show all rows",
"1": "Show 1 row",
"_": "Show %d rows"
},
"pdf": "PDF",
"print": "Print"
},
"select": {
"rows": {
"0": "Click on a row to select it",
"1": "%d row selected",
"_": "%d rows selected"
},
"cells": {
"0": "Click on a cel to select it",
"1": "%d cell selected",
"_": "%d cells selected"
},
"columns": {
"0": "Click on a column to select it",
"1": "%d column selected",
"_": "%d columns selected"
}
}
}
},
"base": {
"doesNotWorkWithoutJavascript": "<strong>Warning: </strong>Crafty doesn't work properly when JavaScript isn't enabled!"
}
}

311
app/translations/fi_FI.json Normal file
View File

@ -0,0 +1,311 @@
{
"login": {
"forgotPassword": "Unohdin salasanan",
"login": "Kirjaudu Sisään",
"password": "Salasana",
"username": "Käyttäjänimi"
},
"error": {
"hereIsTheError": "Tässä on virhe",
"contact": "Ota yhteyttä Crafty Control -tukeen Discordin kautta",
"terribleFailure": "Mikä kauhea epäonnistuminen!",
"embarassing": "No, tämähän on noloa.",
"error": "Virhe!"
},
"404": {
"contact": "Ota yhteyttä Crafty Control -tukeen Discordin kautta",
"unableToFind": "Emme löytäneet etsimääsi sivua. Yritä uudelleen tai palaa takaisin ja päivitä.",
"notFound": "Sivua ei löytynyt"
},
"footer": {
"version": "Versio",
"copyright": "Tekijänoikeus",
"allRightsReserved": "Kaikki oikeudet pidätetään"
},
"sidebar": {
"dashboard": "Kojelauta",
"servers": "Palvelimet",
"documentation": "Dokumentaatio",
"credits": "Hyvitykset",
"contribute": "Osallistu",
"newServer": "Luo uusi palvelin",
"navigation": "Navigaatio"
},
"serverWizard": {
"newServer": "Luo uusi palvelin",
"importServer": "Tuo olemassa oleva palvelin",
"importZip": "Tuo zip-tiedostosta",
"serverName": "Palvelimen nimi",
"serverPath": "Palvelimen polku",
"serverType": "Palvelimen tyypi",
"absoluteServerPath": "Absoluuttinen polku palvelimeesi",
"serverJar": "Palvelimen Jar -tiedosto",
"minMem": "Minimi Muisti",
"maxMem": "Maximi Muisti",
"serverPort": "Palvelimen portti",
"defaultPort": "Oletusarvo 25565",
"sizeInGB": "Koko gigatavuissa",
"zipPath": "Zip -tiedoston polku",
"absoluteZipPath": "Absoluuttinen polku zip -tiedostoosi",
"resetForm": "Nollaa lomake",
"importServerButton": "Tuo palvelin!",
"buildServer": "Rakenna palvelin!",
"quickSettings": "Pika-asetukset",
"quickSettingsDescription": "Älä huoli, voit muuttaa näitä myöhemmin",
"myNewServer": "Minun uusi palvelin",
"bePatient": "Ole kärsivällinen, kun ' + (importing ? 'tuomme' : 'lataamme') + ' palvelinta",
"importing": "Tuomme palvelinta...",
"downloading": "Lataamme palvelinta..."
},
"dashboard": {
"dashboard": "Kojelauta",
"memUsage": "Muistin käyttö",
"cpuUsage": "Prosessorin käyttö",
"host": "Kone",
"players": "Pelaajat",
"backups": "Varmuuskopiot",
"newServer": "Luo uusi palvelin",
"allServers": "Kaikki palvelimet",
"server": "Palvelin",
"actions": "Toiminnot",
"world": "Maailma",
"status": "Tila",
"online": "Päällä",
"offline": "Pois päältä",
"lastBackup": "Viimeinen:",
"nextBackup": "Seuraava:",
"servers": "Palvelimet",
"cannotSeeOnMobile": "Etkö näe kaikkea mobiililaitteella?",
"cannotSeeOnMobile2": "Yritä vierittää taulukkoa sivuttain.",
"max": "maks.",
"avg": "ka.",
"bePatientStart": "Ole kärsivällinen, kun käynnistämme palvelimen.<br /> Tämä näyttö päivittyy hetken kuluttua",
"bePatientStop": "Ole kärsivällinen, kun sammutamme palvelimen.<br /> Tämä näyttö päivittyy hetken kuluttua",
"bePatientRestart": "Ole kärsivällinen, kun käynnistämme palvelimen uudelleen.<br /> Tämä näyttö päivittyy hetken kuluttua",
"bePatientClone": "Ole kärsivällinen, kun kloonaamme palvelimen.<br /> Tämä näyttö päivittyy hetken kuluttua",
"sendingCommand": "Lähetämme komentoasi",
"cpuCurFreq": "Nykyinen kellotaajuus",
"cpuMaxFreq": "Maksimi kellotaajuus",
"cpuCores": "Suorittimen ytimet"
},
"accessDenied": {
"accessDenied": "Käyttö estetty",
"noAccess": "Sinulla ei ole pääsyä tähän resurssiin",
"contactAdmin": "Ota yhteyttä palvelimen järjestelmänvalvojaan saadaksesi tämän resurssin käyttöoikeuden, tai jos luulet, että sinulla pitäisi olla pääsy tähän resurssiin, ota yhteyttä tukeen.",
"contact": "Ota yhteyttä Crafty Control -tukeen Discordin kautta"
},
"serverStats": {
"online": "Päällä",
"offline": "Pois päältä",
"serverStatus": "Palvelimen tila",
"serverStarted": "Palvelin käynnistyi",
"serverUptime": "Palvelimen käyttöaika",
"players": "Pelaajat",
"memUsage": "Muistin käyttö",
"cpuUsage": "Prosessorin käyttö",
"version": "Versio",
"description": "Kuvaus",
"errorCalculatingUptime": "Virhe laskettaessa käyttöaikaa",
"serverTime": "Palvelimen aikaa",
"unableToConnect": "Yhteyden muodostaminen epäonnistui"
},
"serverDetails": {
"serverDetails": "Palvelimen tiedot",
"terminal": "Pääte",
"logs": "Lokit",
"schedule": "Ajoitus",
"backup": "Varmuuskopiot",
"files": "Tiedostojenhallinta",
"config": "Asetukset",
"playerControls": "Pelaajahallinta"
},
"serverTerm": {
"stopRefresh": "Lopeta päivitys",
"commandInput": "Kirjoita komento",
"sendCommand": "Lähetä komento",
"start": "Käynnistä",
"restart": "Uudelleen&shy;käynnistä",
"stop": "Sammuta"
},
"serverPlayerManagement": {
"players": "Pelaajat",
"bannedPlayers": "Kielletyt pelaajat",
"loadingBannedPlayers": "Ladataan kiellettyjen pelaajien listaa"
},
"serverBackups": {
"backupNow": "Varmuuskopioi nyt!",
"backupAtMidnight": "Automaattisesti varmuuskopioi keskiyöllä?",
"storageLocation": "Varmuuskopiointisijainti",
"storageLocationDesc": "Mihin haluat tallentaa varmuuskopiot?",
"maxBackups": "Varmuuskopioiden enimmäismäärä",
"maxBackupsDesc": "Crafty ei tallenna enempää kuin N varmuuskopiota, poistamalla vanhimmat (syötä 0, jos haluat säilyttää kaikki)",
"save": "Tallenna",
"cancel": "Peruuta",
"currentBackups": "Nykyiset varmuuskopiot",
"download": "Lataa",
"path": "Polku",
"size": "Koko",
"delete": "Poista",
"backupTask": "Varmuuskopiointitehtävä on aloitettu.",
"destroyBackup": "Tuhotaanko varmuuskopio \" + file_to_del + \"?",
"confirmDelete": "Haluatko poistaa tämän varmuuskopion? Tätä ei voi peruuttaa.",
"confirm": "Vahvista"
},
"serverFiles": {
"noscript": "Tiedostojenhallinta ei toimi ilman JavaScriptiä",
"error": "Virhe tiedostoja haettaessa",
"files": "Tiedostot",
"default": "Oletus",
"save": "Tallenna",
"editingFile": "Muokataan tiedostoa",
"delete": "Poista",
"createFile": "Luo tiedosto",
"createDir": "Luo hakemisto",
"rename": "Nimeä uudelleen",
"createFileQuestion": "Minkä nimen haluat uudelle tiedostolle?",
"createDirQuestion": "Minkä nimen haluat uudelle hakemistolle?",
"renameItemQuestion": "Mikä uuden nimen pitäisi olla?",
"deleteItemQuestion": "Haluatko varmasti poistaa \" + name + \"?",
"deleteItemQuestionMessage": "Olet poistamassa \\\"\" + path + \"\\\"!<br/><br/>Tämä toiminta on peruuttamaton ja se menetetään ikuisesti!",
"yesDelete": "Kyllä, ymmärrän seuraukset",
"noDelete": "En",
"unsupportedLanguage": "Varoitus: Tätä tiedostotyyppiä ei tueta",
"keybindings": "Pikanäppäimet"
},
"serverConfig": {
"serverName": "Palvelimen nimi",
"serverNameDesc": "Miksi haluat kutsua tätä palvelinta",
"serverPath": "Palvelimen työkansio",
"serverPathDesc": "Absoluuttinen polku palvelimeen (ilman suoritettavaa tiedostoa)",
"serverLogLocation": "Palvelimen lokin sijainti",
"serverLogLocationDesc": "Absoluuttinen polku lokitiedostoon",
"serverExecutable": "Suoritettava tiedosto",
"serverExecutableDesc": "Palvelimen suoritettava tiedosto",
"serverExecutionCommand": "Palvelimen suorituskomento",
"serverExecutionCommandDesc": "Mikä käynnistetään piilotetussa terminaalissa",
"serverStopCommand": "Palvelimen pysäytyskomento",
"serverStopCommandDesc": "Komento lähettää palvelimen pysäyttämiseen",
"serverAutostartDelay": "Palvelimen automaattisen käynnistyksen viive",
"serverAutostartDelayDesc": "Viive ennen automaattista käynnistystä (Jos tämä on käytössä alla)",
"serverIP": "Palvelimen IP-osoite",
"serverIPDesc": "IP-osoite, johon Craftyn tulisi muodostaa yhteys tilastojen saamiseen (Kokeile todellista ip:tä \"127.0.0.1\" sijaan, jos sinulla on ongelmia)",
"serverPort": "Palvelimen portti",
"serverPortDesc": "Portti, johon Craftyn tulisi muodostaa yhteys tilastojen saamiseen",
"removeOldLogsAfter": "Poista vanhat lokit N päivän jälkeen",
"removeOldLogsAfterDesc": "Kuinka monta päivää lokitiedoston on oltava vanha poistettavaksi (0 ei ole käytössä)",
"serverAutoStart": "Palvelimen automaattinen käynnistys",
"serverCrashDetection": "Palvelimen kaatumisen havaitseminen",
"save": "Tallenna",
"cancel": "Peruuta",
"deleteServer": "Poista palvelin",
"stopBeforeDeleting": "Pysäytä palvelin ennen sen poistamista"
},
"serverConfigHelp": {
"title": "Palvelimen asetukset",
"desc": "Tässä voit muuttaa palvelimesi asetuksia",
"perms": [
"On suositeltavaa, että <code>EI</code> muuta Craftyn hallinnoiman palvelimen polkuja.",
"Polkujen muuttaminen <code>VOI</code> rikkoa asioita, varsinkin Linux-tyyppisissä käyttöjärjestelmissä, joissa tiedostojen käyttöoikeudet on lukittu paremmin.",
"<br /><br/>",
"Jos sinusta tuntuu, että sinun on vaihdettava palvelimen sijaintia, voit tehdä niin, kunhan annat \"crafty\" käyttäjälle luvan lukea / kirjoittaa palvelimen polulle.",
"<br />",
"<br />",
"Linuxissa tämä onnistuu parhaiten suorittamalla seuraavat:<br />",
"<code>",
" sudo chown crafty:crafty /polku/palvelimellesi -R<br />",
" sudo chmod 2775 /polku/palvelimellesi -R<br />",
"</code>"
]
},
"datatables": {
"i18n": {
"decimal": "",
"emptyTable": "Ei tietoja saatavilla taulukossa",
"info": "Näytetään _START_-_END_ / _TOTAL_ merkinnästä",
"infoEmpty": "Showing 0 to 0 of 0 entries",
"infoFiltered": "(filtered from _MAX_ total entries)",
"infoPostFix": "",
"thousands": ",",
"lengthMenu": "Show _MENU_ entries",
"loadingRecords": "Ladataan...",
"processing": "Käsitellään...",
"search": "Hae:",
"zeroRecords": "Tietoja ei löytynyt",
"paginate": {
"first": "Ensimmäinen",
"last": "Viimeinen",
"next": "Seuraava",
"previous": "Edellinen"
},
"aria": {
"sortAscending": ": lajittele sarake nousevasti",
"sortDescending": ": lajittele sarake laskevasti"
},
"buttons": {
"collection": "Kokoelma <span class='ui-button-icon-primary ui-icon ui-icon-triangle-1-s'\/>",
"colvis": "Sarakkeen näkyvyys",
"colvisRestore": "Palauta sarakkeen näkyvyys",
"copy": "Kopioi",
"copyKeys": "Paina <i>ctrl<\/i> tai <i>⌘<\/i> + <i>C<\/i> kopioidaksesi taulukon arvot<br> leikepöydälle. <br><br>Peruuttaaksesi klikkaa tähän tai Esc.",
"copySuccess": {
"1": "Yksi rivi kopioitu leikepöydälle",
"_": "%d riviä kopioitu leikepöydälle"
},
"copyTitle": "Kopioi leikepöydälle",
"csv": "CSV",
"excel": "Excel",
"pageLength": {
"-1": "Näytä kaikki rivit",
"1": "Näytä yksi rivi",
"_": "Näytä %d riviä"
},
"pdf": "PDF",
"print": "Tulosta"
},
"select": {
"rows": {
"0": "Klikkaa riviä valitaksesi sen",
"1": "Yksi rivi valittuna",
"2": "Kaksi riviä valittuna",
"3": "Kolme riviä valittuna",
"4": "Neljä riviä valittuna",
"5": "Viisi riviä valittuna",
"6": "Kuusi riviä valittuna",
"7": "Seitsemän riviä valittuna",
"8": "Kahdeksan riviä valittuna",
"9": "Yhdeksän riviä valittuna",
"_": "%d riviä valittuna"
},
"cells": {
"0": "Klikkaa solua valitaksesi sen",
"1": "Yksi solu valittuna",
"2": "Kaksi solua valittuna",
"3": "Kolme solua valittuna",
"4": "Neljä solua valittuna",
"5": "Viisi solua valittuna",
"6": "Kuusi solua valittuna",
"7": "Seitsemän solua valittuna",
"8": "Kahdeksan solua valittuna",
"9": "Yhdeksän solua valittuna",
"_": "%d solua valittuna"
},
"columns": {
"0": "Klikkaa saraketta valitaksesi sen",
"1": "Yksi sarake valittuna",
"2": "Kaksi saraketta valittuna",
"3": "Kolme saraketta valittuna",
"4": "Neljä saraketta valittuna",
"5": "Viisi saraketta valittuna",
"6": "Kuusi saraketta valittuna",
"7": "Seitsemän saraketta valittuna",
"8": "Kahdeksan saraketta valittuna",
"9": "Yhdeksän saraketta valittuna",
"_": "%d saraketta valittuna"
}
}
}
},
"base": {
"doesNotWorkWithoutJavascript": "<strong>Varoitus: </strong>Crafty ei toimi kunnolla ilman JavaScriptiä!"
}
}