diff --git a/app/controllers/__init__.py b/app/controllers/__init__.py index 92f870f..16e76cc 100644 --- a/app/controllers/__init__.py +++ b/app/controllers/__init__.py @@ -1,3 +1,4 @@ # flake8: noqa F401 from .pagination import create_pagination from .breadcrumbs import create_breadcrumbs +from .book_verify import register_book_verify_route, book_validator diff --git a/app/controllers/book_verify.py b/app/controllers/book_verify.py new file mode 100644 index 0000000..959cddb --- /dev/null +++ b/app/controllers/book_verify.py @@ -0,0 +1,119 @@ +from flask_login import current_user +from flask import Response, flash, redirect, url_for, request + +from app import models as m, db +from app.logger import log + + +class BookRouteVerifier: + _routes = [] + + @classmethod + def add_route(cls, route_name: str): + cls._routes.append(route_name) + + @classmethod + def remove_route(cls, route_name: str): + cls._routes.remove(route_name) + + @classmethod + def is_present(cls, route_name: str) -> bool: + return route_name in cls._routes + + +def register_book_verify_route(blueprint_name: str): + def decorator(func: callable): + BookRouteVerifier.add_route(f"{blueprint_name}.{func.__name__}") + return func + + return decorator + + +def book_validator() -> Response | None: + if not BookRouteVerifier.is_present(request.endpoint): + return None + + request_args = ( + {**request.view_args, **request.args} if request.view_args else {**request.args} + ) + + book_id = request_args.get("book_id") + if book_id: + book: m.Book = db.session.get(m.Book, book_id) + if not book or book.is_deleted or book.owner != current_user: + log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) + flash("You are not owner of this book!", "danger") + return redirect(url_for("book.my_library")) + + collection_id = request_args.get("collection_id") + if collection_id: + collection: m.Collection = db.session.get(m.Collection, collection_id) + if not collection or collection.is_deleted: + log(log.WARNING, "Collection with id [%s] not found", collection_id) + flash("Collection not found", "danger") + return redirect(url_for("book.collection_view", book_id=book_id)) + + sub_collection_id = request_args.get("sub_collection_id") + if sub_collection_id: + sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) + if not sub_collection or sub_collection.is_deleted: + log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id) + flash("Subcollection not found", "danger") + return redirect( + url_for( + "book.sub_collection_view", + book_id=book_id, + collection_id=collection_id, + ) + ) + + section_id = request_args.get("section_id") + if section_id: + section: m.Section = db.session.get(m.Section, section_id) + if not section or collection.is_deleted: + log(log.WARNING, "Section with id [%s] not found", section) + flash("Section not found", "danger") + return redirect( + url_for( + "book.section_view", + book_id=book_id, + collection_id=collection_id, + sub_collection_id=sub_collection_id, + ) + ) + + interpretation_id = request_args.get("interpretation_id") + if interpretation_id: + interpretation: m.Interpretation = db.session.get( + m.Interpretation, interpretation_id + ) + if not interpretation or interpretation.is_deleted: + log(log.WARNING, "Interpretation with id [%s] not found", interpretation_id) + flash("Interpretation not found", "danger") + return redirect( + url_for( + "book.qa_view", + book_id=book_id, + collection_id=collection_id, + sub_collection_id=sub_collection_id, + section_id=section_id, + interpretation_id=interpretation_id, + ) + ) + + comment_id = request_args.get("comment_id") + if comment_id: + comment: m.Comment = db.session.get(m.Comment, comment_id) + if not comment or comment.is_deleted: + log(log.WARNING, "Comment with id [%s] not found", comment_id) + flash("Comment not found", "danger") + return redirect( + url_for( + "book.qa_view", + book_id=book_id, + collection_id=collection_id, + sub_collection_id=sub_collection_id, + section_id=section_id, + interpretation_id=interpretation_id, + ) + ) diff --git a/app/forms/book.py b/app/forms/book.py index ea15623..b5483fc 100644 --- a/app/forms/book.py +++ b/app/forms/book.py @@ -2,7 +2,7 @@ from flask_wtf import FlaskForm from wtforms import StringField, SubmitField, ValidationError from wtforms.validators import DataRequired, Length -from app import models as m, db +from app import models as m from app.logger import log @@ -19,13 +19,6 @@ class EditBookForm(BaseBookForm): book_id = StringField("User ID", [DataRequired()]) submit = SubmitField("Edit book") - def validate_book_id(self, field): - book_id = field.data - book: m.Book = db.session.get(m.Book, book_id) - if not book or book.is_deleted: - log(log.WARNING, "Book with id [%s] not found", book_id) - raise ValidationError("Book not found") - def validate_label(self, field): label = field.data book_id = self.book_id.data diff --git a/app/views/book.py b/app/views/book.py index 5289944..04a2a47 100644 --- a/app/views/book.py +++ b/app/views/book.py @@ -8,13 +8,24 @@ from flask import ( ) from flask_login import login_required, current_user -from app.controllers import create_pagination, create_breadcrumbs +from app.controllers import ( + create_pagination, + create_breadcrumbs, + register_book_verify_route, + book_validator, +) from app import models as m, db, forms as f from app.logger import log bp = Blueprint("book", __name__, url_prefix="/book") +@bp.before_request +def before_request(): + if response := book_validator(): + return response + + @bp.route("/all", methods=["GET"]) def get_all(): q = request.args.get("q", type=str, default=None) @@ -81,6 +92,7 @@ def create(): @bp.route("//edit", methods=["POST"]) +@register_book_verify_route(bp.name) @login_required def edit(book_id: int): form = f.EditBookForm() @@ -289,13 +301,10 @@ def interpretation_view( @bp.route("//settings", methods=["GET"]) +@register_book_verify_route(bp.name) @login_required def settings(book_id: int): book: m.Book = db.session.get(m.Book, book_id) - if not book or book.is_deleted or book.owner != current_user: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) return render_template( "book/settings.html", book=book, roles=m.BookContributor.Roles @@ -303,14 +312,9 @@ def settings(book_id: int): @bp.route("//add_contributor", methods=["POST"]) +@register_book_verify_route(bp.name) @login_required def add_contributor(book_id: int): - book: m.Book = db.session.get(m.Book, book_id) - if not book or book.is_deleted or book.owner != current_user: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - form = f.AddContributorForm() if form.validate_on_submit(): @@ -341,26 +345,21 @@ def add_contributor(book_id: int): @bp.route("//delete_contributor", methods=["POST"]) +@register_book_verify_route(bp.name) @login_required def delete_contributor(book_id: int): - book: m.Book = db.session.get(m.Book, book_id) - if not book or book.is_deleted or book.owner != current_user: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - form = f.DeleteContributorForm() if form.validate_on_submit(): book_contributor = m.BookContributor.query.filter_by( - user_id=int(form.user_id.data), book_id=book.id + user_id=int(form.user_id.data), book_id=book_id ).first() if not book_contributor: log( log.INFO, "BookContributor does not exists user: [%s], book: [%s]", form.user_id.data, - book.id, + book_id, ) flash("Does not exists!", "success") return redirect(url_for("book.settings", book_id=book_id)) @@ -381,26 +380,21 @@ def delete_contributor(book_id: int): @bp.route("//edit_contributor_role", methods=["POST"]) +@register_book_verify_route(bp.name) @login_required def edit_contributor_role(book_id: int): - book: m.Book = db.session.get(m.Book, book_id) - if not book or book.is_deleted or book.owner != current_user: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - form = f.EditContributorRoleForm() if form.validate_on_submit(): book_contributor = m.BookContributor.query.filter_by( - user_id=int(form.user_id.data), book_id=book.id + user_id=int(form.user_id.data), book_id=book_id ).first() if not book_contributor: log( log.INFO, "BookContributor does not exists user: [%s], book: [%s]", form.user_id.data, - book.id, + book_id, ) flash("Does not exists!", "success") return redirect(url_for("book.settings", book_id=book_id)) @@ -434,20 +428,15 @@ def edit_contributor_role(book_id: int): @bp.route("//create_collection", methods=["POST"]) @bp.route("///create_sub_collection", methods=["POST"]) +@register_book_verify_route(bp.name) @login_required def collection_create(book_id: int, collection_id: int | None = None): book: m.Book = db.session.get(m.Book, book_id) - if not book or book.owner != current_user or book.is_deleted: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) + + redirect_url = url_for("book.collection_view", book_id=book_id) if collection_id: collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - elif collection.is_leaf: + if collection.is_leaf: log(log.WARNING, "Collection with id [%s] is leaf", collection_id) flash("You can't create subcollection for this collection", "danger") return redirect( @@ -458,8 +447,6 @@ def collection_create(book_id: int, collection_id: int | None = None): ) ) - redirect_url = url_for("book.collection_view", book_id=book_id) - if collection_id: redirect_url = url_for( "book.sub_collection_view", book_id=book_id, collection_id=collection_id ) @@ -522,35 +509,15 @@ def collection_create(book_id: int, collection_id: int | None = None): @bp.route( "////edit", methods=["POST"] ) +@register_book_verify_route(bp.name) @login_required def collection_edit( book_id: int, collection_id: int, sub_collection_id: int | None = None ): book: m.Book = db.session.get(m.Book, book_id) - if not book or book.owner != current_user or book.is_deleted: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - collection_to_edit = collection if sub_collection_id: - sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - if not sub_collection or sub_collection.is_deleted: - log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id) - flash("SubCollection not found", "danger") - return redirect( - url_for( - "book.sub_collection_view", - book_id=book_id, - collection_id=collection_id, - ) - ) - collection_to_edit = sub_collection + collection = db.session.get(m.Collection, sub_collection_id) form = f.EditCollectionForm() redirect_url = url_for( @@ -564,13 +531,13 @@ def collection_edit( collection_query: m.Collection = m.Collection.query.filter_by( is_deleted=False, label=label, - ).filter(m.Collection.id != collection_to_edit.id) + ).filter(m.Collection.id != collection.id) if sub_collection_id: collection_query = collection_query.filter_by(parent_id=collection_id) else: collection_query = collection_query.filter_by( - parent_id=collection_to_edit.parent.id + parent_id=collection.parent.id ) if collection_query.first(): @@ -578,21 +545,21 @@ def collection_edit( log.INFO, "Collection with similar label already exists. Book: [%s], Collection: [%s], Label: [%s]", book.id, - collection.id, + collection_id, label, ) flash("Collection label must be unique!", "danger") return redirect(redirect_url) if label: - collection_to_edit.label = label + collection.label = label about = form.about.data if about: - collection_to_edit.about = about + collection.about = about - log(log.INFO, "Edit collection [%s]", collection_to_edit.id) - collection_to_edit.save() + log(log.INFO, "Edit collection [%s]", collection.id) + collection.save() flash("Success!", "success") if sub_collection_id: @@ -617,40 +584,19 @@ def collection_edit( "////delete", methods=["POST"], ) +@register_book_verify_route(bp.name) @login_required def collection_delete( book_id: int, collection_id: int, sub_collection_id: int | None = None ): - book: m.Book = db.session.get(m.Book, book_id) - if not book or book.owner != current_user: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - collection_to_delete = collection if sub_collection_id: - sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - if not sub_collection or sub_collection.is_deleted: - log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id) - flash("SubCollection not found", "danger") - return redirect( - url_for( - "book.sub_collection_view", - book_id=book_id, - collection_id=collection_id, - ) - ) - collection_to_delete = sub_collection + collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - collection_to_delete.is_deleted = True + collection.is_deleted = True - log(log.INFO, "Delete collection [%s]", collection_to_delete.id) - collection_to_delete.save() + log(log.INFO, "Delete collection [%s]", collection.id) + collection.save() flash("Success!", "success") return redirect( @@ -671,35 +617,16 @@ def collection_delete( "////create_section", methods=["POST"], ) +@register_book_verify_route(bp.name) @login_required def section_create( book_id: int, collection_id: int, sub_collection_id: int | None = None ): book: m.Book = db.session.get(m.Book, book_id) - if not book or book.is_deleted: - log(log.WARNING, "Book with id [%s] not found", book_id) - flash("Book not found", "danger") - return redirect(url_for("book.my_library")) - collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - sub_collection = None if sub_collection_id: sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - if not sub_collection or sub_collection.is_deleted: - log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id) - flash("Subcollection not found", "danger") - return redirect( - url_for( - "book.sub_collection_view", - book_id=book_id, - collection_id=collection_id, - ) - ) redirect_url = url_for("book.collection_view", book_id=book_id) if collection_id: @@ -744,6 +671,7 @@ def section_create( "/////edit_section", methods=["POST"], ) +@register_book_verify_route(bp.name) @login_required def section_edit( book_id: int, @@ -751,31 +679,6 @@ def section_edit( section_id: int, sub_collection_id: int | None = None, ): - book: m.Book = db.session.get(m.Book, book_id) - if not book or book.owner != current_user or book.is_deleted: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - - collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - - if sub_collection_id: - sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - if not sub_collection or sub_collection.is_deleted: - log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id) - flash("SubCollection not found", "danger") - return redirect( - url_for( - "book.sub_collection_view", - book_id=book_id, - collection_id=collection_id, - ) - ) - redirect_url = url_for( "book.interpretation_view", book_id=book_id, @@ -784,10 +687,6 @@ def section_edit( section_id=section_id, ) section: m.Section = db.session.get(m.Section, section_id) - if not section or section.is_deleted: - log(log.WARNING, "Section with id [%s] not found", section_id) - flash("Section not found", "danger") - return redirect(redirect_url) form = f.EditSectionForm() @@ -822,6 +721,7 @@ def section_edit( "/////delete_section", methods=["POST"], ) +@register_book_verify_route(bp.name) @login_required def section_delete( book_id: int, @@ -829,53 +729,19 @@ def section_delete( section_id: int, sub_collection_id: int | None = None, ): - book: m.Book = db.session.get(m.Book, book_id) - if not book or book.owner != current_user: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - - collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - - collection_to_edit = collection - if sub_collection_id: - sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - if not sub_collection or sub_collection.is_deleted: - log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id) - flash("SubCollection not found", "danger") - return redirect( - url_for( - "book.sub_collection_view", - book_id=book_id, - collection_id=collection_id, - ) - ) - collection_to_edit = sub_collection - - redirect_url = url_for( - "book.section_view", - book_id=book_id, - collection_id=collection_id, - sub_collection_id=sub_collection_id, + collection: m.Collection = db.session.get( + m.Collection, sub_collection_id or collection_id ) section: m.Section = db.session.get(m.Section, section_id) - if not section or section.is_deleted: - log(log.WARNING, "Section with id [%s] not found", section_id) - flash("Section not found", "danger") - return redirect(redirect_url) section.is_deleted = True - if not collection_to_edit.active_sections: + if not collection.active_sections: log( log.INFO, "Section [%s] has no active section. Set is_leaf = False", section.id, ) - collection_to_edit.is_leaf = False + collection.is_leaf = False log(log.INFO, "Delete section [%s]", section.id) section.save() @@ -902,6 +768,7 @@ def section_delete( "/////create_interpretation", methods=["POST"], ) +@register_book_verify_route(bp.name) @login_required def interpretation_create( book_id: int, @@ -909,44 +776,8 @@ def interpretation_create( section_id: int, sub_collection_id: int | None = None, ): - book: m.Book = db.session.get(m.Book, book_id) - if not book or book.is_deleted: - log(log.WARNING, "Book with id [%s] not found", book_id) - flash("Book not found", "danger") - return redirect(url_for("book.my_library")) - - collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - - sub_collection = None - if sub_collection_id: - sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - if not sub_collection or sub_collection.is_deleted: - log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id) - flash("SubCollection not found", "danger") - return redirect( - url_for( - "book.sub_collection_view", - book_id=book_id, - collection_id=collection_id, - ) - ) - section: m.Section = db.session.get(m.Section, section_id) - if not section or collection.is_deleted: - log(log.WARNING, "Section with id [%s] not found", section) - flash("Section not found", "danger") - return redirect( - url_for( - "book.section_view", - book_id=book_id, - collection_id=collection_id, - sub_collection_id=sub_collection_id, - ) - ) + form = f.CreateInterpretationForm() redirect_url = url_for( "book.interpretation_view", book_id=book_id, @@ -955,8 +786,6 @@ def interpretation_create( section_id=section.id, ) - form = f.CreateInterpretationForm() - if form.validate_on_submit(): interpretation: m.Interpretation = m.Interpretation( label=form.label.data, @@ -994,6 +823,7 @@ def interpretation_create( ), methods=["POST"], ) +@register_book_verify_route(bp.name) @login_required def interpretation_edit( book_id: int, @@ -1002,31 +832,10 @@ def interpretation_edit( interpretation_id: int, sub_collection_id: int | None = None, ): - book: m.Book = db.session.get(m.Book, book_id) - if not book or book.owner != current_user or book.is_deleted: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - - collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - - if sub_collection_id: - sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - if not sub_collection or sub_collection.is_deleted: - log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id) - flash("SubCollection not found", "danger") - return redirect( - url_for( - "book.sub_collection_view", - book_id=book_id, - collection_id=collection_id, - ) - ) - + interpretation: m.Interpretation = db.session.get( + m.Interpretation, interpretation_id + ) + form = f.EditInterpretationForm() redirect_url = url_for( "book.qa_view", book_id=book_id, @@ -1035,21 +844,6 @@ def interpretation_edit( section_id=section_id, interpretation_id=interpretation_id, ) - section: m.Section = db.session.get(m.Section, section_id) - if not section or section.is_deleted: - log(log.WARNING, "Section with id [%s] not found", section_id) - flash("Section not found", "danger") - return redirect(redirect_url) - - interpretation: m.Interpretation = db.session.get( - m.Interpretation, interpretation_id - ) - if not interpretation or interpretation.is_deleted: - log(log.WARNING, "Interpretation with id [%s] not found", interpretation_id) - flash("Interpretation not found", "danger") - return redirect(redirect_url) - - form = f.EditInterpretationForm() if form.validate_on_submit(): label = form.label.data @@ -1083,6 +877,7 @@ def interpretation_edit( ), methods=["POST"], ) +@register_book_verify_route(bp.name) @login_required def interpretation_delete( book_id: int, @@ -1091,51 +886,9 @@ def interpretation_delete( interpretation_id: int, sub_collection_id: int | None = None, ): - book: m.Book = db.session.get(m.Book, book_id) - if not book or book.owner != current_user or book.is_deleted: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - - collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - - if sub_collection_id: - sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - if not sub_collection or sub_collection.is_deleted: - log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id) - flash("SubCollection not found", "danger") - return redirect( - url_for( - "book.sub_collection_view", - book_id=book_id, - collection_id=collection_id, - ) - ) - - redirect_url = url_for( - "book.interpretation_view", - book_id=book_id, - collection_id=collection_id, - sub_collection_id=sub_collection_id, - section_id=section_id, - ) - section: m.Section = db.session.get(m.Section, section_id) - if not section or section.is_deleted: - log(log.WARNING, "Section with id [%s] not found", section_id) - flash("Section not found", "danger") - return redirect(redirect_url) - interpretation: m.Interpretation = db.session.get( m.Interpretation, interpretation_id ) - if not interpretation or interpretation.is_deleted: - log(log.WARNING, "Interpretation with id [%s] not found", interpretation_id) - flash("Interpretation not found", "danger") - return redirect(redirect_url) interpretation.is_deleted = True log(log.INFO, "Delete interpretation [%s]", interpretation) @@ -1356,6 +1109,7 @@ def create_comment( ), methods=["POST"], ) +@register_book_verify_route(bp.name) @login_required def comment_delete( book_id: int, @@ -1364,65 +1118,9 @@ def comment_delete( interpretation_id: int, sub_collection_id: int | None = None, ): - book: m.Book = db.session.get(m.Book, book_id) - - if not book or book.is_deleted: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - - collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - - if sub_collection_id: - sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - if not sub_collection or sub_collection.is_deleted: - log( - log.WARNING, - "Sub_collection with id [%s] not found", - sub_collection_id, - ) - flash("SubCollection not found", "danger") - return redirect( - url_for( - "book.sub_collection_view", - book_id=book_id, - collection_id=collection_id, - ) - ) - - redirect_url = url_for( - "book.qa_view", - book_id=book_id, - collection_id=collection_id, - sub_collection_id=sub_collection_id, - section_id=section_id, - interpretation_id=interpretation_id, - ) - section: m.Section = db.session.get(m.Section, section_id) - if not section or section.is_deleted: - log(log.WARNING, "Section with id [%s] not found", section_id) - flash("Section not found", "danger") - return redirect(redirect_url) - - interpretation: m.Interpretation = db.session.get( - m.Interpretation, interpretation_id - ) - if not interpretation or interpretation.is_deleted: - log(log.WARNING, "Interpretation with id [%s] not found", interpretation_id) - flash("Interpretation not found", "danger") - return redirect(redirect_url) - form = f.DeleteCommentForm() comment_id = form.comment_id.data comment: m.Comment = db.session.get(m.Comment, comment_id) - if not comment or comment.is_deleted: - log(log.WARNING, "Comment with id [%s] not found", comment_id) - flash("Comment not found", "danger") - return redirect(redirect_url) if form.validate_on_submit(): comment.is_deleted = True @@ -1430,7 +1128,16 @@ def comment_delete( comment.save() flash("Success!", "success") - return redirect(redirect_url) + return redirect( + url_for( + "book.qa_view", + book_id=book_id, + collection_id=collection_id, + sub_collection_id=sub_collection_id, + section_id=section_id, + interpretation_id=interpretation_id, + ) + ) return redirect( url_for( "book.sub_collection_view", @@ -1451,6 +1158,7 @@ def comment_delete( ), methods=["POST"], ) +@register_book_verify_route(bp.name) @login_required def comment_edit( book_id: int, @@ -1459,65 +1167,9 @@ def comment_edit( interpretation_id: int, sub_collection_id: int | None = None, ): - book: m.Book = db.session.get(m.Book, book_id) - - if not book or book.is_deleted: - log(log.INFO, "User: [%s] is not owner of book: [%s]", current_user, book) - flash("You are not owner of this book!", "danger") - return redirect(url_for("book.my_library")) - - collection: m.Collection = db.session.get(m.Collection, collection_id) - if not collection or collection.is_deleted: - log(log.WARNING, "Collection with id [%s] not found", collection_id) - flash("Collection not found", "danger") - return redirect(url_for("book.collection_view", book_id=book_id)) - - if sub_collection_id: - sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) - if not sub_collection or sub_collection.is_deleted: - log( - log.WARNING, - "Sub_collection with id [%s] not found", - sub_collection_id, - ) - flash("SubCollection not found", "danger") - return redirect( - url_for( - "book.sub_collection_view", - book_id=book_id, - collection_id=collection_id, - ) - ) - - redirect_url = url_for( - "book.qa_view", - book_id=book_id, - collection_id=collection_id, - sub_collection_id=sub_collection_id, - section_id=section_id, - interpretation_id=interpretation_id, - ) - section: m.Section = db.session.get(m.Section, section_id) - if not section or section.is_deleted: - log(log.WARNING, "Section with id [%s] not found", section_id) - flash("Section not found", "danger") - return redirect(redirect_url) - - interpretation: m.Interpretation = db.session.get( - m.Interpretation, interpretation_id - ) - if not interpretation or interpretation.is_deleted: - log(log.WARNING, "Interpretation with id [%s] not found", interpretation_id) - flash("Interpretation not found", "danger") - return redirect(redirect_url) - form = f.EditCommentForm() comment_id = form.comment_id.data comment: m.Comment = db.session.get(m.Comment, comment_id) - if not comment or comment.is_deleted: - log(log.WARNING, "Comment with id [%s] not found", comment_id) - flash("Comment not found", "danger") - return redirect(redirect_url) if form.validate_on_submit(): comment.text = form.text.data @@ -1526,7 +1178,16 @@ def comment_edit( comment.save() flash("Success!", "success") - return redirect(redirect_url) + return redirect( + url_for( + "book.qa_view", + book_id=book_id, + collection_id=collection_id, + sub_collection_id=sub_collection_id, + section_id=section_id, + interpretation_id=interpretation_id, + ) + ) return redirect( url_for( "book.sub_collection_view", diff --git a/tests/test_book.py b/tests/test_book.py index e429353..2b86df9 100644 --- a/tests/test_book.py +++ b/tests/test_book.py @@ -3,7 +3,7 @@ from flask import current_app as Response from flask.testing import FlaskClient, FlaskCliRunner from app import models as m, db -from tests.utils import login +from tests.utils import login, logout def test_create_edit_delete_book(client: FlaskClient): @@ -72,7 +72,7 @@ def test_create_edit_delete_book(client: FlaskClient): ) assert response.status_code == 200 - assert b"Book not found" in response.data + assert b"You are not owner of this book!" in response.data response: Response = client.post( f"/book/{book.id}/edit", @@ -194,6 +194,15 @@ def test_delete_contributor(client: FlaskClient, runner: FlaskCliRunner): assert response.status_code == 200 assert b"Does not exists!" in response.data + response: Response = client.post( + f"/book/{123}/add_contributor", + data=dict(user_id=1, role=m.BookContributor.Roles.MODERATOR), + follow_redirects=True, + ) + + assert response.status_code == 200 + assert b"You are not owner of this book!" in response.data + def test_edit_contributor_role(client: FlaskClient, runner: FlaskCliRunner): _, user = login(client) @@ -225,6 +234,27 @@ def test_edit_contributor_role(client: FlaskClient, runner: FlaskCliRunner): assert response.status_code == 200 assert b"Success!" in response.data + moderator = m.User(username="Moderator", password="test").save() + + moderators_book = m.Book(label="Test Book", user_id=moderator.id).save() + response: Response = client.post( + f"/book/{moderators_book.id}/add_contributor", + data=dict(user_id=moderator.id, role=m.BookContributor.Roles.MODERATOR), + follow_redirects=True, + ) + + assert response.status_code == 200 + assert b"You are not owner of this book!" in response.data + + response: Response = client.post( + f"/book/999/add_contributor", + data=dict(user_id=moderator.id, role=m.BookContributor.Roles.MODERATOR), + follow_redirects=True, + ) + + assert response.status_code == 200 + assert b"You are not owner of this book!" in response.data + def test_crud_collection(client: FlaskClient, runner: FlaskCliRunner): _, user = login(client) @@ -255,6 +285,14 @@ def test_crud_collection(client: FlaskClient, runner: FlaskCliRunner): assert response.status_code == 200 assert b"Collection label must be unique!" in response.data + response: Response = client.post( + f"/book/999/create_collection", + data=dict(label="Test Collection #1 Label", about="Test Collection #1 About"), + follow_redirects=True, + ) + assert response.status_code == 200 + assert b"You are not owner of this book!" in response.data + collection: m.Collection = m.Collection.query.filter_by( label="Test Collection #1 Label" ).first() @@ -290,6 +328,14 @@ def test_crud_collection(client: FlaskClient, runner: FlaskCliRunner): assert response.status_code == 200 assert b"Success!" in response.data + response: Response = client.post( + f"/book/999/{collection.id}/edit", + data=dict(label="Test Collection #1 Label", about="Test Collection #1 About"), + follow_redirects=True, + ) + assert response.status_code == 200 + assert b"You are not owner of this book!" in response.data + edited_collection: m.Collection = m.Collection.query.filter_by( label=new_label, about=new_about ).first() @@ -326,12 +372,20 @@ def test_crud_collection(client: FlaskClient, runner: FlaskCliRunner): assert response.status_code == 200 assert b"Collection not found" in response.data + response: Response = client.post( + f"/book/999/{collection.id}/delete", + follow_redirects=True, + ) + + assert response.status_code == 200 + assert b"You are not owner of this book!" in response.data + def test_crud_subcollection(client: FlaskClient, runner: FlaskCliRunner): _, user = login(client) user: m.User - # add dummmy data + # add dummy data runner.invoke(args=["db-populate"]) book: m.Book = db.session.get(m.Book, 1) @@ -348,6 +402,16 @@ def test_crud_subcollection(client: FlaskClient, runner: FlaskCliRunner): label="Test Collection #1 Label", version_id=book.last_version.id ).save() + response: Response = client.post( + f"/book/999/{leaf_collection.id}/create_sub_collection", + data=dict( + label="Test SubCollection #1 Label", about="Test SubCollection #1 About" + ), + follow_redirects=True, + ) + assert response.status_code == 200 + assert b"You are not owner of this book!" in response.data + response: Response = client.post( f"/book/{book.id}/{leaf_collection.id}/create_sub_collection", data=dict( @@ -435,7 +499,7 @@ def test_crud_subcollection(client: FlaskClient, runner: FlaskCliRunner): ) assert response.status_code == 200 - assert b"SubCollection not found" in response.data + assert b"Subcollection not found" in response.data response: Response = client.post( f"/book/{book.id}/{collection.id}/{sub_collection.id}/delete", @@ -454,7 +518,7 @@ def test_crud_subcollection(client: FlaskClient, runner: FlaskCliRunner): ) assert response.status_code == 200 - assert b"Collection not found" in response.data + assert b"Subcollection not found" in response.data def test_crud_sections(client: FlaskClient, runner: FlaskCliRunner): @@ -1004,3 +1068,36 @@ def test_crud_comment(client: FlaskClient, runner: FlaskCliRunner): assert response.status_code == 200 assert b"Success" in response.data assert str.encode(comment_text) not in response.data + + +def test_access_to_settings_page(client: FlaskClient): + _, user = login(client) + + book_1 = m.Book(label="test", about="test").save() + book_2 = m.Book(label="test", about="test", user_id=user.id).save() + + response: Response = client.get( + f"/book/{book_1.id}/settings", + follow_redirects=True, + ) + + assert response.status_code == 200 + assert b"You are not owner of this book!" in response.data + + response: Response = client.get( + f"/book/{book_2.id}/settings", + follow_redirects=True, + ) + + assert response.status_code == 200 + assert b"You are not owner of this book!" not in response.data + + logout(client) + + response: Response = client.get( + f"/book/{book_2.id}/settings", + follow_redirects=True, + ) + + assert response.status_code == 200 + assert b"You are not owner of this book!" in response.data