@ -22,17 +22,21 @@
import os
import os
import re
import re
import base64
import json
import json
import operator
import operator
from datetime import datetime , timedelta , time
import time
import sys
import string
from datetime import datetime , timedelta
from datetime import time as datetime_time
from functools import wraps
from functools import wraps
from urllib . parse import urlparse
from flask import Blueprint , flash , redirect , url_for , abort , request , make_response , send_from_directory , g , Response
from flask import Blueprint , flash , redirect , url_for , abort , request , make_response , send_from_directory , g , Response
from flask_login import login_required , current_user , logout_user , confirm_login
from markupsafe import Markup
from flask_login import login_required , current_user , logout_user
from flask_babel import gettext as _
from flask_babel import gettext as _
from flask_babel import get_locale , format_time , format_datetime , format_timedelta
from flask_babel import get_locale , format_time , format_datetime , format_timedelta
from flask import session as flask_session
from flask import session as flask_session
from sqlalchemy import and_
from sqlalchemy import and_
from sqlalchemy . orm . attributes import flag_modified
from sqlalchemy . orm . attributes import flag_modified
@ -50,27 +54,28 @@ from .services.worker import WorkerThread
from . babel import get_available_translations , get_available_locale , get_user_locale_language
from . babel import get_available_translations , get_available_locale , get_user_locale_language
from . import debug_info
from . import debug_info
log = logger . create ( )
log = logger . create ( )
feature_support = {
feature_support = {
' ldap ' : bool ( services . ldap ) ,
' ldap ' : bool ( services . ldap ) ,
' goodreads ' : bool ( services . goodreads_support ) ,
' goodreads ' : bool ( services . goodreads_support ) ,
' kobo ' : bool ( services . kobo ) ,
' kobo ' : bool ( services . kobo ) ,
' updater ' : constants . UPDATER_AVAILABLE ,
' updater ' : constants . UPDATER_AVAILABLE ,
' gmail ' : bool ( services . gmail ) ,
' gmail ' : bool ( services . gmail ) ,
' scheduler ' : schedule . use_APScheduler ,
' scheduler ' : schedule . use_APScheduler ,
' gdrive ' : gdrive_support
' gdrive ' : gdrive_support
}
}
try :
try :
import rarfile # pylint: disable=unused-import
import rarfile # pylint: disable=unused-import
feature_support [ ' rar ' ] = True
feature_support [ ' rar ' ] = True
except ( ImportError , SyntaxError ) :
except ( ImportError , SyntaxError ) :
feature_support [ ' rar ' ] = False
feature_support [ ' rar ' ] = False
try :
try :
from . oauth_bb import oauth_check , oauthblueprints
from . oauth_bb import oauth_check , oauthblueprints
feature_support [ ' oauth ' ] = True
feature_support [ ' oauth ' ] = True
except ImportError as err :
except ImportError as err :
log . debug ( ' Cannot import Flask-Dance, login with Oauth will not work: %s ' , err )
log . debug ( ' Cannot import Flask-Dance, login with Oauth will not work: %s ' , err )
@ -78,7 +83,6 @@ except ImportError as err:
oauthblueprints = [ ]
oauthblueprints = [ ]
oauth_check = { }
oauth_check = { }
admi = Blueprint ( ' admin ' , __name__ )
admi = Blueprint ( ' admin ' , __name__ )
@ -98,25 +102,26 @@ def admin_required(f):
@admi.before_app_request
@admi.before_app_request
def before_request ( ) :
def before_request ( ) :
# make remember me function work
try :
if current_user . is_authenticated :
if not ub . check_user_session ( current_user . id ,
confirm_login ( )
flask_session . get ( ' _id ' ) ) and ' opds ' not in request . path \
if not ub . check_user_session ( current_user . id , flask_session . get ( ' _id ' ) ) and ' opds ' not in request . path :
and config . config_session == 1 :
logout_user ( )
logout_user ( )
except AttributeError :
pass # ? fails on requesting /ajax/emailstat during restart ?
g . constants = constants
g . constants = constants
g . user = current_user
g . google_site_verification = os . getenv ( ' GOOGLE_SITE_VERIFICATION ' , ' ' )
g . allow_registration = config . config_public_reg
g . allow_registration = config . config_public_reg
g . allow_anonymous = config . config_anonbrowse
g . allow_anonymous = config . config_anonbrowse
g . allow_upload = config . config_uploading
g . allow_upload = config . config_uploading
g . current_theme = config . config_theme
g . current_theme = config . config_theme
g . config_authors_max = config . config_authors_max
g . config_authors_max = config . config_authors_max
g . shelves_access = ub . session . query ( ub . Shelf ) . filter (
or_ ( ub . Shelf . is_public == 1 , ub . Shelf . user_id == current_user . id ) ) . order_by ( ub . Shelf . name ) . all ( )
if ' /static/ ' not in request . path and not config . db_configured and \
if ' /static/ ' not in request . path and not config . db_configured and \
request . endpoint not in ( ' admin.ajax_db_config ' ,
request . endpoint not in ( ' admin.ajax_db_config ' ,
' admin.simulatedbchange ' ,
' admin.simulatedbchange ' ,
' admin.db_configuration ' ,
' admin.db_configuration ' ,
' web.login ' ,
' web.login ' ,
' web.login_post ' ,
' web.logout ' ,
' web.logout ' ,
' admin.load_dialogtexts ' ,
' admin.load_dialogtexts ' ,
' admin.ajax_pathchooser ' ) :
' admin.ajax_pathchooser ' ) :
@ -134,31 +139,42 @@ def admin_forbidden():
@admin_required
@admin_required
def shutdown ( ) :
def shutdown ( ) :
task = request . get_json ( ) . get ( ' parameter ' , - 1 )
task = request . get_json ( ) . get ( ' parameter ' , - 1 )
show text = { }
show _ text = { }
if task in ( 0 , 1 ) : # valid commandos received
if task in ( 0 , 1 ) : # valid commandos received
# close all database connections
# close all database connections
calibre_db . dispose ( )
calibre_db . dispose ( )
ub . dispose ( )
ub . dispose ( )
if task == 0 :
if task == 0 :
show text[ ' text ' ] = _ ( u ' Server restarted, please reload page ' )
show _ text[ ' text ' ] = _ ( ' Server restarted, please reload page . ' )
else :
else :
show text[ ' text ' ] = _ ( u ' Performing shutdown of server , please close window' )
show _ text[ ' text ' ] = _ ( ' Performing Server shutdown, please close window. ' )
# stop gevent/tornado server
# stop gevent/tornado server
web_server . stop ( task == 0 )
web_server . stop ( task == 0 )
return json . dumps ( show text)
return json . dumps ( show _ text)
if task == 2 :
if task == 2 :
log . warning ( " reconnecting to calibre database " )
log . warning ( " reconnecting to calibre database " )
calibre_db . reconnect_db ( config , ub . app_DB_path )
calibre_db . reconnect_db ( config , ub . app_DB_path )
showtext [ ' text ' ] = _ ( u ' Reconnect successful ' )
show_text [ ' text ' ] = _ ( ' Success! Database Reconnected ' )
return json . dumps ( showtext )
return json . dumps ( show_text )
show_text [ ' text ' ] = _ ( ' Unknown command ' )
return json . dumps ( show_text ) , 400
showtext [ ' text ' ] = _ ( u ' Unknown command ' )
return json . dumps ( showtext ) , 400
@admi.route ( " /metadata_backup " , methods = [ " POST " ] )
@login_required
@admin_required
def queue_metadata_backup ( ) :
show_text = { }
log . warning ( " Queuing all books for metadata backup " )
helper . set_all_metadata_dirty ( )
show_text [ ' text ' ] = _ ( ' Success! Books queued for Metadata Backup, please check Tasks for result ' )
return json . dumps ( show_text )
# method is available without login and not protected by CSRF to make it easy reachable, is per default switched of
# method is available without login and not protected by CSRF to make it easy reachable, is per default switched of f
# needed for docker applications, as changes on metadata.db from host are not visible to application
# needed for docker applications, as changes on metadata.db from host are not visible to application
@admi.route ( " /reconnect " , methods = [ ' GET ' ] )
@admi.route ( " /reconnect " , methods = [ ' GET ' ] )
def reconnect ( ) :
def reconnect ( ) :
@ -187,32 +203,32 @@ def update_thumbnails():
def admin ( ) :
def admin ( ) :
version = updater_thread . get_current_version_info ( )
version = updater_thread . get_current_version_info ( )
if version is False :
if version is False :
commit = _ ( u ' Unknown ' )
commit = _ ( ' Unknown ' )
else :
else :
if ' datetime ' in version :
if ' datetime ' in version :
commit = version [ ' datetime ' ]
commit = version [ ' datetime ' ]
tz = timedelta ( seconds = time . timezone if ( time . localtime ( ) . tm_isdst == 0 ) else time . altzone )
tz = timedelta ( seconds = time . timezone if ( time . localtime ( ) . tm_isdst == 0 ) else time . altzone )
form_date = datetime . strptime ( commit [ : 19 ] , " % Y- % m- %d T % H: % M: % S " )
form_date = datetime . strptime ( commit [ : 19 ] , " % Y- % m- %d T % H: % M: % S " )
if len ( commit ) > 19 : # check if string has timezone
if len ( commit ) > 19 : # check if string has timezone
if commit [ 19 ] == ' + ' :
if commit [ 19 ] == ' + ' :
form_date - = timedelta ( hours = int ( commit [ 20 : 22 ] ) , minutes = int ( commit [ 23 : ] ) )
form_date - = timedelta ( hours = int ( commit [ 20 : 22 ] ) , minutes = int ( commit [ 23 : ] ) )
elif commit [ 19 ] == ' - ' :
elif commit [ 19 ] == ' - ' :
form_date + = timedelta ( hours = int ( commit [ 20 : 22 ] ) , minutes = int ( commit [ 23 : ] ) )
form_date + = timedelta ( hours = int ( commit [ 20 : 22 ] ) , minutes = int ( commit [ 23 : ] ) )
commit = format_datetime ( form_date - tz , format = ' short ' )
commit = format_datetime ( form_date - tz , format = ' short ' )
else :
else :
commit = version [ ' version ' ]
commit = version [ ' version ' ] . replace ( " b " , " Beta " )
all_user = ub . session . query ( ub . User ) . all ( )
all_user = ub . session . query ( ub . User ) . all ( )
email_settings = config . get_mail_settings ( )
# email_settings = mail_config.get_mail_settings( )
schedule_time = format_time ( time( hour = config . schedule_start_time ) , format = " short " )
schedule_time = format_time ( datetime_ time( hour = config . schedule_start_time ) , format = " short " )
t = timedelta ( hours = config . schedule_duration / / 60 , minutes = config . schedule_duration % 60 )
t = timedelta ( hours = config . schedule_duration / / 60 , minutes = config . schedule_duration % 60 )
schedule_duration = format_timedelta ( t , threshold = .99 )
schedule_duration = format_timedelta ( t , threshold = .99 )
return render_title_template ( " admin.html " , allUser = all_user , email= email_settings , config= config , commit = commit ,
return render_title_template ( " admin.html " , allUser = all_user , config= config , commit = commit ,
feature_support = feature_support , schedule_time = schedule_time ,
feature_support = feature_support , schedule_time = schedule_time ,
schedule_duration = schedule_duration ,
schedule_duration = schedule_duration ,
title = _ ( u " Admin page " ) , page = " admin " )
title = _ ( " Admin page " ) , page = " admin " )
@admi.route ( " /admin/dbconfig " , methods = [ " GET " , " POST " ] )
@admi.route ( " /admin/dbconfig " , methods = [ " GET " , " POST " ] )
@ -232,7 +248,7 @@ def configuration():
config = config ,
config = config ,
provider = oauthblueprints ,
provider = oauthblueprints ,
feature_support = feature_support ,
feature_support = feature_support ,
title = _ ( u " Basic Configuration " ) , page = " config " )
title = _ ( " Basic Configuration " ) , page = " config " )
@admi.route ( " /admin/ajaxconfig " , methods = [ " POST " ] )
@admi.route ( " /admin/ajaxconfig " , methods = [ " POST " ] )
@ -260,9 +276,9 @@ def calibreweb_alive():
@login_required
@login_required
@admin_required
@admin_required
def view_configuration ( ) :
def view_configuration ( ) :
read_column = calibre_db . session . query ( db . CustomColumns ) \
read_column = calibre_db . session . query ( db . CustomColumns ) \
. filter ( and_ ( db . CustomColumns . datatype == ' bool ' , db . CustomColumns . mark_for_delete == 0 ) ) . all ( )
. filter ( and_ ( db . CustomColumns . datatype == ' bool ' , db . CustomColumns . mark_for_delete == 0 ) ) . all ( )
restrict_columns = calibre_db . session . query ( db . CustomColumns ) \
restrict_columns = calibre_db . session . query ( db . CustomColumns ) \
. filter ( and_ ( db . CustomColumns . datatype == ' text ' , db . CustomColumns . mark_for_delete == 0 ) ) . all ( )
. filter ( and_ ( db . CustomColumns . datatype == ' text ' , db . CustomColumns . mark_for_delete == 0 ) ) . all ( )
languages = calibre_db . speaking_language ( )
languages = calibre_db . speaking_language ( )
translations = get_available_locale ( )
translations = get_available_locale ( )
@ -270,7 +286,7 @@ def view_configuration():
restrictColumns = restrict_columns ,
restrictColumns = restrict_columns ,
languages = languages ,
languages = languages ,
translations = translations ,
translations = translations ,
title = _ ( u " UI Configuration " ) , page = " uiconfig " )
title = _ ( " UI Configuration " ) , page = " uiconfig " )
@admi.route ( " /admin/usertable " )
@admi.route ( " /admin/usertable " )
@ -281,11 +297,11 @@ def edit_user_table():
languages = calibre_db . speaking_language ( )
languages = calibre_db . speaking_language ( )
translations = get_available_locale ( )
translations = get_available_locale ( )
all_user = ub . session . query ( ub . User )
all_user = ub . session . query ( ub . User )
tags = calibre_db . session . query ( db . Tags ) \
tags = calibre_db . session . query ( db . Tags ) \
. join ( db . books_tags_link ) \
. join ( db . books_tags_link ) \
. join ( db . Books ) \
. join ( db . Books ) \
. filter ( calibre_db . common_filters ( ) ) \
. filter ( calibre_db . common_filters ( ) ) \
. group_by ( text ( ' books_tags_link.tag ' ) ) \
. group_by ( text ( ' books_tags_link.tag ' ) ) \
. order_by ( db . Tags . name ) . all ( )
. order_by ( db . Tags . name ) . all ( )
if config . config_restricted_column :
if config . config_restricted_column :
custom_values = calibre_db . session . query ( db . cc_classes [ config . config_restricted_column ] ) . all ( )
custom_values = calibre_db . session . query ( db . cc_classes [ config . config_restricted_column ] ) . all ( )
@ -304,7 +320,7 @@ def edit_user_table():
all_roles = constants . ALL_ROLES ,
all_roles = constants . ALL_ROLES ,
kobo_support = kobo_support ,
kobo_support = kobo_support ,
sidebar_settings = constants . sidebar_settings ,
sidebar_settings = constants . sidebar_settings ,
title = _ ( u " Edit Users " ) ,
title = _ ( " Edit Users " ) ,
page = " usertable " )
page = " usertable " )
@ -462,20 +478,20 @@ def edit_list_user(param):
elif param . endswith ( ' role ' ) :
elif param . endswith ( ' role ' ) :
value = int ( vals [ ' field_index ' ] )
value = int ( vals [ ' field_index ' ] )
if user . name == " Guest " and value in \
if user . name == " Guest " and value in \
[ constants . ROLE_ADMIN , constants . ROLE_PASSWD , constants . ROLE_EDIT_SHELFS ] :
[ constants . ROLE_ADMIN , constants . ROLE_PASSWD , constants . ROLE_EDIT_SHELFS ] :
raise Exception ( _ ( " Guest can ' t have this role " ) )
raise Exception ( _ ( " Guest can ' t have this role " ) )
# check for valid value, last on checks for power of 2 value
# check for valid value, last on checks for power of 2 value
if value > 0 and value < = constants . ROLE_VIEWER and ( value & value - 1 == 0 or value == 1 ) :
if value > 0 and value < = constants . ROLE_VIEWER and ( value & value - 1 == 0 or value == 1 ) :
if vals [ ' value ' ] == ' true ' :
if vals [ ' value ' ] == ' true ' :
user . role | = value
user . role | = value
elif vals [ ' value ' ] == ' false ' :
elif vals [ ' value ' ] == ' false ' :
if value == constants . ROLE_ADMIN :
if value == constants . ROLE_ADMIN :
if not ub . session . query ( ub . User ) . \
if not ub . session . query ( ub . User ) . \
filter ( ub . User . role . op ( ' & ' ) ( constants . ROLE_ADMIN ) == constants . ROLE_ADMIN ,
filter ( ub . User . role . op ( ' & ' ) ( constants . ROLE_ADMIN ) == constants . ROLE_ADMIN ,
ub . User . id != user . id ) . count ( ) :
ub . User . id != user . id ) . count ( ) :
return Response (
return Response (
json . dumps ( [ { ' type ' : " danger " ,
json . dumps ( [ { ' type ' : " danger " ,
' message ' : _ ( u " No admin user remaining, can ' t remove admin role " ,
' message ' : _ ( " No admin user remaining, can ' t remove admin role " ,
nick = user . name ) } ] ) , mimetype = ' application/json ' )
nick = user . name ) } ] ) , mimetype = ' application/json ' )
user . role & = ~ value
user . role & = ~ value
else :
else :
@ -487,7 +503,7 @@ def edit_list_user(param):
if user . name == " Guest " and value == constants . SIDEBAR_READ_AND_UNREAD :
if user . name == " Guest " and value == constants . SIDEBAR_READ_AND_UNREAD :
raise Exception ( _ ( " Guest can ' t have this view " ) )
raise Exception ( _ ( " Guest can ' t have this view " ) )
# check for valid value, last on checks for power of 2 value
# check for valid value, last on checks for power of 2 value
if value > 0 and value < = constants . SIDEBAR_LIST and ( value & value - 1 == 0 or value == 1 ) :
if value > 0 and value < = constants . SIDEBAR_LIST and ( value & value - 1 == 0 or value == 1 ) :
if vals [ ' value ' ] == ' true ' :
if vals [ ' value ' ] == ' true ' :
user . sidebar_view | = value
user . sidebar_view | = value
elif vals [ ' value ' ] == ' false ' :
elif vals [ ' value ' ] == ' false ' :
@ -552,13 +568,13 @@ def update_view_configuration():
calibre_db . update_title_sort ( config )
calibre_db . update_title_sort ( config )
if not check_valid_read_column ( to_save . get ( " config_read_column " , " 0 " ) ) :
if not check_valid_read_column ( to_save . get ( " config_read_column " , " 0 " ) ) :
flash ( _ ( u " Invalid Read Column " ) , category = " error " )
flash ( _ ( " Invalid Read Column " ) , category = " error " )
log . debug ( " Invalid Read column " )
log . debug ( " Invalid Read column " )
return view_configuration ( )
return view_configuration ( )
_config_int ( to_save , " config_read_column " )
_config_int ( to_save , " config_read_column " )
if not check_valid_restricted_column ( to_save . get ( " config_restricted_column " , " 0 " ) ) :
if not check_valid_restricted_column ( to_save . get ( " config_restricted_column " , " 0 " ) ) :
flash ( _ ( u " Invalid Restricted Column " ) , category = " error " )
flash ( _ ( " Invalid Restricted Column " ) , category = " error " )
log . debug ( " Invalid Restricted Column " )
log . debug ( " Invalid Restricted Column " )
return view_configuration ( )
return view_configuration ( )
_config_int ( to_save , " config_restricted_column " )
_config_int ( to_save , " config_restricted_column " )
@ -578,7 +594,7 @@ def update_view_configuration():
config . config_default_show | = constants . DETAIL_RANDOM
config . config_default_show | = constants . DETAIL_RANDOM
config . save ( )
config . save ( )
flash ( _ ( u " Calibre-Web configuration updated " ) , category = " success " )
flash ( _ ( " Calibre-Web configuration updated " ) , category = " success " )
log . debug ( " Calibre-Web configuration updated " )
log . debug ( " Calibre-Web configuration updated " )
before_request ( )
before_request ( )
@ -613,7 +629,8 @@ def load_dialogtexts(element_id):
elif element_id == " db_submit " :
elif element_id == " db_submit " :
texts [ " main " ] = _ ( ' Are you sure you want to change Calibre library location? ' )
texts [ " main " ] = _ ( ' Are you sure you want to change Calibre library location? ' )
elif element_id == " admin_refresh_cover_cache " :
elif element_id == " admin_refresh_cover_cache " :
texts [ " main " ] = _ ( ' Calibre-Web will search for updated Covers and update Cover Thumbnails, this may take a while? ' )
texts [ " main " ] = _ ( ' Calibre-Web will search for updated Covers '
' and update Cover Thumbnails, this may take a while? ' )
elif element_id == " btnfullsync " :
elif element_id == " btnfullsync " :
texts [ " main " ] = _ ( " Are you sure you want delete Calibre-Web ' s sync database "
texts [ " main " ] = _ ( " Are you sure you want delete Calibre-Web ' s sync database "
" to force a full sync with your Kobo Reader? " )
" to force a full sync with your Kobo Reader? " )
@ -639,7 +656,7 @@ def edit_domain(allow):
@admin_required
@admin_required
def add_domain ( allow ) :
def add_domain ( allow ) :
domain_name = request . form . to_dict ( ) [ ' domainname ' ] . replace ( ' * ' , ' % ' ) . replace ( ' ? ' , ' _ ' ) . lower ( )
domain_name = request . form . to_dict ( ) [ ' domainname ' ] . replace ( ' * ' , ' % ' ) . replace ( ' ? ' , ' _ ' ) . lower ( )
check = ub . session . query ( ub . Registration ) . filter ( ub . Registration . domain == domain_name ) \
check = ub . session . query ( ub . Registration ) . filter ( ub . Registration . domain == domain_name ) \
. filter ( ub . Registration . allow == allow ) . first ( )
. filter ( ub . Registration . allow == allow ) . first ( )
if not check :
if not check :
new_domain = ub . Registration ( domain = domain_name , allow = allow )
new_domain = ub . Registration ( domain = domain_name , allow = allow )
@ -744,6 +761,7 @@ def edit_restriction(res_type, user_id):
ub . session_commit ( " Changed denied columns of user {} to {} " . format ( usr . name , usr . denied_column_value ) )
ub . session_commit ( " Changed denied columns of user {} to {} " . format ( usr . name , usr . denied_column_value ) )
return " "
return " "
@admi.route ( " /ajax/addrestriction/<int:res_type> " , methods = [ ' POST ' ] )
@admi.route ( " /ajax/addrestriction/<int:res_type> " , methods = [ ' POST ' ] )
@login_required
@login_required
@admin_required
@admin_required
@ -856,16 +874,16 @@ def delete_restriction(res_type, user_id):
@login_required
@login_required
@admin_required
@admin_required
def list_restriction ( res_type , user_id ) :
def list_restriction ( res_type , user_id ) :
if res_type == 0 : # Tags as template
if res_type == 0 : # Tags as template
restrict = [ { ' Element ' : x , ' type ' : _ ( ' Deny ' ) , ' id ' : ' d ' + str ( i ) }
restrict = [ { ' Element ' : x , ' type ' : _ ( ' Deny ' ) , ' id ' : ' d ' + str ( i ) }
for i , x in enumerate ( config . list_denied_tags ( ) ) if x != ' ' ]
for i , x in enumerate ( config . list_denied_tags ( ) ) if x != ' ' ]
allow = [ { ' Element ' : x , ' type ' : _ ( ' Allow ' ) , ' id ' : ' a ' + str ( i ) }
allow = [ { ' Element ' : x , ' type ' : _ ( ' Allow ' ) , ' id ' : ' a ' + str ( i ) }
for i , x in enumerate ( config . list_allowed_tags ( ) ) if x != ' ' ]
for i , x in enumerate ( config . list_allowed_tags ( ) ) if x != ' ' ]
json_dumps = restrict + allow
json_dumps = restrict + allow
elif res_type == 1 : # CustomC as template
elif res_type == 1 : # CustomC as template
restrict = [ { ' Element ' : x , ' type ' : _ ( ' Deny ' ) , ' id ' : ' d ' + str ( i ) }
restrict = [ { ' Element ' : x , ' type ' : _ ( ' Deny ' ) , ' id ' : ' d ' + str ( i ) }
for i , x in enumerate ( config . list_denied_column_values ( ) ) if x != ' ' ]
for i , x in enumerate ( config . list_denied_column_values ( ) ) if x != ' ' ]
allow = [ { ' Element ' : x , ' type ' : _ ( ' Allow ' ) , ' id ' : ' a ' + str ( i ) }
allow = [ { ' Element ' : x , ' type ' : _ ( ' Allow ' ) , ' id ' : ' a ' + str ( i ) }
for i , x in enumerate ( config . list_allowed_column_values ( ) ) if x != ' ' ]
for i , x in enumerate ( config . list_allowed_column_values ( ) ) if x != ' ' ]
json_dumps = restrict + allow
json_dumps = restrict + allow
elif res_type == 2 : # Tags per user
elif res_type == 2 : # Tags per user
@ -873,9 +891,9 @@ def list_restriction(res_type, user_id):
usr = ub . session . query ( ub . User ) . filter ( ub . User . id == user_id ) . first ( )
usr = ub . session . query ( ub . User ) . filter ( ub . User . id == user_id ) . first ( )
else :
else :
usr = current_user
usr = current_user
restrict = [ { ' Element ' : x , ' type ' : _ ( ' Deny ' ) , ' id ' : ' d ' + str ( i ) }
restrict = [ { ' Element ' : x , ' type ' : _ ( ' Deny ' ) , ' id ' : ' d ' + str ( i ) }
for i , x in enumerate ( usr . list_denied_tags ( ) ) if x != ' ' ]
for i , x in enumerate ( usr . list_denied_tags ( ) ) if x != ' ' ]
allow = [ { ' Element ' : x , ' type ' : _ ( ' Allow ' ) , ' id ' : ' a ' + str ( i ) }
allow = [ { ' Element ' : x , ' type ' : _ ( ' Allow ' ) , ' id ' : ' a ' + str ( i ) }
for i , x in enumerate ( usr . list_allowed_tags ( ) ) if x != ' ' ]
for i , x in enumerate ( usr . list_allowed_tags ( ) ) if x != ' ' ]
json_dumps = restrict + allow
json_dumps = restrict + allow
elif res_type == 3 : # CustomC per user
elif res_type == 3 : # CustomC per user
@ -883,9 +901,9 @@ def list_restriction(res_type, user_id):
usr = ub . session . query ( ub . User ) . filter ( ub . User . id == user_id ) . first ( )
usr = ub . session . query ( ub . User ) . filter ( ub . User . id == user_id ) . first ( )
else :
else :
usr = current_user
usr = current_user
restrict = [ { ' Element ' : x , ' type ' : _ ( ' Deny ' ) , ' id ' : ' d ' + str ( i ) }
restrict = [ { ' Element ' : x , ' type ' : _ ( ' Deny ' ) , ' id ' : ' d ' + str ( i ) }
for i , x in enumerate ( usr . list_denied_column_values ( ) ) if x != ' ' ]
for i , x in enumerate ( usr . list_denied_column_values ( ) ) if x != ' ' ]
allow = [ { ' Element ' : x , ' type ' : _ ( ' Allow ' ) , ' id ' : ' a ' + str ( i ) }
allow = [ { ' Element ' : x , ' type ' : _ ( ' Allow ' ) , ' id ' : ' a ' + str ( i ) }
for i , x in enumerate ( usr . list_allowed_column_values ( ) ) if x != ' ' ]
for i , x in enumerate ( usr . list_allowed_column_values ( ) ) if x != ' ' ]
json_dumps = restrict + allow
json_dumps = restrict + allow
else :
else :
@ -915,7 +933,7 @@ def ajax_pathchooser():
def check_valid_read_column ( column ) :
def check_valid_read_column ( column ) :
if column != " 0 " :
if column != " 0 " :
if not calibre_db . session . query ( db . CustomColumns ) . filter ( db . CustomColumns . id == column ) \
if not calibre_db . session . query ( db . CustomColumns ) . filter ( db . CustomColumns . id == column ) \
. filter ( and_ ( db . CustomColumns . datatype == ' bool ' , db . CustomColumns . mark_for_delete == 0 ) ) . all ( ) :
. filter ( and_ ( db . CustomColumns . datatype == ' bool ' , db . CustomColumns . mark_for_delete == 0 ) ) . all ( ) :
return False
return False
return True
return True
@ -923,7 +941,7 @@ def check_valid_read_column(column):
def check_valid_restricted_column ( column ) :
def check_valid_restricted_column ( column ) :
if column != " 0 " :
if column != " 0 " :
if not calibre_db . session . query ( db . CustomColumns ) . filter ( db . CustomColumns . id == column ) \
if not calibre_db . session . query ( db . CustomColumns ) . filter ( db . CustomColumns . id == column ) \
. filter ( and_ ( db . CustomColumns . datatype == ' text ' , db . CustomColumns . mark_for_delete == 0 ) ) . all ( ) :
. filter ( and_ ( db . CustomColumns . datatype == ' text ' , db . CustomColumns . mark_for_delete == 0 ) ) . all ( ) :
return False
return False
return True
return True
@ -951,7 +969,7 @@ def prepare_tags(user, action, tags_name, id_list):
raise Exception ( _ ( " Tag not found " ) )
raise Exception ( _ ( " Tag not found " ) )
new_tags_list = [ x . name for x in tags ]
new_tags_list = [ x . name for x in tags ]
else :
else :
tags = calibre_db . session . query ( db . cc_classes [ config . config_restricted_column ] ) \
tags = calibre_db . session . query ( db . cc_classes [ config . config_restricted_column ] ) \
. filter ( db . cc_classes [ config . config_restricted_column ] . id . in_ ( id_list ) ) . all ( )
. filter ( db . cc_classes [ config . config_restricted_column ] . id . in_ ( id_list ) ) . all ( )
new_tags_list = [ x . value for x in tags ]
new_tags_list = [ x . value for x in tags ]
saved_tags_list = user . __dict__ [ tags_name ] . split ( " , " ) if len ( user . __dict__ [ tags_name ] ) else [ ]
saved_tags_list = user . __dict__ [ tags_name ] . split ( " , " ) if len ( user . __dict__ [ tags_name ] ) else [ ]
@ -964,6 +982,19 @@ def prepare_tags(user, action, tags_name, id_list):
return " , " . join ( saved_tags_list )
return " , " . join ( saved_tags_list )
def get_drives ( current ) :
drive_letters = [ ]
for d in string . ascii_uppercase :
if os . path . exists ( ' {} : ' . format ( d ) ) and current [ 0 ] . lower ( ) != d . lower ( ) :
drive = " {} : \\ " . format ( d )
data = { " name " : drive , " fullpath " : drive }
data [ " sort " ] = " _ " + data [ " fullpath " ] . lower ( )
data [ " type " ] = " dir "
data [ " size " ] = " "
drive_letters . append ( data )
return drive_letters
def pathchooser ( ) :
def pathchooser ( ) :
browse_for = " folder "
browse_for = " folder "
folder_only = request . args . get ( ' folder ' , False ) == " true "
folder_only = request . args . get ( ' folder ' , False ) == " true "
@ -971,43 +1002,45 @@ def pathchooser():
path = os . path . normpath ( request . args . get ( ' path ' , " " ) )
path = os . path . normpath ( request . args . get ( ' path ' , " " ) )
if os . path . isfile ( path ) :
if os . path . isfile ( path ) :
old file = path
old _ file = path
path = os . path . dirname ( path )
path = os . path . dirname ( path )
else :
else :
old file = " "
old _ file = " "
absolute = False
absolute = False
if os . path . isdir ( path ) :
if os . path . isdir ( path ) :
# if os.path.isabs(path):
cwd = os . path . realpath ( path )
cwd = os . path . realpath ( path )
absolute = True
absolute = True
# else:
# cwd = os.path.relpath(path)
else :
else :
cwd = os . getcwd ( )
cwd = os . getcwd ( )
cwd = os . path . normpath ( os . path . realpath ( cwd ) )
cwd = os . path . normpath ( os . path . realpath ( cwd ) )
parent dir = os . path . dirname ( cwd )
parent _ dir = os . path . dirname ( cwd )
if not absolute :
if not absolute :
if os . path . realpath ( cwd ) == os . path . realpath ( " / " ) :
if os . path . realpath ( cwd ) == os . path . realpath ( " / " ) :
cwd = os . path . relpath ( cwd )
cwd = os . path . relpath ( cwd )
else :
else :
cwd = os . path . relpath ( cwd ) + os . path . sep
cwd = os . path . relpath ( cwd ) + os . path . sep
parent dir = os . path . relpath ( parent dir) + os . path . sep
parent _ dir = os . path . relpath ( parent _ dir) + os . path . sep
if os . path . realpath ( cwd ) == os . path . realpath ( " / " ) :
files = [ ]
parentdir = " "
if os . path . realpath ( cwd ) == os . path . realpath ( " / " ) \
or ( sys . platform == " win32 " and os . path . realpath ( cwd ) [ 1 : ] == os . path . realpath ( " / " ) [ 1 : ] ) :
# we are in root
parent_dir = " "
if sys . platform == " win32 " :
files = get_drives ( cwd )
try :
try :
folders = os . listdir ( cwd )
folders = os . listdir ( cwd )
except Exception :
except Exception :
folders = [ ]
folders = [ ]
files = [ ]
for f in folders :
for f in folders :
try :
try :
data = { " name " : f , " fullpath " : os . path . join ( cwd , f ) }
sanitized_f = str ( Markup . escape ( f ) )
data = { " name " : sanitized_f , " fullpath " : os . path . join ( cwd , sanitized_f ) }
data [ " sort " ] = data [ " fullpath " ] . lower ( )
data [ " sort " ] = data [ " fullpath " ] . lower ( )
except Exception :
except Exception :
continue
continue
@ -1037,9 +1070,9 @@ def pathchooser():
context = {
context = {
" cwd " : cwd ,
" cwd " : cwd ,
" files " : files ,
" files " : files ,
" parentdir " : parent dir,
" parentdir " : parent _ dir,
" type " : browse_for ,
" type " : browse_for ,
" oldfile " : old file,
" oldfile " : old _ file,
" absolute " : absolute ,
" absolute " : absolute ,
}
}
return json . dumps ( context )
return json . dumps ( context )
@ -1058,7 +1091,7 @@ def _config_checkbox_int(to_save, x):
def _config_string ( to_save , x ) :
def _config_string ( to_save , x ) :
return config . set_from_dictionary ( to_save , x , lambda y : y . strip ( ) if y else y )
return config . set_from_dictionary ( to_save , x , lambda y : y . strip ( ) . strip ( u ' \u200B \u200C \u200D \ufeff ' ) if y else y )
def _configuration_gdrive_helper ( to_save ) :
def _configuration_gdrive_helper ( to_save ) :
@ -1077,12 +1110,12 @@ def _configuration_gdrive_helper(to_save):
if not gdrive_secrets :
if not gdrive_secrets :
return _configuration_result ( _ ( ' client_secrets.json Is Not Configured For Web Application ' ) )
return _configuration_result ( _ ( ' client_secrets.json Is Not Configured For Web Application ' ) )
gdriveutils . update_settings (
gdriveutils . update_settings (
gdrive_secrets [ ' client_id ' ] ,
gdrive_secrets [ ' client_id ' ] ,
gdrive_secrets [ ' client_secret ' ] ,
gdrive_secrets [ ' client_secret ' ] ,
gdrive_secrets [ ' redirect_uris ' ] [ 0 ]
gdrive_secrets [ ' redirect_uris ' ] [ 0 ]
)
)
# always show google d rive settings, but in case of error deny support
# always show Google D rive settings, but in case of error deny support
new_gdrive_value = ( not gdrive_error ) and ( " config_use_google_drive " in to_save )
new_gdrive_value = ( not gdrive_error ) and ( " config_use_google_drive " in to_save )
if config . config_use_google_drive and not new_gdrive_value :
if config . config_use_google_drive and not new_gdrive_value :
config . config_google_drive_watch_changes_response = { }
config . config_google_drive_watch_changes_response = { }
@ -1097,12 +1130,12 @@ def _configuration_oauth_helper(to_save):
reboot_required = False
reboot_required = False
for element in oauthblueprints :
for element in oauthblueprints :
if to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_id " ] != element [ ' oauth_client_id ' ] \
if to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_id " ] != element [ ' oauth_client_id ' ] \
or to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_secret " ] != element [ ' oauth_client_secret ' ] :
or to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_secret " ] != element [ ' oauth_client_secret ' ] :
reboot_required = True
reboot_required = True
element [ ' oauth_client_id ' ] = to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_id " ]
element [ ' oauth_client_id ' ] = to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_id " ]
element [ ' oauth_client_secret ' ] = to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_secret " ]
element [ ' oauth_client_secret ' ] = to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_secret " ]
if to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_id " ] \
if to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_id " ] \
and to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_secret " ] :
and to_save [ " config_ " + str ( element [ ' id ' ] ) + " _oauth_client_secret " ] :
active_oauths + = 1
active_oauths + = 1
element [ " active " ] = 1
element [ " active " ] = 1
else :
else :
@ -1132,7 +1165,6 @@ def _configuration_logfile_helper(to_save):
def _configuration_ldap_helper ( to_save ) :
def _configuration_ldap_helper ( to_save ) :
reboot_required = False
reboot_required = False
reboot_required | = _config_string ( to_save , " config_ldap_provider_url " )
reboot_required | = _config_int ( to_save , " config_ldap_port " )
reboot_required | = _config_int ( to_save , " config_ldap_port " )
reboot_required | = _config_int ( to_save , " config_ldap_authentication " )
reboot_required | = _config_int ( to_save , " config_ldap_authentication " )
reboot_required | = _config_string ( to_save , " config_ldap_dn " )
reboot_required | = _config_string ( to_save , " config_ldap_dn " )
@ -1147,21 +1179,26 @@ def _configuration_ldap_helper(to_save):
reboot_required | = _config_string ( to_save , " config_ldap_cert_path " )
reboot_required | = _config_string ( to_save , " config_ldap_cert_path " )
reboot_required | = _config_string ( to_save , " config_ldap_key_path " )
reboot_required | = _config_string ( to_save , " config_ldap_key_path " )
_config_string ( to_save , " config_ldap_group_name " )
_config_string ( to_save , " config_ldap_group_name " )
if to_save . get ( " config_ldap_serv_password " , " " ) != " " :
address = urlparse ( to_save . get ( " config_ldap_provider_url " , " " ) )
to_save [ " config_ldap_provider_url " ] = ( address . hostname or address . path ) . strip ( " / " )
reboot_required | = _config_string ( to_save , " config_ldap_provider_url " )
if to_save . get ( " config_ldap_serv_password_e " , " " ) != " " :
reboot_required | = 1
reboot_required | = 1
config . set_from_dictionary ( to_save , " config_ldap_serv_password " , base64 . b64encode , encode = ' UTF-8 ' )
config . set_from_dictionary ( to_save , " config_ldap_serv_password _e " )
config . save ( )
config . save ( )
if not config . config_ldap_provider_url \
if not config . config_ldap_provider_url \
or not config . config_ldap_port \
or not config . config_ldap_port \
or not config . config_ldap_dn \
or not config . config_ldap_dn \
or not config . config_ldap_user_object :
or not config . config_ldap_user_object :
return reboot_required , _configuration_result ( _ ( ' Please Enter a LDAP Provider, '
return reboot_required , _configuration_result ( _ ( ' Please Enter a LDAP Provider, '
' Port, DN and User Object Identifier ' ) )
' Port, DN and User Object Identifier ' ) )
if config . config_ldap_authentication > constants . LDAP_AUTH_ANONYMOUS :
if config . config_ldap_authentication > constants . LDAP_AUTH_ANONYMOUS :
if config . config_ldap_authentication > constants . LDAP_AUTH_UNAUTHENTICATE :
if config . config_ldap_authentication > constants . LDAP_AUTH_UNAUTHENTICATE :
if not config . config_ldap_serv_username or not bool ( config . config_ldap_serv_password ) :
if not config . config_ldap_serv_username or not bool ( config . config_ldap_serv_password _e ) :
return reboot_required , _configuration_result ( _ ( ' Please Enter a LDAP Service Account and Password ' ) )
return reboot_required , _configuration_result ( _ ( ' Please Enter a LDAP Service Account and Password ' ) )
else :
else :
if not config . config_ldap_serv_username :
if not config . config_ldap_serv_username :
@ -1225,16 +1262,16 @@ def new_user():
content . default_language = config . config_default_language
content . default_language = config . config_default_language
return render_title_template ( " user_edit.html " , new_user = 1 , content = content ,
return render_title_template ( " user_edit.html " , new_user = 1 , content = content ,
config = config , translations = translations ,
config = config , translations = translations ,
languages = languages , title = _ ( u" Add new u ser" ) , page = " newuser " ,
languages = languages , title = _ ( " Add New U ser" ) , page = " newuser " ,
kobo_support = kobo_support , registered_oauth = oauth_check )
kobo_support = kobo_support , registered_oauth = oauth_check )
@admi.route ( " /admin/mailsettings " )
@admi.route ( " /admin/mailsettings " , methods = [ " GET " ] )
@login_required
@login_required
@admin_required
@admin_required
def edit_mailsettings ( ) :
def edit_mailsettings ( ) :
content = config . get_mail_settings ( )
content = config . get_mail_settings ( )
return render_title_template ( " email_edit.html " , content = content , title = _ ( u " Edit E - mail Server Settings" ) ,
return render_title_template ( " email_edit.html " , content = content , title = _ ( " Edit E mail Server Settings" ) ,
page = " mailset " , feature_support = feature_support )
page = " mailset " , feature_support = feature_support )
@ -1253,40 +1290,44 @@ def update_mailsettings():
elif to_save . get ( " gmail " ) :
elif to_save . get ( " gmail " ) :
try :
try :
config . mail_gmail_token = services . gmail . setup_gmail ( config . mail_gmail_token )
config . mail_gmail_token = services . gmail . setup_gmail ( config . mail_gmail_token )
flash ( _ ( u" Gmail Account Verification Successful " ) , category = " success " )
flash ( _ ( " Success! Gmail Account Verified. " ) , category = " success " )
except Exception as ex :
except Exception as ex :
flash ( str ( ex ) , category = " error " )
flash ( str ( ex ) , category = " error " )
log . error ( ex )
log . error ( ex )
return edit_mailsettings ( )
return edit_mailsettings ( )
else :
else :
_config_string ( to_save , " mail_server " )
_config_int ( to_save , " mail_port " )
_config_int ( to_save , " mail_port " )
_config_int ( to_save , " mail_use_ssl " )
_config_int ( to_save , " mail_use_ssl " )
_config_string ( to_save , " mail_login " )
if to_save . get ( " mail_password_e " , " " ) :
_config_string ( to_save , " mail_password " )
_config_string ( to_save , " mail_password_e " )
_config_string ( to_save , " mail_from " )
_config_int ( to_save , " mail_size " , lambda y : int ( y ) * 1024 * 1024 )
_config_int ( to_save , " mail_size " , lambda y : int ( y ) * 1024 * 1024 )
config . mail_server = to_save . get ( ' mail_server ' , " " ) . strip ( )
config . mail_from = to_save . get ( ' mail_from ' , " " ) . strip ( )
config . mail_login = to_save . get ( ' mail_login ' , " " ) . strip ( )
try :
try :
config . save ( )
config . save ( )
except ( OperationalError , InvalidRequestError ) as e :
except ( OperationalError , InvalidRequestError ) as e :
ub . session . rollback ( )
ub . session . rollback ( )
log . error_or_exception ( " Settings Database error: {} " . format ( e ) )
log . error_or_exception ( " Settings Database error: {} " . format ( e ) )
flash ( _ ( u " Database error: %(error)s . " , error = e . orig ) , category = " error " )
flash ( _ ( " Oops! Database Error: %(error)s . " , error = e . orig ) , category = " error " )
return edit_mailsettings ( )
except Exception as e :
flash ( _ ( " Oops! Database Error: %(error)s . " , error = e . orig ) , category = " error " )
return edit_mailsettings ( )
return edit_mailsettings ( )
if to_save . get ( " test " ) :
if to_save . get ( " test " ) :
if current_user . email :
if current_user . email :
result = send_test_mail ( current_user . email , current_user . name )
result = send_test_mail ( current_user . email , current_user . name )
if result is None :
if result is None :
flash ( _ ( u " Test e-mail queued for sending to %(email)s , please check Tasks for result " ,
flash ( _ ( " Test e-mail queued for sending to %(email)s , please check Tasks for result " ,
email = current_user . email ) , category = " info " )
email = current_user . email ) , category = " info " )
else :
else :
flash ( _ ( u " There was an error sending the Test e-mail: %(res)s " , res = result ) , category = " error " )
flash ( _ ( " There was an error sending the Test e-mail: %(res)s " , res = result ) , category = " error " )
else :
else :
flash ( _ ( u " Please configure your e-mail address first... " ) , category = " error " )
flash ( _ ( " Please configure your e-mail address first... " ) , category = " error " )
else :
else :
flash ( _ ( u" E-mail server s ettings updated" ) , category = " success " )
flash ( _ ( " Email Server S ettings updated" ) , category = " success " )
return edit_mailsettings ( )
return edit_mailsettings ( )
@ -1300,16 +1341,16 @@ def edit_scheduledtasks():
duration_field = list ( )
duration_field = list ( )
for n in range ( 24 ) :
for n in range ( 24 ) :
time_field . append ( ( n , format_time ( time( hour = n ) , format = " short " , ) ) )
time_field . append ( ( n , format_time ( datetime_ time( hour = n ) , format = " short " , ) ) )
for n in range ( 5 , 65 , 5 ) :
for n in range ( 5 , 65 , 5 ) :
t = timedelta ( hours = n / / 60 , minutes = n % 60 )
t = timedelta ( hours = n / / 60 , minutes = n % 60 )
duration_field . append ( ( n , format_timedelta ( t , threshold = .9 ) ) )
duration_field . append ( ( n , format_timedelta ( t , threshold = .9 7 ) ) )
return render_title_template ( " schedule_edit.html " ,
return render_title_template ( " schedule_edit.html " ,
config = content ,
config = content ,
starttime = time_field ,
starttime = time_field ,
duration = duration_field ,
duration = duration_field ,
title = _ ( u " Edit Scheduled Tasks Settings " ) )
title = _ ( " Edit Scheduled Tasks Settings " ) )
@admi.route ( " /admin/scheduledtasks " , methods = [ " POST " ] )
@admi.route ( " /admin/scheduledtasks " , methods = [ " POST " ] )
@ -1319,23 +1360,24 @@ def update_scheduledtasks():
error = False
error = False
to_save = request . form . to_dict ( )
to_save = request . form . to_dict ( )
if 0 < = int ( to_save . get ( " schedule_start_time " ) ) < = 23 :
if 0 < = int ( to_save . get ( " schedule_start_time " ) ) < = 23 :
_config_int ( to_save , " schedule_start_time " )
_config_int ( to_save , " schedule_start_time " )
else :
else :
flash ( _ ( u " Invalid start time for task specified " ) , category = " error " )
flash ( _ ( " Invalid start time for task specified " ) , category = " error " )
error = True
error = True
if 0 < int ( to_save . get ( " schedule_duration " ) ) < = 60 :
if 0 < int ( to_save . get ( " schedule_duration " ) ) < = 60 :
_config_int ( to_save , " schedule_duration " )
_config_int ( to_save , " schedule_duration " )
else :
else :
flash ( _ ( u " Invalid duration for task specified " ) , category = " error " )
flash ( _ ( " Invalid duration for task specified " ) , category = " error " )
error = True
error = True
_config_checkbox ( to_save , " schedule_generate_book_covers " )
_config_checkbox ( to_save , " schedule_generate_book_covers " )
_config_checkbox ( to_save , " schedule_generate_series_covers " )
_config_checkbox ( to_save , " schedule_generate_series_covers " )
_config_checkbox ( to_save , " schedule_metadata_backup " )
_config_checkbox ( to_save , " schedule_reconnect " )
_config_checkbox ( to_save , " schedule_reconnect " )
if not error :
if not error :
try :
try :
config . save ( )
config . save ( )
flash ( _ ( u " Scheduled tasks settings updated " ) , category = " success " )
flash ( _ ( " Scheduled tasks settings updated " ) , category = " success " )
# Cancel any running tasks
# Cancel any running tasks
schedule . end_scheduled_tasks ( )
schedule . end_scheduled_tasks ( )
@ -1345,7 +1387,7 @@ def update_scheduledtasks():
except IntegrityError :
except IntegrityError :
ub . session . rollback ( )
ub . session . rollback ( )
log . error ( " An unknown error occurred while saving scheduled tasks settings " )
log . error ( " An unknown error occurred while saving scheduled tasks settings " )
flash ( _ ( u " An unknown error occurred. Please try again later." ) , category = " error " )
flash ( _ ( " Oops! An unknown error occurred. Please try again later." ) , category = " error " )
except OperationalError :
except OperationalError :
ub . session . rollback ( )
ub . session . rollback ( )
log . error ( " Settings DB is not Writeable " )
log . error ( " Settings DB is not Writeable " )
@ -1360,7 +1402,7 @@ def update_scheduledtasks():
def edit_user ( user_id ) :
def edit_user ( user_id ) :
content = ub . session . query ( ub . User ) . filter ( ub . User . id == int ( user_id ) ) . first ( ) # type: ub.User
content = ub . session . query ( ub . User ) . filter ( ub . User . id == int ( user_id ) ) . first ( ) # type: ub.User
if not content or ( not config . config_anonbrowse and content . name == " Guest " ) :
if not content or ( not config . config_anonbrowse and content . name == " Guest " ) :
flash ( _ ( u " User not found " ) , category = " error " )
flash ( _ ( " User not found " ) , category = " error " )
return redirect ( url_for ( ' admin.admin ' ) )
return redirect ( url_for ( ' admin.admin ' ) )
languages = calibre_db . speaking_language ( return_all_languages = True )
languages = calibre_db . speaking_language ( return_all_languages = True )
translations = get_available_locale ( )
translations = get_available_locale ( )
@ -1379,7 +1421,7 @@ def edit_user(user_id):
registered_oauth = oauth_check ,
registered_oauth = oauth_check ,
mail_configured = config . get_mail_server_configured ( ) ,
mail_configured = config . get_mail_server_configured ( ) ,
kobo_support = kobo_support ,
kobo_support = kobo_support ,
title = _ ( u " Edit User %(nick)s " , nick = content . name ) ,
title = _ ( " Edit User %(nick)s " , nick = content . name ) ,
page = " edituser " )
page = " edituser " )
@ -1390,14 +1432,14 @@ def reset_user_password(user_id):
if current_user is not None and current_user . is_authenticated :
if current_user is not None and current_user . is_authenticated :
ret , message = reset_password ( user_id )
ret , message = reset_password ( user_id )
if ret == 1 :
if ret == 1 :
log . debug ( u " Password for user %s reset " , message )
log . debug ( " Password for user %s reset " , message )
flash ( _ ( u " Password for user %(user)s reset " , user = message ) , category = " success " )
flash ( _ ( " Success! Password for user %(user)s reset " , user = message ) , category = " success " )
elif ret == 0 :
elif ret == 0 :
log . error ( u " An unknown error occurred. Please try again later. " )
log . error ( " An unknown error occurred. Please try again later. " )
flash ( _ ( u " An unknown error occurred. Please try again later." ) , category = " error " )
flash ( _ ( " Oops! An unknown error occurred. Please try again later." ) , category = " error " )
else :
else :
log . error ( u " Please configure the SMTP mail settings first.. ." )
log . error ( " Please configure the SMTP mail settings ." )
flash ( _ ( u " Please configure the SMTP mail settings first.. ." ) , category = " error " )
flash ( _ ( " Oops! Please configure the SMTP mail settings." ) , category = " error " )
return redirect ( url_for ( ' admin.admin ' ) )
return redirect ( url_for ( ' admin.admin ' ) )
@ -1408,7 +1450,7 @@ def view_logfile():
logfiles = { 0 : logger . get_logfile ( config . config_logfile ) ,
logfiles = { 0 : logger . get_logfile ( config . config_logfile ) ,
1 : logger . get_accesslogfile ( config . config_access_logfile ) }
1 : logger . get_accesslogfile ( config . config_access_logfile ) }
return render_title_template ( " logviewer.html " ,
return render_title_template ( " logviewer.html " ,
title = _ ( u " Logfile viewer " ) ,
title = _ ( " Logfile viewer " ) ,
accesslog_enable = config . config_access_log ,
accesslog_enable = config . config_access_log ,
log_enable = bool ( config . config_logfile != logger . LOG_TO_STDOUT ) ,
log_enable = bool ( config . config_logfile != logger . LOG_TO_STDOUT ) ,
logfiles = logfiles ,
logfiles = logfiles ,
@ -1458,7 +1500,7 @@ def download_debug():
@admin_required
@admin_required
def get_update_status ( ) :
def get_update_status ( ) :
if feature_support [ ' updater ' ] :
if feature_support [ ' updater ' ] :
log . info ( u " Update status requested " )
log . info ( " Update status requested " )
return updater_thread . get_available_updates ( request . method )
return updater_thread . get_available_updates ( request . method )
else :
else :
return ' '
return ' '
@ -1519,11 +1561,11 @@ def ldap_import_create_user(user, user_data):
log . warning ( " LDAP User %s Already in Database " , user_data )
log . warning ( " LDAP User %s Already in Database " , user_data )
return 0 , None
return 0 , None
kindle mail = ' '
ereader_ mail = ' '
if ' mail ' in user_data :
if ' mail ' in user_data :
useremail = user_data [ ' mail ' ] [ 0 ] . decode ( ' utf-8 ' )
useremail = user_data [ ' mail ' ] [ 0 ] . decode ( ' utf-8 ' )
if len ( user_data [ ' mail ' ] ) > 1 :
if len ( user_data [ ' mail ' ] ) > 1 :
kindle mail = user_data [ ' mail ' ] [ 1 ] . decode ( ' utf-8 ' )
ereader_ mail = user_data [ ' mail ' ] [ 1 ] . decode ( ' utf-8 ' )
else :
else :
log . debug ( ' No Mail Field Found in LDAP Response ' )
log . debug ( ' No Mail Field Found in LDAP Response ' )
@ -1539,7 +1581,7 @@ def ldap_import_create_user(user, user_data):
content . name = username
content . name = username
content . password = ' ' # dummy password which will be replaced by ldap one
content . password = ' ' # dummy password which will be replaced by ldap one
content . email = useremail
content . email = useremail
content . kindle_mail = kindle mail
content . kindle_mail = ereader_ mail
content . default_language = config . config_default_language
content . default_language = config . config_default_language
content . locale = config . config_default_locale
content . locale = config . config_default_locale
content . role = config . config_default_role
content . role = config . config_default_role
@ -1551,7 +1593,7 @@ def ldap_import_create_user(user, user_data):
ub . session . add ( content )
ub . session . add ( content )
try :
try :
ub . session . commit ( )
ub . session . commit ( )
return 1 , None # increase no of users
return 1 , None # increase no of users
except Exception as ex :
except Exception as ex :
log . warning ( " Failed to create LDAP user: %s - %s " , user , ex )
log . warning ( " Failed to create LDAP user: %s - %s " , user , ex )
ub . session . rollback ( )
ub . session . rollback ( )
@ -1653,7 +1695,7 @@ def _db_configuration_update_helper():
except ( OperationalError , InvalidRequestError ) as e :
except ( OperationalError , InvalidRequestError ) as e :
ub . session . rollback ( )
ub . session . rollback ( )
log . error_or_exception ( " Settings Database error: {} " . format ( e ) )
log . error_or_exception ( " Settings Database error: {} " . format ( e ) )
_db_configuration_result ( _ ( u" Database e rror: %(error)s . " , error = e . orig ) , gdrive_error )
_db_configuration_result ( _ ( " Oops! Database E rror: %(error)s . " , error = e . orig ) , gdrive_error )
try :
try :
metadata_db = os . path . join ( to_save [ ' config_calibre_dir ' ] , " metadata.db " )
metadata_db = os . path . join ( to_save [ ' config_calibre_dir ' ] , " metadata.db " )
if config . config_use_google_drive and is_gdrive_ready ( ) and not os . path . exists ( metadata_db ) :
if config . config_use_google_drive and is_gdrive_ready ( ) and not os . path . exists ( metadata_db ) :
@ -1663,10 +1705,11 @@ def _db_configuration_update_helper():
return _db_configuration_result ( ' {} ' . format ( ex ) , gdrive_error )
return _db_configuration_result ( ' {} ' . format ( ex ) , gdrive_error )
if db_change or not db_valid or not config . db_configured \
if db_change or not db_valid or not config . db_configured \
or config . config_calibre_dir != to_save [ " config_calibre_dir " ] :
or config . config_calibre_dir != to_save [ " config_calibre_dir " ] :
if not calibre_db . setup_db ( to_save [ ' config_calibre_dir ' ] , ub . app_DB_path ) :
if not os . path . exists ( metadata_db ) or not to_save [ ' config_calibre_dir ' ] :
return _db_configuration_result ( _ ( ' DB Location is not Valid, Please Enter Correct Path ' ) ,
return _db_configuration_result ( _ ( ' DB Location is not Valid, Please Enter Correct Path ' ) , gdrive_error )
gdrive_error )
else :
calibre_db . setup_db ( to_save [ ' config_calibre_dir ' ] , ub . app_DB_path )
config . store_calibre_uuid ( calibre_db , db . Library_Id )
config . store_calibre_uuid ( calibre_db , db . Library_Id )
# if db changed -> delete shelfs, delete download books, delete read books, kobo sync...
# if db changed -> delete shelfs, delete download books, delete read books, kobo sync...
if db_change :
if db_change :
@ -1684,7 +1727,10 @@ def _db_configuration_update_helper():
_config_string ( to_save , " config_calibre_dir " )
_config_string ( to_save , " config_calibre_dir " )
calibre_db . update_config ( config )
calibre_db . update_config ( config )
if not os . access ( os . path . join ( config . config_calibre_dir , " metadata.db " ) , os . W_OK ) :
if not os . access ( os . path . join ( config . config_calibre_dir , " metadata.db " ) , os . W_OK ) :
flash ( _ ( u " DB is not Writeable " ) , category = " warning " )
flash ( _ ( " DB is not Writeable " ) , category = " warning " )
_config_string ( to_save , " config_calibre_split_dir " )
config . config_calibre_split = to_save . get ( ' config_calibre_split ' , 0 ) == " on "
calibre_db . update_config ( config )
config . save ( )
config . save ( )
return _db_configuration_result ( None , gdrive_error )
return _db_configuration_result ( None , gdrive_error )
@ -1705,6 +1751,7 @@ def _configuration_update_helper():
_config_checkbox_int ( to_save , " config_uploading " )
_config_checkbox_int ( to_save , " config_uploading " )
_config_checkbox_int ( to_save , " config_unicode_filename " )
_config_checkbox_int ( to_save , " config_unicode_filename " )
_config_checkbox_int ( to_save , " config_embed_metadata " )
# Reboot on config_anonbrowse with enabled ldap, as decoraters are changed in this case
# Reboot on config_anonbrowse with enabled ldap, as decoraters are changed in this case
reboot_required | = ( _config_checkbox_int ( to_save , " config_anonbrowse " )
reboot_required | = ( _config_checkbox_int ( to_save , " config_anonbrowse " )
and config . config_login_type == constants . LOGIN_LDAP )
and config . config_login_type == constants . LOGIN_LDAP )
@ -1747,10 +1794,11 @@ def _configuration_update_helper():
# Goodreads configuration
# Goodreads configuration
_config_checkbox ( to_save , " config_use_goodreads " )
_config_checkbox ( to_save , " config_use_goodreads " )
_config_string ( to_save , " config_goodreads_api_key " )
_config_string ( to_save , " config_goodreads_api_key " )
_config_string ( to_save , " config_goodreads_api_secret " )
if to_save . get ( " config_goodreads_api_secret_e " , " " ) :
_config_string ( to_save , " config_goodreads_api_secret_e " )
if services . goodreads_support :
if services . goodreads_support :
services . goodreads_support . connect ( config . config_goodreads_api_key ,
services . goodreads_support . connect ( config . config_goodreads_api_key ,
config . config_goodreads_api_secret ,
config . config_goodreads_api_secret _e ,
config . config_use_goodreads )
config . config_use_goodreads )
_config_int ( to_save , " config_updatechannel " )
_config_int ( to_save , " config_updatechannel " )
@ -1763,10 +1811,25 @@ def _configuration_update_helper():
if config . config_login_type == constants . LOGIN_OAUTH :
if config . config_login_type == constants . LOGIN_OAUTH :
reboot_required | = _configuration_oauth_helper ( to_save )
reboot_required | = _configuration_oauth_helper ( to_save )
# logfile configuration
reboot , message = _configuration_logfile_helper ( to_save )
reboot , message = _configuration_logfile_helper ( to_save )
if message :
if message :
return message
return message
reboot_required | = reboot
reboot_required | = reboot
# security configuration
_config_checkbox ( to_save , " config_password_policy " )
_config_checkbox ( to_save , " config_password_number " )
_config_checkbox ( to_save , " config_password_lower " )
_config_checkbox ( to_save , " config_password_upper " )
_config_checkbox ( to_save , " config_password_special " )
if 0 < int ( to_save . get ( " config_password_min_length " , " 0 " ) ) < 41 :
_config_int ( to_save , " config_password_min_length " )
else :
return _configuration_result ( _ ( ' Password length has to be between 1 and 40 ' ) )
reboot_required | = _config_int ( to_save , " config_session " )
reboot_required | = _config_checkbox ( to_save , " config_ratelimiter " )
# Rarfile Content configuration
# Rarfile Content configuration
_config_string ( to_save , " config_rarfile_location " )
_config_string ( to_save , " config_rarfile_location " )
if " config_rarfile_location " in to_save :
if " config_rarfile_location " in to_save :
@ -1776,7 +1839,7 @@ def _configuration_update_helper():
except ( OperationalError , InvalidRequestError ) as e :
except ( OperationalError , InvalidRequestError ) as e :
ub . session . rollback ( )
ub . session . rollback ( )
log . error_or_exception ( " Settings Database error: {} " . format ( e ) )
log . error_or_exception ( " Settings Database error: {} " . format ( e ) )
_configuration_result ( _ ( u" Database e rror: %(error)s . " , error = e . orig ) )
_configuration_result ( _ ( " Oops! Database E rror: %(error)s . " , error = e . orig ) )
config . save ( )
config . save ( )
if reboot_required :
if reboot_required :
@ -1792,7 +1855,7 @@ def _configuration_result(error_flash=None, reboot=False):
config . load ( )
config . load ( )
resp [ ' result ' ] = [ { ' type ' : " danger " , ' message ' : error_flash } ]
resp [ ' result ' ] = [ { ' type ' : " danger " , ' message ' : error_flash } ]
else :
else :
resp [ ' result ' ] = [ { ' type ' : " success " , ' message ' : _ ( u " Calibre-Web configuration updated " ) } ]
resp [ ' result ' ] = [ { ' type ' : " success " , ' message ' : _ ( " Calibre-Web configuration updated " ) } ]
resp [ ' reboot ' ] = reboot
resp [ ' reboot ' ] = reboot
resp [ ' config_upload ' ] = config . config_upload_formats
resp [ ' config_upload ' ] = config . config_upload_formats
return Response ( json . dumps ( resp ) , mimetype = ' application/json ' )
return Response ( json . dumps ( resp ) , mimetype = ' application/json ' )
@ -1823,7 +1886,7 @@ def _db_configuration_result(error_flash=None, gdrive_error=None):
gdriveError = gdrive_error ,
gdriveError = gdrive_error ,
gdrivefolders = gdrivefolders ,
gdrivefolders = gdrivefolders ,
feature_support = feature_support ,
feature_support = feature_support ,
title = _ ( u " Database Configuration " ) , page = " dbconfig " )
title = _ ( " Database Configuration " ) , page = " dbconfig " )
def _handle_new_user ( to_save , content , languages , translations , kobo_support ) :
def _handle_new_user ( to_save , content , languages , translations , kobo_support ) :
@ -1835,25 +1898,25 @@ def _handle_new_user(to_save, content, languages, translations, kobo_support):
content . sidebar_view | = constants . DETAIL_RANDOM
content . sidebar_view | = constants . DETAIL_RANDOM
content . role = constants . selected_roles ( to_save )
content . role = constants . selected_roles ( to_save )
content . password = generate_password_hash ( to_save [ " password " ] )
try :
try :
if not to_save [ " name " ] or not to_save [ " email " ] or not to_save [ " password " ] :
if not to_save [ " name " ] or not to_save [ " email " ] or not to_save [ " password " ] :
log . info ( " Missing entries on new user " )
log . info ( " Missing entries on new user " )
raise Exception ( _ ( u " Please fill out all fields! " ) )
raise Exception ( _ ( " Oops! Please complete all fields. " ) )
content . password = generate_password_hash ( helper . valid_password ( to_save . get ( " password " , " " ) ) )
content . email = check_email ( to_save [ " email " ] )
content . email = check_email ( to_save [ " email " ] )
# Query User name, if not existing, change
# Query user name, if not existing, change
content . name = check_username ( to_save [ " name " ] )
content . name = check_username ( to_save [ " name " ] )
if to_save . get ( " kindle_mail " ) :
if to_save . get ( " kindle_mail " ) :
content . kindle_mail = valid_email ( to_save [ " kindle_mail " ] )
content . kindle_mail = valid_email ( to_save [ " kindle_mail " ] )
if config . config_public_reg and not check_valid_domain ( content . email ) :
if config . config_public_reg and not check_valid_domain ( content . email ) :
log . info ( " E-mail: {} for new user is not from valid domain " . format ( content . email ) )
log . info ( " E-mail: {} for new user is not from valid domain " . format ( content . email ) )
raise Exception ( _ ( u " E-mail is not from valid domain " ) )
raise Exception ( _ ( " E-mail is not from valid domain " ) )
except Exception as ex :
except Exception as ex :
flash ( str ( ex ) , category = " error " )
flash ( str ( ex ) , category = " error " )
return render_title_template ( " user_edit.html " , new_user = 1 , content = content ,
return render_title_template ( " user_edit.html " , new_user = 1 , content = content ,
config = config ,
config = config ,
translations = translations ,
translations = translations ,
languages = languages , title = _ ( u " Add new user " ) , page = " newuser " ,
languages = languages , title = _ ( " Add new user " ) , page = " newuser " ,
kobo_support = kobo_support , registered_oauth = oauth_check )
kobo_support = kobo_support , registered_oauth = oauth_check )
try :
try :
content . allowed_tags = config . config_allowed_tags
content . allowed_tags = config . config_allowed_tags
@ -1864,17 +1927,17 @@ def _handle_new_user(to_save, content, languages, translations, kobo_support):
content . kobo_only_shelves_sync = to_save . get ( " kobo_only_shelves_sync " , 0 ) == " on "
content . kobo_only_shelves_sync = to_save . get ( " kobo_only_shelves_sync " , 0 ) == " on "
ub . session . add ( content )
ub . session . add ( content )
ub . session . commit ( )
ub . session . commit ( )
flash ( _ ( u " User ' %(user)s ' created " , user = content . name ) , category = " success " )
flash ( _ ( " User ' %(user)s ' created " , user = content . name ) , category = " success " )
log . debug ( " User {} created " . format ( content . name ) )
log . debug ( " User {} created " . format ( content . name ) )
return redirect ( url_for ( ' admin.admin ' ) )
return redirect ( url_for ( ' admin.admin ' ) )
except IntegrityError :
except IntegrityError :
ub . session . rollback ( )
ub . session . rollback ( )
log . error ( " Found an existing account for {} or {} " . format ( content . name , content . email ) )
log . error ( " Found an existing account for {} or {} " . format ( content . name , content . email ) )
flash ( _ ( " Found an existing account for this e-mail address or name." ) , category = " error " )
flash ( _ ( " Oops! An account already exists for this Email. or name." ) , category = " error " )
except OperationalError as e :
except OperationalError as e :
ub . session . rollback ( )
ub . session . rollback ( )
log . error_or_exception ( " Settings Database error: {} " . format ( e ) )
log . error_or_exception ( " Settings Database error: {} " . format ( e ) )
flash ( _ ( u" Database e rror: %(error)s . " , error = e . orig ) , category = " error " )
flash ( _ ( " Oops! Database E rror: %(error)s . " , error = e . orig ) , category = " error " )
def _delete_user ( content ) :
def _delete_user ( content ) :
@ -1902,10 +1965,10 @@ def _delete_user(content):
log . info ( " User {} deleted " . format ( content . name ) )
log . info ( " User {} deleted " . format ( content . name ) )
return _ ( " User ' %(nick)s ' deleted " , nick = content . name )
return _ ( " User ' %(nick)s ' deleted " , nick = content . name )
else :
else :
log . warning ( _ ( " Can ' t delete Guest User " ) )
# log.warning(_("Can't delete Guest User") )
raise Exception ( _ ( " Can ' t delete Guest User " ) )
raise Exception ( _ ( " Can ' t delete Guest User " ) )
else :
else :
log . warning ( " No admin user remaining, can ' t delete user " )
# log.warning("No admin user remaining, can't delete user" )
raise Exception ( _ ( " No admin user remaining, can ' t delete user " ) )
raise Exception ( _ ( " No admin user remaining, can ' t delete user " ) )
@ -1923,14 +1986,6 @@ def _handle_edit_user(to_save, content, languages, translations, kobo_support):
log . warning ( " No admin user remaining, can ' t remove admin role from {} " . format ( content . name ) )
log . warning ( " No admin user remaining, can ' t remove admin role from {} " . format ( content . name ) )
flash ( _ ( " No admin user remaining, can ' t remove admin role " ) , category = " error " )
flash ( _ ( " No admin user remaining, can ' t remove admin role " ) , category = " error " )
return redirect ( url_for ( ' admin.admin ' ) )
return redirect ( url_for ( ' admin.admin ' ) )
if to_save . get ( " password " ) :
content . password = generate_password_hash ( to_save [ " password " ] )
anonymous = content . is_anonymous
content . role = constants . selected_roles ( to_save )
if anonymous :
content . role | = constants . ROLE_ANONYMOUS
else :
content . role & = ~ constants . ROLE_ANONYMOUS
val = [ int ( k [ 5 : ] ) for k in to_save if k . startswith ( ' show_ ' ) ]
val = [ int ( k [ 5 : ] ) for k in to_save if k . startswith ( ' show_ ' ) ]
sidebar , __ = get_sidebar_config ( )
sidebar , __ = get_sidebar_config ( )
@ -1958,9 +2013,21 @@ def _handle_edit_user(to_save, content, languages, translations, kobo_support):
if to_save . get ( " locale " ) :
if to_save . get ( " locale " ) :
content . locale = to_save [ " locale " ]
content . locale = to_save [ " locale " ]
try :
try :
if to_save . get ( " email " , content . email ) != content . email :
anonymous = content . is_anonymous
content . email = check_email ( to_save [ " email " ] )
content . role = constants . selected_roles ( to_save )
# Query User name, if not existing, change
if anonymous :
content . role | = constants . ROLE_ANONYMOUS
else :
content . role & = ~ constants . ROLE_ANONYMOUS
if to_save . get ( " password " , " " ) :
content . password = generate_password_hash ( helper . valid_password ( to_save . get ( " password " , " " ) ) )
new_email = valid_email ( to_save . get ( " email " , content . email ) )
if not new_email :
raise Exception ( _ ( " Email can ' t be empty and has to be a valid Email " ) )
if new_email != content . email :
content . email = check_email ( new_email )
# Query username, if not existing, change
if to_save . get ( " name " , content . name ) != content . name :
if to_save . get ( " name " , content . name ) != content . name :
if to_save . get ( " name " ) == " Guest " :
if to_save . get ( " name " ) == " Guest " :
raise Exception ( _ ( " Guest Name can ' t be changed " ) )
raise Exception ( _ ( " Guest Name can ' t be changed " ) )
@ -1979,24 +2046,24 @@ def _handle_edit_user(to_save, content, languages, translations, kobo_support):
content = content ,
content = content ,
config = config ,
config = config ,
registered_oauth = oauth_check ,
registered_oauth = oauth_check ,
title = _ ( u " Edit User %(nick)s " , nick = content . name ) ,
title = _ ( " Edit User %(nick)s " , nick = content . name ) ,
page = " edituser " )
page = " edituser " )
try :
try :
ub . session_commit ( )
ub . session_commit ( )
flash ( _ ( u " User ' %(nick)s ' updated " , nick = content . name ) , category = " success " )
flash ( _ ( " User ' %(nick)s ' updated " , nick = content . name ) , category = " success " )
except IntegrityError as ex :
except IntegrityError as ex :
ub . session . rollback ( )
ub . session . rollback ( )
log . error ( " An unknown error occurred while changing user: {} " . format ( str ( ex ) ) )
log . error ( " An unknown error occurred while changing user: {} " . format ( str ( ex ) ) )
flash ( _ ( u " An unknown error occurred. Please try again later." ) , category = " error " )
flash ( _ ( " Oops! An unknown error occurred. Please try again later." ) , category = " error " )
except OperationalError as e :
except OperationalError as e :
ub . session . rollback ( )
ub . session . rollback ( )
log . error_or_exception ( " Settings Database error: {} " . format ( e ) )
log . error_or_exception ( " Settings Database error: {} " . format ( e ) )
flash ( _ ( u" Database e rror: %(error)s . " , error = e . orig ) , category = " error " )
flash ( _ ( " Oops! Database E rror: %(error)s . " , error = e . orig ) , category = " error " )
return " "
return " "
def extract_user_data_from_field ( user , field ) :
def extract_user_data_from_field ( user , field ) :
match = re . search ( field + r " =([ \ . \ d \ s \ w-]+) " , user , re . IGNORECASE | re . UNICODE )
match = re . search ( field + r " =([ @ \ . \ d \ s \ w-]+) " , user , re . IGNORECASE | re . UNICODE )
if match :
if match :
return match . group ( 1 )
return match . group ( 1 )
else :
else :