mirror of https://github.com/logos-co/open-law.git
finish flask admin customization
This commit is contained in:
parent
298096e126
commit
d8a85b37fb
|
@ -100,6 +100,7 @@ def create_app(environment="development"):
|
|||
InterpretationView,
|
||||
CommentView,
|
||||
TagView,
|
||||
BookContributorView,
|
||||
)
|
||||
|
||||
app.config["FLASK_ADMIN_SWATCH"] = "Flatly"
|
||||
|
@ -125,6 +126,12 @@ def create_app(environment="development"):
|
|||
),
|
||||
CommentView(m.Comment, db.session, name="Comment", endpoint="/comment_"),
|
||||
TagView(m.Tag, db.session, name="Tag", endpoint="/tag_"),
|
||||
BookContributorView(
|
||||
m.BookContributor,
|
||||
db.session,
|
||||
name="BookContributor",
|
||||
endpoint="/book_contributor_",
|
||||
),
|
||||
]:
|
||||
admin.add_view(view)
|
||||
|
||||
|
|
|
@ -8,3 +8,4 @@ from .section import SectionsView
|
|||
from .interpretation import InterpretationView
|
||||
from .comment import CommentView
|
||||
from .tag import TagView
|
||||
from .book_contributors import BookContributorView
|
||||
|
|
|
@ -8,8 +8,13 @@ from .protected_model_view import ProtectedModelView
|
|||
|
||||
|
||||
class BooksView(ProtectedModelView):
|
||||
column_list = ("id", "label", "about", "is_deleted", "user_id", "created_at")
|
||||
column_labels = dict(user_id="Created by")
|
||||
column_list = ("id", "label", "about", "is_deleted", "owner", "created_at")
|
||||
form_edit_rules = (
|
||||
"label",
|
||||
"about",
|
||||
"is_deleted",
|
||||
"created_at",
|
||||
)
|
||||
|
||||
@expose("/delete/", methods=("POST",))
|
||||
def delete_view(self):
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
from flask_admin.base import expose
|
||||
from flask_admin.helpers import (
|
||||
get_redirect_target,
|
||||
flash_errors,
|
||||
)
|
||||
from flask import redirect, flash, request
|
||||
from flask_admin.babel import gettext
|
||||
from wtforms import SelectField
|
||||
from flask_admin.contrib.sqla.fields import QuerySelectField
|
||||
from flask_admin.form import form
|
||||
from flask_admin.model.template import EndpointLinkRowAction
|
||||
|
||||
from .protected_model_view import ProtectedModelView
|
||||
from app import models as m, forms as f
|
||||
from app.controllers.contributor import (
|
||||
add_contributor_to_book,
|
||||
delete_contributor_from_book,
|
||||
)
|
||||
from app.controllers.permission import set_access_level
|
||||
from app.logger import log
|
||||
|
||||
|
||||
class BookContributorViewCreateForm(form.Form):
|
||||
book = QuerySelectField("Book", get_label="label")
|
||||
user = QuerySelectField("User", get_label="username")
|
||||
role = SelectField(
|
||||
"Role",
|
||||
choices=[
|
||||
(role.value, role.name)
|
||||
for role in m.BookContributor.Roles
|
||||
if role.value > 0
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class BookContributorView(ProtectedModelView):
|
||||
column_list = ("id", "created_at", "role", "user", "book")
|
||||
can_create = True
|
||||
can_edit = False
|
||||
column_extra_row_actions = [ # Add a new action button
|
||||
EndpointLinkRowAction("glyphicon glyphicon-list-alt", ".edit_access_level"),
|
||||
]
|
||||
|
||||
@expose("/edit_access_level/<string:id>", methods=("GET", "POST"))
|
||||
def edit_access_level(self, id):
|
||||
model: m.BookContributor = self.get_one((id,))
|
||||
|
||||
user = model.user
|
||||
book = model.book
|
||||
form: f.EditPermissionForm = f.EditPermissionForm()
|
||||
if form.validate_on_submit():
|
||||
set_access_level(form, book)
|
||||
|
||||
return self.render(
|
||||
"admin/contributor_access_level.html", user=user, book=book, id=id
|
||||
)
|
||||
|
||||
def create_form(self):
|
||||
form = BookContributorViewCreateForm(request.form)
|
||||
form.book.query = m.Book.query.filter_by(is_deleted=False)
|
||||
form.user.query = m.User.query
|
||||
return form
|
||||
|
||||
@expose("/new/", methods=("GET", "POST"))
|
||||
def create_view(self):
|
||||
return_url = get_redirect_target() or self.get_url(".index_view")
|
||||
|
||||
if not self.can_delete:
|
||||
return redirect(return_url)
|
||||
|
||||
form: BookContributorViewCreateForm = self.create_form()
|
||||
|
||||
if (
|
||||
form.user.data
|
||||
and form.book.data
|
||||
and form.book.data.user_id == form.user.data.id
|
||||
):
|
||||
flash("This user is owner of this book", "danger")
|
||||
elif self.validate_form(form):
|
||||
add_contributor_to_book(form, form.book.data.id, user_id=form.user.data.id)
|
||||
|
||||
return self.render("admin/model/create.html", form=form)
|
||||
|
||||
@expose("/delete/", methods=("POST",))
|
||||
def delete_view(self):
|
||||
return_url = get_redirect_target() or self.get_url(".index_view")
|
||||
|
||||
if not self.can_delete:
|
||||
return redirect(return_url)
|
||||
|
||||
form = self.delete_form()
|
||||
|
||||
if self.validate_form(form):
|
||||
# id is InputRequired()
|
||||
id = form.id.data
|
||||
|
||||
model = self.get_one(id)
|
||||
|
||||
if model is None:
|
||||
flash(gettext("Record does not exist."), "error")
|
||||
return redirect(return_url)
|
||||
|
||||
try:
|
||||
delete_contributor_from_book(form, model.book_id, user_id=model.user_id)
|
||||
except Exception as e:
|
||||
log(
|
||||
log.EXCEPTION,
|
||||
"AdminPanel delete contributor unexpected error [%s]",
|
||||
str(e),
|
||||
)
|
||||
return redirect(return_url)
|
||||
|
||||
else:
|
||||
flash_errors(form, message="Failed to delete record. %(error)s")
|
||||
|
||||
return redirect(return_url)
|
|
@ -20,6 +20,13 @@ class CollectionsView(ProtectedModelView):
|
|||
"is_deleted",
|
||||
"created_at",
|
||||
)
|
||||
form_edit_rules = (
|
||||
"label",
|
||||
"about",
|
||||
"position",
|
||||
"is_deleted",
|
||||
"created_at",
|
||||
)
|
||||
|
||||
@expose("/delete/", methods=("POST",))
|
||||
def delete_view(self):
|
||||
|
|
|
@ -12,6 +12,14 @@ from .protected_model_view import ProtectedModelView
|
|||
class CommentView(ProtectedModelView):
|
||||
column_list = (
|
||||
"id",
|
||||
"text",
|
||||
"approved",
|
||||
"edited",
|
||||
"is_deleted",
|
||||
"user",
|
||||
"created_at",
|
||||
)
|
||||
form_edit_rules = (
|
||||
"text",
|
||||
"approved",
|
||||
"edited",
|
||||
|
|
|
@ -15,6 +15,14 @@ class InterpretationView(ProtectedModelView):
|
|||
"plain_text",
|
||||
"approved",
|
||||
"is_deleted",
|
||||
"user",
|
||||
"created_at",
|
||||
)
|
||||
form_edit_rules = (
|
||||
"text",
|
||||
"plain_text",
|
||||
"approved",
|
||||
"is_deleted",
|
||||
"created_at",
|
||||
)
|
||||
|
||||
|
|
|
@ -12,6 +12,13 @@ from .protected_model_view import ProtectedModelView
|
|||
class SectionsView(ProtectedModelView):
|
||||
column_list = (
|
||||
"id",
|
||||
"label",
|
||||
"position",
|
||||
"is_deleted",
|
||||
"user",
|
||||
"created_at",
|
||||
)
|
||||
form_edit_rules = (
|
||||
"label",
|
||||
"position",
|
||||
"is_deleted",
|
||||
|
|
|
@ -15,6 +15,11 @@ class TagView(ProtectedModelView):
|
|||
"name",
|
||||
"created_at",
|
||||
)
|
||||
form_edit_rules = (
|
||||
"id",
|
||||
"name",
|
||||
"created_at",
|
||||
)
|
||||
|
||||
@expose("/delete/", methods=("POST",))
|
||||
def delete_view(self):
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
from flask import flash, redirect, url_for
|
||||
|
||||
from app import forms as f, models as m, db
|
||||
from app.logger import log
|
||||
|
||||
|
||||
def add_contributor_to_book(
|
||||
form: f.AddContributorForm,
|
||||
book_id: int,
|
||||
selected_tab: str = "",
|
||||
user_id: int = None,
|
||||
):
|
||||
if not user_id:
|
||||
user_id = form.user_id.data
|
||||
book_contributor = m.BookContributor.query.filter_by(
|
||||
user_id=user_id, book_id=book_id
|
||||
).first()
|
||||
if book_contributor:
|
||||
log(log.INFO, "Contributor: [%s] already exists", book_contributor)
|
||||
flash("Already exists!", "danger")
|
||||
return redirect(
|
||||
url_for("book.settings", selected_tab=selected_tab, book_id=book_id)
|
||||
)
|
||||
|
||||
role = m.BookContributor.Roles(int(form.role.data))
|
||||
contributor = m.BookContributor(user_id=user_id, book_id=book_id, role=role)
|
||||
log(log.INFO, "New contributor [%s]", contributor)
|
||||
contributor.save()
|
||||
|
||||
groups = (
|
||||
db.session.query(m.AccessGroup)
|
||||
.filter(
|
||||
m.BookAccessGroups.book_id == book_id,
|
||||
m.AccessGroup.id == m.BookAccessGroups.access_group_id,
|
||||
m.AccessGroup.name == role.name.lower(),
|
||||
)
|
||||
.all()
|
||||
)
|
||||
for group in groups:
|
||||
m.UserAccessGroups(user_id=user_id, access_group_id=group.id).save()
|
||||
|
||||
flash("Contributor was added!", "success")
|
||||
return redirect(
|
||||
url_for("book.settings", selected_tab=selected_tab, book_id=book_id)
|
||||
)
|
||||
|
||||
|
||||
def delete_contributor_from_book(
|
||||
form: f.DeleteContributorForm,
|
||||
book_id: int,
|
||||
selected_tab: str = "",
|
||||
user_id: int = None,
|
||||
):
|
||||
if not user_id:
|
||||
user_id = form.user_id.data
|
||||
book_contributor = m.BookContributor.query.filter_by(
|
||||
user_id=user_id, book_id=book_id
|
||||
).first()
|
||||
if not book_contributor:
|
||||
log(
|
||||
log.INFO,
|
||||
"BookContributor does not exists user: [%s], book: [%s]",
|
||||
user_id,
|
||||
book_id,
|
||||
)
|
||||
flash("Does not exists!", "success")
|
||||
return redirect(
|
||||
url_for("book.settings", selected_tab=selected_tab, book_id=book_id)
|
||||
)
|
||||
|
||||
book: m.Book = db.session.get(m.Book, book_id)
|
||||
user: m.User = db.session.get(m.User, user_id)
|
||||
if book:
|
||||
for access_group in book.access_groups:
|
||||
access_group: m.AccessGroup
|
||||
if user in access_group.users:
|
||||
log(
|
||||
log.INFO,
|
||||
"Delete user [%s] from AccessGroup [%s]",
|
||||
user,
|
||||
access_group,
|
||||
)
|
||||
relationships_to_delete = m.UserAccessGroups.query.filter_by(
|
||||
user_id=user_id, access_group_id=access_group.id
|
||||
).all()
|
||||
for relationship in relationships_to_delete:
|
||||
db.session.delete(relationship)
|
||||
|
||||
log(log.INFO, "Delete BookContributor [%s]", book_contributor)
|
||||
db.session.delete(book_contributor)
|
||||
db.session.commit()
|
||||
|
||||
flash("Success!", "success")
|
||||
return redirect(
|
||||
url_for("book.settings", selected_tab=selected_tab, book_id=book_id)
|
||||
)
|
|
@ -0,0 +1,89 @@
|
|||
import json
|
||||
|
||||
from flask_login import current_user
|
||||
from flask import flash, redirect, url_for
|
||||
|
||||
from app.logger import log
|
||||
from app import models as m, db, forms as f
|
||||
from app.controllers.create_access_groups import (
|
||||
create_editor_group,
|
||||
create_moderator_group,
|
||||
)
|
||||
|
||||
|
||||
def set_access_level(form: f.EditPermissionForm, book: m.Book):
|
||||
user_id = form.user_id.data
|
||||
contributor: m.BookContributor = m.BookContributor.query.filter_by(
|
||||
user_id=user_id, book_id=book.id
|
||||
).first()
|
||||
if not contributor:
|
||||
log(
|
||||
log.INFO,
|
||||
"User: [%s] is not contributor of book: [%s]",
|
||||
current_user,
|
||||
book,
|
||||
)
|
||||
flash("User are not contributor of this book!", "danger")
|
||||
return redirect(url_for("book.my_library"))
|
||||
|
||||
user: m.User = contributor.user
|
||||
users_access_groups: list[m.AccessGroup] = list(
|
||||
set(book.list_access_groups).intersection(user.access_groups)
|
||||
)
|
||||
if len(users_access_groups) > 1:
|
||||
log(
|
||||
log.WARNING,
|
||||
"User: [%s] has more than 1 access group in book [%s]",
|
||||
user,
|
||||
book,
|
||||
)
|
||||
|
||||
for users_access in users_access_groups:
|
||||
users_access: m.AccessGroup
|
||||
users_access.users.remove(user)
|
||||
|
||||
permissions_json = json.loads(form.permissions.data)
|
||||
book_ids = permissions_json.get("book", [])
|
||||
for book_id in book_ids:
|
||||
entire_boot_access_group = m.AccessGroup.query.filter_by(
|
||||
book_id=book_id, name=contributor.role.name.lower()
|
||||
).first()
|
||||
m.UserAccessGroups(
|
||||
user_id=user.id, access_group_id=entire_boot_access_group.id
|
||||
).save(False)
|
||||
db.session.commit()
|
||||
flash("Success!", "success")
|
||||
return redirect(url_for("book.settings", book_id=book.id))
|
||||
|
||||
new_access_group = None
|
||||
match contributor.role:
|
||||
case m.BookContributor.Roles.EDITOR:
|
||||
new_access_group = create_editor_group(book.id)
|
||||
case m.BookContributor.Roles.MODERATOR:
|
||||
new_access_group = create_moderator_group(book.id)
|
||||
case _:
|
||||
log(
|
||||
log.CRITICAL,
|
||||
"Unknown contributor's [%s] role: [%s]",
|
||||
contributor,
|
||||
contributor.role,
|
||||
)
|
||||
flash("Unknown contributor's role", "danger")
|
||||
return redirect(url_for("book.settings", book_id=book.id))
|
||||
m.UserAccessGroups(user_id=user.id, access_group_id=new_access_group.id).save(False)
|
||||
|
||||
collection_ids = permissions_json.get("collection", [])
|
||||
for collection_id in collection_ids:
|
||||
m.CollectionAccessGroups(
|
||||
collection_id=collection_id, access_group_id=new_access_group.id
|
||||
).save(False)
|
||||
|
||||
section_ids = permissions_json.get("section", [])
|
||||
for section_id in section_ids:
|
||||
m.SectionAccessGroups(
|
||||
section_id=section_id, access_group_id=new_access_group.id
|
||||
).save(False)
|
||||
|
||||
db.session.commit()
|
||||
flash("Success!", "success")
|
||||
return redirect(url_for("book.settings", book_id=book.id))
|
File diff suppressed because one or more lines are too long
149172
app/static/js/main.js
149172
app/static/js/main.js
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,70 @@
|
|||
{% extends 'admin/base.html' %}
|
||||
|
||||
{% block head_css %}
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<span class="hidden trigger-refreshAccessLevelTree"></span>
|
||||
<div>
|
||||
<div>Edit {{ user }} access level in book {{ book }}</div>
|
||||
<form action="{{ url_for('.edit_access_level', id=id) }}" method="post">
|
||||
{{ form_hidden_tag() }}
|
||||
<input type="hidden" value="{{ book.id }}" name="book_id" id="permission_modal_book_id"/>
|
||||
<input type="hidden" value="{{ user.id }}" name="user_id" id="permission_modal_user_id"/>
|
||||
<input type="hidden" name="permissions" id="permissions_json"/>
|
||||
<div>
|
||||
<div class="checkbox-tree">
|
||||
<ul>
|
||||
<li>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
data-root="true"
|
||||
data-access-to="book"
|
||||
data-access-to-id="{{ book.id }}"
|
||||
/>
|
||||
<span>{{ book.label }}</span>
|
||||
</div>
|
||||
{%- for collection in book.last_version.children_collections recursive %}
|
||||
<ul>
|
||||
<li>
|
||||
<div>
|
||||
<input type="checkbox" data-access-to="collection" data-access-to-id="{{ collection.id }}"/>
|
||||
<span>{{ collection.label }}</span>
|
||||
</div>
|
||||
|
||||
{% if collection.active_children %}
|
||||
{{ loop(collection.active_children)}}
|
||||
{% else %}
|
||||
|
||||
{% for section in collection.sections %}
|
||||
<ul>
|
||||
<li>
|
||||
<div>
|
||||
<input type="checkbox" data-access-to="section" data-access-to-id="{{ section.id }}"/>
|
||||
<span>{{ section.label }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
{%- endfor %}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600">
|
||||
<button name="submit" type="submit"
|
||||
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}" type="text/javascript" defer></script>
|
||||
{% endblock %}
|
|
@ -41,47 +41,20 @@
|
|||
</div>
|
||||
|
||||
{% if collection.active_children %}
|
||||
|
||||
{{ loop(collection.active_children)}}
|
||||
|
||||
{#
|
||||
{% for sub_collection in collection.active_children %}
|
||||
<ul class="ml-5">
|
||||
<li>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input type="checkbox" data-access-to="collection" data-access-to-id="{{ sub_collection.id }}" class="w-4 h-4 text-blue-600 bg-gray-300 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-400 dark:border-gray-600" />
|
||||
<span class="text-center dark:text-gray-300">{{ sub_collection.label }}</span>
|
||||
</div>
|
||||
{% for section in sub_collection.sections %}
|
||||
<ul class="ml-4">
|
||||
<li>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input type="checkbox" data-access-to="section" data-access-to-id="{{ section.id }}" class="w-4 h-4 text-blue-600 bg-gray-300 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-400 dark:border-gray-600" />
|
||||
<span class="text-center dark:text-gray-300">{{ section.label }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</li>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
#}
|
||||
|
||||
|
||||
{% else %}
|
||||
|
||||
{% for section in collection.sections %}
|
||||
<ul class="ml-5">
|
||||
<li>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input type="checkbox" data-access-to="section" data-access-to-id="{{ section.id }}" class="w-4 h-4 text-purple-600 bg-purple-100 border-purple-400 rounded focus:ring-purple-500 dark:focus:ring-purple-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-purple-300 dark:border-purple-500" />
|
||||
|
||||
{% for section in collection.sections %}
|
||||
<ul class="ml-5">
|
||||
<li>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input type="checkbox" data-access-to="section" data-access-to-id="{{ section.id }}" class="w-4 h-4 text-purple-600 bg-purple-100 border-purple-400 rounded focus:ring-purple-500 dark:focus:ring-purple-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-purple-300 dark:border-purple-500" />
|
||||
|
||||
<span class="text-center dark:text-gray-300">{{ section.label }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
<span class="text-center dark:text-gray-300">{{ section.label }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
|
|
@ -88,7 +88,6 @@
|
|||
{% if book.user_id == current_user.id %}
|
||||
<div class="hidden px-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="permissions" role="tabpanel" aria-labelledby="permissions-tab">
|
||||
<div class="px-5">
|
||||
|
||||
<div class="mb-3 relative overflow-x-auto shadow-md sm:rounded-lg">
|
||||
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
|
|
|
@ -12,6 +12,10 @@ from app.controllers import (
|
|||
)
|
||||
from app import models as m, db, forms as f
|
||||
from app.controllers.require_permission import require_permission
|
||||
from app.controllers.contributor import (
|
||||
add_contributor_to_book,
|
||||
delete_contributor_from_book,
|
||||
)
|
||||
from app.logger import log
|
||||
from .bp import bp
|
||||
|
||||
|
@ -48,38 +52,8 @@ def add_contributor(book_id: int):
|
|||
form = f.AddContributorForm()
|
||||
selected_tab = "user_permissions"
|
||||
if form.validate_on_submit():
|
||||
user_id = form.user_id.data
|
||||
book_contributor = m.BookContributor.query.filter_by(
|
||||
user_id=user_id, book_id=book_id
|
||||
).first()
|
||||
if book_contributor:
|
||||
log(log.INFO, "Contributor: [%s] already exists", book_contributor)
|
||||
flash("Already exists!", "danger")
|
||||
return redirect(
|
||||
url_for("book.settings", selected_tab=selected_tab, book_id=book_id)
|
||||
)
|
||||
|
||||
role = m.BookContributor.Roles(int(form.role.data))
|
||||
contributor = m.BookContributor(user_id=user_id, book_id=book_id, role=role)
|
||||
log(log.INFO, "New contributor [%s]", contributor)
|
||||
contributor.save()
|
||||
|
||||
groups = (
|
||||
db.session.query(m.AccessGroup)
|
||||
.filter(
|
||||
m.BookAccessGroups.book_id == book_id,
|
||||
m.AccessGroup.id == m.BookAccessGroups.access_group_id,
|
||||
m.AccessGroup.name == role.name.lower(),
|
||||
)
|
||||
.all()
|
||||
)
|
||||
for group in groups:
|
||||
m.UserAccessGroups(user_id=user_id, access_group_id=group.id).save()
|
||||
|
||||
flash("Contributor was added!", "success")
|
||||
return redirect(
|
||||
url_for("book.settings", selected_tab=selected_tab, book_id=book_id)
|
||||
)
|
||||
response = add_contributor_to_book(form, book_id, selected_tab)
|
||||
return response
|
||||
else:
|
||||
log(log.ERROR, "Book create errors: [%s]", form.errors)
|
||||
for field, errors in form.errors.items():
|
||||
|
@ -104,47 +78,8 @@ def delete_contributor(book_id: int):
|
|||
selected_tab = "user_permissions"
|
||||
|
||||
if form.validate_on_submit():
|
||||
user_id = int(form.user_id.data)
|
||||
book_contributor = m.BookContributor.query.filter_by(
|
||||
user_id=user_id, book_id=book_id
|
||||
).first()
|
||||
if not book_contributor:
|
||||
log(
|
||||
log.INFO,
|
||||
"BookContributor does not exists user: [%s], book: [%s]",
|
||||
user_id,
|
||||
book_id,
|
||||
)
|
||||
flash("Does not exists!", "success")
|
||||
return redirect(
|
||||
url_for("book.settings", selected_tab=selected_tab, book_id=book_id)
|
||||
)
|
||||
|
||||
book: m.Book = db.session.get(m.Book, book_id)
|
||||
user: m.User = db.session.get(m.User, user_id)
|
||||
for access_group in book.access_groups:
|
||||
access_group: m.AccessGroup
|
||||
if user in access_group.users:
|
||||
log(
|
||||
log.INFO,
|
||||
"Delete user [%s] from AccessGroup [%s]",
|
||||
user,
|
||||
access_group,
|
||||
)
|
||||
relationships_to_delete = m.UserAccessGroups.query.filter_by(
|
||||
user_id=user_id, access_group_id=access_group.id
|
||||
).all()
|
||||
for relationship in relationships_to_delete:
|
||||
db.session.delete(relationship)
|
||||
|
||||
log(log.INFO, "Delete BookContributor [%s]", book_contributor)
|
||||
db.session.delete(book_contributor)
|
||||
db.session.commit()
|
||||
|
||||
flash("Success!", "success")
|
||||
return redirect(
|
||||
url_for("book.settings", selected_tab=selected_tab, book_id=book_id)
|
||||
)
|
||||
response = delete_contributor_from_book(form, book_id, selected_tab)
|
||||
return response
|
||||
else:
|
||||
log(log.ERROR, "Delete contributor errors: [%s]", form.errors)
|
||||
for field, errors in form.errors.items():
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import json
|
||||
|
||||
from flask import redirect, url_for, Blueprint, flash, request
|
||||
from flask_login import current_user
|
||||
|
||||
from app import forms as f, models as m, db
|
||||
from app.logger import log
|
||||
from app.controllers.create_access_groups import (
|
||||
create_editor_group,
|
||||
create_moderator_group,
|
||||
)
|
||||
from app.controllers.permission import set_access_level
|
||||
|
||||
bp = Blueprint("permission", __name__, url_prefix="/permission")
|
||||
|
||||
|
@ -24,84 +19,8 @@ def set_permissions():
|
|||
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"))
|
||||
|
||||
user_id = form.user_id.data
|
||||
contributor: m.BookContributor = m.BookContributor.query.filter_by(
|
||||
user_id=user_id, book_id=book_id
|
||||
).first()
|
||||
if not contributor:
|
||||
log(
|
||||
log.INFO,
|
||||
"User: [%s] is not contributor of book: [%s]",
|
||||
current_user,
|
||||
book,
|
||||
)
|
||||
flash("User are not contributor of this book!", "danger")
|
||||
return redirect(url_for("book.my_library"))
|
||||
|
||||
user: m.User = contributor.user
|
||||
users_access_groups: list[m.AccessGroup] = list(
|
||||
set(book.list_access_groups).intersection(user.access_groups)
|
||||
)
|
||||
if len(users_access_groups) > 1:
|
||||
log(
|
||||
log.WARNING,
|
||||
"User: [%s] has more than 1 access group in book [%s]",
|
||||
user,
|
||||
book,
|
||||
)
|
||||
|
||||
for users_access in users_access_groups:
|
||||
users_access: m.AccessGroup
|
||||
users_access.users.remove(user)
|
||||
|
||||
permissions_json = json.loads(form.permissions.data)
|
||||
book_ids = permissions_json.get("book", [])
|
||||
for book_id in book_ids:
|
||||
entire_boot_access_group = m.AccessGroup.query.filter_by(
|
||||
book_id=book_id, name=contributor.role.name.lower()
|
||||
).first()
|
||||
m.UserAccessGroups(
|
||||
user_id=user.id, access_group_id=entire_boot_access_group.id
|
||||
).save(False)
|
||||
db.session.commit()
|
||||
flash("Success!", "success")
|
||||
return redirect(url_for("book.settings", book_id=book_id))
|
||||
|
||||
new_access_group = None
|
||||
match contributor.role:
|
||||
case m.BookContributor.Roles.EDITOR:
|
||||
new_access_group = create_editor_group(book.id)
|
||||
case m.BookContributor.Roles.MODERATOR:
|
||||
new_access_group = create_moderator_group(book.id)
|
||||
case _:
|
||||
log(
|
||||
log.CRITICAL,
|
||||
"Unknown contributor's [%s] role: [%s]",
|
||||
contributor,
|
||||
contributor.role,
|
||||
)
|
||||
flash("Unknown contributor's role", "danger")
|
||||
return redirect(url_for("book.settings", book_id=book_id))
|
||||
m.UserAccessGroups(user_id=user.id, access_group_id=new_access_group.id).save(
|
||||
False
|
||||
)
|
||||
|
||||
collection_ids = permissions_json.get("collection", [])
|
||||
for collection_id in collection_ids:
|
||||
m.CollectionAccessGroups(
|
||||
collection_id=collection_id, access_group_id=new_access_group.id
|
||||
).save(False)
|
||||
|
||||
section_ids = permissions_json.get("section", [])
|
||||
for section_id in section_ids:
|
||||
m.SectionAccessGroups(
|
||||
section_id=section_id, access_group_id=new_access_group.id
|
||||
).save(False)
|
||||
|
||||
db.session.commit()
|
||||
flash("Success!", "success")
|
||||
return redirect(url_for("book.settings", book_id=book_id))
|
||||
response = set_access_level(form, book)
|
||||
return response
|
||||
|
||||
log(log.ERROR, "Errors edit contributor access level: [%s]", form.errors)
|
||||
for field, errors in form.errors.items():
|
||||
|
|
|
@ -998,7 +998,7 @@ multidict = ">=4.0"
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "98f397700dc3b143f24e0b942e8d0ff5b50a6a1a8baacf69472a78856071dff8"
|
||||
content-hash = "bfd495cb50e26c429a12b53d258d2df31dc074a154445eb4f94f48e8b136252f"
|
||||
|
||||
[metadata.files]
|
||||
abnf = [
|
||||
|
|
|
@ -8,7 +8,6 @@ authors = ["denys <denysburimov@gmail.com>"]
|
|||
python = "^3.11"
|
||||
flask = "^2.2.3"
|
||||
flask-migrate = "^4.0.4"
|
||||
flask-wtf = "^1.1.1"
|
||||
flask-mail = "^0.9.1"
|
||||
flask-login = "^0.6.2"
|
||||
python-dotenv = "^1.0.0"
|
||||
|
@ -18,6 +17,8 @@ psycopg2-binary = "^2.9.5"
|
|||
pydantic = "^1.10.7"
|
||||
siwe = "^2.2.0"
|
||||
flask-admin = "^1.6.1"
|
||||
wtforms = "^3.0.1"
|
||||
flask-wtf = "^1.1.1"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^7.1.1"
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[pytest]
|
||||
filterwarnings =
|
||||
ignore::DeprecationWarning
|
|
@ -32,4 +32,17 @@ export function initRefreshAccessLevelTree() {
|
|||
refreshAccessLevelTree(userId, bookId);
|
||||
});
|
||||
});
|
||||
|
||||
const trigger = document.querySelector('.trigger-refreshAccessLevelTree');
|
||||
if (trigger) {
|
||||
const userIdInput: HTMLInputElement = document.querySelector(
|
||||
'input[name=user_id]',
|
||||
);
|
||||
const bookIdInput: HTMLInputElement = document.querySelector(
|
||||
'input[name=book_id]',
|
||||
);
|
||||
const userId = userIdInput.value;
|
||||
const bookId = bookIdInput.value;
|
||||
refreshAccessLevelTree(userId, bookId);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue