Use ajax to add/remove books from shelves
Gracefully fall back to standard requests if JavaScript is disabled
This commit is contained in:
parent
2e8a268dfa
commit
5c3a5b6c39
|
@ -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"])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user