newsbox/actions/profiles.go

242 lines
6.8 KiB
Go
Raw Normal View History

2023-02-15 16:56:06 +00:00
package actions
import (
"fmt"
"net/http"
"time"
2023-02-15 19:51:54 +00:00
"github.com/acaloiaro/neoq"
2023-02-15 16:56:06 +00:00
"github.com/gobuffalo/buffalo"
"github.com/gobuffalo/pop/v6"
"github.com/gobuffalo/x/responder"
"github.com/gofrs/uuid"
"github.com/pkg/errors"
"newsbox/internal"
"newsbox/models"
)
// Show gets the profile data for a user and renders it
func ShowProfile(c buffalo.Context) error {
// Get the DB connection from the context
tx, ok := c.Value("tx").(*pop.Connection)
if !ok {
return fmt.Errorf("no transaction found")
}
// Allocate an empty User
user := &models.User{}
ownerID := c.Session().Get("current_user_id").(uuid.UUID)
if err := tx.Eager().Find(user, ownerID); err != nil {
return c.Error(http.StatusNotFound, err)
}
loc := internal.ZoneLocation(user.Preferences.TimeZoneUtcOffset)
dt := time.Date(0000, 0, 0, user.Preferences.EmailHourOfDay, 00, 00, 00, loc).Hour()
dz := loc.String()
return responder.Wants("html", func(c buffalo.Context) error {
c.Set("user", user)
c.Set("delivery_time", dt)
c.Set("delivery_time_zone", dz)
return c.Render(http.StatusOK, r.HTML("profiles/show.plush.html"))
}).Wants("json", func(c buffalo.Context) error {
return c.Render(200, r.JSON(user))
}).Wants("xml", func(c buffalo.Context) error {
return c.Render(200, r.XML(user))
}).Respond(c)
}
// Edit renders a edit form for a user's profile
func EditProfile(c buffalo.Context) error {
// Get the DB connection from the context
tx, ok := c.Value("tx").(*pop.Connection)
if !ok {
return fmt.Errorf("no transaction found")
}
// Allocate an empty User
user := &models.User{}
ownerID := c.Session().Get("current_user_id").(uuid.UUID)
if err := tx.Eager().Find(user, ownerID); err != nil {
return c.Error(http.StatusNotFound, err)
}
c.Set("user", user)
return c.Render(http.StatusOK, r.HTML("profiles/edit.plush.html"))
}
// Update updates a user's profile in the DB
func UpdateProfile(c buffalo.Context) error {
// Get the DB connection from the context
tx, ok := c.Value("tx").(*pop.Connection)
if !ok {
return fmt.Errorf("no transaction found")
}
// Allocate an empty User
user := &models.User{}
ownerID := c.Session().Get("current_user_id").(uuid.UUID)
if err := tx.Find(user, ownerID); err != nil {
return c.Error(http.StatusNotFound, err)
}
// The user before any updates take place
origUser := *user
// Bind User to the html form elements
if err := c.Bind(user); err != nil {
return err
}
verrs, err := tx.ValidateAndSave(user)
if err != nil {
return err
}
if verrs.HasAny() {
return responder.Wants("html", func(c buffalo.Context) error {
// Make the errors available inside the html template
c.Set("errors", verrs)
// Render again the edit.html template that the user can
// correct the input.
c.Set("user", user)
return c.Render(http.StatusBadRequest, r.HTML("profiles/edit.plush.html"))
}).Wants("json", func(c buffalo.Context) error {
return c.Render(http.StatusBadRequest, r.JSON(verrs))
}).Wants("xml", func(c buffalo.Context) error {
return c.Render(http.StatusBadRequest, r.XML(verrs))
}).Respond(c)
}
verrs, err = tx.ValidateAndSave(&user.Preferences)
if err != nil {
return err
}
if verrs.HasAny() {
return responder.Wants("html", func(c buffalo.Context) error {
// Make the errors available inside the html template
c.Set("errors", verrs)
// Render again the edit.html template that the user can
// correct the input.
c.Set("user", user)
return c.Render(http.StatusBadRequest, r.HTML("profiles/edit.plush.html"))
}).Wants("json", func(c buffalo.Context) error {
return c.Render(http.StatusBadRequest, r.JSON(verrs))
}).Wants("xml", func(c buffalo.Context) error {
return c.Render(http.StatusBadRequest, r.XML(verrs))
}).Respond(c)
}
var emailChanged bool
if origUser.Email != user.Email {
// Remove any pre-existing email verifications to ensure that the user doesn't end up in an unfixiable state with
// multiple pending verifications
err = tx.RawQuery("DELETE FROM user_email_verifications WHERE owner_id = ?", user.ID).Exec()
if err != nil {
return errors.WithStack(err)
}
uev := &models.UserEmailVerification{OwnerID: user.ID}
err = tx.Save(uev)
if err != nil {
return errors.WithStack(err)
}
invitationAddr := user.Email.Interface().(string)
2023-02-15 19:51:54 +00:00
emailAddrChangedJob := neoq.Job{
Queue: "email_verifications",
Payload: map[string]any{
"recipient": invitationAddr,
"verification_url": fmt.Sprintf("%s/users/email_verification/%s", internal.BaseUrl(), uev.ID),
},
2023-02-15 16:56:06 +00:00
}
2023-02-15 19:51:54 +00:00
nq, _ := neoq.New(internal.DatabaseUrl())
_, err = nq.Enqueue(emailAddrChangedJob)
2023-02-15 16:56:06 +00:00
if err != nil {
return err
}
emailChanged = true
}
return responder.Wants("html", func(c buffalo.Context) error {
if emailChanged {
c.Flash().Add("success", T.Translate(c, "profile.email.updated.success"))
} else {
// If there are no errors set a success message
c.Flash().Add("success", T.Translate(c, "profile.updated.success"))
}
// and redirect to the show page
return c.Redirect(http.StatusSeeOther, "/profile/")
}).Wants("json", func(c buffalo.Context) error {
return c.Render(http.StatusOK, r.JSON(user))
}).Wants("xml", func(c buffalo.Context) error {
return c.Render(http.StatusOK, r.XML(user))
}).Respond(c)
}
// UpdatePassword updates a user's password
func UpdatePassword(c buffalo.Context) error {
// Get the DB connection from the context
tx, ok := c.Value("tx").(*pop.Connection)
if !ok {
return fmt.Errorf("no transaction found")
}
// The user that will be bound to the form values
user := &models.User{}
ownerID := c.Session().Get("current_user_id").(uuid.UUID)
if err := tx.Find(user, ownerID); err != nil {
return c.Error(http.StatusNotFound, err)
}
// Bind user variable to form values
if err := c.Bind(user); err != nil {
return err
}
verrs, err := tx.ValidateAndUpdate(user)
if err != nil {
return err
}
if verrs.HasAny() {
return responder.Wants("html", func(c buffalo.Context) error {
// Make the errors available inside the html template
c.Set("errors", verrs)
// Render again the edit.html template that the user can
// correct the input.
c.Set("user", user)
return c.Render(http.StatusBadRequest, r.HTML("profiles/edit.plush.html"))
}).Wants("json", func(c buffalo.Context) error {
return c.Render(http.StatusBadRequest, r.JSON(verrs))
}).Respond(c)
}
return responder.Wants("html", func(c buffalo.Context) error {
// If there are no errors set a success message
c.Flash().Add("success", T.Translate(c, "user.updated.success"))
// and redirect to the show page
return c.Redirect(http.StatusSeeOther, "/profile/")
}).Wants("json", func(c buffalo.Context) error {
return c.Render(http.StatusOK, r.JSON(user))
}).Wants("xml", func(c buffalo.Context) error {
return c.Render(http.StatusOK, r.XML(user))
}).Respond(c)
}