mirror of https://github.com/logos-co/open-law.git
Merge branch 'develop' into svyat/feat/code_optimization
This commit is contained in:
commit
8711a8cfa0
|
@ -8,6 +8,7 @@ from app.logger import log
|
||||||
|
|
||||||
class BaseBookForm(FlaskForm):
|
class BaseBookForm(FlaskForm):
|
||||||
label = StringField("Label", [DataRequired(), Length(6, 256)])
|
label = StringField("Label", [DataRequired(), Length(6, 256)])
|
||||||
|
about = StringField("About")
|
||||||
|
|
||||||
|
|
||||||
class CreateBookForm(BaseBookForm):
|
class CreateBookForm(BaseBookForm):
|
||||||
|
@ -22,10 +23,14 @@ class EditBookForm(BaseBookForm):
|
||||||
label = field.data
|
label = field.data
|
||||||
book_id = self.book_id.data
|
book_id = self.book_id.data
|
||||||
|
|
||||||
existing_book: m.Book = m.Book.query.filter_by(
|
existing_book: m.Book = (
|
||||||
is_deleted=False,
|
m.Book.query.filter_by(
|
||||||
label=label,
|
is_deleted=False,
|
||||||
).first()
|
label=label,
|
||||||
|
)
|
||||||
|
.filter(m.Book.id != book_id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
if existing_book:
|
if existing_book:
|
||||||
log(
|
log(
|
||||||
log.WARNING, "Book with label [%s] already exists: [%s]", label, book_id
|
log.WARNING, "Book with label [%s] already exists: [%s]", label, book_id
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -19,9 +19,9 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="overflow-x-auto shadow-md sm:rounded-lg md:mr-64">
|
<div class="overflow-x-auto shadow-md sm:rounded-lg md:mr-64">
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
<div class="fixed z-40 w-full md:w-4/5 bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
|
<div class="fixed z-40 w-full bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
<h1 class="font-extrabold text-lg dark:text-white ml-4 mt-5"> {{book.label}} </h1>
|
<h1 class="font-extrabold text-lg dark:text-white ml-4"> {{book.label}} </h1>
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
<div class="mb-1">
|
<div class="mb-1">
|
||||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center" id="myTab" data-tabs-toggle="#myTabContent" role="tablist">
|
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center" id="myTab" data-tabs-toggle="#myTabContent" role="tablist">
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!-- prettier-ignore-->
|
||||||
|
<div id="delete_book_modal" tabindex="-1" aria-hidden="true" class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
||||||
|
<div class="relative w-full max-w-2xl max-h-full">
|
||||||
|
<!-- Modal content -->
|
||||||
|
<form action="{{ url_for('book.delete',book_id=book.id) }}" method="post" class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
||||||
|
{{ form_hidden_tag() }}
|
||||||
|
<!-- Modal header -->
|
||||||
|
<div class="flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600">
|
||||||
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">Delete book</h3>
|
||||||
|
<button id="modalAddCloseButton" data-modal-hide="delete_book_modal" type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"> <svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path> </svg> </button>
|
||||||
|
</div>
|
||||||
|
<!-- Modal body -->
|
||||||
|
|
||||||
|
<!-- Modal footer -->
|
||||||
|
<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-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-800">Confirm Deletion</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -20,7 +20,7 @@
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
{% for book in books if not book.is_deleted%}
|
{% for book in books if not book.is_deleted%}
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
<dl class=" bg-white dark:bg-gray-900 max-w-full p-5 text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 m-3 border-2 border-gray-200 border-solid rounded-lg dark:border-gray-700">
|
<dl class=" bg-white dark:bg-gray-900 h-max w-full p-5 text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 m-3 border-2 border-gray-200 border-solid rounded-lg dark:border-gray-700">
|
||||||
<dt class="mb-2"><a class="flex flex-col pb-4" href="{{url_for('book.collection_view',book_id=book.id)}}">{{book.owner.username}}/{{book.label}}</a></dt>
|
<dt class="mb-2"><a class="flex flex-col pb-4" href="{{url_for('book.collection_view',book_id=book.id)}}">{{book.owner.username}}/{{book.label}}</a></dt>
|
||||||
<dd class="flex flex-col md:flex-row text-lg font-semibold text-gray-500 md:text-lg dark:text-gray-400">
|
<dd class="flex flex-col md:flex-row text-lg font-semibold text-gray-500 md:text-lg dark:text-gray-400">
|
||||||
{% if book.versions %}
|
{% if book.versions %}
|
||||||
|
|
|
@ -1,91 +1,107 @@
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% include 'book/add_contributor_modal.html' %}
|
{% include 'book/add_contributor_modal.html' %}
|
||||||
|
{% include 'book/delete_book_modal.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Hide right_sidebar -->
|
<!-- Hide right_sidebar -->
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
{% block right_sidebar %} {% endblock %}
|
{% block right_sidebar %} {% endblock %}
|
||||||
|
|
||||||
<div class="p-5">
|
<div class="p-3 pl-4">
|
||||||
<div class="flex justify-between ml-4 mb-3">
|
<div class="flex justify-between ml-4 mb-3">
|
||||||
<h1 class="text-2xl font-extrabold dark:text-white">Settings</h1>
|
<h1 class="text-2xl font-extrabold dark:text-white">{{book.label}}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-1">
|
||||||
<form action="{{ url_for('book.edit', book_id=book.id) }}" method="post" class="mb-0 flex flex-col space-y-2 w-1/2">
|
|
||||||
{{ form_hidden_tag() }}
|
|
||||||
<input value="{{book.id}}" type="text" name="book_id" id="book_id" class="hidden" placeholder="Book id" required>
|
|
||||||
<div>
|
|
||||||
<label for="label" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Label</label>
|
|
||||||
<input value="{{book.label}}" type="text" name="label" id="label" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="My Book" required>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button type="submit" class="text-center 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-4 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Save</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="p-5">
|
|
||||||
<div class="flex justify-between ml-4 mb-2">
|
|
||||||
<h1 class="text-2xl font-extrabold dark:text-white">Contributors</h1>
|
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
<button
|
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center" id="myTab" data-tabs-toggle="#myTabContent" role="tablist">
|
||||||
type="button" data-modal-target="add-contributor-modal" data-modal-toggle="add-contributor-modal"
|
<li class="mr-2" role="presentation">
|
||||||
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-4 py-2.5 text-center inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
|
<!-- prettier-ignore -->
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg>
|
<button class="flex items-center space-x-2 p-4 border-b-2 rounded-t-lg" id="settings-tab" data-tabs-target="#settings" type="button" role="tab" aria-controls="settings" aria-selected="false">
|
||||||
Add
|
<!-- prettier-ignore -->
|
||||||
</button>
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> </svg>
|
||||||
|
<span>Book settings</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="mr-2" role="presentation">
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
<button class="flex items-center space-x-2 p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300" id="permissions-tab" data-tabs-target="#permissions" type="button" role="tab" aria-controls="permissions" aria-selected="false">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M6 13.5V3.75m0 9.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 3.75V16.5m12-3V3.75m0 9.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 3.75V16.5m-6-9V3.75m0 3.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 9.75V10.5" /> </svg>
|
||||||
|
<span>User permissions</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="myTabContent">
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
<div class="hidden pl-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="settings" role="tabpanel" aria-labelledby="settings-tab">
|
||||||
|
<form action="{{ url_for('book.edit', book_id=book.id) }}" method="post" class="mb-0 flex flex-col space-y-2 w-1/2">
|
||||||
|
{{ form_hidden_tag() }}
|
||||||
|
<input value="{{book.id}}" type="text" name="book_id" id="book_id" class="hidden" placeholder="Book id" required>
|
||||||
|
<div>
|
||||||
|
<label for="label" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Label</label>
|
||||||
|
<input value="{{book.label}}" type="text" name="label" id="label" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="My Book" required>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="about" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Book about</label>
|
||||||
|
<textarea type="text" name="about" id="about" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="About my Book">{{book.about if not book.about==None}}</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
|
<div>
|
||||||
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
<button type="submit" class="text-center 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-4 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Submit</button>
|
||||||
<thead
|
</div>
|
||||||
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
</form>
|
||||||
<tr>
|
<button type="button" data-modal-target="delete_book_modal" data-modal-toggle="delete_book_modal" class="mt-3 text-red-700 hover:text-white border border-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:border-red-500 dark:text-red-500 dark:hover:text-white dark:hover:bg-red-600 dark:focus:ring-red-900">Delete Book</button>
|
||||||
<th scope="col" class="px-6 py-3">Username</th>
|
</div>
|
||||||
<th scope="col" class="px-6 py-3">Role</th>
|
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="permissions" role="tabpanel" aria-labelledby="permissions-tab">
|
||||||
<th scope="col" class="px-6 py-3">Action</th>
|
<div class="p-5">
|
||||||
</tr>
|
<div class="flex justify-between ml-4 mb-2">
|
||||||
</thead>
|
<h1 class="text-2xl font-extrabold dark:text-white">Contributors</h1>
|
||||||
<tbody>
|
<!-- prettier-ignore -->
|
||||||
{% for contributor in book.contributors %}
|
<button type="button" data-modal-target="add-contributor-modal" data-modal-toggle="add-contributor-modal" 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-4 py-2.5 text-center inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke"currentColor" class="w-6 h-6 mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg>
|
||||||
<tr class="bg-white border-b dark:bg-gray-900 dark:border-gray-700">
|
Add
|
||||||
<td class="px-6 py-4">{{ contributor.user.username }}</td>
|
</button>
|
||||||
<td class="px-6 py-4">
|
</div>
|
||||||
<form action="{{ url_for('book.edit_contributor_role', book_id=book.id) }}" method="post" class="mb-0 flex space-x-2">
|
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
|
||||||
{{ form_hidden_tag() }}
|
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
||||||
<input type="hidden" name="user_id" id="user_id" value="{{ contributor.user_id }}" />
|
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||||
<select
|
<tr> <th scope="col" class="px-6 py-3">Username</th> <th scope="col" class="px-6 py-3">Role</th> <th scope="col" class="px-6 py-3">Action</th> </tr>
|
||||||
id="role"
|
</thead>
|
||||||
name="role"
|
<tbody>
|
||||||
data-user-id="{{ contributor.user.id }}"
|
{% for contributor in book.contributors %}
|
||||||
class="contributor-role-select block w-1/2 py-1.5 px-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
<tr class="bg-white border-b dark:bg-gray-900 dark:border-gray-700">
|
||||||
>
|
<td class="px-6 py-4">{{ contributor.user.username }}</td>
|
||||||
|
<td class="px-6 py-4">
|
||||||
{% for role in roles if role.value %}
|
<form action="{{ url_for('book.edit_contributor_role', book_id=book.id) }}" method="post" class="mb-0 flex space-x-2">
|
||||||
<option
|
{{ form_hidden_tag() }}
|
||||||
{% if contributor.role == role %} selected {% endif %}
|
<input type="hidden" name="user_id" id="user_id" value="{{ contributor.user_id }}" />
|
||||||
value="{{ role.value }}">
|
<select id="role" name="role" data-user-id="{{ contributor.user.id }}" class="contributor-role-select block w-1/2 py-1.5 px-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" >
|
||||||
{{ role.name.title() }}
|
{% for role in roles if role.value %}
|
||||||
</option>
|
<option
|
||||||
{% endfor %}
|
{% if contributor.role == role %} selected {% endif %}
|
||||||
</select>
|
value="{{ role.value }}">
|
||||||
<button type="submit" class="text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 font-medium rounded-lg text-sm px-5 py-1 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700">Save</button>
|
{{ role.name.title() }}
|
||||||
</form>
|
</option>
|
||||||
</td>
|
{% endfor %}
|
||||||
<td class="px-6 py-4">
|
</select>
|
||||||
<!-- prettier-ignore -->
|
<button type="submit" class="text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 font-medium rounded-lg text-sm px-5 py-1 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700">Save</button>
|
||||||
<form action="{{ url_for('book.delete_contributor', book_id=book.id) }}" method="post" class="mb-0">
|
</form>
|
||||||
{{ form_hidden_tag() }}
|
</td>
|
||||||
<input type="hidden" name="user_id" id="user_id" value="{{ contributor.user_id }}" />
|
<td class="px-6 py-4">
|
||||||
|
<!-- prettier-ignore -->
|
||||||
<button type="submit" class="text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-sm rounded-lg text-sm px-5 py-1.5 dark:bg-red-600 dark:hover:bg-red-700 focus:outline-none dark:focus:ring-red-800">Delete</button>
|
<form action="{{ url_for('book.delete_contributor', book_id=book.id) }}" method="post" class="mb-0">
|
||||||
</form>
|
{{ form_hidden_tag() }}
|
||||||
</td>
|
<input type="hidden" name="user_id" id="user_id" value="{{ contributor.user_id }}" />
|
||||||
</tr>
|
<button type="submit" class="text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-sm rounded-lg text-sm px-5 py-1.5 dark:bg-red-600 dark:hover:bg-red-700 focus:outline-none dark:focus:ring-red-800">Delete</button>
|
||||||
{% endfor %}
|
</form>
|
||||||
</tbody>
|
</td>
|
||||||
</table>
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -124,21 +124,21 @@
|
||||||
<a
|
<a
|
||||||
href="{{ url_for('book.my_library') }}"
|
href="{{ url_for('book.my_library') }}"
|
||||||
class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
<span>My Library</span>
|
<span class="text-center md:text-left">My Library</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="my-auto">
|
<li class="my-auto">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
<span>My Contributions</span>
|
<span class="text-center md:text-left">My Contributions</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
<span>Favorite Books</span>
|
<span class="text-center md:text-left">Favorite Books</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -2,6 +2,13 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
|
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
<h1 class="hidden md:inline font-extrabold text-lg dark:text-white ml-4 mt-5">Open Common Law</h1>
|
||||||
|
<p
|
||||||
|
class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400">
|
||||||
|
An open-source law hosting platform that allows online communities to easily
|
||||||
|
create, collaborate, and publish their own body of law.
|
||||||
|
</p>
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
<ul class="flex md:flex-wrap -mb-px text-xs md:text-sm font-medium text-center" id="myTab" data-tabs-toggle="#myTabContent" role="tablist">
|
<ul class="flex md:flex-wrap -mb-px text-xs md:text-sm font-medium text-center" id="myTab" data-tabs-toggle="#myTabContent" role="tablist">
|
||||||
<li class="mr-2 w-full md:w-auto" role="presentation">
|
<li class="mr-2 w-full md:w-auto" role="presentation">
|
||||||
|
|
|
@ -99,8 +99,10 @@ def edit(book_id: int):
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
book: m.Book = db.session.get(m.Book, book_id)
|
book: m.Book = db.session.get(m.Book, book_id)
|
||||||
label = form.label.data
|
label = form.label.data
|
||||||
|
about = form.about.data
|
||||||
|
|
||||||
book.label = label
|
book.label = label
|
||||||
|
book.about = about
|
||||||
log(log.INFO, "Update Book: [%s]", book)
|
log(log.INFO, "Update Book: [%s]", book)
|
||||||
book.save()
|
book.save()
|
||||||
flash("Success!", "success")
|
flash("Success!", "success")
|
||||||
|
@ -114,6 +116,23 @@ def edit(book_id: int):
|
||||||
return redirect(url_for("book.settings", book_id=book_id))
|
return redirect(url_for("book.settings", book_id=book_id))
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/<int:book_id>/delete", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def delete(book_id: int):
|
||||||
|
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"))
|
||||||
|
|
||||||
|
book.is_deleted = True
|
||||||
|
log(log.INFO, "Book deleted: [%s]", book)
|
||||||
|
book.save()
|
||||||
|
flash("Success!", "success")
|
||||||
|
return redirect(url_for("book.my_library"))
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/<int:book_id>/collections", methods=["GET"])
|
@bp.route("/<int:book_id>/collections", methods=["GET"])
|
||||||
def collection_view(book_id: int):
|
def collection_view(book_id: int):
|
||||||
book = db.session.get(m.Book, book_id)
|
book = db.session.get(m.Book, book_id)
|
||||||
|
|
|
@ -6,7 +6,7 @@ from app import models as m, db
|
||||||
from tests.utils import login, logout
|
from tests.utils import login, logout
|
||||||
|
|
||||||
|
|
||||||
def test_create_edit_book(client: FlaskClient):
|
def test_create_edit_delete_book(client: FlaskClient):
|
||||||
login(client)
|
login(client)
|
||||||
|
|
||||||
BOOK_NAME = "Test Book"
|
BOOK_NAME = "Test Book"
|
||||||
|
@ -74,18 +74,6 @@ def test_create_edit_book(client: FlaskClient):
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b"You are not owner of this book!" in response.data
|
assert b"You are not owner of this book!" in response.data
|
||||||
|
|
||||||
response: Response = client.post(
|
|
||||||
f"/book/{book.id}/edit",
|
|
||||||
data=dict(
|
|
||||||
book_id=book.id,
|
|
||||||
label=BOOK_NAME,
|
|
||||||
),
|
|
||||||
follow_redirects=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert b"Book label must be unique!" in response.data
|
|
||||||
|
|
||||||
response: Response = client.post(
|
response: Response = client.post(
|
||||||
f"/book/{book.id}/edit",
|
f"/book/{book.id}/edit",
|
||||||
data=dict(
|
data=dict(
|
||||||
|
@ -100,6 +88,19 @@ def test_create_edit_book(client: FlaskClient):
|
||||||
book = db.session.get(m.Book, book.id)
|
book = db.session.get(m.Book, book.id)
|
||||||
assert book.label != BOOK_NAME
|
assert book.label != BOOK_NAME
|
||||||
|
|
||||||
|
response: Response = client.post(
|
||||||
|
f"/book/{book.id}/delete",
|
||||||
|
data=dict(
|
||||||
|
book_id=book.id,
|
||||||
|
),
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert b"Success!" in response.data
|
||||||
|
book = db.session.get(m.Book, book.id)
|
||||||
|
assert book.is_deleted == True
|
||||||
|
|
||||||
|
|
||||||
def test_add_contributor(client: FlaskClient):
|
def test_add_contributor(client: FlaskClient):
|
||||||
_, user = login(client)
|
_, user = login(client)
|
||||||
|
|
Loading…
Reference in New Issue