Server side file browser

This commit is contained in:
Ozzieisaacs 2020-12-11 08:37:37 +01:00
parent 2508c1abb2
commit d3986ca14a
5 changed files with 76 additions and 96 deletions

View File

@ -520,13 +520,21 @@ def list_restriction(res_type):
response.headers["Content-Type"] = "application/json; charset=utf-8" response.headers["Content-Type"] = "application/json; charset=utf-8"
return response return response
@admi.route("/basicconfig/pathchooser/")
@unconfigured
def config_pathchooser():
return pathchooser()
@admi.route("/ajax/pathchooser/", endpoint="pathchooser") @admi.route("/ajax/pathchooser/")
@admi.route("/ajax/filechooser/", endpoint="filechooser")
@login_required @login_required
@admin_required @admin_required
def ajax_pathchooser():
return pathchooser()
def pathchooser(): def pathchooser():
browse_for = "folder" if request.endpoint == "admin.pathchooser" else "file" browse_for = "folder" # if request.endpoint == "admin.pathchooser" else "file"
folder_only = request.args.get('folder', False) == "true"
file_filter = request.args.get('filter', "")
path = os.path.normpath(request.args.get('path', "")) path = os.path.normpath(request.args.get('path', ""))
if os.path.isfile(path): if os.path.isfile(path):
@ -538,11 +546,11 @@ def pathchooser():
abs = False abs = False
if os.path.isdir(path): if os.path.isdir(path):
if os.path.isabs(path): #if os.path.isabs(path):
cwd = os.path.realpath(path) cwd = os.path.realpath(path)
abs = True abs = True
else: #else:
cwd = os.path.relpath(path) # cwd = os.path.relpath(path)
else: else:
cwd = os.getcwd() cwd = os.getcwd()
@ -564,18 +572,19 @@ def pathchooser():
folders = [] folders = []
files = [] files = []
locale = get_locale() # locale = get_locale()
for f in folders: for f in folders:
try: try:
data = {"name": f, "fullpath": os.path.join(cwd, f)} data = {"name": f, "fullpath": os.path.join(cwd, f)}
data["sort"] = data["fullpath"].lower() data["sort"] = data["fullpath"].lower()
data["modified"] = format_datetime(datetime.fromtimestamp(int(os.path.getmtime(os.path.join(cwd, f)))),
format='short', locale=locale)
data["ext"] = os.path.splitext(f)[1]
except Exception: except Exception:
continue continue
if os.path.isfile(os.path.join(cwd, f)): if os.path.isfile(os.path.join(cwd, f)):
if folder_only:
continue
if file_filter != "" and file_filter != f:
continue
data["type"] = "file" data["type"] = "file"
data["size"] = os.path.getsize(os.path.join(cwd, f)) data["size"] = os.path.getsize(os.path.join(cwd, f))
@ -604,7 +613,7 @@ def pathchooser():
return json.dumps(context) return json.dumps(context)
@admi.route("/config", methods=["GET", "POST"]) @admi.route("/basicconfig", methods=["GET", "POST"])
@unconfigured @unconfigured
def basic_configuration(): def basic_configuration():
logout_user() logout_user()

View File

@ -213,22 +213,33 @@ $(function() {
}); });
} }
function fillFileTable(path, type) { function fillFileTable(path, type, folder, filt) {
if (type === "dir") { if (window.location.pathname.endsWith("/basicconfig")) {
var request_path = "/../../ajax/pathchooser/"; var request_path = "/../basicconfig/pathchooser/";
} else { } else {
var request_path = "/../../ajax/filechooser/"; var request_path = "/../../ajax/pathchooser/";
} }
$.ajax({ $.ajax({
dataType: "json", dataType: "json",
data: { data: {
path: path, path: path,
folder: folder,
filter: filt
}, },
url: window.location.pathname + request_path, url: window.location.pathname + request_path,
success: function success(data) { success: function success(data) {
if ($("#element_selected").text() ==="") {
$("#element_selected").text(data.cwd);
}
$("#file_table > tbody > tr").each(function () { $("#file_table > tbody > tr").each(function () {
if ($(this).attr("id") !== "parent") { if ($(this).attr("id") !== "parent") {
$(this).closest("tr").remove(); $(this).closest("tr").remove();
} else {
if(data.absolute && data.parentdir !== "") {
$(this)[0].attributes['data-path'].value = data.parentdir;
} else {
$(this)[0].attributes['data-path'].value = "..";
}
} }
}); });
if (data.parentdir !== "") { if (data.parentdir !== "") {
@ -444,83 +455,42 @@ $(function() {
$("#fileModal").on("show.bs.modal", function(e) { $("#fileModal").on("show.bs.modal", function(e) {
//get data-id attribute of the clicked element and store in button var target = $(e.relatedTarget);
//var submit = true; var path = $("#" + target.data("link"))[0].value;
//var cwd = "{{oldfile|default(cwd, True)|abspath|replace('\\', '\\\\')}}"; var folder = target.data("folderonly");
//var isabsolute = true; var filter = target.data("filefilter");
fillFileTable("","dir"); $("#element_selected").text(path);
$("#file_confirm")[0].attributes["data-link"].value = target.data("link");
$("#file_confirm")[0].attributes["data-folderonly"].value = (typeof folder === 'undefined') ? false : true;
$("#file_confirm")[0].attributes["data-filefilter"].value = (typeof filter === 'undefined') ? "" : filter;
$("#file_confirm")[0].attributes["data-newfile"].value = target.data("newfile");
fillFileTable(path,"dir", folder, filter);
});
$("#file_confirm").click(function() {
$("#" + $(this).data("link"))[0].value = $("#element_selected").text()
}); });
//(".tr-clickable").on("click",
$(document).on("click", ".tr-clickable", function() { $(document).on("click", ".tr-clickable", function() {
var path = this.attributes['data-path'].value; var path = this.attributes["data-path"].value;
var type = this.attributes['data-type'].value; var type = this.attributes["data-type"].value;
fillFileTable(path, type); var folder = $(file_confirm).data("folderonly");
var filter = $(file_confirm).data("filefilter");
var newfile = $(file_confirm).data("newfile");
if (newfile !== 'undefined') {
$("#element_selected").text(path + $("#new_file".text()));
} else {
$("#element_selected").text(path);
}
if(type === "dir") {
fillFileTable(path, type, folder, filter);
}
}); });
/*{% if type == 'folder' %} {# browsing for folder #}
var abspath = "{{url_for('app.pathchooser') + '?path=' + cwd|abspath|quote_plus}}";
var relpath = "{{url_for('app.pathchooser') + '?path=' + cwd|relpath|quote_plus}}";
{% else %} {# browsing for file #}
var abspath = "{{url_for('app.filechooser') + '?path=' + oldfile|default(cwd, True)|abspath|quote_plus}}";
var relpath = "{{url_for('app.filechooser') + '?path=' + oldfile|default(cwd, True)|relpath|quote_plus}}";
{% endif %}*/
/*document.addEventListener("readystatechange", function(event) {
if (this.readyState === "complete") {
document.getElementById("tbody").style.height = (window.innerHeight - 25) + "px";
window.onresize = function (event) {
document.getElementById("tbody").style.height = (window.innerHeight - 25) + "px";
};
var clickables = document.getElementsByClassName("tr-clickable");
for (var i = 0; i < clickables.length; i++) {
clickables[i].onclick = (function () {
var onclick = clickables[i].onclick;
return function (e) {
if (onclick != null && !onclick()) {
return false
}
if (this.dataset.href !== undefined && this.dataset.href !== "#") {
window.location.href = this.dataset.href;
return false
} else {
return true;
}
}
})();
}
}
});
function updateParent()
{
if (window.top.SettingsUI !== undefined) {
window.top.SettingsUI.prototype.pathchooserChanged(this);
}
}
function setInvalid() {
submit = false;
cwd = "";
updateParent();
}
function setValid() {
submit = true;
updateParent();
}
function setFile(fullpath, name)
{
cwd = fullpath;
/*{*% if type == "file" %} {# browsing for file #}
abspath = "{{url_for('app.filechooser')}}?path={{cwd|abspath|quote_plus}}" + encodeURIComponent(name);
relpath = "{{url_for('app.filechooser')}}?path={{cwd|relpath|quote_plus}}" + encodeURIComponent(name);
{% endif %}*/
/*setValid();
}*/
$("#btndeletetoken").click(function() { $("#btndeletetoken").click(function() {
//get data-id attribute of the clicked element //get data-id attribute of the clicked element
var pathname = document.getElementsByTagName("script"), src = pathname[pathname.length - 1].src; var pathname = document.getElementsByTagName("script"), src = pathname[pathname.length - 1].src;
var path = src.substring(0, src.lastIndexOf("/")); var path = src.substring(0, src.lastIndexOf("/"));
// var domainId = $(this).value("domainId");
$.ajax({ $.ajax({
method:"get", method:"get",
url: path + "/../../kobo_auth/deleteauthtoken/" + this.value, url: path + "/../../kobo_auth/deleteauthtoken/" + this.value,

View File

@ -19,7 +19,7 @@
<div class="form-group required input-group"> <div class="form-group required input-group">
<input type="text" class="form-control" id="config_calibre_dir" name="config_calibre_dir" value="{% if config.config_calibre_dir != None %}{{ config.config_calibre_dir }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_calibre_dir" name="config_calibre_dir" value="{% if config.config_calibre_dir != None %}{{ config.config_calibre_dir }}{% endif %}" autocomplete="off">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" id="library_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button> <button type="button" data-toggle="modal" id="converter_modal_path" data-link="config_calibre_dir" data-filefilter="metadata.db" data-target="#fileModal" id="library_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
</span> </span>
</div> </div>
{% if feature_support['gdrive'] %} {% if feature_support['gdrive'] %}
@ -94,14 +94,14 @@
<div class="form-group input-group"> <div class="form-group input-group">
<input type="text" class="form-control" id="config_certfile" name="config_certfile" value="{% if config.config_certfile != None %}{{ config.config_certfile }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_certfile" name="config_certfile" value="{% if config.config_certfile != None %}{{ config.config_certfile }}{% endif %}" autocomplete="off">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" id="certfile_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button> <button type="button" data-toggle="modal" data-link="config_certfile" data-target="#fileModal" id="certfile_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
</span> </span>
</div> </div>
<label for="config_calibre_dir" >{{_('SSL Keyfile location (leave it empty for non-SSL Servers)')}}</label> <label for="config_calibre_dir" >{{_('SSL Keyfile location (leave it empty for non-SSL Servers)')}}</label>
<div class="form-group input-group"> <div class="form-group input-group">
<input type="text" class="form-control" id="config_keyfile" name="config_keyfile" value="{% if config.config_keyfile != None %}{{ config.config_keyfile }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_keyfile" name="config_keyfile" value="{% if config.config_keyfile != None %}{{ config.config_keyfile }}{% endif %}" autocomplete="off">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" id="keyfile_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button> <button type="button" id="keyfile_path" data-toggle="modal" data-link="config_keyfile" data-target="#fileModal" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
</span> </span>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -268,21 +268,21 @@
<div class="form-group input-group"> <div class="form-group input-group">
<input type="text" class="form-control" id="config_ldap_cacert_path" name="config_ldap_cacert_path" value="{% if config.config_ldap_cacert_path != None %}{{ config.config_ldap_cacert_path }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_ldap_cacert_path" name="config_ldap_cacert_path" value="{% if config.config_ldap_cacert_path != None %}{{ config.config_ldap_cacert_path }}{% endif %}" autocomplete="off">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" id="library_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button> <button type="button" id="library_path" data-toggle="modal" data-link="config_ldap_cacert_path" data-target="#fileModal" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
</span> </span>
</div> </div>
<label for="config_ldap_cert_path">{{_('LDAP Certificate Path (Only needed for Client Certificate Authentication)')}}</label> <label for="config_ldap_cert_path">{{_('LDAP Certificate Path (Only needed for Client Certificate Authentication)')}}</label>
<div class="form-group input-group"> <div class="form-group input-group">
<input type="text" class="form-control" id="config_ldap_cert_path" name="config_ldap_cert_path" value="{% if config.config_ldap_cert_path != None %}{{ config.config_ldap_cert_path }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_ldap_cert_path" name="config_ldap_cert_path" value="{% if config.config_ldap_cert_path != None %}{{ config.config_ldap_cert_path }}{% endif %}" autocomplete="off">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" id="library_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button> <button type="button" id="library_path" data-toggle="modal" data-link="config_ldap_cert_path" data-target="#fileModal" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
</span> </span>
</div> </div>
<label for="config_ldap_key_path">{{_('LDAP Keyfile Path (Only needed for Client Certificate Authentication)')}}</label> <label for="config_ldap_key_path">{{_('LDAP Keyfile Path (Only needed for Client Certificate Authentication)')}}</label>
<div class="form-group input-group"> <div class="form-group input-group">
<input type="text" class="form-control" id="config_ldap_key_path" name="config_ldap_key_path" value="{% if config.config_ldap_key_path != None %}{{ config.config_ldap_key_path }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_ldap_key_path" name="config_ldap_key_path" value="{% if config.config_ldap_key_path != None %}{{ config.config_ldap_key_path }}{% endif %}" autocomplete="off">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" id="library_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button> <button type="button" id="library_path" data-toggle="modal" data-link="config_ldap_key_path" data-target="#fileModal" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
</span> </span>
</div> </div>
</div> </div>
@ -384,7 +384,7 @@
<div class="form-group input-group"> <div class="form-group input-group">
<input type="text" class="form-control" id="config_converterpath" name="config_converterpath" value="{% if config.config_converterpath != None %}{{ config.config_converterpath }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_converterpath" name="config_converterpath" value="{% if config.config_converterpath != None %}{{ config.config_converterpath }}{% endif %}" autocomplete="off">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" id="converter_modal_path" data-toggle="modal" data-target="#fileModal" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button> <button type="button" data-toggle="modal" id="converter_modal_path" data-link="config_converterpath" data-target="#fileModal" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
</span> </span>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -395,7 +395,7 @@
<div class="form-group input-group"> <div class="form-group input-group">
<input type="text" class="form-control" id="config_kepubifypath" name="config_kepubifypath" value="{% if config.config_kepubifypath != None %}{{ config.config_kepubifypath }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_kepubifypath" name="config_kepubifypath" value="{% if config.config_kepubifypath != None %}{{ config.config_kepubifypath }}{% endif %}" autocomplete="off">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" id="kepubify_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button> <button type="button" id="kepubify_path" data-toggle="modal" data-link="config_kepubifypath" data-target="#fileModal" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
</span> </span>
</div> </div>
{% if feature_support['rar'] %} {% if feature_support['rar'] %}
@ -403,7 +403,7 @@
<div class="form-group input-group"> <div class="form-group input-group">
<input type="text" class="form-control" id="config_rarfile_location" name="config_rarfile_location" value="{% if config.config_rarfile_location != None %}{{ config.config_rarfile_location }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_rarfile_location" name="config_rarfile_location" value="{% if config.config_rarfile_location != None %}{{ config.config_rarfile_location }}{% endif %}" autocomplete="off">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" id="unrar_path" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button> <button type="button" id="unrar_path" data-toggle="modal" data-link="config_rarfile_location" data-target="#fileModal" class="btn btn-default"><span class="glyphicon glyphicon-folder-open"></span></button>
</span> </span>
</div> </div>
{% endif %} {% endif %}

View File

@ -94,7 +94,8 @@
</table> </table>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<input type="button" class="btn btn-primary" value="{{_('Select')}}" name="file_confirm" id="file_confirm" data-dismiss="modal"> <div class="text-left" id="element_selected"></div>
<input type="button" class="btn btn-primary" data-path="" data-link="" data-folderonly="" data-filefilter="" data-newfile="" value="{{_('Select')}}" name="file_confirm" id="file_confirm" data-dismiss="modal">
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
</div> </div>
</div> </div>

View File

@ -304,7 +304,7 @@ def before_request():
g.shelves_access = ub.session.query(ub.Shelf).filter( g.shelves_access = ub.session.query(ub.Shelf).filter(
or_(ub.Shelf.is_public == 1, ub.Shelf.user_id == current_user.id)).order_by(ub.Shelf.name).all() or_(ub.Shelf.is_public == 1, ub.Shelf.user_id == current_user.id)).order_by(ub.Shelf.name).all()
if not config.db_configured and request.endpoint not in ( if not config.db_configured and request.endpoint not in (
'admin.basic_configuration', 'login') and '/static/' not in request.path: 'admin.basic_configuration', 'login', "admin.config_pathchooser") and '/static/' not in request.path:
return redirect(url_for('admin.basic_configuration')) return redirect(url_for('admin.basic_configuration'))