diff --git a/cps/admin.py b/cps/admin.py
index 5f481fa1..5daad958 100644
--- a/cps/admin.py
+++ b/cps/admin.py
@@ -54,6 +54,7 @@ from .services.worker import WorkerThread
from .usermanagement import user_login_required
from .babel import get_available_translations, get_available_locale, get_user_locale_language
from . import debug_info
+from .string_helper import strip_whitespaces
log = logger.create()
@@ -463,9 +464,9 @@ def edit_list_user(param):
if 'value[]' in vals:
setattr(user, param, prepare_tags(user, vals['action'][0], param, vals['value[]']))
else:
- setattr(user, param, vals['value'].strip())
+ setattr(user, param, strip_whitespaces(vals['value']))
else:
- vals['value'] = vals['value'].strip()
+ vals['value'] = strip_whitespaces(vals['value'])
if param == 'name':
if user.name == "Guest":
raise Exception(_("Guest Name can't be changed"))
@@ -690,7 +691,7 @@ def delete_domain():
def list_domain(allow):
answer = ub.session.query(ub.Registration).filter(ub.Registration.allow == allow).all()
json_dumps = json.dumps([{"domain": r.domain.replace('%', '*').replace('_', '?'), "id": r.id} for r in answer])
- js = json.dumps(json_dumps.replace('"', "'")).lstrip('"').strip('"')
+ js = json.dumps(json_dumps.replace('"', "'")).strip('"')
response = make_response(js.replace("'", '"'))
response.headers["Content-Type"] = "application/json; charset=utf-8"
return response
@@ -1100,7 +1101,7 @@ def _config_checkbox_int(to_save, x):
def _config_string(to_save, x):
- return config.set_from_dictionary(to_save, x, lambda y: y.strip().strip(u'\u200B\u200C\u200D\ufeff') if y else y)
+ return config.set_from_dictionary(to_save, x, lambda y: strip_whitespaces(y) if y else y)
def _configuration_gdrive_helper(to_save):
@@ -1311,9 +1312,9 @@ def update_mailsettings():
if to_save.get("mail_password_e", ""):
_config_string(to_save, "mail_password_e")
_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()
+ config.mail_server = strip_whitespaces(to_save.get('mail_server', ""))
+ config.mail_from = strip_whitespaces(to_save.get('mail_from', ""))
+ config.mail_login = strip_whitespaces(to_save.get('mail_login', ""))
try:
config.save()
except (OperationalError, InvalidRequestError) as e:
@@ -1678,10 +1679,10 @@ def cancel_task():
def _db_simulate_change():
param = request.form.to_dict()
to_save = dict()
- to_save['config_calibre_dir'] = re.sub(r'[\\/]metadata\.db$',
+ to_save['config_calibre_dir'] = strip_whitespaces(re.sub(r'[\\/]metadata\.db$',
'',
param['config_calibre_dir'],
- flags=re.IGNORECASE).strip()
+ flags=re.IGNORECASE))
db_valid, db_change = calibre_db.check_valid_db(to_save["config_calibre_dir"],
ub.app_DB_path,
config.config_calibre_uuid)
@@ -1775,9 +1776,8 @@ def _configuration_update_helper():
if "config_upload_formats" in to_save:
to_save["config_upload_formats"] = ','.join(
- helper.uniq([x.lstrip().rstrip().lower() for x in to_save["config_upload_formats"].split(',')]))
+ helper.uniq([x.strip().lower() for x in to_save["config_upload_formats"].split(',')]))
_config_string(to_save, "config_upload_formats")
- # constants.EXTENSIONS_UPLOAD = config.config_upload_formats.split(',')
_config_string(to_save, "config_calibre")
_config_string(to_save, "config_binariesdir")
diff --git a/cps/config_sql.py b/cps/config_sql.py
index 044c12b5..6a840af5 100644
--- a/cps/config_sql.py
+++ b/cps/config_sql.py
@@ -35,7 +35,7 @@ except ImportError:
from . import constants, logger
from .subproc_wrapper import process_wait
-
+from .string_helper import strip_whitespaces
log = logger.create()
_Base = declarative_base()
@@ -288,19 +288,19 @@ class ConfigSQL(object):
def list_denied_tags(self):
mct = self.config_denied_tags or ""
- return [t.strip() for t in mct.split(",")]
+ return [strip_whitespaces(t) for t in mct.split(",")]
def list_allowed_tags(self):
mct = self.config_allowed_tags or ""
- return [t.strip() for t in mct.split(",")]
+ return [strip_whitespaces(t) for t in mct.split(",")]
def list_denied_column_values(self):
mct = self.config_denied_column_value or ""
- return [t.strip() for t in mct.split(",")]
+ return [strip_whitespaces(t) for t in mct.split(",")]
def list_allowed_column_values(self):
mct = self.config_allowed_column_value or ""
- return [t.strip() for t in mct.split(",")]
+ return [strip_whitespaces(t) for t in mct.split(",")]
def get_log_level(self):
return logger.get_level_name(self.config_log_level)
@@ -372,7 +372,7 @@ class ConfigSQL(object):
db_file = os.path.join(self.config_calibre_dir, 'metadata.db')
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(',')]
+
from . import cli_param
if os.environ.get('FLASK_DEBUG'):
logfile = logger.setup(logger.LOG_TO_STDOUT, logger.logging.DEBUG)
diff --git a/cps/db.py b/cps/db.py
index d0edb871..dc08a7de 100644
--- a/cps/db.py
+++ b/cps/db.py
@@ -48,7 +48,7 @@ from flask import flash
from . import logger, ub, isoLanguages
from .pagination import Pagination
-
+from .string_helper import strip_whitespaces
log = logger.create()
@@ -875,10 +875,11 @@ class CalibreDB:
authors_ordered = list()
# error = False
for auth in sort_authors:
- results = self.session.query(Authors).filter(Authors.sort == auth.lstrip().strip()).all()
+ auth = strip_whitespaces(auth)
+ results = self.session.query(Authors).filter(Authors.sort == auth).all()
# ToDo: How to handle not found author name
if not len(results):
- log.error("Author {} not found to display name in right order".format(auth.strip()))
+ log.error("Author {} not found to display name in right order".format(auth))
# error = True
break
for r in results:
@@ -918,7 +919,7 @@ class CalibreDB:
.filter(and_(Books.authors.any(and_(*q)), func.lower(Books.title).ilike("%" + title + "%"))).first()
def search_query(self, term, config, *join):
- term.strip().lower()
+ strip_whitespaces(term).lower()
self.session.connection().connection.connection.create_function("lower", 1, lcase)
q = list()
author_terms = re.split("[, ]+", term)
@@ -1026,7 +1027,7 @@ class CalibreDB:
if match:
prep = match.group(1)
title = title[len(prep):] + ', ' + prep
- return title.strip()
+ return strip_whitespaces(title)
try:
# sqlalchemy <1.4.24 and sqlalchemy 2.0
diff --git a/cps/editbooks.py b/cps/editbooks.py
index f1943a79..122b1c2c 100644
--- a/cps/editbooks.py
+++ b/cps/editbooks.py
@@ -47,7 +47,7 @@ from .kobo_sync_status import change_archived_books
from .redirect import get_redirect_location
from .file_helper import validate_mime_type
from .usermanagement import user_login_required, login_required_if_no_ano
-
+from .string_helper import strip_whitespaces
editbook = Blueprint('edit-book', __name__)
log = logger.create()
@@ -979,7 +979,7 @@ def render_edit_book(book_id):
def edit_book_ratings(to_save, book):
changed = False
- if to_save.get("rating", "").strip():
+ if strip_whitespaces(to_save.get("rating", "")):
old_rating = False
if len(book.ratings) > 0:
old_rating = book.ratings[0].rating
@@ -1003,14 +1003,14 @@ def edit_book_ratings(to_save, book):
def edit_book_tags(tags, book):
input_tags = tags.split(',')
- input_tags = list(map(lambda it: it.strip(), input_tags))
+ input_tags = list(map(lambda it: strip_whitespaces(it), input_tags))
# Remove duplicates
input_tags = helper.uniq(input_tags)
return modify_database_object(input_tags, book.tags, db.Tags, calibre_db.session, 'tags')
def edit_book_series(series, book):
- input_series = [series.strip()]
+ input_series = [strip_whitespaces(series)]
input_series = [x for x in input_series if x != '']
return modify_database_object(input_series, book.series, db.Series, calibre_db.session, 'series')
@@ -1072,7 +1072,7 @@ def edit_book_languages(languages, book, upload_mode=False, invalid=None):
def edit_book_publisher(publishers, book):
changed = False
if publishers:
- publisher = publishers.rstrip().strip()
+ publisher = strip_whitespaces(publishers)
if len(book.publishers) == 0 or (len(book.publishers) > 0 and publisher != book.publishers[0].name):
changed |= modify_database_object([publisher], book.publishers, db.Publishers, calibre_db.session,
'publisher')
@@ -1119,7 +1119,7 @@ def edit_cc_data_string(book, c, to_save, cc_db_value, cc_string):
changed = False
if c.datatype == 'rating':
to_save[cc_string] = str(int(float(to_save[cc_string]) * 2))
- if to_save[cc_string].strip() != cc_db_value:
+ if strip_whitespaces(to_save[cc_string]) != cc_db_value:
if cc_db_value is not None:
# remove old cc_val
del_cc = getattr(book, cc_string)[0]
@@ -1129,15 +1129,15 @@ def edit_cc_data_string(book, c, to_save, cc_db_value, cc_string):
changed = True
cc_class = db.cc_classes[c.id]
new_cc = calibre_db.session.query(cc_class).filter(
- cc_class.value == to_save[cc_string].strip()).first()
+ cc_class.value == strip_whitespaces(to_save[cc_string])).first()
# if no cc val is found add it
if new_cc is None:
- new_cc = cc_class(value=to_save[cc_string].strip())
+ new_cc = cc_class(value=strip_whitespaces(to_save[cc_string]))
calibre_db.session.add(new_cc)
changed = True
calibre_db.session.flush()
new_cc = calibre_db.session.query(cc_class).filter(
- cc_class.value == to_save[cc_string].strip()).first()
+ cc_class.value == strip_whitespaces(to_save[cc_string])).first()
# add cc value to book
getattr(book, cc_string).append(new_cc)
return changed, to_save
@@ -1165,7 +1165,7 @@ def edit_cc_data(book_id, book, to_save, cc):
cc_db_value = getattr(book, cc_string)[0].value
else:
cc_db_value = None
- if to_save[cc_string].strip():
+ if strip_whitespaces(to_save[cc_string]):
if c.datatype in ['int', 'bool', 'float', "datetime", "comments"]:
change, to_save = edit_cc_data_value(book_id, book, c, to_save, cc_db_value, cc_string)
else:
@@ -1181,7 +1181,7 @@ def edit_cc_data(book_id, book, to_save, cc):
changed = True
else:
input_tags = to_save[cc_string].split(',')
- input_tags = list(map(lambda it: it.strip(), input_tags))
+ input_tags = list(map(lambda it: strip_whitespaces(it), input_tags))
changed |= modify_database_object(input_tags,
getattr(book, cc_string),
db.cc_classes[c.id],
@@ -1284,7 +1284,7 @@ def upload_cover(cover_request, book):
def handle_title_on_edit(book, book_title):
# handle book title
- book_title = book_title.rstrip().strip()
+ book_title = strip_whitespaces(book_title)
if book.title != book_title:
if book_title == '':
book_title = _(u'Unknown')
diff --git a/cps/epub.py b/cps/epub.py
index c802f61d..e84822f3 100644
--- a/cps/epub.py
+++ b/cps/epub.py
@@ -25,6 +25,7 @@ from . import config, logger
from .helper import split_authors
from .epub_helper import get_content_opf, default_ns
from .constants import BookMeta
+from .string_helper import strip_whitespaces
log = logger.create()
@@ -90,7 +91,7 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
elif s == 'date':
epub_metadata[s] = tmp[0][:10]
else:
- epub_metadata[s] = tmp[0].strip()
+ epub_metadata[s] = strip_whitespaces(tmp[0])
else:
epub_metadata[s] = 'Unknown'
diff --git a/cps/helper.py b/cps/helper.py
index bc95762c..39c9c384 100644
--- a/cps/helper.py
+++ b/cps/helper.py
@@ -52,6 +52,7 @@ except ImportError:
UnacceptableAddressException = MissingSchema = BaseException
from . import calibre_db, cli_param
+from .string_helper import strip_whitespaces
from .tasks.convert import TaskConvert
from . import logger, config, db, ub, fs
from . import gdriveutils as gd
@@ -118,7 +119,7 @@ def convert_book_format(book_id, calibre_path, old_book_format, new_book_format,
# Texts are not lazy translated as they are supposed to get send out as is
def send_test_mail(ereader_mail, user_name):
for email in ereader_mail.split(','):
- email = email.strip()
+ email = strip_whitespaces(email)
WorkerThread.add(user_name, TaskEmail(_('Calibre-Web Test Email'), None, None,
config.get_mail_settings(), email, N_("Test Email"),
_('This Email has been sent via Calibre-Web.')))
@@ -228,7 +229,7 @@ def send_mail(book_id, book_format, convert, ereader_mail, calibrepath, user_id)
link = '{}'.format(url_for('web.show_book', book_id=book_id), escape(book.title))
email_text = N_("%(book)s send to eReader", book=link)
for email in ereader_mail.split(','):
- email = email.strip()
+ email = strip_whitespaces(email)
WorkerThread.add(user_id, TaskEmail(_("Send to eReader"), book.path, converted_file_name,
config.get_mail_settings(), email,
email_text, _('This Email has been sent via Calibre-Web.'), book.id))
@@ -252,7 +253,7 @@ def get_valid_filename(value, replace_whitespace=True, chars=128):
# pipe has to be replaced with comma
value = re.sub(r'[|]+', ',', value, flags=re.U)
- value = value.encode('utf-8')[:chars].decode('utf-8', errors='ignore').strip()
+ value = strip_whitespaces(value.encode('utf-8')[:chars].decode('utf-8', errors='ignore'))
if not value:
raise ValueError("Filename cannot be empty")
@@ -267,11 +268,11 @@ def split_authors(values):
commas = author.count(',')
if commas == 1:
author_split = author.split(',')
- authors_list.append(author_split[1].strip() + ' ' + author_split[0].strip())
+ authors_list.append(strip_whitespaces(author_split[1]) + ' ' + strip_whitespaces(author_split[0]))
elif commas > 1:
- authors_list.extend([x.strip() for x in author.split(',')])
+ authors_list.extend([strip_whitespaces(x) for x in author.split(',')])
else:
- authors_list.append(author.strip())
+ authors_list.append(strip_whitespaces(author))
return authors_list
@@ -661,7 +662,7 @@ def check_email(email):
def check_username(username):
- username = username.strip()
+ username = strip_whitespaces(username)
if ub.session.query(ub.User).filter(func.lower(ub.User.name) == username.lower()).scalar():
log.error("This username is already taken")
raise Exception(_("This username is already taken"))
@@ -670,14 +671,14 @@ def check_username(username):
def valid_email(emails):
for email in emails.split(','):
- email = email.strip()
- # if email is not deleted
- if email:
- # Regex according to https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email#validation
- if not re.search(r"^[\w.!#$%&'*+\\/=?^_`{|}~-]+@[\w](?:[\w-]{0,61}[\w])?(?:\.[\w](?:[\w-]{0,61}[\w])?)*$",
- email):
- log.error("Invalid Email address format")
- raise Exception(_("Invalid Email address format"))
+ email = strip_whitespaces(email)
+ # if email is not deleted
+ if email:
+ # Regex according to https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email#validation
+ if not re.search(r"^[\w.!#$%&'*+\\/=?^_`{|}~-]+@[\w](?:[\w-]{0,61}[\w])?(?:\.[\w](?:[\w-]{0,61}[\w])?)*$",
+ email):
+ log.error("Invalid Email address format")
+ raise Exception(_("Invalid Email address format"))
return email
diff --git a/cps/search.py b/cps/search.py
index 6054ec9e..da74984b 100644
--- a/cps/search.py
+++ b/cps/search.py
@@ -24,9 +24,9 @@ from flask_babel import format_date
from flask_babel import gettext as _
from sqlalchemy.sql.expression import func, not_, and_, or_, text, true
from sqlalchemy.sql.functions import coalesce
-from sqlalchemy import exists
from . import logger, db, calibre_db, config, ub
+from .string_helper import strip_whitespaces
from .usermanagement import login_required_if_no_ano
from .render_template import render_title_template
from .pagination import Pagination
@@ -267,11 +267,11 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
description = term.get("comment")
read_status = term.get("read_status")
if author_name:
- author_name = author_name.strip().lower().replace(',', '|')
+ author_name = strip_whitespaces(author_name).lower().replace(',', '|')
if book_title:
- book_title = book_title.strip().lower()
+ book_title = strip_whitespaces(book_title).lower()
if publisher:
- publisher = publisher.strip().lower()
+ publisher = strip_whitespaces(publisher).lower()
search_term = []
cc_present = False
diff --git a/cps/string_helper.py b/cps/string_helper.py
new file mode 100644
index 00000000..b2d0cf81
--- /dev/null
+++ b/cps/string_helper.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
+# Copyright (C) 2024 OzzieIsaacs
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
Start Time: 2024-08-11 19:57:31
+Start Time: 2024-08-12 18:42:30
Stop Time: 2024-08-12 03:10:54
+Stop Time: 2024-08-13 01:57:45
Duration: 5h 59 min
+Duration: 6h 1 min
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_thumbnails.py", line 140, in test_cover_change_on_upload_new_cover - self.assertGreaterEqual(diff(BytesIO(updated_cover), BytesIO(original_cover), delete_diff_file=True), 0.03) -AssertionError: 0.023989181595169266 not greater than or equal to 0.03-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_upload_audio.py", line 367, in test_upload_flac - self.assertEqual('Album', details['series']) -AssertionError: 'Album' != 'Flac Album' -- Album -+ Flac Album-