From 21026340ad6056180ca0c9315d6bf755e56313d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= Date: Wed, 5 May 2021 14:26:56 +0200 Subject: [PATCH 01/11] remove singledispatch from requirements It's not used anywhere. --- requirements.txt | 1 - setup.cfg | 1 - 2 files changed, 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index a90226b2..8f273296 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ Babel>=1.3, <2.9 Flask-Babel>=0.11.1,<2.1.0 Flask-Login>=0.3.2,<0.5.1 Flask-Principal>=0.3.2,<0.5.1 -singledispatch>=3.4.0.0,<3.5.0.0 backports_abc>=0.4 Flask>=1.0.2,<1.2.0 iso-639>=0.4.5,<0.5.0 diff --git a/setup.cfg b/setup.cfg index 89e0f598..8266391c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,6 @@ install_requires = Flask-Babel>=0.11.1,<2.1.0 Flask-Login>=0.3.2,<0.5.1 Flask-Principal>=0.3.2,<0.5.1 - singledispatch>=3.4.0.0,<3.5.0.0 backports_abc>=0.4 Flask>=1.0.2,<1.2.0 iso-639>=0.4.5,<0.5.0 From c6dadbe75e0e49f6ff4e55e2f5623bad6fdc9db3 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Thu, 13 May 2021 09:17:01 +0200 Subject: [PATCH 02/11] restrictions are now applied to kobo sync requests (Fix #1938) --- cps/kobo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cps/kobo.py b/cps/kobo.py index 8988ef3f..085bf1bc 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -158,6 +158,7 @@ def HandleSyncRequest(): .filter(db.Books.last_modified >= sync_token.books_last_modified) .filter(db.Books.id>sync_token.books_last_id) .filter(db.Data.format.in_(KOBO_FORMATS)) + .filter(calibre_db.common_filters()) .order_by(db.Books.last_modified) .order_by(db.Books.id) .limit(SYNC_ITEM_LIMIT) @@ -168,6 +169,7 @@ def HandleSyncRequest(): .join(db.Data).outerjoin(ub.ArchivedBook, db.Books.id == ub.ArchivedBook.book_id) .filter(db.Books.last_modified > sync_token.books_last_modified) .filter(db.Data.format.in_(KOBO_FORMATS)) + .filter(calibre_db.common_filters()) .order_by(db.Books.last_modified) .order_by(db.Books.id) .limit(SYNC_ITEM_LIMIT) From 9ef705650bf867e3a351eb67d02a810bcd849b36 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Thu, 13 May 2021 09:41:27 +0200 Subject: [PATCH 03/11] Access pages with stored order per default (#1987) Fix for toggle-text of enable-sort button on shelf page --- cps/static/js/main.js | 2 +- cps/templates/detail.html | 8 ++++---- cps/templates/grid.html | 4 ++-- cps/templates/list.html | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cps/static/js/main.js b/cps/static/js/main.js index 927b65ac..ca9f3e14 100644 --- a/cps/static/js/main.js +++ b/cps/static/js/main.js @@ -533,7 +533,7 @@ $(function() { $("#pub_new").toggleClass("disabled"); $("#pub_old").toggleClass("disabled"); var alternative_text = $("#toggle_order_shelf").data('alt-text'); - $("#toggle_order_shelf")[0].attributes['data-alt-text'].value = $("#toggle_order_shelf").html(); + $("#toggle_order_shelf").data('alt-text', $("#toggle_order_shelf").html()); $("#toggle_order_shelf").html(alternative_text); }); diff --git a/cps/templates/detail.html b/cps/templates/detail.html index 342cce53..5b1f2448 100644 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -100,7 +100,7 @@

{{entry.title}}

{% for author in entry.authors %} - {{author.name.replace('|',',')}} + {{author.name.replace('|',',')}} {% if not loop.last %} & {% endif %} @@ -122,7 +122,7 @@ {% endif %} {% if entry.series|length > 0 %} -

{{_('Book')}} {{entry.series_index}} {{_('of')}} {{entry.series[0].name}}

+

{{_('Book')}} {{entry.series_index}} {{_('of')}} {{entry.series[0].name}}

{% endif %} {% if entry.languages.__len__() > 0 %} @@ -151,7 +151,7 @@ {% for tag in entry.tags %} - {{tag.name}} + {{tag.name}} {%endfor%}

@@ -162,7 +162,7 @@ diff --git a/cps/templates/grid.html b/cps/templates/grid.html index 6b03d89c..1e79c43d 100644 --- a/cps/templates/grid.html +++ b/cps/templates/grid.html @@ -27,7 +27,7 @@ {% for entry in entries %}
diff --git a/cps/templates/list.html b/cps/templates/list.html index 1e171ca5..c68bda10 100644 --- a/cps/templates/list.html +++ b/cps/templates/list.html @@ -32,7 +32,7 @@ {% endif %}
{{entry.count}}
-
+
{% if entry.name %}
{% for number in range(entry.name) %} From b0cc52e0aab2039602efb4c98ff5983c8a049a53 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Thu, 13 May 2021 10:39:36 +0200 Subject: [PATCH 04/11] Enable custom column datetime (adv. search not working yet) #1984 --- cps/db.py | 10 ++++++---- cps/editbooks.py | 7 ++++++- cps/static/js/edit_books.js | 6 +----- cps/templates/book_edit.html | 14 ++++++++++++++ cps/templates/detail.html | 2 ++ 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/cps/db.py b/cps/db.py index 66c289dd..808d8abe 100644 --- a/cps/db.py +++ b/cps/db.py @@ -59,7 +59,7 @@ except ImportError: log = logger.create() -cc_exceptions = ['datetime', 'comments', 'composite', 'series'] +cc_exceptions = ['comments', 'composite', 'series'] cc_classes = {} Base = declarative_base() @@ -491,23 +491,25 @@ class CalibreDB(): ccdict['value'] = Column(Float) elif row.datatype == 'int': ccdict['value'] = Column(Integer) + elif row.datatype == 'datetime': + ccdict['value'] = Column(TIMESTAMP) elif row.datatype == 'bool': ccdict['value'] = Column(Boolean) else: ccdict['value'] = Column(String) - if row.datatype in ['float', 'int', 'bool']: + if row.datatype in ['float', 'int', 'bool', 'datetime']: ccdict['book'] = Column(Integer, ForeignKey('books.id')) cc_classes[row.id] = type(str('custom_column_' + str(row.id)), (Base,), ccdict) for cc_id in cc_ids: - if (cc_id[1] == 'bool') or (cc_id[1] == 'int') or (cc_id[1] == 'float'): + if cc_id[1] in ['bool', 'int', 'float', 'datetime']: setattr(Books, 'custom_column_' + str(cc_id[0]), relationship(cc_classes[cc_id[0]], primaryjoin=( Books.id == cc_classes[cc_id[0]].book), backref='books')) - elif (cc_id[1] == 'series'): + elif cc_id[1] == 'series': setattr(Books, 'custom_column_' + str(cc_id[0]), relationship(books_custom_column_links[cc_id[0]], diff --git a/cps/editbooks.py b/cps/editbooks.py index 45e0f9fe..f231ab0d 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -501,6 +501,11 @@ def edit_cc_data_number(book_id, book, c, to_save, cc_db_value, cc_string): to_save[cc_string] = None elif c.datatype == 'bool': to_save[cc_string] = 1 if to_save[cc_string] == 'True' else 0 + elif c.datatype == 'datetime': + try: + to_save[cc_string] = datetime.strptime(to_save[cc_string], "%Y-%m-%d") + except ValueError: + to_save[cc_string] = db.Books.DEFAULT_PUBDATE if to_save[cc_string] != cc_db_value: if cc_db_value is not None: @@ -559,7 +564,7 @@ def edit_cc_data(book_id, book, to_save): else: cc_db_value = None if to_save[cc_string].strip(): - if c.datatype == 'int' or c.datatype == 'bool' or c.datatype == 'float': + if c.datatype in ['int', 'bool', 'float', "datetime"]: changed, to_save = edit_cc_data_number(book_id, book, c, to_save, cc_db_value, cc_string) else: changed, to_save = edit_cc_data_string(book, c, to_save, cc_db_value, cc_string) diff --git a/cps/static/js/edit_books.js b/cps/static/js/edit_books.js index 8cedf688..fa3885bb 100644 --- a/cps/static/js/edit_books.js +++ b/cps/static/js/edit_books.js @@ -63,6 +63,7 @@ if (!Modernizr.inputtypes.date) { }).trigger("change"); } + /* Takes a prefix, query typeahead callback, Bloodhound typeahead adapter and returns the completions it gets from the bloodhound engine prefixed. @@ -78,11 +79,6 @@ function prefixedSource(prefix, query, cb, bhAdapter) { }); } -/*function getPath() { - var jsFileLocation = $("script[src*=edit_books]").attr("src"); // the js file path - return jsFileLocation.substr(0, jsFileLocation.search("/static/js/edit_books.js")); // the js folder path -}*/ - var authors = new Bloodhound({ name: "authors", datumTokenizer: function datumTokenizer(datum) { diff --git a/cps/templates/book_edit.html b/cps/templates/book_edit.html index c0fc141e..daafc013 100644 --- a/cps/templates/book_edit.html +++ b/cps/templates/book_edit.html @@ -149,6 +149,20 @@ {% endif %}> {% endif %} + {% if c.datatype == 'datetime' %} + +
+ 0 %} + value="{% if book['custom_column_' ~ c.id][0].value %}{{ book['custom_column_' ~ c.id][0].value|formatdateinput}}{% endif %}" + {% endif %}> + 0 %} + value="{% if book['custom_column_' ~ c.id][0].value %}{{book['custom_column_' ~ c.id][0].value|formatdate}}{% endif %}" + {% endif %}> +
+ {% endif %} + {% if c.datatype == 'enumeration' %} {% endif %} + {% if c.datatype == 'datetime' %} +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ {% endif %} + {% if c.datatype in ['text', 'series'] and not c.is_multiple %} {% endif %} diff --git a/cps/web.py b/cps/web.py index adf0d51e..12f3058b 100644 --- a/cps/web.py +++ b/cps/web.py @@ -1067,8 +1067,8 @@ def search(): @login_required_if_no_ano def advanced_search(): values = dict(request.form) - params = ['include_tag', 'exclude_tag', 'include_serie', 'exclude_serie', 'include_shelf','exclude_shelf','include_language', - 'exclude_language', 'include_extension', 'exclude_extension'] + params = ['include_tag', 'exclude_tag', 'include_serie', 'exclude_serie', 'include_shelf', 'exclude_shelf', + 'include_language', 'exclude_language', 'include_extension', 'exclude_extension'] for param in params: values[param] = list(request.form.getlist(param)) flask_session['query'] = json.dumps(values) @@ -1077,20 +1077,30 @@ def advanced_search(): def adv_search_custom_columns(cc, term, q): for c in cc: - custom_query = term.get('custom_column_' + str(c.id)) - if custom_query != '' and custom_query is not None: - if c.datatype == 'bool': + if c.datatype == "datetime": + custom_start = term.get('custom_column_' + str(c.id) + '_start') + custom_end = term.get('custom_column_' + str(c.id) + '_end') + if custom_start: q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any( - db.cc_classes[c.id].value == (custom_query == "True"))) - elif c.datatype == 'int' or c.datatype == 'float': + db.cc_classes[c.id].value >= custom_start)) + if custom_end: q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any( - db.cc_classes[c.id].value == custom_query)) - elif c.datatype == 'rating': - q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any( - db.cc_classes[c.id].value == int(float(custom_query) * 2))) - else: - q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any( - func.lower(db.cc_classes[c.id].value).ilike("%" + custom_query + "%"))) + db.cc_classes[c.id].value <= custom_end)) + else: + custom_query = term.get('custom_column_' + str(c.id)) + if custom_query != '' and custom_query is not None: + if c.datatype == 'bool': + q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any( + db.cc_classes[c.id].value == (custom_query == "True"))) + elif c.datatype == 'int' or c.datatype == 'float': + q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any( + db.cc_classes[c.id].value == custom_query)) + elif c.datatype == 'rating': + q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any( + db.cc_classes[c.id].value == int(float(custom_query) * 2))) + else: + q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any( + func.lower(db.cc_classes[c.id].value).ilike("%" + custom_query + "%"))) return q @@ -1262,10 +1272,20 @@ def render_adv_search_results(term, offset=None, order=None, limit=None): searchterm = [] cc_present = False for c in cc: - if term.get('custom_column_' + str(c.id)): - searchterm.extend([(u"%s: %s" % (c.name, term.get('custom_column_' + str(c.id))))]) + if c.datatype == "datetime": + column_start = term.get('custom_column_' + str(c.id) + '_start') + column_end = term.get('custom_column_' + str(c.id) + '_end') + if column_start: + searchterm.extend([u"{} >= {}".format(c.name, column_start)]) + cc_present = True + if column_end: + searchterm.extend([u"{} <= {}".format(c.name, column_end)]) + cc_present = True + elif term.get('custom_column_' + str(c.id)): + searchterm.extend([(u"{}: {}".format(c.name, term.get('custom_column_' + str(c.id))))]) cc_present = True + if any(tags.values()) or author_name or book_title or publisher or pub_start or pub_end or rating_low \ or rating_high or description or cc_present or read_status: searchterm, pub_start, pub_end = extend_search_term(searchterm, From 6bf360fbfb434232dd50b9bf6ca2264079bc807f Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Thu, 13 May 2021 14:00:01 +0200 Subject: [PATCH 07/11] Added "comments" type to supported custom columns --- cps/db.py | 8 +++--- cps/editbooks.py | 6 ++--- cps/static/js/edit_books.js | 45 +++++++++++++++++++++------------- cps/templates/book_edit.html | 5 ++-- cps/templates/detail.html | 6 ++--- cps/templates/search_form.html | 2 +- 6 files changed, 42 insertions(+), 30 deletions(-) diff --git a/cps/db.py b/cps/db.py index 808d8abe..98ad5898 100644 --- a/cps/db.py +++ b/cps/db.py @@ -59,7 +59,7 @@ except ImportError: log = logger.create() -cc_exceptions = ['comments', 'composite', 'series'] +cc_exceptions = ['composite', 'series'] cc_classes = {} Base = declarative_base() @@ -473,7 +473,7 @@ class CalibreDB(): } books_custom_column_links[row.id] = type(str('books_custom_column_' + str(row.id) + '_link'), (Base,), dicttable) - else: + if row.datatype in ['rating', 'text', 'enumeration']: books_custom_column_links[row.id] = Table('books_custom_column_' + str(row.id) + '_link', Base.metadata, Column('book', Integer, ForeignKey('books.id'), @@ -497,12 +497,12 @@ class CalibreDB(): ccdict['value'] = Column(Boolean) else: ccdict['value'] = Column(String) - if row.datatype in ['float', 'int', 'bool', 'datetime']: + if row.datatype in ['float', 'int', 'bool', 'datetime', 'comments']: ccdict['book'] = Column(Integer, ForeignKey('books.id')) cc_classes[row.id] = type(str('custom_column_' + str(row.id)), (Base,), ccdict) for cc_id in cc_ids: - if cc_id[1] in ['bool', 'int', 'float', 'datetime']: + if cc_id[1] in ['bool', 'int', 'float', 'datetime', 'comments']: setattr(Books, 'custom_column_' + str(cc_id[0]), relationship(cc_classes[cc_id[0]], diff --git a/cps/editbooks.py b/cps/editbooks.py index f231ab0d..a155e029 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -495,7 +495,7 @@ def edit_book_publisher(publishers, book): return changed -def edit_cc_data_number(book_id, book, c, to_save, cc_db_value, cc_string): +def edit_cc_data_value(book_id, book, c, to_save, cc_db_value, cc_string): changed = False if to_save[cc_string] == 'None': to_save[cc_string] = None @@ -564,8 +564,8 @@ def edit_cc_data(book_id, book, to_save): else: cc_db_value = None if to_save[cc_string].strip(): - if c.datatype in ['int', 'bool', 'float', "datetime"]: - changed, to_save = edit_cc_data_number(book_id, book, c, to_save, cc_db_value, cc_string) + if c.datatype in ['int', 'bool', 'float', "datetime", "comments"]: + changed, to_save = edit_cc_data_value(book_id, book, c, to_save, cc_db_value, cc_string) else: changed, to_save = edit_cc_data_string(book, c, to_save, cc_db_value, cc_string) else: diff --git a/cps/static/js/edit_books.js b/cps/static/js/edit_books.js index fa3885bb..389a247f 100644 --- a/cps/static/js/edit_books.js +++ b/cps/static/js/edit_books.js @@ -10,25 +10,36 @@ if ($("#description").length) { menubar: "edit view format", language: language }); - - if (!Modernizr.inputtypes.date) { - $("#pubdate").datepicker({ - format: "yyyy-mm-dd", - language: language - }).on("change", function () { - // Show localized date over top of the standard YYYY-MM-DD date - var pubDate; - var results = /(\d{4})[-\/\\](\d{1,2})[-\/\\](\d{1,2})/.exec(this.value); // YYYY-MM-DD - if (results) { - pubDate = new Date(results[1], parseInt(results[2], 10) - 1, results[3]) || new Date(this.value); - $("#fake_pubdate") - .val(pubDate.toLocaleDateString(language)) - .removeClass("hidden"); - } - }).trigger("change"); - } } +if ($(".tiny_editor").length) { + tinymce.init({ + selector: ".tiny_editor", + branding: false, + menubar: "edit view format", + language: language + }); +} + +tiny_editor +if (!Modernizr.inputtypes.date) { + $("#pubdate").datepicker({ + format: "yyyy-mm-dd", + language: language + }).on("change", function () { + // Show localized date over top of the standard YYYY-MM-DD date + var pubDate; + var results = /(\d{4})[-\/\\](\d{1,2})[-\/\\](\d{1,2})/.exec(this.value); // YYYY-MM-DD + if (results) { + pubDate = new Date(results[1], parseInt(results[2], 10) - 1, results[3]) || new Date(this.value); + $("#fake_pubdate") + .val(pubDate.toLocaleDateString(language)) + .removeClass("hidden"); + } + }).trigger("change"); +} + + if (!Modernizr.inputtypes.date) { $("#Publishstart").datepicker({ format: "yyyy-mm-dd", diff --git a/cps/templates/book_edit.html b/cps/templates/book_edit.html index daafc013..194a3248 100644 --- a/cps/templates/book_edit.html +++ b/cps/templates/book_edit.html @@ -150,7 +150,6 @@ {% endif %} {% if c.datatype == 'datetime' %} -
0 %} @@ -163,7 +162,9 @@
{% endif %} - + {% if c.datatype == 'comments' %} + + {% endif %} {% if c.datatype == 'enumeration' %} {% endif %} From 6bc426b21da563fbbe8fb5e9f4bd26b5b1410a3a Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Fri, 14 May 2021 07:29:48 +0200 Subject: [PATCH 08/11] Fix edit_books.js --- cps/static/js/edit_books.js | 1 - 1 file changed, 1 deletion(-) diff --git a/cps/static/js/edit_books.js b/cps/static/js/edit_books.js index 389a247f..8ec020c0 100644 --- a/cps/static/js/edit_books.js +++ b/cps/static/js/edit_books.js @@ -21,7 +21,6 @@ if ($(".tiny_editor").length) { }); } -tiny_editor if (!Modernizr.inputtypes.date) { $("#pubdate").datepicker({ format: "yyyy-mm-dd", From 06347b7e3c62ed11fb3da963356a13a508ffa196 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Fri, 14 May 2021 08:26:56 +0200 Subject: [PATCH 09/11] Bugfixes from testrun --- cps/templates/search.html | 2 +- cps/web.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cps/templates/search.html b/cps/templates/search.html index 78396d80..81ab2d99 100644 --- a/cps/templates/search.html +++ b/cps/templates/search.html @@ -16,7 +16,7 @@