mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'lukas-development' into 'dev'
File Manager See merge request crafty-controller/crafty-commander!19
This commit is contained in:
commit
c154f316b7
@ -51,7 +51,7 @@ class Controller:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# if this server path no longer exists - let's warn and bomb out
|
# if this server path no longer exists - let's warn and bomb out
|
||||||
if not helper.check_path_exits(s['path']):
|
if not helper.check_path_exists(s['path']):
|
||||||
logger.warning("Unable to find server {} at path {}. Skipping this server".format(s['server_name'],
|
logger.warning("Unable to find server {} at path {}. Skipping this server".format(s['server_name'],
|
||||||
s['path']))
|
s['path']))
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import base64
|
|||||||
import socket
|
import socket
|
||||||
import random
|
import random
|
||||||
import logging
|
import logging
|
||||||
|
import html
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from socket import gethostname
|
from socket import gethostname
|
||||||
@ -280,7 +281,7 @@ class Helpers:
|
|||||||
return "%.1f%s%s" % (num, 'Y', suffix)
|
return "%.1f%s%s" % (num, 'Y', suffix)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_path_exits(path: str):
|
def check_path_exists(path: str):
|
||||||
logger.debug('Looking for path: {}'.format(path))
|
logger.debug('Looking for path: {}'.format(path))
|
||||||
|
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
@ -463,6 +464,34 @@ class Helpers:
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate_tree(folder, output=""):
|
||||||
|
for raw_filename in os.listdir(folder):
|
||||||
|
print(raw_filename)
|
||||||
|
filename = html.escape(raw_filename)
|
||||||
|
print(filename)
|
||||||
|
rel = os.path.join(folder, raw_filename)
|
||||||
|
if os.path.isdir(rel):
|
||||||
|
output += \
|
||||||
|
"""<li class="tree-item" data-path="{}">
|
||||||
|
\n<div data-path="{}" data-name="{}" class="tree-caret tree-ctx-item tree-folder">{}</div>
|
||||||
|
\n<ul class="tree-nested">"""\
|
||||||
|
.format(os.path.join(folder, filename), os.path.join(folder, filename), filename, filename)
|
||||||
|
|
||||||
|
output += helper.generate_tree(rel)
|
||||||
|
output += '</ul>\n</li>'
|
||||||
|
else:
|
||||||
|
console.debug('os.path.isdir(rel): "{}", rel: "{}"'.format(os.path.isdir(rel), rel))
|
||||||
|
output += """<li
|
||||||
|
class="tree-item tree-ctx-item tree-file"
|
||||||
|
data-path="{}"
|
||||||
|
data-name="{}"
|
||||||
|
onclick="clickOnFile(event)">{}</li>""".format(os.path.join(folder, filename), filename, filename)
|
||||||
|
return output
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def in_path(x, y):
|
||||||
|
return os.path.abspath(y).__contains__(os.path.abspath(x))
|
||||||
|
|
||||||
|
|
||||||
helper = Helpers()
|
helper = Helpers()
|
||||||
|
@ -97,7 +97,7 @@ class Server:
|
|||||||
console.critical("Server executable path: {} does not seem to exist".format(full_path))
|
console.critical("Server executable path: {} does not seem to exist".format(full_path))
|
||||||
helper.do_exit()
|
helper.do_exit()
|
||||||
|
|
||||||
if not helper.check_path_exits(self.server_path):
|
if not helper.check_path_exists(self.server_path):
|
||||||
logger.critical("Server path: {} does not seem to exits".format(self.server_path))
|
logger.critical("Server path: {} does not seem to exits".format(self.server_path))
|
||||||
console.critical("Server path: {} does not seem to exits".format(self.server_path))
|
console.critical("Server path: {} does not seem to exits".format(self.server_path))
|
||||||
helper.do_exit()
|
helper.do_exit()
|
||||||
|
@ -3,6 +3,8 @@ import logging
|
|||||||
import tornado.web
|
import tornado.web
|
||||||
import tornado.escape
|
import tornado.escape
|
||||||
import bleach
|
import bleach
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
from app.classes.shared.console import console
|
from app.classes.shared.console import console
|
||||||
from app.classes.shared.models import Users, installer
|
from app.classes.shared.models import Users, installer
|
||||||
@ -79,7 +81,58 @@ class AjaxHandler(BaseHandler):
|
|||||||
page_data['notify_data'] = data
|
page_data['notify_data'] = data
|
||||||
self.render_page('ajax/notify.html', page_data)
|
self.render_page('ajax/notify.html', page_data)
|
||||||
|
|
||||||
|
elif page == "get_file":
|
||||||
|
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")
|
||||||
|
console.warning("Server ID not found in get_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
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)):
|
||||||
|
logger.warning("Invalid path in get_file ajax call")
|
||||||
|
console.warning("Invalid path in get_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
file = open(file_path)
|
||||||
|
file_contents = file.read()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
console.debug("Send file contents")
|
||||||
|
self.write(file_contents)
|
||||||
|
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")
|
||||||
|
console.warning("Server ID not found in get_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
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']))
|
||||||
|
self.finish()
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
def post(self, page):
|
def post(self, page):
|
||||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||||
error = bleach.clean(self.get_argument('error', "WTF Error!"))
|
error = bleach.clean(self.get_argument('error', "WTF Error!"))
|
||||||
@ -90,11 +143,12 @@ class AjaxHandler(BaseHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if page == "send_command":
|
if page == "send_command":
|
||||||
command = bleach.clean(self.get_body_argument('command', default=None, strip=True))
|
command = self.get_body_argument('command', default=None, strip=True)
|
||||||
server_id = bleach.clean(self.get_argument('id'))
|
server_id = self.get_argument('id')
|
||||||
|
|
||||||
if server_id is None:
|
if server_id is None:
|
||||||
logger.warning("Server ID not found in send_command ajax call")
|
logger.warning("Server ID not found in send_command ajax call")
|
||||||
|
console.warning("Server ID not found in send_command ajax call")
|
||||||
|
|
||||||
srv_obj = controller.get_server_obj(server_id)
|
srv_obj = controller.get_server_obj(server_id)
|
||||||
|
|
||||||
@ -102,3 +156,192 @@ class AjaxHandler(BaseHandler):
|
|||||||
if srv_obj.check_running():
|
if srv_obj.check_running():
|
||||||
srv_obj.send_command(command)
|
srv_obj.send_command(command)
|
||||||
|
|
||||||
|
elif page == "create_file":
|
||||||
|
file_parent = self.get_body_argument('file_parent', default=None, strip=True)
|
||||||
|
file_name = self.get_body_argument('file_name', default=None, strip=True)
|
||||||
|
file_path = os.path.join(file_parent, file_name)
|
||||||
|
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")
|
||||||
|
console.warning("Server ID not found in create_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
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)):
|
||||||
|
logger.warning("Invalid path in create_file ajax call")
|
||||||
|
console.warning("Invalid path in create_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Create the file by opening it
|
||||||
|
with open(file_path, 'w') as file_object:
|
||||||
|
file_object.close()
|
||||||
|
|
||||||
|
elif page == "create_dir":
|
||||||
|
dir_parent = self.get_body_argument('dir_parent', default=None, strip=True)
|
||||||
|
dir_name = self.get_body_argument('dir_name', default=None, strip=True)
|
||||||
|
dir_path = os.path.join(dir_parent, dir_name)
|
||||||
|
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")
|
||||||
|
console.warning("Server ID not found in create_dir ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
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)):
|
||||||
|
logger.warning("Invalid path in create_dir ajax call")
|
||||||
|
console.warning("Invalid path in create_dir ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Create the directory
|
||||||
|
os.mkdir(dir_path)
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
|
def delete(self, page):
|
||||||
|
if page == "del_file":
|
||||||
|
file_path = self.get_body_argument('file_path', default=None, strip=True)
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 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")
|
||||||
|
console.warning("Server ID not found in del_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
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)):
|
||||||
|
logger.warning("Invalid path in del_file ajax call")
|
||||||
|
console.warning("Invalid path in del_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Delete the file
|
||||||
|
os.remove(file_path)
|
||||||
|
|
||||||
|
elif page == "del_dir":
|
||||||
|
dir_path = self.get_body_argument('dir_path', default=None, strip=True)
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 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")
|
||||||
|
console.warning("Server ID not found in del_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not helper.in_path(db_helper.get_server_data_by_id(server_id)['path'], dir_path) \
|
||||||
|
or not helper.check_path_exists(os.path.abspath(dir_path)):
|
||||||
|
logger.warning("Invalid path in del_file ajax call")
|
||||||
|
console.warning("Invalid path in del_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Delete the file
|
||||||
|
# os.rmdir(dir_path)
|
||||||
|
shutil.rmtree(dir_path) # Removes also when there are contents
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
|
def put(self, page):
|
||||||
|
if page == "save_file":
|
||||||
|
file_contents = self.get_body_argument('file_contents', default=None, strip=True)
|
||||||
|
file_path = self.get_body_argument('file_path', default=None, strip=True)
|
||||||
|
server_id = self.get_argument('id', None)
|
||||||
|
print(file_contents)
|
||||||
|
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")
|
||||||
|
console.warning("Server ID not found in save_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
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)):
|
||||||
|
logger.warning("Invalid path in save_file ajax call")
|
||||||
|
console.warning("Invalid path in save_file ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Open the file in write mode and store the content in file_object
|
||||||
|
with open(file_path, 'w') as file_object:
|
||||||
|
file_object.write(file_contents)
|
||||||
|
|
||||||
|
elif page == "rename_item":
|
||||||
|
item_path = self.get_body_argument('item_path', default=None, strip=True)
|
||||||
|
new_item_name = self.get_body_argument('new_item_name', default=None, strip=True)
|
||||||
|
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")
|
||||||
|
console.warning("Server ID not found in rename_item 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 rename_item ajax call (1)")
|
||||||
|
console.warning("Server ID not found in rename_item ajax call (1)")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if item_path is None or new_item_name is None:
|
||||||
|
logger.warning("Invalid path in rename_item ajax call (2)")
|
||||||
|
console.warning("Invalid path in rename_item ajax call (2)")
|
||||||
|
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 (3)")
|
||||||
|
console.warning("Invalid path in rename_item ajax call (3)")
|
||||||
|
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")
|
||||||
|
console.warning("Invalid path 2 in rename_item ajax call")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# RENAME
|
||||||
|
os.rename(item_path, new_item_path)
|
||||||
|
@ -68,9 +68,6 @@ class PanelHandler(BaseHandler):
|
|||||||
elif page == 'file_edit':
|
elif page == 'file_edit':
|
||||||
template = "panel/file_edit.html"
|
template = "panel/file_edit.html"
|
||||||
|
|
||||||
elif page == 'files_menu':
|
|
||||||
template = "panel/files_menu.html"
|
|
||||||
|
|
||||||
elif page == "remove_server":
|
elif page == "remove_server":
|
||||||
server_id = self.get_argument('id', None)
|
server_id = self.get_argument('id', None)
|
||||||
server_data = controller.get_server_data(server_id)
|
server_data = controller.get_server_data(server_id)
|
||||||
@ -112,10 +109,12 @@ class PanelHandler(BaseHandler):
|
|||||||
self.redirect("/panel/error?error=Invalid Server ID")
|
self.redirect("/panel/error?error=Invalid Server ID")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
valid_subpages = ['term', 'logs', 'config']
|
valid_subpages = ['term', 'logs', 'config', 'files']
|
||||||
|
|
||||||
if subpage not in valid_subpages:
|
if subpage not in valid_subpages:
|
||||||
|
console.debug('not a valid subpage')
|
||||||
subpage = 'term'
|
subpage = 'term'
|
||||||
|
console.debug('Subpage: "{}"'.format(subpage))
|
||||||
|
|
||||||
# server_data isn't needed since the server_stats also pulls server data
|
# server_data isn't needed since the server_stats also pulls server data
|
||||||
# page_data['server_data'] = db_helper.get_server_data_by_id(server_id)
|
# page_data['server_data'] = db_helper.get_server_data_by_id(server_id)
|
||||||
|
@ -64,6 +64,14 @@
|
|||||||
</li>
|
</li>
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/panel/files?id=1">
|
||||||
|
<i class="fas fa-copy"></i>
|
||||||
|
<span class="menu-title">Files Test</span>
|
||||||
|
</a>
|
||||||
|
</li>-->
|
||||||
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
{% extends ../base.html %}
|
|
||||||
|
|
||||||
{% block meta %}
|
|
||||||
<!-- <meta http-equiv="refresh" content="60">-->
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
{% block title %}Crafty Controller - Looking at files in server -< server name >- (-< server path id >-){% end %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="content-wrapper">
|
|
||||||
|
|
||||||
<!-- Page Title Header Starts-->
|
|
||||||
<div class="row page-title-header">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="page-header">
|
|
||||||
<h4 class="page-title">
|
|
||||||
Looking at files in server -< server name >- (-< server path id >-)
|
|
||||||
<br />
|
|
||||||
<small>Path: -< location relative to server directory >-</small>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- Page Title Header Ends-->
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col-sm-12 grid-margin">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
|
|
||||||
<ul class="files-list list-group">
|
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center"
|
|
||||||
style="background-color: var(--dark) !important;">
|
|
||||||
<a href="/panel/file_edit?path=-< file location relative to server directory >-" class="text-white">-< name >-</a>
|
|
||||||
<span>
|
|
||||||
<a href="/panel/file_edit?path=-< file location relative to server directory >-" class="mx-1 btn btn-dark">Edit</a>
|
|
||||||
<button onclick="alert('will make a modal')" class="mx-1 btn btn-dark">Rename</button>
|
|
||||||
<button onclick="alert('will make a confirmation modal')" class="mx-1 btn btn-danger">Delete</button>
|
|
||||||
<span style="margin-left: 0.3rem;width: 5rem;display: inline-block;">
|
|
||||||
<span class="badge badge-primary badge-pill">File</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center"
|
|
||||||
style="background-color: var(--dark) !important;">
|
|
||||||
<a href="/panel/files_menu?path=-< location relative to server directory >-" class="text-white">-< name >-</a>
|
|
||||||
<span>
|
|
||||||
<a href="/panel/files_menu?path=-< location relative to server directory >-" class="mx-1 btn btn-dark">Open</a>
|
|
||||||
<button onclick="alert('will make a modal')" class="mx-1 btn btn-dark">Rename</button>
|
|
||||||
<button onclick="alert('will make a confirmation modal')" class="mx-1 btn btn-danger">Delete</button>
|
|
||||||
<span style="margin-left: 0.3rem;width: 5rem;display: inline-block;">
|
|
||||||
<span class="badge badge-primary badge-pill">Folder</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
File:
|
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center"
|
|
||||||
style="background-color: var(--dark) !important;">
|
|
||||||
<a href="/panel/file_edit?path=-< file location relative to server directory >-" class="text-white">-< name >-</a>
|
|
||||||
<span>
|
|
||||||
<a href="/panel/file_edit?path=-< file location relative to server directory >-" class="mx-1 btn btn-dark">Edit</a>
|
|
||||||
<button onclick="alert('will make a modal')" class="mx-1 btn btn-dark">Rename</button>
|
|
||||||
<button onclick="alert('will make a confirmation modal')" class="mx-1 btn btn-danger">Delete</button>
|
|
||||||
<span style="margin-left: 0.3rem;width: 5rem;display: inline-block;">
|
|
||||||
<span class="badge badge-primary badge-pill">File</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
Folder:
|
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center"
|
|
||||||
style="background-color: var(--dark) !important;">
|
|
||||||
<a href="/panel/files_menu?path=-< location relative to server directory >-" class="text-white">-< name >-</a>
|
|
||||||
<span>
|
|
||||||
<a href="/panel/files_menu?path=-< location relative to server directory >-" class="mx-1 btn btn-dark">Open</a>
|
|
||||||
<button onclick="alert('will make a modal')" class="mx-1 btn btn-dark">Rename</button>
|
|
||||||
<button onclick="alert('will make a confirmation modal')" class="mx-1 btn btn-danger">Delete</button>
|
|
||||||
<span style="margin-left: 0.3rem;width: 5rem;display: inline-block;">
|
|
||||||
<span class="badge badge-primary badge-pill">Folder</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
-->
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- content-wrapper ends -->
|
|
||||||
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
{% block js %}
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
// empty for now
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% end %}
|
|
657
app/frontend/templates/panel/server_files.html
Normal file
657
app/frontend/templates/panel/server_files.html
Normal file
@ -0,0 +1,657 @@
|
|||||||
|
{% extends ../base.html %}
|
||||||
|
|
||||||
|
{% block meta %}
|
||||||
|
<!-- <meta http-equiv="refresh" content="60">-->
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% block title %}Crafty Controller - Server Details{% end %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
|
||||||
|
<!-- Page Title Header Starts-->
|
||||||
|
<div class="row page-title-header">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="page-header">
|
||||||
|
<h4 class="page-title">
|
||||||
|
Server Details - {{ data['server_stats'][0]['server_id']['server_name'] }}
|
||||||
|
<br />
|
||||||
|
<small>UUID: {{ data['server_stats'][0]['server_id']['server_uuid'] }}</small>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- Page Title Header Ends-->
|
||||||
|
|
||||||
|
{% include "parts/details_stats.html %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-sm-12 grid-margin">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body pt-0">
|
||||||
|
<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'][0]['server_id']['server_id'] }}&subpage=term" role="tab" aria-selected="false">
|
||||||
|
<i class="fas fa-file-signature"></i>Terminal</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=logs" role="tab" aria-selected="false">
|
||||||
|
<i class="fas fa-file-signature"></i>Logs</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=tasks" role="tab" aria-selected="false">
|
||||||
|
<i class="fas fa-clock"></i>Schedule</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=backup" role="tab" aria-selected="false">
|
||||||
|
<i class="fas fa-save"></i>Backup</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=files" role="tab" aria-selected="false">
|
||||||
|
<i class="fas fa-folder-tree"></i>Files</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/panel/server_detail?id={{ data['server_stats'][0]['server_id']['server_id'] }}&subpage=config" role="tab" aria-selected="true">
|
||||||
|
<i class="fas fa-cogs"></i>Config</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 col-sm-12">
|
||||||
|
<noscript>
|
||||||
|
The file manager does not work without JavaScript
|
||||||
|
</noscript>
|
||||||
|
<div id="files-tree-nav" class="overlay">
|
||||||
|
|
||||||
|
<!-- Button to close the overlay navigation -->
|
||||||
|
<a href="javascript:void(0)" class="closebtn" onclick="document.getElementById('files-tree-nav').style.height = '0%';">×</a>
|
||||||
|
|
||||||
|
<!-- 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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
/* The Overlay (background) */
|
||||||
|
.overlay {
|
||||||
|
/* Height & width depends on how you want to reveal the overlay (see JS below) */
|
||||||
|
height: 0;
|
||||||
|
width: 100vw;
|
||||||
|
position: fixed; /* Stay in place */
|
||||||
|
z-index: 1031; /* Sit on top */
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
background-color: rgb(0,0,0); /* Black fallback color */
|
||||||
|
background-color: rgba(0,0,0, 0.9); /* Black w/opacity */
|
||||||
|
overflow-x: hidden; /* Disable horizontal scroll */
|
||||||
|
transition: 0.5s; /* 0.5 second transition effect to slide in or slide down the overlay (height or width, depending on reveal) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Position the content inside the overlay */
|
||||||
|
.overlay-content {
|
||||||
|
position: relative;
|
||||||
|
top: 25%; /* 25% from the top */
|
||||||
|
width: 100%; /* 100% width */
|
||||||
|
text-align: center; /* Centered text/links */
|
||||||
|
margin-top: 30px; /* 30px top margin to avoid conflict with the close button on smaller screens */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The navigation links inside the overlay */
|
||||||
|
.overlay a {
|
||||||
|
padding: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 36px;
|
||||||
|
color: #818181;
|
||||||
|
display: block; /* Display block instead of inline */
|
||||||
|
transition: 0.3s; /* Transition effects on hover (color) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When you mouse over the navigation links, change their color */
|
||||||
|
.overlay a:hover, .overlay a:focus {
|
||||||
|
color: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Position the close button (top right corner) */
|
||||||
|
.overlay .closebtn {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 45px;
|
||||||
|
font-size: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When the height of the screen is less than 450 pixels, change the font-size of the links and position the close button again, so they don't overlap */
|
||||||
|
@media screen and (max-height: 450px) {
|
||||||
|
.overlay a {font-size: 20px}
|
||||||
|
.overlay .closebtn {
|
||||||
|
font-size: 40px;
|
||||||
|
top: 15px;
|
||||||
|
right: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<ul class="tree-view">
|
||||||
|
<li>
|
||||||
|
<div class="tree-caret tree-ctx-item files-tree-title">Files</div>
|
||||||
|
<ul class="tree-nested" id="files-tree">
|
||||||
|
<li>Error while getting files</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
/* Remove default bullets */
|
||||||
|
.tree-view {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style the caret/arrow */
|
||||||
|
.tree-caret {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none; /* Prevent text selection */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the caret/arrow with a unicode, and style it */
|
||||||
|
.tree-caret::before {
|
||||||
|
content: "\25B6";
|
||||||
|
color: white;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
||||||
|
.tree-caret-down::before {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide the nested list */
|
||||||
|
.tree-nested {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="col-md-6 col-sm-12">
|
||||||
|
Editing file <span id="editingFile"></span>
|
||||||
|
<div id="editor">file_contents</div>
|
||||||
|
<h3 id="file_warn"></h3>
|
||||||
|
<button class="btn btn-success" onclick="save()">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- content-wrapper ends -->
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
<script src="/static/assets/vendors/ace-builds/src-min/ace.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||||
|
function getCookie(name) {
|
||||||
|
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||||
|
return r ? r[1] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let editor = ace.edit('editor');
|
||||||
|
editor.setTheme('ace/theme/dracula');
|
||||||
|
editor.session.setUseSoftTabs(true);
|
||||||
|
|
||||||
|
let extensionChanges = [
|
||||||
|
{
|
||||||
|
regex: /^js$/,
|
||||||
|
replaceWith: 'ace/mode/javascript'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^py$/,
|
||||||
|
replaceWith: 'ace/mode/python'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^html$/,
|
||||||
|
replaceWith: 'ace/mode/html'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^yml$/,
|
||||||
|
replaceWith: 'ace/mode/yaml'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^yaml$/,
|
||||||
|
replaceWith: 'ace/mode/yaml'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^txt$/,
|
||||||
|
replaceWith: 'ace/mode/text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^json$/,
|
||||||
|
replaceWith: 'ace/mode/json'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^java$/,
|
||||||
|
replaceWith: 'ace/mode/java'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^cpp$/,
|
||||||
|
replaceWith: 'ace/mode/c_cpp'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^css$/,
|
||||||
|
replaceWith: 'ace/mode/css'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^scss$/,
|
||||||
|
replaceWith: 'ace/mode/scss'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^sass$/,
|
||||||
|
replaceWith: 'ace/mode/sass'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^lua$/,
|
||||||
|
replaceWith: 'ace/mode/lua'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^php$/,
|
||||||
|
replaceWith: 'ace/mode/php'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^ps1$/,
|
||||||
|
replaceWith: 'ace/mode/powershell'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^svg$/,
|
||||||
|
replaceWith: 'ace/mode/svg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^sh$/,
|
||||||
|
replaceWith: 'ace/mode/sh'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^xml$/,
|
||||||
|
replaceWith: 'ace/mode/xml'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /^ts$/,
|
||||||
|
replaceWith: 'ace/mode/typescript'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
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'][0]['server_id']['server_id'] }}&file_path=' + encodeURIComponent(filePath),
|
||||||
|
dataType: 'text',
|
||||||
|
success: function (data) {
|
||||||
|
console.log('Got File Contents From Server');
|
||||||
|
editor.session.setValue(data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFileName(name) {
|
||||||
|
let fileName = name || 'default.txt';
|
||||||
|
document.getElementById('editingFile').innerText = fileName;
|
||||||
|
|
||||||
|
if (fileName.match('.')) {
|
||||||
|
|
||||||
|
// The pop method removes and returns the last element.
|
||||||
|
setMode(fileName
|
||||||
|
.split('.')
|
||||||
|
.pop()
|
||||||
|
.replace('ace/mode/', ''));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
setMode('txt');
|
||||||
|
document
|
||||||
|
.querySelector('#file_warn')
|
||||||
|
.innerText = 'Warning: This is not a supported language';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFileName();
|
||||||
|
|
||||||
|
function setMode (extension) {
|
||||||
|
// if the extension matches with the RegEx it will return the replaceWith
|
||||||
|
// property. else it will return the one it has. defaults to the extension.
|
||||||
|
// this runs for each element in extensionChanges.
|
||||||
|
let aceMode = extensionChanges.reduce((output, element) => {
|
||||||
|
return extension.match(element.regex)
|
||||||
|
? element.replaceWith
|
||||||
|
: output;
|
||||||
|
}, extension);
|
||||||
|
|
||||||
|
if (!aceMode.startsWith('ace/mode/')) {
|
||||||
|
document
|
||||||
|
.querySelector('#file_warn')
|
||||||
|
.innerText = 'Warning: This is not a supported language';
|
||||||
|
} else {
|
||||||
|
document
|
||||||
|
.querySelector('#file_warn')
|
||||||
|
.innerText = '';
|
||||||
|
|
||||||
|
console.log(aceMode || 'ace/mode/text');
|
||||||
|
editor.session.setMode(aceMode || 'ace/mode/text');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
let text = editor.session.getValue();
|
||||||
|
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
$.ajax({
|
||||||
|
type: "PUT",
|
||||||
|
headers: {'X-XSRFToken': token},
|
||||||
|
url: '/ajax/save_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||||
|
data: {
|
||||||
|
file_contents: text,
|
||||||
|
file_path: filePath
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
console.log("got response:");
|
||||||
|
console.log(data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFile(parent, name, callback) {
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: {'X-XSRFToken': token},
|
||||||
|
url: '/ajax/create_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||||
|
data: {
|
||||||
|
file_parent: parent,
|
||||||
|
file_name: name
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
console.log("got response:");
|
||||||
|
console.log(data);
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDir(parent, name, callback) {
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
headers: {'X-XSRFToken': token},
|
||||||
|
url: '/ajax/create_dir?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||||
|
data: {
|
||||||
|
dir_parent: parent,
|
||||||
|
dir_name: name
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
console.log("got response:");
|
||||||
|
console.log(data);
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renameItem(path, name, callback) {
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
$.ajax({
|
||||||
|
type: "PUT",
|
||||||
|
headers: {'X-XSRFToken': token},
|
||||||
|
url: '/ajax/rename_item?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||||
|
data: {
|
||||||
|
item_path: path,
|
||||||
|
new_item_name: name
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
console.log("got response:");
|
||||||
|
console.log(data);
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteFile(path, callback) {
|
||||||
|
console.log('Deleting: ' + path)
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
$.ajax({
|
||||||
|
type: "DELETE",
|
||||||
|
headers: {'X-XSRFToken': token},
|
||||||
|
url: '/ajax/del_file?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||||
|
data: {
|
||||||
|
file_path: path
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
console.log("got response:");
|
||||||
|
console.log(data);
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteDir(path, callback) {
|
||||||
|
var token = getCookie("_xsrf")
|
||||||
|
$.ajax({
|
||||||
|
type: "DELETE",
|
||||||
|
headers: {'X-XSRFToken': token},
|
||||||
|
url: '/ajax/del_dir?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||||
|
data: {
|
||||||
|
dir_path: path
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
console.log("got response:");
|
||||||
|
console.log(data);
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTreeView() {
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: '/ajax/get_tree?id={{ data['server_stats'][0]['server_id']['server_id'] }}',
|
||||||
|
dataType: 'text',
|
||||||
|
success: function(data){
|
||||||
|
console.log("got response:");
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
dataArr = data.split('\n');
|
||||||
|
serverDir = dataArr.shift(); // Remove & return first element (server directory)
|
||||||
|
text = dataArr.join('\n');
|
||||||
|
|
||||||
|
document.getElementById('files-tree').innerHTML = text;
|
||||||
|
|
||||||
|
document.getElementsByClassName('files-tree-title')[0].setAttribute('data-path', serverDir);
|
||||||
|
document.getElementsByClassName('files-tree-title')[0].setAttribute('data-name', 'Files');
|
||||||
|
|
||||||
|
setTimeout(function () {setTreeViewContext()}, 1000);
|
||||||
|
|
||||||
|
var toggler = document.getElementsByClassName("tree-caret");
|
||||||
|
var i;
|
||||||
|
|
||||||
|
for (i = 0; i < toggler.length; i++) {
|
||||||
|
if (toggler[i].classList.contains('files-tree-title')) continue;
|
||||||
|
toggler[i].addEventListener("click", function caretListener() {
|
||||||
|
this.parentElement.querySelector(".tree-nested").classList.toggle("d-block");
|
||||||
|
this.classList.toggle("tree-caret-down");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTreeViewContext() {
|
||||||
|
var treeItems = document.getElementsByClassName('tree-ctx-item');
|
||||||
|
|
||||||
|
for (var i = 0; i < treeItems.length; i++) {
|
||||||
|
var treeItem = treeItems[i];
|
||||||
|
treeItem.addEventListener('contextmenu', function contextListener(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var ctxmenuPath = event.target.getAttribute('data-path');
|
||||||
|
var ctxmenuName = event.target.getAttribute('data-name');
|
||||||
|
if (!ctxmenuPath) {
|
||||||
|
console.log({ 'event.target': event.target, ctxmenuPath });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('#renameItem').show();
|
||||||
|
|
||||||
|
var isDir = event.target.classList.contains('tree-folder');
|
||||||
|
$('#createFile').toggle(isDir);
|
||||||
|
$('#createDir').toggle(isDir);
|
||||||
|
$('#deleteDir').toggle(isDir);
|
||||||
|
|
||||||
|
var isFile = event.target.classList.contains('tree-file');
|
||||||
|
$('#deleteFile').toggle(isFile);
|
||||||
|
console.log({ 'event.target': event.target, isDir, isFile });
|
||||||
|
|
||||||
|
if(event.target.classList.contains('files-tree-title')) {
|
||||||
|
$('#createFile').show();
|
||||||
|
$('#createDir').show();
|
||||||
|
$('#renameItem').hide();
|
||||||
|
$('#deleteDir').hide();
|
||||||
|
$('#deleteFile').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById('files-tree-nav-content')
|
||||||
|
.setAttribute('data-path', ctxmenuPath);
|
||||||
|
|
||||||
|
document.getElementById('files-tree-nav-content')
|
||||||
|
.setAttribute('data-name', ctxmenuName);
|
||||||
|
|
||||||
|
document.getElementById("files-tree-nav").style.height = "100%";
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createFileE(event) {
|
||||||
|
bootbox.prompt('What name do you want for the new file?', function(result) {
|
||||||
|
path = event.target.parentElement.getAttribute('data-path');
|
||||||
|
name = event.target.parentElement.getAttribute('data-name');
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
createFile(path, result, function () {
|
||||||
|
getTreeView()
|
||||||
|
document.getElementById('files-tree-nav').style.height = '0%';
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDirE(event) {
|
||||||
|
bootbox.prompt('What name do you want for the new directory?', function(result) {
|
||||||
|
path = event.target.parentElement.getAttribute('data-path');
|
||||||
|
name = event.target.parentElement.getAttribute('data-name');
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
createDir(path, result, function () {
|
||||||
|
getTreeView()
|
||||||
|
document.getElementById('files-tree-nav').style.height = '0%';
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function renameItemE(event) {
|
||||||
|
bootbox.prompt('What should the new name be?', function(result) {
|
||||||
|
path = event.target.parentElement.getAttribute('data-path');
|
||||||
|
name = event.target.parentElement.getAttribute('data-name');
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
renameItem(path, result, function () {
|
||||||
|
getTreeView()
|
||||||
|
document.getElementById('files-tree-nav').style.height = '0%';
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteFileE(event) {
|
||||||
|
path = event.target.parentElement.getAttribute('data-path');
|
||||||
|
name = event.target.parentElement.getAttribute('data-name');
|
||||||
|
bootbox.confirm({
|
||||||
|
size: "",
|
||||||
|
title: "Are you sure you want to delete " + name + "?",
|
||||||
|
closeButton: false,
|
||||||
|
message: "You are deleting \"" + path + "\"!<br/><br/>This action will be irreversible and it'll be lost forever!",
|
||||||
|
buttons: {
|
||||||
|
confirm: {
|
||||||
|
label: 'Yes, I understand the consequences',
|
||||||
|
className: 'btn-danger'
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
label: 'No',
|
||||||
|
className: 'btn-link'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
callback: function(result) {
|
||||||
|
if (!result) return;
|
||||||
|
deleteFile(path, function () {
|
||||||
|
getTreeView()
|
||||||
|
document.getElementById('files-tree-nav').style.height = '0%';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteDirE(event) {
|
||||||
|
path = event.target.parentElement.getAttribute('data-path');
|
||||||
|
name = event.target.parentElement.getAttribute('data-name');
|
||||||
|
bootbox.confirm({
|
||||||
|
size: "",
|
||||||
|
title: "Are you sure you want to delete " + name + "?",
|
||||||
|
closeButton: false,
|
||||||
|
message: "You are deleting \"" + path + "\"!<br/><br/>This action will be irreversible and it'll be lost forever!",
|
||||||
|
buttons: {
|
||||||
|
confirm: {
|
||||||
|
label: 'Yes, I understand the consequences',
|
||||||
|
className: 'btn-danger'
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
label: 'No',
|
||||||
|
className: 'btn-link'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
callback: function(result) {
|
||||||
|
if (!result) return;
|
||||||
|
deleteDir(path, function () {
|
||||||
|
getTreeView()
|
||||||
|
document.getElementById('files-tree-nav').style.height = '0%';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementsByClassName('files-tree-title')[0].addEventListener("click", function caretListener() {
|
||||||
|
this.parentElement.querySelector(".tree-nested").classList.toggle("d-block");
|
||||||
|
this.classList.toggle("tree-caret-down");
|
||||||
|
});
|
||||||
|
|
||||||
|
getTreeView();
|
||||||
|
setTreeViewContext();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% end %}
|
Loading…
Reference in New Issue
Block a user