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
f77d72fd
Commit
f77d72fd
authored
Mar 14, 2021
by
Ozzie Isaacs
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into development
# Conflicts: # cps/static/css/style.css
parents
b9c0c8d2
9b80c847
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
553 additions
and
476 deletions
+553
-476
comic.py
cps/comic.py
+38
-32
config_sql.py
cps/config_sql.py
+7
-4
constants.py
cps/constants.py
+1
-1
db.py
cps/db.py
+1
-3
helper.py
cps/helper.py
+56
-48
isoLanguages.py
cps/isoLanguages.py
+1
-1
oauth.py
cps/oauth.py
+125
-129
oauth_bb.py
cps/oauth_bb.py
+4
-1
server.py
cps/server.py
+1
-1
SyncToken.py
cps/services/SyncToken.py
+3
-3
caliBlur_override.css
cps/static/css/caliBlur_override.css
+18
-16
kthoom.css
cps/static/css/kthoom.css
+2
-3
main.css
cps/static/css/main.css
+30
-48
style.css
cps/static/css/style.css
+110
-47
caliBlur.js
cps/static/js/caliBlur.js
+1
-1
edit_books.js
cps/static/js/edit_books.js
+6
-6
filter_list.js
cps/static/js/filter_list.js
+2
-2
kthoom.js
cps/static/js/kthoom.js
+3
-0
main.js
cps/static/js/main.js
+2
-2
table.js
cps/static/js/table.js
+1
-0
upload.py
cps/tasks/upload.py
+0
-1
ub.py
cps/ub.py
+71
-48
usermanagement.py
cps/usermanagement.py
+1
-1
web.py
cps/web.py
+69
-78
No files found.
cps/comic.py
View file @
f77d72fd
...
...
@@ -74,6 +74,41 @@ def _cover_processing(tmp_file_name, img, extension):
return
tmp_cover_name
def
_extract_Cover_from_archive
(
original_file_extension
,
tmp_file_name
,
rarExecutable
):
cover_data
=
None
if
original_file_extension
.
upper
()
==
'.CBZ'
:
cf
=
zipfile
.
ZipFile
(
tmp_file_name
)
for
name
in
cf
.
namelist
():
ext
=
os
.
path
.
splitext
(
name
)
if
len
(
ext
)
>
1
:
extension
=
ext
[
1
]
.
lower
()
if
extension
in
COVER_EXTENSIONS
:
cover_data
=
cf
.
read
(
name
)
break
elif
original_file_extension
.
upper
()
==
'.CBT'
:
cf
=
tarfile
.
TarFile
(
tmp_file_name
)
for
name
in
cf
.
getnames
():
ext
=
os
.
path
.
splitext
(
name
)
if
len
(
ext
)
>
1
:
extension
=
ext
[
1
]
.
lower
()
if
extension
in
COVER_EXTENSIONS
:
cover_data
=
cf
.
extractfile
(
name
)
.
read
()
break
elif
original_file_extension
.
upper
()
==
'.CBR'
and
use_rarfile
:
try
:
rarfile
.
UNRAR_TOOL
=
rarExecutable
cf
=
rarfile
.
RarFile
(
tmp_file_name
)
for
name
in
cf
.
getnames
():
ext
=
os
.
path
.
splitext
(
name
)
if
len
(
ext
)
>
1
:
extension
=
ext
[
1
]
.
lower
()
if
extension
in
COVER_EXTENSIONS
:
cover_data
=
cf
.
read
(
name
)
break
except
Exception
as
e
:
log
.
debug
(
'Rarfile failed with error:
%
s'
,
e
)
return
cover_data
def
_extractCover
(
tmp_file_name
,
original_file_extension
,
rarExecutable
):
cover_data
=
extension
=
None
...
...
@@ -87,37 +122,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable):
cover_data
=
archive
.
getPage
(
index
)
break
else
:
if
original_file_extension
.
upper
()
==
'.CBZ'
:
cf
=
zipfile
.
ZipFile
(
tmp_file_name
)
for
name
in
cf
.
namelist
():
ext
=
os
.
path
.
splitext
(
name
)
if
len
(
ext
)
>
1
:
extension
=
ext
[
1
]
.
lower
()
if
extension
in
COVER_EXTENSIONS
:
cover_data
=
cf
.
read
(
name
)
break
elif
original_file_extension
.
upper
()
==
'.CBT'
:
cf
=
tarfile
.
TarFile
(
tmp_file_name
)
for
name
in
cf
.
getnames
():
ext
=
os
.
path
.
splitext
(
name
)
if
len
(
ext
)
>
1
:
extension
=
ext
[
1
]
.
lower
()
if
extension
in
COVER_EXTENSIONS
:
cover_data
=
cf
.
extractfile
(
name
)
.
read
()
break
elif
original_file_extension
.
upper
()
==
'.CBR'
and
use_rarfile
:
try
:
rarfile
.
UNRAR_TOOL
=
rarExecutable
cf
=
rarfile
.
RarFile
(
tmp_file_name
)
for
name
in
cf
.
getnames
():
ext
=
os
.
path
.
splitext
(
name
)
if
len
(
ext
)
>
1
:
extension
=
ext
[
1
]
.
lower
()
if
extension
in
COVER_EXTENSIONS
:
cover_data
=
cf
.
read
(
name
)
break
except
Exception
as
e
:
log
.
debug
(
'Rarfile failed with error:
%
s'
,
e
)
cover_data
=
_extract_Cover_from_archive
(
original_file_extension
,
tmp_file_name
,
rarExecutable
)
return
_cover_processing
(
tmp_file_name
,
cover_data
,
extension
)
...
...
@@ -142,7 +147,8 @@ def get_comic_info(tmp_file_path, original_file_name, original_file_extension, r
file_path
=
tmp_file_path
,
extension
=
original_file_extension
,
title
=
loadedMetadata
.
title
or
original_file_name
,
author
=
" & "
.
join
([
credit
[
"person"
]
for
credit
in
loadedMetadata
.
credits
if
credit
[
"role"
]
==
"Writer"
])
or
u'Unknown'
,
author
=
" & "
.
join
([
credit
[
"person"
]
for
credit
in
loadedMetadata
.
credits
if
credit
[
"role"
]
==
"Writer"
])
or
u'Unknown'
,
cover
=
_extractCover
(
tmp_file_path
,
original_file_extension
,
rarExecutable
),
description
=
loadedMetadata
.
comments
or
""
,
tags
=
""
,
...
...
cps/config_sql.py
View file @
f77d72fd
...
...
@@ -146,15 +146,16 @@ class _ConfigSQL(object):
self
.
load
()
change
=
False
if
self
.
config_converterpath
==
None
:
if
self
.
config_converterpath
==
None
:
# pylint: disable=access-member-before-definition
change
=
True
self
.
config_converterpath
=
autodetect_calibre_binary
()
if
self
.
config_kepubifypath
==
None
:
if
self
.
config_kepubifypath
==
None
:
# pylint: disable=access-member-before-definition
change
=
True
self
.
config_kepubifypath
=
autodetect_kepubify_binary
()
if
self
.
config_rarfile_location
==
None
:
if
self
.
config_rarfile_location
==
None
:
# pylint: disable=access-member-before-definition
change
=
True
self
.
config_rarfile_location
=
autodetect_unrar_binary
()
if
change
:
...
...
@@ -181,7 +182,8 @@ class _ConfigSQL(object):
return
None
return
self
.
config_keyfile
def
get_config_ipaddress
(
self
):
@
staticmethod
def
get_config_ipaddress
():
return
cli
.
ipadress
or
""
def
_has_role
(
self
,
role_flag
):
...
...
@@ -299,6 +301,7 @@ class _ConfigSQL(object):
have_metadata_db
=
os
.
path
.
isfile
(
db_file
)
self
.
db_configured
=
have_metadata_db
constants
.
EXTENSIONS_UPLOAD
=
[
x
.
lstrip
()
.
rstrip
()
.
lower
()
for
x
in
self
.
config_upload_formats
.
split
(
','
)]
# pylint: disable=access-member-before-definition
logfile
=
logger
.
setup
(
self
.
config_logfile
,
self
.
config_log_level
)
if
logfile
!=
self
.
config_logfile
:
log
.
warning
(
"Log path
%
s not valid, falling back to default"
,
self
.
config_logfile
)
...
...
cps/constants.py
View file @
f77d72fd
...
...
@@ -126,7 +126,7 @@ LDAP_AUTH_SIMPLE = 0
DEFAULT_MAIL_SERVER
=
"mail.example.org"
DEFAULT_PASSWORD
=
"admin123"
DEFAULT_PASSWORD
=
"admin123"
# nosec # noqa
DEFAULT_PORT
=
8083
env_CALIBRE_PORT
=
os
.
environ
.
get
(
"CALIBRE_PORT"
,
DEFAULT_PORT
)
try
:
...
...
cps/db.py
View file @
f77d72fd
...
...
@@ -156,10 +156,8 @@ class Identifiers(Base):
return
u"https://portal.issn.org/resource/ISSN/{0}"
.
format
(
self
.
val
)
elif
format_type
==
"isfdb"
:
return
u"http://www.isfdb.org/cgi-bin/pl.cgi?{0}"
.
format
(
self
.
val
)
elif
format_type
==
"url"
:
return
u"{0}"
.
format
(
self
.
val
)
else
:
return
u"
"
return
u"
{0}"
.
format
(
self
.
val
)
class
Comments
(
Base
):
...
...
cps/helper.py
View file @
f77d72fd
...
...
@@ -134,63 +134,71 @@ def send_registration_mail(e_mail, user_name, default_password, resend=False):
taskMessage
=
_
(
u"Registration e-mail for user:
%(name)
s"
,
name
=
user_name
),
text
=
txt
))
return
def
check_send_to_kindle_without_converter
(
entry
):
bookformats
=
list
()
# no converter - only for mobi and pdf formats
for
ele
in
iter
(
entry
.
data
):
if
ele
.
uncompressed_size
<
config
.
mail_size
:
if
'MOBI'
in
ele
.
format
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Mobi'
)})
if
'PDF'
in
ele
.
format
:
bookformats
.
append
({
'format'
:
'Pdf'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Pdf'
)})
if
'AZW'
in
ele
.
format
:
bookformats
.
append
({
'format'
:
'Azw'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Azw'
)})
return
bookformats
def
check_send_to_kindle_with_converter
(
entry
):
bookformats
=
list
()
formats
=
list
()
for
ele
in
iter
(
entry
.
data
):
if
ele
.
uncompressed_size
<
config
.
mail_size
:
formats
.
append
(
ele
.
format
)
if
'MOBI'
in
formats
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Mobi'
)})
if
'AZW'
in
formats
:
bookformats
.
append
({
'format'
:
'Azw'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Azw'
)})
if
'PDF'
in
formats
:
bookformats
.
append
({
'format'
:
'Pdf'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Pdf'
)})
if
'EPUB'
in
formats
and
'MOBI'
not
in
formats
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
1
,
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Epub'
,
format
=
'Mobi'
)})
if
'AZW3'
in
formats
and
not
'MOBI'
in
formats
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
2
,
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Azw3'
,
format
=
'Mobi'
)})
return
bookformats
def
check_send_to_kindle
(
entry
):
"""
returns all available book formats for sending to Kindle
"""
if
len
(
entry
.
data
):
bookformats
=
list
()
if
not
config
.
config_converterpath
:
# no converter - only for mobi and pdf formats
for
ele
in
iter
(
entry
.
data
):
if
ele
.
uncompressed_size
<
config
.
mail_size
:
if
'MOBI'
in
ele
.
format
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Mobi'
)})
if
'PDF'
in
ele
.
format
:
bookformats
.
append
({
'format'
:
'Pdf'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Pdf'
)})
if
'AZW'
in
ele
.
format
:
bookformats
.
append
({
'format'
:
'Azw'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Azw'
)})
book_formats
=
check_send_to_kindle_with_converter
(
entry
)
else
:
formats
=
list
()
for
ele
in
iter
(
entry
.
data
):
if
ele
.
uncompressed_size
<
config
.
mail_size
:
formats
.
append
(
ele
.
format
)
if
'MOBI'
in
formats
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Mobi'
)})
if
'AZW'
in
formats
:
bookformats
.
append
({
'format'
:
'Azw'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Azw'
)})
if
'PDF'
in
formats
:
bookformats
.
append
({
'format'
:
'Pdf'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Pdf'
)})
if
config
.
config_converterpath
:
if
'EPUB'
in
formats
and
'MOBI'
not
in
formats
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
1
,
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Epub'
,
format
=
'Mobi'
)})
if
'AZW3'
in
formats
and
not
'MOBI'
in
formats
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
2
,
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Azw3'
,
format
=
'Mobi'
)})
return
bookformats
book_formats
=
check_send_to_kindle_with_converter
(
entry
)
return
book_formats
else
:
log
.
error
(
u'Cannot find book entry
%
d'
,
entry
.
id
)
return
None
...
...
@@ -742,7 +750,7 @@ def format_runtime(runtime):
# helper function to apply localize status information in tasklist entries
def
render_task_status
(
tasklist
):
renderedtasklist
=
list
()
for
num
,
user
,
added
,
task
in
tasklist
:
for
__
,
user
,
added
,
task
in
tasklist
:
if
user
==
current_user
.
nickname
or
current_user
.
role_admin
():
ret
=
{}
if
task
.
start_time
:
...
...
cps/isoLanguages.py
View file @
f77d72fd
...
...
@@ -72,7 +72,7 @@ def get_valid_language_codes(locale, language_names, remainder=None):
languages
=
list
()
if
""
in
language_names
:
language_names
.
remove
(
""
)
for
k
,
v
in
get_language_names
(
locale
)
.
items
():
for
k
,
__
in
get_language_names
(
locale
)
.
items
():
if
k
in
language_names
:
languages
.
append
(
k
)
language_names
.
remove
(
k
)
...
...
cps/oauth.py
View file @
f77d72fd
This diff is collapsed.
Click to expand it.
cps/oauth_bb.py
View file @
f77d72fd
...
...
@@ -35,7 +35,10 @@ from sqlalchemy.orm.exc import NoResultFound
from
.
import
constants
,
logger
,
config
,
app
,
ub
from
.oauth
import
OAuthBackend
,
backend_resultcode
try
:
from
.oauth
import
OAuthBackend
,
backend_resultcode
except
NameError
:
pass
oauth_check
=
{}
...
...
cps/server.py
View file @
f77d72fd
...
...
@@ -137,7 +137,7 @@ class WebServer(object):
return
sock
,
_readable_listen_address
(
*
address
)
@
staticmethod
def
_get_args_for_reloading
(
self
):
"""Determine how the script was executed, and return the args needed
to execute it again in a new process.
...
...
cps/services/SyncToken.py
View file @
f77d72fd
...
...
@@ -64,7 +64,7 @@ class SyncToken:
books_last_modified: Datetime representing the last modified book that the device knows about.
"""
SYNC_TOKEN_HEADER
=
"x-kobo-synctoken"
SYNC_TOKEN_HEADER
=
"x-kobo-synctoken"
# nosec
VERSION
=
"1-1-0"
LAST_MODIFIED_ADDED_VERSION
=
"1-1-0"
MIN_VERSION
=
"1-0-0"
...
...
@@ -91,7 +91,7 @@ class SyncToken:
def
__init__
(
self
,
raw_kobo_store_token
=
""
,
raw_kobo_store_token
=
""
,
# nosec
books_last_created
=
datetime
.
min
,
books_last_modified
=
datetime
.
min
,
archive_last_modified
=
datetime
.
min
,
...
...
@@ -110,7 +110,7 @@ class SyncToken:
@
staticmethod
def
from_headers
(
headers
):
sync_token_header
=
headers
.
get
(
SyncToken
.
SYNC_TOKEN_HEADER
,
""
)
if
sync_token_header
==
""
:
if
sync_token_header
==
""
:
# nosec
return
SyncToken
()
# On the first sync from a Kobo device, we may receive the SyncToken
...
...
cps/static/css/caliBlur_override.css
View file @
f77d72fd
body
.serieslist.grid-view
div
.container-fluid
>
div
>
div
.col-sm-10
:before
{
display
:
none
;
body
.serieslist.grid-view
div
.container-fluid
>
div
>
div
.col-sm-10
:before
{
display
:
none
;
}
.cover
.badge
{
position
:
absolute
;
top
:
0
;
left
:
0
;
color
:
#fff
;
background-color
:
#cc7b19
;
border-radius
:
0
;
padding
:
0
8px
;
box-shadow
:
0
0
4px
rgba
(
0
,
0
,
0
,
.6
);
line-height
:
24px
;
position
:
absolute
;
top
:
0
;
left
:
0
;
color
:
#fff
;
background-color
:
#cc7b19
;
border-radius
:
0
;
padding
:
0
8px
;
box-shadow
:
0
0
4px
rgba
(
0
,
0
,
0
,
.6
);
line-height
:
24px
;
}
.cover
{
box-shadow
:
0
0
4px
rgba
(
0
,
0
,
0
,
.6
);
.cover
{
box-shadow
:
0
0
4px
rgba
(
0
,
0
,
0
,
.6
);
}
.cover
.read
{
padding
:
0
0px
;
line-height
:
15px
;
.cover
.read
{
padding
:
0px
0px
;
line-height
:
15px
;
}
cps/static/css/kthoom.css
View file @
f77d72fd
...
...
@@ -33,7 +33,6 @@ body {
position
:
relative
;
cursor
:
pointer
;
padding
:
4px
;
transition
:
all
0.2s
ease
;
}
...
...
@@ -45,7 +44,7 @@ body {
#sidebar
a
.active
,
#sidebar
a
.active
img
+
span
{
background-color
:
#45
B29D
;
background-color
:
#45
b29d
;
}
#sidebar
li
img
{
...
...
@@ -99,7 +98,7 @@ body {
background-color
:
#ccc
;
}
#progress
.bar-read
{
#progress
.bar-read
{
color
:
#fff
;
background-color
:
#45b29d
;
}
...
...
cps/static/css/main.css
View file @
f77d72fd
...
...
@@ -35,7 +35,6 @@ body {
height
:
8%
;
min-height
:
20px
;
padding
:
10px
;
/* margin: 0 50px 0 50px; */
position
:
relative
;
color
:
#4f4f4f
;
font-weight
:
100
;
...
...
@@ -114,7 +113,7 @@ body {
top
:
50%
;
margin-top
:
-192px
;
font-size
:
64px
;
color
:
#
E2E2E
2
;
color
:
#
e2e2e
2
;
font-family
:
arial
,
sans-serif
;
font-weight
:
bold
;
cursor
:
pointer
;
...
...
@@ -148,12 +147,6 @@ body {
overflow
:
hidden
;
}
#sidebar
.open
{
/* left: 0; */
/* -webkit-transform: translate(0, 0);
-moz-transform: translate(0, 0); */
}
#main
.closed
{
/* left: 300px; */
-webkit-transform
:
translate
(
300px
,
0
);
...
...
@@ -238,7 +231,7 @@ input:-moz-placeholder { color: #454545; }
left
:
50%
;
margin-left
:
-1px
;
top
:
10%
;
opacity
:
.15
;
opacity
:
0
.15
;
box-shadow
:
-2px
0
15px
rgba
(
0
,
0
,
0
,
1
);
display
:
none
;
}
...
...
@@ -291,7 +284,7 @@ input:-moz-placeholder { color: #454545; }
#tocView
li
,
#bookmarksView
li
{
margin-bottom
:
10px
;
margin-bottom
:
10px
;
width
:
225px
;
font-family
:
Georgia
,
"Times New Roman"
,
Times
,
serif
;
list-style
:
none
;
...
...
@@ -299,8 +292,7 @@ input:-moz-placeholder { color: #454545; }
}
#tocView
li
:active
,
#tocView
li
.currentChapter
{
#tocView
li
.currentChapter
{
list-style
:
none
;
}
...
...
@@ -319,7 +311,7 @@ input:-moz-placeholder { color: #454545; }
.list_item.currentChapter
>
a
,
.list_item
a
:hover
{
color
:
#f1f1f1
color
:
#f1f1f1
;
}
/* #tocView li.openChapter > a, */
...
...
@@ -328,7 +320,7 @@ input:-moz-placeholder { color: #454545; }
}
.list_item
ul
{
padding-left
:
10px
;
padding-left
:
10px
;
margin-top
:
8px
;
display
:
none
;
}
...
...
@@ -414,7 +406,7 @@ input:-moz-placeholder { color: #454545; }
}
#notes
{
padding
:
0
0
0
34px
;
padding
:
0
0
0
34px
;
}
#notes
li
{
...
...
@@ -449,8 +441,9 @@ input:-moz-placeholder { color: #454545; }
border-radius
:
5px
;
}
#note-text
[
disabled
],
#note-text
[
disabled
=
"disabled"
]
{
opacity
:
0.5
;
#note-text
[
disabled
],
#note-text
[
disabled
=
"disabled"
]
{
opacity
:
0.5
;
}
#note-anchor
{
...
...
@@ -478,26 +471,24 @@ input:-moz-placeholder { color: #454545; }
color
:
#f1f1f1
;
}
#settingsPanel
.xsmall
{
font-size
:
x-small
;
}
#settingsPanel
.small
{
font-size
:
small
;
}
#settingsPanel
.medium
{
font-size
:
medium
;
}
#settingsPanel
.large
{
font-size
:
large
;
}
#settingsPanel
.xlarge
{
font-size
:
x-large
;
}
#settingsPanel
.xsmall
{
font-size
:
x-small
;
}
#settingsPanel
.small
{
font-size
:
small
;
}
#settingsPanel
.medium
{
font-size
:
medium
;
}
#settingsPanel
.large
{
font-size
:
large
;
}
#settingsPanel
.xlarge
{
font-size
:
x-large
;
}
.highlight
{
background-color
:
yellow
}
.highlight
{
background-color
:
yellow
;
}
.modal
{
position
:
fixed
;
top
:
50%
;
left
:
50%
;
//
width
:
50%
;
width
:
630px
;
height
:
auto
;
z-index
:
2000
;
visibility
:
hidden
;
margin-left
:
-320px
;
margin-top
:
-160px
;
}
.overlay
{
...
...
@@ -516,12 +507,12 @@ input:-moz-placeholder { color: #454545; }
}
.md-show
{
visibility
:
visible
;
visibility
:
visible
;
}
.md-show
~
.overlay
{
opacity
:
1
;
visibility
:
visible
;
opacity
:
1
;
visibility
:
visible
;
}
/* Content styles */
...
...
@@ -593,7 +584,6 @@ input:-moz-placeholder { color: #454545; }
}
.md-content
>
.closer
{
//
font-size
:
18px
;
position
:
absolute
;
right
:
0
;
top
:
0
;
...
...
@@ -602,7 +592,7 @@ input:-moz-placeholder { color: #454545; }
}
@media
only
screen
and
(
max-width
:
1040px
)
and
(
orientation
:
portrait
)
{
#viewer
{
#viewer
{
width
:
80%
;
margin-left
:
10%
;
}
...
...
@@ -614,7 +604,7 @@ input:-moz-placeholder { color: #454545; }
}
@media
only
screen
and
(
max-width
:
900px
)
{
#viewer
{
#viewer
{
width
:
60%
;
margin-left
:
20%
;
}
...
...
@@ -653,9 +643,9 @@ input:-moz-placeholder { color: #454545; }
-webkit-transform
:
translate
(
0
,
0
);
-moz-transform
:
translate
(
0
,
0
);
-ms-transform
:
translate
(
0
,
0
);
-webkit-transition
:
-webkit-transform
.3s
;
-moz-transition
:
-moz-transform
.3s
;
transition
:
-moz-transform
.3s
;
-webkit-transition
:
-webkit-transform
0
.3s
;
-moz-transition
:
-moz-transform
0
.3s
;
transition
:
-moz-transform
0
.3s
;
}
#main
.closed
{
...
...
@@ -681,12 +671,11 @@ input:-moz-placeholder { color: #454545; }
font-size
:
12px
;
}
#tocView
>
ul
{
#tocView
>
ul
{
padding-left
:
10px
;
}
}
/* For iPad portrait layouts only */
@media
only
screen
and
(
min-device-width
:
481px
)
and
(
max-device-width
:
1024px
)
and
(
orientation
:
portrait
)
{
#viewer
iframe
{
...
...
@@ -694,20 +683,13 @@ input:-moz-placeholder { color: #454545; }
height
:
740px
;
}
}
/*For iPad landscape layouts only *//*
@media only screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation: landscape) {
#viewer iframe {
width: 460px;
height: 415px;
}
}*/
@media
only
screen
and
(
min-device-width
:
768px
)
and
(
max-device-width
:
1024px
)
and
(
orientation
:
landscape
)
/*and (-webkit-min-device-pixel-ratio: 2)*/
{
#viewer
{
#viewer
{
width
:
80%
;
margin-left
:
10%
;
}
...
...
@@ -720,8 +702,8 @@ and (orientation : landscape)
/*For iPad landscape layouts only */
@media
only
screen
and
(
min-device-width
:
481px
)
and
(
max-device-width
:
1024px
)
and
(
orientation
:
landscape
)
{
#viewer
iframe
{
width
:
960px
;
height
:
515px
;
width
:
960px
;
height
:
515px
;
}
}
...
...
@@ -764,8 +746,8 @@ and (orientation : landscape)
/* For iPhone landscape layouts only */
@media
only
screen
and
(
max-device-width
:
374px
)
and
(
orientation
:
landscape
)
{
#viewer
iframe
{
width
:
256px
;
height
:
124px
;
width
:
256px
;
height
:
124px
;
}
}
...
...
cps/static/css/style.css
View file @
f77d72fd
.tooltip.bottom
.tooltip-inner
{
font-size
:
13px
;
font-family
:
Open
Sans
Semibold
,
Helvetica
Neue
,
Helvetica
,
Arial
,
sans-serif
;
font-family
:
Open
Sans
Semibold
,
Helvetica
Neue
,
Helvetica
,
Arial
,
sans-serif
;
-webkit-font-smoothing
:
antialiased
;
-moz-osx-font-smoothing
:
grayscale
;
padding
:
3px
10px
;
...
...
@@ -48,10 +48,14 @@ body {
body
h2
{
font-weight
:
normal
;
color
:
#444
;
color
:
#444
;
}
a
,
.danger
,
.book-remove
,
.user-remove
,
.editable-empty
,
.editable-empty
:hover
{
color
:
#45b29d
;
}
a
,
.danger
,
.book-remove
,
.editable-empty
,
.editable-empty
:hover
{
color
:
#45b29d
;
}
.book-remove
:hover
{
color
:
#23527c
;
}
...
...
@@ -60,15 +64,17 @@ a, .danger, .book-remove, .user-remove, .editable-empty, .editable-empty:hover {
.btn-default
a
{
color
:
#444
;
}
.btn-default
a
:hover
{
color
:
#45b29d
;
text-decoration
:
None
;
color
:
#45b29d
;
text-decoration
:
None
;
}
.btn-default
:hover
{
color
:
#45b29d
;
color
:
#45b29d
;
}
.editable-click
,
a
.editable-click
,
a
.editable-click
:hover
{
border-bottom
:
None
;
}
.editable-click
,
a
.editable-click
,
a
.editable-click
:hover
{
border-bottom
:
None
;
}
.navigation
.nav-head
{
text-transform
:
uppercase
;
...
...
@@ -121,9 +127,10 @@ a, .danger, .book-remove, .user-remove, .editable-empty, .editable-empty:hover {
max-height
:
100%
;
}
.container-fluid
.discover
{
margin-bottom
:
50px
;
}
.container-fluid
.discover
{
margin-bottom
:
50px
;
}
.container-fluid
.new-books
{
border-top
:
1px
solid
#ccc
;
}
.container-fluid
.new-books
h2
{
margin
:
50px
0
0
0
;
}
.container-fluid
.book
{
margin-top
:
20px
;
display
:
flex
;
...
...
@@ -176,9 +183,10 @@ a, .danger, .book-remove, .user-remove, .editable-empty, .editable-empty:hover {
.container-fluid
.book
.meta
.rating
{
margin-top
:
5px
;
}
.rating
.glyphicon-star-empty
{
color
:
#444
;
}
.rating
.glyphicon-star.good
{
color
:
#444
;
}
.rating-clear
.glyphicon-remove
{
color
:
#333
}
.rating-clear
.glyphicon-remove
{
color
:
#333
;
}
.container-fluid
.author
.author-hidden
,
.container-fluid
.author
.author-hidden-divider
{
display
:
none
;
}
.container-fluid
.author
.author-hidden
,
.container-fluid
.author
.author-hidden-divider
{
display
:
none
;
}
.navbar-brand
{
font-family
:
'Grand Hotel'
,
cursive
;
...
...
@@ -192,7 +200,7 @@ a, .danger, .book-remove, .user-remove, .editable-empty, .editable-empty:hover {
border-top
:
1px
solid
#ccc
;
}
.more-stuff
>
li
{
margin-bottom
:
10px
;
}
.more-stuff
>
li
{
margin-bottom
:
10px
;
}
.navbar-collapse.in
.navbar-nav
{
margin
:
0
;
}
span
.glyphicon.glyphicon-tags
{
...
...
@@ -213,19 +221,20 @@ span.glyphicon.glyphicon-tags {
box-shadow
:
0
5px
8px
-6px
#777
;
}
.navbar-default
.navbar-toggle
.icon-bar
{
background-color
:
#000
;
}
.navbar-default
.navbar-toggle
{
border-color
:
#000
;
}
.navbar-default
.navbar-toggle
.icon-bar
{
background-color
:
#000
;
}
.navbar-default
.navbar-toggle
{
border-color
:
#000
;
}
.cover
{
margin-bottom
:
10px
;
}
.cover
.badge
{
position
:
absolute
;
top
:
2px
;
left
:
2px
;
color
:
#000
;
border-radius
:
10px
;
background-color
:
#fff
;
.cover
.badge
{
position
:
absolute
;
top
:
2px
;
left
:
2px
;
color
:
#000
;
border-radius
:
10px
;
background-color
:
#fff
;
}
.cover
.read
{
.cover
.read
{
left
:
auto
;
right
:
2px
;
width
:
17px
;
...
...
@@ -233,14 +242,17 @@ span.glyphicon.glyphicon-tags {
display
:
inline-block
;
padding
:
2px
;
}
.cover-height
{
max-height
:
100px
;}
.cover-height
{
max-height
:
100px
;
}
.col-sm-2
a
.cover-small
{
margin
:
5px
;
max-height
:
200px
;
}
.btn-file
{
position
:
relative
;
overflow
:
hidden
;}
.btn-file
{
position
:
relative
;
overflow
:
hidden
;
}
.btn-file
input
[
type
=
file
]
{
position
:
absolute
;
...
...
@@ -258,24 +270,62 @@ span.glyphicon.glyphicon-tags {
display
:
block
;
}
.btn-toolbar
.btn
,
.discover
.btn
{
margin-bottom
:
5px
;
}
.button-link
{
color
:
#fff
;
}
.btn-primary
:hover
,
.btn-primary
:focus
,
.btn-primary
:active
,
.btn-primary.active
,
.open
.dropdown-toggle.btn-primary
{
background-color
:
#1C5484
;
}
.btn-primary.disabled
,
.btn-primary
[
disabled
],
fieldset
[
disabled
]
.btn-primary
,
.btn-primary.disabled
:hover
,
.btn-primary
[
disabled
]
:hover
,
fieldset
[
disabled
]
.btn-primary
:hover
,
.btn-primary.disabled
:focus
,
.btn-primary
[
disabled
]
:focus
,
fieldset
[
disabled
]
.btn-primary
:focus
,
.btn-primary.disabled
:active
,
.btn-primary
[
disabled
]
:active
,
fieldset
[
disabled
]
.btn-primary
:active
,
.btn-primary.disabled.active
,
.btn-primary
[
disabled
]
.active
,
fieldset
[
disabled
]
.btn-primary.active
{
background-color
:
#89B9E2
;
}
.btn-toolbar
>
.btn
+
.btn
,
.btn-toolbar
>
.btn-group
+
.btn
,
.btn-toolbar
>
.btn
+
.btn-group
,
.btn-toolbar
>
.btn-group
+
.btn-group
{
margin-left
:
0
;
}
.panel-body
{
background-color
:
#f5f5f5
;
}
.spinner
{
margin
:
0
41%
;
}
.spinner2
{
margin
:
0
41%
;
}
.intend-form
{
margin-left
:
20px
;
}
table
.bg-dark-danger
{
background-color
:
#d9534f
;
color
:
#fff
;
}
table
.bg-dark-danger
a
{
color
:
#fff
;
}
table
.bg-dark-danger
:hover
{
background-color
:
#c9302c
;
}
table
.bg-primary
:hover
{
background-color
:
#1C5484
;
}
table
.bg-primary
a
{
color
:
#fff
;
}
.block-label
{
display
:
block
;}
.fake-input
{
position
:
absolute
;
pointer-events
:
none
;
top
:
0
;
}
input
.pill
{
position
:
absolute
;
opacity
:
0
;
}
.btn-toolbar
.btn
,
.discover
.btn
{
margin-bottom
:
5px
;
}
.button-link
{
color
:
#fff
;
}
.btn-primary
:hover
,
.btn-primary
:focus
,
.btn-primary
:active
,
.btn-primary.active
,
.open
.dropdown-toggle.btn-primary
{
background-color
:
#1c5484
;
}
.btn-primary.disabled
,
.btn-primary
[
disabled
],
fieldset
[
disabled
]
.btn-primary
,
.btn-primary.disabled
:hover
,
.btn-primary
[
disabled
]
:hover
,
fieldset
[
disabled
]
.btn-primary
:hover
,
.btn-primary.disabled
:focus
,
.btn-primary
[
disabled
]
:focus
,
fieldset
[
disabled
]
.btn-primary
:focus
,
.btn-primary.disabled
:active
,
.btn-primary
[
disabled
]
:active
,
fieldset
[
disabled
]
.btn-primary
:active
,
.btn-primary.disabled.active
,
.btn-primary
[
disabled
]
.active
,
fieldset
[
disabled
]
.btn-primary.active
{
background-color
:
#89b9e2
;
}
.btn-toolbar
>
.btn
+
.btn
,
.btn-toolbar
>
.btn-group
+
.btn
,
.btn-toolbar
>
.btn
+
.btn-group
,
.btn-toolbar
>
.btn-group
+
.btn-group
{
margin-left
:
0
;
}
.panel-body
{
background-color
:
#f5f5f5
;
}
.spinner
{
margin
:
0
41%
;
}
.spinner2
{
margin
:
0
41%
;
}
.intend-form
{
margin-left
:
20px
;
}
table
.bg-dark-danger
{
background-color
:
#d9534f
;
color
:
#fff
;
}
table
.bg-dark-danger
a
{
color
:
#fff
;
}
table
.bg-dark-danger
:hover
{
background-color
:
#c9302c
;
}
table
.bg-primary
:hover
{
background-color
:
#1c5484
;
}
table
.bg-primary
a
{
color
:
#fff
;
}
.block-label
{
display
:
block
;
}
.fake-input
{
position
:
absolute
;
pointer-events
:
none
;
top
:
0
;
}
input
.pill
{
position
:
absolute
;
opacity
:
0
;
}
input
.pill
+
label
{
border
:
2px
solid
#45b29d
;
...
...
@@ -298,11 +348,24 @@ input.pill:checked + label {
input
.pill
:not
(
:checked
)
+
label
.glyphicon
{
display
:
none
;
}
.author-bio
img
{
margin
:
0
1em
1em
0
;
}
.author-link
{
display
:
inline-block
;
margin-top
:
10px
;
width
:
100px
;
}
.author-link
img
{
display
:
block
;
height
:
100%
;
}
#remove-from-shelves
.btn
,
#shelf-action-errors
{
margin-left
:
5px
;
}
.tags_click
,
.serie_click
,
.language_click
{
margin-right
:
5px
;
}
.author-link
{
display
:
inline-block
;
margin-top
:
10px
;
width
:
100px
;
}
.author-link
img
{
display
:
block
;
height
:
100%
;
}
#remove-from-shelves
.btn
,
#shelf-action-errors
{
margin-left
:
5px
;
}
.tags_click
,
.serie_click
,
.language_click
{
margin-right
:
5px
;
}
#meta-info
{
height
:
600px
;
...
...
@@ -325,11 +388,11 @@ input.pill:not(:checked) + label .glyphicon { display: none; }
#btn-upload-cover
{
display
:
none
;
}
.panel-title
>
a
{
text-decoration
:
none
;
}
.editable-buttons
{
display
:
inline-block
;
display
:
inline-block
;
margin-left
:
7px
;
}
.editable-input
{
display
:
inline-block
;
}
.editable-input
{
display
:
inline-block
;
}
.editable-cancel
{
margin-bottom
:
0
!important
;
...
...
cps/static/js/caliBlur.js
View file @
f77d72fd
...
...
@@ -677,7 +677,7 @@ $(".navbar-collapse.collapse.in").before('<div class="sidebar-backdrop"></div>')
// Get rid of leading white space
recentlyAdded
=
$
(
"#nav_new a:contains('Recently')"
).
text
().
trim
();
$
(
"#nav_new a:contains('Recently')"
).
contents
().
filter
(
function
()
{
return
this
.
nodeType
==
3
return
this
.
nodeType
==
=
3
}).
each
(
function
()
{
this
.
textContent
=
this
.
textContent
.
replace
(
" Recently Added"
,
recentlyAdded
);
});
...
...
cps/static/js/edit_books.js
View file @
f77d72fd
/**
* Created by SpeedProg on 05.04.2015.
*/
/* global Bloodhound, language, Modernizr, tinymce */
/* global Bloodhound, language, Modernizr, tinymce
, getPath
*/
if
(
$
(
"#description"
).
length
)
{
tinymce
.
init
({
...
...
@@ -250,14 +250,14 @@ promisePublishers.done(function() {
});
$
(
"#search"
).
on
(
"change input.typeahead:selected"
,
function
(
event
)
{
if
(
event
.
target
.
type
==
"search"
&&
event
.
target
.
tagName
==
"INPUT"
)
{
if
(
event
.
target
.
type
==
=
"search"
&&
event
.
target
.
tagName
=
==
"INPUT"
)
{
return
;
}
var
form
=
$
(
"form"
).
serialize
();
$
.
getJSON
(
getPath
()
+
"/get_matching_tags"
,
form
,
function
(
data
)
{
$
(
".tags_click"
).
each
(
function
()
{
if
(
$
.
inArray
(
parseInt
(
$
(
this
).
val
(),
10
),
data
.
tags
)
===
-
1
)
{
if
(
!
$
(
this
).
prop
(
"selected"
))
{
if
(
!
$
(
this
).
prop
(
"selected"
))
{
$
(
this
).
prop
(
"disabled"
,
true
);
}
}
else
{
...
...
@@ -265,10 +265,10 @@ $("#search").on("change input.typeahead:selected", function(event) {
}
});
$
(
"#include_tag option:selected"
).
each
(
function
()
{
$
(
"#exclude_tag"
).
find
(
"[value="
+
$
(
this
).
val
()
+
"]"
).
prop
(
"disabled"
,
true
);
$
(
"#exclude_tag"
).
find
(
"[value="
+
$
(
this
).
val
()
+
"]"
).
prop
(
"disabled"
,
true
);
});
$
(
'#include_tag'
).
selectpicker
(
"refresh"
);
$
(
'#exclude_tag'
).
selectpicker
(
"refresh"
);
$
(
"#include_tag"
).
selectpicker
(
"refresh"
);
$
(
"#exclude_tag"
).
selectpicker
(
"refresh"
);
});
});
...
...
cps/static/js/filter_list.js
View file @
f77d72fd
...
...
@@ -88,7 +88,7 @@ $("#desc").click(function() {
// Find count of middle element
var
count
=
$
(
".row:visible"
).
length
;
if
(
count
>
20
)
{
middle
=
parseInt
(
count
/
2
)
+
(
count
%
2
);
middle
=
parseInt
(
count
/
2
,
10
)
+
(
count
%
2
);
//var middle = parseInt(count / 2) + (count % 2);
// search for the middle of all visible elements
...
...
@@ -135,7 +135,7 @@ $("#asc").click(function() {
// Find count of middle element
var
count
=
$
(
".row:visible"
).
length
;
if
(
count
>
20
)
{
var
middle
=
parseInt
(
count
/
2
)
+
(
count
%
2
);
var
middle
=
parseInt
(
count
/
2
,
10
)
+
(
count
%
2
);
//var middle = parseInt(count / 2) + (count % 2);
// search for the middle of all visible elements
...
...
cps/static/js/kthoom.js
View file @
f77d72fd
...
...
@@ -146,6 +146,9 @@ kthoom.ImageFile = function(file) {
case
"jpeg"
:
this
.
mimeType
=
"image/jpeg"
;
break
;
case
"png"
:
this
.
mimeType
=
"image/png"
;
break
;
case
"gif"
:
this
.
mimeType
=
"image/gif"
;
break
;
...
...
cps/static/js/main.js
View file @
f77d72fd
...
...
@@ -38,10 +38,10 @@ $(document).on("change", "input[type=\"checkbox\"][data-control]", function () {
$
(
document
).
on
(
"change"
,
"select[data-control]"
,
function
()
{
var
$this
=
$
(
this
);
var
name
=
$this
.
data
(
"control"
);
var
showOrHide
=
parseInt
(
$this
.
val
());
var
showOrHide
=
parseInt
(
$this
.
val
()
,
10
);
// var showOrHideLast = $("#" + name + " option:last").val()
for
(
var
i
=
0
;
i
<
$
(
this
)[
0
].
length
;
i
++
)
{
var
element
=
parseInt
(
$
(
this
)[
0
][
i
].
value
);
var
element
=
parseInt
(
$
(
this
)[
0
][
i
].
value
,
10
);
if
(
element
===
showOrHide
)
{
$
(
"[data-related^="
+
name
+
"][data-related*=-"
+
element
+
"]"
).
show
();
}
else
{
...
...
cps/static/js/table.js
View file @
f77d72fd
...
...
@@ -16,6 +16,7 @@
*/
/* exported TableActions, RestrictionActions, EbookActions, responseHandler */
/* global getPath, ConfirmDialog */
var
selections
=
[];
...
...
cps/tasks/upload.py
View file @
f77d72fd
...
...
@@ -12,7 +12,6 @@ class TaskUpload(CalibreTask):
def
run
(
self
,
worker_thread
):
"""Upload task doesn't have anything to do, it's simply a way to add information to the task list"""
pass
@
property
def
name
(
self
):
...
...
cps/ub.py
View file @
f77d72fd
...
...
@@ -138,15 +138,15 @@ class UserBase:
mct
=
self
.
allowed_column_value
or
""
return
[
t
.
strip
()
for
t
in
mct
.
split
(
","
)]
def
get_view_property
(
self
,
page
,
prop
erty
):
def
get_view_property
(
self
,
page
,
prop
):
if
not
self
.
view_settings
.
get
(
page
):
return
None
return
self
.
view_settings
[
page
]
.
get
(
prop
erty
)
return
self
.
view_settings
[
page
]
.
get
(
prop
)
def
set_view_property
(
self
,
page
,
prop
erty
,
value
):
def
set_view_property
(
self
,
page
,
prop
,
value
):
if
not
self
.
view_settings
.
get
(
page
):
self
.
view_settings
[
page
]
=
dict
()
self
.
view_settings
[
page
][
prop
erty
]
=
value
self
.
view_settings
[
page
][
prop
]
=
value
try
:
flag_modified
(
self
,
"view_settings"
)
except
AttributeError
:
...
...
@@ -437,11 +437,8 @@ class RemoteAuthToken(Base):
return
'<Token
%
r>'
%
self
.
id
# Migrate database to current version, has to be updated after every database change. Currently migration from
# everywhere to current should work. Migration is done by checking if relevant columns are existing, and than adding
# rows with SQL commands
def
migrate_Database
(
session
):
engine
=
session
.
bind
# Add missing tables during migration of database
def
add_missing_tables
(
engine
,
session
):
if
not
engine
.
dialect
.
has_table
(
engine
.
connect
(),
"book_read_link"
):
ReadBook
.
__table__
.
create
(
bind
=
engine
)
if
not
engine
.
dialect
.
has_table
(
engine
.
connect
(),
"bookmark"
):
...
...
@@ -459,6 +456,10 @@ def migrate_Database(session):
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"insert into registration (domain, allow) values('
%
.
%
',1)"
)
session
.
commit
()
# migrate all settings missing in registration table
def
migrate_registration_table
(
engine
,
session
):
try
:
session
.
query
(
exists
()
.
where
(
Registration
.
allow
))
.
scalar
()
session
.
commit
()
...
...
@@ -468,27 +469,29 @@ def migrate_Database(session):
conn
.
execute
(
"update registration set 'allow' = 1"
)
session
.
commit
()
try
:
session
.
query
(
exists
()
.
where
(
RemoteAuthToken
.
token_type
))
.
scalar
()
session
.
commit
()
except
exc
.
OperationalError
:
# Database is not compatible, some columns are missing
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"ALTER TABLE remote_auth_token ADD column 'token_type' INTEGER DEFAULT 0"
)
conn
.
execute
(
"update remote_auth_token set 'token_type' = 0"
)
session
.
commit
()
# Handle table exists, but no content
cnt
=
session
.
query
(
Registration
)
.
count
()
if
not
cnt
:
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"insert into registration (domain, allow) values('
%
.
%
',1)"
)
session
.
commit
()
except
exc
.
OperationalError
:
# Database is not writeable
print
(
'Settings database is not writeable. Exiting...'
)
sys
.
exit
(
2
)
# Remove login capability of user Guest
def
migrate_guest_password
(
engine
,
session
):
try
:
session
.
query
(
exists
()
.
where
(
ReadBook
.
read_status
))
.
scalar
()
except
exc
.
OperationalError
:
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'read_status' INTEGER DEFAULT 0"
)
conn
.
execute
(
"UPDATE book_read_link SET 'read_status' = 1 WHERE is_read"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'last_modified' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'last_time_started_reading' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'times_started_reading' INTEGER DEFAULT 0"
)
conn
.
execute
(
"UPDATE user SET password='' where nickname = 'Guest' and password !=''"
)
session
.
commit
()
test
=
session
.
query
(
ReadBook
)
.
filter
(
ReadBook
.
last_modified
==
None
)
.
all
()
for
book
in
test
:
book
.
last_modified
=
datetime
.
datetime
.
utcnow
()
session
.
commit
()
except
exc
.
OperationalError
:
print
(
'Settings database is not writeable. Exiting...'
)
sys
.
exit
(
2
)
def
migrate_shelfs
(
engine
,
session
):
try
:
session
.
query
(
exists
()
.
where
(
Shelf
.
uuid
))
.
scalar
()
except
exc
.
OperationalError
:
...
...
@@ -504,22 +507,51 @@ def migrate_Database(session):
for
book_shelf
in
session
.
query
(
BookShelf
)
.
all
():
book_shelf
.
date_added
=
datetime
.
datetime
.
now
()
session
.
commit
()
try
:
# Handle table exists, but no content
cnt
=
session
.
query
(
Registration
)
.
count
()
if
not
cnt
:
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"insert into registration (domain, allow) values('
%
.
%
',1)"
)
session
.
commit
()
except
exc
.
OperationalError
:
# Database is not writeable
print
(
'Settings database is not writeable. Exiting...'
)
sys
.
exit
(
2
)
try
:
session
.
query
(
exists
()
.
where
(
BookShelf
.
order
))
.
scalar
()
except
exc
.
OperationalError
:
# Database is not compatible, some columns are missing
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1"
)
session
.
commit
()
def
migrate_readBook
(
engine
,
session
):
try
:
session
.
query
(
exists
()
.
where
(
ReadBook
.
read_status
))
.
scalar
()
except
exc
.
OperationalError
:
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'read_status' INTEGER DEFAULT 0"
)
conn
.
execute
(
"UPDATE book_read_link SET 'read_status' = 1 WHERE is_read"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'last_modified' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'last_time_started_reading' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'times_started_reading' INTEGER DEFAULT 0"
)
session
.
commit
()
test
=
session
.
query
(
ReadBook
)
.
filter
(
ReadBook
.
last_modified
==
None
)
.
all
()
for
book
in
test
:
book
.
last_modified
=
datetime
.
datetime
.
utcnow
()
session
.
commit
()
def
migrate_remoteAuthToken
(
engine
,
session
):
try
:
session
.
query
(
exists
()
.
where
(
RemoteAuthToken
.
token_type
))
.
scalar
()
session
.
commit
()
except
exc
.
OperationalError
:
# Database is not compatible, some columns are missing
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"ALTER TABLE remote_auth_token ADD column 'token_type' INTEGER DEFAULT 0"
)
conn
.
execute
(
"update remote_auth_token set 'token_type' = 0"
)
session
.
commit
()
# Migrate database to current version, has to be updated after every database change. Currently migration from
# everywhere to current should work. Migration is done by checking if relevant columns are existing, and than adding
# rows with SQL commands
def
migrate_Database
(
session
):
engine
=
session
.
bind
add_missing_tables
(
engine
,
session
)
migrate_registration_table
(
engine
,
session
)
migrate_readBook
(
engine
,
session
)
migrate_remoteAuthToken
(
engine
,
session
)
migrate_shelfs
(
engine
,
session
)
try
:
create
=
False
session
.
query
(
exists
()
.
where
(
User
.
sidebar_view
))
.
scalar
()
...
...
@@ -578,8 +610,7 @@ def migrate_Database(session):
"locale VARCHAR(2),"
"sidebar_view INTEGER,"
"default_language VARCHAR(3),"
# "series_view VARCHAR(10),"
"view_settings VARCHAR,"
"view_settings VARCHAR,"
"UNIQUE (nickname),"
"UNIQUE (email))"
)
conn
.
execute
(
"INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale,"
...
...
@@ -590,15 +621,7 @@ def migrate_Database(session):
conn
.
execute
(
"DROP TABLE user"
)
conn
.
execute
(
"ALTER TABLE user_id RENAME TO user"
)
session
.
commit
()
# Remove login capability of user Guest
try
:
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"UPDATE user SET password='' where nickname = 'Guest' and password !=''"
)
session
.
commit
()
except
exc
.
OperationalError
:
print
(
'Settings database is not writeable. Exiting...'
)
sys
.
exit
(
2
)
migrate_guest_password
(
engine
,
session
)
def
clean_database
(
session
):
...
...
cps/usermanagement.py
View file @
f77d72fd
...
...
@@ -72,7 +72,7 @@ def load_user_from_request(request):
def
load_user_from_auth_header
(
header_val
):
if
header_val
.
startswith
(
'Basic '
):
header_val
=
header_val
.
replace
(
'Basic '
,
''
,
1
)
basic_username
=
basic_password
=
''
basic_username
=
basic_password
=
''
# nosec
try
:
header_val
=
base64
.
b64decode
(
header_val
)
.
decode
(
'utf-8'
)
basic_username
=
header_val
.
split
(
':'
)[
0
]
...
...
cps/web.py
View file @
f77d72fd
This diff is collapsed.
Click to expand it.
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