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
725fc658
Commit
725fc658
authored
Mar 14, 2021
by
Ozzie Isaacs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Some code cosmetics
parent
e4e27662
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
460 additions
and
410 deletions
+460
-410
comic.py
cps/comic.py
+38
-32
config_sql.py
cps/config_sql.py
+7
-4
constants.py
cps/constants.py
+1
-1
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
+0
-2
style.css
cps/static/css/style.css
+57
-36
caliBlur.js
cps/static/js/caliBlur.js
+1
-1
filter_list.js
cps/static/js/filter_list.js
+2
-2
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 @
725fc658
...
@@ -74,6 +74,41 @@ def _cover_processing(tmp_file_name, img, extension):
...
@@ -74,6 +74,41 @@ def _cover_processing(tmp_file_name, img, extension):
return
tmp_cover_name
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
):
def
_extractCover
(
tmp_file_name
,
original_file_extension
,
rarExecutable
):
cover_data
=
extension
=
None
cover_data
=
extension
=
None
...
@@ -87,37 +122,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable):
...
@@ -87,37 +122,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable):
cover_data
=
archive
.
getPage
(
index
)
cover_data
=
archive
.
getPage
(
index
)
break
break
else
:
else
:
if
original_file_extension
.
upper
()
==
'.CBZ'
:
cover_data
=
_extract_Cover_from_archive
(
original_file_extension
,
tmp_file_name
,
rarExecutable
)
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_processing
(
tmp_file_name
,
cover_data
,
extension
)
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
...
@@ -142,7 +147,8 @@ def get_comic_info(tmp_file_path, original_file_name, original_file_extension, r
file_path
=
tmp_file_path
,
file_path
=
tmp_file_path
,
extension
=
original_file_extension
,
extension
=
original_file_extension
,
title
=
loadedMetadata
.
title
or
original_file_name
,
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
),
cover
=
_extractCover
(
tmp_file_path
,
original_file_extension
,
rarExecutable
),
description
=
loadedMetadata
.
comments
or
""
,
description
=
loadedMetadata
.
comments
or
""
,
tags
=
""
,
tags
=
""
,
...
...
cps/config_sql.py
View file @
725fc658
...
@@ -146,15 +146,16 @@ class _ConfigSQL(object):
...
@@ -146,15 +146,16 @@ class _ConfigSQL(object):
self
.
load
()
self
.
load
()
change
=
False
change
=
False
if
self
.
config_converterpath
==
None
:
if
self
.
config_converterpath
==
None
:
# pylint: disable=access-member-before-definition
change
=
True
change
=
True
self
.
config_converterpath
=
autodetect_calibre_binary
()
self
.
config_converterpath
=
autodetect_calibre_binary
()
if
self
.
config_kepubifypath
==
None
:
if
self
.
config_kepubifypath
==
None
:
# pylint: disable=access-member-before-definition
change
=
True
change
=
True
self
.
config_kepubifypath
=
autodetect_kepubify_binary
()
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
change
=
True
self
.
config_rarfile_location
=
autodetect_unrar_binary
()
self
.
config_rarfile_location
=
autodetect_unrar_binary
()
if
change
:
if
change
:
...
@@ -181,7 +182,8 @@ class _ConfigSQL(object):
...
@@ -181,7 +182,8 @@ class _ConfigSQL(object):
return
None
return
None
return
self
.
config_keyfile
return
self
.
config_keyfile
def
get_config_ipaddress
(
self
):
@
staticmethod
def
get_config_ipaddress
():
return
cli
.
ipadress
or
""
return
cli
.
ipadress
or
""
def
_has_role
(
self
,
role_flag
):
def
_has_role
(
self
,
role_flag
):
...
@@ -299,6 +301,7 @@ class _ConfigSQL(object):
...
@@ -299,6 +301,7 @@ class _ConfigSQL(object):
have_metadata_db
=
os
.
path
.
isfile
(
db_file
)
have_metadata_db
=
os
.
path
.
isfile
(
db_file
)
self
.
db_configured
=
have_metadata_db
self
.
db_configured
=
have_metadata_db
constants
.
EXTENSIONS_UPLOAD
=
[
x
.
lstrip
()
.
rstrip
()
.
lower
()
for
x
in
self
.
config_upload_formats
.
split
(
','
)]
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
)
logfile
=
logger
.
setup
(
self
.
config_logfile
,
self
.
config_log_level
)
if
logfile
!=
self
.
config_logfile
:
if
logfile
!=
self
.
config_logfile
:
log
.
warning
(
"Log path
%
s not valid, falling back to default"
,
self
.
config_logfile
)
log
.
warning
(
"Log path
%
s not valid, falling back to default"
,
self
.
config_logfile
)
...
...
cps/constants.py
View file @
725fc658
...
@@ -104,7 +104,7 @@ LDAP_AUTH_SIMPLE = 0
...
@@ -104,7 +104,7 @@ LDAP_AUTH_SIMPLE = 0
DEFAULT_MAIL_SERVER
=
"mail.example.org"
DEFAULT_MAIL_SERVER
=
"mail.example.org"
DEFAULT_PASSWORD
=
"admin123"
DEFAULT_PASSWORD
=
"admin123"
# nosec # noqa
DEFAULT_PORT
=
8083
DEFAULT_PORT
=
8083
env_CALIBRE_PORT
=
os
.
environ
.
get
(
"CALIBRE_PORT"
,
DEFAULT_PORT
)
env_CALIBRE_PORT
=
os
.
environ
.
get
(
"CALIBRE_PORT"
,
DEFAULT_PORT
)
try
:
try
:
...
...
cps/helper.py
View file @
725fc658
...
@@ -134,63 +134,71 @@ def send_registration_mail(e_mail, user_name, default_password, resend=False):
...
@@ -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
),
taskMessage
=
_
(
u"Registration e-mail for user:
%(name)
s"
,
name
=
user_name
),
text
=
txt
text
=
txt
))
))
return
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
):
def
check_send_to_kindle
(
entry
):
"""
"""
returns all available book formats for sending to Kindle
returns all available book formats for sending to Kindle
"""
"""
if
len
(
entry
.
data
):
if
len
(
entry
.
data
):
bookformats
=
list
()
if
not
config
.
config_converterpath
:
if
not
config
.
config_converterpath
:
# no converter - only for mobi and pdf formats
book_formats
=
check_send_to_kindle_with_converter
(
entry
)
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'
)})
else
:
else
:
formats
=
list
()
book_formats
=
check_send_to_kindle_with_converter
(
entry
)
for
ele
in
iter
(
entry
.
data
):
return
book_formats
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
else
:
else
:
log
.
error
(
u'Cannot find book entry
%
d'
,
entry
.
id
)
log
.
error
(
u'Cannot find book entry
%
d'
,
entry
.
id
)
return
None
return
None
...
@@ -742,7 +750,7 @@ def format_runtime(runtime):
...
@@ -742,7 +750,7 @@ def format_runtime(runtime):
# helper function to apply localize status information in tasklist entries
# helper function to apply localize status information in tasklist entries
def
render_task_status
(
tasklist
):
def
render_task_status
(
tasklist
):
renderedtasklist
=
list
()
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
():
if
user
==
current_user
.
nickname
or
current_user
.
role_admin
():
ret
=
{}
ret
=
{}
if
task
.
start_time
:
if
task
.
start_time
:
...
...
cps/isoLanguages.py
View file @
725fc658
...
@@ -71,7 +71,7 @@ def get_valid_language_codes(locale, language_names, remainder=None):
...
@@ -71,7 +71,7 @@ def get_valid_language_codes(locale, language_names, remainder=None):
languages
=
list
()
languages
=
list
()
if
""
in
language_names
:
if
""
in
language_names
:
language_names
.
remove
(
""
)
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
:
if
k
in
language_names
:
languages
.
append
(
k
)
languages
.
append
(
k
)
language_names
.
remove
(
k
)
language_names
.
remove
(
k
)
...
...
cps/oauth.py
View file @
725fc658
This diff is collapsed.
Click to expand it.
cps/oauth_bb.py
View file @
725fc658
...
@@ -35,7 +35,10 @@ from sqlalchemy.orm.exc import NoResultFound
...
@@ -35,7 +35,10 @@ from sqlalchemy.orm.exc import NoResultFound
from
.
import
constants
,
logger
,
config
,
app
,
ub
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
=
{}
oauth_check
=
{}
...
...
cps/server.py
View file @
725fc658
...
@@ -137,7 +137,7 @@ class WebServer(object):
...
@@ -137,7 +137,7 @@ class WebServer(object):
return
sock
,
_readable_listen_address
(
*
address
)
return
sock
,
_readable_listen_address
(
*
address
)
@
staticmethod
def
_get_args_for_reloading
(
self
):
def
_get_args_for_reloading
(
self
):
"""Determine how the script was executed, and return the args needed
"""Determine how the script was executed, and return the args needed
to execute it again in a new process.
to execute it again in a new process.
...
...
cps/services/SyncToken.py
View file @
725fc658
...
@@ -64,7 +64,7 @@ class SyncToken:
...
@@ -64,7 +64,7 @@ class SyncToken:
books_last_modified: Datetime representing the last modified book that the device knows about.
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"
VERSION
=
"1-1-0"
LAST_MODIFIED_ADDED_VERSION
=
"1-1-0"
LAST_MODIFIED_ADDED_VERSION
=
"1-1-0"
MIN_VERSION
=
"1-0-0"
MIN_VERSION
=
"1-0-0"
...
@@ -91,7 +91,7 @@ class SyncToken:
...
@@ -91,7 +91,7 @@ class SyncToken:
def
__init__
(
def
__init__
(
self
,
self
,
raw_kobo_store_token
=
""
,
raw_kobo_store_token
=
""
,
# nosec
books_last_created
=
datetime
.
min
,
books_last_created
=
datetime
.
min
,
books_last_modified
=
datetime
.
min
,
books_last_modified
=
datetime
.
min
,
archive_last_modified
=
datetime
.
min
,
archive_last_modified
=
datetime
.
min
,
...
@@ -110,7 +110,7 @@ class SyncToken:
...
@@ -110,7 +110,7 @@ class SyncToken:
@
staticmethod
@
staticmethod
def
from_headers
(
headers
):
def
from_headers
(
headers
):
sync_token_header
=
headers
.
get
(
SyncToken
.
SYNC_TOKEN_HEADER
,
""
)
sync_token_header
=
headers
.
get
(
SyncToken
.
SYNC_TOKEN_HEADER
,
""
)
if
sync_token_header
==
""
:
if
sync_token_header
==
""
:
# nosec
return
SyncToken
()
return
SyncToken
()
# On the first sync from a Kobo device, we may receive the SyncToken
# On the first sync from a Kobo device, we may receive the SyncToken
...
...
cps/static/css/caliBlur_override.css
View file @
725fc658
body
.serieslist.grid-view
div
.container-fluid
>
div
>
div
.col-sm-10
:before
{
body
.serieslist.grid-view
div
.container-fluid
>
div
>
div
.col-sm-10
:before
{
display
:
none
;
display
:
none
;
}
}
.cover
.badge
{
.cover
.badge
{
position
:
absolute
;
position
:
absolute
;
top
:
0
;
top
:
0
;
left
:
0
;
left
:
0
;
color
:
#fff
;
color
:
#fff
;
background-color
:
#cc7b19
;
background-color
:
#cc7b19
;
border-radius
:
0
;
border-radius
:
0
;
padding
:
0
8px
;
padding
:
0
8px
;
box-shadow
:
0
0
4px
rgba
(
0
,
0
,
0
,
.6
);
box-shadow
:
0
0
4px
rgba
(
0
,
0
,
0
,
.6
);
line-height
:
24px
;
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
{
.cover
.read
{
padding
:
0
0px
;
padding
:
0px
0px
;
line-height
:
15px
;
line-height
:
15px
;
}
}
cps/static/css/kthoom.css
View file @
725fc658
...
@@ -33,7 +33,6 @@ body {
...
@@ -33,7 +33,6 @@ body {
position
:
relative
;
position
:
relative
;
cursor
:
pointer
;
cursor
:
pointer
;
padding
:
4px
;
padding
:
4px
;
transition
:
all
0.2s
ease
;
transition
:
all
0.2s
ease
;
}
}
...
@@ -45,7 +44,7 @@ body {
...
@@ -45,7 +44,7 @@ body {
#sidebar
a
.active
,
#sidebar
a
.active
,
#sidebar
a
.active
img
+
span
{
#sidebar
a
.active
img
+
span
{
background-color
:
#45
B29D
;
background-color
:
#45
b29d
;
}
}
#sidebar
li
img
{
#sidebar
li
img
{
...
@@ -99,7 +98,7 @@ body {
...
@@ -99,7 +98,7 @@ body {
background-color
:
#ccc
;
background-color
:
#ccc
;
}
}
#progress
.bar-read
{
#progress
.bar-read
{
color
:
#fff
;
color
:
#fff
;
background-color
:
#45b29d
;
background-color
:
#45b29d
;
}
}
...
...
cps/static/css/main.css
View file @
725fc658
...
@@ -490,7 +490,6 @@ input:-moz-placeholder { color: #454545; }
...
@@ -490,7 +490,6 @@ input:-moz-placeholder { color: #454545; }
position
:
fixed
;
position
:
fixed
;
top
:
50%
;
top
:
50%
;
left
:
50%
;
left
:
50%
;
//
width
:
50%
;
width
:
630px
;
width
:
630px
;
height
:
auto
;
height
:
auto
;
z-index
:
2000
;
z-index
:
2000
;
...
@@ -593,7 +592,6 @@ input:-moz-placeholder { color: #454545; }
...
@@ -593,7 +592,6 @@ input:-moz-placeholder { color: #454545; }
}
}
.md-content
>
.closer
{
.md-content
>
.closer
{
//
font-size
:
18px
;
position
:
absolute
;
position
:
absolute
;
right
:
0
;
right
:
0
;
top
:
0
;
top
:
0
;
...
...
cps/static/css/style.css
View file @
725fc658
.tooltip.bottom
.tooltip-inner
{
.tooltip.bottom
.tooltip-inner
{
font-size
:
13px
;
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
;
-webkit-font-smoothing
:
antialiased
;
-moz-osx-font-smoothing
:
grayscale
;
-moz-osx-font-smoothing
:
grayscale
;
padding
:
3px
10px
;
padding
:
3px
10px
;
...
@@ -48,25 +48,25 @@ body {
...
@@ -48,25 +48,25 @@ body {
body
h2
{
body
h2
{
font-weight
:
normal
;
font-weight
:
normal
;
color
:
#444
;
color
:
#444
;
}
}
a
,
.danger
,
.book-remove
,
.editable-empty
,
.editable-empty
:hover
{
color
:
#45b29d
;
}
a
,
.danger
,
.book-remove
,
.editable-empty
,
.editable-empty
:hover
{
color
:
#45b29d
;
}
.book-remove
:hover
{
color
:
#23527c
;
}
.book-remove
:hover
{
color
:
#23527c
;
}
.btn-default
a
{
color
:
#444
;
}
.btn-default
a
{
color
:
#444
;
}
.btn-default
a
:hover
{
.btn-default
a
:hover
{
color
:
#45b29d
;
color
:
#45b29d
;
text-decoration
:
None
;
text-decoration
:
None
;
}
}
.btn-default
:hover
{
.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
{
.navigation
.nav-head
{
text-transform
:
uppercase
;
text-transform
:
uppercase
;
...
@@ -119,7 +119,7 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d
...
@@ -119,7 +119,7 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d
max-height
:
100%
;
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
{
border-top
:
1px
solid
#ccc
;
}
.container-fluid
.new-books
h2
{
margin
:
50px
0
0
0
;
}
.container-fluid
.new-books
h2
{
margin
:
50px
0
0
0
;
}
.container-fluid
.book
{
.container-fluid
.book
{
...
@@ -174,9 +174,10 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d
...
@@ -174,9 +174,10 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d
.container-fluid
.book
.meta
.rating
{
margin-top
:
5px
;
}
.container-fluid
.book
.meta
.rating
{
margin-top
:
5px
;
}
.rating
.glyphicon-star-empty
{
color
:
#444
;
}
.rating
.glyphicon-star-empty
{
color
:
#444
;
}
.rating
.glyphicon-star.good
{
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
{
.navbar-brand
{
font-family
:
'Grand Hotel'
,
cursive
;
font-family
:
'Grand Hotel'
,
cursive
;
...
@@ -190,7 +191,7 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d
...
@@ -190,7 +191,7 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d
border-top
:
1px
solid
#ccc
;
border-top
:
1px
solid
#ccc
;
}
}
.more-stuff
>
li
{
margin-bottom
:
10px
;
}
.more-stuff
>
li
{
margin-bottom
:
10px
;
}
.navbar-collapse.in
.navbar-nav
{
margin
:
0
;
}
.navbar-collapse.in
.navbar-nav
{
margin
:
0
;
}
span
.glyphicon.glyphicon-tags
{
span
.glyphicon.glyphicon-tags
{
...
@@ -211,17 +212,17 @@ span.glyphicon.glyphicon-tags {
...
@@ -211,17 +212,17 @@ span.glyphicon.glyphicon-tags {
box-shadow
:
0
5px
8px
-6px
#777
;
box-shadow
:
0
5px
8px
-6px
#777
;
}
}
.navbar-default
.navbar-toggle
.icon-bar
{
background-color
:
#000
;
}
.navbar-default
.navbar-toggle
.icon-bar
{
background-color
:
#000
;
}
.navbar-default
.navbar-toggle
{
border-color
:
#000
;
}
.navbar-default
.navbar-toggle
{
border-color
:
#000
;
}
.cover
{
margin-bottom
:
10px
;
}
.cover
{
margin-bottom
:
10px
;
}
.cover
.badge
{
.cover
.badge
{
position
:
absolute
;
position
:
absolute
;
top
:
2px
;
top
:
2px
;
left
:
2px
;
left
:
2px
;
color
:
#000
;
color
:
#000
;
border-radius
:
10px
;
border-radius
:
10px
;
background-color
:
#fff
;
background-color
:
#fff
;
}
}
.cover
.read
{
.cover
.read
{
left
:
auto
;
left
:
auto
;
...
@@ -231,14 +232,14 @@ span.glyphicon.glyphicon-tags {
...
@@ -231,14 +232,14 @@ span.glyphicon.glyphicon-tags {
display
:
inline-block
;
display
:
inline-block
;
padding
:
2px
;
padding
:
2px
;
}
}
.cover-height
{
max-height
:
100px
;}
.cover-height
{
max-height
:
100px
;
}
.col-sm-2
a
.cover-small
{
.col-sm-2
a
.cover-small
{
margin
:
5px
;
margin
:
5px
;
max-height
:
200px
;
max-height
:
200px
;
}
}
.btn-file
{
position
:
relative
;
overflow
:
hidden
;
}
.btn-file
{
position
:
relative
;
overflow
:
hidden
;
}
.btn-file
input
[
type
=
file
]
{
.btn-file
input
[
type
=
file
]
{
position
:
absolute
;
position
:
absolute
;
...
@@ -256,22 +257,42 @@ span.glyphicon.glyphicon-tags {
...
@@ -256,22 +257,42 @@ span.glyphicon.glyphicon-tags {
display
:
block
;
display
:
block
;
}
}
.btn-toolbar
.btn
,
.discover
.btn
{
margin-bottom
:
5px
;
}
.btn-toolbar
.btn
,
.button-link
{
color
:
#fff
;
}
.discover
.btn
{
margin-bottom
:
5px
;
}
.btn-primary
:hover
,
.btn-primary
:focus
,
.btn-primary
:active
,
.btn-primary.active
,
.open
.dropdown-toggle.btn-primary
{
background-color
:
#1C5484
;
}
.button-link
{
color
:
#fff
;
}
.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
;
}
.btn-primary
:hover
,
.panel-body
{
background-color
:
#f5f5f5
;
}
.btn-primary
:focus
,
.spinner
{
margin
:
0
41%
;
}
.btn-primary
:active
,
.spinner2
{
margin
:
0
41%
;
}
.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
;
}
.intend-form
{
margin-left
:
20px
;
}
table
.bg-dark-danger
{
background-color
:
#d9534f
;
color
:
#fff
;
}
table
.bg-dark-danger
{
background-color
:
#d9534f
;
color
:
#fff
;
}
table
.bg-dark-danger
a
{
color
:
#fff
;
}
table
.bg-dark-danger
a
{
color
:
#fff
;
}
table
.bg-dark-danger
:hover
{
background-color
:
#c9302c
;
}
table
.bg-dark-danger
:hover
{
background-color
:
#c9302c
;
}
table
.bg-primary
:hover
{
background-color
:
#1C
5484
;
}
table
.bg-primary
:hover
{
background-color
:
#1c
5484
;
}
table
.bg-primary
a
{
color
:
#fff
;
}
table
.bg-primary
a
{
color
:
#fff
;
}
.block-label
{
display
:
block
;
}
.block-label
{
display
:
block
;
}
.fake-input
{
position
:
absolute
;
pointer-events
:
none
;
top
:
0
;
}
.fake-input
{
position
:
absolute
;
pointer-events
:
none
;
top
:
0
;
}
input
.pill
{
position
:
absolute
;
opacity
:
0
;
}
input
.pill
{
position
:
absolute
;
opacity
:
0
;
}
...
...
cps/static/js/caliBlur.js
View file @
725fc658
...
@@ -677,7 +677,7 @@ $(".navbar-collapse.collapse.in").before('<div class="sidebar-backdrop"></div>')
...
@@ -677,7 +677,7 @@ $(".navbar-collapse.collapse.in").before('<div class="sidebar-backdrop"></div>')
// Get rid of leading white space
// Get rid of leading white space
recentlyAdded
=
$
(
"#nav_new a:contains('Recently')"
).
text
().
trim
();
recentlyAdded
=
$
(
"#nav_new a:contains('Recently')"
).
text
().
trim
();
$
(
"#nav_new a:contains('Recently')"
).
contents
().
filter
(
function
()
{
$
(
"#nav_new a:contains('Recently')"
).
contents
().
filter
(
function
()
{
return
this
.
nodeType
==
3
return
this
.
nodeType
==
=
3
}).
each
(
function
()
{
}).
each
(
function
()
{
this
.
textContent
=
this
.
textContent
.
replace
(
" Recently Added"
,
recentlyAdded
);
this
.
textContent
=
this
.
textContent
.
replace
(
" Recently Added"
,
recentlyAdded
);
});
});
...
...
cps/static/js/filter_list.js
View file @
725fc658
...
@@ -88,7 +88,7 @@ $("#desc").click(function() {
...
@@ -88,7 +88,7 @@ $("#desc").click(function() {
// Find count of middle element
// Find count of middle element
var
count
=
$
(
".row:visible"
).
length
;
var
count
=
$
(
".row:visible"
).
length
;
if
(
count
>
20
)
{
if
(
count
>
20
)
{
middle
=
parseInt
(
count
/
2
)
+
(
count
%
2
);
middle
=
parseInt
(
count
/
2
,
10
)
+
(
count
%
2
);
//var middle = parseInt(count / 2) + (count % 2);
//var middle = parseInt(count / 2) + (count % 2);
// search for the middle of all visible elements
// search for the middle of all visible elements
...
@@ -135,7 +135,7 @@ $("#asc").click(function() {
...
@@ -135,7 +135,7 @@ $("#asc").click(function() {
// Find count of middle element
// Find count of middle element
var
count
=
$
(
".row:visible"
).
length
;
var
count
=
$
(
".row:visible"
).
length
;
if
(
count
>
20
)
{
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);
//var middle = parseInt(count / 2) + (count % 2);
// search for the middle of all visible elements
// search for the middle of all visible elements
...
...
cps/static/js/main.js
View file @
725fc658
...
@@ -38,10 +38,10 @@ $(document).on("change", "input[type=\"checkbox\"][data-control]", function () {
...
@@ -38,10 +38,10 @@ $(document).on("change", "input[type=\"checkbox\"][data-control]", function () {
$
(
document
).
on
(
"change"
,
"select[data-control]"
,
function
()
{
$
(
document
).
on
(
"change"
,
"select[data-control]"
,
function
()
{
var
$this
=
$
(
this
);
var
$this
=
$
(
this
);
var
name
=
$this
.
data
(
"control"
);
var
name
=
$this
.
data
(
"control"
);
var
showOrHide
=
parseInt
(
$this
.
val
());
var
showOrHide
=
parseInt
(
$this
.
val
()
,
10
);
// var showOrHideLast = $("#" + name + " option:last").val()
// var showOrHideLast = $("#" + name + " option:last").val()
for
(
var
i
=
0
;
i
<
$
(
this
)[
0
].
length
;
i
++
)
{
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
)
{
if
(
element
===
showOrHide
)
{
$
(
"[data-related^="
+
name
+
"][data-related*=-"
+
element
+
"]"
).
show
();
$
(
"[data-related^="
+
name
+
"][data-related*=-"
+
element
+
"]"
).
show
();
}
else
{
}
else
{
...
...
cps/static/js/table.js
View file @
725fc658
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
*/
*/
/* exported TableActions, RestrictionActions, EbookActions, responseHandler */
/* exported TableActions, RestrictionActions, EbookActions, responseHandler */
/* global getPath, ConfirmDialog */
var
selections
=
[];
var
selections
=
[];
...
...
cps/tasks/upload.py
View file @
725fc658
...
@@ -12,7 +12,6 @@ class TaskUpload(CalibreTask):
...
@@ -12,7 +12,6 @@ class TaskUpload(CalibreTask):
def
run
(
self
,
worker_thread
):
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"""
"""Upload task doesn't have anything to do, it's simply a way to add information to the task list"""
pass
@
property
@
property
def
name
(
self
):
def
name
(
self
):
...
...
cps/ub.py
View file @
725fc658
...
@@ -138,15 +138,15 @@ class UserBase:
...
@@ -138,15 +138,15 @@ class UserBase:
mct
=
self
.
allowed_column_value
or
""
mct
=
self
.
allowed_column_value
or
""
return
[
t
.
strip
()
for
t
in
mct
.
split
(
","
)]
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
):
if
not
self
.
view_settings
.
get
(
page
):
return
None
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
):
if
not
self
.
view_settings
.
get
(
page
):
self
.
view_settings
[
page
]
=
dict
()
self
.
view_settings
[
page
]
=
dict
()
self
.
view_settings
[
page
][
prop
erty
]
=
value
self
.
view_settings
[
page
][
prop
]
=
value
try
:
try
:
flag_modified
(
self
,
"view_settings"
)
flag_modified
(
self
,
"view_settings"
)
except
AttributeError
:
except
AttributeError
:
...
@@ -437,11 +437,8 @@ class RemoteAuthToken(Base):
...
@@ -437,11 +437,8 @@ class RemoteAuthToken(Base):
return
'<Token
%
r>'
%
self
.
id
return
'<Token
%
r>'
%
self
.
id
# Migrate database to current version, has to be updated after every database change. Currently migration from
# Add missing tables during migration of database
# everywhere to current should work. Migration is done by checking if relevant columns are existing, and than adding
def
add_missing_tables
(
engine
,
session
):
# rows with SQL commands
def
migrate_Database
(
session
):
engine
=
session
.
bind
if
not
engine
.
dialect
.
has_table
(
engine
.
connect
(),
"book_read_link"
):
if
not
engine
.
dialect
.
has_table
(
engine
.
connect
(),
"book_read_link"
):
ReadBook
.
__table__
.
create
(
bind
=
engine
)
ReadBook
.
__table__
.
create
(
bind
=
engine
)
if
not
engine
.
dialect
.
has_table
(
engine
.
connect
(),
"bookmark"
):
if
not
engine
.
dialect
.
has_table
(
engine
.
connect
(),
"bookmark"
):
...
@@ -459,6 +456,10 @@ def migrate_Database(session):
...
@@ -459,6 +456,10 @@ def migrate_Database(session):
with
engine
.
connect
()
as
conn
:
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"insert into registration (domain, allow) values('
%
.
%
',1)"
)
conn
.
execute
(
"insert into registration (domain, allow) values('
%
.
%
',1)"
)
session
.
commit
()
session
.
commit
()
# migrate all settings missing in registration table
def
migrate_registration_table
(
engine
,
session
):
try
:
try
:
session
.
query
(
exists
()
.
where
(
Registration
.
allow
))
.
scalar
()
session
.
query
(
exists
()
.
where
(
Registration
.
allow
))
.
scalar
()
session
.
commit
()
session
.
commit
()
...
@@ -468,27 +469,29 @@ def migrate_Database(session):
...
@@ -468,27 +469,29 @@ def migrate_Database(session):
conn
.
execute
(
"update registration set 'allow' = 1"
)
conn
.
execute
(
"update registration set 'allow' = 1"
)
session
.
commit
()
session
.
commit
()
try
:
try
:
session
.
query
(
exists
()
.
where
(
RemoteAuthToken
.
token_type
))
.
scalar
()
# Handle table exists, but no content
session
.
commit
()
cnt
=
session
.
query
(
Registration
)
.
count
()
except
exc
.
OperationalError
:
# Database is not compatible, some columns are missing
if
not
cnt
:
with
engine
.
connect
()
as
conn
:
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"ALTER TABLE remote_auth_token ADD column 'token_type' INTEGER DEFAULT 0"
)
conn
.
execute
(
"insert into registration (domain, allow) values('
%
.
%
',1)"
)
conn
.
execute
(
"update remote_auth_token set 'token_type' = 0"
)
session
.
commit
()
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
:
try
:
session
.
query
(
exists
()
.
where
(
ReadBook
.
read_status
))
.
scalar
()
except
exc
.
OperationalError
:
with
engine
.
connect
()
as
conn
:
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'read_status' INTEGER DEFAULT 0"
)
conn
.
execute
(
"UPDATE user SET password='' where nickname = 'Guest' and password !=''"
)
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
()
session
.
commit
()
test
=
session
.
query
(
ReadBook
)
.
filter
(
ReadBook
.
last_modified
==
None
)
.
all
()
except
exc
.
OperationalError
:
for
book
in
test
:
print
(
'Settings database is not writeable. Exiting...'
)
book
.
last_modified
=
datetime
.
datetime
.
utcnow
()
sys
.
exit
(
2
)
session
.
commit
()
def
migrate_shelfs
(
engine
,
session
):
try
:
try
:
session
.
query
(
exists
()
.
where
(
Shelf
.
uuid
))
.
scalar
()
session
.
query
(
exists
()
.
where
(
Shelf
.
uuid
))
.
scalar
()
except
exc
.
OperationalError
:
except
exc
.
OperationalError
:
...
@@ -504,22 +507,51 @@ def migrate_Database(session):
...
@@ -504,22 +507,51 @@ def migrate_Database(session):
for
book_shelf
in
session
.
query
(
BookShelf
)
.
all
():
for
book_shelf
in
session
.
query
(
BookShelf
)
.
all
():
book_shelf
.
date_added
=
datetime
.
datetime
.
now
()
book_shelf
.
date_added
=
datetime
.
datetime
.
now
()
session
.
commit
()
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
:
try
:
session
.
query
(
exists
()
.
where
(
BookShelf
.
order
))
.
scalar
()
session
.
query
(
exists
()
.
where
(
BookShelf
.
order
))
.
scalar
()
except
exc
.
OperationalError
:
# Database is not compatible, some columns are missing
except
exc
.
OperationalError
:
# Database is not compatible, some columns are missing
with
engine
.
connect
()
as
conn
:
with
engine
.
connect
()
as
conn
:
conn
.
execute
(
"ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1"
)
conn
.
execute
(
"ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1"
)
session
.
commit
()
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
:
try
:
create
=
False
create
=
False
session
.
query
(
exists
()
.
where
(
User
.
sidebar_view
))
.
scalar
()
session
.
query
(
exists
()
.
where
(
User
.
sidebar_view
))
.
scalar
()
...
@@ -578,8 +610,7 @@ def migrate_Database(session):
...
@@ -578,8 +610,7 @@ def migrate_Database(session):
"locale VARCHAR(2),"
"locale VARCHAR(2),"
"sidebar_view INTEGER,"
"sidebar_view INTEGER,"
"default_language VARCHAR(3),"
"default_language VARCHAR(3),"
# "series_view VARCHAR(10),"
"view_settings VARCHAR,"
"view_settings VARCHAR,"
"UNIQUE (nickname),"
"UNIQUE (nickname),"
"UNIQUE (email))"
)
"UNIQUE (email))"
)
conn
.
execute
(
"INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale,"
conn
.
execute
(
"INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale,"
...
@@ -590,15 +621,7 @@ def migrate_Database(session):
...
@@ -590,15 +621,7 @@ def migrate_Database(session):
conn
.
execute
(
"DROP TABLE user"
)
conn
.
execute
(
"DROP TABLE user"
)
conn
.
execute
(
"ALTER TABLE user_id RENAME TO user"
)
conn
.
execute
(
"ALTER TABLE user_id RENAME TO user"
)
session
.
commit
()
session
.
commit
()
migrate_guest_password
(
engine
,
session
)
# 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
)
def
clean_database
(
session
):
def
clean_database
(
session
):
...
...
cps/usermanagement.py
View file @
725fc658
...
@@ -72,7 +72,7 @@ def load_user_from_request(request):
...
@@ -72,7 +72,7 @@ def load_user_from_request(request):
def
load_user_from_auth_header
(
header_val
):
def
load_user_from_auth_header
(
header_val
):
if
header_val
.
startswith
(
'Basic '
):
if
header_val
.
startswith
(
'Basic '
):
header_val
=
header_val
.
replace
(
'Basic '
,
''
,
1
)
header_val
=
header_val
.
replace
(
'Basic '
,
''
,
1
)
basic_username
=
basic_password
=
''
basic_username
=
basic_password
=
''
# nosec
try
:
try
:
header_val
=
base64
.
b64decode
(
header_val
)
.
decode
(
'utf-8'
)
header_val
=
base64
.
b64decode
(
header_val
)
.
decode
(
'utf-8'
)
basic_username
=
header_val
.
split
(
':'
)[
0
]
basic_username
=
header_val
.
split
(
':'
)[
0
]
...
...
cps/web.py
View file @
725fc658
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