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
f705889c
Commit
f705889c
authored
Dec 17, 2019
by
Ozzieisaacs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Inital Kobo
parent
7098d088
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
688 additions
and
13 deletions
+688
-13
cps.py
cps.py
+3
-0
admin.py
cps/admin.py
+14
-0
config_sql.py
cps/config_sql.py
+1
-0
db.py
cps/db.py
+3
-3
helper.py
cps/helper.py
+20
-6
kobo.py
cps/kobo.py
+523
-0
kobo_auth.py
cps/kobo_auth.py
+102
-0
config_edit.html
cps/templates/config_edit.html
+4
-0
user_edit.html
cps/templates/user_edit.html
+4
-0
ub.py
cps/ub.py
+10
-3
web.py
cps/web.py
+3
-1
requirements.txt
requirements.txt
+1
-0
No files found.
cps.py
View file @
f705889c
...
@@ -41,6 +41,8 @@ from cps.shelf import shelf
...
@@ -41,6 +41,8 @@ from cps.shelf import shelf
from
cps.admin
import
admi
from
cps.admin
import
admi
from
cps.gdrive
import
gdrive
from
cps.gdrive
import
gdrive
from
cps.editbooks
import
editbook
from
cps.editbooks
import
editbook
from
cps.kobo
import
kobo
try
:
try
:
from
cps.oauth_bb
import
oauth
from
cps.oauth_bb
import
oauth
oauth_available
=
True
oauth_available
=
True
...
@@ -58,6 +60,7 @@ def main():
...
@@ -58,6 +60,7 @@ def main():
app
.
register_blueprint
(
admi
)
app
.
register_blueprint
(
admi
)
app
.
register_blueprint
(
gdrive
)
app
.
register_blueprint
(
gdrive
)
app
.
register_blueprint
(
editbook
)
app
.
register_blueprint
(
editbook
)
app
.
register_blueprint
(
kobo
)
if
oauth_available
:
if
oauth_available
:
app
.
register_blueprint
(
oauth
)
app
.
register_blueprint
(
oauth
)
success
=
web_server
.
start
()
success
=
web_server
.
start
()
...
...
cps/admin.py
View file @
f705889c
...
@@ -300,6 +300,8 @@ def _configuration_update_helper():
...
@@ -300,6 +300,8 @@ def _configuration_update_helper():
if
config
.
config_certfile
and
not
os
.
path
.
isfile
(
config
.
config_certfile
):
if
config
.
config_certfile
and
not
os
.
path
.
isfile
(
config
.
config_certfile
):
return
_configuration_result
(
'Certfile location is not valid, please enter correct path'
,
gdriveError
)
return
_configuration_result
(
'Certfile location is not valid, please enter correct path'
,
gdriveError
)
_config_string
(
"config_server_url"
)
_config_checkbox_int
(
"config_uploading"
)
_config_checkbox_int
(
"config_uploading"
)
_config_checkbox_int
(
"config_anonbrowse"
)
_config_checkbox_int
(
"config_anonbrowse"
)
_config_checkbox_int
(
"config_public_reg"
)
_config_checkbox_int
(
"config_public_reg"
)
...
@@ -597,6 +599,18 @@ def edit_user(user_id):
...
@@ -597,6 +599,18 @@ def edit_user(user_id):
content
.
default_language
=
to_save
[
"default_language"
]
content
.
default_language
=
to_save
[
"default_language"
]
if
"locale"
in
to_save
and
to_save
[
"locale"
]:
if
"locale"
in
to_save
and
to_save
[
"locale"
]:
content
.
locale
=
to_save
[
"locale"
]
content
.
locale
=
to_save
[
"locale"
]
if
"kobo_user_key"
in
to_save
and
to_save
[
"kobo_user_key"
]:
kobo_user_key_hash
=
generate_password_hash
(
to_save
[
"kobo_user_key"
])
if
kobo_user_key_hash
!=
content
.
kobo_user_key_hash
:
existing_kobo_user_key
=
ub
.
session
.
query
(
ub
.
User
)
.
filter
(
ub
.
User
.
kobo_user_key_hash
==
kobo_user_key_hash
)
.
first
()
if
not
existing_kobo_user_key
:
content
.
kobo_user_key_hash
=
kobo_user_key_hash
else
:
flash
(
_
(
u"Found an existing account for this Kobo UserKey."
),
category
=
"error"
)
return
render_title_template
(
"user_edit.html"
,
translations
=
translations
,
languages
=
languages
,
new_user
=
0
,
content
=
content
,
downloads
=
downloads
,
registered_oauth
=
oauth_check
,
title
=
_
(
u"Edit User
%(nick)
s"
,
nick
=
content
.
nickname
),
page
=
"edituser"
)
if
to_save
[
"email"
]
and
to_save
[
"email"
]
!=
content
.
email
:
if
to_save
[
"email"
]
and
to_save
[
"email"
]
!=
content
.
email
:
existing_email
=
ub
.
session
.
query
(
ub
.
User
)
.
filter
(
ub
.
User
.
email
==
to_save
[
"email"
]
.
lower
())
\
existing_email
=
ub
.
session
.
query
(
ub
.
User
)
.
filter
(
ub
.
User
.
email
==
to_save
[
"email"
]
.
lower
())
\
.
first
()
.
first
()
...
...
cps/config_sql.py
View file @
f705889c
...
@@ -49,6 +49,7 @@ class _Settings(_Base):
...
@@ -49,6 +49,7 @@ class _Settings(_Base):
config_port
=
Column
(
Integer
,
default
=
constants
.
DEFAULT_PORT
)
config_port
=
Column
(
Integer
,
default
=
constants
.
DEFAULT_PORT
)
config_certfile
=
Column
(
String
)
config_certfile
=
Column
(
String
)
config_keyfile
=
Column
(
String
)
config_keyfile
=
Column
(
String
)
config_server_url
=
Column
(
String
,
default
=
''
)
config_calibre_web_title
=
Column
(
String
,
default
=
u'Calibre-Web'
)
config_calibre_web_title
=
Column
(
String
,
default
=
u'Calibre-Web'
)
config_books_per_page
=
Column
(
Integer
,
default
=
60
)
config_books_per_page
=
Column
(
Integer
,
default
=
60
)
...
...
cps/db.py
View file @
f705889c
...
@@ -25,7 +25,7 @@ import ast
...
@@ -25,7 +25,7 @@ import ast
from
sqlalchemy
import
create_engine
from
sqlalchemy
import
create_engine
from
sqlalchemy
import
Table
,
Column
,
ForeignKey
from
sqlalchemy
import
Table
,
Column
,
ForeignKey
from
sqlalchemy
import
String
,
Integer
,
Boolean
from
sqlalchemy
import
String
,
Integer
,
Boolean
,
TIMESTAMP
from
sqlalchemy.orm
import
relationship
,
sessionmaker
,
scoped_session
from
sqlalchemy.orm
import
relationship
,
sessionmaker
,
scoped_session
from
sqlalchemy.ext.declarative
import
declarative_base
from
sqlalchemy.ext.declarative
import
declarative_base
...
@@ -251,10 +251,10 @@ class Books(Base):
...
@@ -251,10 +251,10 @@ class Books(Base):
title
=
Column
(
String
)
title
=
Column
(
String
)
sort
=
Column
(
String
)
sort
=
Column
(
String
)
author_sort
=
Column
(
String
)
author_sort
=
Column
(
String
)
timestamp
=
Column
(
String
)
timestamp
=
Column
(
TIMESTAMP
)
pubdate
=
Column
(
String
)
pubdate
=
Column
(
String
)
series_index
=
Column
(
String
)
series_index
=
Column
(
String
)
last_modified
=
Column
(
String
)
last_modified
=
Column
(
TIMESTAMP
)
path
=
Column
(
String
)
path
=
Column
(
String
)
has_cover
=
Column
(
Integer
)
has_cover
=
Column
(
Integer
)
uuid
=
Column
(
String
)
uuid
=
Column
(
String
)
...
...
cps/helper.py
View file @
f705889c
...
@@ -446,32 +446,46 @@ def delete_book(book, calibrepath, book_format):
...
@@ -446,32 +446,46 @@ def delete_book(book, calibrepath, book_format):
return
delete_book_file
(
book
,
calibrepath
,
book_format
)
return
delete_book_file
(
book
,
calibrepath
,
book_format
)
def
get_cover_on_failure
(
use_generic_cover
):
if
use_generic_cover
:
return
send_from_directory
(
_STATIC_DIR
,
"generic_cover.jpg"
)
else
:
return
None
def
get_book_cover
(
book_id
):
def
get_book_cover
(
book_id
):
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
first
()
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
first
()
if
book
.
has_cover
:
return
get_book_cover_internal
(
book
,
use_generic_cover_on_failure
=
True
)
def
get_book_cover_with_uuid
(
book_uuid
,
use_generic_cover_on_failure
=
True
):
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
uuid
==
book_uuid
)
.
first
()
return
get_book_cover_internal
(
book
,
use_generic_cover_on_failure
)
def
get_book_cover_internal
(
book
,
use_generic_cover_on_failure
):
if
book
and
book
.
has_cover
:
if
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
try
:
try
:
if
not
gd
.
is_gdrive_ready
():
if
not
gd
.
is_gdrive_ready
():
return
send_from_directory
(
_STATIC_DIR
,
"generic_cover.jpg"
)
return
get_cover_on_failure
(
use_generic_cover_on_failure
)
path
=
gd
.
get_cover_via_gdrive
(
book
.
path
)
path
=
gd
.
get_cover_via_gdrive
(
book
.
path
)
if
path
:
if
path
:
return
redirect
(
path
)
return
redirect
(
path
)
else
:
else
:
log
.
error
(
'
%
s/cover.jpg not found on Google Drive'
,
book
.
path
)
log
.
error
(
'
%
s/cover.jpg not found on Google Drive'
,
book
.
path
)
return
send_from_directory
(
_STATIC_DIR
,
"generic_cover.jpg"
)
return
get_cover_on_failure
(
use_generic_cover_on_failure
)
except
Exception
as
e
:
except
Exception
as
e
:
log
.
exception
(
e
)
log
.
exception
(
e
)
# traceback.print_exc()
# traceback.print_exc()
return
send_from_directory
(
_STATIC_DIR
,
"generic_cover.jpg"
)
return
get_cover_on_failure
(
use_generic_cover_on_failure
)
else
:
else
:
cover_file_path
=
os
.
path
.
join
(
config
.
config_calibre_dir
,
book
.
path
)
cover_file_path
=
os
.
path
.
join
(
config
.
config_calibre_dir
,
book
.
path
)
if
os
.
path
.
isfile
(
os
.
path
.
join
(
cover_file_path
,
"cover.jpg"
)):
if
os
.
path
.
isfile
(
os
.
path
.
join
(
cover_file_path
,
"cover.jpg"
)):
return
send_from_directory
(
cover_file_path
,
"cover.jpg"
)
return
send_from_directory
(
cover_file_path
,
"cover.jpg"
)
else
:
else
:
return
send_from_directory
(
_STATIC_DIR
,
"generic_cover.jpg"
)
return
get_cover_on_failure
(
use_generic_cover_on_failure
)
else
:
else
:
return
send_from_directory
(
_STATIC_DIR
,
"generic_cover.jpg"
)
return
get_cover_on_failure
(
use_generic_cover_on_failure
)
# saves book cover from url
# saves book cover from url
...
...
cps/kobo.py
0 → 100644
View file @
f705889c
This diff is collapsed.
Click to expand it.
cps/kobo_auth.py
0 → 100644
View file @
f705889c
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
# Copyright (C) 2018-2019 shavitmichael, OzzieIsaacs
#
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module is used to control authentication/authorization of Kobo sync requests.
This module also includes research notes into the auth protocol used by Kobo devices.
Log-in:
When first booting a Kobo device the user must sign into a Kobo (or affiliate) account.
Upon successful sign-in, the user is redirected to
https://auth.kobobooks.com/CrossDomainSignIn?id=<some id>
which serves the following response:
<script type='text/javascript'>location.href='kobo://UserAuthenticated?userId=<redacted>&userKey<redacted>&email=<redacted>&returnUrl=https
%3
a
%2
f
%2
fwww.kobo.com';</script>.
And triggers the insertion of a userKey into the device's User table.
IMPORTANT SECURITY CAUTION:
Together, the device's DeviceId and UserKey act as an *irrevocable* authentication
token to most (if not all) Kobo APIs. In fact, in most cases only the UserKey is
required to authorize the API call.
Changing Kobo password *does not* invalidate user keys! This is apparently a known
issue for a few years now https://www.mobileread.com/forums/showpost.php?p=3476851&postcount=13
(although this poster hypothesised that Kobo could blacklist a DeviceId, many endpoints
will still grant access given the userkey.)
Official Kobo Store Api authorization:
* For most of the endpoints we care about (sync, metadata, tags, etc), the userKey is
passed in the x-kobo-userkey header, and is sufficient to authorize the API call.
* Some endpoints (e.g: AnnotationService) instead make use of Bearer tokens pass through
an authorization header. To get a BearerToken, the device makes a POST request to the
v1/auth/device endpoint with the secret UserKey and the device's DeviceId.
* The book download endpoint passes an auth token as a URL param instead of a header.
Our implementation:
For now, we rely on the official Kobo store's UserKey for authentication.
Once authenticated, we set the login cookie on the response that will be sent back for
the duration of the session to authorize subsequent API calls.
Ideally we'd only perform UserKey-based authentication for the v1/initialization or the
v1/device/auth call, however sessions don't always start with those calls.
Because of the irrevocable power granted by the key, we only ever store and compare a
hash of the key. To obtain their UserKey, a user can query the user table from the
.kobo/KoboReader.sqlite database found on their device.
This isn't exactly user friendly however.
Some possible alternatives that require more research:
* Instead of having users query the device database to find out their UserKey, we could
provide a list of recent Kobo sync attempts in the calibre-web UI for users to
authenticate sync attempts (e.g: 'this was me' button).
* We may be able to craft a sign-in flow with a redirect back to the CalibreWeb
server containing the KoboStore's UserKey (if the same as the devices?).
* Can we create our own UserKey instead of relying on the real store's userkey?
(Maybe using something like location.href=kobo://UserAuthenticated?userId=...?)
"""
from
functools
import
wraps
from
flask
import
request
,
make_response
from
flask_login
import
login_user
from
werkzeug.security
import
check_password_hash
from
.
import
logger
,
ub
,
lm
USER_KEY_HEADER
=
"x-kobo-userkey"
USER_KEY_URL_PARAM
=
"kobo_userkey"
log
=
logger
.
create
()
def
disable_failed_auth_redirect_for_blueprint
(
bp
):
lm
.
blueprint_login_views
[
bp
.
name
]
=
None
@
lm
.
request_loader
def
load_user_from_kobo_request
(
request
):
user_key
=
request
.
headers
.
get
(
USER_KEY_HEADER
)
if
user_key
:
for
user
in
(
ub
.
session
.
query
(
ub
.
User
)
.
filter
(
ub
.
User
.
kobo_user_key_hash
!=
""
)
.
all
()
):
if
check_password_hash
(
str
(
user
.
kobo_user_key_hash
),
user_key
):
# The Kobo device won't preserve the cookie accross sessions, even if we
# were to set remember_me=true.
login_user
(
user
)
return
user
log
.
info
(
"Received Kobo request without a recognizable UserKey."
)
return
None
cps/templates/config_edit.html
View file @
f705889c
...
@@ -104,6 +104,10 @@
...
@@ -104,6 +104,10 @@
<!--option-- value="3" {% if config.config_updatechannel == 3 %}selected{% endif %}>{{_('Nightly (Automatic)')}}</option-->
<!--option-- value="3" {% if config.config_updatechannel == 3 %}selected{% endif %}>{{_('Nightly (Automatic)')}}</option-->
</select>
</select>
</div>
</div>
<div
class=
"form-group"
>
<label
for=
"config_server_url"
>
{{_('Server Url. This is only used for the (experimental) Kobo device library sync')}}
</label>
<input
type=
"text"
class=
"form-control"
name=
"config_server_url"
id=
"config_server_url"
value=
"{% if config.config_server_url != None %}{{ config.config_server_url }}{% endif %}"
autocomplete=
"off"
>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
...
...
cps/templates/user_edit.html
View file @
f705889c
...
@@ -28,6 +28,10 @@
...
@@ -28,6 +28,10 @@
<input
type=
"email"
class=
"form-control"
name=
"kindle_mail"
id=
"kindle_mail"
value=
"{{ content.kindle_mail if content.kindle_mail != None }}"
>
<input
type=
"email"
class=
"form-control"
name=
"kindle_mail"
id=
"kindle_mail"
value=
"{{ content.kindle_mail if content.kindle_mail != None }}"
>
</div>
</div>
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<label
for=
"kobo_user_key"
>
{{_('KoboStore UserKey')}}
</label>
<input
type=
"password"
class=
"form-control"
name=
"kobo_user_key"
id=
"kobo_user_key"
value=
""
autocomplete=
"off"
>
</div>
<label
for=
"locale"
>
{{_('Language')}}
</label>
<label
for=
"locale"
>
{{_('Language')}}
</label>
<select
name=
"locale"
id=
"locale"
class=
"form-control"
>
<select
name=
"locale"
id=
"locale"
class=
"form-control"
>
{% for translation in translations %}
{% for translation in translations %}
...
...
cps/ub.py
View file @
f705889c
...
@@ -173,6 +173,7 @@ class User(UserBase, Base):
...
@@ -173,6 +173,7 @@ class User(UserBase, Base):
role
=
Column
(
SmallInteger
,
default
=
constants
.
ROLE_USER
)
role
=
Column
(
SmallInteger
,
default
=
constants
.
ROLE_USER
)
password
=
Column
(
String
)
password
=
Column
(
String
)
kindle_mail
=
Column
(
String
(
120
),
default
=
""
)
kindle_mail
=
Column
(
String
(
120
),
default
=
""
)
kobo_user_key_hash
=
Column
(
String
,
unique
=
True
,
default
=
""
)
shelf
=
relationship
(
'Shelf'
,
backref
=
'user'
,
lazy
=
'dynamic'
,
order_by
=
'Shelf.name'
)
shelf
=
relationship
(
'Shelf'
,
backref
=
'user'
,
lazy
=
'dynamic'
,
order_by
=
'Shelf.name'
)
downloads
=
relationship
(
'Downloads'
,
backref
=
'user'
,
lazy
=
'dynamic'
)
downloads
=
relationship
(
'Downloads'
,
backref
=
'user'
,
lazy
=
'dynamic'
)
locale
=
Column
(
String
(
2
),
default
=
"en"
)
locale
=
Column
(
String
(
2
),
default
=
"en"
)
...
@@ -375,7 +376,12 @@ def migrate_Database(session):
...
@@ -375,7 +376,12 @@ def migrate_Database(session):
except
exc
.
OperationalError
:
except
exc
.
OperationalError
:
conn
=
engine
.
connect
()
conn
=
engine
.
connect
()
conn
.
execute
(
"ALTER TABLE user ADD column `mature_content` INTEGER DEFAULT 1"
)
conn
.
execute
(
"ALTER TABLE user ADD column `mature_content` INTEGER DEFAULT 1"
)
try
:
session
.
query
(
exists
()
.
where
(
User
.
kobo_user_key_hash
))
.
scalar
()
except
exc
.
OperationalError
:
# Database is not compatible, some rows are missing
conn
=
engine
.
connect
()
conn
.
execute
(
"ALTER TABLE user ADD column `kobo_user_key_hash` VARCHAR"
)
session
.
commit
()
if
session
.
query
(
User
)
.
filter
(
User
.
role
.
op
(
'&'
)(
constants
.
ROLE_ANONYMOUS
)
==
constants
.
ROLE_ANONYMOUS
)
.
first
()
is
None
:
if
session
.
query
(
User
)
.
filter
(
User
.
role
.
op
(
'&'
)(
constants
.
ROLE_ANONYMOUS
)
==
constants
.
ROLE_ANONYMOUS
)
.
first
()
is
None
:
create_anonymous_user
(
session
)
create_anonymous_user
(
session
)
try
:
try
:
...
@@ -391,6 +397,7 @@ def migrate_Database(session):
...
@@ -391,6 +397,7 @@ def migrate_Database(session):
"role SMALLINT,"
"role SMALLINT,"
"password VARCHAR,"
"password VARCHAR,"
"kindle_mail VARCHAR(120),"
"kindle_mail VARCHAR(120),"
"kobo_user_key_hash VARCHAR,"
"locale VARCHAR(2),"
"locale VARCHAR(2),"
"sidebar_view INTEGER,"
"sidebar_view INTEGER,"
"default_language VARCHAR(3),"
"default_language VARCHAR(3),"
...
@@ -398,9 +405,9 @@ def migrate_Database(session):
...
@@ -398,9 +405,9 @@ def migrate_Database(session):
"UNIQUE (nickname),"
"UNIQUE (nickname),"
"UNIQUE (email),"
"UNIQUE (email),"
"CHECK (mature_content IN (0, 1)))"
)
"CHECK (mature_content IN (0, 1)))"
)
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,
kobo_user_key_hash,
locale,"
"sidebar_view, default_language, mature_content) "
"sidebar_view, default_language, mature_content) "
"SELECT id, nickname, email, role, password, kindle_mail, locale,"
"SELECT id, nickname, email, role, password, kindle_mail,
kobo_user_key_hash,
locale,"
"sidebar_view, default_language, mature_content FROM user"
)
"sidebar_view, default_language, mature_content FROM user"
)
# delete old user table and rename new user_id table to user:
# delete old user table and rename new user_id table to user:
conn
.
execute
(
"DROP TABLE user"
)
conn
.
execute
(
"DROP TABLE user"
)
...
...
cps/web.py
View file @
f705889c
...
@@ -1288,6 +1288,8 @@ def profile():
...
@@ -1288,6 +1288,8 @@ def profile():
current_user
.
password
=
generate_password_hash
(
to_save
[
"password"
])
current_user
.
password
=
generate_password_hash
(
to_save
[
"password"
])
if
"kindle_mail"
in
to_save
and
to_save
[
"kindle_mail"
]
!=
current_user
.
kindle_mail
:
if
"kindle_mail"
in
to_save
and
to_save
[
"kindle_mail"
]
!=
current_user
.
kindle_mail
:
current_user
.
kindle_mail
=
to_save
[
"kindle_mail"
]
current_user
.
kindle_mail
=
to_save
[
"kindle_mail"
]
if
"kobo_user_key"
in
to_save
and
to_save
[
"kobo_user_key"
]:
current_user
.
kobo_user_key_hash
=
generate_password_hash
(
to_save
[
"kobo_user_key"
])
if
to_save
[
"email"
]
and
to_save
[
"email"
]
!=
current_user
.
email
:
if
to_save
[
"email"
]
and
to_save
[
"email"
]
!=
current_user
.
email
:
if
config
.
config_public_reg
and
not
check_valid_domain
(
to_save
[
"email"
]):
if
config
.
config_public_reg
and
not
check_valid_domain
(
to_save
[
"email"
]):
flash
(
_
(
u"E-mail is not from valid domain"
),
category
=
"error"
)
flash
(
_
(
u"E-mail is not from valid domain"
),
category
=
"error"
)
...
@@ -1331,7 +1333,7 @@ def profile():
...
@@ -1331,7 +1333,7 @@ def profile():
ub
.
session
.
commit
()
ub
.
session
.
commit
()
except
IntegrityError
:
except
IntegrityError
:
ub
.
session
.
rollback
()
ub
.
session
.
rollback
()
flash
(
_
(
u"Found an existing account for this e-mail address."
),
category
=
"error"
)
flash
(
_
(
u"Found an existing account for this e-mail address
or Kobo UserKey
."
),
category
=
"error"
)
return
render_title_template
(
"user_edit.html"
,
content
=
current_user
,
downloads
=
downloads
,
return
render_title_template
(
"user_edit.html"
,
content
=
current_user
,
downloads
=
downloads
,
translations
=
translations
,
translations
=
translations
,
title
=
_
(
u"
%(name)
s's profile"
,
name
=
current_user
.
nickname
),
page
=
"me"
,
title
=
_
(
u"
%(name)
s's profile"
,
name
=
current_user
.
nickname
),
page
=
"me"
,
...
...
requirements.txt
View file @
f705889c
...
@@ -13,3 +13,4 @@ SQLAlchemy>=1.1.0
...
@@ -13,3 +13,4 @@ SQLAlchemy>=1.1.0
tornado
>=4.1
tornado
>=4.1
Wand
>=0.4.4
Wand
>=0.4.4
unidecode
>=0.04.19
unidecode
>=0.04.19
jsonschema
>=3.2.0
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