Merge branch 'develop' into svyat/fix/deleting_collections

This commit is contained in:
Kostiantyn Stoliarskyi 2023-05-17 16:39:57 +03:00
commit 206411caa6
26 changed files with 286 additions and 3238 deletions

View File

@ -1,6 +1,6 @@
# flake8: noqa F401
from .auth import LoginForm
from .user import UserForm, NewUserForm, EditUserForm
from .user import UserForm, NewUserForm, EditUserForm, ReactivateUserForm
from .book import CreateBookForm, EditBookForm
from .contributor import (
AddContributorForm,

View File

@ -68,3 +68,7 @@ class EditUserForm(FlaskForm):
.first()
):
raise ValidationError("This username is taken.")
class ReactivateUserForm(FlaskForm):
submit = SubmitField("Save")

View File

@ -1,4 +1,5 @@
from flask_login import current_user
from sqlalchemy import and_
from app import db, models as m
from app.models.utils import BaseModel
@ -17,7 +18,7 @@ class Book(BaseModel):
owner = db.relationship("User", viewonly=True)
stars = db.relationship("User", secondary="books_stars", back_populates="stars")
contributors = db.relationship("BookContributor")
versions = db.relationship("BookVersion")
versions = db.relationship("BookVersion", order_by="asc(BookVersion.id)")
def __repr__(self):
return f"<{self.id}: {self.label}>"
@ -34,3 +35,55 @@ class Book(BaseModel):
).first()
if book_star:
return True
@property
def approved_comments(self):
comments = (
db.session.query(
m.Comment,
)
.filter(
and_(
m.BookVersion.id == self.last_version.id,
m.Section.version_id == m.BookVersion.id,
m.Collection.id == m.Section.collection_id,
m.Interpretation.section_id == m.Section.id,
m.Comment.interpretation_id == m.Interpretation.id,
m.Comment.approved.is_(True),
m.Comment.is_deleted.is_(False),
m.BookVersion.is_deleted.is_(False),
m.Interpretation.is_deleted.is_(False),
m.Section.is_deleted.is_(False),
m.Collection.is_deleted.is_(False),
),
)
.order_by(m.Comment.created_at.desc())
.all()
)
return comments
@property
def approved_interpretations(self):
interpretations = (
db.session.query(
m.Interpretation,
)
.filter(
and_(
m.BookVersion.id == self.last_version.id,
m.Section.version_id == m.BookVersion.id,
m.Collection.id == m.Section.collection_id,
m.Interpretation.section_id == m.Section.id,
m.Interpretation.approved.is_(True),
m.BookVersion.is_deleted.is_(False),
m.Interpretation.is_deleted.is_(False),
m.Section.is_deleted.is_(False),
m.Collection.is_deleted.is_(False),
),
)
.order_by(m.Interpretation.created_at.desc())
.all()
)
return interpretations

View File

@ -1,5 +1,3 @@
from datetime import datetime
from flask_login import current_user
from app import db, models as m
@ -13,7 +11,6 @@ class Interpretation(BaseModel):
text = db.Column(db.Text, unique=False, nullable=False)
approved = db.Column(db.Boolean, default=False)
marked = db.Column(db.Boolean, default=False)
created_at = db.Column(db.DateTime, default=datetime.now)
# Foreign keys
user_id = db.Column(db.ForeignKey("users.id"))

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -74,9 +74,10 @@
{% block body %}
{% if current_user.is_authenticated %}
{% block right_sidebar %} {% include 'right_sidebar.html' %} {% endblock %}
{% include 'book/add_book_modal.html' %}
{% endif %}
<!-- prettier-ignore -->
<script src="{{ url_for('static', filename='js/main.js') }}" type="text/javascript" defer></script>

View File

@ -25,11 +25,11 @@
</span>
<span class="space-x-0.5 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
<p>55</p>
<p>{{ book.approved_interpretations|length }}</p>
</span>
<span class="space-x-0.5 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> </svg>
<p>55</p>
<p>{{ book.approved_comments|length }}</p>
</span>
</div>
</dd>

View File

@ -36,11 +36,11 @@
</span>
<span class="space-x-0.5 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
<p>55</p>
<p>{{ book.approved_interpretations|length }}</p>
</span>
<span class="space-x-0.5 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> </svg>
<p>55</p>
<p>{{ book.approved_comments|length }}</p>
</span>
</div>
</dd>

View File

@ -1,16 +1,18 @@
<!-- prettier-ignore -->
{% extends 'base.html' %}
{% if current_user.is_authenticated %}
{% include 'book/delete_section_modal.html' %}
{% include 'book/edit_section_modal.html' %}
<!-- show delete section btn on rightside bar -->
{% set show_delete_section = True %} {% set show_edit_section = True %}
<!-- prettier-ignore -->
{% set show_delete_section = True %}
<!-- prettier-ignore -->
{% set show_edit_section = True %}
{% block right_sidebar %}
{% include 'book/right_sidebar.html' %}
{% endblock %}
{% endif %}
{% block content %}
{% include 'book/breadcrumbs_navigation.html'%}
@ -18,17 +20,10 @@
<!-- prettier-ignore -->
<div class="fixed z-30 w-full top-44 pt-6 bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
<!-- prettier-ignore -->
<h1 class="text-l font-extrabold dark:text-white ml-4"> Interpretations page </h1>
<h1 class="text-l font-extrabold dark:text-white ml-4">{{section.label}}</h1>
<!-- prettier-ignore -->
<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">
<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="text-tab" data-tabs-target="#section-text" type="button" role="tab" aria-controls="text" 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="M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 01-2.555-.337A5.972 5.972 0 015.41 20.97a5.969 5.969 0 01-.474-.065 4.48 4.48 0 00.978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25z" /> </svg>
<span>Text</span>
</button>
</li>
<li class="mr-2" role="presentation">
<button class="flex items-center space-x-2 p-4 border-b-2 rounded-t-lg" id="interpretation-tab" data-tabs-target="#interpretation" type="button" role="tab" aria-controls="interpretation" 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="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m5.231 13.481L15 17.25m-4.5-15H5.625c-.621 0-1.125.504-1.125 1.125v16.5c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9zm3.75 11.625a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" /> </svg>
@ -39,22 +34,6 @@
</div>
</div>
<div id="myTabContent" class="mt-40">
<!-- prettier-ignore -->
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="section-text" role="tabpanel" aria-labelledby="text-tab">
<dl class="bg-white dark:bg-gray-900 max-w-full p-3 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">
<div class="flex flex-col w-full">
<!-- prettier-ignore -->
<dt class="flex w-full mb-1 text-gray-500 md:text-lg dark:text-gray-400 flex-col">
<div class="ql-snow">
<div class="ql-editor">
{{ section.about|safe }}
</div>
</div>
</dt>
</div>
</dl>
</div>
<!-- prettier-ignore -->
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="interpretation" role="tabpanel" aria-labelledby="interpretation-tab">
{% if not current_user.is_authenticated %}
@ -181,7 +160,7 @@
<div class="flex border-t-2 pt-3 mt-6 align-center justify-between md:w-full">
<div>
<span class="hidden md:inline-block">Interpretation by</span>
{{interpretation.user.username}} on {{interpretation.created_at.strftime('%B %d, %Y')}}
<a href="{{url_for('user.profile',user_id=interpretation.user.id)}}" class=" text-blue-500 {% if interpretation.user.is_deleted %}line-through{% endif %}">{{interpretation.user.username}}</a> on {{interpretation.created_at.strftime('%B %d, %Y')}}
</div>
<div class="flex ml-auto justify-between w-24">
<span class="space-x-0.5 flex items-center">

View File

@ -2,7 +2,7 @@
<ol class="inline-flex items-center overflow-x-scroll md:overflow-auto p-0">
{% for breadcrumb in local_breadcrumbs if breadcrumb.type != "MyBookList" and breadcrumb.type != "AuthorBookList" %}
<li class="inline-flex items-center align-middle justify-center">
{% if not loop.index==local_breadcrumbs|length %}
{% if not loop.index==local_breadcrumbs|length-1 %}
<a href="{{ breadcrumb.url }}" class="inline-flex text-xs font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white" data-tooltip-target="breadcrumb-{{loop.index}}-tooltip" data-tooltip-placement="bottom">
{% else %}
<span class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white" data-tooltip-target="breadcrumb-{{loop.index}}-tooltip" data-tooltip-placement="bottom">
@ -18,7 +18,7 @@
{% else %}
</span>
{% endif %}
{% if not loop.index==local_breadcrumbs|length %}
{% if not loop.index==local_breadcrumbs|length-1 %}
<svg aria-hidden="true" class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path> </svg>
{% endif %}
</li>

View File

@ -1,22 +1,20 @@
<!-- prettier-ignore -->
{% extends 'base.html' %}
{% if current_user.is_authenticated %}
{% include 'book/delete_interpretation_modal.html' %}
{% include 'book/delete_comment_modal.html' %}
{% include 'book/edit_comment_modal.html' %}
{% include 'book/edit_interpretation_modal.html' %}
<!-- show delete section btn on rightside bar -->
{% set show_edit_interpretation = True %}
<!-- prettier-ignore -->
{% set show_delete_interpretation = True %}
<!-- prettier-ignore -->
{% block right_sidebar %}
{% include 'book/right_sidebar.html' %}
{% include 'book/right_sidebar.html' %}
{% endblock %}
{% endif %}
{% block content %}
{% include 'book/breadcrumbs_navigation.html'%}
@ -29,6 +27,17 @@
</div>
<div class="p-1">
<!-- prettier-ignore -->
{% if not current_user.is_authenticated %}
<div class="bg-white dark:bg-gray-900 max-w-full p-6 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">
<div class="grid gap-6">
<div class="col-span-6 sm:col-span-3 truncate">
<h3 class="text-xl font-semibold text-gray-900 dark:text-white ">Connect you wallet to start contributing!</h3>
</div>
</div>
</div>
<!-- prettier-ignore -->
{% endif %}
{% if current_user.is_authenticated %}
<form {% if sub_collection %}
action="{{ url_for('book.create_comment', book_id=book.id, collection_id=collection.id, sub_collection_id=sub_collection.id,section_id=section.id,interpretation_id=interpretation.id) }}"
{% else %}
@ -45,6 +54,8 @@
<!-- prettier-ignore -->
<button type="submit" class="ml-auto 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 w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"> Leave comment </button>
</form>
<!-- prettier-ignore -->
{% endif %}
</div>
<!-- prettier-ignore -->
<dl class="w-md md:w-full text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700">

View File

@ -5,10 +5,7 @@
<!-- prettier-ignore -->
<h1 class="hidden md:inline font-extrabold text-lg dark:text-white ml-4 mt-5">{{book.label}}</h1>
<!-- prettier-ignore -->
<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>
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400"> {{book.about}} </p>
<!-- 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">
<li class="mr-2 w-full md:w-auto" role="presentation">

View File

@ -63,6 +63,9 @@
<div class="px-4 py-3" role="none">
<p class="text-center text-sm text-gray-900 dark:text-white" role="none"> {{current_user.username}} </p>
</div>
<div class="px-4 py-3" role="none">
<a class="text-center text-sm text-gray-900 dark:text-white" role="none" href="{{ url_for('user.profile',user_id=current_user.id) }}"> View profile </a>
</div>
<div class="px-4 py-3" role="none">
<a class="text-center text-sm text-gray-900 dark:text-white" role="none" href="{{ url_for('user.edit_profile') }}"> Edit profile </a>
</div>

View File

@ -79,7 +79,7 @@
<div class="flex mt-auto align-center justify-between md:w-full">
<div>
<span class="hidden md:inline-block">Interpretation by</span>
{{interpretation.user.username}} on {{interpretation.created_at.strftime('%B %d, %Y')}}
<a href="{{url_for('user.profile',user_id=interpretation.user.id)}}" class=" text-blue-500 {% if interpretation.user.is_deleted %}line-through{% endif %}">{{interpretation.user.username}}</a> on {{interpretation.created_at.strftime('%B %d, %Y')}}
</div>
<div class="flex ml-auto justify-between w-24">
<span class="space-x-0.5 flex items-center">
@ -105,7 +105,7 @@
<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">
{% if book.versions %}
<p> Last updated on {{book.versions[-1].updated_at.strftime('%B %d, %Y')}} </p>
<p> Last updated by <a href="{{url_for('user.profile',user_id=book.owner.id)}}" class=" text-blue-500 {% if book.owner.is_deleted %}line-through{% endif %}">{{book.owner.username}}</a> on {{book.versions[-1].updated_at.strftime('%B %d, %Y')}} </p>
{% endif %}
<div class="flex ml-auto align-center justify-center space-x-3">
<span class="book-star-block space-x-0.5 flex items-center">
@ -114,11 +114,11 @@
</span>
<span class="space-x-0.5 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
<p>55</p>
<p>{{ book.approved_interpretations|length }}</p>
</span>
<span class="space-x-0.5 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> </svg>
<p>55</p>
<p>{{ book.approved_comments|length }}</p>
</span>
</div>
</dd>

View File

@ -6,14 +6,15 @@
{{ 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 profile </h3>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white"> Are you sure you want to deactivate your profile? </h3>
<button id="modalAddCloseButton" data-modal-hide="delete_profile_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 class="flex flex-col items-start p-6 border-t border-gray-200 rounded-b dark:border-gray-600">
<button id="modalAddCloseButton" data-modal-hide="delete_profile_modal" type="button" class="text-white mb-2 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">No, cancel</button>
<button name="submit" type="submit" class="text-white mb-2 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">Yes, deactivate profile</button>
</div>
</form>
</div>

View File

@ -2,16 +2,19 @@
{% extends 'base.html' %}
{% block content %}
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
{% if user.is_deleted %}
<h1 class="hidden md:inline font-extrabold text-lg dark:text-white ml-4">Sorry this user was deactivated</h1>
{% else %}
<div class="flex p-5 items-center">
<!-- prettier-ignore -->
{% if user.avatar_img %}
<!-- prettier-ignore -->
<img class=" w-10 h-10 rounded-full mr-3" src="data:image/jpeg;base64,{{ current_user.avatar_img }}" alt="user avatar">
<img class=" w-10 h-10 rounded-full mr-3" src="data:image/jpeg;base64,{{ user.avatar_img }}" alt="user avatar">
{% else %}
<!-- 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-10 h-10"> <path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" /> </svg>
{% endif %}
<!-- prettier-ignore -->
<h1 class="hidden md:inline font-extrabold text-lg dark:text-white ml-4">{{user.username}}</h1>
<!-- prettier-ignore -->
@ -19,6 +22,7 @@
{{user.wallet_id}}
</span>
</div>
{% endif %}
<!-- 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">
<li class="mr-2 w-full md:w-auto" role="presentation">
@ -30,7 +34,6 @@
<li class="mr-2 w-full md:w-auto" role="presentation">
<!-- prettier-ignore -->
<button class="inline-flex p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300" id="contributions-tab" data-tabs-target="#contributions" type="button" role="tab" aria-controls="contributions" 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 mr-3"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
Contributions
</button>
</li>
@ -56,11 +59,11 @@
</span>
<span class="space-x-0.5 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
<p>55</p>
<p>{{ book.approved_interpretations|length }}</p>
</span>
<span class="space-x-0.5 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> </svg>
<p>55</p>
<p>{{ book.approved_comments|length }}</p>
</span>
</div>
</dd>
@ -120,7 +123,7 @@
<div class="flex mt-auto align-center justify-between md:w-full">
<div>
<span class="hidden md:inline-block">Interpretation by</span>
{{interpretation.user.username}} on {{interpretation.created_at.strftime('%B %d, %Y')}}
<a href="{{url_for('user.profile',user_id=interpretation.user.id)}}" class=" text-blue-500 {% if interpretation.user.is_deleted %}line-through{% endif %}">{{interpretation.user.username}}</a> on {{interpretation.created_at.strftime('%B %d, %Y')}}
</div>
<div class="flex ml-auto justify-between w-24">
<span class="space-x-0.5 flex items-center">

View File

@ -0,0 +1,23 @@
<!-- prettier-ignore -->
{% extends 'base.html' %}
{% include 'user/delete_profile_modal.html' %}
{% block content %}
<!-- component -->
<section>
<div class="w-full lg:w-4/12 px-4 mx-auto pt-6">
<div>
<!-- prettier-ignore -->
<div class="w-full lg:max-w-xl p-6 space-y-8 sm:p-8 bg-white rounded-lg shadow-xl dark:bg-gray-800">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">You profile where deactivated earlier. Do you want to reactivate it?</h2>
<!-- prettier-ignore -->
<form class="mt-8 space-y-6 from" role="form" action="{{ url_for('user.profile_reactivate') }}" method="post">
{{ form.hidden_tag() }}
<button type="submit" class="w-full px-5 py-3 text-base font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 sm:w-auto dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Reactivate profile</button>
</form>
</div>
</div>
</div>
</section>
{% block right_sidebar %}{% endblock %}
<!-- prettier-ignore -->
{% endblock %}

View File

@ -103,6 +103,9 @@ def verify():
log(log.INFO, "Register new user")
flash("User created and logged in successful.", "success")
return redirect(url_for("user.edit_profile"))
if user.is_deleted:
login_user(user=user)
return redirect(url_for("user.profile_reactivate"))
login_user(user=user)
log(log.INFO, "Verify success.")
flash("Verify success.", "success")

View File

@ -940,7 +940,6 @@ def interpretation_delete(
),
methods=["GET"],
)
@login_required
def qa_view(
book_id: int,
collection_id: int,

View File

@ -46,7 +46,7 @@ def edit_profile():
user.is_activated = True
user.save()
return redirect(url_for("main.index"))
elif form.is_submitted:
elif form.is_submitted():
log(log.ERROR, "Update user errors: [%s]", form.errors)
for field, errors in form.errors.items():
field_label = form._fields[field].label.text
@ -59,7 +59,6 @@ def edit_profile():
@bp.route("/<int:user_id>/profile")
@login_required
def profile(user_id: int):
user: m.User = db.session.get(m.User, user_id)
interpretations: m.Interpretation = m.Interpretation.query.filter_by(
@ -93,8 +92,6 @@ def create():
@login_required
def profile_delete():
user: m.User = db.session.get(m.User, current_user.id)
for book in user.books:
book.is_deleted = True
user.is_deleted = True
log(log.INFO, "User deleted. User: [%s]", user)
user.save()
@ -103,6 +100,22 @@ def profile_delete():
return redirect(url_for("home.get_all"))
@bp.route("/profile_reactivate", methods=["GET", "POST"])
def profile_reactivate():
user: m.User = db.session.get(m.User, current_user.id)
if not user:
log(log.CRITICAL, "No such user. User: [%s]", user)
return redirect(url_for("home.get_all"))
form = f.ReactivateUserForm()
if form.validate_on_submit():
user.is_deleted = False
log(log.INFO, "Form submitted. User reactivated: [%s]", user)
flash("User reactivated!", "success")
user.save()
return redirect(url_for("home.get_all"))
return render_template("user/reactivate.html", form=form)
@bp.route("/search", methods=["GET"])
@login_required
def search():

View File

@ -8,15 +8,9 @@ const $addUserModalElement: HTMLElement =
const modalOptions: ModalOptions = {
placement: 'bottom-right',
closable: true,
onHide: () => {
console.log('modal is hidden');
},
onShow: () => {
console.log('user id: ');
},
onToggle: () => {
console.log('modal has been toggled');
},
onHide: () => {},
onShow: () => {},
onToggle: () => {},
};
const addModal: ModalInterface = new Modal($addUserModalElement, modalOptions);

View File

@ -54,7 +54,9 @@ export function initWallet() {
credentials: 'include',
redirect: 'follow',
});
window.location.reload();
if (res2.status == 200) {
window.location.replace(res2.url);
} else window.location.reload();
}
const connectWalletBtns = document.querySelectorAll('#connectWalletBtn');

View File

@ -879,9 +879,7 @@ def test_crud_interpretation(client: FlaskClient, runner: FlaskCliRunner):
# edit
m.Interpretation(
label="Test",
text="Test",
section_id=section_in_collection.id,
label="Test", text="Test", section_id=section_in_collection.id, user_id=user.id
).save()
m.Interpretation(
@ -1087,8 +1085,11 @@ def test_crud_comment(client: FlaskClient, runner: FlaskCliRunner):
def test_access_to_settings_page(client: FlaskClient):
_, user = login(client)
book_1 = m.Book(label="test", about="test").save()
book_1 = m.Book(label="test", about="test", user_id=user.id).save()
m.BookVersion(semver="1.0.0", book_id=book_1.id).save()
book_2 = m.Book(label="test", about="test", user_id=user.id).save()
m.BookVersion(semver="1.0.0", book_id=book_2.id).save()
response: Response = client.get(
f"/book/{book_1.id}/settings",
@ -1096,7 +1097,6 @@ def test_access_to_settings_page(client: FlaskClient):
)
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",

View File

@ -0,0 +1,114 @@
from flask.testing import FlaskClient
from app import models as m
from tests.utils import login, create_test_book
def test_approved_interpretations(client: FlaskClient):
_, user = login(client)
create_test_book(user.id)
dummy_user = m.User(username="Bob").save()
create_test_book(dummy_user.id)
book: m.Book = m.Book.query.first()
assert len(book.approved_interpretations) == 0
for interpretation in m.Interpretation.query.all():
interpretation.approved = True
interpretation.save()
assert len(book.approved_interpretations) == 1
section: m.Section = m.Section.query.first()
assert section
interpretation: m.Interpretation = m.Interpretation(
section_id=section.id, label="231", text="123", approved=True
).save()
assert len(book.approved_interpretations) == 2
interpretation.is_deleted = True
interpretation.save()
assert len(book.approved_interpretations) == 1
collection: m.Collection = m.Collection.query.first()
sub_collection: m.Collection = m.Collection(
parent_id=collection.id,
label="123",
).save()
section: m.Section = m.Section(
label="123", collection_id=sub_collection.id, version_id=book.last_version.id
).save()
interpretation: m.Interpretation = m.Interpretation(
section_id=section.id, label="231", text="123", approved=True
).save()
assert len(book.approved_interpretations) == 2
sub_collection.is_deleted = True
sub_collection.save()
assert len(book.approved_interpretations) == 1
sub_collection.is_deleted = False
sub_collection.save()
assert len(book.approved_interpretations) == 2
# collection.is_deleted = True
# collection.save()
# assert len(book.approved_interpretations) == 0
def test_approved_comments(client: FlaskClient):
_, user = login(client)
create_test_book(user.id)
dummy_user = m.User(username="Bob").save()
create_test_book(dummy_user.id)
book: m.Book = m.Book.query.first()
assert len(book.approved_comments) == 0
for comment in m.Comment.query.all():
comment.approved = True
comment.save()
assert len(book.approved_comments) == 1
interpretation: m.Interpretation = m.Interpretation.query.first()
assert interpretation
comment: m.Comment = m.Comment(
text="231", approved=True, interpretation_id=interpretation.id
).save()
assert len(book.approved_comments) == 2
comment.is_deleted = True
comment.save()
assert len(book.approved_comments) == 1
comment: m.Comment = m.Comment(
text="456", approved=True, interpretation_id=interpretation.id
).save()
assert len(book.approved_comments) == 2
interpretation.is_deleted = True
interpretation.save()
assert len(book.approved_comments) == 0
interpretation.is_deleted = False
interpretation.save()
assert len(book.approved_comments) == 2
collection: m.Collection = m.Collection.query.first()
collection.is_deleted = True
collection.save()
interpretation.is_deleted = False
interpretation.save()
assert len(book.approved_comments) == 0

View File

@ -120,6 +120,7 @@ def test_profile(client):
user_id=user.id,
)
book.save()
m.BookVersion(semver="1.0.0", book_id=book.id).save()
assert book
# profile page
@ -139,4 +140,3 @@ def test_profile(client):
)
assert res
assert user.is_deleted
assert user.books[0].is_deleted