Merge branch 'dev' into tweak/password-reset

This commit is contained in:
Zedifus 2022-09-04 21:41:27 +01:00
commit 385ceb9655
31 changed files with 810 additions and 453 deletions

View File

@ -2,9 +2,11 @@
## --- [4.0.12] - 2022/09/04 ## --- [4.0.12] - 2022/09/04
### New features ### New features
- Win Portable Updater will now be included in Windows Package ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/446)) - Win Portable Updater will now be included in Windows Package ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/446))
- Bedrock Server Creator ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/443))
### Bug fixes ### Bug fixes
- Fix performance issues on server metrics panels (Temporarily setting to 24hr query) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/440)) - Fix performance issues on server metrics panels 'with metrics range' ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/440)) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/448))
- Fix no id on import3 servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/442)) - Fix no id on import3 servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/442))
- Fix functionality of bedrock update ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/441))
### Tweaks ### Tweaks
TBD TBD
### Lang ### Lang

View File

@ -105,10 +105,9 @@ class ServersController(metaclass=Singleton):
server_instance.update_server_instance() server_instance.update_server_instance()
return ret return ret
def get_history_stats(self, server_id): def get_history_stats(self, server_id, days):
now = datetime.datetime.now()
srv = ServersController().get_server_instance_by_id(server_id) srv = ServersController().get_server_instance_by_id(server_id)
return srv.stats_helper.get_history_stats(server_id) return srv.stats_helper.get_history_stats(server_id, days)
@staticmethod @staticmethod
def update_unloaded_server(server_obj): def update_unloaded_server(server_obj):

View File

@ -138,8 +138,8 @@ class HelperServerStats:
) )
return server_data return server_data
def get_history_stats(self, server_id): def get_history_stats(self, server_id, num_days):
max_age = datetime.datetime.now() - timedelta(days=1) max_age = datetime.datetime.now() - timedelta(days=num_days)
return ( return (
ServerStats.select() ServerStats.select()
.where(ServerStats.created > max_age) .where(ServerStats.created > max_age)

View File

@ -64,6 +64,11 @@ class FileHelpers:
FileHelpers.copy_dir(src_path, dest_path) FileHelpers.copy_dir(src_path, dest_path)
FileHelpers.del_dirs(src_path) FileHelpers.del_dirs(src_path)
@staticmethod
def move_dir_exist(src_path, dest_path):
FileHelpers.copy_dir(src_path, dest_path, True)
FileHelpers.del_dirs(src_path)
@staticmethod @staticmethod
def move_file(src_path, dest_path): def move_file(src_path, dest_path):
FileHelpers.copy_file(src_path, dest_path) FileHelpers.copy_file(src_path, dest_path)
@ -290,7 +295,7 @@ class FileHelpers:
for item in os.listdir(full_root_path): for item in os.listdir(full_root_path):
if os.path.isdir(os.path.join(full_root_path, item)): if os.path.isdir(os.path.join(full_root_path, item)):
try: try:
FileHelpers.move_dir( FileHelpers.move_dir_exist(
os.path.join(full_root_path, item), os.path.join(full_root_path, item),
os.path.join(new_dir, item), os.path.join(new_dir, item),
) )

View File

@ -107,6 +107,42 @@ class Helpers:
logger.error(f"Unable to check for new crafty version! \n{e}") logger.error(f"Unable to check for new crafty version! \n{e}")
return False return False
@staticmethod
def get_latest_bedrock_url():
"""
Get latest bedrock executable url \n\n
returns url if successful, False if not
"""
url = "https://minecraft.net/en-us/download/server/bedrock/"
headers = {
"Accept-Encoding": "identity",
"Accept-Language": "en",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/104.0.0.0 Safari/537.36"
),
}
target_win = 'https://minecraft.azureedge.net/bin-win/[^"]*'
target_linux = 'https://minecraft.azureedge.net/bin-linux/[^"]*'
try:
# Get minecraft server download page
# (hopefully the don't change the structure)
download_page = get(url, headers=headers)
# Search for our string targets
win_download_url = re.search(target_win, download_page.text).group(0)
linux_download_url = re.search(target_linux, download_page.text).group(0)
if os.name == "nt":
return win_download_url
return linux_download_url
except Exception as e:
logger.error(f"Unable to resolve remote bedrock download url! \n{e}")
return False
@staticmethod @staticmethod
def find_java_installs(): def find_java_installs():
# If we're windows return oracle java versions, # If we're windows return oracle java versions,

View File

@ -3,6 +3,7 @@ import time
import shutil import shutil
import logging import logging
import threading import threading
import urllib
from app.classes.controllers.server_perms_controller import PermissionsServers from app.classes.controllers.server_perms_controller import PermissionsServers
from app.classes.controllers.servers_controller import ServersController from app.classes.controllers.servers_controller import ServersController
@ -214,3 +215,43 @@ class ImportHelpers:
os.chmod(full_jar_path, 0o2760) os.chmod(full_jar_path, 0o2760)
# deletes temp dir # deletes temp dir
FileHelpers.del_dirs(temp_dir) FileHelpers.del_dirs(temp_dir)
def download_bedrock_server(self, path, new_id):
download_thread = threading.Thread(
target=self.download_threaded_bedrock_server,
daemon=True,
args=(path, new_id),
name=f"{new_id}_download",
)
download_thread.start()
def download_threaded_bedrock_server(self, path, new_id):
# downloads zip from remote url
try:
bedrock_url = Helpers.get_latest_bedrock_url()
if bedrock_url.lower().startswith("https"):
urllib.request.urlretrieve(
bedrock_url,
os.path.join(path, "bedrock_server.zip"),
)
unzip_path = os.path.join(path, "bedrock_server.zip")
unzip_path = self.helper.wtol_path(unzip_path)
# unzips archive that was downloaded.
FileHelpers.unzip_file(unzip_path)
# adjusts permissions for execution if os is not windows
if not self.helper.is_os_windows():
os.chmod(os.path.join(path, "bedrock_server"), 0o0744)
# we'll delete the zip we downloaded now
os.remove(os.path.join(path, "bedrock_server.zip"))
except Exception as e:
logger.critical(
f"Failed to download bedrock executable during server creation! \n{e}"
)
ServersController.finish_import(new_id)
server_users = PermissionsServers.get_server_user_list(new_id)
for user in server_users:
self.helper.websocket_helper.broadcast_user(user, "send_start_reload", {})

View File

@ -697,6 +697,49 @@ class Controller:
) )
return new_id return new_id
def create_bedrock_server(self, server_name, user_id):
server_id = Helpers.create_uuid()
new_server_dir = os.path.join(self.helper.servers_dir, server_id)
backup_path = os.path.join(self.helper.backup_path, server_id)
server_exe = "bedrock_server"
if Helpers.is_os_windows():
# if this is windows we will override the linux bedrock server name.
server_exe = "bedrock_server.exe"
new_server_dir = Helpers.wtol_path(new_server_dir)
backup_path = Helpers.wtol_path(backup_path)
new_server_dir.replace(" ", "^ ")
backup_path.replace(" ", "^ ")
Helpers.ensure_dir_exists(new_server_dir)
Helpers.ensure_dir_exists(backup_path)
full_jar_path = os.path.join(new_server_dir, server_exe)
if Helpers.is_os_windows():
server_command = f'"{full_jar_path}"'
else:
server_command = f"./{server_exe}"
logger.debug("command: " + server_command)
server_log_file = ""
server_stop = "stop"
new_id = self.register_server(
server_name,
server_id,
new_server_dir,
backup_path,
server_command,
server_exe,
server_log_file,
server_stop,
"19132",
user_id,
server_type="minecraft-bedrock",
)
ServersController.set_import(new_id)
self.import_helper.download_bedrock_server(new_server_dir, new_id)
return new_id
def import_bedrock_zip_server( def import_bedrock_zip_server(
self, self,
server_name: str, server_name: str,

View File

@ -9,6 +9,7 @@ import threading
import logging.config import logging.config
import subprocess import subprocess
import html import html
import urllib.request
# TZLocal is set as a hidden import on win pipeline # TZLocal is set as a hidden import on win pipeline
from tzlocal import get_localzone from tzlocal import get_localzone
@ -337,7 +338,7 @@ class ServerInstance:
"eula =true", "eula =true",
] ]
if not e_flag: if not e_flag and self.settings["type"] == "minecraft-java":
if user_id: if user_id:
self.helper.websocket_helper.broadcast_user( self.helper.websocket_helper.broadcast_user(
user_id, "send_eula_bootbox", {"id": self.server_id} user_id, "send_eula_bootbox", {"id": self.server_id}
@ -1078,20 +1079,21 @@ class ServerInstance:
) )
# checks if backup directory already exists # checks if backup directory already exists
if os.path.isdir(backup_dir): if os.path.isdir(backup_dir):
backup_executable = os.path.join(backup_dir, "old_server.jar") backup_executable = os.path.join(backup_dir, self.settings["executable"])
else: else:
logger.info( logger.info(
f"Executable backup directory not found for Server: {self.name}." f"Executable backup directory not found for Server: {self.name}."
f" Creating one." f" Creating one."
) )
os.mkdir(backup_dir) os.mkdir(backup_dir)
backup_executable = os.path.join(backup_dir, "old_server.jar") backup_executable = os.path.join(backup_dir, self.settings["executable"])
if os.path.isfile(backup_executable): if len(os.listdir(backup_dir)) > 0:
# removes old backup # removes old backup
logger.info(f"Old backup found for server: {self.name}. Removing...") logger.info(f"Old backups found for server: {self.name}. Removing...")
os.remove(backup_executable) for item in os.listdir(backup_dir):
logger.info(f"Old backup removed for server: {self.name}.") os.remove(os.path.join(backup_dir, item))
logger.info(f"Old backups removed for server: {self.name}.")
else: else:
logger.info(f"No old backups found for server: {self.name}") logger.info(f"No old backups found for server: {self.name}")
@ -1100,31 +1102,76 @@ class ServerInstance:
self.settings["executable"], self.settings["executable"],
) )
try:
# copies to backup dir # copies to backup dir
FileHelpers.copy_file(current_executable, backup_executable) FileHelpers.copy_file(current_executable, backup_executable)
except FileNotFoundError:
logger.error("Could not create backup of jarfile. File not found.")
# wait for backup
while self.is_backingup:
time.sleep(10)
# check if backup was successful
if self.last_backup_failed:
server_users = PermissionsServers.get_server_user_list(self.server_id)
for user in server_users:
self.helper.websocket_helper.broadcast_user(
user,
"notification",
"Backup failed for " + self.name + ". canceling update.",
)
return False
# lets download the files
if HelperServers.get_server_type_by_id(self.server_id) != "minecraft-bedrock":
# boolean returns true for false for success # boolean returns true for false for success
downloaded = Helpers.download_file( downloaded = Helpers.download_file(
self.settings["executable_update_url"], current_executable self.settings["executable_update_url"], current_executable
) )
else:
# downloads zip from remote url
try:
bedrock_url = Helpers.get_latest_bedrock_url()
if bedrock_url.lower().startswith("https"):
urllib.request.urlretrieve(
bedrock_url,
os.path.join(self.settings["path"], "bedrock_server.zip"),
)
while self.stats_helper.get_server_stats()["updating"]: unzip_path = os.path.join(self.settings["path"], "bedrock_server.zip")
if downloaded and not self.is_backingup: unzip_path = self.helper.wtol_path(unzip_path)
# unzips archive that was downloaded.
FileHelpers.unzip_file(unzip_path)
# adjusts permissions for execution if os is not windows
if not self.helper.is_os_windows():
os.chmod(
os.path.join(self.settings["path"], "bedrock_server"), 0o0744
)
# we'll delete the zip we downloaded now
os.remove(os.path.join(self.settings["path"], "bedrock_server.zip"))
downloaded = True
except Exception as e:
logger.critical(
f"Failed to download bedrock executable for update \n{e}"
)
if downloaded:
logger.info("Executable updated successfully. Starting Server") logger.info("Executable updated successfully. Starting Server")
self.stats_helper.set_update(False) self.stats_helper.set_update(False)
if len(self.helper.websocket_helper.clients) > 0: if len(self.helper.websocket_helper.clients) > 0:
# There are clients # There are clients
self.check_update() self.check_update()
server_users = PermissionsServers.get_server_user_list( server_users = PermissionsServers.get_server_user_list(self.server_id)
self.server_id
)
for user in server_users: for user in server_users:
self.helper.websocket_helper.broadcast_user( self.helper.websocket_helper.broadcast_user(
user, user,
"notification", "notification",
"Executable update finished for " + self.name, "Executable update finished for " + self.name,
) )
# sleep so first notif can completely run
time.sleep(3) time.sleep(3)
self.helper.websocket_helper.broadcast_page( self.helper.websocket_helper.broadcast_page(
"/panel/server_detail", "/panel/server_detail",
@ -1138,6 +1185,9 @@ class ServerInstance:
self.helper.websocket_helper.broadcast_page( self.helper.websocket_helper.broadcast_page(
"/panel/dashboard", "send_start_reload", {} "/panel/dashboard", "send_start_reload", {}
) )
self.helper.websocket_helper.broadcast_page(
"/panel/server_detail", "remove_spinner", {}
)
server_users = PermissionsServers.get_server_user_list(self.server_id) server_users = PermissionsServers.get_server_user_list(self.server_id)
for user in server_users: for user in server_users:
self.helper.websocket_helper.broadcast_user( self.helper.websocket_helper.broadcast_user(
@ -1155,8 +1205,7 @@ class ServerInstance:
) )
if was_started: if was_started:
self.start_server() self.start_server()
elif not downloaded and not self.is_backingup: else:
time.sleep(5)
server_users = PermissionsServers.get_server_user_list(self.server_id) server_users = PermissionsServers.get_server_user_list(self.server_id)
for user in server_users: for user in server_users:
self.helper.websocket_helper.broadcast_user( self.helper.websocket_helper.broadcast_user(

View File

@ -756,8 +756,21 @@ class PanelHandler(BaseHandler):
page_data["backup_path"] = Helpers.wtol_path(server_info["backup_path"]) page_data["backup_path"] = Helpers.wtol_path(server_info["backup_path"])
if subpage == "metrics": if subpage == "metrics":
try:
days = int(self.get_argument("days", "1"))
except ValueError as e:
self.redirect(
f"/panel/error?error=Type error: Argument must be an int {e}"
)
page_data["options"] = [1, 2, 3]
if not days in page_data["options"]:
page_data["options"].insert(0, days)
else:
page_data["options"].insert(
0, page_data["options"].pop(page_data["options"].index(days))
)
page_data["history_stats"] = self.controller.servers.get_history_stats( page_data["history_stats"] = self.controller.servers.get_history_stats(
server_id server_id, days
) )
def get_banned_players_html(): def get_banned_players_html():
@ -1500,7 +1513,8 @@ class PanelHandler(BaseHandler):
if Helpers.is_os_windows(): if Helpers.is_os_windows():
server_path.replace(" ", "^ ") server_path.replace(" ", "^ ")
server_path = Helpers.wtol_path(server_path) server_path = Helpers.wtol_path(server_path)
log_path = self.get_argument("log_path", None) log_path = self.get_argument("log_path", "")
if log_path:
if Helpers.is_os_windows(): if Helpers.is_os_windows():
log_path.replace(" ", "^ ") log_path.replace(" ", "^ ")
log_path = Helpers.wtol_path(log_path) log_path = Helpers.wtol_path(log_path)
@ -1510,7 +1524,7 @@ class PanelHandler(BaseHandler):
execution_command = self.get_argument("execution_command", None) execution_command = self.get_argument("execution_command", None)
server_ip = self.get_argument("server_ip", None) server_ip = self.get_argument("server_ip", None)
server_port = self.get_argument("server_port", None) server_port = self.get_argument("server_port", None)
executable_update_url = self.get_argument("executable_update_url", None) executable_update_url = self.get_argument("executable_update_url", "")
show_status = int(float(self.get_argument("show_status", "0"))) show_status = int(float(self.get_argument("show_status", "0")))
else: else:
execution_command = server_obj.execution_command execution_command = server_obj.execution_command

View File

@ -97,6 +97,7 @@ class ServerHandler(BaseHandler):
"version_data": self.helper.get_version_string(), "version_data": self.helper.get_version_string(),
"user_data": exec_user, "user_data": exec_user,
"user_role": exec_user_role, "user_role": exec_user_role,
"online": Helpers.check_internet(),
"roles": list_roles, "roles": list_roles,
"super_user": exec_user["superuser"], "super_user": exec_user["superuser"],
"user_crafty_permissions": exec_user_crafty_permissions, "user_crafty_permissions": exec_user_crafty_permissions,
@ -173,7 +174,6 @@ class ServerHandler(BaseHandler):
) )
return return
page_data["online"] = Helpers.check_internet()
page_data["server_types"] = self.controller.server_jars.get_serverjar_data() page_data["server_types"] = self.controller.server_jars.get_serverjar_data()
page_data["js_server_types"] = json.dumps( page_data["js_server_types"] = json.dumps(
self.controller.server_jars.get_serverjar_data() self.controller.server_jars.get_serverjar_data()
@ -548,25 +548,14 @@ class ServerHandler(BaseHandler):
self.get_remote_ip(), self.get_remote_ip(),
) )
else: else:
if len(server_parts) != 2:
self.redirect("/panel/error?error=Invalid server data") new_server_id = self.controller.create_bedrock_server(
return
server_type, server_version = server_parts
# TODO: add server type check here and call the correct server
# add functions if not a jar
new_server_id = self.controller.create_jar_server(
server_type,
server_version,
server_name, server_name,
min_mem,
max_mem,
port,
exec_user["user_id"], exec_user["user_id"],
) )
self.controller.management.add_to_audit_log( self.controller.management.add_to_audit_log(
exec_user["user_id"], exec_user["user_id"],
f"created a {server_version} {str(server_type).capitalize()} " "created a Bedrock " f'server named "{server_name}"',
f'server named "{server_name}"',
# Example: Admin created a 1.16.5 Bukkit server named "survival" # Example: Admin created a 1.16.5 Bukkit server named "survival"
new_server_id, new_server_id,
self.get_remote_ip(), self.get_remote_ip(),

View File

@ -378,7 +378,7 @@
} }
bootbox.confirm({ bootbox.confirm({
title: "{% raw translate('error', 'eulaTitle', data['lang']) %}", title: "{% raw translate('error', 'eulaTitle', data['lang']) %}",
message: "{% raw translate('error', 'eulaMsg', data['lang']) %} <br><br><a href='https://account.mojang.com/documents/minecraft_eula' target='_blank'>EULA</a><br><br>{% raw translate('error', 'eulaAgree', data['lang']) %}", message: "{% raw translate('error', 'eulaMsg', data['lang']) %}<a href='https://www.minecraft.net/en-us/eula' target='_blank'>Minecraft EULA</a>",
buttons: { buttons: {
confirm: { confirm: {
label: 'Yes', label: 'Yes',

View File

@ -186,7 +186,8 @@
{% elif server['stats']['updating']%} {% elif server['stats']['updating']%}
<!-- WHAT HAPPENED HERE --> <!-- WHAT HAPPENED HERE -->
<a data-id="{{server['server_data']['server_id']}}" class="">{{ translate('serverTerm', 'updating', <a data-id="{{server['server_data']['server_id']}}" class=""><i
class="fa fa-spinner fa-spin"></i>&nbsp;{{ translate('serverTerm', 'updating',
data['lang']) }}</i></a> data['lang']) }}</i></a>
{% elif server['stats']['waiting_start']%} {% elif server['stats']['waiting_start']%}
<!-- WHAT HAPPENED HERE --> <!-- WHAT HAPPENED HERE -->

View File

@ -67,7 +67,7 @@
placeholder="{{ translate('serverConfig', 'serverPath', data['lang']) }}" required> placeholder="{{ translate('serverConfig', 'serverPath', data['lang']) }}" required>
</div> </div>
{% if data['server_stats']['server_type'] != "minecraft-bedrock" %}
<div class="form-group"> <div class="form-group">
<label for="log_path">{{ translate('serverConfig', 'serverLogLocation', data['lang']) }} <small <label for="log_path">{{ translate('serverConfig', 'serverLogLocation', data['lang']) }} <small
class="text-muted ml-1"> - {{ translate('serverConfig', 'serverLogLocationDesc', data['lang']) class="text-muted ml-1"> - {{ translate('serverConfig', 'serverLogLocationDesc', data['lang'])
@ -76,6 +76,7 @@
value="{{ data['server_stats']['server_id']['log_path'] }}" value="{{ data['server_stats']['server_id']['log_path'] }}"
placeholder="{{ translate('serverConfig', 'serverLogLocation', data['lang']) }}" required> placeholder="{{ translate('serverConfig', 'serverLogLocation', data['lang']) }}" required>
</div> </div>
{% end %}
<div class="form-group"> <div class="form-group">
<label for="executable">{{ translate('serverConfig', 'serverExecutable', data['lang']) }} <small <label for="executable">{{ translate('serverConfig', 'serverExecutable', data['lang']) }} <small
@ -138,6 +139,7 @@
</div> </div>
{% if data['super_user'] %} {% if data['super_user'] %}
{% if data['server_stats']['server_type'] != "minecraft-bedrock" %}
<div class="form-group"> <div class="form-group">
<label for="executable_update_url">{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }} <label for="executable_update_url">{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }}
<small class="text-muted ml-1"> - {{ translate('serverConfig', 'exeUpdateURLDesc', data['lang']) <small class="text-muted ml-1"> - {{ translate('serverConfig', 'exeUpdateURLDesc', data['lang'])
@ -146,6 +148,7 @@
value="{{ data['server_stats']['server_id']['executable_update_url'] }}" value="{{ data['server_stats']['server_id']['executable_update_url'] }}"
placeholder="{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }}"> placeholder="{{ translate('serverConfig', 'exeUpdateURL', data['lang']) }}">
</div> </div>
{% end %}
<div class="form-group"> <div class="form-group">
<label for="server_ip">{{ translate('serverConfig', 'serverIP', data['lang']) }} <small <label for="server_ip">{{ translate('serverConfig', 'serverIP', data['lang']) }} <small
@ -250,14 +253,31 @@
<button onclick="send_command(serverId, 'update_executable');" id="update_executable" <button onclick="send_command(serverId, 'update_executable');" id="update_executable"
style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled">{{ translate('serverConfig', style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled">{{ translate('serverConfig',
'update', data['lang']) }}</button> 'update', data['lang']) }}</button>
{% if data['server_stats']['updating'] %}
<button onclick="send_command(serverId, 'update_executable');" id="update_executable"
style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled">{{ translate('serverConfig',
'update', data['lang']) }}&nbsp;<i id="update-spinner" class="fa fa-spinner fa-spin"></i></button>
{% else %}
<button onclick="send_command(serverId, 'update_executable');" id="update_executable"
style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled">{{ translate('serverConfig',
'update', data['lang']) }}&nbsp;<i style="visibility: hidden;" id="update-spinner"
class="fa fa-spinner fa-spin"></i></button>
{% end %}
<a class="btn btn-sm btn-danger disabled">{{ translate('serverConfig', 'deleteServer', data['lang']) <a class="btn btn-sm btn-danger disabled">{{ translate('serverConfig', 'deleteServer', data['lang'])
}}</a><br /> }}</a><br />
<small>{{ translate('serverConfig', 'stopBeforeDeleting', data['lang']) }}</small> <small>{{ translate('serverConfig', 'stopBeforeDeleting', data['lang']) }}</small>
{% else %} {% else %}
{% if not data['failed'] %} {% if not data['failed'] %}
{% if data['server_stats']['updating'] %}
<button onclick="send_command(serverId, 'update_executable');" id="update_executable" <button onclick="send_command(serverId, 'update_executable');" id="update_executable"
style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1">{{ translate('serverConfig', style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1">{{ translate('serverConfig',
'update', data['lang']) }}</button> 'update', data['lang']) }}&nbsp;<i id="update-spinner" class="fa fa-spinner fa-spin"></i></button>
{% else %}
<button onclick="send_command(serverId, 'update_executable');" id="update_executable"
style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1">{{ translate('serverConfig',
'update', data['lang']) }}&nbsp;<i style="visibility: hidden;" id="update-spinner"
class="fa fa-spinner fa-spin"></i></button>
{% end %}
{% end %} {% end %}
{% if not data['failed'] %} {% if not data['failed'] %}
<button onclick="deleteConfirm()" class="btn btn-sm btn-danger">{{ translate('serverConfig', <button onclick="deleteConfirm()" class="btn btn-sm btn-danger">{{ translate('serverConfig',
@ -353,6 +373,9 @@
function send_command(serverId, command) { function send_command(serverId, command) {
//<!-- this getCookie function is in base.html--> //<!-- this getCookie function is in base.html-->
var token = getCookie("_xsrf"); var token = getCookie("_xsrf");
if (command == "update_executable") {
document.getElementById("update-spinner").style.visibility = "visible";
}
$.ajax({ $.ajax({
type: "POST", type: "POST",
@ -505,6 +528,12 @@
}); });
} }
$(document).ready(function () {
webSocket.on('remove_spinner', function () {
document.getElementById("update-spinner").style.visibility = "hidden";
});
});
</script> </script>
{% end %} {% end %}

View File

@ -38,6 +38,16 @@
<span class="d-block d-sm-none"> <span class="d-block d-sm-none">
{% include "parts/m_server_controls_list.html %} {% include "parts/m_server_controls_list.html %}
</span> </span>
<div class="col-2">
<div class="form-group">
<label for="days">Metric Period</label>
<select required class="form-control form-control-lg select-css" id="days" name="days">
{% for value in data['options'] %}
<option value="{{value}}">{{value}} Day(s)</option>
{% end %}
</select>
</div>
</div>
<button style="float: right; visibility: hidden;" class="btn btn-outline-success reset-button" <button style="float: right; visibility: hidden;" class="btn btn-outline-success reset-button"
id="reset-button"><i class="fas fa-undo"></i>&nbsp;{{ translate('serverMetrics', 'resetZoom', data['lang']) id="reset-button"><i class="fas fa-undo"></i>&nbsp;{{ translate('serverMetrics', 'resetZoom', data['lang'])
}}</button> }}</button>
@ -69,6 +79,7 @@
} }
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
const serverId = new URLSearchParams(document.location.search).get('id')
var zoomed = false; var zoomed = false;
//line //line
var ctxL = document.getElementById("lineChart").getContext('2d'); var ctxL = document.getElementById("lineChart").getContext('2d');
@ -162,6 +173,10 @@
} }
}); });
$(window).ready(function () { $(window).ready(function () {
$('#days').on("change", function () {
let days = $('#days').find(":selected").val();
window.location.href = "/panel/server_detail?id=" + serverId + "&days=" + days + "&subpage=metrics"
});
$('body').click(function () { $('body').click(function () {
$('.hints').popover("hide"); $('.hints').popover("hide");
}); });

View File

@ -55,7 +55,8 @@
{% if data['permissions']['Commands'] in data['user_permissions'] %} {% if data['permissions']['Commands'] in data['user_permissions'] %}
{% if data['server_stats']['updating']%} {% if data['server_stats']['updating']%}
<div id="update_control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible"> <div id="update_control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
<button onclick="" id="start-btn" style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'updating', data['lang']) }}</button> <button onclick="" id="start-btn" style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled"><i
class="fa fa-spinner fa-spin"></i>&nbsp;{{ translate('serverTerm', 'updating', data['lang']) }}</button>
<button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button> <button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button> <button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
</div> </div>

View File

@ -18,6 +18,67 @@
<br> <br>
<div class="d-none" id="overlay" onclick="hide(event)"></div> <div class="d-none" id="overlay" onclick="hide(event)"></div>
<div class="row"> <div class="row">
{% if data['online'] %}
<div class="col-sm-6 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h4>{{ translate('serverWizard', 'newServer', data['lang']) }}</h4>
<br />
<p class="card-description">
<form method="post" name="create_server" class="server-wizard" onSubmit="wait_msg()">
{% raw xsrf_form_html() %}
<div class="row">
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
<input type="text" class="form-control" id="server_name" name="server_name"
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
</div>
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<div id="accordion-1">
<div class="card">
<div class="card-header p-2" id="Role-1">
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-1" aria-expanded="true"
aria-controls="collapseRole-1">
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }}
<small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
data['lang']) }}</small>
</p>
</div>
<div id="collapseRole-1" class="collapse" aria-labelledby="Role-1" data-parent="">
<div class="card-body scroll">
<div class="form-group">
{% for r in data['roles'] %}
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
type="checkbox">&nbsp;
{{ r['role_name'].capitalize() }}</label></span>
{% end %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<button onclick="eula_confirm()" type="button" class="btn btn-primary mr-2">{{ translate('serverWizard',
'buildServer',
data['lang']) }}</button>
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm', data['lang'])
}}</button>
</div>
</form>
</p>
</div>
</div>
{% end %}
<div class="col-sm-6 grid-margin stretch-card"> <div class="col-sm-6 grid-margin stretch-card">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
@ -34,21 +95,25 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="form-group"> <div class="form-group">
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label> <label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
<input type="text" class="form-control" id="server_name" name="server_name" value="" placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required> <input type="text" class="form-control" id="server_name" name="server_name" value=""
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
</div> </div>
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<div class="form-group"> <div class="form-group">
<label for="server">{{ translate('serverWizard', 'serverPath', data['lang']) }} <small>{{ translate('serverWizard', 'absoluteServerPath', data['lang']) }}</small></label> <label for="server">{{ translate('serverWizard', 'serverPath', data['lang']) }} <small>{{
<input type="text" class="form-control" id="server_path" name="server_path" placeholder="/var/opt/server" required> translate('serverWizard', 'absoluteServerPath', data['lang']) }}</small></label>
<input type="text" class="form-control" id="server_path" name="server_path"
placeholder="/var/opt/server" required>
</div> </div>
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<div class="form-group"> <div class="form-group">
<label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label> <label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label>
<input type="text" class="form-control" id="server_jar" name="server_jar" value="" placeholder="bedrock_server" required> <input type="text" class="form-control" id="server_jar" name="server_jar" value=""
placeholder="bedrock_server" required>
</div> </div>
</div> </div>
@ -56,14 +121,18 @@
</div> </div>
<br /> <br />
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription', data['lang']) }}</small></h4> <h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
data['lang']) }}</small></h4>
<hr> <hr>
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="form-group"> <div class="form-group">
<label for="port2">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small></small></label> <label for="port2">{{ translate('serverWizard', 'serverPort', data['lang']) }}
<input type="number" class="form-control" id="port2" name="port" value="19132" step="1" min="1" required> <small></small></label>
<input type="number" class="form-control" id="port2" name="port" value="19132" step="1" min="1"
required>
</div> </div>
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
@ -71,15 +140,19 @@
<div id="accordion-2"> <div id="accordion-2">
<div class="card"> <div class="card">
<div class="card-header p-2" id="Role-2"> <div class="card-header p-2" id="Role-2">
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-2" aria-expanded="true" aria-controls="collapseRole-2"> <p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-2" aria-expanded="true"
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }} <small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate', data['lang']) }}</small> aria-controls="collapseRole-2">
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }}
<small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
data['lang']) }}</small>
</p> </p>
</div> </div>
<div id="collapseRole-2" class="collapse" aria-labelledby="Role-2" data-parent=""> <div id="collapseRole-2" class="collapse" aria-labelledby="Role-2" data-parent="">
<div class="card-body scroll"> <div class="card-body scroll">
<div class="form-group"> <div class="form-group">
{% for r in data['roles'] %} {% for r in data['roles'] %}
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}" type="checkbox">&nbsp; <span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
type="checkbox">&nbsp;
{{ r['role_name'].capitalize() }}</label></span> {{ r['role_name'].capitalize() }}</label></span>
{% end %} {% end %}
</div> </div>
@ -90,8 +163,10 @@
</div> </div>
</div> </div>
</div> </div>
<button type="submit" class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton', data['lang']) }}</button> <button type="submit" class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton',
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm', data['lang']) }}</button> data['lang']) }}</button>
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm', data['lang'])
}}</button>
</form> </form>
</p> </p>
@ -115,22 +190,27 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="form-group"> <div class="form-group">
<label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label> <label for="server_name">{{ translate('serverWizard', 'serverName', data['lang']) }}</label>
<input type="text" class="form-control" id="server_name" name="server_name" value="" placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required> <input type="text" class="form-control" id="server_name" name="server_name" value=""
placeholder="{{ translate('serverWizard', 'myNewServer', data['lang']) }}" required>
</div> </div>
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<div class="form-group"> <div class="form-group">
<label for="server">{{ translate('serverWizard', 'zipPath', data['lang']) }} <small>{{ translate('serverWizard', 'absoluteZipPath', data['lang']) }}</small></label> <label for="server">{{ translate('serverWizard', 'zipPath', data['lang']) }} <small>{{
<input type="text" class="form-control" id="server_path" name="server_path" placeholder="/var/opt/server.zip" required> translate('serverWizard', 'absoluteZipPath', data['lang']) }}</small></label>
<input type="text" class="form-control" id="server_path" name="server_path"
placeholder="/var/opt/server.zip" required>
</div> </div>
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<div class="form-group"> <div class="form-group">
<label for="server">{{ translate('serverWizard', 'selectRoot', data['lang']) }} <small>{{ translate('serverWizard', 'explainRoot', data['lang']) }}</small></label> <label for="server">{{ translate('serverWizard', 'selectRoot', data['lang']) }} <small>{{
translate('serverWizard', 'explainRoot', data['lang']) }}</small></label>
<br> <br>
<button class="btn btn-primary mr-2" id="root_files_button" type="button">{{ translate('serverWizard', 'clickRoot', data['lang']) }}</button> <button class="btn btn-primary mr-2" id="root_files_button" type="button">{{
translate('serverWizard', 'clickRoot', data['lang']) }}</button>
</div> </div>
</div> </div>
@ -138,7 +218,8 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="form-group"> <div class="form-group">
<label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label> <label for="server_jar">{{ translate('serverWizard', 'serverJar', data['lang']) }}</label>
<input type="text" class="form-control" id="server_jar" name="server_jar" value="" placeholder="bedrock_server" required> <input type="text" class="form-control" id="server_jar" name="server_jar" value=""
placeholder="bedrock_server" required>
</div> </div>
</div> </div>
</div> </div>
@ -147,14 +228,18 @@
<div class="col-sm-12"> <div class="col-sm-12">
<h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription', data['lang']) }}</small></h4> <h4 class="card-title">{{ translate('serverWizard', 'quickSettings', data['lang']) }} <small
style="text-transform: none;"> - {{ translate('serverWizard', 'quickSettingsDescription',
data['lang']) }}</small></h4>
<hr> <hr>
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="form-group"> <div class="form-group">
<label for="port3">{{ translate('serverWizard', 'serverPort', data['lang']) }} <small></small></label> <label for="port3">{{ translate('serverWizard', 'serverPort', data['lang']) }}
<input type="number" class="form-control" id="port3" name="port" value="19132" step="1" min="1" required> <small></small></label>
<input type="number" class="form-control" id="port3" name="port" value="19132" step="1" min="1"
required>
</div> </div>
</div> </div>
@ -163,15 +248,19 @@
<div id="accordion-3"> <div id="accordion-3">
<div class="card"> <div class="card">
<div class="card-header p-2" id="Role-3"> <div class="card-header p-2" id="Role-3">
<p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-3" aria-expanded="true" aria-controls="collapseRole-3"> <p class="mb-0 p-0" data-toggle="collapse" data-target="#collapseRole-3" aria-expanded="true"
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang']) }} <small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate', data['lang']) }}</small> aria-controls="collapseRole-3">
<i class="fas fa-chevron-down"></i> {{ translate('serverWizard', 'addRole', data['lang'])
}} <small style="text-transform: none;"> - {{ translate('serverWizard', 'autoCreate',
data['lang']) }}</small>
</p> </p>
</div> </div>
<div id="collapseRole-3" class="collapse" aria-labelledby="Role-3" data-parent=""> <div id="collapseRole-3" class="collapse" aria-labelledby="Role-3" data-parent="">
<div class="card-body scroll"> <div class="card-body scroll">
<div class="form-group"> <div class="form-group">
{% for r in data['roles'] %} {% for r in data['roles'] %}
<span class="d-block menu-option"><label><input name="{{ r['role_id'] }}" type="checkbox">&nbsp; <span class="d-block menu-option"><label><input name="{{ r['role_id'] }}"
type="checkbox">&nbsp;
{{ r['role_name'].capitalize() }}</label></span> {{ r['role_name'].capitalize() }}</label></span>
{% end %} {% end %}
</div> </div>
@ -186,17 +275,20 @@
<input type="text" class="form-control" id="zip_root_path" name="zip_root_path"> <input type="text" class="form-control" id="zip_root_path" name="zip_root_path">
</div> </div>
</div> </div>
<div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select" aria-hidden="true"> <div class="modal fade" id="dir_select" tabindex="-1" role="dialog" aria-labelledby="dir_select"
aria-hidden="true">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">{{ translate('serverWizard', 'selectZipDir', data['lang']) }}</h5> <h5 class="modal-title" id="exampleModalLongTitle">{{ translate('serverWizard',
'selectZipDir', data['lang']) }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="tree-ctx-item" id="main-tree-div" data-path="" style="overflow: scroll; max-height:75%;"> <div class="tree-ctx-item" id="main-tree-div" data-path=""
style="overflow: scroll; max-height:75%;">
<input type="radio" id="main-tree-input" name="root_path" value="" checked> <input type="radio" id="main-tree-input" name="root_path" value="" checked>
<span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path=""> <span id="main-tree" class="files-tree-title tree-caret-down root-dir" data-path="">
<i class="far fa-folder"></i> <i class="far fa-folder"></i>
@ -207,15 +299,20 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ translate('serverWizard', 'close', data['lang']) }}</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">{{
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary">{{ translate('serverWizard', 'save', data['lang']) }}</button> translate('serverWizard', 'close', data['lang']) }}</button>
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary">{{
translate('serverWizard', 'save', data['lang']) }}</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<button id="zip_submit" type="submit" title="You must select server root dir first" disabled class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton', data['lang']) }}</button> <button id="zip_submit" type="submit" title="You must select server root dir first" disabled
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm', data['lang']) }}</button> class="btn btn-primary mr-2">{{ translate('serverWizard', 'importServerButton', data['lang'])
}}</button>
<button type="reset" class="btn btn-danger mr-2">{{ translate('serverWizard', 'resetForm', data['lang'])
}}</button>
</div> </div>
</div> </div>
</form> </form>
@ -229,10 +326,12 @@
max-height: 12em; max-height: 12em;
overflow-y: auto; overflow-y: auto;
} }
.menu-btn { .menu-btn {
font-size: 0.9em; font-size: 0.9em;
padding: 2px 10px; padding: 2px 10px;
} }
.menu { .menu {
padding-top: 10px; padding-top: 10px;
z-index: 200; z-index: 200;
@ -240,10 +339,12 @@
position: absolute; position: absolute;
background-color: #2a2c44; background-color: #2a2c44;
} }
.menu-option { .menu-option {
padding: 6px 20px 6px; padding: 6px 20px 6px;
color: white; color: white;
} }
#overlay { #overlay {
position: absolute; position: absolute;
top: 0px; top: 0px;
@ -267,13 +368,15 @@
.tree-item, .tree-item,
.files-tree-title { .files-tree-title {
cursor: pointer; cursor: pointer;
user-select: none; /* Prevent text selection */ user-select: none;
/* Prevent text selection */
} }
/* Create the caret/arrow with a unicode, and style it */ /* Create the caret/arrow with a unicode, and style it */
.tree-caret .fa-folder { .tree-caret .fa-folder {
display: inline-block; display: inline-block;
} }
.tree-caret .fa-folder-open { .tree-caret .fa-folder-open {
display: none; display: none;
} }
@ -282,6 +385,7 @@
.tree-caret-down .fa-folder { .tree-caret-down .fa-folder {
display: none; display: none;
} }
.tree-caret-down .fa-folder-open { .tree-caret-down .fa-folder-open {
display: inline-block; display: inline-block;
} }
@ -295,6 +399,32 @@
{% end %} {% end %}
{% block js%} {% block js%}
<script>
function eula_confirm() {
bootbox.confirm({
title: "{% raw translate('error', 'eulaTitle', data['lang']) %}",
message: "{% raw translate('error', 'eulaMsg', data['lang']) %}<a href='https://www.minecraft.net/en-us/eula' target='_blank'>Minecraft EULA</a>&nbsp; {% raw translate('error', 'privMsg', data['lang']) %}<a target='_blank' href='https://privacy.microsoft.com/en-us/privacystatement'>Microsoft Privacy Policy</a>",
buttons: {
confirm: {
label: "{% raw translate('error', 'agree', data['lang']) %}",
className: 'btn-info'
},
cancel: {
label: "{% raw translate('error', 'cancel', data['lang']) %}",
className: 'btn-secondary'
}
},
callback: function (result) {
if (result == true) {
document.create_server.submit();
}
else {
return;
}
}
})
}
</script>
<script> <script>
document.getElementById("root_files_button").addEventListener("click", function () { document.getElementById("root_files_button").addEventListener("click", function () {
if (document.forms["zip"]["server_path"].value != "") { if (document.forms["zip"]["server_path"].value != "") {

View File

@ -168,7 +168,7 @@
"embarassing": "Oh je, das ist peinlich.", "embarassing": "Oh je, das ist peinlich.",
"error": "Fehler!", "error": "Fehler!",
"eulaAgree": "Stimmen Sie zu?", "eulaAgree": "Stimmen Sie zu?",
"eulaMsg": "Sie müssen der EULA zustimmen. Eine Kopie der Mojang EULA ist unter dieser Nachricht verlinkt.", "eulaMsg": "Sie müssen der EULA zustimmen. Eine Kopie der Minecraft EULA ist unter dieser Nachricht verlinkt.",
"eulaTitle": "Der EULA zustimmen", "eulaTitle": "Der EULA zustimmen",
"hereIsTheError": "Das ist der Fehler", "hereIsTheError": "Das ist der Fehler",
"internet": "Wir haben festgestellt, dass der Rechner, auf dem Crafty läuft, keine Verbindung zum Internet hat. Client-Verbindungen zum Server können dadurch eingeschränkt sein.", "internet": "Wir haben festgestellt, dass der Rechner, auf dem Crafty läuft, keine Verbindung zum Internet hat. Client-Verbindungen zum Server können dadurch eingeschränkt sein.",

View File

@ -168,8 +168,11 @@
"embarassing": "Oh my, well, this is embarrassing.", "embarassing": "Oh my, well, this is embarrassing.",
"error": "Error!", "error": "Error!",
"eulaAgree": "Do you agree?", "eulaAgree": "Do you agree?",
"eulaMsg": "You must agree to the EULA. A copy of the Mojang EULA is linked under this message.", "eulaMsg": "You must agree to the ",
"privMsg": "and the ",
"eulaTitle": "Agree To EULA", "eulaTitle": "Agree To EULA",
"agree": "Agree",
"cancel": "Cancel",
"fileTooLarge": "Upload failed. File upload too large. Contact system administrator for assistance.", "fileTooLarge": "Upload failed. File upload too large. Contact system administrator for assistance.",
"hereIsTheError": "Here is the error", "hereIsTheError": "Here is the error",
"internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.", "internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.",

View File

@ -169,7 +169,7 @@
"embarassing": "Oh por, bueno, esto es vergonzoso.", "embarassing": "Oh por, bueno, esto es vergonzoso.",
"error": "Error!", "error": "Error!",
"eulaAgree": "Estás de acuerdo?", "eulaAgree": "Estás de acuerdo?",
"eulaMsg": "Debes aceptar el EULA. Una copia del EULA de Mojang esta vinculada debajo de este mensaje.", "eulaMsg": "Debes aceptar el EULA. Una copia del EULA de Minecraft esta vinculada debajo de este mensaje.",
"eulaTitle": "Aceptar EULA", "eulaTitle": "Aceptar EULA",
"fileTooLarge": "Subida fallida. Carga de archivo demasiado grande. Póngase en contacto con el administrador del sistema para obtener ayuda.", "fileTooLarge": "Subida fallida. Carga de archivo demasiado grande. Póngase en contacto con el administrador del sistema para obtener ayuda.",
"hereIsTheError": "Aquí está el error.", "hereIsTheError": "Aquí está el error.",

View File

@ -192,7 +192,7 @@
"embarassing": "No, tämähän on noloa.", "embarassing": "No, tämähän on noloa.",
"error": "Virhe!", "error": "Virhe!",
"eulaAgree": "Oletko samaa mieltä?", "eulaAgree": "Oletko samaa mieltä?",
"eulaMsg": "Sinun on hyväksyttävä EULA. Kopio Mojang EULA:sta on linkitetty tämän viestin alla.", "eulaMsg": "Sinun on hyväksyttävä EULA. Kopio Minecraft EULA:sta on linkitetty tämän viestin alla.",
"eulaTitle": "Hyväksy EULA", "eulaTitle": "Hyväksy EULA",
"fileTooLarge": "Lataus epäonnistui. Tiedosto on liian suuri. Ota yhteyttä järjestelmänvalvojaan saadaksesi apua.", "fileTooLarge": "Lataus epäonnistui. Tiedosto on liian suuri. Ota yhteyttä järjestelmänvalvojaan saadaksesi apua.",
"hereIsTheError": "Tässä on virhe", "hereIsTheError": "Tässä on virhe",

View File

@ -168,7 +168,7 @@
"embarassing": "Oulà, c'est embarrassant.", "embarassing": "Oulà, c'est embarrassant.",
"error": "Erreur !", "error": "Erreur !",
"eulaAgree": "Êtes-vous d'accord?", "eulaAgree": "Êtes-vous d'accord?",
"eulaMsg": "Vous devez accepter le EULA. Une copie du CLUF de Mojang est liée sous ce message.", "eulaMsg": "Vous devez accepter le EULA. Une copie du CLUF de Minecraft est liée sous ce message.",
"eulaTitle": "Accepter le EULA", "eulaTitle": "Accepter le EULA",
"fileTooLarge": "Echec du chargement. Le fichier est trop gros. Demande de l'aide à l'administrateur système.", "fileTooLarge": "Echec du chargement. Le fichier est trop gros. Demande de l'aide à l'administrateur système.",
"hereIsTheError": "Il y a une erreur", "hereIsTheError": "Il y a une erreur",

View File

@ -167,7 +167,7 @@
"embarassing": "Och, dit is beskamsum.", "embarassing": "Och, dit is beskamsum.",
"error": "Error!", "error": "Error!",
"eulaAgree": "Binne jo akkoard?", "eulaAgree": "Binne jo akkoard?",
"eulaMsg": "Jo moatte ynstimme mei de EULA. In kopy fan 'e Mojang EULA is keppele ûnder dit berjocht.", "eulaMsg": "Jo moatte ynstimme mei de EULA. In kopy fan 'e Minecraft EULA is keppele ûnder dit berjocht.",
"eulaTitle": "Akseptearje mei EULA", "eulaTitle": "Akseptearje mei EULA",
"hereIsTheError": "Hjir is de error", "hereIsTheError": "Hjir is de error",
"internet": "Wy hawwe ûntdutsen dat de masine dy't Crafty draait gjin ferbining hat mei it ynternet. Clientferbiningen mei de server kinne beheind wêze.", "internet": "Wy hawwe ûntdutsen dat de masine dy't Crafty draait gjin ferbining hat mei it ynternet. Clientferbiningen mei de server kinne beheind wêze.",

View File

@ -167,7 +167,7 @@
"embarassing": "Uhh, ovo je sramotno.", "embarassing": "Uhh, ovo je sramotno.",
"error": "Greška!", "error": "Greška!",
"eulaAgree": "Slažete li se?", "eulaAgree": "Slažete li se?",
"eulaMsg": "Morate prihvatiti EULA-u. Poveznica Mojangove EULA-e se nalazi ispod ove poruke.", "eulaMsg": "Morate prihvatiti EULA-u. Poveznica Minecraft EULA-e se nalazi ispod ove poruke.",
"eulaTitle": "Prihvatite EULA-u", "eulaTitle": "Prihvatite EULA-u",
"hereIsTheError": "Ovdje je pogreška", "hereIsTheError": "Ovdje je pogreška",
"internet": "Otkrili smo da uređaj koji pokreće Crafty nije povezan s internetom. Povezivanje klijenta s poslužiteljem može biti ograničeno.", "internet": "Otkrili smo da uređaj koji pokreće Crafty nije povezan s internetom. Povezivanje klijenta s poslužiteljem može biti ograničeno.",

View File

@ -168,7 +168,7 @@
"embarassing": "Oh tidak, ini adalah hal yang memalukan", "embarassing": "Oh tidak, ini adalah hal yang memalukan",
"error": "Error!", "error": "Error!",
"eulaAgree": "Apakah anda setuju?", "eulaAgree": "Apakah anda setuju?",
"eulaMsg": "Anda harus menyetujui EULA. Salinan Mojang EULA ditautkan di bawah pesan ini.", "eulaMsg": "Anda harus menyetujui EULA. Salinan Minecraft EULA ditautkan di bawah pesan ini.",
"eulaTitle": "Setuju Ke EULA", "eulaTitle": "Setuju Ke EULA",
"fileTooLarge": "Gagal menggungah. File yang ingin di unggah terlalu besar. Kontak administrator system untuk pengawasan", "fileTooLarge": "Gagal menggungah. File yang ingin di unggah terlalu besar. Kontak administrator system untuk pengawasan",
"hereIsTheError": "Disini Error nya", "hereIsTheError": "Disini Error nya",

View File

@ -167,7 +167,7 @@
"embarassing": "Oh my, well, this is embarrassing.", "embarassing": "Oh my, well, this is embarrassing.",
"error": "Error!", "error": "Error!",
"eulaAgree": "Do you agree?", "eulaAgree": "Do you agree?",
"eulaMsg": "You must agree to the EULA. A copy of the Mojang EULA is linked under this message.", "eulaMsg": "You must agree to the EULA. A copy of the Minecraft EULA is linked under this message.",
"eulaTitle": "Agree To EULA", "eulaTitle": "Agree To EULA",
"hereIsTheError": "Here is the error", "hereIsTheError": "Here is the error",
"internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.", "internet": "We have detected the machine running Crafty has no connection to the internet. Client connections to the server may be limited.",

View File

@ -168,7 +168,7 @@
"embarassing": "Ak nu, tas ir apkaunojoši.", "embarassing": "Ak nu, tas ir apkaunojoši.",
"error": "Kļūda!", "error": "Kļūda!",
"eulaAgree": "Vai jūs piekrītat?", "eulaAgree": "Vai jūs piekrītat?",
"eulaMsg": "Jums ir jāpiekrīt EULA. Kopija no Mojang EULA ir saitēta zem šī ziņojuma.", "eulaMsg": "Jums ir jāpiekrīt EULA. Kopija no Minecraft EULA ir saitēta zem šī ziņojuma.",
"eulaTitle": "Piekrist EULA", "eulaTitle": "Piekrist EULA",
"fileTooLarge": "Augšupielāde neizdevās. Faila augšupielāde ir pārāk liela. Sazinies ar sistēmas administratoru priekš palīdzības.", "fileTooLarge": "Augšupielāde neizdevās. Faila augšupielāde ir pārāk liela. Sazinies ar sistēmas administratoru priekš palīdzības.",
"hereIsTheError": "Rekur ir kļūda", "hereIsTheError": "Rekur ir kļūda",

View File

@ -168,7 +168,7 @@
"embarassing": "Oh, nou, dit is gênant.", "embarassing": "Oh, nou, dit is gênant.",
"error": "Fout!", "error": "Fout!",
"eulaAgree": "Bent u het eens?", "eulaAgree": "Bent u het eens?",
"eulaMsg": "U moet akkoord gaan met de EULA. Een kopie van de Mojang EULA is gelinkt onder dit bericht.", "eulaMsg": "U moet akkoord gaan met de EULA. Een kopie van de Minecraft EULA is gelinkt onder dit bericht.",
"eulaTitle": "Akkoord gaan met EULA", "eulaTitle": "Akkoord gaan met EULA",
"fileTooLarge": "Uploaden mislukt. Bestand uploaden te groot. Neem contact op met de systeembeheerder voor assistentie", "fileTooLarge": "Uploaden mislukt. Bestand uploaden te groot. Neem contact op met de systeembeheerder voor assistentie",
"hereIsTheError": "Hier is de fout", "hereIsTheError": "Hier is de fout",

View File

@ -167,7 +167,7 @@
"embarassing": "Oh, oeps, dit is gênant.", "embarassing": "Oh, oeps, dit is gênant.",
"error": "Error!", "error": "Error!",
"eulaAgree": "Stem je in met de EULA?", "eulaAgree": "Stem je in met de EULA?",
"eulaMsg": "Je moet instemmen met de EULA. Een kopie van de Mojang EULA is onder dit bericht gelinkt.", "eulaMsg": "Je moet instemmen met de EULA. Een kopie van de Minecraft EULA is onder dit bericht gelinkt.",
"eulaTitle": "Instemmen met de EULA", "eulaTitle": "Instemmen met de EULA",
"hereIsTheError": "Hier is de error", "hereIsTheError": "Hier is de error",
"internet": "We hebben gedetecteerd dat de serverhost geen internetverbinding heeft. Client connecties zijn mogelijk gelimiteerd.", "internet": "We hebben gedetecteerd dat de serverhost geen internetverbinding heeft. Client connecties zijn mogelijk gelimiteerd.",

View File

@ -168,7 +168,7 @@
"embarassing": "Oh, więc, to jest żenujące.", "embarassing": "Oh, więc, to jest żenujące.",
"error": "Błąd!", "error": "Błąd!",
"eulaAgree": "Czy się zgadzasz?", "eulaAgree": "Czy się zgadzasz?",
"eulaMsg": "Musisz się zgodzić na EULA. Kopia EULA Mojangu jest zalinkowana pod tą wiadomością.", "eulaMsg": "Musisz się zgodzić na EULA. Kopia EULA Minecraft jest zalinkowana pod tą wiadomością.",
"eulaTitle": "Zgódź się na EULA", "eulaTitle": "Zgódź się na EULA",
"fileTooLarge": "Upload nie udany. Plik jest za duży. Skontaktuj się z administratorem dla pomocy.", "fileTooLarge": "Upload nie udany. Plik jest za duży. Skontaktuj się z administratorem dla pomocy.",
"hereIsTheError": "Tu jest problem", "hereIsTheError": "Tu jest problem",

View File

@ -168,7 +168,7 @@
"embarassing": "Oh. Bem, isso é constrangedor", "embarassing": "Oh. Bem, isso é constrangedor",
"error": "Erro!", "error": "Erro!",
"eulaAgree": "Você concorda?", "eulaAgree": "Você concorda?",
"eulaMsg": "Você deve concordar com os Termos de Licença. Uma cópia dos Termos de Licença da Mojang está linkado abaixo desta mensagem.", "eulaMsg": "Você deve concordar com os Termos de Licença. Uma cópia dos Termos de Licença da Minecraft está linkado abaixo desta mensagem.",
"eulaTitle": "Concordar com os Termos de Licença", "eulaTitle": "Concordar com os Termos de Licença",
"fileTooLarge": "Upload falhou. Arquivo muito grande. Contato o administrador do sistema para assistência.", "fileTooLarge": "Upload falhou. Arquivo muito grande. Contato o administrador do sistema para assistência.",
"hereIsTheError": "Aqui está o erro", "hereIsTheError": "Aqui está o erro",

View File

@ -168,7 +168,7 @@
"embarassing": "哦,天哪,这太尴尬了。", "embarassing": "哦,天哪,这太尴尬了。",
"error": "错误!", "error": "错误!",
"eulaAgree": "你同意吗?", "eulaAgree": "你同意吗?",
"eulaMsg": "你必须同意最终用户许可协议EULA。一份 Mojang EULA 副本的链接在此消息下方。", "eulaMsg": "你必须同意最终用户许可协议EULA。一份 Minecraft EULA 副本的链接在此消息下方。",
"eulaTitle": "同意最终用户许可协议EULA", "eulaTitle": "同意最终用户许可协议EULA",
"fileTooLarge": "上传失败。上传的文件过大。联系系统管理员以获取协助。", "fileTooLarge": "上传失败。上传的文件过大。联系系统管理员以获取协助。",
"hereIsTheError": "错误如下", "hereIsTheError": "错误如下",