mirror of
				https://github.com/janeczku/calibre-web
				synced 2025-10-31 15:23:02 +00:00 
			
		
		
		
	Merge branch 'master' of https://github.com/janeczku/calibre-web
This commit is contained in:
		
							
								
								
									
										11
									
								
								cps/admin.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								cps/admin.py
									
									
									
									
									
								
							| @@ -1186,11 +1186,20 @@ def _db_configuration_update_helper(): | |||||||
|         if not calibre_db.setup_db(to_save['config_calibre_dir'], ub.app_DB_path): |         if not calibre_db.setup_db(to_save['config_calibre_dir'], ub.app_DB_path): | ||||||
|             return _db_configuration_result(_('DB Location is not Valid, Please Enter Correct Path'), |             return _db_configuration_result(_('DB Location is not Valid, Please Enter Correct Path'), | ||||||
|                                             gdrive_error) |                                             gdrive_error) | ||||||
|  |         # if db changed -> delete shelfs, delete download books, delete read books, kobo sync... | ||||||
|  |         ub.session.query(ub.Downloads).delete() | ||||||
|  |         ub.session.query(ub.ArchivedBook).delete() | ||||||
|  |         ub.session.query(ub.ArchivedBook).delete() | ||||||
|  |         ub.session.query(ub.ReadBook).delete() | ||||||
|  |         ub.session.query(ub.BookShelf).delete() | ||||||
|  |         ub.session.query(ub.Bookmark).delete() | ||||||
|  |         ub.session.query(ub.KoboReadingState).delete() | ||||||
|  |         ub.session.query(ub.KoboStatistics).delete() | ||||||
|  |         ub.session.query(ub.KoboSyncedBooks).delete() | ||||||
|         _config_string(to_save, "config_calibre_dir") |         _config_string(to_save, "config_calibre_dir") | ||||||
|         calibre_db.update_config(config) |         calibre_db.update_config(config) | ||||||
|         if not os.access(os.path.join(config.config_calibre_dir, "metadata.db"), os.W_OK): |         if not os.access(os.path.join(config.config_calibre_dir, "metadata.db"), os.W_OK): | ||||||
|             flash(_(u"DB is not Writeable"), category="warning") |             flash(_(u"DB is not Writeable"), category="warning") | ||||||
|             # warning = {'type': "warning", 'message': _(u"DB is not Writeable")} |  | ||||||
|     config.save() |     config.save() | ||||||
|     return _db_configuration_result(None, gdrive_error) |     return _db_configuration_result(None, gdrive_error) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -550,11 +550,8 @@ class CalibreDB(): | |||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def setup_db(cls, config_calibre_dir, app_db_path): |     def setup_db(cls, config_calibre_dir, app_db_path): | ||||||
|         # cls.config = config |  | ||||||
|         cls.dispose() |         cls.dispose() | ||||||
|  |  | ||||||
|         # toDo: if db changed -> delete shelfs, delete download books, delete read boks, kobo sync?? |  | ||||||
|  |  | ||||||
|         if not config_calibre_dir: |         if not config_calibre_dir: | ||||||
|             cls.config.invalidate() |             cls.config.invalidate() | ||||||
|             return False |             return False | ||||||
|   | |||||||
| @@ -823,7 +823,7 @@ def get_cc_columns(filter_config_custom_read=False): | |||||||
|  |  | ||||||
| def get_download_link(book_id, book_format, client): | def get_download_link(book_id, book_format, client): | ||||||
|     book_format = book_format.split(".")[0] |     book_format = book_format.split(".")[0] | ||||||
|     book = calibre_db.get_filtered_book(book_id) |     book = calibre_db.get_filtered_book(book_id, allow_show_archived=True) | ||||||
|     if book: |     if book: | ||||||
|         data1 = calibre_db.get_book_format(book.id, book_format.upper()) |         data1 = calibre_db.get_book_format(book.id, book_format.upper()) | ||||||
|     else: |     else: | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								cps/kobo.py
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								cps/kobo.py
									
									
									
									
									
								
							| @@ -355,7 +355,9 @@ def HandleMetadataRequest(book_uuid): | |||||||
|         return redirect_or_proxy_request() |         return redirect_or_proxy_request() | ||||||
|  |  | ||||||
|     metadata = get_metadata(book) |     metadata = get_metadata(book) | ||||||
|     return jsonify([metadata]) |     response = make_response(json.dumps([metadata], ensure_ascii=False)) | ||||||
|  |     response.headers["Content-Type"] = "application/json; charset=utf-8" | ||||||
|  |     return response | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_download_url_for_book(book, book_format): | def get_download_url_for_book(book, book_format): | ||||||
| @@ -413,15 +415,12 @@ def get_description(book): | |||||||
| def get_author(book): | def get_author(book): | ||||||
|     if not book.authors: |     if not book.authors: | ||||||
|         return {"Contributors": None} |         return {"Contributors": None} | ||||||
|     if len(book.authors) > 1: |     author_list = [] | ||||||
|         author_list = [] |     autor_roles = [] | ||||||
|         autor_roles = [] |     for author in book.authors: | ||||||
|         for author in book.authors: |         autor_roles.append({"Name":author.name})    #.encode('unicode-escape').decode('latin-1') | ||||||
|             autor_roles.append({"Name":author.name})    #.encode('unicode-escape').decode('latin-1') |         author_list.append(author.name) | ||||||
|             author_list.append(author.name) |     return {"ContributorRoles": autor_roles, "Contributors":author_list} | ||||||
|         return {"ContributorRoles": autor_roles, "Contributors":author_list} |  | ||||||
|     return {"ContributorRoles": [{"Name":book.authors[0].name}], |  | ||||||
|             "Contributors": book.authors[0].name} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_publisher(book): | def get_publisher(book): | ||||||
| @@ -987,6 +986,25 @@ def HandleUserRequest(dummy=None): | |||||||
|     return redirect_or_proxy_request() |     return redirect_or_proxy_request() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @csrf.exempt | ||||||
|  | @kobo.route("/v1/user/loyalty/benefits", methods=["GET"]) | ||||||
|  | def handle_benefits(): | ||||||
|  |     if config.config_kobo_proxy: | ||||||
|  |         return redirect_or_proxy_request() | ||||||
|  |     else: | ||||||
|  |         return make_response(jsonify({"Benefits": {}})) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @csrf.exempt | ||||||
|  | @kobo.route("/v1/analytics/gettests", methods=["GET", "POST"]) | ||||||
|  | def handle_getests(): | ||||||
|  |     if config.config_kobo_proxy: | ||||||
|  |         return redirect_or_proxy_request() | ||||||
|  |     else: | ||||||
|  |         testkey = request.headers.get("X-Kobo-userkey","") | ||||||
|  |         return make_response(jsonify({"Result": "Success", "TestKey":testkey, "Tests": {}})) | ||||||
|  |  | ||||||
|  |  | ||||||
| @csrf.exempt | @csrf.exempt | ||||||
| @kobo.route("/v1/products/<dummy>/prices", methods=["GET", "POST"]) | @kobo.route("/v1/products/<dummy>/prices", methods=["GET", "POST"]) | ||||||
| @kobo.route("/v1/products/<dummy>/recommendations", methods=["GET", "POST"]) | @kobo.route("/v1/products/<dummy>/recommendations", methods=["GET", "POST"]) | ||||||
| @@ -1002,6 +1020,7 @@ def HandleUserRequest(dummy=None): | |||||||
| @kobo.route("/v1/products/deals", methods=["GET", "POST"]) | @kobo.route("/v1/products/deals", methods=["GET", "POST"]) | ||||||
| @kobo.route("/v1/products", methods=["GET", "POST"]) | @kobo.route("/v1/products", methods=["GET", "POST"]) | ||||||
| @kobo.route("/v1/affiliate", methods=["GET", "POST"]) | @kobo.route("/v1/affiliate", methods=["GET", "POST"]) | ||||||
|  | @kobo.route("/v1/deals", methods=["GET", "POST"]) | ||||||
| def HandleProductsRequest(dummy=None): | def HandleProductsRequest(dummy=None): | ||||||
|     log.debug("Unimplemented Products Request received: %s", request.base_url) |     log.debug("Unimplemented Products Request received: %s", request.base_url) | ||||||
|     return redirect_or_proxy_request() |     return redirect_or_proxy_request() | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ | |||||||
| from flask_login import current_user | from flask_login import current_user | ||||||
| from . import ub | from . import ub | ||||||
| import datetime | import datetime | ||||||
| from sqlalchemy.sql.expression import or_ | from sqlalchemy.sql.expression import or_, and_ | ||||||
|  |  | ||||||
| # Add the current book id to kobo_synced_books table for current user, if entry is already present, | # Add the current book id to kobo_synced_books table for current user, if entry is already present, | ||||||
| # do nothing (safety precaution) | # do nothing (safety precaution) | ||||||
| @@ -42,18 +42,18 @@ def remove_synced_book(book_id): | |||||||
|     ub.session_commit() |     ub.session_commit() | ||||||
|  |  | ||||||
|  |  | ||||||
| def add_archived_books(book_id): | def change_archived_books(book_id, state=None, message=None): | ||||||
|     archived_book = (ub.session.query(ub.ArchivedBook) |     archived_book = ub.session.query(ub.ArchivedBook).filter(and_(ub.ArchivedBook.user_id == int(current_user.id), | ||||||
|                      .filter(ub.ArchivedBook.book_id == book_id) |                                                                   ub.ArchivedBook.book_id == book_id)).first() | ||||||
|                      .filter(ub.ArchivedBook.user_id == current_user.id) |  | ||||||
|                      .first()) |  | ||||||
|     if not archived_book: |     if not archived_book: | ||||||
|         archived_book = ub.ArchivedBook(user_id=current_user.id, book_id=book_id) |         archived_book = ub.ArchivedBook(user_id=current_user.id, book_id=book_id) | ||||||
|     archived_book.is_archived = True |  | ||||||
|  |     archived_book.is_archived = state if state else not archived_book.is_archived | ||||||
|     archived_book.last_modified = datetime.datetime.utcnow() |     archived_book.last_modified = datetime.datetime.utcnow() | ||||||
|  |  | ||||||
|     ub.session.merge(archived_book) |     ub.session.merge(archived_book) | ||||||
|     ub.session_commit() |     ub.session_commit(message) | ||||||
|  |     return archived_book.is_archived | ||||||
|  |  | ||||||
|  |  | ||||||
| # select all books which are synced by the current user and do not belong to a synced shelf and them to archive | # select all books which are synced by the current user and do not belong to a synced shelf and them to archive | ||||||
| @@ -65,7 +65,7 @@ def update_on_sync_shelfs(user_id): | |||||||
|                         .filter(or_(ub.Shelf.kobo_sync == 0, ub.Shelf.kobo_sync == None)) |                         .filter(or_(ub.Shelf.kobo_sync == 0, ub.Shelf.kobo_sync == None)) | ||||||
|                         .filter(ub.KoboSyncedBooks.user_id == user_id).all()) |                         .filter(ub.KoboSyncedBooks.user_id == user_id).all()) | ||||||
|     for b in books_to_archive: |     for b in books_to_archive: | ||||||
|         add_archived_books(b.book_id) |         change_archived_books(b.book_id, True) | ||||||
|         ub.session.query(ub.KoboSyncedBooks) \ |         ub.session.query(ub.KoboSyncedBooks) \ | ||||||
|             .filter(ub.KoboSyncedBooks.book_id == b.book_id) \ |             .filter(ub.KoboSyncedBooks.book_id == b.book_id) \ | ||||||
|             .filter(ub.KoboSyncedBooks.user_id == user_id).delete() |             .filter(ub.KoboSyncedBooks.user_id == user_id).delete() | ||||||
|   | |||||||
| @@ -182,10 +182,9 @@ class SyncToken: | |||||||
|         return b64encode_json(token) |         return b64encode_json(token) | ||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return "{},{},{},{},{},{}".format(self.raw_kobo_store_token, |         return "{},{},{},{},{},{}".format(self.books_last_created, | ||||||
|                                        self.books_last_created, |  | ||||||
|                                        self.books_last_modified, |                                        self.books_last_modified, | ||||||
|                                        self.archive_last_modified, |                                        self.archive_last_modified, | ||||||
|                                        self.reading_state_last_modified, |                                        self.reading_state_last_modified, | ||||||
|                                        self.tags_last_modified) |                                        self.tags_last_modified, self.raw_kobo_store_token) | ||||||
|                                        #self.books_last_id) |                                        #self.books_last_id) | ||||||
|   | |||||||
| @@ -284,11 +284,7 @@ $(function() { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     function fillFileTable(path, type, folder, filt) { |     function fillFileTable(path, type, folder, filt) { | ||||||
|         if (window.location.pathname.endsWith("/basicconfig")) { |         var request_path = "/../../ajax/pathchooser/"; | ||||||
|             var request_path = "/../basicconfig/pathchooser/"; |  | ||||||
|         } else { |  | ||||||
|             var request_path = "/../../ajax/pathchooser/"; |  | ||||||
|         } |  | ||||||
|         $.ajax({ |         $.ajax({ | ||||||
|             dataType: "json", |             dataType: "json", | ||||||
|             data: { |             data: { | ||||||
|   | |||||||
| @@ -631,14 +631,14 @@ function singleUserFormatter(value, row) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function checkboxFormatter(value, row){ | function checkboxFormatter(value, row){ | ||||||
|     if(value & this.column) |     if (value & this.column) | ||||||
|         return '<input type="checkbox" class="chk" data-pk="' + row.id + '" data-name="' + this.field + '" checked onchange="checkboxChange(this, ' + row.id + ', \'' + this.name + '\', ' + this.column + ')">'; |         return '<input type="checkbox" class="chk" data-pk="' + row.id + '" data-name="' + this.field + '" checked onchange="checkboxChange(this, ' + row.id + ', \'' + this.name + '\', ' + this.column + ')">'; | ||||||
|     else |     else | ||||||
|         return '<input type="checkbox" class="chk" data-pk="' + row.id + '" data-name="' + this.field + '" onchange="checkboxChange(this, ' + row.id + ', \'' + this.name + '\', ' + this.column + ')">'; |         return '<input type="checkbox" class="chk" data-pk="' + row.id + '" data-name="' + this.field + '" onchange="checkboxChange(this, ' + row.id + ', \'' + this.name + '\', ' + this.column + ')">'; | ||||||
| } | } | ||||||
|  |  | ||||||
| function singlecheckboxFormatter(value, row){ | function singlecheckboxFormatter(value, row){ | ||||||
|     if(value) |     if (value) | ||||||
|         return '<input type="checkbox" class="chk" data-pk="' + row.id + '" data-name="' + this.field + '" checked onchange="checkboxChange(this, ' + row.id + ', \'' + this.name + '\', 0)">'; |         return '<input type="checkbox" class="chk" data-pk="' + row.id + '" data-name="' + this.field + '" checked onchange="checkboxChange(this, ' + row.id + ', \'' + this.name + '\', 0)">'; | ||||||
|     else |     else | ||||||
|         return '<input type="checkbox" class="chk" data-pk="' + row.id + '" data-name="' + this.field + '" onchange="checkboxChange(this, ' + row.id + ', \'' + this.name + '\', 0)">'; |         return '<input type="checkbox" class="chk" data-pk="' + row.id + '" data-name="' + this.field + '" onchange="checkboxChange(this, ' + row.id + ', \'' + this.name + '\', 0)">'; | ||||||
| @@ -793,7 +793,7 @@ function handleListServerResponse (data) { | |||||||
| function checkboxChange(checkbox, userId, field, field_index) { | function checkboxChange(checkbox, userId, field, field_index) { | ||||||
|     $.ajax({ |     $.ajax({ | ||||||
|         method: "post", |         method: "post", | ||||||
|         url: window.location.pathname + "/../../ajax/editlistusers/" + field, |         url: getPath() + "/ajax/editlistusers/" + field, | ||||||
|         data: {"pk": userId, "field_index": field_index, "value": checkbox.checked}, |         data: {"pk": userId, "field_index": field_index, "value": checkbox.checked}, | ||||||
|         error: function(data) { |         error: function(data) { | ||||||
|             handleListServerResponse([{type:"danger", message:data.responseText}]) |             handleListServerResponse([{type:"danger", message:data.responseText}]) | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								cps/web.py
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								cps/web.py
									
									
									
									
									
								
							| @@ -56,6 +56,7 @@ from .redirect import redirect_back | |||||||
| from .usermanagement import login_required_if_no_ano | from .usermanagement import login_required_if_no_ano | ||||||
| from .kobo_sync_status import remove_synced_book | from .kobo_sync_status import remove_synced_book | ||||||
| from .render_template import render_title_template | from .render_template import render_title_template | ||||||
|  | from .kobo_sync_status import change_archived_books | ||||||
|  |  | ||||||
| feature_support = { | feature_support = { | ||||||
|     'ldap': bool(services.ldap), |     'ldap': bool(services.ldap), | ||||||
| @@ -190,24 +191,15 @@ def toggle_read(book_id): | |||||||
|             return "Custom Column No.{} is not existing in calibre database".format(config.config_read_column), 400 |             return "Custom Column No.{} is not existing in calibre database".format(config.config_read_column), 400 | ||||||
|         except (OperationalError, InvalidRequestError) as e: |         except (OperationalError, InvalidRequestError) as e: | ||||||
|             calibre_db.session.rollback() |             calibre_db.session.rollback() | ||||||
|             log.error(u"Read status could not set: %e", e) |             log.error(u"Read status could not set: {}".format(e)) | ||||||
|             return "Read status could not set: {}".format(e), 400 |             return "Read status could not set: {}".format(e), 400 | ||||||
|     return "" |     return "" | ||||||
|  |  | ||||||
| @web.route("/ajax/togglearchived/<int:book_id>", methods=['POST']) | @web.route("/ajax/togglearchived/<int:book_id>", methods=['POST']) | ||||||
| @login_required | @login_required | ||||||
| def toggle_archived(book_id): | def toggle_archived(book_id): | ||||||
|     archived_book = ub.session.query(ub.ArchivedBook).filter(and_(ub.ArchivedBook.user_id == int(current_user.id), |     is_archived = change_archived_books(book_id, message="Book {} archivebit toggled".format(book_id)) | ||||||
|                                                                   ub.ArchivedBook.book_id == book_id)).first() |     if is_archived: | ||||||
|     if archived_book: |  | ||||||
|         archived_book.is_archived = not archived_book.is_archived |  | ||||||
|         archived_book.last_modified = datetime.utcnow() |  | ||||||
|     else: |  | ||||||
|         archived_book = ub.ArchivedBook(user_id=current_user.id, book_id=book_id) |  | ||||||
|         archived_book.is_archived = True |  | ||||||
|     ub.session.merge(archived_book) |  | ||||||
|     ub.session_commit("Book {} archivebit toggled".format(book_id)) |  | ||||||
|     if archived_book.is_archived: |  | ||||||
|         remove_synced_book(book_id) |         remove_synced_book(book_id) | ||||||
|     return "" |     return "" | ||||||
|  |  | ||||||
| @@ -801,7 +793,6 @@ def list_books(): | |||||||
|  |  | ||||||
|     if sort == "state": |     if sort == "state": | ||||||
|         state = json.loads(request.args.get("state", "[]")) |         state = json.loads(request.args.get("state", "[]")) | ||||||
|         # order = [db.Books.timestamp.asc()] if order == "asc" else [db.Books.timestamp.desc()]   # ToDo wrong: sort ticked |  | ||||||
|     elif sort == "tags": |     elif sort == "tags": | ||||||
|         order = [db.Tags.name.asc()] if order == "asc" else [db.Tags.name.desc()] |         order = [db.Tags.name.asc()] if order == "asc" else [db.Tags.name.desc()] | ||||||
|         join = db.books_tags_link,db.Books.id == db.books_tags_link.c.book, db.Tags |         join = db.books_tags_link,db.Books.id == db.books_tags_link.c.book, db.Tags | ||||||
| @@ -1525,9 +1516,6 @@ def register(): | |||||||
|  |  | ||||||
| @web.route('/login', methods=['GET', 'POST']) | @web.route('/login', methods=['GET', 'POST']) | ||||||
| def login(): | def login(): | ||||||
|     #if not config.db_configured: |  | ||||||
|     #    log.debug(u"Redirect to initial configuration") |  | ||||||
|     #    return redirect(url_for('admin.basic_configuration')) |  | ||||||
|     if current_user is not None and current_user.is_authenticated: |     if current_user is not None and current_user.is_authenticated: | ||||||
|         return redirect(url_for('web.index')) |         return redirect(url_for('web.index')) | ||||||
|     if config.config_login_type == constants.LOGIN_LDAP and not services.ldap: |     if config.config_login_type == constants.LOGIN_LDAP and not services.ldap: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 cbartondock
					cbartondock