From 725fc658f8aecec30910101fb83b3539128814c8 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Sun, 14 Mar 2021 13:28:52 +0100 Subject: [PATCH] Some code cosmetics --- cps/comic.py | 70 ++++---- cps/config_sql.py | 11 +- cps/constants.py | 2 +- cps/helper.py | 104 ++++++----- cps/isoLanguages.py | 2 +- cps/oauth.py | 254 +++++++++++++-------------- cps/oauth_bb.py | 5 +- cps/server.py | 2 +- cps/services/SyncToken.py | 6 +- cps/static/css/caliBlur_override.css | 34 ++-- cps/static/css/kthoom.css | 5 +- cps/static/css/main.css | 2 - cps/static/css/style.css | 93 ++++++---- cps/static/js/caliBlur.js | 2 +- cps/static/js/filter_list.js | 4 +- cps/static/js/main.js | 4 +- cps/static/js/table.js | 1 + cps/tasks/upload.py | 1 - cps/ub.py | 119 ++++++++----- cps/usermanagement.py | 2 +- cps/web.py | 147 ++++++++-------- 21 files changed, 460 insertions(+), 410 deletions(-) diff --git a/cps/comic.py b/cps/comic.py index a25f9a51..c2b30197 100644 --- a/cps/comic.py +++ b/cps/comic.py @@ -74,6 +74,41 @@ def _cover_processing(tmp_file_name, img, extension): return tmp_cover_name +def _extract_Cover_from_archive(original_file_extension, tmp_file_name, rarExecutable): + cover_data = None + if original_file_extension.upper() == '.CBZ': + cf = zipfile.ZipFile(tmp_file_name) + for name in cf.namelist(): + ext = os.path.splitext(name) + if len(ext) > 1: + extension = ext[1].lower() + if extension in COVER_EXTENSIONS: + cover_data = cf.read(name) + break + elif original_file_extension.upper() == '.CBT': + cf = tarfile.TarFile(tmp_file_name) + for name in cf.getnames(): + ext = os.path.splitext(name) + if len(ext) > 1: + extension = ext[1].lower() + if extension in COVER_EXTENSIONS: + cover_data = cf.extractfile(name).read() + break + elif original_file_extension.upper() == '.CBR' and use_rarfile: + try: + rarfile.UNRAR_TOOL = rarExecutable + cf = rarfile.RarFile(tmp_file_name) + for name in cf.getnames(): + ext = os.path.splitext(name) + if len(ext) > 1: + extension = ext[1].lower() + if extension in COVER_EXTENSIONS: + cover_data = cf.read(name) + break + except Exception as e: + log.debug('Rarfile failed with error: %s', e) + return cover_data + def _extractCover(tmp_file_name, original_file_extension, rarExecutable): cover_data = extension = None @@ -87,37 +122,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable): cover_data = archive.getPage(index) break else: - if original_file_extension.upper() == '.CBZ': - cf = zipfile.ZipFile(tmp_file_name) - for name in cf.namelist(): - ext = os.path.splitext(name) - if len(ext) > 1: - extension = ext[1].lower() - if extension in COVER_EXTENSIONS: - cover_data = cf.read(name) - break - elif original_file_extension.upper() == '.CBT': - cf = tarfile.TarFile(tmp_file_name) - for name in cf.getnames(): - ext = os.path.splitext(name) - if len(ext) > 1: - extension = ext[1].lower() - if extension in COVER_EXTENSIONS: - cover_data = cf.extractfile(name).read() - break - elif original_file_extension.upper() == '.CBR' and use_rarfile: - try: - rarfile.UNRAR_TOOL = rarExecutable - cf = rarfile.RarFile(tmp_file_name) - for name in cf.getnames(): - ext = os.path.splitext(name) - if len(ext) > 1: - extension = ext[1].lower() - if extension in COVER_EXTENSIONS: - cover_data = cf.read(name) - break - except Exception as e: - log.debug('Rarfile failed with error: %s', e) + cover_data = _extract_Cover_from_archive(original_file_extension, tmp_file_name, rarExecutable) return _cover_processing(tmp_file_name, cover_data, extension) @@ -142,7 +147,8 @@ def get_comic_info(tmp_file_path, original_file_name, original_file_extension, r file_path=tmp_file_path, extension=original_file_extension, title=loadedMetadata.title or original_file_name, - author=" & ".join([credit["person"] for credit in loadedMetadata.credits if credit["role"] == "Writer"]) or u'Unknown', + author=" & ".join([credit["person"] + for credit in loadedMetadata.credits if credit["role"] == "Writer"]) or u'Unknown', cover=_extractCover(tmp_file_path, original_file_extension, rarExecutable), description=loadedMetadata.comments or "", tags="", diff --git a/cps/config_sql.py b/cps/config_sql.py index f7b300c0..60e17eb3 100644 --- a/cps/config_sql.py +++ b/cps/config_sql.py @@ -146,15 +146,16 @@ class _ConfigSQL(object): self.load() change = False - if self.config_converterpath == None: + if self.config_converterpath == None: # pylint: disable=access-member-before-definition change = True self.config_converterpath = autodetect_calibre_binary() - if self.config_kepubifypath == None: + if self.config_kepubifypath == None: # pylint: disable=access-member-before-definition + change = True self.config_kepubifypath = autodetect_kepubify_binary() - if self.config_rarfile_location == None: + if self.config_rarfile_location == None: # pylint: disable=access-member-before-definition change = True self.config_rarfile_location = autodetect_unrar_binary() if change: @@ -181,7 +182,8 @@ class _ConfigSQL(object): return None return self.config_keyfile - def get_config_ipaddress(self): + @staticmethod + def get_config_ipaddress(): return cli.ipadress or "" def _has_role(self, role_flag): @@ -299,6 +301,7 @@ class _ConfigSQL(object): have_metadata_db = os.path.isfile(db_file) self.db_configured = have_metadata_db constants.EXTENSIONS_UPLOAD = [x.lstrip().rstrip().lower() for x in self.config_upload_formats.split(',')] + # pylint: disable=access-member-before-definition logfile = logger.setup(self.config_logfile, self.config_log_level) if logfile != self.config_logfile: log.warning("Log path %s not valid, falling back to default", self.config_logfile) diff --git a/cps/constants.py b/cps/constants.py index ac48a5b8..200bec8d 100644 --- a/cps/constants.py +++ b/cps/constants.py @@ -104,7 +104,7 @@ LDAP_AUTH_SIMPLE = 0 DEFAULT_MAIL_SERVER = "mail.example.org" -DEFAULT_PASSWORD = "admin123" +DEFAULT_PASSWORD = "admin123" # nosec # noqa DEFAULT_PORT = 8083 env_CALIBRE_PORT = os.environ.get("CALIBRE_PORT", DEFAULT_PORT) try: diff --git a/cps/helper.py b/cps/helper.py index 92cdb2fb..88c0550b 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -134,63 +134,71 @@ def send_registration_mail(e_mail, user_name, default_password, resend=False): taskMessage=_(u"Registration e-mail for user: %(name)s", name=user_name), text=txt )) - return +def check_send_to_kindle_without_converter(entry): + bookformats = list() + # no converter - only for mobi and pdf formats + for ele in iter(entry.data): + if ele.uncompressed_size < config.mail_size: + if 'MOBI' in ele.format: + bookformats.append({'format': 'Mobi', + 'convert': 0, + 'text': _('Send %(format)s to Kindle', format='Mobi')}) + if 'PDF' in ele.format: + bookformats.append({'format': 'Pdf', + 'convert': 0, + 'text': _('Send %(format)s to Kindle', format='Pdf')}) + if 'AZW' in ele.format: + bookformats.append({'format': 'Azw', + 'convert': 0, + 'text': _('Send %(format)s to Kindle', format='Azw')}) + return bookformats + +def check_send_to_kindle_with_converter(entry): + bookformats = list() + formats = list() + for ele in iter(entry.data): + if ele.uncompressed_size < config.mail_size: + formats.append(ele.format) + if 'MOBI' in formats: + bookformats.append({'format': 'Mobi', + 'convert': 0, + 'text': _('Send %(format)s to Kindle', format='Mobi')}) + if 'AZW' in formats: + bookformats.append({'format': 'Azw', + 'convert': 0, + 'text': _('Send %(format)s to Kindle', format='Azw')}) + if 'PDF' in formats: + bookformats.append({'format': 'Pdf', + 'convert': 0, + 'text': _('Send %(format)s to Kindle', format='Pdf')}) + if 'EPUB' in formats and 'MOBI' not in formats: + bookformats.append({'format': 'Mobi', + 'convert': 1, + 'text': _('Convert %(orig)s to %(format)s and send to Kindle', + orig='Epub', + format='Mobi')}) + if 'AZW3' in formats and not 'MOBI' in formats: + bookformats.append({'format': 'Mobi', + 'convert': 2, + 'text': _('Convert %(orig)s to %(format)s and send to Kindle', + orig='Azw3', + format='Mobi')}) + return bookformats + + def check_send_to_kindle(entry): """ returns all available book formats for sending to Kindle """ if len(entry.data): - bookformats = list() if not config.config_converterpath: - # no converter - only for mobi and pdf formats - for ele in iter(entry.data): - if ele.uncompressed_size < config.mail_size: - if 'MOBI' in ele.format: - bookformats.append({'format': 'Mobi', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Mobi')}) - if 'PDF' in ele.format: - bookformats.append({'format': 'Pdf', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Pdf')}) - if 'AZW' in ele.format: - bookformats.append({'format': 'Azw', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Azw')}) + book_formats = check_send_to_kindle_with_converter(entry) else: - formats = list() - for ele in iter(entry.data): - if ele.uncompressed_size < config.mail_size: - formats.append(ele.format) - if 'MOBI' in formats: - bookformats.append({'format': 'Mobi', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Mobi')}) - if 'AZW' in formats: - bookformats.append({'format': 'Azw', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Azw')}) - if 'PDF' in formats: - bookformats.append({'format': 'Pdf', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Pdf')}) - if config.config_converterpath: - if 'EPUB' in formats and 'MOBI' not in formats: - bookformats.append({'format': 'Mobi', - 'convert':1, - 'text': _('Convert %(orig)s to %(format)s and send to Kindle', - orig='Epub', - format='Mobi')}) - if 'AZW3' in formats and not 'MOBI' in formats: - bookformats.append({'format': 'Mobi', - 'convert': 2, - 'text': _('Convert %(orig)s to %(format)s and send to Kindle', - orig='Azw3', - format='Mobi')}) - return bookformats + book_formats = check_send_to_kindle_with_converter(entry) + return book_formats else: log.error(u'Cannot find book entry %d', entry.id) return None @@ -742,7 +750,7 @@ def format_runtime(runtime): # helper function to apply localize status information in tasklist entries def render_task_status(tasklist): renderedtasklist = list() - for num, user, added, task in tasklist: + for __, user, added, task in tasklist: if user == current_user.nickname or current_user.role_admin(): ret = {} if task.start_time: diff --git a/cps/isoLanguages.py b/cps/isoLanguages.py index 4c0aefc3..896d4faf 100644 --- a/cps/isoLanguages.py +++ b/cps/isoLanguages.py @@ -71,7 +71,7 @@ def get_valid_language_codes(locale, language_names, remainder=None): languages = list() if "" in language_names: language_names.remove("") - for k, v in get_language_names(locale).items(): + for k, __ in get_language_names(locale).items(): if k in language_names: languages.append(k) language_names.remove(k) diff --git a/cps/oauth.py b/cps/oauth.py index 67ef2703..a8995180 100644 --- a/cps/oauth.py +++ b/cps/oauth.py @@ -19,7 +19,6 @@ from __future__ import division, print_function, unicode_literals from flask import session - try: from flask_dance.consumer.backend.sqla import SQLAlchemyBackend, first, _get_real_user from sqlalchemy.orm.exc import NoResultFound @@ -34,134 +33,131 @@ except ImportError: except ImportError: pass -try: - class OAuthBackend(SQLAlchemyBackend): - """ - Stores and retrieves OAuth tokens using a relational database through - the `SQLAlchemy`_ ORM. - - .. _SQLAlchemy: https://www.sqlalchemy.org/ - """ - def __init__(self, model, session, provider_id, - user=None, user_id=None, user_required=None, anon_user=None, - cache=None): - self.provider_id = provider_id - super(OAuthBackend, self).__init__(model, session, user, user_id, user_required, anon_user, cache) - - def get(self, blueprint, user=None, user_id=None): - if self.provider_id + '_oauth_token' in session and session[self.provider_id + '_oauth_token'] != '': - return session[self.provider_id + '_oauth_token'] - # check cache - cache_key = self.make_cache_key(blueprint=blueprint, user=user, user_id=user_id) - token = self.cache.get(cache_key) - if token: - return token - - # if not cached, make database queries - query = ( - self.session.query(self.model) - .filter_by(provider=self.provider_id) - ) - uid = first([user_id, self.user_id, blueprint.config.get("user_id")]) - u = first(_get_real_user(ref, self.anon_user) - for ref in (user, self.user, blueprint.config.get("user"))) - - use_provider_user_id = False - if self.provider_id + '_oauth_user_id' in session and session[self.provider_id + '_oauth_user_id'] != '': - query = query.filter_by(provider_user_id=session[self.provider_id + '_oauth_user_id']) - use_provider_user_id = True - - if self.user_required and not u and not uid and not use_provider_user_id: - # raise ValueError("Cannot get OAuth token without an associated user") - return None - # check for user ID - if hasattr(self.model, "user_id") and uid: - query = query.filter_by(user_id=uid) - # check for user (relationship property) - elif hasattr(self.model, "user") and u: - query = query.filter_by(user=u) - # if we have the property, but not value, filter by None - elif hasattr(self.model, "user_id"): - query = query.filter_by(user_id=None) - # run query - try: - token = query.one().token - except NoResultFound: - token = None - - # cache the result - self.cache.set(cache_key, token) +class OAuthBackend(SQLAlchemyBackend): + """ + Stores and retrieves OAuth tokens using a relational database through + the `SQLAlchemy`_ ORM. + + .. _SQLAlchemy: https://www.sqlalchemy.org/ + """ + def __init__(self, model, session, provider_id, + user=None, user_id=None, user_required=None, anon_user=None, + cache=None): + self.provider_id = provider_id + super(OAuthBackend, self).__init__(model, session, user, user_id, user_required, anon_user, cache) + + def get(self, blueprint, user=None, user_id=None): + if self.provider_id + '_oauth_token' in session and session[self.provider_id + '_oauth_token'] != '': + return session[self.provider_id + '_oauth_token'] + # check cache + cache_key = self.make_cache_key(blueprint=blueprint, user=user, user_id=user_id) + token = self.cache.get(cache_key) + if token: return token - def set(self, blueprint, token, user=None, user_id=None): - uid = first([user_id, self.user_id, blueprint.config.get("user_id")]) - u = first(_get_real_user(ref, self.anon_user) - for ref in (user, self.user, blueprint.config.get("user"))) - - if self.user_required and not u and not uid: - raise ValueError("Cannot set OAuth token without an associated user") - - # if there was an existing model, delete it - existing_query = ( - self.session.query(self.model) - .filter_by(provider=self.provider_id) - ) - # check for user ID - has_user_id = hasattr(self.model, "user_id") - if has_user_id and uid: - existing_query = existing_query.filter_by(user_id=uid) - # check for user (relationship property) - has_user = hasattr(self.model, "user") - if has_user and u: - existing_query = existing_query.filter_by(user=u) - # queue up delete query -- won't be run until commit() - existing_query.delete() - # create a new model for this token - kwargs = { - "provider": self.provider_id, - "token": token, - } - if has_user_id and uid: - kwargs["user_id"] = uid - if has_user and u: - kwargs["user"] = u - self.session.add(self.model(**kwargs)) - # commit to delete and add simultaneously - self.session.commit() - # invalidate cache - self.cache.delete(self.make_cache_key( - blueprint=blueprint, user=user, user_id=user_id - )) - - def delete(self, blueprint, user=None, user_id=None): - query = ( - self.session.query(self.model) - .filter_by(provider=self.provider_id) - ) - uid = first([user_id, self.user_id, blueprint.config.get("user_id")]) - u = first(_get_real_user(ref, self.anon_user) - for ref in (user, self.user, blueprint.config.get("user"))) - - if self.user_required and not u and not uid: - raise ValueError("Cannot delete OAuth token without an associated user") - - # check for user ID - if hasattr(self.model, "user_id") and uid: - query = query.filter_by(user_id=uid) - # check for user (relationship property) - elif hasattr(self.model, "user") and u: - query = query.filter_by(user=u) - # if we have the property, but not value, filter by None - elif hasattr(self.model, "user_id"): - query = query.filter_by(user_id=None) - # run query - query.delete() - self.session.commit() - # invalidate cache - self.cache.delete(self.make_cache_key( - blueprint=blueprint, user=user, user_id=user_id, - )) - -except Exception: - pass + # if not cached, make database queries + query = ( + self.session.query(self.model) + .filter_by(provider=self.provider_id) + ) + uid = first([user_id, self.user_id, blueprint.config.get("user_id")]) + u = first(_get_real_user(ref, self.anon_user) + for ref in (user, self.user, blueprint.config.get("user"))) + + use_provider_user_id = False + if self.provider_id + '_oauth_user_id' in session and session[self.provider_id + '_oauth_user_id'] != '': + query = query.filter_by(provider_user_id=session[self.provider_id + '_oauth_user_id']) + use_provider_user_id = True + + if self.user_required and not u and not uid and not use_provider_user_id: + # raise ValueError("Cannot get OAuth token without an associated user") + return None + # check for user ID + if hasattr(self.model, "user_id") and uid: + query = query.filter_by(user_id=uid) + # check for user (relationship property) + elif hasattr(self.model, "user") and u: + query = query.filter_by(user=u) + # if we have the property, but not value, filter by None + elif hasattr(self.model, "user_id"): + query = query.filter_by(user_id=None) + # run query + try: + token = query.one().token + except NoResultFound: + token = None + + # cache the result + self.cache.set(cache_key, token) + + return token + + def set(self, blueprint, token, user=None, user_id=None): + uid = first([user_id, self.user_id, blueprint.config.get("user_id")]) + u = first(_get_real_user(ref, self.anon_user) + for ref in (user, self.user, blueprint.config.get("user"))) + + if self.user_required and not u and not uid: + raise ValueError("Cannot set OAuth token without an associated user") + + # if there was an existing model, delete it + existing_query = ( + self.session.query(self.model) + .filter_by(provider=self.provider_id) + ) + # check for user ID + has_user_id = hasattr(self.model, "user_id") + if has_user_id and uid: + existing_query = existing_query.filter_by(user_id=uid) + # check for user (relationship property) + has_user = hasattr(self.model, "user") + if has_user and u: + existing_query = existing_query.filter_by(user=u) + # queue up delete query -- won't be run until commit() + existing_query.delete() + # create a new model for this token + kwargs = { + "provider": self.provider_id, + "token": token, + } + if has_user_id and uid: + kwargs["user_id"] = uid + if has_user and u: + kwargs["user"] = u + self.session.add(self.model(**kwargs)) + # commit to delete and add simultaneously + self.session.commit() + # invalidate cache + self.cache.delete(self.make_cache_key( + blueprint=blueprint, user=user, user_id=user_id + )) + + def delete(self, blueprint, user=None, user_id=None): + query = ( + self.session.query(self.model) + .filter_by(provider=self.provider_id) + ) + uid = first([user_id, self.user_id, blueprint.config.get("user_id")]) + u = first(_get_real_user(ref, self.anon_user) + for ref in (user, self.user, blueprint.config.get("user"))) + + if self.user_required and not u and not uid: + raise ValueError("Cannot delete OAuth token without an associated user") + + # check for user ID + if hasattr(self.model, "user_id") and uid: + query = query.filter_by(user_id=uid) + # check for user (relationship property) + elif hasattr(self.model, "user") and u: + query = query.filter_by(user=u) + # if we have the property, but not value, filter by None + elif hasattr(self.model, "user_id"): + query = query.filter_by(user_id=None) + # run query + query.delete() + self.session.commit() + # invalidate cache + self.cache.delete(self.make_cache_key( + blueprint=blueprint, user=user, user_id=user_id, + )) diff --git a/cps/oauth_bb.py b/cps/oauth_bb.py index b17d38c7..3fee2e04 100644 --- a/cps/oauth_bb.py +++ b/cps/oauth_bb.py @@ -35,7 +35,10 @@ from sqlalchemy.orm.exc import NoResultFound from . import constants, logger, config, app, ub -from .oauth import OAuthBackend, backend_resultcode +try: + from .oauth import OAuthBackend, backend_resultcode +except NameError: + pass oauth_check = {} diff --git a/cps/server.py b/cps/server.py index bf564108..675e6af0 100644 --- a/cps/server.py +++ b/cps/server.py @@ -137,7 +137,7 @@ class WebServer(object): return sock, _readable_listen_address(*address) - + @staticmethod def _get_args_for_reloading(self): """Determine how the script was executed, and return the args needed to execute it again in a new process. diff --git a/cps/services/SyncToken.py b/cps/services/SyncToken.py index 26eb396c..da5d93a3 100644 --- a/cps/services/SyncToken.py +++ b/cps/services/SyncToken.py @@ -64,7 +64,7 @@ class SyncToken: books_last_modified: Datetime representing the last modified book that the device knows about. """ - SYNC_TOKEN_HEADER = "x-kobo-synctoken" + SYNC_TOKEN_HEADER = "x-kobo-synctoken" # nosec VERSION = "1-1-0" LAST_MODIFIED_ADDED_VERSION = "1-1-0" MIN_VERSION = "1-0-0" @@ -91,7 +91,7 @@ class SyncToken: def __init__( self, - raw_kobo_store_token="", + raw_kobo_store_token="", # nosec books_last_created=datetime.min, books_last_modified=datetime.min, archive_last_modified=datetime.min, @@ -110,7 +110,7 @@ class SyncToken: @staticmethod def from_headers(headers): sync_token_header = headers.get(SyncToken.SYNC_TOKEN_HEADER, "") - if sync_token_header == "": + if sync_token_header == "": # nosec return SyncToken() # On the first sync from a Kobo device, we may receive the SyncToken diff --git a/cps/static/css/caliBlur_override.css b/cps/static/css/caliBlur_override.css index 00ba3cca..2f3dcb58 100644 --- a/cps/static/css/caliBlur_override.css +++ b/cps/static/css/caliBlur_override.css @@ -1,22 +1,24 @@ -body.serieslist.grid-view div.container-fluid>div>div.col-sm-10:before{ - display: none; +body.serieslist.grid-view div.container-fluid > div > div.col-sm-10:before{ + display: none; } + .cover .badge{ - position: absolute; - top: 0; - left: 0; - color: #fff; - background-color: #cc7b19; - border-radius: 0; - padding: 0 8px; - box-shadow: 0 0 4px rgba(0,0,0,.6); - line-height: 24px; + position: absolute; + top: 0; + left: 0; + color: #fff; + background-color: #cc7b19; + border-radius: 0; + padding: 0 8px; + box-shadow: 0 0 4px rgba(0, 0, 0, .6); + line-height: 24px; } -.cover{ - box-shadow: 0 0 4px rgba(0,0,0,.6); + +.cover { + box-shadow: 0 0 4px rgba(0, 0, 0, .6); } -.cover .read{ - padding: 0 0px; - line-height: 15px; +.cover .read { + padding: 0px 0px; + line-height: 15px; } diff --git a/cps/static/css/kthoom.css b/cps/static/css/kthoom.css index cc38740b..233cfe94 100644 --- a/cps/static/css/kthoom.css +++ b/cps/static/css/kthoom.css @@ -33,7 +33,6 @@ body { position: relative; cursor: pointer; padding: 4px; - transition: all 0.2s ease; } @@ -45,7 +44,7 @@ body { #sidebar a.active, #sidebar a.active img + span { - background-color: #45B29D; + background-color: #45b29d; } #sidebar li img { @@ -99,7 +98,7 @@ body { background-color: #ccc; } -#progress .bar-read { +#progress .bar-read { color: #fff; background-color: #45b29d; } diff --git a/cps/static/css/main.css b/cps/static/css/main.css index 2831a0fd..c65dd0f7 100644 --- a/cps/static/css/main.css +++ b/cps/static/css/main.css @@ -490,7 +490,6 @@ input:-moz-placeholder { color: #454545; } position: fixed; top: 50%; left: 50%; - // width: 50%; width: 630px; height: auto; z-index: 2000; @@ -593,7 +592,6 @@ input:-moz-placeholder { color: #454545; } } .md-content > .closer { - //font-size: 18px; position: absolute; right: 0; top: 0; diff --git a/cps/static/css/style.css b/cps/static/css/style.css index 8c99aaa0..e5259e32 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -1,7 +1,7 @@ .tooltip.bottom .tooltip-inner { font-size: 13px; - font-family: Open Sans Semibold,Helvetica Neue,Helvetica,Arial,sans-serif; + font-family: Open Sans Semibold, Helvetica Neue, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; padding: 3px 10px; @@ -48,25 +48,25 @@ body { body h2 { font-weight: normal; - color:#444; + color: #444; } -a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d; } +a, .danger, .book-remove, .editable-empty, .editable-empty:hover { color: #45b29d; } .book-remove:hover { color: #23527c; } .btn-default a { color: #444; } .btn-default a:hover { - color: #45b29d; - text-decoration: None; + color: #45b29d; + text-decoration: None; } .btn-default:hover { - color: #45b29d; + color: #45b29d; } -.editable-click, a.editable-click, a.editable-click:hover { border-bottom: None; } +.editable-click, a.editable-click, a.editable-click:hover { border-bottom: None; } .navigation .nav-head { text-transform: uppercase; @@ -119,7 +119,7 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d max-height: 100%; } -.container-fluid .discover{ margin-bottom: 50px; } +.container-fluid .discover { margin-bottom: 50px; } .container-fluid .new-books { border-top: 1px solid #ccc; } .container-fluid .new-books h2 { margin: 50px 0 0 0; } .container-fluid .book { @@ -174,9 +174,10 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d .container-fluid .book .meta .rating { margin-top: 5px; } .rating .glyphicon-star-empty { color: #444; } .rating .glyphicon-star.good { color: #444; } -.rating-clear .glyphicon-remove { color: #333 } +.rating-clear .glyphicon-remove { color: #333; } -.container-fluid .author .author-hidden, .container-fluid .author .author-hidden-divider { display: none; } +.container-fluid .author .author-hidden, +.container-fluid .author .author-hidden-divider { display: none; } .navbar-brand { font-family: 'Grand Hotel', cursive; @@ -190,7 +191,7 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d border-top: 1px solid #ccc; } -.more-stuff>li { margin-bottom: 10px; } +.more-stuff > li { margin-bottom: 10px; } .navbar-collapse.in .navbar-nav { margin: 0; } span.glyphicon.glyphicon-tags { @@ -211,17 +212,17 @@ span.glyphicon.glyphicon-tags { box-shadow: 0 5px 8px -6px #777; } -.navbar-default .navbar-toggle .icon-bar {background-color: #000; } -.navbar-default .navbar-toggle {border-color: #000; } +.navbar-default .navbar-toggle .icon-bar { background-color: #000; } +.navbar-default .navbar-toggle { border-color: #000; } .cover { margin-bottom: 10px; } .cover .badge{ - position: absolute; - top: 2px; - left: 2px; - color: #000; - border-radius: 10px; - background-color: #fff; + position: absolute; + top: 2px; + left: 2px; + color: #000; + border-radius: 10px; + background-color: #fff; } .cover .read{ left: auto; @@ -231,14 +232,14 @@ span.glyphicon.glyphicon-tags { display: inline-block; padding: 2px; } -.cover-height { max-height: 100px;} +.cover-height { max-height: 100px; } .col-sm-2 a .cover-small { margin: 5px; max-height: 200px; } -.btn-file {position: relative; overflow: hidden;} +.btn-file { position: relative; overflow: hidden; } .btn-file input[type=file] { position: absolute; @@ -256,22 +257,42 @@ span.glyphicon.glyphicon-tags { display: block; } -.btn-toolbar .btn,.discover .btn { margin-bottom: 5px; } -.button-link {color: #fff; } -.btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary{ background-color: #1C5484; } -.btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { background-color: #89B9E2; } -.btn-toolbar>.btn+.btn, .btn-toolbar>.btn-group+.btn, .btn-toolbar>.btn+.btn-group, .btn-toolbar>.btn-group+.btn-group { margin-left: 0; } -.panel-body {background-color: #f5f5f5; } -.spinner {margin: 0 41%; } -.spinner2 {margin: 0 41%; } +.btn-toolbar .btn, +.discover .btn { margin-bottom: 5px; } +.button-link { color: #fff; } + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { background-color: #1c5484; } + +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { background-color: #89B9E2; } + +.btn-toolbar > .btn+.btn, +.btn-toolbar > .btn-group+.btn, +.btn-toolbar > .btn+.btn-group, +.btn-toolbar > .btn-group+.btn-group { margin-left: 0; } + +.panel-body { background-color: #f5f5f5; } +.spinner { margin: 0 41%; } +.spinner2 { margin: 0 41%; } .intend-form { margin-left:20px; } -table .bg-dark-danger {background-color: #d9534f; color: #fff; } -table .bg-dark-danger a {color: #fff; } -table .bg-dark-danger:hover {background-color: #c9302c; } -table .bg-primary:hover {background-color: #1C5484; } -table .bg-primary a {color: #fff; } -.block-label {display: block;} -.fake-input {position: absolute; pointer-events: none; top: 0; } +table .bg-dark-danger { background-color: #d9534f; color: #fff; } +table .bg-dark-danger a { color: #fff; } +table .bg-dark-danger:hover { background-color: #c9302c; } +table .bg-primary:hover { background-color: #1c5484; } +table .bg-primary a { color: #fff; } +.block-label { display: block; } +.fake-input { position: absolute; pointer-events: none; top: 0; } input.pill { position: absolute; opacity: 0; } diff --git a/cps/static/js/caliBlur.js b/cps/static/js/caliBlur.js index 7769a4ea..f7ab8539 100644 --- a/cps/static/js/caliBlur.js +++ b/cps/static/js/caliBlur.js @@ -677,7 +677,7 @@ $(".navbar-collapse.collapse.in").before('') // Get rid of leading white space recentlyAdded = $("#nav_new a:contains('Recently')").text().trim(); $("#nav_new a:contains('Recently')").contents().filter(function () { - return this.nodeType == 3 + return this.nodeType === 3 }).each(function () { this.textContent = this.textContent.replace(" Recently Added", recentlyAdded); }); diff --git a/cps/static/js/filter_list.js b/cps/static/js/filter_list.js index 679c5359..ef7639fa 100644 --- a/cps/static/js/filter_list.js +++ b/cps/static/js/filter_list.js @@ -88,7 +88,7 @@ $("#desc").click(function() { // Find count of middle element var count = $(".row:visible").length; if (count > 20) { - middle = parseInt(count / 2) + (count % 2); + middle = parseInt(count / 2, 10) + (count % 2); //var middle = parseInt(count / 2) + (count % 2); // search for the middle of all visible elements @@ -135,7 +135,7 @@ $("#asc").click(function() { // Find count of middle element var count = $(".row:visible").length; if (count > 20) { - var middle = parseInt(count / 2) + (count % 2); + var middle = parseInt(count / 2, 10) + (count % 2); //var middle = parseInt(count / 2) + (count % 2); // search for the middle of all visible elements diff --git a/cps/static/js/main.js b/cps/static/js/main.js index e0f2dfe7..13484bb3 100644 --- a/cps/static/js/main.js +++ b/cps/static/js/main.js @@ -38,10 +38,10 @@ $(document).on("change", "input[type=\"checkbox\"][data-control]", function () { $(document).on("change", "select[data-control]", function() { var $this = $(this); var name = $this.data("control"); - var showOrHide = parseInt($this.val()); + var showOrHide = parseInt($this.val(), 10); // var showOrHideLast = $("#" + name + " option:last").val() for (var i = 0; i < $(this)[0].length; i++) { - var element = parseInt($(this)[0][i].value); + var element = parseInt($(this)[0][i].value, 10); if (element === showOrHide) { $("[data-related^=" + name + "][data-related*=-" + element + "]").show(); } else { diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 2cf4c6a2..17e4915f 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -16,6 +16,7 @@ */ /* exported TableActions, RestrictionActions, EbookActions, responseHandler */ +/* global getPath, ConfirmDialog */ var selections = []; diff --git a/cps/tasks/upload.py b/cps/tasks/upload.py index ce2cb07b..d7ef34c2 100644 --- a/cps/tasks/upload.py +++ b/cps/tasks/upload.py @@ -12,7 +12,6 @@ class TaskUpload(CalibreTask): def run(self, worker_thread): """Upload task doesn't have anything to do, it's simply a way to add information to the task list""" - pass @property def name(self): diff --git a/cps/ub.py b/cps/ub.py index 1969ef53..b97e0670 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -138,15 +138,15 @@ class UserBase: mct = self.allowed_column_value or "" return [t.strip() for t in mct.split(",")] - def get_view_property(self, page, property): + def get_view_property(self, page, prop): if not self.view_settings.get(page): return None - return self.view_settings[page].get(property) + return self.view_settings[page].get(prop) - def set_view_property(self, page, property, value): + def set_view_property(self, page, prop, value): if not self.view_settings.get(page): self.view_settings[page] = dict() - self.view_settings[page][property] = value + self.view_settings[page][prop] = value try: flag_modified(self, "view_settings") except AttributeError: @@ -437,11 +437,8 @@ class RemoteAuthToken(Base): return '' % self.id -# Migrate database to current version, has to be updated after every database change. Currently migration from -# everywhere to current should work. Migration is done by checking if relevant columns are existing, and than adding -# rows with SQL commands -def migrate_Database(session): - engine = session.bind +# Add missing tables during migration of database +def add_missing_tables(engine, session): if not engine.dialect.has_table(engine.connect(), "book_read_link"): ReadBook.__table__.create(bind=engine) if not engine.dialect.has_table(engine.connect(), "bookmark"): @@ -459,6 +456,10 @@ def migrate_Database(session): with engine.connect() as conn: conn.execute("insert into registration (domain, allow) values('%.%',1)") session.commit() + + +# migrate all settings missing in registration table +def migrate_registration_table(engine, session): try: session.query(exists().where(Registration.allow)).scalar() session.commit() @@ -468,27 +469,29 @@ def migrate_Database(session): conn.execute("update registration set 'allow' = 1") session.commit() try: - session.query(exists().where(RemoteAuthToken.token_type)).scalar() - session.commit() - except exc.OperationalError: # Database is not compatible, some columns are missing - with engine.connect() as conn: - conn.execute("ALTER TABLE remote_auth_token ADD column 'token_type' INTEGER DEFAULT 0") - conn.execute("update remote_auth_token set 'token_type' = 0") - session.commit() + # Handle table exists, but no content + cnt = session.query(Registration).count() + if not cnt: + with engine.connect() as conn: + conn.execute("insert into registration (domain, allow) values('%.%',1)") + session.commit() + except exc.OperationalError: # Database is not writeable + print('Settings database is not writeable. Exiting...') + sys.exit(2) + + +# Remove login capability of user Guest +def migrate_guest_password(engine, session): try: - session.query(exists().where(ReadBook.read_status)).scalar() - except exc.OperationalError: with engine.connect() as conn: - conn.execute("ALTER TABLE book_read_link ADD column 'read_status' INTEGER DEFAULT 0") - conn.execute("UPDATE book_read_link SET 'read_status' = 1 WHERE is_read") - conn.execute("ALTER TABLE book_read_link ADD column 'last_modified' DATETIME") - conn.execute("ALTER TABLE book_read_link ADD column 'last_time_started_reading' DATETIME") - conn.execute("ALTER TABLE book_read_link ADD column 'times_started_reading' INTEGER DEFAULT 0") + conn.execute("UPDATE user SET password='' where nickname = 'Guest' and password !=''") session.commit() - test = session.query(ReadBook).filter(ReadBook.last_modified == None).all() - for book in test: - book.last_modified = datetime.datetime.utcnow() - session.commit() + except exc.OperationalError: + print('Settings database is not writeable. Exiting...') + sys.exit(2) + + +def migrate_shelfs(engine, session): try: session.query(exists().where(Shelf.uuid)).scalar() except exc.OperationalError: @@ -504,22 +507,51 @@ def migrate_Database(session): for book_shelf in session.query(BookShelf).all(): book_shelf.date_added = datetime.datetime.now() session.commit() - try: - # Handle table exists, but no content - cnt = session.query(Registration).count() - if not cnt: - with engine.connect() as conn: - conn.execute("insert into registration (domain, allow) values('%.%',1)") - session.commit() - except exc.OperationalError: # Database is not writeable - print('Settings database is not writeable. Exiting...') - sys.exit(2) try: session.query(exists().where(BookShelf.order)).scalar() except exc.OperationalError: # Database is not compatible, some columns are missing with engine.connect() as conn: conn.execute("ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1") session.commit() + + +def migrate_readBook(engine, session): + try: + session.query(exists().where(ReadBook.read_status)).scalar() + except exc.OperationalError: + with engine.connect() as conn: + conn.execute("ALTER TABLE book_read_link ADD column 'read_status' INTEGER DEFAULT 0") + conn.execute("UPDATE book_read_link SET 'read_status' = 1 WHERE is_read") + conn.execute("ALTER TABLE book_read_link ADD column 'last_modified' DATETIME") + conn.execute("ALTER TABLE book_read_link ADD column 'last_time_started_reading' DATETIME") + conn.execute("ALTER TABLE book_read_link ADD column 'times_started_reading' INTEGER DEFAULT 0") + session.commit() + test = session.query(ReadBook).filter(ReadBook.last_modified == None).all() + for book in test: + book.last_modified = datetime.datetime.utcnow() + session.commit() + + +def migrate_remoteAuthToken(engine, session): + try: + session.query(exists().where(RemoteAuthToken.token_type)).scalar() + session.commit() + except exc.OperationalError: # Database is not compatible, some columns are missing + with engine.connect() as conn: + conn.execute("ALTER TABLE remote_auth_token ADD column 'token_type' INTEGER DEFAULT 0") + conn.execute("update remote_auth_token set 'token_type' = 0") + session.commit() + +# Migrate database to current version, has to be updated after every database change. Currently migration from +# everywhere to current should work. Migration is done by checking if relevant columns are existing, and than adding +# rows with SQL commands +def migrate_Database(session): + engine = session.bind + add_missing_tables(engine, session) + migrate_registration_table(engine, session) + migrate_readBook(engine, session) + migrate_remoteAuthToken(engine, session) + migrate_shelfs(engine, session) try: create = False session.query(exists().where(User.sidebar_view)).scalar() @@ -578,8 +610,7 @@ def migrate_Database(session): "locale VARCHAR(2)," "sidebar_view INTEGER," "default_language VARCHAR(3)," - # "series_view VARCHAR(10)," - "view_settings VARCHAR," + "view_settings VARCHAR," "UNIQUE (nickname)," "UNIQUE (email))") conn.execute("INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale," @@ -590,15 +621,7 @@ def migrate_Database(session): conn.execute("DROP TABLE user") conn.execute("ALTER TABLE user_id RENAME TO user") session.commit() - - # Remove login capability of user Guest - try: - with engine.connect() as conn: - conn.execute("UPDATE user SET password='' where nickname = 'Guest' and password !=''") - session.commit() - except exc.OperationalError: - print('Settings database is not writeable. Exiting...') - sys.exit(2) + migrate_guest_password(engine, session) def clean_database(session): diff --git a/cps/usermanagement.py b/cps/usermanagement.py index 3a00f34d..cdba4d98 100644 --- a/cps/usermanagement.py +++ b/cps/usermanagement.py @@ -72,7 +72,7 @@ def load_user_from_request(request): def load_user_from_auth_header(header_val): if header_val.startswith('Basic '): header_val = header_val.replace('Basic ', '', 1) - basic_username = basic_password = '' + basic_username = basic_password = '' # nosec try: header_val = base64.b64decode(header_val).decode('utf-8') basic_username = header_val.split(':')[0] diff --git a/cps/web.py b/cps/web.py index 09744baf..0349b332 100644 --- a/cps/web.py +++ b/cps/web.py @@ -216,7 +216,7 @@ def update_view(): for param in to_save[element]: current_user.set_view_property(element, param, to_save[element][param]) except Exception as e: - log.error("Could not save view_settings: %r %r: e", request, to_save, e) + log.error("Could not save view_settings: %r %r: %e", request, to_save, e) return "Invalid request", 400 return "1", 200 @@ -340,7 +340,7 @@ def get_matching_tags(): return json_dumps -def render_books_list(data, sort, book_id, page): +def get_sort_function(sort, data): order = [db.Books.timestamp.desc()] if sort == 'stored': sort = current_user.get_view_property(data, 'stored') @@ -366,6 +366,11 @@ def render_books_list(data, sort, book_id, page): order = [db.Books.series_index.asc()] if sort == 'seriesdesc': order = [db.Books.series_index.desc()] + return order + + +def render_books_list(data, sort, book_id, page): + order = get_sort_function(sort, data) if data == "rated": if current_user.check_visibility(constants.SIDEBAR_BEST_RATED): @@ -453,18 +458,11 @@ def render_hot_books(page): def render_downloaded_books(page, order): if current_user.check_visibility(constants.SIDEBAR_DOWNLOAD): - # order = order or [] if current_user.show_detail_random(): random = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()) \ .order_by(func.random()).limit(config.config_random_books) else: random = false() - # off = int(int(config.config_books_per_page) * (page - 1)) - '''entries, random, pagination = calibre_db.fill_indexpage(page, 0, - db.Books, - db_filter, - order, - ub.ReadBook, db.Books.id==ub.ReadBook.book_id)''' entries, __, pagination = calibre_db.fill_indexpage(page, 0, @@ -748,7 +746,7 @@ def list_books(): search = request.args.get("search") total_count = calibre_db.session.query(db.Books).count() if search: - entries, filtered_count, pagination = calibre_db.get_search_results(search, off, order, limit) + entries, filtered_count, __ = calibre_db.get_search_results(search, off, order, limit) else: entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), limit, db.Books, True, order) filtered_count = total_count @@ -1411,12 +1409,71 @@ def logout(): # ################################### Users own configuration ######################################################### +def change_profile(kobo_support, local_oauth_check, oauth_status, translations, languages): + to_save = request.form.to_dict() + current_user.random_books = 0 + if current_user.role_passwd() or current_user.role_admin(): + if "password" in to_save and 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: + current_user.kindle_mail = to_save["kindle_mail"] + if "allowed_tags" in to_save and to_save["allowed_tags"] != current_user.allowed_tags: + current_user.allowed_tags = to_save["allowed_tags"].strip() + if "email" in to_save and to_save["email"] != current_user.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") + return render_title_template("user_edit.html", content=current_user, + title=_(u"%(name)s's profile", name=current_user.nickname), page="me", + kobo_support=kobo_support, + registered_oauth=local_oauth_check, oauth_status=oauth_status) + current_user.email = to_save["email"] + if "nickname" in to_save and to_save["nickname"] != current_user.nickname: + # Query User nickname, if not existing, change + if not ub.session.query(ub.User).filter(ub.User.nickname == to_save["nickname"]).scalar(): + current_user.nickname = to_save["nickname"] + else: + flash(_(u"This username is already taken"), category="error") + return render_title_template("user_edit.html", + translations=translations, + languages=languages, + kobo_support=kobo_support, + new_user=0, content=current_user, + registered_oauth=local_oauth_check, + title=_(u"Edit User %(nick)s", + nick=current_user.nickname), + page="edituser") + if "show_random" in to_save and to_save["show_random"] == "on": + current_user.random_books = 1 + if "default_language" in to_save: + current_user.default_language = to_save["default_language"] + if "locale" in to_save: + current_user.locale = to_save["locale"] + + val = 0 + for key, __ in to_save.items(): + if key.startswith('show'): + val += int(key[5:]) + current_user.sidebar_view = val + if "Show_detail_random" in to_save: + current_user.sidebar_view += constants.DETAIL_RANDOM + + try: + ub.session.commit() + flash(_(u"Profile updated"), category="success") + log.debug(u"Profile updated") + except IntegrityError: + ub.session.rollback() + flash(_(u"Found an existing account for this e-mail address."), category="error") + log.debug(u"Found an existing account for this e-mail address.") + except OperationalError as e: + ub.session.rollback() + log.error("Database error: %s", e) + flash(_(u"Database error: %(error)s.", error=e), category="error") @web.route("/me", methods=["GET", "POST"]) @login_required def profile(): - # downloads = list() languages = calibre_db.speaking_language() translations = babel.list_translations() + [LC('en')] kobo_support = feature_support['kobo'] and config.config_kobo_sync @@ -1427,74 +1484,8 @@ def profile(): oauth_status = None local_oauth_check = {} - '''entries, __, pagination = calibre_db.fill_indexpage(page, - 0, - db.Books, - ub.Downloads.user_id == int(current_user.id), # True, - [], - ub.Downloads, db.Books.id == ub.Downloads.book_id)''' - if request.method == "POST": - to_save = request.form.to_dict() - current_user.random_books = 0 - if current_user.role_passwd() or current_user.role_admin(): - if "password" in to_save and 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: - current_user.kindle_mail = to_save["kindle_mail"] - if "allowed_tags" in to_save and to_save["allowed_tags"] != current_user.allowed_tags: - current_user.allowed_tags = to_save["allowed_tags"].strip() - if "email" in to_save and to_save["email"] != current_user.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") - return render_title_template("user_edit.html", content=current_user, - title=_(u"%(name)s's profile", name=current_user.nickname), page="me", - kobo_support=kobo_support, - registered_oauth=local_oauth_check, oauth_status=oauth_status) - current_user.email = to_save["email"] - if "nickname" in to_save and to_save["nickname"] != current_user.nickname: - # Query User nickname, if not existing, change - if not ub.session.query(ub.User).filter(ub.User.nickname == to_save["nickname"]).scalar(): - current_user.nickname = to_save["nickname"] - else: - flash(_(u"This username is already taken"), category="error") - return render_title_template("user_edit.html", - translations=translations, - languages=languages, - kobo_support=kobo_support, - new_user=0, content=current_user, - registered_oauth=local_oauth_check, - title=_(u"Edit User %(nick)s", - nick=current_user.nickname), - page="edituser") - if "show_random" in to_save and to_save["show_random"] == "on": - current_user.random_books = 1 - if "default_language" in to_save: - current_user.default_language = to_save["default_language"] - if "locale" in to_save: - current_user.locale = to_save["locale"] - - val = 0 - for key, __ in to_save.items(): - if key.startswith('show'): - val += int(key[5:]) - current_user.sidebar_view = val - if "Show_detail_random" in to_save: - current_user.sidebar_view += constants.DETAIL_RANDOM - - try: - ub.session.commit() - flash(_(u"Profile updated"), category="success") - log.debug(u"Profile updated") - except IntegrityError: - ub.session.rollback() - flash(_(u"Found an existing account for this e-mail address."), category="error") - log.debug(u"Found an existing account for this e-mail address.") - except OperationalError as e: - ub.session.rollback() - log.error("Database error: %s", e) - flash(_(u"Database error: %(error)s.", error=e), category="error") - + change_profile(kobo_support, local_oauth_check, oauth_status, translations, languages) return render_title_template("user_edit.html", translations=translations, profile=1,