From 039d9b8feab0aa94c912102455c5fdda480fd817 Mon Sep 17 00:00:00 2001 From: SvyatoslavArtymovych Date: Mon, 5 Jun 2023 17:40:25 +0300 Subject: [PATCH] access_tree/ --- .../book/modals/access_level_modal.html | 12 +++- .../book/modals/access_level_modal_v2.html | 2 +- app/views/permission.py | 60 ++++++++++++++++++- src/checkBoxTree.ts | 1 - src/main.ts | 2 + src/refreshAccessLevelTree.ts | 35 +++++++++++ tests/test_permissions.py | 30 ++++++++++ 7 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 src/refreshAccessLevelTree.ts diff --git a/app/templates/book/modals/access_level_modal.html b/app/templates/book/modals/access_level_modal.html index 9d37794..d5ee753 100644 --- a/app/templates/book/modals/access_level_modal.html +++ b/app/templates/book/modals/access_level_modal.html @@ -3,7 +3,7 @@ class="fixed top-0 left-0 right-0 z-[150] hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
-
+ {{ form_hidden_tag() }} @@ -23,7 +23,13 @@
  • - + {{ book.label }}
    {%- for collection in book.last_version.children_collections recursive %} @@ -43,7 +49,7 @@
    • - + {{ sub_collection.label }}
      {% for section in sub_collection.sections %} diff --git a/app/templates/book/modals/access_level_modal_v2.html b/app/templates/book/modals/access_level_modal_v2.html index 3eac823..c1311ad 100644 --- a/app/templates/book/modals/access_level_modal_v2.html +++ b/app/templates/book/modals/access_level_modal_v2.html @@ -8,7 +8,7 @@ class="fixed top-0 left-0 right-0 z-[150] hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
      - + {{ form_hidden_tag() }} diff --git a/app/views/permission.py b/app/views/permission.py index ebfdff8..19309eb 100644 --- a/app/views/permission.py +++ b/app/views/permission.py @@ -1,14 +1,15 @@ -from flask import redirect, url_for, Blueprint, flash +from flask import redirect, url_for, Blueprint, flash, request from flask_login import current_user +from sqlalchemy import or_ from app import forms as f, models as m, db from app.logger import log -bp = Blueprint("permission", __name__, "/permission") +bp = Blueprint("permission", __name__, url_prefix="/permission") @bp.route("/set", methods=["POST"]) -def set(): +def set_permissions(): form: f.EditPermissionForm = f.EditPermissionForm() if form.validate_on_submit(): @@ -37,3 +38,56 @@ def set(): # permissions = json.loads(form.permissions.data) return {"status": "ok"} + + +@bp.route("/access_tree", methods=["GET"]) +def access_tree(): + user_id = request.args.get("user_id", type=int) + book_id = request.args.get("book_id", type=int) + if not user_id or not book_id: + return {"message": "get parameters user_id and book_id are required"}, 404 + + user: m.User = db.session.get(m.User, user_id) + if not user: + return {"message": f"user with id {user_id} not found"}, 404 + book: m.Book = db.session.get(m.Book, book_id) + if not book: + return {"message": f"book with id {user_id} not found"}, 404 + + access_tree = { + "book": [], + "collection": [], + "section": [], + } + + users_access_groups: list[m.AccessGroup] = list( + set(book.list_access_groups).intersection(user.access_groups) + ) + + if list(set(book.access_groups).intersection(users_access_groups)): + access_tree["book"].append(book_id) + + collections = ( + db.session.query(m.Collection).filter( + m.Collection.version_id == book.last_version.id, + m.Collection.is_root == False, # noqa: E712 + m.Collection.is_deleted == False, # noqa: E712 + ) + ).all() + + for collection in collections: + if list(set(collection.access_groups).intersection(users_access_groups)): + access_tree["collection"].append(collection.id) + + sections = ( + db.session.query(m.Section).filter( + m.Section.version_id == book.last_version.id, + m.Section.is_deleted == False, # noqa: E712 + ) + ).all() + + for section in sections: + if list(set(section.access_groups).intersection(users_access_groups)): + access_tree["section"].append(section.id) + + return {"access_tree": access_tree} diff --git a/src/checkBoxTree.ts b/src/checkBoxTree.ts index 61b0933..7198313 100644 --- a/src/checkBoxTree.ts +++ b/src/checkBoxTree.ts @@ -59,7 +59,6 @@ const handleCheckboxClick = (checkbox: HTMLInputElement) => { export const initCheckBoxTree = () => { const permissionsJSON: Permissions = { book: [], - sub_collection: [], collection: [], section: [], }; diff --git a/src/main.ts b/src/main.ts index 253cc44..f4230ea 100644 --- a/src/main.ts +++ b/src/main.ts @@ -30,6 +30,7 @@ import {slashSearch} from './slashSearch'; import {editInterpretations} from './editInterpretations'; import {deleteInterpretation} from './deleteInterpretation'; import {indeterminateInputs} from './indeterminateInputs'; +import {initRefreshAccessLevelTree} from './refreshAccessLevelTree'; initQuillReadOnly(); initBooks(); @@ -63,3 +64,4 @@ slashSearch(); editInterpretations(); deleteInterpretation(); indeterminateInputs(); +initRefreshAccessLevelTree(); diff --git a/src/refreshAccessLevelTree.ts b/src/refreshAccessLevelTree.ts new file mode 100644 index 0000000..f5ae0e9 --- /dev/null +++ b/src/refreshAccessLevelTree.ts @@ -0,0 +1,35 @@ +const refreshAccessLevelTree = async (userId: string, bookId: string) => { + const urlParams = new URLSearchParams({ + user_id: userId, + book_id: bookId, + }); + const res = await fetch('/permission/access_tree?' + urlParams); + const json = await res.json(); + + Object.entries(json.access_tree).map(([key, ids]: [string, number[]]) => { + const checkboxes = document.querySelectorAll( + `input[type=checkbox][data-access-to=${key}]`, + ); + + checkboxes.forEach((element: HTMLInputElement) => { + const id = parseInt(element.getAttribute('data-access-to-id')); + if (ids.includes(id)) { + element.checked = true; + } + }); + }); +}; + +export function initRefreshAccessLevelTree() { + const editPermissionsBtns = document.querySelectorAll( + '.edit-permissions-btn', + ); + + editPermissionsBtns.forEach(element => { + const userId = element.getAttribute('data-user-id'); + const bookId = element.getAttribute('data-book-id'); + element.addEventListener('click', () => { + refreshAccessLevelTree(userId, bookId); + }); + }); +} diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 274b61b..9115cb0 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -257,3 +257,33 @@ def test_moderator_access_to_entire_book(client): ) assert b"You do not have permission" not in response.data assert b"Success!" in response.data + + +def test_editor_access_tree_entire_book(client): + login(client) + book = create_book(client) + collection_1, _ = create_collection(client, book.id) + collection_2, _ = create_collection(client, book.id) + + editor = m.User(username="editor", password="editor").save() + response: Response = client.post( + f"/book/{book.id}/add_contributor", + data=dict(user_id=editor.id, role=m.BookContributor.Roles.EDITOR), + follow_redirects=True, + ) + assert response.status_code == 200 + assert b"Contributor was added!" in response.data + + response: Response = client.get( + f"/permission/access_tree?user_id={editor.id}&book_id={book.id}", + follow_redirects=True, + ) + assert response.status_code == 200 + json = response.json + access_tree = json.get("access_tree") + assert access_tree + assert book.id in access_tree.get("book") + collections_ids = access_tree.get("collection") + assert collections_ids + assert collection_1.id in collections_ids + assert collection_2.id in collections_ids