Skip to content

Commit

Permalink
Merge pull request #1 from knadh/master
Browse files Browse the repository at this point in the history
update
  • Loading branch information
TomBoss committed Feb 15, 2021
2 parents 50549f3 + 99ff64b commit b4fea57
Show file tree
Hide file tree
Showing 6 changed files with 495 additions and 490 deletions.
17 changes: 10 additions & 7 deletions cmd/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,14 @@ func handleViewCampaignMessage(c echo.Context) error {
}

// Get the subscriber.
var sub models.Subscriber
if err := app.queries.GetSubscriber.Get(&sub, 0, subUUID); err != nil {
sub, err := getSubscriber(0, subUUID, "", app)
if err != nil {
if err == sql.ErrNoRows {
return c.Render(http.StatusNotFound, tplMessage,
makeMsgTpl(app.i18n.T("public.notFoundTitle"), "",
app.i18n.T("public.errorFetchingEmail")))
}

app.log.Printf("error fetching campaign subscriber: %v", err)
return c.Render(http.StatusInternalServerError, tplMessage,
makeMsgTpl(app.i18n.T("public.errorTitle"), "",
app.i18n.Ts("public.errorFetchingCampaign")))
Expand Down Expand Up @@ -324,14 +323,18 @@ func handleSubscriptionForm(c echo.Context) error {
// Insert the subscriber into the DB.
req.Status = models.SubscriberStatusEnabled
req.ListUUIDs = pq.StringArray(req.SubListUUIDs)
if _, err := insertSubscriber(req.SubReq, app); err != nil && err != errSubscriberExists {
_, _, hasOptin, err := insertSubscriber(req.SubReq, app)
if err != nil {
return c.Render(http.StatusInternalServerError, tplMessage,
makeMsgTpl(app.i18n.T("public.errorTitle"), "", fmt.Sprintf("%s", err.(*echo.HTTPError).Message)))
}

return c.Render(http.StatusOK, tplMessage,
makeMsgTpl(app.i18n.T("public.subTitle"), "",
app.i18n.Ts("public.subConfirmed")))
msg := "public.subConfirmed"
if hasOptin {
msg = "public.subOptinPending"
}

return c.Render(http.StatusOK, tplMessage, makeMsgTpl(app.i18n.T("public.subTitle"), "", app.i18n.Ts(msg)))
}

// handleLinkRedirect redirects a link UUID to its original underlying link
Expand Down
94 changes: 45 additions & 49 deletions cmd/subscribers.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ func handleGetSubscriber(c echo.Context) error {
id, _ = strconv.Atoi(c.Param("id"))
)

sub, err := getSubscriber(id, app)
if id < 1 {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidID"))
}

sub, err := getSubscriber(id, "", "", app)
if err != nil {
return err
}
Expand Down Expand Up @@ -273,14 +277,13 @@ func handleCreateSubscriber(c echo.Context) error {
}

// Insert the subscriber into the DB.
sub, err := insertSubscriber(req, app)
sub, isNew, _, err := insertSubscriber(req, app)
if err != nil {
if err == errSubscriberExists {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("subscribers.emailExists"))
}

return err
}
if !isNew {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("subscribers.emailExists"))
}

return c.JSON(http.StatusOK, okResp{sub})
}
Expand Down Expand Up @@ -321,11 +324,11 @@ func handleUpdateSubscriber(c echo.Context) error {
}

// Send a confirmation e-mail (if there are any double opt-in lists).
sub, err := getSubscriber(int(id), app)
sub, err := getSubscriber(int(id), "", "", app)
if err != nil {
return err
}
_ = sendOptinConfirmation(sub, []int64(req.Lists), app)
_, _ = sendOptinConfirmation(sub, []int64(req.Lists), app)

return c.JSON(http.StatusOK, okResp{sub})
}
Expand All @@ -335,27 +338,22 @@ func handleSubscriberSendOptin(c echo.Context) error {
var (
app = c.Get("app").(*App)
id, _ = strconv.Atoi(c.Param("id"))
out models.Subscribers
)

if id < 1 {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidID"))
}

// Fetch the subscriber.
err := app.queries.GetSubscriber.Select(&out, id, nil)
out, err := getSubscriber(id, "", "", app)
if err != nil {
app.log.Printf("error fetching subscriber: %v", err)
return echo.NewHTTPError(http.StatusInternalServerError,
app.i18n.Ts("globals.messages.errorFetching",
"name", "{globals.terms.subscribers}", "error", pqErrMsg(err)))
}
if len(out) == 0 {
return echo.NewHTTPError(http.StatusBadRequest,
app.i18n.Ts("globals.messages.notFound", "name", "{globals.terms.subscriber}"))
}

if err := sendOptinConfirmation(out[0], nil, app); err != nil {
if _, err := sendOptinConfirmation(out, nil, app); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError,
app.i18n.T("subscribers.errorSendingOptin"))
}
Expand Down Expand Up @@ -619,56 +617,53 @@ func handleExportSubscriberData(c echo.Context) error {
return c.Blob(http.StatusOK, "application/json", b)
}

// insertSubscriber inserts a subscriber and returns the ID.
func insertSubscriber(req subimporter.SubReq, app *App) (models.Subscriber, error) {
// insertSubscriber inserts a subscriber and returns the ID. The first bool indicates if
// it was a new subscriber, and the second bool indicates if the subscriber was sent an optin confirmation.
func insertSubscriber(req subimporter.SubReq, app *App) (models.Subscriber, bool, bool, error) {
uu, err := uuid.NewV4()
if err != nil {
return req.Subscriber, err
return req.Subscriber, false, false, err
}
req.UUID = uu.String()

err = app.queries.InsertSubscriber.Get(&req.ID,
isNew := true
if err = app.queries.InsertSubscriber.Get(&req.ID,
req.UUID,
req.Email,
strings.TrimSpace(req.Name),
req.Status,
req.Attribs,
req.Lists,
req.ListUUIDs)
if err != nil {
req.ListUUIDs); err != nil {
if pqErr, ok := err.(*pq.Error); ok && pqErr.Constraint == "subscribers_email_key" {
return req.Subscriber, errSubscriberExists
isNew = false
} else {
// return req.Subscriber, errSubscriberExists
app.log.Printf("error inserting subscriber: %v", err)
return req.Subscriber, false, false, echo.NewHTTPError(http.StatusInternalServerError,
app.i18n.Ts("globals.messages.errorCreating",
"name", "{globals.terms.subscriber}", "error", pqErrMsg(err)))
}

app.log.Printf("error inserting subscriber: %v", err)
return req.Subscriber, echo.NewHTTPError(http.StatusInternalServerError,
app.i18n.Ts("globals.messages.errorCreating",
"name", "{globals.terms.subscriber}", "error", pqErrMsg(err)))
}

// Fetch the subscriber's full data.
sub, err := getSubscriber(req.ID, app)
// Fetch the subscriber's full data. If the subscriber already existed and wasn't
// created, the id will be empty. Fetch the details by e-mail then.
sub, err := getSubscriber(req.ID, "", strings.ToLower(req.Email), app)
if err != nil {
return sub, err
return sub, false, false, err
}

// Send a confirmation e-mail (if there are any double opt-in lists).
_ = sendOptinConfirmation(sub, []int64(req.Lists), app)
return sub, nil
num, _ := sendOptinConfirmation(sub, []int64(req.Lists), app)
return sub, isNew, num > 0, nil
}

// getSubscriber gets a single subscriber by ID.
func getSubscriber(id int, app *App) (models.Subscriber, error) {
var (
out models.Subscribers
)
// getSubscriber gets a single subscriber by ID, uuid, or e-mail in that order.
// Only one of these params should have a value.
func getSubscriber(id int, uuid, email string, app *App) (models.Subscriber, error) {
var out models.Subscribers

if id < 1 {
return models.Subscriber{},
echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidID"))
}

if err := app.queries.GetSubscriber.Select(&out, id, nil); err != nil {
if err := app.queries.GetSubscriber.Select(&out, id, uuid, email); err != nil {
app.log.Printf("error fetching subscriber: %v", err)
return models.Subscriber{}, echo.NewHTTPError(http.StatusInternalServerError,
app.i18n.Ts("globals.messages.errorFetching",
Expand Down Expand Up @@ -733,21 +728,22 @@ func exportSubscriberData(id int64, subUUID string, exportables map[string]bool,
}

// sendOptinConfirmation sends a double opt-in confirmation e-mail to a subscriber
// if at least one of the given listIDs is set to optin=double
func sendOptinConfirmation(sub models.Subscriber, listIDs []int64, app *App) error {
// if at least one of the given listIDs is set to optin=double. It returns the number of
// opt-in lists that were found.
func sendOptinConfirmation(sub models.Subscriber, listIDs []int64, app *App) (int, error) {
var lists []models.List

// Fetch double opt-in lists from the given list IDs.
// Get the list of subscription lists where the subscriber hasn't confirmed.
if err := app.queries.GetSubscriberLists.Select(&lists, sub.ID, nil,
pq.Int64Array(listIDs), nil, models.SubscriptionStatusUnconfirmed, models.ListOptinDouble); err != nil {
app.log.Printf("error fetching lists for opt-in: %s", pqErrMsg(err))
return err
return 0, err
}

// None.
if len(lists) == 0 {
return nil
return 0, nil
}

var (
Expand All @@ -764,9 +760,9 @@ func sendOptinConfirmation(sub models.Subscriber, listIDs []int64, app *App) err
if err := app.sendNotification([]string{sub.Email},
app.i18n.T("subscribers.optinSubject"), notifSubscriberOptin, out); err != nil {
app.log.Printf("error sending opt-in e-mail: %s", err)
return err
return 0, err
}
return nil
return len(lists), nil
}

// sanitizeSQLExp does basic sanitisation on arbitrary
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/ListSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<b-tag v-for="l in selectedItems"
:key="l.id"
:class="l.subscriptionStatus"
:closable="!disabled && l.subscriptionStatus !== 'unsubscribed'"
:closable="true"
:data-id="l.id"
@close="removeList(l.id)">
{{ l.name }} <sup>{{ l.subscriptionStatus }}</sup>
Expand Down
1 change: 1 addition & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@
"public.privacyWipeHelp": "Delete all your subscriptions and related data from the database permanently.",
"public.sub": "Subscribe",
"public.subConfirmed": "Subscribed successfully.",
"public.subOptinPending": "An e-mail has been sent to you to confirm your subscription(s).",
"public.subConfirmedTitle": "Confirmed",
"public.subName": "Name (optional)",
"public.subNotFound": "Subscription not found.",
Expand Down
Loading

0 comments on commit b4fea57

Please sign in to comment.