Use ajax to add/remove books from shelves

Gracefully fall back to standard requests if JavaScript is disabled
This commit is contained in:
Jonathan Rehm 2017-07-21 20:42:01 -07:00
parent 2e8a268dfa
commit 5c3a5b6c39
5 changed files with 151 additions and 58 deletions

View File

@ -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
View 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);
});
});

View File

@ -185,41 +185,62 @@
{% if g.user.shelf.all() or g.public_shelfes %} {% if g.user.shelf.all() or g.public_shelfes %}
<div class="btn-toolbar" role="toolbar"> <div class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group" aria-label="Add to shelves"> <div class="btn-group" role="group" aria-label="Add to shelves">
<button id="btnGroupDrop2" type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button id="btnGroupDrop2" type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<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>
{% endif %} <a href="{{ url_for('add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
{%endfor%} data-remove-href="{{ url_for('remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
{% for shelf in g.public_shelfes %} data-shelf-action="add"
{% 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> {{shelf.name}}
{% endif %}
{%endfor%}
</ul>
</div>
{% if books_shelfs %}
<div class="btn-group" role="group" aria-label="Remove from shelves">
{% for shelf in g.user.shelf %}
{% 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">
<span class="glyphicon glyphicon-remove"></span> {{shelf.name}}
</a> </a>
{% endif %} </li>
{%endfor%} {% endif %}
{% for shelf in g.public_shelfes %} {%endfor%}
{% if shelf.id in books_shelfs %} {% for shelf in g.public_shelfes %}
<a href="{{ url_for('remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}" class="btn btn-sm btn-default" role="button"> {% if not shelf.id in books_shelfs %}
<span class="glyphicon glyphicon-remove"></span> {{shelf.name}} <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> </a>
{% endif %} </li>
{%endfor%} {% endif %}
{% endif %} {%endfor%}
</ul>
</div> </div>
<div id="remove-from-shelves" class="btn-group" role="group" aria-label="Remove from shelves">
{% if books_shelfs %}
{% for shelf in g.user.shelf %}
{% 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) }}"
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}}
</a>
{% endif %}
{%endfor%}
{% for shelf in g.public_shelfes %}
{% if shelf.id in books_shelfs %}
<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}}
</a>
{% endif %}
{%endfor%}
{% endif %}
</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 %}

View File

@ -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%}

View File

@ -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)
return redirect(url_for('index')) if not request.is_xhr:
maxOrder = ub.session.query(func.max(ub.BookShelf.order)).filter(ub.BookShelf.shelf == shelf_id).first() 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, 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)
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: 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()):
app.logger.info("Sorry you are not allowed to remove a book from this shelf: %s" % shelf.name) if not request.is_xhr:
return redirect(url_for('index')) 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, 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()
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"]) @app.route("/shelf/create", methods=["GET", "POST"])