Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
D
douban-api-proxy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
captainwong
douban-api-proxy
Commits
2508c1ab
Commit
2508c1ab
authored
Dec 10, 2020
by
Ozzieisaacs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Started implement server side filechooser
parent
983e3b22
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
247 additions
and
19 deletions
+247
-19
admin.py
cps/admin.py
+85
-1
gdrive.py
cps/gdrive.py
+10
-14
main.js
cps/static/js/main.js
+114
-0
config_edit.html
cps/templates/config_edit.html
+4
-3
layout.html
cps/templates/layout.html
+1
-1
modal_dialogs.html
cps/templates/modal_dialogs.html
+33
-0
No files found.
cps/admin.py
View file @
2508c1ab
...
...
@@ -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
():
...
...
cps/gdrive.py
View file @
2508c1ab
...
...
@@ -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
)
...
...
cps/static/js/main.js
View file @
2508c1ab
...
...
@@ -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
=
"<span class=
\"
glyphicon glyphicon-folder-close
\"
></span>"
;
}
else
{
var
type
=
""
;
}
$
(
"<tr class=
\"
tr-clickable
\"
data-type=
\"
"
+
entry
.
type
+
"
\"
data-path=
\"
"
+
entry
.
fullpath
+
"
\"
><td>"
+
type
+
"</td><td>"
+
entry
.
name
+
"</td><td>"
+
entry
.
size
+
"</td></tr>"
).
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
;
...
...
cps/templates/config_edit.html
View file @
2508c1ab
...
...
@@ -384,7 +384,7 @@
<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"
>
<span
class=
"input-group-btn"
>
<button
type=
"button"
id=
"converter_
path
"
class=
"btn btn-default"
><span
class=
"glyphicon glyphicon-folder-open"
></span></button>
<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>
</span>
</div>
<div
class=
"form-group"
>
...
...
@@ -412,8 +412,6 @@
</div>
{% endif %}
</div>
<div
class=
"col-sm-12"
>
{% if not show_login_button %}
<button
type=
"submit"
name=
"submit"
class=
"btn btn-default"
>
{{_('Save')}}
</button>
...
...
@@ -428,6 +426,9 @@
</form>
</div>
{% endblock %}
{% block modal %}
{{ filechooser_modal() }}
{% endblock %}
{% block js %}
<script
type=
"text/javascript"
>
$
(
document
).
on
(
'change'
,
'#config_use_google_drive'
,
function
()
{
...
...
cps/templates/layout.html
View file @
2508c1ab
{% from 'modal_dialogs.html' import restrict_modal, delete_book %}
{% from 'modal_dialogs.html' import restrict_modal, delete_book
, filechooser_modal
%}
<!DOCTYPE html>
<html
lang=
"{{ g.user.locale }}"
>
<head>
...
...
cps/templates/modal_dialogs.html
View file @
2508c1ab
...
...
@@ -68,3 +68,36 @@
</div>
{% endif %}
{% endmacro %}
{% macro filechooser_modal() %}
<div
class=
"modal fade"
id=
"fileModal"
role=
"dialog"
aria-labelledby=
"metafileLabel"
>
<div
class=
"modal-dialog"
>
<div
class=
"modal-content"
>
<div
class=
"modal-header bg-info text-center"
>
<span>
{{_('Choose File Location')}}
</span>
</div>
<div
class=
"modal-body"
>
<table
id=
"file_table"
class=
"table table-striped"
>
<thead>
<tr>
<th>
{{_('type')}}
</th>
<th>
{{_('name')}}
</th>
<th>
{{_('size')}}
</th>
</tr>
</thead>
<tbody
id=
"tbody"
>
<tr
class=
"tr-clickable hidden"
id=
"parent"
data-type=
"dir"
data-path=
".."
>
<td><span
class=
"glyphicon glyphicon-folder-close"
></span></td>
<td
title=
"{{_('Parent Directory')}}"
><span
class=
"parentdir"
>
..
</span></td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div
class=
"modal-footer"
>
<input
type=
"button"
class=
"btn btn-primary"
value=
"{{_('Select')}}"
name=
"file_confirm"
id=
"file_confirm"
data-dismiss=
"modal"
>
<button
type=
"button"
class=
"btn btn-default"
data-dismiss=
"modal"
>
{{_('Cancel')}}
</button>
</div>
</div>
</div>
</div>
{% endmacro %}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment