Merge branch 'develop' into svyat/feat/ordering

This commit is contained in:
SvyatoslavArtymovych 2023-06-02 16:03:51 +03:00
commit 82db550d23
20 changed files with 81 additions and 145042 deletions

View File

@ -46,7 +46,7 @@ def book_validator() -> Response | None:
return redirect(url_for("book.my_library")) return redirect(url_for("book.my_library"))
collection_id = request_args.get("collection_id") collection_id = request_args.get("collection_id")
if collection_id: if collection_id is not None:
collection: m.Collection = db.session.get(m.Collection, collection_id) collection: m.Collection = db.session.get(m.Collection, collection_id)
if not collection or collection.is_deleted: if not collection or collection.is_deleted:
log(log.WARNING, "Collection with id [%s] not found", collection_id) log(log.WARNING, "Collection with id [%s] not found", collection_id)
@ -54,7 +54,7 @@ def book_validator() -> Response | None:
return redirect(url_for("book.collection_view", book_id=book_id)) return redirect(url_for("book.collection_view", book_id=book_id))
sub_collection_id = request_args.get("sub_collection_id") sub_collection_id = request_args.get("sub_collection_id")
if sub_collection_id: if sub_collection_id is not None:
sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id) sub_collection: m.Collection = db.session.get(m.Collection, sub_collection_id)
if not sub_collection or sub_collection.is_deleted: if not sub_collection or sub_collection.is_deleted:
log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id) log(log.WARNING, "Sub_collection with id [%s] not found", sub_collection_id)
@ -66,7 +66,7 @@ def book_validator() -> Response | None:
) )
section_id = request_args.get("section_id") section_id = request_args.get("section_id")
if section_id: if section_id is not None:
section: m.Section = db.session.get(m.Section, section_id) section: m.Section = db.session.get(m.Section, section_id)
if not section: if not section:
log(log.WARNING, "Section with id [%s] not found", section) log(log.WARNING, "Section with id [%s] not found", section)
@ -74,7 +74,7 @@ def book_validator() -> Response | None:
return redirect(url_for("book.collection_view", book_id=book_id)) return redirect(url_for("book.collection_view", book_id=book_id))
interpretation_id = request_args.get("interpretation_id") interpretation_id = request_args.get("interpretation_id")
if interpretation_id: if interpretation_id is not None:
interpretation: m.Interpretation = db.session.get( interpretation: m.Interpretation = db.session.get(
m.Interpretation, interpretation_id m.Interpretation, interpretation_id
) )
@ -88,7 +88,7 @@ def book_validator() -> Response | None:
) )
comment_id = request_args.get("comment_id") comment_id = request_args.get("comment_id")
if comment_id: if comment_id is not None:
comment: m.Comment = db.session.get(m.Comment, comment_id) comment: m.Comment = db.session.get(m.Comment, comment_id)
if not comment or comment.is_deleted: if not comment or comment.is_deleted:
log(log.WARNING, "Comment with id [%s] not found", comment_id) log(log.WARNING, "Comment with id [%s] not found", comment_id)

View File

@ -42,6 +42,9 @@ def check_permissions(
if type(entity) == m.Comment: if type(entity) == m.Comment:
log(log.INFO, "Entity is Comment. Replace it by entity.interpretation") log(log.INFO, "Entity is Comment. Replace it by entity.interpretation")
entity = entity.interpretation entity = entity.interpretation
elif type(entity) == m.Interpretation and entity.user_id == current_user.id:
log(log.INFO, "User [%s] is interpretation creator [%s]", current_user, entity)
return None
if not entity or not entity.access_groups: if not entity or not entity.access_groups:
log( log(

BIN
app/static/img/logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

View File

@ -15,7 +15,8 @@
<link <link
rel="icon" rel="icon"
type="image/x-icon" type="image/x-icon"
href="{{ url_for('static', filename='img/logo.svg') }}" /> href="{{ url_for('static', filename='img/logo.ico') }}"
/>
<!-- styles --> <!-- styles -->
<!-- prettier-ignore --> <!-- prettier-ignore -->
@ -103,7 +104,7 @@
<!-- DO NOT REMOVE THIS! --> <!-- DO NOT REMOVE THIS! -->
<!-- Adding tailwind classes that are not in html, but will be added by jinja --> <!-- Adding tailwind classes that are not in html, but will be added by jinja -->
<span class="hid"> <span class="hidden">
<span class="border-sky-300 bg-sky-100 !dark:text-black hidden text-orange-500 !no-underline !dark:bg-blue-600 !dark:hover:bg-blue-700 !dark:text-white"></span> <span class="border-sky-300 bg-sky-100 !dark:text-black hidden text-orange-500 !no-underline !dark:bg-blue-600 !dark:hover:bg-blue-700 !dark:text-white"></span>
</span> </span>
</body> </body>

View File

@ -97,6 +97,3 @@
{% include 'book/modals/add_book_modal.html' %} {% include 'book/modals/add_book_modal.html' %}
<!-- prettier-ignore --> <!-- prettier-ignore -->
{% endblock %} {% endblock %}
<!-- prettier-ignore -->
{% block scripts %}
{% endblock %}

View File

@ -3,19 +3,20 @@
{% block right_sidebar %} {% block right_sidebar %}
{% endblock %} {% endblock %}
{% if current_user.is_authenticated %}
{% include 'book/modals/add_collection_modal.html' %}
{% include 'book/modals/delete_collection_modal.html' %}
{% include 'book/modals/add_sub_collection_modal.html' %}
{% include 'book/modals/delete_sub_collection_modal.html' %}
{% include 'book/modals/add_section_modal.html' %}
{% include 'book/modals/delete_section_modal.html' %}
{% endif %}
{% block title %}{{book.label[:32]}}{% endblock %} {% block title %}{{book.label[:32]}}{% endblock %}
{% block content %} {% block content %}
{% if current_user.is_authenticated %}
{% include 'book/modals/add_collection_modal.html' %}
{% include 'book/modals/delete_collection_modal.html' %}
{% include 'book/modals/add_sub_collection_modal.html' %}
{% include 'book/modals/delete_sub_collection_modal.html' %}
{% include 'book/modals/add_section_modal.html' %}
{% include 'book/modals/delete_section_modal.html' %}
{% endif %}
<div class="flex overflow-hidden"> <div class="flex overflow-hidden">
<div <div
id="accordion-collapse" id="accordion-collapse"

View File

@ -1,17 +1,19 @@
<!-- prettier-ignore --> <!-- prettier-ignore -->
{% extends 'base.html' %} {% extends 'base.html' %}
{% if current_user.is_authenticated %}
{% include 'book/modals/approve_interpretation_modal.html' %}
{% include 'book/modals/edit_interpretation_modal.html' %}
{% include 'book/modals/delete_interpretation_modal.html' %}
{% block right_sidebar %}
{% endblock %}
{% endif %}
{% block title %}{{section.label}}{% endblock %} {% block title %}{{section.label}}{% endblock %}
{% block content %} {% block content %}
{% if current_user.is_authenticated %}
{% include 'book/modals/approve_interpretation_modal.html' %}
{% include 'book/modals/edit_interpretation_modal.html' %}
{% include 'book/modals/delete_interpretation_modal.html' %}
{% block right_sidebar %}
{% endblock %}
{% endif %}
{% include 'book/breadcrumbs_navigation.html'%} {% include 'book/breadcrumbs_navigation.html'%}
<div class="overflow-x-auto shadow-md sm:rounded-lg"> <div class="overflow-x-auto shadow-md sm:rounded-lg">
<!-- prettier-ignore --> <!-- prettier-ignore -->
@ -146,7 +148,7 @@
</div> </div>
{% endif %} {% endif %}
{% if interpretation.book.owner == current_user or access_to_delete_interpretation %} {% if interpretation.book.owner == current_user or interpretation.user_id == current_user.id or access_to_delete_interpretation %}
<div class="relative mt-1"> <div class="relative mt-1">
<button id="callDeleteInterpretationModal" data-popover-target="popover-delete" data-interpretation-id="{{interpretation.id}}" type="button" data-modal-target="delete_interpretation_modal" data-modal-toggle="delete_interpretation_modal" class="space-x-0.5 flex items-center"> <button id="callDeleteInterpretationModal" data-popover-target="popover-delete" data-interpretation-id="{{interpretation.id}}" type="button" data-modal-target="delete_interpretation_modal" data-modal-toggle="delete_interpretation_modal" class="space-x-0.5 flex items-center">
<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="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" /> </svg> <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="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" /> </svg>

View File

@ -39,7 +39,6 @@
<div> <div>
<label for="role" class="mb-2 block text-sm font-medium text-gray-900 dark:text-white">Select an option</label > <label for="role" class="mb-2 block text-sm font-medium text-gray-900 dark:text-white">Select an option</label >
<select id="role" name="role" class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"> <select id="role" name="role" class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option selected> Select a role </option>
{% for role in roles if role.value %} {% for role in roles if role.value %}
<option {% if role == 1 %} selected {% endif %} value="{{ role.value }}">{{ role.name.title() }}</option> <option {% if role == 1 %} selected {% endif %} value="{{ role.value }}">{{ role.name.title() }}</option>
{% endfor %} {% endfor %}

View File

@ -23,14 +23,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="p-6 pt-0 space-y-6">
<div class="grid gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="about-collection" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" >About</label >
<textarea name="about" id="about-collection" rows="4" class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="About collection..."></textarea>
</div>
</div>
</div>
<!-- Modal footer --> <!-- Modal footer -->
<div class="flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600"> <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">Create</button> <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">Create</button>

View File

@ -5,7 +5,8 @@
<form <form
id="delete_section_modal_form" id="delete_section_modal_form"
action="{{ url_for('book.section_delete', book_id=book.id, section_id=0) }}" action="{{ url_for('book.section_delete', book_id=book.id, section_id=0) }}"
method="post" class="relative bg-white rounded-lg shadow dark:bg-gray-700"> method="post" class="relative bg-white rounded-lg shadow dark:bg-gray-700"
>
{{ form_hidden_tag() }} {{ form_hidden_tag() }}
<input type="hidden" name="section_id" id="delete_section_modal_section_id" value="" /> <input type="hidden" name="section_id" id="delete_section_modal_section_id" value="" />

View File

@ -1,18 +1,17 @@
<!-- prettier-ignore --> <!-- prettier-ignore -->
{% extends 'base.html' %} {% extends 'base.html' %}
{% if current_user.is_authenticated %}
{% include 'book/modals/delete_comment_modal.html' %}
{% include 'book/modals/edit_comment_modal.html' %}
{% block right_sidebar %}
{% endblock %}
{% endif %}
{% block title %}{{ section.label[:32] }}{% endblock %} {% block title %}{{ section.label[:32] }}{% endblock %}
{% block content %} {% block content %}
{% if current_user.is_authenticated %}
{% include 'book/modals/delete_comment_modal.html' %}
{% include 'book/modals/edit_comment_modal.html' %}
{% block right_sidebar %}
{% endblock %}
{% endif %}
{% include 'book/breadcrumbs_navigation.html'%} {% include 'book/breadcrumbs_navigation.html'%}
<div class="shadow-md mt-5 h-auto overflow-x-hidden"> <div class="shadow-md mt-5 h-auto overflow-x-hidden">
<div class="ql-snow mt-20"> <div class="ql-snow mt-20">

View File

@ -1,13 +1,15 @@
<!-- prettier-ignore --> <!-- prettier-ignore -->
{% extends 'base.html' %} {% extends 'base.html' %}
{% include 'book/modals/access_level_modal.html' %}
{% include 'book/modals/add_contributor_modal.html' %}
{% include 'book/modals/delete_book_modal.html' %}
{% block title %}Book Settings{% endblock %} {% block title %}Book Settings{% endblock %}
{% block content %} {% block content %}
{% include 'book/modals/access_level_modal.html' %}
{% include 'book/modals/add_contributor_modal.html' %}
{% include 'book/modals/delete_book_modal.html' %}
<!-- Hide right_sidebar --> <!-- Hide right_sidebar -->
<!-- prettier-ignore --> <!-- prettier-ignore -->
{% block right_sidebar %} {% endblock %} {% block right_sidebar %} {% endblock %}
@ -76,7 +78,9 @@
<div class="flex justify-between"> <div class="flex justify-between">
<button type="submit" class="mr-2 mb-2 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> <button type="submit" class="mr-2 mb-2 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>
<button type="button" data-modal-target="delete_book_modal" data-modal-toggle="delete_book_modal" class="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> {% if book.user_id == current_user %}
<button type="button" data-modal-target="delete_book_modal" data-modal-toggle="delete_book_modal" class="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>
{% endif %}
</div> </div>
</form> </form>
</div> </div>

View File

@ -1,10 +1,13 @@
<!-- prettier-ignore --> <!-- prettier-ignore -->
{% extends 'base.html' %} {% extends 'base.html' %}
{% include 'user/delete_profile_modal.html' %}
{% block title %}Edit Profile{% endblock %} {% block title %}Edit Profile{% endblock %}
{% block content %} {% block content %}
{% include 'user/delete_profile_modal.html' %}
<!-- component --> <!-- component -->
<section> <section>
<div class="w-full lg:w-4/12 px-4 mx-auto pt-6"> <div class="w-full lg:w-4/12 px-4 mx-auto pt-6">

View File

@ -1,10 +1,12 @@
<!-- prettier-ignore --> <!-- prettier-ignore -->
{% extends 'base.html' %} {% extends 'base.html' %}
{% include 'user/delete_profile_modal.html' %}
{% block title %}Reactivate Profile{% endblock %} {% block title %}Reactivate Profile{% endblock %}
{% block content %} {% block content %}
{% include 'user/delete_profile_modal.html' %}
<!-- component --> <!-- component -->
<section> <section>
<div class="w-full lg:w-4/12 px-4 mx-auto pt-6"> <div class="w-full lg:w-4/12 px-4 mx-auto pt-6">

View File

@ -1,9 +1,4 @@
from flask import ( from flask import render_template, flash, redirect, url_for, request
render_template,
flash,
redirect,
url_for,
)
from flask_login import login_required, current_user from flask_login import login_required, current_user
from sqlalchemy import and_, or_ from sqlalchemy import and_, or_
@ -180,11 +175,12 @@ def delete(book_id: int):
@bp.route("/<int:book_id>/statistics", methods=["GET"]) @bp.route("/<int:book_id>/statistics", methods=["GET"])
def statistic_view(book_id: int): def statistic_view(book_id: int):
book = db.session.get(m.Book, book_id) book = db.session.get(m.Book, book_id)
active_tab = request.args.get("active_tab")
if not book or book.is_deleted: if not book or book.is_deleted:
log(log.WARNING, "Book with id [%s] not found", book_id) log(log.WARNING, "Book with id [%s] not found", book_id)
flash("Book not found", "danger") flash("Book not found", "danger")
return redirect(url_for("book.my_library")) return redirect(url_for("book.my_library"))
return render_template("book/stat.html", book=book) return render_template("book/stat.html", book=book, active_tab=active_tab)
@bp.route("/favorite_books", methods=["GET"]) @bp.route("/favorite_books", methods=["GET"])
@ -226,22 +222,31 @@ def my_contributions():
log(log.INFO, "Creating query for interpretations") log(log.INFO, "Creating query for interpretations")
interpretations = ( interpretations = (
db.session.query( db.session.query(m.Interpretation)
m.Interpretation, .join(
m.Comment, m.Comment.interpretation_id == m.Interpretation.id, full=True
)
.join(
m.InterpretationVote,
m.InterpretationVote.interpretation_id == m.Interpretation.id,
full=True,
) )
.filter( .filter(
or_( or_(
and_( and_(
m.Interpretation.id == m.Comment.interpretation_id,
m.Comment.user_id == current_user.id, m.Comment.user_id == current_user.id,
m.Comment.is_deleted.is_(False), m.Comment.is_deleted.is_(False),
m.Interpretation.is_deleted.is_(False),
), ),
and_( and_(
m.Interpretation.user_id == current_user.id, m.Interpretation.user_id == current_user.id,
m.Interpretation.is_deleted.is_(False), m.Interpretation.is_deleted.is_(False),
), ),
) and_(
m.InterpretationVote.user_id == current_user.id,
m.InterpretationVote.interpretation_id == m.Interpretation.id,
),
),
m.Interpretation.is_deleted == False, # noqa: E712
) )
.group_by(m.Interpretation.id) .group_by(m.Interpretation.id)
.order_by(m.Interpretation.created_at.desc()) .order_by(m.Interpretation.created_at.desc())

View File

@ -63,8 +63,8 @@ export function editInterpretations() {
let newActionPath: string = ''; let newActionPath: string = '';
newActionPath = defaultActionPath.replace( newActionPath = defaultActionPath.replace(
'0/interpretation_edit', '0/edit_interpretation',
`${interpretationId}/interpretation_edit`, `${interpretationId}/edit_interpretation`,
); );
editInterpretationForm.setAttribute('action', `${newActionPath}`); editInterpretationForm.setAttribute('action', `${newActionPath}`);

View File

@ -40,7 +40,7 @@ export function renameCollection() {
}, },
body: JSON.stringify({ body: JSON.stringify({
label: newLabel, label: newLabel,
csrf_token: scrfInput.value, csrf_token: scrfInput ? scrfInput.value : '',
}), }),
}); });
if (response.status == 200) { if (response.status == 200) {

View File

@ -12,7 +12,6 @@ export function renameSection() {
document.querySelectorAll(`[id^="edit-section-label-"]`); document.querySelectorAll(`[id^="edit-section-label-"]`);
const scrfInput: HTMLInputElement = const scrfInput: HTMLInputElement =
document.querySelector('#csrf_token'); document.querySelector('#csrf_token');
console.log(scrfInput.value);
const oldName = inputsForRename[index].value; const oldName = inputsForRename[index].value;
inputsForRename[index].removeAttribute('readonly'); inputsForRename[index].removeAttribute('readonly');
inputsForRename[index].value = oldName; inputsForRename[index].value = oldName;
@ -44,7 +43,7 @@ export function renameSection() {
body: JSON.stringify({ body: JSON.stringify({
label: newLabel, label: newLabel,
section_id: sectionId, section_id: sectionId,
csrf_token: scrfInput.value, csrf_token: scrfInput ? scrfInput.value : '',
}), }),
}); });
if (response.status == 200) { if (response.status == 200) {

View File

@ -44,7 +44,7 @@ export function renameSubCollection() {
}, },
body: JSON.stringify({ body: JSON.stringify({
label: newLabel, label: newLabel,
csrf_token: scrfInput.value, csrf_token: scrfInput ? scrfInput.value : '',
}), }),
}); });
if (response.status == 200) { if (response.status == 200) {