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)
|
||||
async def account(request: Request, username: str):
|
||||
_ = l10n.get_translator_for_request(request)
|
||||
context = await make_variable_context(request,
|
||||
_("Account") + " " + username)
|
||||
context = await make_variable_context(request, _("Account") + username)
|
||||
|
||||
user = db.query(models.User, models.User.Username == username).first()
|
||||
if not user:
|
||||
|
|
|
@ -318,7 +318,8 @@ async def pkgbase_comments_post(
|
|||
|
||||
@router.get("/pkgbase/{name}/comments/{id}/form")
|
||||
@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}. """
|
||||
pkgbase = get_pkg_or_base(name, models.PackageBase)
|
||||
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["comment"] = comment
|
||||
|
||||
if not next:
|
||||
next = f"/pkgbase/{name}"
|
||||
|
||||
context["next"] = next
|
||||
|
||||
form = render_raw_template(
|
||||
request, "partials/packages/comment_form.html", context)
|
||||
return JSONResponse({"form": form})
|
||||
|
@ -341,7 +347,8 @@ async def pkgbase_comment_form(request: Request, name: str, id: int):
|
|||
async def pkgbase_comment_post(
|
||||
request: Request, name: str, id: int,
|
||||
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)
|
||||
db_comment = get_pkgbase_comment(pkgbase, id)
|
||||
|
||||
|
@ -366,14 +373,18 @@ async def pkgbase_comment_post(
|
|||
PackageBase=pkgbase)
|
||||
update_comment_render(db_comment.ID)
|
||||
|
||||
if not next:
|
||||
next = f"/pkgbase/{pkgbase.Name}"
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
@router.post("/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)
|
||||
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.DelTS = now
|
||||
|
||||
return RedirectResponse(f"/pkgbase/{name}",
|
||||
status_code=HTTPStatus.SEE_OTHER)
|
||||
if not next:
|
||||
next = f"/pkgbase/{name}"
|
||||
|
||||
return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
|
||||
|
||||
|
||||
@router.post("/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)
|
||||
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.DelTS = None
|
||||
|
||||
return RedirectResponse(f"/pkgbase/{name}",
|
||||
status_code=HTTPStatus.SEE_OTHER)
|
||||
if not next:
|
||||
next = f"/pkgbase/{name}"
|
||||
|
||||
return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
|
||||
|
||||
|
||||
@router.post("/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)
|
||||
comment = get_pkgbase_comment(pkgbase, id)
|
||||
|
||||
|
@ -434,13 +451,16 @@ async def pkgbase_comment_pin(request: Request, name: str, id: int):
|
|||
with db.begin():
|
||||
comment.PinnedTS = now
|
||||
|
||||
return RedirectResponse(f"/pkgbase/{name}",
|
||||
status_code=HTTPStatus.SEE_OTHER)
|
||||
if not next:
|
||||
next = f"/pkgbase/{name}"
|
||||
|
||||
return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
|
||||
|
||||
|
||||
@router.post("/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)
|
||||
comment = get_pkgbase_comment(pkgbase, id)
|
||||
|
||||
|
@ -455,8 +475,10 @@ async def pkgbase_comment_unpin(request: Request, name: str, id: int):
|
|||
with db.begin():
|
||||
comment.PinnedTS = 0
|
||||
|
||||
return RedirectResponse(f"/pkgbase/{name}",
|
||||
status_code=HTTPStatus.SEE_OTHER)
|
||||
if not next:
|
||||
next = f"/pkgbase/{name}"
|
||||
|
||||
return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
|
||||
|
||||
|
||||
@router.get("/pkgbase/{name}/comaintainers")
|
||||
|
|
|
@ -18,12 +18,19 @@
|
|||
</div>
|
||||
|
||||
{% 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) %}
|
||||
<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
|
||||
| format(
|
||||
'<a href="/pkgbase/{{ comment.PackageBase.Name }}">',
|
||||
'<a href="/pkgbase/%s">' | format(comment.PackageBase.Name),
|
||||
comment.PackageBase.Name,
|
||||
"</a>",
|
||||
'<a href="/account/%s/comments#comment-%s">' | format(
|
||||
|
@ -34,19 +41,27 @@
|
|||
"</a>"
|
||||
) | safe
|
||||
}}
|
||||
</h4>
|
||||
<div id="comment-{{ comment.ID }}-content" class="article-content">
|
||||
<div>
|
||||
{% if comment.RenderedComment %}
|
||||
{{ comment.RenderedComment | safe }}
|
||||
{% else %}
|
||||
{{ comment.Comments }}
|
||||
{% if comment.Editor %}
|
||||
{% set edited_on = comment.EditedTS | dt | as_timezone(timezone) %}
|
||||
<span class="edited">
|
||||
({{ "edited on %s by %s" | tr
|
||||
| format(edited_on.strftime('%Y-%m-%d %H:%M'),
|
||||
'<a href="/account/%s">%s</a>' | format(
|
||||
comment.Editor.Username, comment.Editor.Username))
|
||||
| safe
|
||||
}})
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% include "partials/comment_actions.html" %}
|
||||
</h4>
|
||||
|
||||
{% include "partials/comment_content.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% 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 -->
|
||||
<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>
|
||||
</head>
|
||||
|
|
|
@ -35,76 +35,9 @@
|
|||
}})
|
||||
</span>
|
||||
{% 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]) %}
|
||||
<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 %}
|
||||
{% include "partials/comment_actions.html" %}
|
||||
</h4>
|
||||
<div id="comment-{{ comment.ID }}-content" class="{{ article_cls }}">
|
||||
<div>
|
||||
{% if comment.RenderedComment %}
|
||||
{{ comment.RenderedComment | safe }}
|
||||
{% else %}
|
||||
{{ comment.Comments }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include "partials/comment_content.html" %}
|
||||
{% endif %}
|
||||
|
|
|
@ -13,6 +13,7 @@ Routes:
|
|||
|
||||
<form action="{{ action }}" method="post">
|
||||
<fieldset>
|
||||
<input type="hidden" name="next" value="{{ next }}" />
|
||||
<p>
|
||||
{{ "Git commit identifiers referencing commits in the AUR package "
|
||||
"repository and URLs are converted to links automatically." | tr }}
|
||||
|
|
|
@ -39,72 +39,3 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
{% 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)
|
||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
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,
|
||||
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")}
|
||||
pkgbasename = package.PackageBase.Name
|
||||
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