feat: allow users to hide deleted comments

Closes: #435

Signed-off-by: Leonidas Spyropoulos <artafinde@archlinux.org>
This commit is contained in:
Leonidas Spyropoulos 2023-04-21 19:47:55 +01:00
parent 174af5f025
commit 6ede837b4f
No known key found for this signature in database
GPG key ID: 59E43E106B247368
9 changed files with 132 additions and 5 deletions

View file

@ -209,6 +209,7 @@ def make_account_form_context(
context["cn"] = args.get("CN", user.CommentNotify)
context["un"] = args.get("UN", user.UpdateNotify)
context["on"] = args.get("ON", user.OwnershipNotify)
context["hdc"] = args.get("HDC", user.HideDeletedComments)
context["inactive"] = args.get("J", user.InactivityTS != 0)
else:
context["username"] = args.get("U", str())
@ -227,6 +228,7 @@ def make_account_form_context(
context["cn"] = args.get("CN", True)
context["un"] = args.get("UN", False)
context["on"] = args.get("ON", True)
context["hdc"] = args.get("HDC", False)
context["inactive"] = args.get("J", False)
context["password"] = args.get("P", str())
@ -253,6 +255,7 @@ async def account_register(
CN: bool = Form(default=False), # Comment Notify
CU: bool = Form(default=False), # Update Notify
CO: bool = Form(default=False), # Owner Notify
HDC: bool = Form(default=False), # Hide Deleted Comments
captcha: str = Form(default=str()),
):
context = await make_variable_context(request, "Register")
@ -281,6 +284,7 @@ async def account_register_post(
CN: bool = Form(default=False),
UN: bool = Form(default=False),
ON: bool = Form(default=False),
HDC: bool = Form(default=False),
captcha: str = Form(default=None),
captcha_salt: str = Form(...),
):
@ -334,6 +338,7 @@ async def account_register_post(
CommentNotify=CN,
UpdateNotify=UN,
OwnershipNotify=ON,
HideDeletedComments=HDC,
ResetKey=resetkey,
AccountType=atype,
)
@ -417,6 +422,7 @@ async def account_edit_post(
CN: bool = Form(default=False), # Comment Notify
UN: bool = Form(default=False), # Update Notify
ON: bool = Form(default=False), # Owner Notify
HDC: bool = Form(default=False), # Hide Deleted Comments
T: int = Form(default=None),
passwd: str = Form(default=str()),
):

View file

@ -108,6 +108,12 @@ Users = Table(
Column("OwnershipNotify", TINYINT(1), nullable=False, server_default=text("1")),
Column("SSOAccountID", String(255), nullable=True, unique=True),
Index("UsersAccountTypeID", "AccountTypeID"),
Column(
"HideDeletedComments",
TINYINT(unsigned=True),
nullable=False,
server_default=text("0"),
),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",

View file

@ -22,6 +22,7 @@ def simple(
CN: bool = False,
UN: bool = False,
ON: bool = False,
HDC: bool = False,
S: bool = False,
user: models.User = None,
**kwargs,
@ -41,6 +42,7 @@ def simple(
user.CommentNotify = strtobool(CN)
user.UpdateNotify = strtobool(UN)
user.OwnershipNotify = strtobool(ON)
user.HideDeletedComments = strtobool(HDC)
@db.retry_deadlock

View file

@ -0,0 +1,33 @@
"""Add HideDeletedComments to User
Revision ID: e4e49ffce091
Revises: 9e3158957fd7
Create Date: 2023-04-19 23:24:25.854874
"""
from alembic import op
from sqlalchemy.exc import OperationalError
from aurweb.models.user import User
# revision identifiers, used by Alembic.
revision = "e4e49ffce091"
down_revision = "9e3158957fd7"
branch_labels = None
depends_on = None
table = User.__table__
def upgrade():
try:
op.add_column(table.name, table.c.HideDeletedComments)
except OperationalError:
print(
f"Column HideDeletedComments already exists in '{table.name}',"
f" skipping migration."
)
def downgrade():
op.drop_column(table.name, "HideDeletedComments")

View file

@ -1402,6 +1402,10 @@ msgstr ""
msgid "Specify multiple SSH Keys separated by new line, empty lines are ignored."
msgstr ""
#: templates/partials/account_form.html
msgid "Hide deleted comments"
msgstr ""
#: template/account_edit_form.php
msgid "SSH Public Key"
msgstr ""

View file

@ -4,7 +4,7 @@
{% endif %}
{% if not comment.Deleter or request.user.has_credential(creds.COMMENT_VIEW_DELETED, approved=[comment.Deleter]) %}
{% if not (request.user.HideDeletedComments and comment.DelTS) %}
{% set commented_at = comment.CommentTS | dt | as_timezone(timezone) %}
<h4 id="comment-{{ comment.ID }}" class="{{ header_cls }}">
{{
@ -38,3 +38,4 @@
{% include "partials/comment_content.html" %}
{% endif %}
{% endif %}

View file

@ -182,7 +182,7 @@
maxlength="50" name="K" value="{{ pgp }}">
</p>
<!-- Homepage -->
<!-- Language -->
<p>
<label for="id_language">
{% trans %}Language{% endtrans %}:
@ -202,10 +202,10 @@
</select>
</p>
<!-- Homepage -->
<!-- Timezone -->
<p>
<label for="id_timezone">
{% trans %}Timezone{% endtrans %}
{% trans %}Timezone{% endtrans %}:
</label>
<select id="id_timezone" name="TZ">
@ -219,6 +219,19 @@
</select>
</p>
<!-- Hide Deleted Comments -->
<p>
<label for="id_hidedeletedcomments">
{% trans %}Hide deleted comments{% endtrans %}:
</label>
<input id="id_hidedeletedcomments" type="checkbox" name="HDC"
{% if hdc %}
checked="checked"
{% endif %}
>
</p>
</fieldset>
{% if form_type == "UpdateAccount" %}

View file

@ -6,6 +6,7 @@
{% endif %}
{% if not comment.Deleter or request.user.has_credential(creds.COMMENT_VIEW_DELETED, approved=[comment.Deleter]) %}
{% if not (request.user.HideDeletedComments and comment.DelTS) %}
<h4 id="comment-{{ comment.ID }}" class="{{ header_cls }}">
{% set commented_at = comment.CommentTS | dt | as_timezone(timezone) %}
{% set view_account_info = 'View account information for %s' | tr | format(comment.User.Username) %}
@ -41,3 +42,4 @@
{% include "partials/comment_content.html" %}
{% endif %}
{% endif %}

View file

@ -122,6 +122,22 @@ def tu_user():
yield tu_user
@pytest.fixture
def user_who_hates_grey_comments() -> User:
"""Yield a specific User who doesn't like grey comments."""
account_type = db.query(AccountType, AccountType.ID == USER_ID).first()
with db.begin():
user_who_hates_grey_comments = db.create(
User,
Username="test_hater",
Email="test_hater@example.org",
Passwd="testPassword",
AccountType=account_type,
HideDeletedComments=True,
)
yield user_who_hates_grey_comments
@pytest.fixture
def package(maintainer: User) -> Package:
"""Yield a Package created by user."""
@ -193,6 +209,23 @@ def comment(user: User, package: Package) -> PackageComment:
yield comment
@pytest.fixture
def deleted_comment(user: User, package: Package) -> PackageComment:
pkgbase = package.PackageBase
now = time.utcnow()
with db.begin():
comment = db.create(
PackageComment,
User=user,
PackageBase=pkgbase,
Comments="Test comment.",
RenderedComment=str(),
CommentTS=now,
DelTS=now,
)
yield comment
@pytest.fixture
def packages(maintainer: User) -> list[Package]:
"""Yield 55 packages named pkg_0 .. pkg_54."""
@ -409,7 +442,9 @@ def test_paged_depends_required(client: TestClient, package: Package):
assert "Show 6 more" not in resp.text
def test_package_comments(client: TestClient, user: User, package: Package):
def test_package_comments(
client: TestClient, user: User, user_who_hates_grey_comments: User, package: Package
):
now = time.utcnow()
with db.begin():
comment = db.create(
@ -419,6 +454,14 @@ def test_package_comments(client: TestClient, user: User, package: Package):
Comments="Test comment",
CommentTS=now,
)
deleted_comment = db.create(
PackageComment,
PackageBase=package.PackageBase,
User=user,
Comments="Deleted Test comment",
CommentTS=now,
DelTS=now - 1,
)
cookies = {"AURSID": user.login(Request(), "testPassword")}
with client as request:
@ -426,12 +469,29 @@ def test_package_comments(client: TestClient, user: User, package: Package):
resp = request.get(package_endpoint(package))
assert resp.status_code == int(HTTPStatus.OK)
root = parse_root(resp.text)
expected = [comment.Comments, deleted_comment.Comments]
comments = root.xpath(
'.//div[contains(@class, "package-comments")]'
'/div[@class="article-content"]/div/text()'
)
assert len(comments) == 2
for i, row in enumerate(expected):
assert comments[i].strip() == row
cookies = {"AURSID": user_who_hates_grey_comments.login(Request(), "testPassword")}
with client as request:
request.cookies = cookies
resp = request.get(package_endpoint(package))
assert resp.status_code == int(HTTPStatus.OK)
root = parse_root(resp.text)
expected = [comment.Comments]
comments = root.xpath(
'.//div[contains(@class, "package-comments")]'
'/div[@class="article-content"]/div/text()'
)
assert len(comments) == 1 # Deleted comment is hidden
for i, row in enumerate(expected):
assert comments[i].strip() == row