diff --git a/cmd/public.go b/cmd/public.go index 0a871c26e..94c684fdf 100644 --- a/cmd/public.go +++ b/cmd/public.go @@ -47,9 +47,13 @@ type publicTpl struct { Description string } -type unsubTpl struct { +type updateTpl struct { publicTpl SubUUID string + Email string + AM bool + PM bool + Sponsored bool AllowBlocklist bool AllowExport bool AllowWipe bool @@ -156,39 +160,49 @@ func handleViewCampaignMessage(c echo.Context) error { // campaigns link to. func handleSubscriptionPage(c echo.Context) error { var ( - app = c.Get("app").(*App) - campUUID = c.Param("campUUID") - subUUID = c.Param("subUUID") - unsub = c.Request().Method == http.MethodPost - blocklist, _ = strconv.ParseBool(c.FormValue("blocklist")) - out = unsubTpl{} + app = c.Get("app").(*App) + subUUID = c.Param("subUUID") + update = c.Request().Method == http.MethodPost + out = updateTpl{} ) out.SubUUID = subUUID - out.Title = app.i18n.T("public.unsubscribeTitle") + out.Title = app.i18n.T("public.rtmSubscriptionTitle") out.AllowBlocklist = app.constants.Privacy.AllowBlocklist out.AllowExport = app.constants.Privacy.AllowExport out.AllowWipe = app.constants.Privacy.AllowWipe - // Unsubscribe. - if unsub { - // Is blocklisting allowed? - if !app.constants.Privacy.AllowBlocklist { - blocklist = false - } + var sub models.RtmSubscriptions + if err := app.queries.GetRtmSubscriptions.Get(&sub, subUUID); err != nil { + app.log.Printf("GetRtmSubscriptions error: %v", err) + return c.Render(http.StatusInternalServerError, tplMessage, + makeMsgTpl(app.i18n.T("public.errorTitle"), "", + app.i18n.Ts("public.errorProcessingRequest"))) + } - if _, err := app.queries.Unsubscribe.Exec(campUUID, subUUID, blocklist); err != nil { - app.log.Printf("error unsubscribing: %v", err) + out.Email = sub.Email + out.AM = sub.AM + out.PM = sub.PM + out.Sponsored = sub.Sponsored + + // Update subscriptions. + if update { + am := c.FormValue("am") != "" + pm := c.FormValue("pm") != "" + sponsored := c.FormValue("sponsored") != "" + + if _, err := app.queries.UpdateRtmSubscriptions.Exec(subUUID, am, pm, sponsored); err != nil { + app.log.Printf("UpdateRtmSubscriptions error: %v", err) return c.Render(http.StatusInternalServerError, tplMessage, makeMsgTpl(app.i18n.T("public.errorTitle"), "", app.i18n.Ts("public.errorProcessingRequest"))) } return c.Render(http.StatusOK, tplMessage, - makeMsgTpl(app.i18n.T("public.unsubbedTitle"), "", - app.i18n.T("public.unsubbedInfo"))) + makeMsgTpl(app.i18n.T("public.rtmSubscriptionSuccess"), "", + app.i18n.T("public.rtmSubscriptionInfo"))) } - return c.Render(http.StatusOK, "subscription", out) + return c.Render(http.StatusOK, "rtm-subscription", out) } // handleOptinPage renders the double opt-in confirmation page that subscribers diff --git a/cmd/queries.go b/cmd/queries.go index 90c6a9b2f..b5d40cb76 100644 --- a/cmd/queries.go +++ b/cmd/queries.go @@ -33,6 +33,10 @@ type Queries struct { Unsubscribe *sqlx.Stmt `query:"unsubscribe"` ExportSubscriberData *sqlx.Stmt `query:"export-subscriber-data"` + // RTM queries + GetRtmSubscriptions *sqlx.Stmt `query:"get-rtm-subscriptions"` + UpdateRtmSubscriptions *sqlx.Stmt `query:"update-rtm-subscriptions"` + // Non-prepared arbitrary subscriber queries. QuerySubscribers string `query:"query-subscribers"` QuerySubscribersCount string `query:"query-subscribers-count"` diff --git a/i18n/en.json b/i18n/en.json index 419a8c69f..252670ee3 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -302,6 +302,11 @@ "public.unsubHelp": "Do you want to unsubscribe from this mailing list?", "public.unsubTitle": "Unsubscribe", "public.unsubbedInfo": "You have unsubscribed successfully.", + "public.rtmSubscription": "Update", + "public.rtmSubscriptionSuccess": "Success", + "public.rtmSubscriptionTitle": "Update Subscriptions", + "public.rtmSubscriptionHelp": "Select which newsletters you'd like to be subscribed to.", + "public.rtmSubscriptionInfo": "The changes you made to your subscriptions have been succesfully applied.", "public.unsubbedTitle": "Unsubscribed", "public.unsubscribeTitle": "Unsubscribe from mailing list", "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.", diff --git a/models/models.go b/models/models.go index 9388c08c8..9b0dfc3c2 100644 --- a/models/models.go +++ b/models/models.go @@ -270,6 +270,14 @@ type Bounce struct { Total int `db:"total" json:"-"` } +// Data used for RTM subscription management +type RtmSubscriptions struct { + Email string `db:"email" json:"email"` + AM bool `db:"am" json:"am"` + PM bool `db:"pm" json:"pm"` + Sponsored bool `db:"sponsored" json:"sponsored"` +} + // markdown is a global instance of Markdown parser and renderer. var markdown = goldmark.New( goldmark.WithParserOptions( diff --git a/queries.sql b/queries.sql index a155c36bf..7d1fc9c73 100644 --- a/queries.sql +++ b/queries.sql @@ -196,6 +196,132 @@ UPDATE subscriber_lists SET status = 'unsubscribed' WHERE -- If $3 is false, unsubscribe from the campaign's lists, otherwise all lists. CASE WHEN $3 IS FALSE THEN list_id = ANY(SELECT list_id FROM lists) ELSE list_id != 0 END; +-- name: get-rtm-subscriptions +-- Get subscriptions by user and tag +-- $1 = subscribers.uuid +WITH + subscriber AS ( + SELECT id, email + FROM subscribers + WHERE uuid = $1 + ), + subscribed AS ( + SELECT array_agg(list_id) AS lists + FROM subscriber_lists AS l, subscriber AS s + WHERE l.subscriber_id = s.id + AND l.status != 'unsubscribed' + ), + am AS ( + SELECT array_agg(id) AS lists + FROM lists + WHERE 'am' = ANY (tags) + ), + pm AS ( + SELECT array_agg(id) AS lists + FROM lists + WHERE 'pm' = ANY (tags) + ), + sponsored AS ( + SELECT array_agg(id) AS lists + FROM lists + WHERE 'sponsored' = ANY (tags) + ) +SELECT + subscriber.email, + COALESCE(am.lists && subscribed.lists, false) AS am, + COALESCE(pm.lists && subscribed.lists, false) AS pm, + COALESCE(sponsored.lists && subscribed.lists, false) AS sponsored +FROM subscriber, am, pm, sponsored, subscribed; + +-- name: update-rtm-subscriptions +-- Subscribe AM, PM, Sponsored subscriptions by user and tag +-- $1 = subscribers.uuid +-- $2 = bool (am) +-- $3 = bool (pm) +-- $4 = bool (sponsored) +WITH + subscriber AS ( + SELECT id + FROM subscribers + WHERE uuid = $1 + ), + amSubscribe AS ( + INSERT INTO subscriber_lists (subscriber_id, list_id, status, updated_at) + SELECT subscriber.id, am.primary, 'unconfirmed', now() + FROM subscriber, ( + SELECT id AS primary + FROM lists + WHERE 'am' = ANY (tags) + AND 'primary' = ANY (tags) + ) AS am + WHERE true = $2 + ON CONFLICT (subscriber_id, list_id) + DO UPDATE SET status = 'unconfirmed', updated_at = now() + ), + pmSubscribe AS ( + INSERT INTO subscriber_lists (subscriber_id, list_id, status, updated_at) + SELECT subscriber.id, pm.primary, 'unconfirmed', now() + FROM subscriber, ( + SELECT id AS primary + FROM lists + WHERE 'pm' = ANY (tags) + AND 'primary' = ANY (tags) + ) AS pm + WHERE true = $3 + ON CONFLICT (subscriber_id, list_id) + DO UPDATE SET status = 'unconfirmed', updated_at = now() + ), + sponsoredSubscribe AS ( + INSERT INTO subscriber_lists (subscriber_id, list_id, status, updated_at) + SELECT subscriber.id, sponsored.primary, 'unconfirmed', now() + FROM subscriber, ( + SELECT id AS primary + FROM lists + WHERE 'sponsored' = ANY (tags) + AND 'primary' = ANY (tags) + ) AS sponsored + WHERE true = $4 + ON CONFLICT (subscriber_id, list_id) + DO UPDATE SET status = 'unconfirmed', updated_at = now() + ), + amUnsubscribe AS ( + UPDATE subscriber_lists SET + status = 'unsubscribed', updated_at = now() + FROM subscriber, ( + SELECT id + FROM lists + WHERE 'am' = ANY (tags) + ) AS am + WHERE false = $2 + AND subscriber_id = subscriber.id + AND list_id = am.id + ), + pmUnsubscribe AS ( + UPDATE subscriber_lists SET + status = 'unsubscribed', updated_at = now() + FROM subscriber, ( + SELECT id + FROM lists + WHERE 'pm' = ANY (tags) + ) AS pm + WHERE false = $3 + AND subscriber_id = subscriber.id + AND list_id = pm.id + ), + sponsoredUnsubscribe AS ( + UPDATE subscriber_lists SET + status = 'unsubscribed', updated_at = now() + FROM subscriber, ( + SELECT id + FROM lists + WHERE 'sponsored' = ANY (tags) + ) AS sponsored + WHERE false = $4 + AND subscriber_id = subscriber.id + AND list_id = sponsored.id + ) +SELECT true; + -- privacy -- name: export-subscriber-data WITH prof AS ( diff --git a/static/public/templates/rtm-subscription.html b/static/public/templates/rtm-subscription.html new file mode 100644 index 000000000..eb3b009fd --- /dev/null +++ b/static/public/templates/rtm-subscription.html @@ -0,0 +1,70 @@ +{{ define "rtm-subscription" }} +{{ template "header" .}} +
+

{{ L.T "public.rtmSubscriptionTitle" }}

+

{{ L.T "public.rtmSubscriptionHelp" }}

+
+
+

+ + +

+

+ + +

+

+ + +

+

+ +

+
+
+
+ +{{ if or .Data.AllowExport .Data.AllowWipe }} +
+
+

{{ L.T "public.privacyTitle" }}

+ {{ if .Data.AllowExport }} +
+ + +
+ {{ L.T "public.privacyExportHelp" }} +
+ {{ end }} + + {{ if .Data.AllowWipe }} +
+ + +
+ {{ L.T "public.privacyWipeHelp" }} +
+ {{ end }} +

+ +

+
+
+ +{{ end }} + +{{ template "footer" .}} +{{ end }} \ No newline at end of file