From 52f449b7d01acdea0e9a395d5a6bfe7e7646c9fc Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Sun, 29 Dec 2024 18:07:25 +0100 Subject: [PATCH] Improved actions code and behaviour --- internal/webserver/controller/auth/signin.go | 14 +++- .../controller/document/controller.go | 2 +- .../webserver/controller/document/detail.go | 45 +++++++---- .../webserver/controller/document/reader.go | 4 +- .../webserver/controller/document/send.go | 2 +- .../webserver/controller/document/upload.go | 10 +-- .../controller/highlight/controller.go | 2 +- .../webserver/controller/home/controller.go | 2 +- internal/webserver/controller/home/index.go | 2 +- internal/webserver/embedded/js/send-email.js | 80 +++++++------------ .../webserver/embedded/translations/es.yml | 6 +- .../webserver/embedded/translations/fr.yml | 6 +- .../webserver/embedded/views/document.html | 4 +- internal/webserver/embedded/views/layout.html | 2 +- .../embedded/views/partials/actions.html | 18 ++--- .../embedded/views/partials/docs-list.html | 4 +- .../embedded/views/partials/main.html | 9 +++ internal/webserver/infrastructure/noemail.go | 2 +- internal/webserver/infrastructure/smtp.go | 4 +- .../webserver/infrastructure/smtp_mock.go | 2 +- internal/webserver/webserver.go | 4 +- internal/webserver/webserver_test.go | 2 +- 22 files changed, 116 insertions(+), 110 deletions(-) diff --git a/internal/webserver/controller/auth/signin.go b/internal/webserver/controller/auth/signin.go index 17dbd87a..e881bb98 100644 --- a/internal/webserver/controller/auth/signin.go +++ b/internal/webserver/controller/auth/signin.go @@ -6,6 +6,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/golang-jwt/jwt/v4" + "github.com/svera/coreander/v4/internal/webserver/infrastructure" "github.com/svera/coreander/v4/internal/webserver/model" ) @@ -23,9 +24,16 @@ func (a *Controller) SignIn(c *fiber.Ctx) error { } if user == nil || user.Password != model.Hash(c.FormValue("password")) { + emailSendingConfigured := true + if _, ok := a.sender.(*infrastructure.NoEmail); ok { + emailSendingConfigured = false + } + return c.Status(fiber.StatusUnauthorized).Render("auth/login", fiber.Map{ - "Title": "Login", - "Error": "Wrong email or password", + "Title": "Login", + "Error": "Wrong email or password", + "EmailSendingConfigured": emailSendingConfigured, + "DisableLoginLink": true, }, "layout") } @@ -46,7 +54,7 @@ func (a *Controller) SignIn(c *fiber.Ctx) error { }) referer := string(c.Context().Referer()) - if referer != "" && !strings.HasSuffix(referer, "sessions/new") { + if referer != "" && !strings.Contains(referer, "/sessions") { return c.Redirect(referer) } diff --git a/internal/webserver/controller/document/controller.go b/internal/webserver/controller/document/controller.go index 0c7a7214..12d7005f 100644 --- a/internal/webserver/controller/document/controller.go +++ b/internal/webserver/controller/document/controller.go @@ -10,7 +10,7 @@ import ( const relatedDocuments = 4 type Sender interface { - SendDocument(address string, libraryPath string, fileName string) error + SendDocument(address, subject, libraryPath, fileName string) error From() string } diff --git a/internal/webserver/controller/document/detail.go b/internal/webserver/controller/document/detail.go index f93966ed..8bab9127 100644 --- a/internal/webserver/controller/document/detail.go +++ b/internal/webserver/controller/document/detail.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/gofiber/fiber/v2" + "github.com/svera/coreander/v4/internal/index" "github.com/svera/coreander/v4/internal/webserver/infrastructure" "github.com/svera/coreander/v4/internal/webserver/model" ) @@ -41,25 +42,12 @@ func (d *Controller) Detail(c *fiber.Ctx) error { return fiber.ErrNotFound } - title := fmt.Sprintf("%s | Coreander", document.Title) + title := fmt.Sprintf("%s", document.Title) if len(document.Authors) > 0 { - title = fmt.Sprintf("%s - %s | Coreander", strings.Join(document.Authors, ", "), document.Title) + title = fmt.Sprintf("%s - %s", strings.Join(document.Authors, ", "), document.Title) } - sameSubjects, err := d.idx.SameSubjects(document.Slug, relatedDocuments) - if err != nil { - fmt.Println(err) - } - - sameAuthors, err := d.idx.SameAuthors(document.Slug, relatedDocuments) - if err != nil { - fmt.Println(err) - } - - sameSeries, err := d.idx.SameSeries(document.Slug, relatedDocuments) - if err != nil { - fmt.Println(err) - } + sameSubjects, sameAuthors, sameSeries := d.related(document.Slug, (int(session.ID))) if session.ID > 0 { document = d.hlRepository.Highlighted(int(session.ID), document) @@ -82,3 +70,28 @@ func (d *Controller) Detail(c *fiber.Ctx) error { "Message": msg, }, "layout") } + +func (d *Controller) related(slug string, sessionID int) (sameSubjects, sameAuthors, sameSeries []index.Document) { + var err error + if sameSubjects, err = d.idx.SameSubjects(slug, relatedDocuments); err != nil { + fmt.Println(err) + } + for i := range sameSubjects { + sameSubjects[i] = d.hlRepository.Highlighted(sessionID, sameSubjects[i]) + } + + if sameAuthors, err = d.idx.SameAuthors(slug, relatedDocuments); err != nil { + fmt.Println(err) + } + for i := range sameAuthors { + sameAuthors[i] = d.hlRepository.Highlighted(sessionID, sameAuthors[i]) + } + + if sameSeries, err = d.idx.SameSeries(slug, relatedDocuments); err != nil { + fmt.Println(err) + } + for i := range sameSeries { + sameSeries[i] = d.hlRepository.Highlighted(sessionID, sameSeries[i]) + } + return sameSubjects, sameAuthors, sameSeries +} diff --git a/internal/webserver/controller/document/reader.go b/internal/webserver/controller/document/reader.go index 68e4f404..d97a357d 100644 --- a/internal/webserver/controller/document/reader.go +++ b/internal/webserver/controller/document/reader.go @@ -27,10 +27,10 @@ func (d *Controller) Reader(c *fiber.Ctx) error { template := "reader" - title := fmt.Sprintf("%s | Coreander", document.Title) + title := fmt.Sprintf("%s", document.Title) authors := strings.Join(document.Authors, ", ") if authors != "" { - title = fmt.Sprintf("%s - %s | Coreander", authors, document.Title) + title = fmt.Sprintf("%s - %s", authors, document.Title) } return c.Render(template, fiber.Map{ "Title": title, diff --git a/internal/webserver/controller/document/send.go b/internal/webserver/controller/document/send.go index ba6b6c14..3a1cf971 100644 --- a/internal/webserver/controller/document/send.go +++ b/internal/webserver/controller/document/send.go @@ -30,5 +30,5 @@ func (d *Controller) Send(c *fiber.Ctx) error { return fiber.ErrInternalServerError } - return d.sender.SendDocument(c.FormValue("email"), d.config.LibraryPath, document.ID) + return d.sender.SendDocument(c.FormValue("email"), document.Title, d.config.LibraryPath, document.ID) } diff --git a/internal/webserver/controller/document/upload.go b/internal/webserver/controller/document/upload.go index 5bda91bd..d29de62f 100644 --- a/internal/webserver/controller/document/upload.go +++ b/internal/webserver/controller/document/upload.go @@ -17,7 +17,7 @@ import ( func (d *Controller) UploadForm(c *fiber.Ctx) error { return c.Render("upload", fiber.Map{ - "Title": "Coreander", + "Title": "Upload document", "MaxSize": d.config.UploadDocumentMaxSize, }, "layout") } @@ -27,7 +27,7 @@ func (d *Controller) Upload(c *fiber.Ctx) error { if err != nil { if errors.Is(err, fasthttp.ErrMissingFile) { return c.Status(fiber.StatusBadRequest).Render("upload", fiber.Map{ - "Title": "Coreander", + "Title": "Upload document", "Error": "Invalid file type", }, "layout") } @@ -36,21 +36,21 @@ func (d *Controller) Upload(c *fiber.Ctx) error { allowedTypes := []string{"application/epub+zip", "application/pdf"} if !slices.Contains(allowedTypes, file.Header.Get("Content-Type")) { return c.Status(fiber.StatusBadRequest).Render("upload", fiber.Map{ - "Title": "Coreander", + "Title": "Upload document", "Error": "Invalid file type", }, "layout") } if file.Size > int64(d.config.UploadDocumentMaxSize*1024*1024) { return c.Status(fiber.StatusRequestEntityTooLarge).Render("upload", fiber.Map{ - "Title": "Coreander", + "Title": "Upload Document", "Error": fmt.Sprintf("Document too large, the maximum allowed size is %d megabytes", d.config.UploadDocumentMaxSize), }, "layout") } destination := filepath.Join(d.config.LibraryPath, file.Filename) internalServerErrorStatus := c.Status(fiber.StatusInternalServerError).Render("upload", fiber.Map{ - "Title": "Coreander", + "Title": "Upload Document", "Error": "Error uploading document", }, "layout") diff --git a/internal/webserver/controller/highlight/controller.go b/internal/webserver/controller/highlight/controller.go index 28c38ee8..c376a973 100644 --- a/internal/webserver/controller/highlight/controller.go +++ b/internal/webserver/controller/highlight/controller.go @@ -27,7 +27,7 @@ type usersRepository interface { } type Sender interface { - SendDocument(address string, libraryPath string, fileName string) error + SendDocument(address, subject, libraryPath, fileName string) error From() string } diff --git a/internal/webserver/controller/home/controller.go b/internal/webserver/controller/home/controller.go index 179797d5..abd49dfb 100644 --- a/internal/webserver/controller/home/controller.go +++ b/internal/webserver/controller/home/controller.go @@ -6,7 +6,7 @@ import ( ) type Sender interface { - SendDocument(address string, libraryPath string, fileName string) error + SendDocument(address, subject, libraryPath, fileName string) error From() string } diff --git a/internal/webserver/controller/home/index.go b/internal/webserver/controller/home/index.go index a66a0d65..2462be09 100644 --- a/internal/webserver/controller/home/index.go +++ b/internal/webserver/controller/home/index.go @@ -22,7 +22,7 @@ func (d *Controller) Index(c *fiber.Ctx) error { return c.Render("index", fiber.Map{ "Count": count, - "Title": "Coreander", + "Title": "Home", "EmailSendingConfigured": emailSendingConfigured, "EmailFrom": d.sender.From(), "HomeNavbar": true, diff --git a/internal/webserver/embedded/js/send-email.js b/internal/webserver/embedded/js/send-email.js index 8e8ab8d2..3cb6d8af 100644 --- a/internal/webserver/embedded/js/send-email.js +++ b/internal/webserver/embedded/js/send-email.js @@ -1,63 +1,39 @@ "use strict" -Array.from(document.getElementsByClassName("send-email")).forEach(form => { - form.addEventListener("submit", event => { - event.preventDefault(); +document.body.addEventListener('htmx:configRequest', function (evt) { + const post = evt.detail.elt.getAttribute("hx-post") + + if (!post || !post.includes("/send")) { + return + } - const submit = form.querySelector('button'); - let sendIcon = form.querySelector('.bi-send-fill'); - let spinner = form.querySelector('.spinner-border'); + let text = evt.detail.elt.getAttribute("data-success-message") + text = text.replace("${email}", evt.detail.parameters['email']) + evt.detail.elt.setAttribute("data-success-message", text) - submit.setAttribute("disabled", true); - spinner.classList.remove("visually-hidden"); - sendIcon.classList.add("visually-hidden"); - fetch(form.getAttribute("action"), { - method: "POST", - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - }, - body: new URLSearchParams({ - 'email': form.elements[0].value - }) - }) - .then((response) => { - let message = form.querySelector(".send-email-message") - message.classList.remove("visually-hidden"); - if (!response.ok) { - if (response.status == "403") { - location.reload() - } else { - message.innerHTML = form.getAttribute("data-error-message"); - message.classList.remove("text-success"); - message.classList.add("text-danger"); - } - } else { - message.innerHTML = form.getAttribute("data-success-message"); - message.classList.remove("text-danger"); - message.classList.add("text-success"); - } - submit.removeAttribute("disabled"); - sendIcon.classList.remove("visually-hidden"); - spinner.classList.add("visually-hidden"); - }) - .catch(function (error) { - // Catch errors - console.log(error); - }); - }); -}); + text = evt.detail.elt.getAttribute("data-error-message") + text = text.replace("${email}", evt.detail.parameters['email']) + evt.detail.elt.setAttribute("data-error-message", text) +}) document.body.addEventListener('htmx:responseError', function (evt) { - const parent = evt.detail.elt.closest(".actions").parentNode; - parent.querySelector(".quick-email-error").classList.remove("d-none"); - parent.querySelector(".quick-email-success").classList.add("d-none"); -}); + if (evt.detail.xhr.status === 403) { + location.reload() + return + } + + const toast = document.getElementById('live-toast') + toast.querySelector(".toast-body").innerHTML = evt.detail.elt.getAttribute("data-error-message") + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toast) + toastBootstrap.show() +}) document.body.addEventListener('htmx:afterRequest', function (evt) { const post = evt.detail.elt.getAttribute("hx-post") if (!evt.detail.failed && post && post.includes("/send")) { - const parent = evt.detail.elt.closest(".actions").parentNode; - parent.querySelector(".quick-email-error").classList.add("d-none"); - parent.querySelector(".quick-email-success").classList.remove("d-none"); + const toast = document.getElementById('live-toast') + toast.querySelector(".toast-body").innerHTML = evt.detail.elt.getAttribute("data-success-message") + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toast) + toastBootstrap.show() } -}); +}) diff --git a/internal/webserver/embedded/translations/es.yml b/internal/webserver/embedded/translations/es.yml index 704a8379..1a15858b 100644 --- a/internal/webserver/embedded/translations/es.yml +++ b/internal/webserver/embedded/translations/es.yml @@ -21,8 +21,8 @@ "Send": Enviar "Actions": Acciones "Send to email unavailable, no email service configured": "Enviar a correo electrónico no disponible, no se ha configurado ningún servicio" -"Document sent successfully": "Documento enviado con éxito" -"There was an error sending the document, please try again later": "Hubo un error al enviar el documento, por favor, vuelva a intentarlo más tarde" +"%s sent to ${email}": "%s enviado a ${email}" +"There was an error sending %s to ${email}, please try again later": "Hubo un error al enviar %s a ${email}, por favor, vuelva a intentarlo más tarde" "Read": "Leer" "Users": "Usuarios" "Username": "Usuario" @@ -136,3 +136,5 @@ "Unexpected error, check your connection and try to refresh the page.": "Error inesperado, comprueba tu conexión y recarga la página." "Unexpected server error": "Error inesperado en el servidor" "Titles by %s": "Títulos de %s" +"Send to %s": "Enviar a %s" +"Home": "Inicio" diff --git a/internal/webserver/embedded/translations/fr.yml b/internal/webserver/embedded/translations/fr.yml index db448ce1..5e591288 100644 --- a/internal/webserver/embedded/translations/fr.yml +++ b/internal/webserver/embedded/translations/fr.yml @@ -21,8 +21,8 @@ "Send": Envoyer "Actions": Actions "Send to email unavailable, no email service configured": "Envoyer par e-mail indisponible, aucun service de messagerie configuré" -"Document sent successfully": "Document envoyé avec succès" -"There was an error sending the document, please try again later": "Une erreur s'est produite lors de l'envoi du document, veuillez réessayer ultérieurement" +"%s sent to ${email}": "%s envoyé à ${email}" +"There was an error sending %s to ${email}, please try again later": "Une erreur s'est produite lors de l'envoi du %s à ${email}, veuillez réessayer ultérieurement" "Read": "Lire" "Users": "Utilisateurs" "Username": "Nom d'utilisateur" @@ -136,3 +136,5 @@ "Unexpected error, check your connection and try to refresh the page.": "Erreur inattendue, vérifiez votre connexion et essayez d'actualiser la page." "Unexpected server error": "Erreur de serveur inattendue" "Titles by %s": "Titres par %s" +"Send to %s": "Envoyer à %s" +"Home": "Accueil" diff --git a/internal/webserver/embedded/views/document.html b/internal/webserver/embedded/views/document.html index df785229..84ced8b9 100644 --- a/internal/webserver/embedded/views/document.html +++ b/internal/webserver/embedded/views/document.html @@ -5,13 +5,13 @@ {{$document := .Document}}
-
+
{{template "partials/cover" dict "Lang" .Lang "Document" .Document "Session" .Session "DisableCoverMainLink" true}}
{{template "partials/actions" dict "Lang" $lang "Document" .Document "Session" $session "EmailSendingConfigured" .EmailSendingConfigured "EmailFrom" $emailFrom}}
-
+
{{ if ne .Document.Series "" }} {{$seriesTitle := t $lang "Search for more titles belonging to %s" diff --git a/internal/webserver/embedded/views/layout.html b/internal/webserver/embedded/views/layout.html index d4a7bf8e..f2aec82a 100644 --- a/internal/webserver/embedded/views/layout.html +++ b/internal/webserver/embedded/views/layout.html @@ -6,7 +6,7 @@ - {{t .Lang .Title}} + {{t .Lang .Title}} | Coreander diff --git a/internal/webserver/embedded/views/partials/actions.html b/internal/webserver/embedded/views/partials/actions.html index f931f897..a13a9d7c 100644 --- a/internal/webserver/embedded/views/partials/actions.html +++ b/internal/webserver/embedded/views/partials/actions.html @@ -1,7 +1,7 @@ {{end}}
-

{{t .Lang "There was an error sending the document, please try again later"}}

-

{{t .Lang "Document sent successfully"}}

diff --git a/internal/webserver/embedded/views/partials/docs-list.html b/internal/webserver/embedded/views/partials/docs-list.html index 183b68bf..5d57c3aa 100644 --- a/internal/webserver/embedded/views/partials/docs-list.html +++ b/internal/webserver/embedded/views/partials/docs-list.html @@ -7,13 +7,13 @@ {{if .Results}} {{range $i, $document := .Results.Hits}}
-
+
{{template "partials/cover" dict "Lang" $lang "Document" $document "Session" $session}}
{{template "partials/actions" dict "Lang" $lang "Document" $document "Session" $session "EmailSendingConfigured" $emailSendingConfigured "EmailFrom" $emailFrom}}
-
+
{{ if ne $document.Series "" }} {{$seriesTitle := t $lang "Search for more titles belonging to %s" diff --git a/internal/webserver/embedded/views/partials/main.html b/internal/webserver/embedded/views/partials/main.html index 4f7dc415..63440a14 100644 --- a/internal/webserver/embedded/views/partials/main.html +++ b/internal/webserver/embedded/views/partials/main.html @@ -42,3 +42,12 @@ {{embed}}
+ +
+ +
diff --git a/internal/webserver/infrastructure/noemail.go b/internal/webserver/infrastructure/noemail.go index 088ea06b..794dcca3 100644 --- a/internal/webserver/infrastructure/noemail.go +++ b/internal/webserver/infrastructure/noemail.go @@ -7,7 +7,7 @@ func (s *NoEmail) Send(address, subject, body string) error { return nil } -func (s *NoEmail) SendDocument(address string, libraryPath string, fileName string) error { +func (s *NoEmail) SendDocument(address, subject, libraryPath, fileName string) error { return nil } diff --git a/internal/webserver/infrastructure/smtp.go b/internal/webserver/infrastructure/smtp.go index ee3f9d64..e1eb349b 100644 --- a/internal/webserver/infrastructure/smtp.go +++ b/internal/webserver/infrastructure/smtp.go @@ -22,8 +22,8 @@ func (s *SMTP) Send(address, subject, body string) error { } // SendDocument sends an email with the designated file attached to it to the chosen address -func (s *SMTP) SendDocument(address string, libraryPath string, fileName string) error { - m := s.compose(address, "", "") +func (s *SMTP) SendDocument(address, subject, libraryPath, fileName string) error { + m := s.compose(address, subject, "") m.Attach(fmt.Sprintf("%s/%s", libraryPath, fileName)) return s.send(m) diff --git a/internal/webserver/infrastructure/smtp_mock.go b/internal/webserver/infrastructure/smtp_mock.go index 9952446c..7dd62836 100644 --- a/internal/webserver/infrastructure/smtp_mock.go +++ b/internal/webserver/infrastructure/smtp_mock.go @@ -18,7 +18,7 @@ func (s *SMTPMock) Send(address, subject, body string) error { return nil } -func (s *SMTPMock) SendDocument(address string, libraryPath string, fileName string) error { +func (s *SMTPMock) SendDocument(address, subject, libraryPath, fileName string) error { defer s.Wg.Done() s.mu.Lock() diff --git a/internal/webserver/webserver.go b/internal/webserver/webserver.go index 7683d6bd..3ffe4edf 100644 --- a/internal/webserver/webserver.go +++ b/internal/webserver/webserver.go @@ -50,7 +50,7 @@ type Config struct { type Sender interface { Send(address, subject, body string) error - SendDocument(address string, libraryPath string, fileName string) error + SendDocument(address, subject, libraryPath, fileName string) error From() string } @@ -182,7 +182,7 @@ func errorHandler(c *fiber.Ctx, err error) error { fmt.Sprintf("errors/%d", code), fiber.Map{ "Lang": chooseBestLanguage(c), - "Title": "Coreander", + "Title": "Error", "Version": c.App().Config().AppName, "Session": session, }, diff --git a/internal/webserver/webserver_test.go b/internal/webserver/webserver_test.go index 5fea2e3f..fd92ec5e 100644 --- a/internal/webserver/webserver_test.go +++ b/internal/webserver/webserver_test.go @@ -163,7 +163,7 @@ func mustReturnForbiddenAndShowLogin(response *http.Response, t *testing.T) { if err != nil { t.Fatal(err) } - if selection != "Login" { + if selection != "Login | Coreander" { t.Errorf("Expected login page, received %s", selection) } }