From 5c3a5b6c39f5bdd63a2baf35c78711d7445d512b Mon Sep 17 00:00:00 2001 From: Jonathan Rehm Date: Fri, 21 Jul 2017 20:42:01 -0700 Subject: [PATCH 1/3] Use ajax to add/remove books from shelves Gracefully fall back to standard requests if JavaScript is disabled --- cps/static/css/style.css | 5 ++ cps/static/js/details.js | 39 ++++++++++++++++ cps/templates/detail.html | 96 ++++++++++++++++++++++----------------- cps/templates/layout.html | 6 +-- cps/web.py | 63 +++++++++++++++++++------ 5 files changed, 151 insertions(+), 58 deletions(-) create mode 100644 cps/static/js/details.js diff --git a/cps/static/css/style.css b/cps/static/css/style.css index f0ea4cb1..7f053629 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -53,3 +53,8 @@ span.glyphicon.glyphicon-tags {padding-right: 5px;color: #999;vertical-align: te .spinner2 {margin:0 41%;} .block-label {display: block;} + +#remove-from-shelves .btn, +#shelf-action-errors { + margin-left: 5px; +} diff --git a/cps/static/js/details.js b/cps/static/js/details.js new file mode 100644 index 00000000..ba45da17 --- /dev/null +++ b/cps/static/js/details.js @@ -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(` ${this.textContent}`); + break; + case "remove": + $("#add-to-shelves").append(`
  • ${this.textContent}
  • `); + break; + } + this.parentNode.removeChild(this); + }) + .fail(xhr => { + const $msg = $("", { "class": "text-danger"}).text(xhr.responseText); + $("#shelf-action-status").html($msg); + + setTimeout(() => { + $msg.remove(); + }, 10000); + }); +}); \ No newline at end of file diff --git a/cps/templates/detail.html b/cps/templates/detail.html index 6d6ecdab..37cdd01f 100644 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -185,41 +185,62 @@ {% if g.user.shelf.all() or g.public_shelfes %} {% endif %} @@ -233,19 +254,12 @@ {% endif %} - + {% endblock %} {% block js %} - + {% endblock %} diff --git a/cps/templates/layout.html b/cps/templates/layout.html index 1833df5c..0dd9583e 100644 --- a/cps/templates/layout.html +++ b/cps/templates/layout.html @@ -99,17 +99,17 @@ {% for message in get_flashed_messages(with_categories=True) %} {%if message[0] == "error" %} -
    +
    {{ message[1] }}
    {%endif%} {%if message[0] == "info" %} -
    +
    {{ message[1] }}
    {%endif%} {%if message[0] == "success" %} -
    +
    {{ message[1] }}
    {%endif%} diff --git a/cps/web.py b/cps/web.py index dbcaa5bd..32a3d3e4 100755 --- a/cps/web.py +++ b/cps/web.py @@ -1949,45 +1949,80 @@ def send_to_kindle(book_id): @login_required def add_to_shelf(shelf_id, book_id): 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): app.logger.info("Sorry you are not allowed to add a book to the the shelf: %s" % shelf.name) - return redirect(url_for('index')) - maxOrder = ub.session.query(func.max(ub.BookShelf.order)).filter(ub.BookShelf.shelf == shelf_id).first() + if not request.is_xhr: + return redirect(url_for('index')) + 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, ub.BookShelf.book_id == book_id).first() if book_in_shelf: app.logger.info("Book is already part of the shelf: %s" % shelf.name) - return redirect(url_for('index')) + if not request.is_xhr: + 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: maxOrder = 0 else: 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) - ub.session.add(ins) - ub.session.commit() + + ins = ub.BookShelf(shelf=shelf.id, book_id=book_id, order=maxOrder + 1) + ub.session.add(ins) + ub.session.commit() + if not request.is_xhr: flash(_(u"Book has been added to shelf: %(sname)s", sname=shelf.name), category="success") return redirect(request.environ["HTTP_REFERER"]) - else: - app.logger.info("User is not allowed to edit public shelfs") - return redirect(url_for('index')) + return "", 204 @app.route("/shelf/remove//") @login_required def remove_from_shelf(shelf_id, book_id): 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) \ or (shelf.is_public and current_user.role_edit_shelfs()): - app.logger.info("Sorry you are not allowed to remove a book from this shelf: %s" % shelf.name) - return redirect(url_for('index')) + if not request.is_xhr: + app.logger.info("Sorry you are not allowed to remove a book from this shelf: %s" % shelf.name) + 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, 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.commit() - flash(_(u"Book has been removed from shelf: %(sname)s", sname=shelf.name), category="success") - return redirect(request.environ["HTTP_REFERER"]) + + if not request.is_xhr: + flash(_(u"Book has been removed from shelf: %(sname)s", sname=shelf.name), category="success") + return redirect(request.environ["HTTP_REFERER"]) + return "", 204 @app.route("/shelf/create", methods=["GET", "POST"]) From e1a88aa0abb4d0e039016dfb989d308ad6f6767e Mon Sep 17 00:00:00 2001 From: Jonathan Rehm Date: Sat, 22 Jul 2017 07:34:38 -0700 Subject: [PATCH 2/3] Make change requested by Codacy --- cps/static/js/details.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/static/js/details.js b/cps/static/js/details.js index ba45da17..c3aaf4e4 100644 --- a/cps/static/js/details.js +++ b/cps/static/js/details.js @@ -28,7 +28,7 @@ $(document).on("click", "[data-shelf-action]", function (e) { } this.parentNode.removeChild(this); }) - .fail(xhr => { + .fail((xhr) => { const $msg = $("", { "class": "text-danger"}).text(xhr.responseText); $("#shelf-action-status").html($msg); From 693c26c2b32f65bc79b5e9c64efddae01ede3a8b Mon Sep 17 00:00:00 2001 From: Jonathan Rehm Date: Sat, 22 Jul 2017 11:24:47 -0700 Subject: [PATCH 3/3] Attach events to button toolbar Since this is closer to the elements, we can be more sure that we won't have events fire when we don't want them to. For example, if we're viewing the page in a modal, we don't want the event handler living longer than the content itself. --- cps/static/js/details.js | 2 +- cps/templates/detail.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cps/static/js/details.js b/cps/static/js/details.js index c3aaf4e4..74dbb08e 100644 --- a/cps/static/js/details.js +++ b/cps/static/js/details.js @@ -6,7 +6,7 @@ $("#have_read_cb").on("change", function() { $(this).closest("form").submit(); }); -$(document).on("click", "[data-shelf-action]", function (e) { +$("#shelf-actions").on("click", "[data-shelf-action]", function (e) { e.preventDefault(); $.get(this.href) diff --git a/cps/templates/detail.html b/cps/templates/detail.html index 37cdd01f..08f84ca6 100644 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -183,7 +183,7 @@ {% if g.user.is_authenticated %} {% if g.user.shelf.all() or g.public_shelfes %} -