mirror of
				https://github.com/janeczku/calibre-web
				synced 2025-10-31 07:13:02 +00:00 
			
		
		
		
	Use ajax to add/remove books from shelves
Gracefully fall back to standard requests if JavaScript is disabled
This commit is contained in:
		| @@ -53,3 +53,8 @@ span.glyphicon.glyphicon-tags {padding-right: 5px;color: #999;vertical-align: te | |||||||
| .spinner2 {margin:0 41%;} | .spinner2 {margin:0 41%;} | ||||||
|  |  | ||||||
| .block-label {display: block;} | .block-label {display: block;} | ||||||
|  |  | ||||||
|  | #remove-from-shelves .btn, | ||||||
|  | #shelf-action-errors { | ||||||
|  |     margin-left: 5px; | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								cps/static/js/details.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								cps/static/js/details.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | $( document ).ready(function() { | ||||||
|  |     $("#have_read_form").ajaxForm(); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | $("#have_read_cb").on("change", function() { | ||||||
|  |     $(this).closest("form").submit(); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | $(document).on("click", "[data-shelf-action]", function (e) { | ||||||
|  |     e.preventDefault(); | ||||||
|  |  | ||||||
|  |     $.get(this.href) | ||||||
|  |         .done(() => { | ||||||
|  |             const $this = $(this); | ||||||
|  |             switch ($this.data("shelf-action")) { | ||||||
|  |                 case "add": | ||||||
|  |                     $("#remove-from-shelves").append(`<a href="${$this.data("remove-href")}" | ||||||
|  |                        data-add-href="${this.href}" | ||||||
|  |                        class="btn btn-sm btn-default" data-shelf-action="remove" | ||||||
|  |                     ><span class="glyphicon glyphicon-remove"></span> ${this.textContent}</a>`); | ||||||
|  |                     break; | ||||||
|  |                 case "remove": | ||||||
|  |                     $("#add-to-shelves").append(`<li><a href="${$this.data("add-href")}" | ||||||
|  |                       data-remove-href="${this.href}" | ||||||
|  |                       data-shelf-action="add" | ||||||
|  |                     >${this.textContent}</a></li>`); | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |             this.parentNode.removeChild(this); | ||||||
|  |         }) | ||||||
|  |         .fail(xhr => { | ||||||
|  |             const $msg = $("<span/>", { "class": "text-danger"}).text(xhr.responseText); | ||||||
|  |             $("#shelf-action-status").html($msg); | ||||||
|  |  | ||||||
|  |             setTimeout(() => { | ||||||
|  |                 $msg.remove(); | ||||||
|  |             }, 10000); | ||||||
|  |         }); | ||||||
|  | }); | ||||||
| @@ -189,37 +189,58 @@ | |||||||
|             <span class="glyphicon glyphicon-list"></span> {{_('Add to shelf')}} |             <span class="glyphicon glyphicon-list"></span> {{_('Add to shelf')}} | ||||||
|             <span class="caret"></span> |             <span class="caret"></span> | ||||||
|           </button> |           </button> | ||||||
|               <ul class="dropdown-menu" aria-labelledby="btnGroupDrop2"> |           <ul id="add-to-shelves" class="dropdown-menu" aria-labelledby="btnGroupDrop2"> | ||||||
|             {% for shelf in g.user.shelf %} |             {% for shelf in g.user.shelf %} | ||||||
|               {% if not shelf.id in books_shelfs and shelf.is_public != 1 %} |               {% if not shelf.id in books_shelfs and shelf.is_public != 1 %} | ||||||
|                     <li><a href="{{ url_for('add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}">{{shelf.name}}</a></li> |                 <li> | ||||||
|  |                   <a href="{{ url_for('add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}" | ||||||
|  |                      data-remove-href="{{ url_for('remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}" | ||||||
|  |                      data-shelf-action="add" | ||||||
|  |                   > | ||||||
|  |                     {{shelf.name}} | ||||||
|  |                   </a> | ||||||
|  |                 </li> | ||||||
|               {% endif %} |               {% endif %} | ||||||
|             {%endfor%} |             {%endfor%} | ||||||
|             {% for shelf in g.public_shelfes %} |             {% for shelf in g.public_shelfes %} | ||||||
|               {% if not shelf.id in books_shelfs %} |               {% if not shelf.id in books_shelfs %} | ||||||
|                     <li><a href="{{ url_for('add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}">{{shelf.name}}</a></li> |                 <li> | ||||||
|  |                   <a href="{{ url_for('add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}" | ||||||
|  |                      data-remove-href="{{ url_for('remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}" | ||||||
|  |                      data-shelf-action="add" | ||||||
|  |                   > | ||||||
|  |                     {{shelf.name}} | ||||||
|  |                   </a> | ||||||
|  |                 </li> | ||||||
|               {% endif %} |               {% endif %} | ||||||
|             {%endfor%} |             {%endfor%} | ||||||
|           </ul> |           </ul> | ||||||
|         </div> |         </div> | ||||||
|  |         <div id="remove-from-shelves" class="btn-group" role="group" aria-label="Remove from shelves"> | ||||||
|           {% if books_shelfs %} |           {% if books_shelfs %} | ||||||
|         <div class="btn-group" role="group" aria-label="Remove from shelves"> |  | ||||||
|             {% for shelf in g.user.shelf %} |             {% for shelf in g.user.shelf %} | ||||||
|               {% if shelf.id in books_shelfs and shelf.is_public != 1 %} |               {% if shelf.id in books_shelfs and shelf.is_public != 1 %} | ||||||
|                   <a href="{{ url_for('remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}" class="btn btn-sm btn-default" role="button"> |                 <a href="{{ url_for('remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}" | ||||||
|  |                    data-add-href="{{ url_for('add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}" | ||||||
|  |                    class="btn btn-sm btn-default" role="button" data-shelf-action="remove" | ||||||
|  |                 > | ||||||
|                   <span class="glyphicon glyphicon-remove"></span> {{shelf.name}} |                   <span class="glyphicon glyphicon-remove"></span> {{shelf.name}} | ||||||
|                 </a> |                 </a> | ||||||
|               {% endif %} |               {% endif %} | ||||||
|             {%endfor%} |             {%endfor%} | ||||||
|             {% for shelf in g.public_shelfes %} |             {% for shelf in g.public_shelfes %} | ||||||
|               {% if shelf.id in books_shelfs %} |               {% if shelf.id in books_shelfs %} | ||||||
|                   <a href="{{ url_for('remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}" class="btn btn-sm btn-default" role="button"> |                 <a href="{{ url_for('remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}" | ||||||
|  |                    data-add-href="{{ url_for('add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}" | ||||||
|  |                    class="btn btn-sm btn-default" role="button" data-shelf-action="remove" | ||||||
|  |                 > | ||||||
|                   <span class="glyphicon glyphicon-remove"></span> {{shelf.name}} |                   <span class="glyphicon glyphicon-remove"></span> {{shelf.name}} | ||||||
|                 </a> |                 </a> | ||||||
|               {% endif %} |               {% endif %} | ||||||
|             {%endfor%} |             {%endfor%} | ||||||
|           {% endif %} |           {% endif %} | ||||||
|         </div> |         </div> | ||||||
|  |         <div id="shelf-action-errors" class="pull-left" role="alert"></div> | ||||||
|       </div> |       </div> | ||||||
|       {% endif %} |       {% endif %} | ||||||
|  |  | ||||||
| @@ -233,19 +254,12 @@ | |||||||
|       {% endif %} |       {% endif %} | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|  |  | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
|  | </div> | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  |  | ||||||
| {% block js %} | {% block js %} | ||||||
| <script src="{{ url_for('static', filename='js/libs/jquery.form.js') }}"></script> | <script src="{{ url_for('static', filename='js/libs/jquery.form.js') }}"></script> | ||||||
| <script> | <script src="{{ url_for('static', filename='js/details.js') }}"></script> | ||||||
|   $( document ).ready(function() { |  | ||||||
|     $('#have_read_form').ajaxForm(); |  | ||||||
|   }); |  | ||||||
|   $("#have_read_cb").on('change', function() { |  | ||||||
|     $(this).closest('form').submit(); |  | ||||||
|   }); |  | ||||||
| </script> |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
|   | |||||||
| @@ -99,17 +99,17 @@ | |||||||
|     </div> |     </div> | ||||||
|     {% for message in get_flashed_messages(with_categories=True) %} |     {% for message in get_flashed_messages(with_categories=True) %} | ||||||
|       {%if message[0] == "error" %} |       {%if message[0] == "error" %} | ||||||
|       <div class="row-fluid" style="margin-top: -20px; text-align: center;"> |       <div class="row-fluid text-center" style="margin-top: -20px;"> | ||||||
|         <div id="flash_alert" class="alert alert-danger">{{ message[1] }}</div> |         <div id="flash_alert" class="alert alert-danger">{{ message[1] }}</div> | ||||||
|       </div> |       </div> | ||||||
|       {%endif%} |       {%endif%} | ||||||
|       {%if message[0] == "info" %} |       {%if message[0] == "info" %} | ||||||
|       <div class="row-fluid" style="margin-top: -20px; text-align: center;"> |       <div class="row-fluid text-center" style="margin-top: -20px;"> | ||||||
|         <div id="flash_info" class="alert alert-info">{{ message[1] }}</div> |         <div id="flash_info" class="alert alert-info">{{ message[1] }}</div> | ||||||
|       </div> |       </div> | ||||||
|       {%endif%} |       {%endif%} | ||||||
|       {%if message[0] == "success" %} |       {%if message[0] == "success" %} | ||||||
|       <div class="row-fluid" style="margin-top: -20px; text-align: center;"> |       <div class="row-fluid text-center" style="margin-top: -20px;"> | ||||||
|         <div id="flash_success" class="alert alert-success">{{ message[1] }}</div> |         <div id="flash_success" class="alert alert-success">{{ message[1] }}</div> | ||||||
|       </div> |       </div> | ||||||
|       {%endif%} |       {%endif%} | ||||||
|   | |||||||
							
								
								
									
										45
									
								
								cps/web.py
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								cps/web.py
									
									
									
									
									
								
							| @@ -1949,45 +1949,80 @@ def send_to_kindle(book_id): | |||||||
| @login_required | @login_required | ||||||
| def add_to_shelf(shelf_id, book_id): | def add_to_shelf(shelf_id, book_id): | ||||||
|     shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first() |     shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first() | ||||||
|  |     if shelf is None: | ||||||
|  |         app.logger.info("Invalid shelf specified") | ||||||
|  |         if not request.is_xhr: | ||||||
|  |             return redirect(url_for('index')) | ||||||
|  |         return "Invalid shelf specified", 400 | ||||||
|  |  | ||||||
|     if not shelf.is_public and not shelf.user_id == int(current_user.id): |     if not shelf.is_public and not shelf.user_id == int(current_user.id): | ||||||
|         app.logger.info("Sorry you are not allowed to add a book to the the shelf: %s" % shelf.name) |         app.logger.info("Sorry you are not allowed to add a book to the the shelf: %s" % shelf.name) | ||||||
|  |         if not request.is_xhr: | ||||||
|             return redirect(url_for('index')) |             return redirect(url_for('index')) | ||||||
|     maxOrder = ub.session.query(func.max(ub.BookShelf.order)).filter(ub.BookShelf.shelf == shelf_id).first() |         return "Sorry you are not allowed to add a book to the the shelf: %s" % shelf.name, 403 | ||||||
|  |  | ||||||
|  |     if shelf.is_public and not current_user.role_edit_shelfs(): | ||||||
|  |         app.logger.info("User is not allowed to edit public shelves") | ||||||
|  |         if not request.is_xhr: | ||||||
|  |             return redirect(url_for('index')) | ||||||
|  |         return "User is not allowed to edit public shelves", 403 | ||||||
|  |  | ||||||
|     book_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id, |     book_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id, | ||||||
|                                           ub.BookShelf.book_id == book_id).first() |                                           ub.BookShelf.book_id == book_id).first() | ||||||
|     if book_in_shelf: |     if book_in_shelf: | ||||||
|         app.logger.info("Book is already part of the shelf: %s" % shelf.name) |         app.logger.info("Book is already part of the shelf: %s" % shelf.name) | ||||||
|  |         if not request.is_xhr: | ||||||
|             return redirect(url_for('index')) |             return redirect(url_for('index')) | ||||||
|  |         return "Book is already part of the shelf: %s" % shelf.name, 400 | ||||||
|  |  | ||||||
|  |     maxOrder = ub.session.query(func.max(ub.BookShelf.order)).filter(ub.BookShelf.shelf == shelf_id).first() | ||||||
|     if maxOrder[0] is None: |     if maxOrder[0] is None: | ||||||
|         maxOrder = 0 |         maxOrder = 0 | ||||||
|     else: |     else: | ||||||
|         maxOrder = maxOrder[0] |         maxOrder = maxOrder[0] | ||||||
|     if (shelf.is_public and current_user.role_edit_shelfs()) or not shelf.is_public: |  | ||||||
|     ins = ub.BookShelf(shelf=shelf.id, book_id=book_id, order=maxOrder + 1) |     ins = ub.BookShelf(shelf=shelf.id, book_id=book_id, order=maxOrder + 1) | ||||||
|     ub.session.add(ins) |     ub.session.add(ins) | ||||||
|     ub.session.commit() |     ub.session.commit() | ||||||
|  |     if not request.is_xhr: | ||||||
|         flash(_(u"Book has been added to shelf: %(sname)s", sname=shelf.name), category="success") |         flash(_(u"Book has been added to shelf: %(sname)s", sname=shelf.name), category="success") | ||||||
|         return redirect(request.environ["HTTP_REFERER"]) |         return redirect(request.environ["HTTP_REFERER"]) | ||||||
|     else: |     return "", 204 | ||||||
|         app.logger.info("User is not allowed to edit public shelfs") |  | ||||||
|         return redirect(url_for('index')) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @app.route("/shelf/remove/<int:shelf_id>/<int:book_id>") | @app.route("/shelf/remove/<int:shelf_id>/<int:book_id>") | ||||||
| @login_required | @login_required | ||||||
| def remove_from_shelf(shelf_id, book_id): | def remove_from_shelf(shelf_id, book_id): | ||||||
|     shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first() |     shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first() | ||||||
|  |     if shelf is None: | ||||||
|  |         app.logger.info("Invalid shelf specified") | ||||||
|  |         if not request.is_xhr: | ||||||
|  |             return redirect(url_for('index')) | ||||||
|  |         return "Invalid shelf specified", 400 | ||||||
|  |  | ||||||
|     if not shelf.is_public and not shelf.user_id == int(current_user.id) \ |     if not shelf.is_public and not shelf.user_id == int(current_user.id) \ | ||||||
|             or (shelf.is_public and current_user.role_edit_shelfs()): |             or (shelf.is_public and current_user.role_edit_shelfs()): | ||||||
|  |         if not request.is_xhr: | ||||||
|             app.logger.info("Sorry you are not allowed to remove a book from this shelf: %s" % shelf.name) |             app.logger.info("Sorry you are not allowed to remove a book from this shelf: %s" % shelf.name) | ||||||
|             return redirect(url_for('index')) |             return redirect(url_for('index')) | ||||||
|  |         return "Sorry you are not allowed to add a book to the the shelf: %s" % shelf.name, 403 | ||||||
|  |  | ||||||
|     book_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id, |     book_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id, | ||||||
|                                                        ub.BookShelf.book_id == book_id).first() |                                                        ub.BookShelf.book_id == book_id).first() | ||||||
|  |  | ||||||
|  |     if book_shelf is None: | ||||||
|  |         app.logger.info("Book already removed from shelf") | ||||||
|  |         if not request.is_xhr: | ||||||
|  |             return redirect(url_for('index')) | ||||||
|  |         return "Book already removed from shelf", 410 | ||||||
|  |  | ||||||
|     ub.session.delete(book_shelf) |     ub.session.delete(book_shelf) | ||||||
|     ub.session.commit() |     ub.session.commit() | ||||||
|  |  | ||||||
|  |     if not request.is_xhr: | ||||||
|         flash(_(u"Book has been removed from shelf: %(sname)s", sname=shelf.name), category="success") |         flash(_(u"Book has been removed from shelf: %(sname)s", sname=shelf.name), category="success") | ||||||
|         return redirect(request.environ["HTTP_REFERER"]) |         return redirect(request.environ["HTTP_REFERER"]) | ||||||
|  |     return "", 204 | ||||||
|  |  | ||||||
|  |  | ||||||
| @app.route("/shelf/create", methods=["GET", "POST"]) | @app.route("/shelf/create", methods=["GET", "POST"]) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jonathan Rehm
					Jonathan Rehm