From 9a8a1f75cab3c35e35b74ae2cc3b2969743193fa Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Mon, 1 Jun 2020 10:53:48 +0200 Subject: [PATCH 01/95] For for error unrar binary not found --- cps/admin.py | 7 ++++--- cps/helper.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cps/admin.py b/cps/admin.py index 947d0087..84a94cba 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -683,9 +683,10 @@ def _configuration_update_helper(): reboot_required |= reboot # Rarfile Content configuration _config_string(to_save, "config_rarfile_location") - unrar_status = helper.check_unrar(config.config_rarfile_location) - if unrar_status: - return _configuration_result(unrar_status, gdriveError) + if "config_rarfile_location" in to_save: + unrar_status = helper.check_unrar(config.config_rarfile_location) + if unrar_status: + return _configuration_result(unrar_status, gdriveError) try: metadata_db = os.path.join(config.config_calibre_dir, "metadata.db") diff --git a/cps/helper.py b/cps/helper.py index ecb7180d..b9463416 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -778,7 +778,7 @@ def get_download_link(book_id, book_format, client): book_format = book_format.split(".")[0] book = calibre_db.get_filtered_book(book_id) if book: - data1 = data = calibre_db.get_book_format(book.id, book_format.upper()) + data1 = calibre_db.get_book_format(book.id, book_format.upper()) else: abort(404) if data1: From 13ae28edab1548080ba9d71f40f6dc79a062b7ab Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Thu, 4 Jun 2020 20:34:31 +0200 Subject: [PATCH 02/95] Fix #1449 (Shelf download menu wasn't working for books with more than one format) --- cps/templates/shelfdown.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cps/templates/shelfdown.html b/cps/templates/shelfdown.html index b2ac947f..164fd6d2 100644 --- a/cps/templates/shelfdown.html +++ b/cps/templates/shelfdown.html @@ -76,7 +76,8 @@ {% endfor %} - + + {% endblock %} From d1b533848ded04501b98188112548ac973c502b2 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Fri, 5 Jun 2020 20:13:39 +0200 Subject: [PATCH 03/95] Changed pubdate to timestamp --- cps/web.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/cps/web.py b/cps/web.py index 3bc8763a..f88cdaa4 100644 --- a/cps/web.py +++ b/cps/web.py @@ -834,8 +834,27 @@ def render_language_books(page, name, order): @web.route("/table") @login_required_if_no_ano def books_table(): - return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name, - title=_(u"Language: %(name)s", name=lang_name), page="language") + return render_title_template('book_table.html', title=_(u"Books list"), page="table") + +@web.route("/ajax/listbooks") +@login_required_if_no_ano +def list_books(): + order = [db.Books.timestamp.desc()] + entries, __, __ = calibre_db.fill_indexpage(1, db.Books, True, order) + js_list = json.dumps(entries, cls=db.AlchemyEncoder) + response = make_response(js_list) + response.headers["Content-Type"] = "application/json; charset=utf-8" + return response + +@web.route("/ajax/editbooks") +@login_required_if_no_ano +def edit_list_book(): + pass + +@web.route("/ajax/editbooks") +@login_required_if_no_ano +def delete_list_book(): + pass @web.route("/author") @login_required_if_no_ano From 827b0c6e50ce147c3ecbc1a3e7c82a6489709d53 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sat, 6 Jun 2020 09:52:35 +0200 Subject: [PATCH 04/95] Changed pubdate to timestamp --- cps/db.py | 2 +- cps/jinjia.py | 10 +++------- cps/templates/detail.html | 2 +- cps/ub.py | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cps/db.py b/cps/db.py index 1296c84a..01d50605 100644 --- a/cps/db.py +++ b/cps/db.py @@ -269,7 +269,7 @@ class Books(Base): sort = Column(String(collation='NOCASE')) author_sort = Column(String(collation='NOCASE')) timestamp = Column(TIMESTAMP, default=datetime.utcnow) - pubdate = Column(String) # , default=datetime.utcnow) + pubdate = Column(TIMESTAMP, default=DEFAULT_PUBDATE) series_index = Column(String, nullable=False, default="1.0") last_modified = Column(TIMESTAMP, default=datetime.utcnow) path = Column(String, default="", nullable=False) diff --git a/cps/jinjia.py b/cps/jinjia.py index 28c2621a..b1b42939 100644 --- a/cps/jinjia.py +++ b/cps/jinjia.py @@ -76,22 +76,18 @@ def mimetype_filter(val): @jinjia.app_template_filter('formatdate') def formatdate_filter(val): try: - conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val) - formatdate = datetime.datetime.strptime(conformed_timestamp[:15], "%Y%m%d %H%M%S") - return format_date(formatdate, format='medium', locale=get_locale()) + return format_date(val, format='medium', locale=get_locale()) except AttributeError as e: log.error('Babel error: %s, Current user locale: %s, Current User: %s', e, current_user.locale, current_user.nickname ) - return formatdate + return val @jinjia.app_template_filter('formatdateinput') def format_date_input(val): - conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val) - date_obj = datetime.datetime.strptime(conformed_timestamp[:15], "%Y%m%d %H%M%S") - input_date = date_obj.isoformat().split('T', 1)[0] # Hack to support dates <1900 + input_date = val.isoformat().split('T', 1)[0] # Hack to support dates <1900 return '' if input_date == "0101-01-01" else input_date diff --git a/cps/templates/detail.html b/cps/templates/detail.html index 6edf1677..4a0fbe02 100644 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -160,7 +160,7 @@ {% endif %} - {% if entry.pubdate[:10] != '0101-01-01' %} + {% if (entry.pubdate|string)[:10] != '0101-01-01' %}

{{_('Published')}}: {{entry.pubdate|formatdate}}

diff --git a/cps/ub.py b/cps/ub.py index 3dfb5347..c39b96a6 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -109,7 +109,7 @@ def get_sidebar_config(kwargs=None): "visibility": constants.SIDEBAR_ARCHIVED, 'public': (not g.user.is_anonymous), "page": "archived", "show_text": _('Show archived books'), "config_show": content}) sidebar.append( - {"glyph": "glyphicon-th-list", "text": _('Books List'), "link": 'web.books_list', "id": "list", + {"glyph": "glyphicon-th-list", "text": _('Books List'), "link": 'web.books_table', "id": "list", "visibility": constants.SIDEBAR_LIST, 'public': (not g.user.is_anonymous), "page": "list", "show_text": _('Show Books List'), "config_show": content}) From 0dd0605a1fe38c0ae22e577d23a1da8e6fd57b2a Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sat, 6 Jun 2020 21:21:10 +0200 Subject: [PATCH 05/95] Book list for merging --- cps/db.py | 85 +++++++++++++-- cps/opds.py | 18 ++-- cps/static/css/libs/bootstrap-table.min.css | 11 +- .../bootstrap-table-editable.min.js | 17 +-- .../bootstrap-table/bootstrap-table.min.js | 17 +-- .../locale/bootstrap-table-af-ZA.min.js | 17 +-- .../locale/bootstrap-table-ar-SA.min.js | 17 +-- .../locale/bootstrap-table-ca-ES.min.js | 17 +-- .../locale/bootstrap-table-cs-CZ.min.js | 17 +-- .../locale/bootstrap-table-da-DK.min.js | 17 +-- .../locale/bootstrap-table-de-DE.min.js | 17 +-- .../locale/bootstrap-table-el-GR.min.js | 17 +-- .../locale/bootstrap-table-en-US.min.js | 17 +-- .../locale/bootstrap-table-es-AR.min.js | 17 +-- .../locale/bootstrap-table-es-CL.min.js | 17 +-- .../locale/bootstrap-table-es-CR.min.js | 17 +-- .../locale/bootstrap-table-es-ES.min.js | 17 +-- .../locale/bootstrap-table-es-MX.min.js | 17 +-- .../locale/bootstrap-table-es-NI.min.js | 17 +-- .../locale/bootstrap-table-es-SP.min.js | 17 +-- .../locale/bootstrap-table-et-EE.min.js | 17 +-- .../locale/bootstrap-table-eu-EU.min.js | 17 +-- .../locale/bootstrap-table-fa-IR.min.js | 17 +-- .../locale/bootstrap-table-fi-FI.min.js | 10 ++ .../locale/bootstrap-table-fr-BE.min.js | 17 +-- .../locale/bootstrap-table-fr-CH.min.js | 10 ++ .../locale/bootstrap-table-fr-FR.min.js | 17 +-- .../locale/bootstrap-table-fr-LU.min.js | 10 ++ .../locale/bootstrap-table-he-IL.min.js | 17 +-- .../locale/bootstrap-table-hr-HR.min.js | 17 +-- .../locale/bootstrap-table-hu-HU.min.js | 17 +-- .../locale/bootstrap-table-id-ID.min.js | 17 +-- .../locale/bootstrap-table-it-IT.min.js | 17 +-- .../locale/bootstrap-table-ja-JP.min.js | 17 +-- .../locale/bootstrap-table-ka-GE.min.js | 17 +-- .../locale/bootstrap-table-ko-KR.min.js | 17 +-- .../locale/bootstrap-table-ms-MY.min.js | 17 +-- .../locale/bootstrap-table-nb-NO.min.js | 17 +-- .../locale/bootstrap-table-nl-BE.min.js | 10 ++ .../locale/bootstrap-table-nl-NL.min.js | 17 +-- .../locale/bootstrap-table-pl-PL.min.js | 17 +-- .../locale/bootstrap-table-pt-BR.min.js | 17 +-- .../locale/bootstrap-table-pt-PT.min.js | 17 +-- .../locale/bootstrap-table-ro-RO.min.js | 17 +-- .../locale/bootstrap-table-ru-RU.min.js | 17 +-- .../locale/bootstrap-table-sk-SK.min.js | 17 +-- .../locale/bootstrap-table-sr-Cyrl-RS.min.js | 10 ++ .../locale/bootstrap-table-sr-Latn-RS.min.js | 10 ++ .../locale/bootstrap-table-sv-SE.min.js | 17 +-- .../locale/bootstrap-table-th-TH.min.js | 17 +-- .../locale/bootstrap-table-tr-TR.min.js | 17 +-- .../locale/bootstrap-table-uk-UA.min.js | 17 +-- .../locale/bootstrap-table-ur-PK.min.js | 17 +-- .../locale/bootstrap-table-uz-Latn-UZ.min.js | 17 +-- .../locale/bootstrap-table-vi-VN.min.js | 17 +-- .../locale/bootstrap-table-zh-CN.min.js | 17 +-- .../locale/bootstrap-table-zh-TW.min.js | 17 +-- cps/static/js/table.js | 18 +++- cps/templates/book_table.html | 100 +++++++++--------- cps/web.py | 54 ++++++---- 60 files changed, 735 insertions(+), 427 deletions(-) create mode 100644 cps/static/js/libs/bootstrap-table/locale/bootstrap-table-fi-FI.min.js create mode 100644 cps/static/js/libs/bootstrap-table/locale/bootstrap-table-fr-CH.min.js create mode 100644 cps/static/js/libs/bootstrap-table/locale/bootstrap-table-fr-LU.min.js create mode 100644 cps/static/js/libs/bootstrap-table/locale/bootstrap-table-nl-BE.min.js create mode 100644 cps/static/js/libs/bootstrap-table/locale/bootstrap-table-sr-Cyrl-RS.min.js create mode 100644 cps/static/js/libs/bootstrap-table/locale/bootstrap-table-sr-Latn-RS.min.js diff --git a/cps/db.py b/cps/db.py index 01d50605..bf27b4d9 100644 --- a/cps/db.py +++ b/cps/db.py @@ -30,7 +30,8 @@ from sqlalchemy import create_engine from sqlalchemy import Table, Column, ForeignKey, CheckConstraint from sqlalchemy import String, Integer, Boolean, TIMESTAMP, Float from sqlalchemy.orm import relationship, sessionmaker, scoped_session -from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm.collections import InstrumentedList +from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta from sqlalchemy.exc import OperationalError from flask_login import current_user from sqlalchemy.sql.expression import and_, true, false, text, func, or_ @@ -97,6 +98,9 @@ class Identifiers(Base): self.type = id_type self.book = book + #def get(self): + # return {self.type: self.val} + def formatType(self): if self.type == "amazon": return u"Amazon" @@ -149,6 +153,9 @@ class Comments(Base): self.text = text self.book = book + def get(self): + return self.text + def __repr__(self): return u"".format(self.text) @@ -162,6 +169,9 @@ class Tags(Base): def __init__(self, name): self.name = name + def get(self): + return self.name + def __repr__(self): return u"".format(self.name) @@ -179,6 +189,9 @@ class Authors(Base): self.sort = sort self.link = link + def get(self): + return self.name + def __repr__(self): return u"".format(self.name, self.sort, self.link) @@ -194,6 +207,9 @@ class Series(Base): self.name = name self.sort = sort + def get(self): + return self.name + def __repr__(self): return u"".format(self.name, self.sort) @@ -207,6 +223,9 @@ class Ratings(Base): def __init__(self, rating): self.rating = rating + def get(self): + return self.rating + def __repr__(self): return u"".format(self.rating) @@ -220,6 +239,9 @@ class Languages(Base): def __init__(self, lang_code): self.lang_code = lang_code + def get(self): + return self.lang_code + def __repr__(self): return u"".format(self.lang_code) @@ -235,6 +257,9 @@ class Publishers(Base): self.name = name self.sort = sort + def get(self): + return self.name + def __repr__(self): return u"".format(self.name, self.sort) @@ -255,6 +280,10 @@ class Data(Base): self.uncompressed_size = uncompressed_size self.name = name + # ToDo: Check + def get(self): + return self.name + def __repr__(self): return u"".format(self.book, self.format, self.uncompressed_size, self.name) @@ -301,6 +330,9 @@ class Books(Base): self.path = path self.has_cover = has_cover + #def as_dict(self): + # return {c.name: getattr(self, c.name) for c in self.__table__.columns} + def __repr__(self): return u"".format(self.title, self.sort, self.author_sort, self.timestamp, self.pubdate, self.series_index, @@ -329,6 +361,39 @@ class Custom_Columns(Base): display_dict['enum_values'] = [x.decode('unicode_escape') for x in display_dict['enum_values']] return display_dict +class AlchemyEncoder(json.JSONEncoder): + + def default(self, obj): + if isinstance(obj.__class__, DeclarativeMeta): + # an SQLAlchemy class + fields = {} + for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']: + if field == 'books': + continue + data = obj.__getattribute__(field) + try: + if isinstance(data, str): + data = data.replace("'","\'") + elif isinstance(data, InstrumentedList): + el =list() + for ele in data: + if ele.get: + el.append(ele.get()) + else: + el.append(json.dumps(ele, cls=AlchemyEncoder)) + data =",".join(el) + if data == '[]': + data = "" + else: + json.dumps(data) + fields[field] = data + except: + fields[field] = "" + # a json-encodable dict + return fields + + return json.JSONEncoder.default(self, obj) + class CalibreDB(threading.Thread): @@ -507,10 +572,11 @@ class CalibreDB(threading.Thread): pos_content_cc_filter, ~neg_content_cc_filter, archived_filter) # Fill indexpage with all requested data from database - def fill_indexpage(self, page, database, db_filter, order, *join): - return self.fill_indexpage_with_archived_books(page, database, db_filter, order, False, *join) + def fill_indexpage(self, page, pagesize, database, db_filter, order, *join): + return self.fill_indexpage_with_archived_books(page, pagesize, database, db_filter, order, False, *join) - def fill_indexpage_with_archived_books(self, page, database, db_filter, order, allow_show_archived, *join): + def fill_indexpage_with_archived_books(self, page, pagesize, database, db_filter, order, allow_show_archived, *join): + pagesize = pagesize or self.config.config_books_per_page if current_user.show_detail_random(): randm = self.session.query(Books) \ .filter(self.common_filters(allow_show_archived)) \ @@ -518,14 +584,14 @@ class CalibreDB(threading.Thread): .limit(self.config.config_random_books) else: randm = false() - off = int(int(self.config.config_books_per_page) * (page - 1)) + off = int(int(pagesize) * (page - 1)) query = self.session.query(database) \ .join(*join, isouter=True) \ .filter(db_filter) \ .filter(self.common_filters(allow_show_archived)) - pagination = Pagination(page, self.config.config_books_per_page, + pagination = Pagination(page, pagesize, len(query.all())) - entries = query.order_by(*order).offset(off).limit(self.config.config_books_per_page).all() + entries = query.order_by(*order).offset(off).limit(pagesize).all() for book in entries: book = self.order_authors(book) return entries, randm, pagination @@ -565,7 +631,8 @@ class CalibreDB(threading.Thread): .filter(and_(Books.authors.any(and_(*q)), func.lower(Books.title).ilike("%" + title + "%"))).first() # read search results from calibre-database and return it (function is used for feed and simple search - def get_search_results(self, term): + def get_search_results(self, term, order=None, limit=-1): + order = order or [Books.sort] term.strip().lower() self.session.connection().connection.connection.create_function("lower", 1, lcase) q = list() @@ -578,7 +645,7 @@ class CalibreDB(threading.Thread): Books.authors.any(and_(*q)), Books.publishers.any(func.lower(Publishers.name).ilike("%" + term + "%")), func.lower(Books.title).ilike("%" + term + "%") - )).order_by(Books.sort).all() + )).order_by(*order).limit(limit).all() # Creates for all stored languages a translated speaking name in the array for the UI def speaking_language(self, languages=None): diff --git a/cps/opds.py b/cps/opds.py index 78d5d8ed..5f6cf88d 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -100,7 +100,7 @@ def feed_normal_search(): @requires_basic_auth_if_no_ano def feed_new(): off = request.args.get("offset") or 0 - entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, True, [db.Books.timestamp.desc()]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -118,7 +118,7 @@ def feed_discover(): @requires_basic_auth_if_no_ano def feed_best_rated(): off = request.args.get("offset") or 0 - entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, db.Books.ratings.any(db.Ratings.rating > 9), [db.Books.timestamp.desc()]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -164,7 +164,7 @@ def feed_authorindex(): @requires_basic_auth_if_no_ano def feed_author(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, db.Books.authors.any(db.Authors.id == book_id), [db.Books.timestamp.desc()]) @@ -190,7 +190,7 @@ def feed_publisherindex(): @requires_basic_auth_if_no_ano def feed_publisher(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, db.Books.publishers.any(db.Publishers.id == book_id), [db.Books.timestamp.desc()]) @@ -218,7 +218,7 @@ def feed_categoryindex(): @requires_basic_auth_if_no_ano def feed_category(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, db.Books.tags.any(db.Tags.id == book_id), [db.Books.timestamp.desc()]) @@ -245,7 +245,7 @@ def feed_seriesindex(): @requires_basic_auth_if_no_ano def feed_series(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, db.Books.series.any(db.Series.id == book_id), [db.Books.series_index]) @@ -276,7 +276,7 @@ def feed_ratingindex(): @requires_basic_auth_if_no_ano def feed_ratings(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, db.Books.ratings.any(db.Ratings.id == book_id), [db.Books.timestamp.desc()]) @@ -304,7 +304,7 @@ def feed_formatindex(): @requires_basic_auth_if_no_ano def feed_format(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, db.Books.data.any(db.Data.format == book_id.upper()), [db.Books.timestamp.desc()]) @@ -338,7 +338,7 @@ def feed_languagesindex(): @requires_basic_auth_if_no_ano def feed_languages(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0, db.Books, db.Books.languages.any(db.Languages.id == book_id), [db.Books.timestamp.desc()]) diff --git a/cps/static/css/libs/bootstrap-table.min.css b/cps/static/css/libs/bootstrap-table.min.css index 770b6728..72a8f74f 100644 --- a/cps/static/css/libs/bootstrap-table.min.css +++ b/cps/static/css/libs/bootstrap-table.min.css @@ -1 +1,10 @@ -.fixed-table-container .bs-checkbox,.fixed-table-container .no-records-found{text-align:center}.fixed-table-body thead th .th-inner,.table td,.table th{box-sizing:border-box}.bootstrap-table .table{margin-bottom:0!important;border-bottom:1px solid #ddd;border-collapse:collapse!important;border-radius:1px}.bootstrap-table .table:not(.table-condensed),.bootstrap-table .table:not(.table-condensed)>tbody>tr>td,.bootstrap-table .table:not(.table-condensed)>tbody>tr>th,.bootstrap-table .table:not(.table-condensed)>tfoot>tr>td,.bootstrap-table .table:not(.table-condensed)>tfoot>tr>th,.bootstrap-table .table:not(.table-condensed)>thead>tr>td{padding:8px}.bootstrap-table .table.table-no-bordered>tbody>tr>td,.bootstrap-table .table.table-no-bordered>thead>tr>th{border-right:2px solid transparent}.bootstrap-table .table.table-no-bordered>tbody>tr>td:last-child{border-right:none}.fixed-table-container{position:relative;clear:both;border:1px solid #ddd;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px}.fixed-table-container.table-no-bordered{border:1px solid transparent}.fixed-table-footer,.fixed-table-header{overflow:hidden}.fixed-table-footer{border-top:1px solid #ddd}.fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.fixed-table-container table{width:100%}.fixed-table-container thead th{height:0;padding:0;margin:0;border-left:1px solid #ddd}.fixed-table-container thead th:focus{outline:transparent solid 0}.fixed-table-container thead th:first-child:not([data-not-first-th]){border-left:none;border-top-left-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px}.fixed-table-container tbody td .th-inner,.fixed-table-container thead th .th-inner{padding:8px;line-height:24px;vertical-align:top;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fixed-table-container thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px}.fixed-table-container thead th .both{background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC')}.fixed-table-container thead th .asc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==)}.fixed-table-container thead th .desc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII=)}.fixed-table-container th.detail{width:30px}.fixed-table-container tbody td{border-left:1px solid #ddd}.fixed-table-container tbody tr:first-child td{border-top:none}.fixed-table-container tbody td:first-child{border-left:none}.fixed-table-container tbody .selected td{background-color:#f5f5f5}.fixed-table-container input[type=radio],.fixed-table-container input[type=checkbox]{margin:0 auto!important}.fixed-table-pagination .pagination-detail,.fixed-table-pagination div.pagination{margin-top:10px;margin-bottom:10px}.fixed-table-pagination div.pagination .pagination{margin:0}.fixed-table-pagination .pagination a{padding:6px 12px;line-height:1.428571429}.fixed-table-pagination .pagination-info{line-height:34px;margin-right:5px}.fixed-table-pagination .btn-group{position:relative;display:inline-block;vertical-align:middle}.fixed-table-pagination .dropup .dropdown-menu{margin-bottom:0}.fixed-table-pagination .page-list{display:inline-block}.fixed-table-toolbar .columns-left{margin-right:5px}.fixed-table-toolbar .columns-right{margin-left:5px}.fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.428571429}.fixed-table-toolbar .bs-bars,.fixed-table-toolbar .columns,.fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px;line-height:34px}.fixed-table-pagination li.disabled a{pointer-events:none;cursor:default}.fixed-table-loading{display:none;position:absolute;top:42px;right:0;bottom:0;left:0;z-index:99;background-color:#fff;text-align:center}.fixed-table-body .card-view .title{font-weight:700;display:inline-block;min-width:30%;text-align:left!important}.table td,.table th{vertical-align:middle}.fixed-table-toolbar .dropdown-menu{text-align:left;max-height:300px;overflow:auto}.fixed-table-toolbar .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.fixed-table-toolbar .btn-group>.btn-group>.btn{border-radius:0}.fixed-table-toolbar .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.fixed-table-toolbar .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.bootstrap-table .table>thead>tr>th{vertical-align:bottom;border-bottom:1px solid #ddd}.bootstrap-table .table thead>tr>th{padding:0;margin:0}.bootstrap-table .fixed-table-footer tbody>tr>td{padding:0!important}.bootstrap-table .fixed-table-footer .table{border-bottom:none;border-radius:0;padding:0!important}.bootstrap-table .pull-right .dropdown-menu{right:0;left:auto}p.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden}.fixed-table-pagination:after,.fixed-table-toolbar:after{content:"";display:block;clear:both}.fullscreen{position:fixed;top:0;left:0;z-index:1050;width:100%!important;background:#FFF} \ No newline at end of file +/** + * bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation) + * + * @version v1.16.0 + * @homepage https://bootstrap-table.com + * @author wenzhixin (http://wenzhixin.net.cn/) + * @license MIT + */ + +.bootstrap-table .fixed-table-toolbar::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-toolbar .bs-bars,.bootstrap-table .fixed-table-toolbar .columns,.bootstrap-table .fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group>.btn{border-radius:0}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu{text-align:left;max-height:300px;overflow:auto;-ms-overflow-style:scrollbar;z-index:1001}.bootstrap-table .fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.428571429}.bootstrap-table .fixed-table-toolbar .columns-left{margin-right:5px}.bootstrap-table .fixed-table-toolbar .columns-right{margin-left:5px}.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu{right:0;left:auto}.bootstrap-table .fixed-table-container{position:relative;clear:both}.bootstrap-table .fixed-table-container .table{width:100%;margin-bottom:0!important}.bootstrap-table .fixed-table-container .table td,.bootstrap-table .fixed-table-container .table th{vertical-align:middle;box-sizing:border-box}.bootstrap-table .fixed-table-container .table thead th{vertical-align:bottom;padding:0;margin:0}.bootstrap-table .fixed-table-container .table thead th:focus{outline:0 solid transparent}.bootstrap-table .fixed-table-container .table thead th.detail{width:30px}.bootstrap-table .fixed-table-container .table thead th .th-inner{padding:.75rem;vertical-align:bottom;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bootstrap-table .fixed-table-container .table thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px!important}.bootstrap-table .fixed-table-container .table thead th .both{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC")}.bootstrap-table .fixed-table-container .table thead th .asc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==)}.bootstrap-table .fixed-table-container .table thead th .desc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII=)}.bootstrap-table .fixed-table-container .table tbody tr.selected td{background-color:rgba(0,0,0,.075)}.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td{text-align:center}.bootstrap-table .fixed-table-container .table tbody tr .card-view{display:flex}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title{font-weight:700;display:inline-block;min-width:30%;text-align:left!important}.bootstrap-table .fixed-table-container .table .bs-checkbox{text-align:center}.bootstrap-table .fixed-table-container .table .bs-checkbox label{margin-bottom:0}.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=checkbox],.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=radio]{margin:0 auto!important}.bootstrap-table .fixed-table-container .table.table-sm .th-inner{padding:.3rem}.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer){border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height.has-card-view{border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border{border-left:1px solid #dee2e6;border-right:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table thead th{border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th{border-bottom:1px solid #32383e}.bootstrap-table .fixed-table-container .fixed-table-header{overflow:hidden}.bootstrap-table .fixed-table-container .fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading{align-items:center;background:#fff;display:none;justify-content:center;position:absolute;bottom:0;width:100%;z-index:1000}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap{align-items:baseline;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text{font-size:2rem;margin-right:6px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap{align-items:center;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before{content:"";animation-duration:1.5s;animation-iteration-count:infinite;animation-name:LOADING;background:#212529;border-radius:50%;display:block;height:5px;margin:0 4px;opacity:0;width:5px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot{animation-delay:.3s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after{animation-delay:.6s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark{background:#212529}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before{background:#fff}.bootstrap-table .fixed-table-container .fixed-table-footer{overflow:hidden}.bootstrap-table .fixed-table-pagination::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-pagination>.pagination,.bootstrap-table .fixed-table-pagination>.pagination-detail{margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-pagination>.pagination-detail .pagination-info{line-height:34px;margin-right:5px}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list{display:inline-block}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group{position:relative;display:inline-block;vertical-align:middle}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group .dropdown-menu{margin-bottom:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination{margin:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination a{padding:6px 12px;line-height:1.428571429}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a{color:#c8c8c8}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::before{content:'\2B05'}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::after{content:'\27A1'}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.disabled a{pointer-events:none;cursor:default}.bootstrap-table.fullscreen{position:fixed;top:0;left:0;z-index:1050;width:100%!important;background:#fff;height:calc(100vh);overflow-y:scroll}div.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden}@keyframes LOADING{0%{opacity:0}50%{opacity:1}to{opacity:0}} \ No newline at end of file diff --git a/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js b/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js index e93e8246..cd411037 100644 --- a/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js +++ b/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js @@ -1,7 +1,10 @@ -/* -* bootstrap-table - v1.12.1 - 2018-03-12 -* https://github.com/wenzhixin/bootstrap-table -* Copyright (c) 2018 zhixin wen -* Licensed MIT License -*/ -!function(a){"use strict";a.extend(a.fn.bootstrapTable.defaults,{editable:!0,onEditableInit:function(){return!1},onEditableSave:function(){return!1},onEditableShown:function(){return!1},onEditableHidden:function(){return!1}}),a.extend(a.fn.bootstrapTable.Constructor.EVENTS,{"editable-init.bs.table":"onEditableInit","editable-save.bs.table":"onEditableSave","editable-shown.bs.table":"onEditableShown","editable-hidden.bs.table":"onEditableHidden"});var b=a.fn.bootstrapTable.Constructor,c=b.prototype.initTable,d=b.prototype.initBody;b.prototype.initTable=function(){var b=this;c.apply(this,Array.prototype.slice.apply(arguments)),this.options.editable&&a.each(this.columns,function(c,d){if(d.editable){var e={},f=[],g="editable-",h=function(a,b){var c=a.replace(/([A-Z])/g,function(a){return"-"+a.toLowerCase()});if(c.slice(0,g.length)==g){var d=c.replace(g,"data-");e[d]=b}};a.each(b.options,h),d.formatter=d.formatter||function(a){return a},d._formatter=d._formatter?d._formatter:d.formatter,d.formatter=function(c,g,i){var j=d._formatter?d._formatter(c,g,i):c;a.each(d,h),a.each(e,function(a,b){f.push(" "+a+'="'+b+'"')});var k=!1;return d.editable.hasOwnProperty("noeditFormatter")&&(k=d.editable.noeditFormatter(c,g,i)),k===!1?['"].join(""):k}}})},b.prototype.initBody=function(){var b=this;d.apply(this,Array.prototype.slice.apply(arguments)),this.options.editable&&(a.each(this.columns,function(c,d){d.editable&&(b.$body.find('a[data-name="'+d.field+'"]').editable(d.editable).off("save").on("save",function(c,e){var f=b.getData(),g=a(this).parents("tr[data-index]").data("index"),h=f[g],i=h[d.field];a(this).data("value",e.submitValue),h[d.field]=e.submitValue,b.trigger("editable-save",d.field,h,i,a(this)),b.resetFooter()}),b.$body.find('a[data-name="'+d.field+'"]').editable(d.editable).off("shown").on("shown",function(c,e){var f=b.getData(),g=a(this).parents("tr[data-index]").data("index"),h=f[g];b.trigger("editable-shown",d.field,h,a(this),e)}),b.$body.find('a[data-name="'+d.field+'"]').editable(d.editable).off("hidden").on("hidden",function(c,e){var f=b.getData(),g=a(this).parents("tr[data-index]").data("index"),h=f[g];b.trigger("editable-hidden",d.field,h,a(this),e)}))}),this.trigger("editable-init"))}}(jQuery); \ No newline at end of file +/** + * bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation) + * + * @version v1.16.0 + * @homepage https://bootstrap-table.com + * @author wenzhixin (http://wenzhixin.net.cn/) + * @license MIT + */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],e):e((t=t||self).jQuery)}(this,(function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function n(t,e){return t(e={exports:{}},e.exports),e.exports}var r=function(t){return t&&t.Math==Math&&t},o=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof e&&e)||Function("return this")(),i=function(t){try{return!!t()}catch(t){return!0}},a=!i((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})),c={}.propertyIsEnumerable,u=Object.getOwnPropertyDescriptor,f={f:u&&!c.call({1:2},1)?function(t){var e=u(this,t);return!!e&&e.enumerable}:c},l=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},s={}.toString,d=function(t){return s.call(t).slice(8,-1)},p="".split,h=i((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==d(t)?p.call(t,""):Object(t)}:Object,v=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},y=function(t){return h(v(t))},g=function(t){return"object"==typeof t?null!==t:"function"==typeof t},b=function(t,e){if(!g(t))return t;var n,r;if(e&&"function"==typeof(n=t.toString)&&!g(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!g(r=n.call(t)))return r;if(!e&&"function"==typeof(n=t.toString)&&!g(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")},m={}.hasOwnProperty,x=function(t,e){return m.call(t,e)},O=o.document,w=g(O)&&g(O.createElement),E=function(t){return w?O.createElement(t):{}},j=!a&&!i((function(){return 7!=Object.defineProperty(E("div"),"a",{get:function(){return 7}}).a})),S=Object.getOwnPropertyDescriptor,T={f:a?S:function(t,e){if(t=y(t),e=b(e,!0),j)try{return S(t,e)}catch(t){}if(x(t,e))return l(!f.f.call(t,e),t[e])}},P=function(t){if(!g(t))throw TypeError(String(t)+" is not an object");return t},A=Object.defineProperty,_={f:a?A:function(t,e,n){if(P(t),e=b(e,!0),P(n),j)try{return A(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},I=a?function(t,e,n){return _.f(t,e,l(1,n))}:function(t,e,n){return t[e]=n,t},R=function(t,e){try{I(o,t,e)}catch(n){o[t]=e}return e},C=o["__core-js_shared__"]||R("__core-js_shared__",{}),k=Function.toString;"function"!=typeof C.inspectSource&&(C.inspectSource=function(t){return k.call(t)});var M,$,F,D=C.inspectSource,N=o.WeakMap,q="function"==typeof N&&/native code/.test(D(N)),B=n((function(t){(t.exports=function(t,e){return C[t]||(C[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.6.0",mode:"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})})),L=0,K=Math.random(),V=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++L+K).toString(36)},U=B("keys"),W=function(t){return U[t]||(U[t]=V(t))},z={},Y=o.WeakMap;if(q){var G=new Y,H=G.get,Q=G.has,X=G.set;M=function(t,e){return X.call(G,t,e),e},$=function(t){return H.call(G,t)||{}},F=function(t){return Q.call(G,t)}}else{var Z=W("state");z[Z]=!0,M=function(t,e){return I(t,Z,e),e},$=function(t){return x(t,Z)?t[Z]:{}},F=function(t){return x(t,Z)}}var J,tt,et={set:M,get:$,has:F,enforce:function(t){return F(t)?$(t):M(t,{})},getterFor:function(t){return function(e){var n;if(!g(e)||(n=$(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return n}}},nt=n((function(t){var e=et.get,n=et.enforce,r=String(String).split("String");(t.exports=function(t,e,i,a){var c=!!a&&!!a.unsafe,u=!!a&&!!a.enumerable,f=!!a&&!!a.noTargetGet;"function"==typeof i&&("string"!=typeof e||x(i,"name")||I(i,"name",e),n(i).source=r.join("string"==typeof e?e:"")),t!==o?(c?!f&&t[e]&&(u=!0):delete t[e],u?t[e]=i:I(t,e,i)):u?t[e]=i:R(e,i)})(Function.prototype,"toString",(function(){return"function"==typeof this&&e(this).source||D(this)}))})),rt=o,ot=function(t){return"function"==typeof t?t:void 0},it=function(t,e){return arguments.length<2?ot(rt[t])||ot(o[t]):rt[t]&&rt[t][e]||o[t]&&o[t][e]},at=Math.ceil,ct=Math.floor,ut=function(t){return isNaN(t=+t)?0:(t>0?ct:at)(t)},ft=Math.min,lt=function(t){return t>0?ft(ut(t),9007199254740991):0},st=Math.max,dt=Math.min,pt=function(t){return function(e,n,r){var o,i=y(e),a=lt(i.length),c=function(t,e){var n=ut(t);return n<0?st(n+e,0):dt(n,e)}(r,a);if(t&&n!=n){for(;a>c;)if((o=i[c++])!=o)return!0}else for(;a>c;c++)if((t||c in i)&&i[c]===n)return t||c||0;return!t&&-1}},ht={includes:pt(!0),indexOf:pt(!1)},vt=ht.indexOf,yt=function(t,e){var n,r=y(t),o=0,i=[];for(n in r)!x(z,n)&&x(r,n)&&i.push(n);for(;e.length>o;)x(r,n=e[o++])&&(~vt(i,n)||i.push(n));return i},gt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],bt=gt.concat("length","prototype"),mt={f:Object.getOwnPropertyNames||function(t){return yt(t,bt)}},xt={f:Object.getOwnPropertySymbols},Ot=it("Reflect","ownKeys")||function(t){var e=mt.f(P(t)),n=xt.f;return n?e.concat(n(t)):e},wt=function(t,e){for(var n=Ot(e),r=_.f,o=T.f,i=0;i=74)&&(J=Vt.match(/Chrome\/(\d+)/))&&(tt=J[1]);var Yt,Gt=tt&&+tt,Ht=Bt("species"),Qt=Bt("isConcatSpreadable"),Xt=Gt>=51||!i((function(){var t=[];return t[Qt]=!1,t.concat()[0]!==t})),Zt=(Yt="concat",Gt>=51||!i((function(){var t=[];return(t.constructor={})[Ht]=function(){return{foo:1}},1!==t[Yt](Boolean).foo}))),Jt=function(t){if(!g(t))return!1;var e=t[Qt];return void 0!==e?!!e:Ct(t)};Rt({target:"Array",proto:!0,forced:!Xt||!Zt},{concat:function(t){var e,n,r,o,i,a=kt(this),c=Kt(a,0),u=0;for(e=-1,r=arguments.length;e9007199254740991)throw TypeError("Maximum allowed index exceeded");for(n=0;n=9007199254740991)throw TypeError("Maximum allowed index exceeded");Mt(c,u++,i)}return c.length=u,c}});var te,ee=function(t,e,n){if(function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function")}(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}},ne=[].push,re=function(t){var e=1==t,n=2==t,r=3==t,o=4==t,i=6==t,a=5==t||i;return function(c,u,f,l){for(var s,d,p=kt(c),v=h(p),y=ee(u,f,3),g=lt(v.length),b=0,m=l||Kt,x=e?m(c,g):n?m(c,0):void 0;g>b;b++)if((a||b in v)&&(d=y(s=v[b],b,p),t))if(e)x[b]=d;else if(d)switch(t){case 3:return!0;case 5:return s;case 6:return b;case 2:ne.call(x,s)}else if(o)return!1;return i?-1:r||o?o:x}},oe={forEach:re(0),map:re(1),filter:re(2),some:re(3),every:re(4),find:re(5),findIndex:re(6)},ie=Object.keys||function(t){return yt(t,gt)},ae=a?Object.defineProperties:function(t,e){P(t);for(var n,r=ie(e),o=r.length,i=0;o>i;)_.f(t,n=r[i++],e[n]);return t},ce=it("document","documentElement"),ue=W("IE_PROTO"),fe=function(){},le=function(t){return" + + + + {% endblock %} diff --git a/cps/web.py b/cps/web.py index f88cdaa4..e46a898b 100644 --- a/cps/web.py +++ b/cps/web.py @@ -620,7 +620,7 @@ def get_matching_tags(): @web.route('/page/') @login_required_if_no_ano def index(page): - entries, random, pagination = calibre_db.fill_indexpage(page, db.Books, True, [db.Books.timestamp.desc()]) + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, [db.Books.timestamp.desc()]) return render_title_template('index.html', random=random, entries=entries, pagination=pagination, title=_(u"Recently Added Books"), page="root") @@ -647,7 +647,7 @@ def books_list(data, sort, book_id, page): if data == "rated": if current_user.check_visibility(constants.SIDEBAR_BEST_RATED): - entries, random, pagination = calibre_db.fill_indexpage(page, + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db.Books.ratings.any(db.Ratings.rating > 9), order) @@ -657,7 +657,7 @@ def books_list(data, sort, book_id, page): abort(404) elif data == "discover": if current_user.check_visibility(constants.SIDEBAR_RANDOM): - entries, __, pagination = calibre_db.fill_indexpage(page, db.Books, True, [func.randomblob(2)]) + entries, __, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, [func.randomblob(2)]) pagination = Pagination(1, config.config_books_per_page, config.config_books_per_page) return render_title_template('discover.html', entries=entries, pagination=pagination, id=book_id, title=_(u"Discover (Random Books)"), page="discover") @@ -686,7 +686,7 @@ def books_list(data, sort, book_id, page): elif data == "archived": return render_archived_books(page, order) else: - entries, random, pagination = calibre_db.fill_indexpage(page, db.Books, True, order) + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, order) return render_title_template('index.html', random=random, entries=entries, pagination=pagination, title=_(u"Books"), page="newest") @@ -721,7 +721,7 @@ def render_hot_books(page): def render_author_books(page, author_id, order): - entries, __, pagination = calibre_db.fill_indexpage(page, + entries, __, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db.Books.authors.any(db.Authors.id == author_id), [order[0], db.Series.name, db.Books.series_index], @@ -749,7 +749,7 @@ def render_author_books(page, author_id, order): def render_publisher_books(page, book_id, order): publisher = calibre_db.session.query(db.Publishers).filter(db.Publishers.id == book_id).first() if publisher: - entries, random, pagination = calibre_db.fill_indexpage(page, + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db.Books.publishers.any(db.Publishers.id == book_id), [db.Series.name, order[0], db.Books.series_index], @@ -764,7 +764,7 @@ def render_publisher_books(page, book_id, order): def render_series_books(page, book_id, order): name = calibre_db.session.query(db.Series).filter(db.Series.id == book_id).first() if name: - entries, random, pagination = calibre_db.fill_indexpage(page, + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db.Books.series.any(db.Series.id == book_id), [db.Books.series_index, order[0]]) @@ -776,7 +776,7 @@ def render_series_books(page, book_id, order): def render_ratings_books(page, book_id, order): name = calibre_db.session.query(db.Ratings).filter(db.Ratings.id == book_id).first() - entries, random, pagination = calibre_db.fill_indexpage(page, + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db.Books.ratings.any(db.Ratings.id == book_id), [db.Books.timestamp.desc(), order[0]]) @@ -790,7 +790,7 @@ def render_ratings_books(page, book_id, order): def render_formats_books(page, book_id, order): name = calibre_db.session.query(db.Data).filter(db.Data.format == book_id.upper()).first() if name: - entries, random, pagination = calibre_db.fill_indexpage(page, + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db.Books.data.any(db.Data.format == book_id.upper()), [db.Books.timestamp.desc(), order[0]]) @@ -803,7 +803,7 @@ def render_formats_books(page, book_id, order): def render_category_books(page, book_id, order): name = calibre_db.session.query(db.Tags).filter(db.Tags.id == book_id).first() if name: - entries, random, pagination = calibre_db.fill_indexpage(page, + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db.Books.tags.any(db.Tags.id == book_id), [order[0], db.Series.name, db.Books.series_index], @@ -823,7 +823,7 @@ def render_language_books(page, name, order): lang_name = _(isoLanguages.get(part3=name).name) except KeyError: abort(404) - entries, random, pagination = calibre_db.fill_indexpage(page, + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db.Books.languages.any(db.Languages.lang_code == name), [db.Books.timestamp.desc(), order[0]]) @@ -834,14 +834,32 @@ def render_language_books(page, name, order): @web.route("/table") @login_required_if_no_ano def books_table(): - return render_title_template('book_table.html', title=_(u"Books list"), page="table") + # __, __, pagination = calibre_db.fill_indexpage(1, 0, db.Books, True, [db.Books.timestamp.asc()]) + return render_title_template('book_table.html', title=_(u"Books list"), page="book_table") #, pagination=pagination) @web.route("/ajax/listbooks") @login_required_if_no_ano def list_books(): - order = [db.Books.timestamp.desc()] - entries, __, __ = calibre_db.fill_indexpage(1, db.Books, True, order) - js_list = json.dumps(entries, cls=db.AlchemyEncoder) + off = request.args.get("offset") or 0 + limit = request.args.get("limit") or config.config_books_per_page + sort = request.args.get("sort") + if request.args.get("order") == 'asc': + order = [db.Books.timestamp.asc()] + else: + order = [db.Books.timestamp.desc()] + search = request.args.get("search") + total_count = calibre_db.session.query(db.Books).count() + if search: + entries = calibre_db.get_search_results(search, order, limit) + #ToDo not right web.py 1259 + filtered_count = len(entries) + else: + entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), limit, db.Books, True, order) + filtered_count = total_count + table_entries = {'totalNotFiltered': total_count, 'total': filtered_count, "rows": entries} + js_list = json.dumps(table_entries, cls=db.AlchemyEncoder) + #js_list = json.dumps(entries, cls=db.AlchemyEncoder) + response = make_response(js_list) response.headers["Content-Type"] = "application/json; charset=utf-8" return response @@ -1210,7 +1228,7 @@ def render_read_books(page, are_read, as_xml=False, order=None, *args, **kwargs) ub.ReadBook.read_status == ub.ReadBook.STATUS_FINISHED) else: db_filter = coalesce(ub.ReadBook.read_status, 0) != ub.ReadBook.STATUS_FINISHED - entries, random, pagination = calibre_db.fill_indexpage(page, + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db_filter, order, @@ -1221,7 +1239,7 @@ def render_read_books(page, are_read, as_xml=False, order=None, *args, **kwargs) db_filter = db.cc_classes[config.config_read_column].value == True else: db_filter = coalesce(db.cc_classes[config.config_read_column].value, False) != True - entries, random, pagination = calibre_db.fill_indexpage(page, + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db_filter, order, @@ -1259,7 +1277,7 @@ def render_archived_books(page, order): archived_filter = db.Books.id.in_(archived_book_ids) - entries, random, pagination = calibre_db.fill_indexpage_with_archived_books(page, + entries, random, pagination = calibre_db.fill_indexpage_with_archived_books(page, 0, db.Books, archived_filter, order, From 4038cb5b856324c0f488b87a4b1f0a7669f90e35 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Mon, 8 Jun 2020 17:34:03 +0200 Subject: [PATCH 06/95] Internal paged Search prepared Search for table list is working --- cps/db.py | 11 +++++-- cps/opds.py | 2 +- cps/static/js/table.js | 30 ++++++++++++++++-- cps/templates/book_edit.html | 29 +---------------- cps/templates/book_table.html | 9 +++--- cps/templates/layout.html | 2 +- ...al_restriction.html => modal_dialogs.html} | 31 +++++++++++++++++++ cps/templates/search.html | 2 +- cps/web.py | 14 ++++----- 9 files changed, 82 insertions(+), 48 deletions(-) rename cps/templates/{modal_restriction.html => modal_dialogs.html} (61%) diff --git a/cps/db.py b/cps/db.py index bf27b4d9..2e3fa29e 100644 --- a/cps/db.py +++ b/cps/db.py @@ -631,21 +631,26 @@ class CalibreDB(threading.Thread): .filter(and_(Books.authors.any(and_(*q)), func.lower(Books.title).ilike("%" + title + "%"))).first() # read search results from calibre-database and return it (function is used for feed and simple search - def get_search_results(self, term, order=None, limit=-1): + def get_search_results(self, term, offset=None, order=None, limit=None): order = order or [Books.sort] + if offset != None and limit != None: + offset = int(offset) + limit = offset + int(limit) term.strip().lower() self.session.connection().connection.connection.create_function("lower", 1, lcase) q = list() authorterms = re.split("[, ]+", term) for authorterm in authorterms: q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%"))) - return self.session.query(Books).filter(self.common_filters(True)).filter( + result = self.session.query(Books).filter(self.common_filters(True)).filter( or_(Books.tags.any(func.lower(Tags.name).ilike("%" + term + "%")), Books.series.any(func.lower(Series.name).ilike("%" + term + "%")), Books.authors.any(and_(*q)), Books.publishers.any(func.lower(Publishers.name).ilike("%" + term + "%")), func.lower(Books.title).ilike("%" + term + "%") - )).order_by(*order).limit(limit).all() + )).order_by(*order).all() + result_count = len(result) + return result[offset:limit], result_count # Creates for all stored languages a translated speaking name in the array for the UI def speaking_language(self, languages=None): diff --git a/cps/opds.py b/cps/opds.py index 5f6cf88d..ac0e103b 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -408,7 +408,7 @@ def get_metadata_calibre_companion(uuid, library): def feed_search(term): if term: - entries = calibre_db.get_search_results(term) + entries, __ = calibre_db.get_search_results(term) entriescount = len(entries) if len(entries) > 0 else 1 pagination = Pagination(1, entriescount, entriescount) return render_xml_template('feed.xml', searchterm=term, entries=entries, pagination=pagination) diff --git a/cps/static/js/table.js b/cps/static/js/table.js index 479c7815..879c80eb 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -100,6 +100,33 @@ $(function() { $(e.currentTarget).find("#btndeletedomain").data("domainId", domainId); }); + $("#delete_confirm").click(function() { + //get data-id attribute of the clicked element + var deleteId = $(this).data("deleteid"); + $.ajax({ + method:"get", + url: window.location.pathname + "/../../delete"/+deleteId, + }); + }); + + //triggered when modal is about to be shown + $("#deleteModal").on("show.bs.modal", function(e) { + //get data-id attribute of the clicked element and store in button + var bookId = $(e.relatedTarget).data("delete-id"); + $(e.currentTarget).find("#delete_confirm").data("deleteid", bookId); + }); + // receive result from request, dismiss modal dialog, show flash message + // insert after navbar + /*$("#deleteModal").on("hidden.bs.modal", function () { +
+
{{ message[1] }}
+
*/ + + // to save current setting + // coresponding event: onColumnSwitch + //$table.bootstrapTable('getVisibleColumns') + //$table.bootstrapTable('getHiddenColumns'). + $("#restrictModal").on("hidden.bs.modal", function () { // Destroy table and remove hooks for buttons $("#restrict-elements-table").unbind(); @@ -223,9 +250,8 @@ function RestrictionActions (value, row) { /* Function for deleting books */ function EbookActions (value, row) { return [ - "
", + "
", "", "
" ].join(""); } - diff --git a/cps/templates/book_edit.html b/cps/templates/book_edit.html index 14bc590a..1f7b57b4 100644 --- a/cps/templates/book_edit.html +++ b/cps/templates/book_edit.html @@ -192,34 +192,7 @@ {% endblock %} {% block modal %} -{% if g.user.role_delete_books() %} - -{% endif %} +{{ delete_book(book.id) }} {% endmacro %} +{% macro delete_book(bookid) %} +{% if g.user.role_delete_books() %} + +{% endif %} +{% endmacro %} diff --git a/cps/templates/search.html b/cps/templates/search.html index da59efa6..b0dbc4af 100644 --- a/cps/templates/search.html +++ b/cps/templates/search.html @@ -5,7 +5,7 @@

{{_('No Results Found')}} {{adv_searchterm}}

{{_('Search Term:')}} {{adv_searchterm}}

{% else %} -

{{entries|length}} {{_('Results for:')}} {{adv_searchterm}}

+

{{result_count}} {{_('Results for:')}} {{adv_searchterm}}

{% if g.user.is_authenticated %} {% if g.user.shelf.all() or g.shelves_access %} {% endif %} - {% if audioentries|length > 0 %} + {% if audioentries|length > 0 and g.user.role_viewer() %}
diff --git a/cps/templates/book_table.html b/cps/templates/book_table.html index 4e19c39b..fcede828 100644 --- a/cps/templates/book_table.html +++ b/cps/templates/book_table.html @@ -20,7 +20,9 @@ data-show-search-button="false" data-search-on-enter-key="true" data-checkbox-header="false" - data-maintain-meta-data="true"> + data-maintain-meta-data="true" + data-editable-emptytext=""> + @@ -34,7 +36,7 @@ Authors Tags Series - Series Index + Series Index Language Publishing Date Publishers diff --git a/cps/templates/detail.html b/cps/templates/detail.html index 9014c8a5..956325a7 100644 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -273,7 +273,7 @@ {% if g.user.role_edit() %} {% endif %} diff --git a/cps/templates/layout.html b/cps/templates/layout.html index 34b98159..07659822 100644 --- a/cps/templates/layout.html +++ b/cps/templates/layout.html @@ -155,27 +155,26 @@ {% if pagination and (pagination.has_next or pagination.has_prev) %} {% endif %} - +