From 2508c1abb20c869771f4ad5459498d351442ac95 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Thu, 10 Dec 2020 14:41:45 +0100 Subject: [PATCH] Started implement server side filechooser --- cps/admin.py | 86 ++++++++++++++++++++++- cps/gdrive.py | 24 +++---- cps/static/js/main.js | 114 +++++++++++++++++++++++++++++++ cps/templates/config_edit.html | 7 +- cps/templates/layout.html | 2 +- cps/templates/modal_dialogs.html | 33 +++++++++ 6 files changed, 247 insertions(+), 19 deletions(-) diff --git a/cps/admin.py b/cps/admin.py index 41f21bfb..eaf4613f 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -5,7 +5,7 @@ # andy29485, idalin, Kyosfonica, wuqi, Kennyl, lemmsh, # falgh1, grunjol, csitko, ytils, xybydy, trasba, vrabe, # ruben-herold, marblepebble, JackED42, SiphonSquirrel, -# apetresc, nanu-c, mutschler +# apetresc, nanu-c, mutschler, GammaC0de, vuolter # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ import re import base64 import json import time +import operator from datetime import datetime, timedelta from babel import Locale as LC @@ -520,6 +521,89 @@ def list_restriction(res_type): return response +@admi.route("/ajax/pathchooser/", endpoint="pathchooser") +@admi.route("/ajax/filechooser/", endpoint="filechooser") +@login_required +@admin_required +def pathchooser(): + browse_for = "folder" if request.endpoint == "admin.pathchooser" else "file" + path = os.path.normpath(request.args.get('path', "")) + + if os.path.isfile(path): + oldfile = path + path = os.path.dirname(path) + else: + oldfile = "" + + abs = False + + if os.path.isdir(path): + if os.path.isabs(path): + cwd = os.path.realpath(path) + abs = True + else: + cwd = os.path.relpath(path) + else: + cwd = os.getcwd() + + cwd = os.path.normpath(os.path.realpath(cwd)) + parentdir = os.path.dirname(cwd) + if not abs: + if os.path.realpath(cwd) == os.path.realpath("/"): + cwd = os.path.relpath(cwd) + else: + cwd = os.path.relpath(cwd) + os.path.sep + parentdir = os.path.relpath(parentdir) + os.path.sep + + if os.path.realpath(cwd) == os.path.realpath("/"): + parentdir = "" + + try: + folders = os.listdir(cwd) + except Exception: + folders = [] + + files = [] + locale = get_locale() + for f in folders: + try: + data = {"name": f, "fullpath": os.path.join(cwd, f)} + 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: + continue + + if os.path.isfile(os.path.join(cwd, f)): + data["type"] = "file" + data["size"] = os.path.getsize(os.path.join(cwd, f)) + + power = 0 + while (data["size"] >> 10) > 0.3: + power += 1 + data["size"] >>= 10 + units = ("", "K", "M", "G", "T") + data["size"] = str(data["size"]) + " " + units[power] + "Byte" + else: + data["type"] = "dir" + data["size"] = "" + + files.append(data) + + files = sorted(files, key=operator.itemgetter("type", "sort")) + + context = { + "cwd": cwd, + "files": files, + "parentdir": parentdir, + "type": browse_for, + "oldfile": oldfile, + "absolute": abs, + } + return json.dumps(context) + + @admi.route("/config", methods=["GET", "POST"]) @unconfigured def basic_configuration(): diff --git a/cps/gdrive.py b/cps/gdrive.py index 8f8b9aa8..e2b6298d 100644 --- a/cps/gdrive.py +++ b/cps/gdrive.py @@ -37,7 +37,7 @@ from flask_login import login_required from . import logger, gdriveutils, config, ub, calibre_db from .web import admin_required -gdrive = Blueprint('gdrive', __name__) +gdrive = Blueprint('gdrive', __name__, url_prefix='/gdrive') log = logger.create() try: @@ -50,7 +50,7 @@ current_milli_time = lambda: int(round(time() * 1000)) gdrive_watch_callback_token = 'target=calibreweb-watch_files' -@gdrive.route("/gdrive/authenticate") +@gdrive.route("/authenticate") @login_required @admin_required def authenticate_google_drive(): @@ -63,7 +63,7 @@ def authenticate_google_drive(): return redirect(authUrl) -@gdrive.route("/gdrive/callback") +@gdrive.route("/callback") def google_drive_callback(): auth_code = request.args.get('code') if not auth_code: @@ -77,19 +77,14 @@ def google_drive_callback(): return redirect(url_for('admin.configuration')) -@gdrive.route("/gdrive/watch/subscribe") +@gdrive.route("/watch/subscribe") @login_required @admin_required def watch_gdrive(): if not config.config_google_drive_watch_changes_response: with open(gdriveutils.CLIENT_SECRETS, 'r') as settings: filedata = json.load(settings) - # ToDo: Easier: rstrip('/') - if filedata['web']['redirect_uris'][0].endswith('/'): - filedata['web']['redirect_uris'][0] = filedata['web']['redirect_uris'][0][:-((len('/gdrive/callback')+1))] - else: - filedata['web']['redirect_uris'][0] = filedata['web']['redirect_uris'][0][:-(len('/gdrive/callback'))] - address = '%s/gdrive/watch/callback' % filedata['web']['redirect_uris'][0] + address = filedata['web']['redirect_uris'][0].rstrip('/').replace('/gdrive/callback', '/gdrive/watch/callback') notification_id = str(uuid4()) try: result = gdriveutils.watchChange(gdriveutils.Gdrive.Instance().drive, notification_id, @@ -99,14 +94,15 @@ def watch_gdrive(): except HttpError as e: reason=json.loads(e.content)['error']['errors'][0] if reason['reason'] == u'push.webhookUrlUnauthorized': - flash(_(u'Callback domain is not verified, please follow steps to verify domain in google developer console'), category="error") + flash(_(u'Callback domain is not verified, ' + u'please follow steps to verify domain in google developer console'), category="error") else: flash(reason['message'], category="error") return redirect(url_for('admin.configuration')) -@gdrive.route("/gdrive/watch/revoke") +@gdrive.route("/watch/revoke") @login_required @admin_required def revoke_watch_gdrive(): @@ -122,14 +118,14 @@ def revoke_watch_gdrive(): return redirect(url_for('admin.configuration')) -@gdrive.route("/gdrive/watch/callback", methods=['GET', 'POST']) +@gdrive.route("/watch/callback", methods=['GET', 'POST']) def on_received_watch_confirmation(): if not config.config_google_drive_watch_changes_response: return '' if request.headers.get('X-Goog-Channel-Token') != gdrive_watch_callback_token \ or request.headers.get('X-Goog-Resource-State') != 'change' \ or not request.data: - return redirect(url_for('admin.configuration')) + return '' # redirect(url_for('admin.configuration')) log.debug('%r', request.headers) log.debug('%r', request.data) diff --git a/cps/static/js/main.js b/cps/static/js/main.js index 703ecb29..0414a63c 100644 --- a/cps/static/js/main.js +++ b/cps/static/js/main.js @@ -213,6 +213,45 @@ $(function() { }); } + function fillFileTable(path, type) { + if (type === "dir") { + var request_path = "/../../ajax/pathchooser/"; + } else { + var request_path = "/../../ajax/filechooser/"; + } + $.ajax({ + dataType: "json", + data: { + path: path, + }, + url: window.location.pathname + request_path, + success: function success(data) { + $("#file_table > tbody > tr").each(function () { + if ($(this).attr("id") !== "parent") { + $(this).closest("tr").remove(); + } + }); + if (data.parentdir !== "") { + $("#parent").removeClass('hidden') + } else { + $("#parent").addClass('hidden') + } + // console.log(data); + data.files.forEach(function(entry) { + if(entry.type === "dir") { + var type = ""; + } else { + var type = ""; + } + $("" + type + "" + entry.name + "" + + entry.size + "").appendTo($("#file_table")); + }); + }, + timeout: 2000 + }); + } + $(".discover .row").isotope({ // options itemSelector : ".book", @@ -402,6 +441,81 @@ $(function() { $("#config_delete_kobo_token").show(); }); + + + $("#fileModal").on("show.bs.modal", function(e) { + //get data-id attribute of the clicked element and store in button + //var submit = true; + //var cwd = "{{oldfile|default(cwd, True)|abspath|replace('\\', '\\\\')}}"; + //var isabsolute = true; + fillFileTable("","dir"); + }); + + //(".tr-clickable").on("click", + $(document).on("click", ".tr-clickable", function() { + var path = this.attributes['data-path'].value; + var type = this.attributes['data-type'].value; + fillFileTable(path, type); + }); + + + /*{% 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() { //get data-id attribute of the clicked element var pathname = document.getElementsByTagName("script"), src = pathname[pathname.length - 1].src; diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index 4c522132..b66499dc 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -384,7 +384,7 @@
- +
@@ -412,8 +412,6 @@
{% endif %} - -
{% if not show_login_button %} @@ -428,6 +426,9 @@
{% endblock %} +{% block modal %} +{{ filechooser_modal() }} +{% endblock %} {% block js %}