Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamline delete process for all deletable items #78

Merged
merged 5 commits into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions internal/webserver/controller/document/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (
)

func (d *Controller) Delete(c *fiber.Ctx) error {
if c.FormValue("slug") == "" {
if c.FormValue("id") == "" {
return fiber.ErrBadRequest
}

document, err := d.idx.Document(c.FormValue("slug"))
document, err := d.idx.Document(c.FormValue("id"))
if err != nil {
fmt.Println(err)
return fiber.ErrBadRequest
Expand Down
4 changes: 2 additions & 2 deletions internal/webserver/controller/user/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

// Delete removes a user from the database
func (u *Controller) Delete(c *fiber.Ctx) error {
user, err := u.repository.FindByUuid(c.FormValue("uuid"))
user, err := u.repository.FindByUuid(c.FormValue("id"))
if err != nil {
return fiber.ErrInternalServerError
}
Expand All @@ -20,7 +20,7 @@ func (u *Controller) Delete(c *fiber.Ctx) error {
return fiber.ErrForbidden
}

if err = u.repository.Delete(c.FormValue("uuid")); err != nil {
if err = u.repository.Delete(c.FormValue("id")); err != nil {
return fiber.ErrInternalServerError
}

Expand Down
35 changes: 22 additions & 13 deletions internal/webserver/embedded/js/delete.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,50 @@
const deleteModal = document.getElementById('deleteModal');
"use strict"

// We use several conventions to be able to use the same code to delete different resources.
// The link that initiates the action needs to have an attribute called data-id which must contain an unique identifier
// for the resource to delete.
// This identifier well be sent to the backend controller specified in the form's action attribute
// under the name "id".
// This code is designed to be used alongside partials/delete-modal.html

const deleteModal = document.getElementById('delete-modal');
const deleteForm = document.getElementById('delete-form');

deleteModal.addEventListener('show.bs.modal', event => {
const link = event.relatedTarget;
const slug = link.getAttribute('data-bs-slug');
const modalInputSlug = deleteModal.querySelector('.slug');
const link = event.relatedTarget
const id = link.getAttribute('data-id')
const modalInput = deleteModal.querySelector('.id')

modalInputSlug.value = slug;
modalInput.value = id;
})

deleteModal.addEventListener('hidden.bs.modal', event => {
let message = document.getElementById('delete-document-message');
let message = document.getElementById('error-message-container');
message.classList.add("visually-hidden");
})

function remove(errorMessage) {
deleteForm.addEventListener('submit', event => {
event.preventDefault();
form = document.getElementById("delete-form");
fetch('/document', {
fetch(deleteForm.getAttribute("action"), {
method: "DELETE",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'slug': form.elements['slug'].value,
'id': deleteForm.elements['id'].value,
})
})
.then((response) => {
if (response.ok) {
location.reload();
} else {
message = document.getElementById("delete-document-message")
message = document.getElementById("error-message-container")
message.classList.remove("visually-hidden");
message.innerHTML = errorMessage;
message.innerHTML = deleteForm.getAttribute("data-error-message");
}
})
.catch(function (error) {
// Catch errors
console.log(error);
});
}
})
3 changes: 0 additions & 3 deletions internal/webserver/embedded/views/highlights.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,3 @@ <h1 class="text-center">{{t .Lang "%s highlights" .Session.Name}}</h1>
{{ $length := len .Paginator.Pages }} {{ if gt $length 1 }}
{{template "partials/pagination" .}}
{{end}}

{{template "partials/delete-modal" .}}

4 changes: 2 additions & 2 deletions internal/webserver/embedded/views/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ <h2>{{t .Lang "Your highlights" }}</h2>
{{t $lang "Actions"}}
</button>
{{ template "partials/actions" dict "Lang" $lang "Document" $doc "EmailSendingConfigured"
$emailSendingConfigured "Index" $i "Session" $session "EmailFrom" $emailFrom "OnDehighlight" "remove"}}
$emailSendingConfigured "Session" $session "EmailFrom" $emailFrom "OnDehighlight" "remove"}}
</div>
</div>
</div>
{{end}}
</div>
{{template "partials/delete-modal" .}}
{{template "partials/delete-modal" dict "Lang" .Lang "Action" "/document" "ModalHeader" "Delete document" "ModalBody" "Are you sure you want to delete this document?" "ModalErrorMessage" "There was an error deleting the document"}}

<script type="text/javascript" src="/js/delete.js"></script>
<script type="text/javascript" src="/js/actions.js"></script>
Expand Down
2 changes: 1 addition & 1 deletion internal/webserver/embedded/views/partials/actions.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
{{end}}
{{if and .Session (eq .Session.Role 2)}}
<div class="dropdown-divider"></div>
<li><a href="#" data-bs-toggle="modal" data-bs-target="#deleteModal" data-bs-slug="{{.Document.Slug}}" data-bs-index="{{.Index}}" class="dropdown-item">
<li><a href="#" data-bs-toggle="modal" data-bs-target="#delete-modal" data-id="{{.Document.Slug}}" class="dropdown-item">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash3-fill" viewBox="0 0 16 16">
<path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5Zm-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5ZM4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06Zm6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528ZM8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5Z"/>
</svg>
Expand Down
12 changes: 6 additions & 6 deletions internal/webserver/embedded/views/partials/delete-modal.html
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModal" aria-hidden="true">
<div class="modal fade" id="delete-modal" tabindex="-1" aria-labelledby="delete-modal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="post" action="/delete" onsubmit='remove({{t .Lang "There was an error deleting the document"}})' id="delete-form">
<form action="{{.Action}}" data-error-message='{{t .Lang .ModalErrorMessage}}' id="delete-form">
<div class="modal-header">
<h1 class="modal-title fs-5" id="deleteModalLabel">{{t .Lang "Delete document"}}</h1>
<h1 class="modal-title fs-5" id="delete-modal-label">{{t .Lang .ModalHeader}}</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert alert-danger visually-hidden" role="alert" id="delete-document-message">
<div class="alert alert-danger visually-hidden" role="alert" id="error-message-container">
</div>
<p>{{ t .Lang "Are you sure you want to delete this document?"}}</p>
<p>{{ t .Lang .ModalBody}}</p>
<p>{{ t .Lang "This action cannot be undone"}}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{t .Lang
"Cancel"}}</button>
<button type="submit" class="btn btn-primary">{{t .Lang "Delete"}}</button>
</div>
<input type="hidden" name="slug" value="" class="slug">
<input type="hidden" name="id" value="" class="id">
</form>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions internal/webserver/embedded/views/partials/docs-list.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ <h4 class="text-start">
{{t $lang "Actions"}}
</button>
{{ template "partials/actions" dict "Lang" $lang "Document" $document "EmailSendingConfigured"
$emailSendingConfigured "Index" $i "Session" $session "EmailFrom" $emailFrom "OnDehighlight" "remove"}}
$emailSendingConfigured "Session" $session "EmailFrom" $emailFrom "OnDehighlight" "remove"}}
</div>
</div>
</div>
Expand Down Expand Up @@ -91,7 +91,7 @@ <h5>{{t $lang "Unknown author"}}</h5>
{{end}} {{end}}
</div>

{{template "partials/delete-modal" .}}
{{template "partials/delete-modal" dict "Lang" $lang "Action" "/document" "ModalHeader" "Delete document" "ModalBody" "Are you sure you want to delete this document?" "ModalErrorMessage" "There was an error deleting the document"}}

<script type="text/javascript" src="/js/delete.js"></script>
<script type="text/javascript" src="/js/actions.js"></script>
Expand Down
65 changes: 3 additions & 62 deletions internal/webserver/embedded/views/users/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ <h2>{{t $lang "Users"}}</h2>
{{end}}
</div>
{{ if not (and (eq $admins 1) (eq $user.Role 2)) }}
<a href="#" data-bs-toggle="modal" data-bs-target="#deleteModal" data-bs-uuid="{{$user.Uuid}}">
<a href="#" data-bs-toggle="modal" data-bs-target="#delete-modal" data-id="{{$user.Uuid}}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash3-fill" viewBox="0 0 16 16">
<path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5Zm-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5ZM4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06Zm6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528ZM8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5Z"/>
</svg>
Expand All @@ -40,65 +40,6 @@ <h2>{{t $lang "Users"}}</h2>
{{template "partials/pagination" .}}
{{end}}

<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="post" action="/users/delete" onsubmit='remove({{t .Lang "There was an error deleting the user, please try again later"}})' id="delete-form">
<div class="modal-header">
<h1 class="modal-title fs-5" id="deleteModalLabel">{{t $lang "Delete user"}}</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert alert-danger visually-hidden" role="alert" id="delete-user-message">
</div>
{{t $lang "Are you sure you want to delete this user?"}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{t $lang
"Cancel"}}</button>
<button type="submit" class="btn btn-primary">{{t $lang "Delete"}}</button>
</div>
<input type="hidden" name="uuid" value="" class="uuid">
</form>
</div>
</div>
</div>


<script type="text/javascript">
const deleteModal = document.getElementById('deleteModal')
deleteModal.addEventListener('show.bs.modal', event => {
const link = event.relatedTarget
const uuid = link.getAttribute('data-bs-uuid')
const modalInput = deleteModal.querySelector('.uuid')

modalInput.value = uuid;
})
{{template "partials/delete-modal" dict "Lang" $lang "Action" "/users" "ModalHeader" "Delete user" "ModalBody" "Are you sure you want to delete this user?" "ModalErrorMessage" "There was an error deleting the user, try again later"}}

function remove(errorMessage) {
event.preventDefault();
form = document.getElementById("delete-form");
fetch('/users', {
method: "DELETE",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'uuid': form.elements['uuid'].value,
})
})
.then((response) => {
if (response.ok) {
location.reload();
} else {
message = document.getElementById("delete-user-message")
message.classList.remove("visually-hidden");
message.innerHTML = errorMessage;
}
})
.catch(function (error) {
// Catch errors
console.log(error);
});
}
</script>
<script type="text/javascript" src="/js/delete.js"></script>
6 changes: 5 additions & 1 deletion internal/webserver/highlights_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ func TestHighlights(t *testing.T) {
t.Fatalf("Unexpected error: %v", err.Error())
}

data = url.Values{
"id": {"john-doe-test-epub"},
}

_, err = deleteRequest(data, adminCookie, app, "/document")
if err != nil {
t.Fatalf("Unexpected error: %v", err.Error())
Expand Down Expand Up @@ -148,7 +152,7 @@ func TestHighlights(t *testing.T) {
}

data := url.Values{
"uuid": {regularUser.Uuid},
"id": {regularUser.Uuid},
}

_, err = deleteRequest(data, adminCookie, app, "/users")
Expand Down
2 changes: 1 addition & 1 deletion internal/webserver/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func TestRemoveDocument(t *testing.T) {
)

data := url.Values{
"slug": {tcase.slug},
"id": {tcase.slug},
}

cookie, err := login(app, tcase.email, tcase.password)
Expand Down
6 changes: 3 additions & 3 deletions internal/webserver/user_management_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ func TestUserManagement(t *testing.T) {
})

data = url.Values{
"uuid": {testUser.Uuid},
"id": {testUser.Uuid},
}

t.Run("Try to delete a user without an active session", func(t *testing.T) {
Expand Down Expand Up @@ -264,7 +264,7 @@ func TestUserManagement(t *testing.T) {

t.Run("Try to delete the only existing admin user", func(t *testing.T) {
data = url.Values{
"uuid": {adminUser.Uuid},
"id": {adminUser.Uuid},
}
response, err := deleteRequest(data, adminCookie, app, "/users")
if response == nil {
Expand All @@ -276,7 +276,7 @@ func TestUserManagement(t *testing.T) {

t.Run("Try to delete a non existing user with an admin session", func(t *testing.T) {
data = url.Values{
"uuid": {"abcde"},
"id": {"abcde"},
}

response, err := deleteRequest(data, adminCookie, app, "/users")
Expand Down
Loading