mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
feat(fastapi): add comment actions to /account/{username}/comments
With this change, we've decoupled some partials shared between `/pkgbase/{name}` and `/account/{username}/comments`. The comment actions template now resolves its package base via the `comment` instance instead of requiring `pkgbase`. We've also modified the existing package comment routes to support execution from any location using the `next` parameter. This allows us to reuse code from package comments for account comments actions. Moved the majority of comment editing javascript to its own .js file. Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
parent
adb6252f85
commit
691b7b9091
12 changed files with 276 additions and 182 deletions
|
@ -612,8 +612,7 @@ account_template = (
|
||||||
status_code=HTTPStatus.UNAUTHORIZED)
|
status_code=HTTPStatus.UNAUTHORIZED)
|
||||||
async def account(request: Request, username: str):
|
async def account(request: Request, username: str):
|
||||||
_ = l10n.get_translator_for_request(request)
|
_ = l10n.get_translator_for_request(request)
|
||||||
context = await make_variable_context(request,
|
context = await make_variable_context(request, _("Account") + username)
|
||||||
_("Account") + " " + username)
|
|
||||||
|
|
||||||
user = db.query(models.User, models.User.Username == username).first()
|
user = db.query(models.User, models.User.Username == username).first()
|
||||||
if not user:
|
if not user:
|
||||||
|
|
|
@ -318,7 +318,8 @@ async def pkgbase_comments_post(
|
||||||
|
|
||||||
@router.get("/pkgbase/{name}/comments/{id}/form")
|
@router.get("/pkgbase/{name}/comments/{id}/form")
|
||||||
@auth_required(True, login=False)
|
@auth_required(True, login=False)
|
||||||
async def pkgbase_comment_form(request: Request, name: str, id: int):
|
async def pkgbase_comment_form(request: Request, name: str, id: int,
|
||||||
|
next: str = Query(default=None)):
|
||||||
""" Produce a comment form for comment {id}. """
|
""" Produce a comment form for comment {id}. """
|
||||||
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
||||||
comment = pkgbase.comments.filter(models.PackageComment.ID == id).first()
|
comment = pkgbase.comments.filter(models.PackageComment.ID == id).first()
|
||||||
|
@ -331,6 +332,11 @@ async def pkgbase_comment_form(request: Request, name: str, id: int):
|
||||||
context = await make_single_context(request, pkgbase)
|
context = await make_single_context(request, pkgbase)
|
||||||
context["comment"] = comment
|
context["comment"] = comment
|
||||||
|
|
||||||
|
if not next:
|
||||||
|
next = f"/pkgbase/{name}"
|
||||||
|
|
||||||
|
context["next"] = next
|
||||||
|
|
||||||
form = render_raw_template(
|
form = render_raw_template(
|
||||||
request, "partials/packages/comment_form.html", context)
|
request, "partials/packages/comment_form.html", context)
|
||||||
return JSONResponse({"form": form})
|
return JSONResponse({"form": form})
|
||||||
|
@ -341,7 +347,8 @@ async def pkgbase_comment_form(request: Request, name: str, id: int):
|
||||||
async def pkgbase_comment_post(
|
async def pkgbase_comment_post(
|
||||||
request: Request, name: str, id: int,
|
request: Request, name: str, id: int,
|
||||||
comment: str = Form(default=str()),
|
comment: str = Form(default=str()),
|
||||||
enable_notifications: bool = Form(default=False)):
|
enable_notifications: bool = Form(default=False),
|
||||||
|
next: str = Form(default=None)):
|
||||||
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
||||||
db_comment = get_pkgbase_comment(pkgbase, id)
|
db_comment = get_pkgbase_comment(pkgbase, id)
|
||||||
|
|
||||||
|
@ -366,14 +373,18 @@ async def pkgbase_comment_post(
|
||||||
PackageBase=pkgbase)
|
PackageBase=pkgbase)
|
||||||
update_comment_render(db_comment.ID)
|
update_comment_render(db_comment.ID)
|
||||||
|
|
||||||
|
if not next:
|
||||||
|
next = f"/pkgbase/{pkgbase.Name}"
|
||||||
|
|
||||||
# Redirect to the pkgbase page anchored to the updated comment.
|
# Redirect to the pkgbase page anchored to the updated comment.
|
||||||
return RedirectResponse(f"/pkgbase/{pkgbase.Name}#comment-{db_comment.ID}",
|
return RedirectResponse(f"{next}#comment-{db_comment.ID}",
|
||||||
status_code=HTTPStatus.SEE_OTHER)
|
status_code=HTTPStatus.SEE_OTHER)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/pkgbase/{name}/comments/{id}/delete")
|
@router.post("/pkgbase/{name}/comments/{id}/delete")
|
||||||
@auth_required(True, redirect="/pkgbase/{name}/comments/{id}/delete")
|
@auth_required(True, redirect="/pkgbase/{name}/comments/{id}/delete")
|
||||||
async def pkgbase_comment_delete(request: Request, name: str, id: int):
|
async def pkgbase_comment_delete(request: Request, name: str, id: int,
|
||||||
|
next: str = Form(default=None)):
|
||||||
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
||||||
comment = get_pkgbase_comment(pkgbase, id)
|
comment = get_pkgbase_comment(pkgbase, id)
|
||||||
|
|
||||||
|
@ -390,13 +401,16 @@ async def pkgbase_comment_delete(request: Request, name: str, id: int):
|
||||||
comment.Deleter = request.user
|
comment.Deleter = request.user
|
||||||
comment.DelTS = now
|
comment.DelTS = now
|
||||||
|
|
||||||
return RedirectResponse(f"/pkgbase/{name}",
|
if not next:
|
||||||
status_code=HTTPStatus.SEE_OTHER)
|
next = f"/pkgbase/{name}"
|
||||||
|
|
||||||
|
return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/pkgbase/{name}/comments/{id}/undelete")
|
@router.post("/pkgbase/{name}/comments/{id}/undelete")
|
||||||
@auth_required(True, redirect="/pkgbase/{name}/comments/{id}/undelete")
|
@auth_required(True, redirect="/pkgbase/{name}/comments/{id}/undelete")
|
||||||
async def pkgbase_comment_undelete(request: Request, name: str, id: int):
|
async def pkgbase_comment_undelete(request: Request, name: str, id: int,
|
||||||
|
next: str = Form(default=None)):
|
||||||
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
||||||
comment = get_pkgbase_comment(pkgbase, id)
|
comment = get_pkgbase_comment(pkgbase, id)
|
||||||
|
|
||||||
|
@ -412,13 +426,16 @@ async def pkgbase_comment_undelete(request: Request, name: str, id: int):
|
||||||
comment.Deleter = None
|
comment.Deleter = None
|
||||||
comment.DelTS = None
|
comment.DelTS = None
|
||||||
|
|
||||||
return RedirectResponse(f"/pkgbase/{name}",
|
if not next:
|
||||||
status_code=HTTPStatus.SEE_OTHER)
|
next = f"/pkgbase/{name}"
|
||||||
|
|
||||||
|
return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/pkgbase/{name}/comments/{id}/pin")
|
@router.post("/pkgbase/{name}/comments/{id}/pin")
|
||||||
@auth_required(True, redirect="/pkgbase/{name}/comments/{id}/pin")
|
@auth_required(True, redirect="/pkgbase/{name}/comments/{id}/pin")
|
||||||
async def pkgbase_comment_pin(request: Request, name: str, id: int):
|
async def pkgbase_comment_pin(request: Request, name: str, id: int,
|
||||||
|
next: str = Form(default=None)):
|
||||||
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
||||||
comment = get_pkgbase_comment(pkgbase, id)
|
comment = get_pkgbase_comment(pkgbase, id)
|
||||||
|
|
||||||
|
@ -434,13 +451,16 @@ async def pkgbase_comment_pin(request: Request, name: str, id: int):
|
||||||
with db.begin():
|
with db.begin():
|
||||||
comment.PinnedTS = now
|
comment.PinnedTS = now
|
||||||
|
|
||||||
return RedirectResponse(f"/pkgbase/{name}",
|
if not next:
|
||||||
status_code=HTTPStatus.SEE_OTHER)
|
next = f"/pkgbase/{name}"
|
||||||
|
|
||||||
|
return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/pkgbase/{name}/comments/{id}/unpin")
|
@router.post("/pkgbase/{name}/comments/{id}/unpin")
|
||||||
@auth_required(True, redirect="/pkgbase/{name}/comments/{id}/unpin")
|
@auth_required(True, redirect="/pkgbase/{name}/comments/{id}/unpin")
|
||||||
async def pkgbase_comment_unpin(request: Request, name: str, id: int):
|
async def pkgbase_comment_unpin(request: Request, name: str, id: int,
|
||||||
|
next: str = Form(default=None)):
|
||||||
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
||||||
comment = get_pkgbase_comment(pkgbase, id)
|
comment = get_pkgbase_comment(pkgbase, id)
|
||||||
|
|
||||||
|
@ -455,8 +475,10 @@ async def pkgbase_comment_unpin(request: Request, name: str, id: int):
|
||||||
with db.begin():
|
with db.begin():
|
||||||
comment.PinnedTS = 0
|
comment.PinnedTS = 0
|
||||||
|
|
||||||
return RedirectResponse(f"/pkgbase/{name}",
|
if not next:
|
||||||
status_code=HTTPStatus.SEE_OTHER)
|
next = f"/pkgbase/{name}"
|
||||||
|
|
||||||
|
return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/pkgbase/{name}/comaintainers")
|
@router.get("/pkgbase/{name}/comaintainers")
|
||||||
|
|
|
@ -18,12 +18,19 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% for comment in comments %}
|
{% for comment in comments %}
|
||||||
|
{% set header_cls = "comment-header" %}
|
||||||
|
{% if comment.Deleter %}
|
||||||
|
{% set header_cls = "%s %s" | format(header_cls, "comment-deleted") %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if not comment.Deleter or request.user.has_credential("CRED_COMMENT_VIEW_DELETED", approved=[comment.Deleter]) %}
|
||||||
|
|
||||||
{% set commented_at = comment.CommentTS | dt | as_timezone(timezone) %}
|
{% set commented_at = comment.CommentTS | dt | as_timezone(timezone) %}
|
||||||
<h4 id="comment-{{ comment.ID }}" class="comment-header">
|
<h4 id="comment-{{ comment.ID }}" class="{{ header_cls }}">
|
||||||
{{
|
{{
|
||||||
"Commented on package %s%s%s on %s%s%s" | tr
|
"Commented on package %s%s%s on %s%s%s" | tr
|
||||||
| format(
|
| format(
|
||||||
'<a href="/pkgbase/{{ comment.PackageBase.Name }}">',
|
'<a href="/pkgbase/%s">' | format(comment.PackageBase.Name),
|
||||||
comment.PackageBase.Name,
|
comment.PackageBase.Name,
|
||||||
"</a>",
|
"</a>",
|
||||||
'<a href="/account/%s/comments#comment-%s">' | format(
|
'<a href="/account/%s/comments#comment-%s">' | format(
|
||||||
|
@ -34,19 +41,27 @@
|
||||||
"</a>"
|
"</a>"
|
||||||
) | safe
|
) | safe
|
||||||
}}
|
}}
|
||||||
</h4>
|
{% if comment.Editor %}
|
||||||
<div id="comment-{{ comment.ID }}-content" class="article-content">
|
{% set edited_on = comment.EditedTS | dt | as_timezone(timezone) %}
|
||||||
<div>
|
<span class="edited">
|
||||||
{% if comment.RenderedComment %}
|
({{ "edited on %s by %s" | tr
|
||||||
{{ comment.RenderedComment | safe }}
|
| format(edited_on.strftime('%Y-%m-%d %H:%M'),
|
||||||
{% else %}
|
'<a href="/account/%s">%s</a>' | format(
|
||||||
{{ comment.Comments }}
|
comment.Editor.Username, comment.Editor.Username))
|
||||||
|
| safe
|
||||||
|
}})
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% include "partials/comment_actions.html" %}
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
{% include "partials/comment_content.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
100
templates/partials/comment_actions.html
Normal file
100
templates/partials/comment_actions.html
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
{% set pkgbasename = comment.PackageBase.Name %}
|
||||||
|
|
||||||
|
{% if not comment.Deleter %}
|
||||||
|
{% if request.user.has_credential('CRED_COMMENT_DELETE', approved=[comment.User]) %}
|
||||||
|
<form class="delete-comment-form"
|
||||||
|
method="post"
|
||||||
|
action="/pkgbase/{{ pkgbasename }}/comments/{{ comment.ID }}/delete"
|
||||||
|
>
|
||||||
|
<fieldset style="display:inline;">
|
||||||
|
<input type="hidden"
|
||||||
|
name="next"
|
||||||
|
value="{{ request.url.path }}" />
|
||||||
|
<input type="image"
|
||||||
|
class="delete-comment"
|
||||||
|
src="/images/x.min.svg"
|
||||||
|
width="11"
|
||||||
|
height="11"
|
||||||
|
alt="{{ 'Delete comment' | tr }}"
|
||||||
|
title="{{ 'Delete comment' | tr }}"
|
||||||
|
name="submit" value="1" />
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if request.user.has_credential('CRED_COMMENT_EDIT', approved=[comment.User]) %}
|
||||||
|
<a id="comment-edit-link-{{ comment.ID }}"
|
||||||
|
{# /pkgbase/{name}/comments/{id}/edit #}
|
||||||
|
href="/pkgbase/{{ pkgbasename }}/comments/{{ comment.ID }}/edit?{{ {'next': request.url.path} | urlencode }}"
|
||||||
|
class="edit-comment"
|
||||||
|
title="{{ 'Edit comment' | tr }}"
|
||||||
|
>
|
||||||
|
<img src="/images/pencil.min.svg" alt="{{ 'Edit comment' | tr }}"
|
||||||
|
width="11" height="11">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{# Set the edit event listener for this link. We must do this here
|
||||||
|
so that we can utilize Jinja2's values. #}
|
||||||
|
<script type="text/javascript" nonce="{{ request.user.nonce }}">
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
let link = document.getElementById("comment-edit-link-{{ comment.ID }}");
|
||||||
|
let fn = function(event) {
|
||||||
|
return handleEditCommentClick(event, "{{ comment.PackageBase.Name }}");
|
||||||
|
};
|
||||||
|
link.addEventListener("click", fn);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if request.user.has_credential("CRED_COMMENT_PIN", approved=[comment.PackageBase.Maintainer]) %}
|
||||||
|
{% if comment.PinnedTS %}
|
||||||
|
<form class="pin-comment-form"
|
||||||
|
method="post"
|
||||||
|
action="/pkgbase/{{ comment.PackageBase.Name }}/comments/{{ comment.ID }}/unpin"
|
||||||
|
>
|
||||||
|
<fieldset style="display:inline;">
|
||||||
|
<input type="hidden" name="next" value="{{ request.url.path }}" />
|
||||||
|
<input type="image"
|
||||||
|
class="pin-comment"
|
||||||
|
src="/images/unpin.min.svg"
|
||||||
|
alt="{{ 'Unpin comment' | tr }}"
|
||||||
|
title="{{ 'Unpin comment' | tr }}"
|
||||||
|
name="submit"
|
||||||
|
value="1" width="11" height="11" />
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<form class="pin-comment-form"
|
||||||
|
method="post"
|
||||||
|
action="/pkgbase/{{ comment.PackageBase.Name }}/comments/{{ comment.ID }}/pin"
|
||||||
|
>
|
||||||
|
<fieldset style="display:inline;">
|
||||||
|
<input type="hidden" name="next" value="{{ request.url.path }}" />
|
||||||
|
<input type="image"
|
||||||
|
class="pin-comment"
|
||||||
|
src="/images/pin.min.svg"
|
||||||
|
alt="{{ 'Pin comment' | tr }}"
|
||||||
|
title="{{ 'Pin comment' | tr }}"
|
||||||
|
name="submit"
|
||||||
|
value="1" width="11" height="11" />
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% elif request.user.has_credential("CRED_COMMENT_UNDELETE", approved=[comment.User]) %}
|
||||||
|
<form class="undelete-comment-form"
|
||||||
|
method="post"
|
||||||
|
action="/pkgbase/{{ comment.PackageBase.Name }}/comments/{{ comment.ID }}/undelete"
|
||||||
|
>
|
||||||
|
<fieldset style="display:inline;">
|
||||||
|
<input type="hidden" name="next" value="{{ request.url.path }}" />
|
||||||
|
<input type="image"
|
||||||
|
class="undelete-comment"
|
||||||
|
src="/images/action-undo.min.svg"
|
||||||
|
alt="{{ 'Undelete comment' | tr }}"
|
||||||
|
title="{{ 'Undelete comment' | tr }}"
|
||||||
|
name="submit" value="1" width="11" height="11" />
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
15
templates/partials/comment_content.html
Normal file
15
templates/partials/comment_content.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
{% set article_cls = "article-content" %}
|
||||||
|
{% if comment.Deleter %}
|
||||||
|
{% set article_cls = "%s %s" | format(article_cls, "comment-deleted") %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div id="comment-{{ comment.ID }}-content" class="{{ article_cls }}">
|
||||||
|
<div>
|
||||||
|
{% if comment.RenderedComment %}
|
||||||
|
{{ comment.RenderedComment | safe }}
|
||||||
|
{% else %}
|
||||||
|
{{ comment.Comments }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -15,5 +15,8 @@
|
||||||
<!-- Include local typeahead -->
|
<!-- Include local typeahead -->
|
||||||
<script type="text/javascript" src="/static/js/typeahead.js"></script>
|
<script type="text/javascript" src="/static/js/typeahead.js"></script>
|
||||||
|
|
||||||
|
<!-- On-the-fly comment editing functions -->
|
||||||
|
<script type="text/javascript" src="/static/js/comment-edit.js"></script>
|
||||||
|
|
||||||
<title>AUR ({{ language }}) - {{ title | tr }}</title>
|
<title>AUR ({{ language }}) - {{ title | tr }}</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -35,76 +35,9 @@
|
||||||
}})
|
}})
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not comment.Deleter %}
|
|
||||||
{% if request.user.has_credential('CRED_COMMENT_DELETE', approved=[comment.User]) %}
|
|
||||||
<form class="delete-comment-form" method="post"
|
|
||||||
action="/pkgbase/{{ pkgbase.Name }}/comments/{{ comment.ID }}/delete">
|
|
||||||
<fieldset style="display:inline;">
|
|
||||||
<input type="image" class="delete-comment" src="/images/x.min.svg" width="11" height="11" alt="{{ 'Delete comment' | tr }}" title="{{ 'Delete comment' | tr }}" name="submit" value="1" />
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if request.user.has_credential('CRED_COMMENT_EDIT', approved=[comment.User]) %}
|
{% include "partials/comment_actions.html" %}
|
||||||
<a href="/pkgbase/{{ pkgname }}/edit-comment/?comment_id={{ comment.ID }}" class="edit-comment" title="Edit comment"><img src="/images/pencil.min.svg" alt="Edit comment" width="11" height="11"></a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if request.user.has_credential("CRED_COMMENT_PIN", approved=[pkgbase.Maintainer]) %}
|
|
||||||
{% if comment.PinnedTS %}
|
|
||||||
<form class="pin-comment-form"
|
|
||||||
method="post"
|
|
||||||
action="/pkgbase/{{ pkgbase.Name }}/comments/{{ comment.ID }}/unpin"
|
|
||||||
>
|
|
||||||
<fieldset style="display:inline;">
|
|
||||||
<input type="image"
|
|
||||||
class="pin-comment"
|
|
||||||
src="/images/unpin.min.svg"
|
|
||||||
alt="{{ 'Unpin comment' | tr }}"
|
|
||||||
title="{{ 'Unpin comment' | tr }}"
|
|
||||||
name="submit"
|
|
||||||
value="1" width="11" height="11" />
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<form class="pin-comment-form"
|
|
||||||
method="post"
|
|
||||||
action="/pkgbase/{{ pkgbase.Name }}/comments/{{ comment.ID }}/pin"
|
|
||||||
>
|
|
||||||
<fieldset style="display:inline;">
|
|
||||||
<input type="image"
|
|
||||||
class="pin-comment"
|
|
||||||
src="/images/pin.min.svg"
|
|
||||||
alt="{{ 'Pin comment' | tr }}"
|
|
||||||
title="{{ 'Pin comment' | tr }}"
|
|
||||||
name="submit"
|
|
||||||
value="1" width="11" height="11" />
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% elif request.user.has_credential("CRED_COMMENT_UNDELETE", approved=[comment.User]) %}
|
|
||||||
<form class="undelete-comment-form"
|
|
||||||
method="post"
|
|
||||||
action="/pkgbase/{{ pkgbase.Name }}/comments/{{ comment.ID }}/undelete"
|
|
||||||
>
|
|
||||||
<fieldset style="display:inline;">
|
|
||||||
<input type="image"
|
|
||||||
class="undelete-comment"
|
|
||||||
src="/images/action-undo.min.svg"
|
|
||||||
alt="{{ 'Undelete comment' | tr }}"
|
|
||||||
title="{{ 'Undelete comment' | tr }}"
|
|
||||||
name="submit" value="1" width="11" height="11" />
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</h4>
|
</h4>
|
||||||
<div id="comment-{{ comment.ID }}-content" class="{{ article_cls }}">
|
|
||||||
<div>
|
{% include "partials/comment_content.html" %}
|
||||||
{% if comment.RenderedComment %}
|
|
||||||
{{ comment.RenderedComment | safe }}
|
|
||||||
{% else %}
|
|
||||||
{{ comment.Comments }}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -13,6 +13,7 @@ Routes:
|
||||||
|
|
||||||
<form action="{{ action }}" method="post">
|
<form action="{{ action }}" method="post">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
<input type="hidden" name="next" value="{{ next }}" />
|
||||||
<p>
|
<p>
|
||||||
{{ "Git commit identifiers referencing commits in the AUR package "
|
{{ "Git commit identifiers referencing commits in the AUR package "
|
||||||
"repository and URLs are converted to links automatically." | tr }}
|
"repository and URLs are converted to links automatically." | tr }}
|
||||||
|
|
|
@ -39,72 +39,3 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<script type="text/javascript" nonce="{{ request.user.nonce }}">
|
|
||||||
function add_busy_indicator(sibling) {
|
|
||||||
const img = document.createElement('img');
|
|
||||||
img.src = "/images/ajax-loader.gif";
|
|
||||||
img.classList.add('ajax-loader');
|
|
||||||
img.style.height = 11;
|
|
||||||
img.style.width = 16;
|
|
||||||
img.alt = "Busy…";
|
|
||||||
|
|
||||||
sibling.insertAdjacentElement('afterend', img);
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove_busy_indicator(sibling) {
|
|
||||||
const elem = sibling.nextElementSibling;
|
|
||||||
elem.parentNode.removeChild(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getParentsUntil(elem, className) {
|
|
||||||
// Limit to 10 depth
|
|
||||||
for ( ; elem && elem !== document; elem = elem.parentNode) {
|
|
||||||
if (elem.matches(className)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleEditCommentClick(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const parent_element = getParentsUntil(event.target, '.comment-header');
|
|
||||||
const parent_id = parent_element.id;
|
|
||||||
const comment_id = parent_id.substr(parent_id.indexOf('-') + 1);
|
|
||||||
// The div class="article-content" which contains the comment
|
|
||||||
const edit_form = parent_element.nextElementSibling;
|
|
||||||
|
|
||||||
const url = "/pkgbase/{{ pkgbase.Name }}/comments/" + comment_id + "/form";
|
|
||||||
|
|
||||||
add_busy_indicator(event.target);
|
|
||||||
|
|
||||||
fetch(url, {
|
|
||||||
method: 'GET',
|
|
||||||
credentials: 'same-origin'
|
|
||||||
})
|
|
||||||
.then(function(response) {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw Error(response.statusText);
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(function(data) {
|
|
||||||
remove_busy_indicator(event.target);
|
|
||||||
edit_form.innerHTML = data.form;
|
|
||||||
edit_form.querySelector('textarea').focus();
|
|
||||||
})
|
|
||||||
.catch(function(error) {
|
|
||||||
remove_busy_indicator(event.target);
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const divs = document.querySelectorAll('.edit-comment');;
|
|
||||||
for (let div of divs) {
|
|
||||||
div.addEventListener('click', handleEditCommentClick);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1625,3 +1625,10 @@ def test_post_terms_of_service():
|
||||||
response = request.get("/tos", cookies=cookies, allow_redirects=False)
|
response = request.get("/tos", cookies=cookies, allow_redirects=False)
|
||||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||||
assert response.headers.get("location") == "/"
|
assert response.headers.get("location") == "/"
|
||||||
|
|
||||||
|
|
||||||
|
def test_account_comments_not_found():
|
||||||
|
cookies = {"AURSID": user.login(Request(), "testPassword")}
|
||||||
|
with client as request:
|
||||||
|
resp = request.get("/account/non-existent/comments", cookies=cookies)
|
||||||
|
assert resp.status_code == int(HTTPStatus.NOT_FOUND)
|
||||||
|
|
|
@ -1055,6 +1055,13 @@ def test_pkgbase_comments_missing_comment(client: TestClient, maintainer: User,
|
||||||
|
|
||||||
def test_pkgbase_comments(client: TestClient, maintainer: User, user: User,
|
def test_pkgbase_comments(client: TestClient, maintainer: User, user: User,
|
||||||
package: Package):
|
package: Package):
|
||||||
|
""" This test includes tests against the following routes:
|
||||||
|
- POST /pkgbase/{name}/comments
|
||||||
|
- GET /pkgbase/{name} (to check comments)
|
||||||
|
- Tested against a comment created with the POST route
|
||||||
|
- GET /pkgbase/{name}/comments/{id}/form
|
||||||
|
- Tested against a comment created with the POST route
|
||||||
|
"""
|
||||||
cookies = {"AURSID": maintainer.login(Request(), "testPassword")}
|
cookies = {"AURSID": maintainer.login(Request(), "testPassword")}
|
||||||
pkgbasename = package.PackageBase.Name
|
pkgbasename = package.PackageBase.Name
|
||||||
endpoint = f"/pkgbase/{pkgbasename}/comments"
|
endpoint = f"/pkgbase/{pkgbasename}/comments"
|
||||||
|
|
61
web/html/js/comment-edit.js
Normal file
61
web/html/js/comment-edit.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
function add_busy_indicator(sibling) {
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.src = "/images/ajax-loader.gif";
|
||||||
|
img.classList.add('ajax-loader');
|
||||||
|
img.style.height = 11;
|
||||||
|
img.style.width = 16;
|
||||||
|
img.alt = "Busy…";
|
||||||
|
|
||||||
|
sibling.insertAdjacentElement('afterend', img);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_busy_indicator(sibling) {
|
||||||
|
const elem = sibling.nextElementSibling;
|
||||||
|
elem.parentNode.removeChild(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParentsUntil(elem, className) {
|
||||||
|
// Limit to 10 depth
|
||||||
|
for ( ; elem && elem !== document; elem = elem.parentNode) {
|
||||||
|
if (elem.matches(className)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEditCommentClick(event, pkgbasename) {
|
||||||
|
event.preventDefault();
|
||||||
|
const parent_element = getParentsUntil(event.target, '.comment-header');
|
||||||
|
const parent_id = parent_element.id;
|
||||||
|
const comment_id = parent_id.substr(parent_id.indexOf('-') + 1);
|
||||||
|
// The div class="article-content" which contains the comment
|
||||||
|
const edit_form = parent_element.nextElementSibling;
|
||||||
|
|
||||||
|
const url = "/pkgbase/" + pkgbasename + "/comments/" + comment_id + "/form?";
|
||||||
|
|
||||||
|
add_busy_indicator(event.target);
|
||||||
|
|
||||||
|
fetch(url + new URLSearchParams({ next: window.location.pathname }), {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'same-origin'
|
||||||
|
})
|
||||||
|
.then(function(response) {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw Error(response.statusText);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function(data) {
|
||||||
|
remove_busy_indicator(event.target);
|
||||||
|
edit_form.innerHTML = data.form;
|
||||||
|
edit_form.querySelector('textarea').focus();
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
remove_busy_indicator(event.target);
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue