diff --git a/CHANGELOG.md b/CHANGELOG.md index 56b9730d..4b2417a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,24 @@ # Changelog +## --- [4.0.20] - 2022/01/29 +### New features +- Add option to run command before backup. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/536)) +- Make Config.json editable from panel. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/532)) +- Managed config.json refector (See MR for details). ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/485)) +### Bug fixes +- Fix local java server imports. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/529)) +- Fix Schedule Restore | Add Backup Config Preservation. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/533)) +- Rework `/public` Route. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/538)) +### Tweaks +- Hide stats DB directory from files tree. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/530)) +- Make it so file tree doesn't reload on upload/delete. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/541)) +- Add upload completed feedback to file upload. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/541)) +- Added further login screen customisation settings. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/531)) +- Set backup filename to use same time as schedule. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/534)) +- Move Schedules to from DB to Queue Datatype. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/535)) +- Move raknet icon failure to a debug log. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/537)) +- Add Default redirection to Dashboard if the user is connected. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/540)) +

+ ## --- [4.0.19] - 2022/01/07 ### Bug fixes - Fix port tooltip not showing on dash while server online. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/503)) diff --git a/README.md b/README.md index f9a19ae1..059df365 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com) -# Crafty Controller 4.0.19 +# Crafty Controller 4.0.20 > Python based Control Panel for your Minecraft Server ## What is Crafty Controller? diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py index 47860fe1..2811dce4 100644 --- a/app/classes/controllers/management_controller.py +++ b/app/classes/controllers/management_controller.py @@ -1,4 +1,5 @@ import logging +import queue from app.classes.models.management import HelpersManagement from app.classes.models.servers import HelperServers @@ -9,6 +10,26 @@ logger = logging.getLogger(__name__) class ManagementController: def __init__(self, management_helper): self.management_helper = management_helper + self.command_queue = queue.Queue() + + # ********************************************************************************** + # Config Methods + # ********************************************************************************** + @staticmethod + def set_login_image(path): + HelpersManagement.set_login_image(path) + + @staticmethod + def get_login_image(): + return HelpersManagement.get_login_image() + + @staticmethod + def set_login_opacity(opacity): + return HelpersManagement.set_login_opacity(opacity) + + @staticmethod + def get_login_opacity(): + return HelpersManagement.get_login_opacity() # ********************************************************************************** # Host_Stats Methods @@ -25,12 +46,17 @@ class ManagementController: def get_crafty_api_key(): return HelpersManagement.get_secret_api_key() + @staticmethod + def set_cookie_secret(key): + HelpersManagement.set_cookie_secret(key) + + @staticmethod + def add_crafty_row(): + HelpersManagement.create_crafty_row() + # ********************************************************************************** # Commands Methods # ********************************************************************************** - @staticmethod - def get_unactioned_commands(): - return HelpersManagement.get_unactioned_commands() def send_command(self, user_id, server_id, remote_ip, command): server_name = HelperServers.get_server_friendly_name(server_id) @@ -42,11 +68,12 @@ class ManagementController: server_id, remote_ip, ) - HelpersManagement.add_command(server_id, user_id, remote_ip, command) + self.queue_command( + {"server_id": server_id, "user_id": user_id, "command": command} + ) - @staticmethod - def mark_command_complete(command_id=None): - return HelpersManagement.mark_command_complete(command_id) + def queue_command(self, command_data): + self.command_queue.put(command_data) # ********************************************************************************** # Audit_Log Methods @@ -78,6 +105,10 @@ class ManagementController: command, name, enabled=True, + one_time=False, + cron_string="* * * * *", + parent=None, + delay=0, ): return HelpersManagement.create_scheduled_task( server_id, @@ -88,20 +119,16 @@ class ManagementController: command, name, enabled, + one_time, + cron_string, + parent, + delay, ) @staticmethod def delete_scheduled_task(schedule_id): return HelpersManagement.delete_scheduled_task(schedule_id) - @staticmethod - def set_login_image(path): - HelpersManagement.set_login_image(path) - - @staticmethod - def get_login_image(): - return HelpersManagement.get_login_image() - @staticmethod def update_scheduled_task(schedule_id, updates): return HelpersManagement.update_scheduled_task(schedule_id, updates) @@ -145,9 +172,18 @@ class ManagementController: excluded_dirs: list = None, compress: bool = False, shutdown: bool = False, + before: str = "", + after: str = "", ): return self.management_helper.set_backup_config( - server_id, backup_path, max_backups, excluded_dirs, compress, shutdown + server_id, + backup_path, + max_backups, + excluded_dirs, + compress, + shutdown, + before, + after, ) @staticmethod diff --git a/app/classes/minecraft/stats.py b/app/classes/minecraft/stats.py index e3611509..07dd9c0d 100644 --- a/app/classes/minecraft/stats.py +++ b/app/classes/minecraft/stats.py @@ -300,7 +300,7 @@ class Stats: server_icon = base64.encodebytes(ping_obj["icon"]) except Exception as e: server_icon = False - logger.info( + logger.debug( "Unable to read the server icon due to the following error:", exc_info=e ) ping_data = { diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 55c86bb7..c2b5afde 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -13,7 +13,7 @@ from peewee import ( from playhouse.shortcuts import model_to_dict from app.classes.models.base_model import BaseModel -from app.classes.models.users import Users, HelperUsers +from app.classes.models.users import HelperUsers from app.classes.models.servers import Servers from app.classes.models.server_permissions import PermissionsServers from app.classes.shared.main_models import DatabaseShortcuts @@ -43,7 +43,9 @@ class AuditLog(BaseModel): # ********************************************************************************** class CraftySettings(BaseModel): secret_api_key = CharField(default="") + cookie_secret = CharField(default="") login_photo = CharField(default="login_1.jpg") + login_opacity = IntegerField(default=100) class Meta: table_name = "crafty_settings" @@ -68,22 +70,6 @@ class HostStats(BaseModel): table_name = "host_stats" -# ********************************************************************************** -# Commands Class -# ********************************************************************************** -class Commands(BaseModel): - command_id = AutoField() - created = DateTimeField(default=datetime.datetime.now) - server_id = ForeignKeyField(Servers, backref="server", index=True) - user = ForeignKeyField(Users, backref="user", index=True) - source_ip = CharField(default="127.0.0.1") - command = CharField(default="") - executed = BooleanField(default=False) - - class Meta: - table_name = "commands" - - # ********************************************************************************** # Webhooks Class # ********************************************************************************** @@ -131,6 +117,8 @@ class Backups(BaseModel): server_id = ForeignKeyField(Servers, backref="backups_server") compress = BooleanField(default=False) shutdown = BooleanField(default=False) + before = CharField(default="") + after = CharField(default="") class Meta: table_name = "backups" @@ -150,33 +138,6 @@ class HelpersManagement: query = HostStats.select().order_by(HostStats.id.desc()).get() return model_to_dict(query) - # ********************************************************************************** - # Commands Methods - # ********************************************************************************** - @staticmethod - def add_command(server_id, user_id, remote_ip, command): - Commands.insert( - { - Commands.server_id: server_id, - Commands.user: user_id, - Commands.source_ip: remote_ip, - Commands.command: command, - } - ).execute() - - @staticmethod - def get_unactioned_commands(): - query = Commands.select().where(Commands.executed == 0) - return query - - @staticmethod - def mark_command_complete(command_id=None): - if command_id is not None: - logger.debug(f"Marking Command {command_id} completed") - Commands.update({Commands.executed: True}).where( - Commands.command_id == command_id - ).execute() - # ********************************************************************************** # Audit_Log Methods # ********************************************************************************** @@ -244,9 +205,22 @@ class HelpersManagement: else: return + @staticmethod + def create_crafty_row(): + CraftySettings.insert( + { + CraftySettings.secret_api_key: "", + CraftySettings.cookie_secret: "", + CraftySettings.login_photo: "login_1.jpg", + CraftySettings.login_opacity: 100, + } + ).execute() + @staticmethod def set_secret_api_key(key): - CraftySettings.insert(secret_api_key=key).execute() + CraftySettings.update({CraftySettings.secret_api_key: key}).where( + CraftySettings.id == 1 + ).execute() @staticmethod def get_secret_api_key(): @@ -255,6 +229,22 @@ class HelpersManagement: ) return settings[0].secret_api_key + @staticmethod + def get_cookie_secret(): + settings = CraftySettings.select(CraftySettings.cookie_secret).where( + CraftySettings.id == 1 + ) + return settings[0].cookie_secret + + @staticmethod + def set_cookie_secret(key): + CraftySettings.update({CraftySettings.cookie_secret: key}).where( + CraftySettings.id == 1 + ).execute() + + # ********************************************************************************** + # Config Methods + # ********************************************************************************** @staticmethod def get_login_image(): settings = CraftySettings.select(CraftySettings.login_photo).where( @@ -268,6 +258,19 @@ class HelpersManagement: CraftySettings.id == 1 ).execute() + @staticmethod + def get_login_opacity(): + settings = CraftySettings.select(CraftySettings.login_opacity).where( + CraftySettings.id == 1 + ) + return settings[0].login_opacity + + @staticmethod + def set_login_opacity(opacity): + CraftySettings.update({CraftySettings.login_opacity: opacity}).where( + CraftySettings.id == 1 + ).execute() + # ********************************************************************************** # Schedules Methods # ********************************************************************************** @@ -369,6 +372,8 @@ class HelpersManagement: "server_id": row.server_id_id, "compress": row.compress, "shutdown": row.shutdown, + "before": row.before, + "after": row.after, } except IndexError: conf = { @@ -378,6 +383,8 @@ class HelpersManagement: "server_id": server_id, "compress": False, "shutdown": False, + "before": "", + "after": "", } return conf @@ -393,6 +400,8 @@ class HelpersManagement: excluded_dirs: list = None, compress: bool = False, shutdown: bool = False, + before: str = "", + after: str = "", ): logger.debug(f"Updating server {server_id} backup config with {locals()}") if Backups.select().where(Backups.server_id == server_id).exists(): @@ -405,6 +414,8 @@ class HelpersManagement: "server_id": server_id, "compress": False, "shutdown": False, + "before": "", + "after": "", } new_row = True if max_backups is not None: @@ -414,6 +425,8 @@ class HelpersManagement: conf["excluded_dirs"] = dirs_to_exclude conf["compress"] = compress conf["shutdown"] = shutdown + conf["before"] = before + conf["after"] = after if not new_row: with self.database.atomic(): if backup_path is not None: @@ -473,9 +486,3 @@ class HelpersManagement: f"Not removing {dir_to_del} from excluded directories - " f"not in the excluded directory list for server ID {server_id}" ) - - @staticmethod - def clear_unexecuted_commands(): - Commands.update({Commands.executed: True}).where( - Commands.executed == False # pylint: disable=singleton-comparison - ).execute() diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index c5238ae8..92226425 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -78,6 +78,7 @@ class Helpers: self.websocket_helper = WebSocketHelper(self) self.translation = Translation(self) self.update_available = False + self.ignored_names = ["crafty_managed.txt", "db_stats"] @staticmethod def auto_installer_fix(ex): @@ -376,6 +377,64 @@ class Helpers: return default_return + def set_settings(self, data): + try: + with open(self.settings_file, "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + + except Exception as e: + logger.critical( + f"Config File Error: Unable to read {self.settings_file} due to {e}" + ) + Console.critical( + f"Config File Error: Unable to read {self.settings_file} due to {e}" + ) + return False + + return True + + @staticmethod + def get_master_config(): + # Make changes for users' local config.json files here. As of 4.0.20 + # Config.json was removed from the repo to make it easier for users + # To make non-breaking changes to the file. + return { + "http_port": 8000, + "https_port": 8443, + "language": "en_EN", + "cookie_expire": 30, + "show_errors": True, + "history_max_age": 7, + "stats_update_frequency": 30, + "delete_default_json": False, + "show_contribute_link": True, + "virtual_terminal_lines": 70, + "max_log_lines": 700, + "max_audit_entries": 300, + "disabled_language_files": [], + "stream_size_GB": 1, + "keywords": ["help", "chunk"], + "allow_nsfw_profile_pictures": False, + "enable_user_self_delete": False, + "reset_secrets_on_next_boot": False, + } + + def get_all_settings(self): + try: + with open(self.settings_file, "r", encoding="utf-8") as f: + data = json.load(f) + + except Exception as e: + data = {} + logger.critical( + f"Config File Error: Unable to read {self.settings_file} due to {e}" + ) + Console.critical( + f"Config File Error: Unable to read {self.settings_file} due to {e}" + ) + + return data + @staticmethod def is_subdir(server_path, root_dir): server_path = os.path.realpath(server_path) @@ -947,8 +1006,7 @@ class Helpers: return data - @staticmethod - def generate_tree(folder, output=""): + def generate_tree(self, folder, output=""): dir_list = [] unsorted_files = [] file_list = os.listdir(folder) @@ -965,18 +1023,22 @@ class Helpers: rel = os.path.join(folder, raw_filename) dpath = os.path.join(folder, filename) if os.path.isdir(rel): - output += f"""
  • - \n
    - - - - {filename} - -
  • - \n""" + if filename not in self.ignored_names: + output += f"""
  • + \n
    + + + + {filename} + +
  • + \n""" else: - if filename != "crafty_managed.txt": - output += f"""
  • {filename}
  • """ return output - @staticmethod - def generate_dir(folder, output=""): + def generate_dir(self, folder, output=""): dir_list = [] unsorted_files = [] @@ -1004,17 +1065,21 @@ class Helpers: dpath = os.path.join(folder, filename) rel = os.path.join(folder, raw_filename) if os.path.isdir(rel): - output += f"""
  • - \n
    - - - - {filename} - -
  • """ + if filename not in self.ignored_names: + output += f"""
  • + \n
    + + + + {filename} + +
  • """ else: - if filename != "crafty_managed.txt": - output += f"""
  • -
  • + + + - + - - + {% if data['user_data']['preparing'] %} + {{ translate('notify', 'supportLogs', data['lang']) }}

    + +
    +
    0%
    +
    +
    + {% else %} + {{ translate('notify', 'supportLogs', data['lang']) }} + {% end %} + {% if data['superuser'] %} + {{ translate('notify', 'activityLog', data['lang']) }} + {% end %} + {{ translate('notify', 'logout', data['lang']) }} + + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/frontend/templates/panel/config_json.html b/app/frontend/templates/panel/config_json.html new file mode 100644 index 00000000..a5acdcc1 --- /dev/null +++ b/app/frontend/templates/panel/config_json.html @@ -0,0 +1,320 @@ +{% extends ../base.html %} + +{% block meta %} +{% end %} + +{% block title %}Crafty Controller - {{ translate('panelConfig', 'pageTitle', data['lang']) }}{% end %} + +{% block content %} + + + +
    + +
    +
    + +
    +
    + + +
    +
    +
    +
    + + {% if data['superuser'] %} + {% include "parts/crafty_config_list.html %} + {% end %} + + +
    +
    + +
    + +
    + + +
    + {% raw xsrf_form_html() %} + + {% for item in data['config-json'].items() %} + {% if item[0] == "reset_secrets_on_next_boot" %} +
    + {% else %} +
    + {% end %} +
    + {% if item[0] == 'language' %} + + {% elif item[0] == 'disabled_language_files' %} +
    + + + +
    + {% elif isinstance(item[1], list) %} + + {% elif isinstance(item[1], bool) %} +
    + {% if item[1] == True %} + +  
    + +   + {% else %} + +  
    + +   + {% end %} +
    + {% elif isinstance(item[1], int) %} + + {% else %} + + {% end %} +
    + {% end %} +   + +
    +
    +
    +
    + + +
    + + + + +{% end %} + +{% block js %} + + + +{% end %} \ No newline at end of file diff --git a/app/frontend/templates/panel/custom_login.html b/app/frontend/templates/panel/custom_login.html new file mode 100644 index 00000000..018eb006 --- /dev/null +++ b/app/frontend/templates/panel/custom_login.html @@ -0,0 +1,391 @@ +{% extends ../base.html %} + +{% block meta %} +{% end %} + +{% block title %}Crafty Controller - {{ translate('customLogin', 'pageTitle', data['lang']) }}{% end %} + +{% block content %} + +
    + +
    +
    + +
    +
    + + +
    +
    +
    +
    + + + {% if data['superuser'] %} + {% include "parts/crafty_config_list.html %} + {% end %} + + +
    +
    + +
    + +
    + + +
    +
    +
    +
    +
    +
    +

    {{ translate('customLogin', 'loginImage', data['lang']) }}

    +
    +
    + {% raw xsrf_form_html() %} + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    {{ translate('customLogin', 'preview', data['lang']) }}:
    +
    +
    + +
    + +
    +
    + +
    + + +
    + +
    +
    +
    + Responsive image +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + + + + +{% end %} + +{% block js %} + + + +{% end %} \ No newline at end of file diff --git a/app/frontend/templates/panel/denied.html b/app/frontend/templates/panel/denied.html index f924e553..717c0def 100644 --- a/app/frontend/templates/panel/denied.html +++ b/app/frontend/templates/panel/denied.html @@ -1,69 +1,82 @@ - - - - - Crafty Controller - - - - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    + + + + + Crafty Controller + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    + +
    +


    -
    - -
    -
    +
    -
    - +
    - - - - - - - - - - - - + +
    + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/frontend/templates/panel/panel_config.html b/app/frontend/templates/panel/panel_config.html index a4ee9f66..249daf70 100644 --- a/app/frontend/templates/panel/panel_config.html +++ b/app/frontend/templates/panel/panel_config.html @@ -8,16 +8,16 @@ {% block content %}
    -
    -
    @@ -26,6 +26,23 @@
    + + {% if data['superuser'] %} + {% include "parts/crafty_config_list.html %} + {% end %} + + +
    +
    + +
    + +
    + +
    @@ -33,9 +50,7 @@

    {{ translate('panelConfig', 'users', data['lang']) }}

    {% if data['user_data']['hints'] %} - + {% end %} -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - -

    {{ translate('panelConfig', 'loginImage', data['lang']) }}

    -
    -

    - -

    - {% raw xsrf_form_html() %} - - -
    -
    - -
    -
    -
    - - - - -
    -
    -
    -
    -
    - -

    -
    -
    -
    -
    -
    -

    {{ translate('panelConfig', 'loginBackground', data['lang']) }}




    -
    - -
    -
    -
    {{ translate('panelConfig', 'preview', data['lang']) }}:
    - -
    -
    -
    - - -
    -
    @@ -350,97 +295,5 @@ } }); - {% end %} \ No newline at end of file diff --git a/app/frontend/templates/panel/parts/crafty_config_list.html b/app/frontend/templates/panel/parts/crafty_config_list.html new file mode 100644 index 00000000..5ef6b922 --- /dev/null +++ b/app/frontend/templates/panel/parts/crafty_config_list.html @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index a50b55b5..4af0ba1a 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -107,6 +107,40 @@ translate('serverBackups', 'shutdown', data['lang']) }} {% end %}
    +
    + + {% if data['backup_config']['before'] %} + Run + Command Before Backup +
    + + {% else %} + Run Command + Before Backup +
    + + {% end %} +
    +
    + + {% if data['backup_config']['after'] %} + Run + Command After Backup +
    + + {% else %} + Run Command + Before Backup +
    + + {% end %} +
    @@ -344,6 +378,22 @@ }); } + $("#before-check").on("click", function () { + if ($("#before-check:checked").val()) { + $("#backup_before").css("display", "inline-block"); + } else { + $("#backup_before").css("display", "none"); + $("#backup_before").val(""); + } + }); + $("#after-check").on("click", function () { + if ($("#after-check:checked").val()) { + $("#backup_after").css("display", "inline-block"); + } else { + $("#backup_after").css("display", "none"); + $("#backup_after").val(""); + } + }); $(document).ready(function () { try { diff --git a/app/frontend/templates/panel/server_files.html b/app/frontend/templates/panel/server_files.html index af287b43..5734f3a6 100644 --- a/app/frontend/templates/panel/server_files.html +++ b/app/frontend/templates/panel/server_files.html @@ -697,7 +697,7 @@ }); } - function sendFile(file, path, serverId, left, onProgress) { + async function sendFile(file, path, serverId, left, i, onProgress) { let xmlHttpRequest = new XMLHttpRequest(); let token = getCookie("_xsrf") let fileName = file.name @@ -719,7 +719,36 @@ onProgress(Math.floor(event.loaded / event.total * 100)), false); xmlHttpRequest.addEventListener('load', (event) => { if (event.target.responseText == 'success') { - console.log('Upload for file', file.name, 'was successful!') + console.log('Upload for file', file.name, 'was successful!'); + let caught = false; + try { + var par_el = document.getElementById(path + "ul"); + var items = par_el.children; + } catch { + caught = true; + var par_el = document.getElementById("files-tree"); + var items = par_el.children; + } + let name = file.name; + console.log(par_el) + let full_path = path + '/' + name + let flag = false; + for (var k = 0; k < items.length; ++k) { + if ($(items[k]).attr("data-name") == name) { + flag = true; + } + } + if (!flag) { + if (caught) { + $(par_el).append('
  • ' + name + '
  • '); + } else { + $(par_el).append('
  • ' + name + '
  • '); + } + setTreeViewContext(); + } + $(`#upload-progress-bar-${i + 1}`).removeClass("progress-bar-striped"); + $(`#upload-progress-bar-${i + 1}`).addClass("bg-success"); + $(`#upload-progress-bar-${i + 1}`).html('') } else { alert('Upload failed with response: ' + event.target.responseText); @@ -735,7 +764,7 @@ let uploadWaitDialog; let doUpload = true; - function uploadFilesE(event) { + async function uploadFilesE(event) { path = event.target.parentElement.getAttribute('data-path'); $(function () { var uploadHtml = "
    " + @@ -795,7 +824,7 @@ `; $('#upload-progress-bar-parent').append(progressHtml); - sendFile(files.files[i], path, serverId, nFiles - i - 1, (progress) => { + await sendFile(files.files[i], path, serverId, nFiles - i - 1, i, (progress) => { $(`#upload-progress-bar-${i + 1}`).attr('aria-valuenow', progress) $(`#upload-progress-bar-${i + 1}`).css('width', progress + '%') }); @@ -996,7 +1025,6 @@ function hideUploadBox() { if (!uploadWaitDialog) return; uploadWaitDialog.modal('hide'); - getTreeView(); } function createFileE(event) { @@ -1074,7 +1102,8 @@ callback: function (result) { if (!result) return; deleteFile(path, function () { - getTreeView() + el = document.getElementById(path + "li"); + $(el).remove(); document.getElementById('files-tree-nav').style.display = 'none'; }); } @@ -1102,7 +1131,8 @@ callback: function (result) { if (!result) return; deleteDir(path, function () { - getTreeView() + el = document.getElementById(path + "li"); + $(el).remove(); document.getElementById('files-tree-nav').style.display = 'none'; }); } diff --git a/app/frontend/templates/public/404.html b/app/frontend/templates/public/404.html index 480fe5cf..c598dd19 100644 --- a/app/frontend/templates/public/404.html +++ b/app/frontend/templates/public/404.html @@ -23,7 +23,7 @@ @@ -36,8 +37,9 @@
    -