Add new privacy option 'Record opt-in IP' to record IP address of optin confirmation.

- Add new 'Subscriptions' table on the subscriber list form that shows subs,
  IP, and other data.
- Add new `meta` JSONB field to `subscriber_lsts` table.

Closes #1329.
This commit is contained in:
Kailash Nadh 2023-07-22 13:28:45 +05:30
parent b26950c427
commit ad80c716f9
34 changed files with 122 additions and 41 deletions

View file

@ -69,6 +69,7 @@ type constants struct {
AllowBlocklist bool `koanf:"allow_blocklist"`
AllowExport bool `koanf:"allow_export"`
AllowWipe bool `koanf:"allow_wipe"`
RecordOptinIP bool `koanf:"record_optin_ip"`
Exportable map[string]bool `koanf:"-"`
DomainBlocklist []string `koanf:"-"`
} `koanf:"privacy"`

View file

@ -374,7 +374,16 @@ func handleOptinPage(c echo.Context) error {
// Confirm.
if confirm {
if err := app.core.ConfirmOptionSubscription(subUUID, out.ListUUIDs); err != nil {
meta := models.JSON{}
if app.constants.Privacy.RecordOptinIP {
if h := c.Request().Header.Get("X-Forwarded-For"); h != "" {
meta["optin_ip"] = h
} else if h := c.Request().RemoteAddr; h != "" {
meta["optin_ip"] = strings.Split(h, ":")[0]
}
}
if err := app.core.ConfirmOptionSubscription(subUUID, out.ListUUIDs, meta); err != nil {
app.log.Printf("error unsubscribing: %v", err)
return c.Render(http.StatusInternalServerError, tplMessage,
makeMsgTpl(app.i18n.T("public.errorTitle"), "", app.i18n.Ts("public.errorProcessingRequest")))

View file

@ -75,39 +75,41 @@
</div>
</b-field>
<div>
<div class="mb-5" v-if="data.lists">
<h5>{{ $tc('globals.terms.subscriptions', 2) }} ({{ data.lists.length }})</h5>
<div class="mb-5">
<b-table :data="data.lists" hoverable default-sort="createdAt" class="subscriptions"
>
<b-table-column v-slot="props" field="name"
:label="$tc('globals.terms.list', 1)">
<div>
<router-link :to="`/lists/${props.row.id}`">
{{ props.row.name }}
</router-link>
<br />
<b-tag :class="props.row.optin" :data-cy="`optin-${props.row.optin}`">
<b-icon :icon="props.row.optin === 'double' ?
'account-check-outline' : 'account-off-outline'" size="is-small" />
{{ ' ' }}
{{ $t(`lists.optins.${props.row.optin}`) }}
</b-tag>{{ ' ' }}
</div>
</b-table-column>
<b-table-column v-slot="props" field="status" :label="$t('globals.fields.status')">
{{ props.row.optin === 'double' ? props.row.subscriptionStatus : '-' }}
</b-table-column>
<b-table-column v-slot="props" field="createdAt"
:label="$t('globals.fields.createdAt')">
{{ $utils.niceDate(props.row.subscriptionCreatedAt, true) }}
</b-table-column>
<b-table-column v-slot="props" field="updatedAt"
:label="$t('globals.fields.updatedAt')">
{{ $utils.niceDate(props.row.subscriptionCreatedAt, true) }}
</b-table-column>
</b-table>
</div>
<b-table :data="data.lists" hoverable default-sort="createdAt" class="subscriptions"
>
<b-table-column v-slot="props" field="name"
:label="$tc('globals.terms.list', 1)">
<div>
<router-link :to="`/lists/${props.row.id}`">
{{ props.row.name }}
</router-link>
<br />
<b-tag :class="props.row.optin" :data-cy="`optin-${props.row.optin}`">
<b-icon :icon="props.row.optin === 'double' ?
'account-check-outline' : 'account-off-outline'" size="is-small" />
{{ ' ' }}
{{ $t(`lists.optins.${props.row.optin}`) }}
</b-tag>{{ ' ' }}
</div>
</b-table-column>
<b-table-column v-slot="props" field="status" :label="$t('globals.fields.status')">
{{ props.row.optin === 'double' ? props.row.subscriptionStatus : '-' }}
<template v-if="props.row.optin === 'double'
&& props.row.subscriptionMeta.optinIp">
<br /><span class="is-size-7">{{ props.row.subscriptionMeta.optinIp }}</span>
</template>
</b-table-column>
<b-table-column v-slot="props" field="createdAt"
:label="$t('globals.fields.createdAt')">
{{ $utils.niceDate(props.row.subscriptionCreatedAt, true) }}
</b-table-column>
<b-table-column v-slot="props" field="updatedAt"
:label="$t('globals.fields.updatedAt')">
{{ $utils.niceDate(props.row.subscriptionCreatedAt, true) }}
</b-table-column>
</b-table>
</div>
<div class="bounces" v-show="bounces.length > 0">

View file

@ -36,6 +36,12 @@
name="privacy.allow_wipe" />
</b-field>
<b-field :label="$t('settings.privacy.recordOptinIP')"
:message="$t('settings.privacy.recordOptinIPHelp')">
<b-switch v-model="data['privacy.record_optin_ip']"
name="privacy.record_optin_ip" />
</b-field>
<b-field :label="$t('settings.privacy.domainBlocklist')"
:message="$t('settings.privacy.domainBlocklistHelp')">
<b-input type="textarea"

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Inclou la capçalera `List-Unsubscribe`",
"settings.privacy.listUnsubHeaderHelp": "Inclou capçaleres de cancel·lació de subscripció que permetin als clients de correu electrònic permetre als usuaris donar-se de baixa amb un sol clic.",
"settings.privacy.name": "Privadesa",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Reinicia",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Zahrnout záhlaví `List-Unsubscribe`",
"settings.privacy.listUnsubHeaderHelp": "Zahrnout záhlaví zrušení odběrů, která umožňují e-mailovým klientům, aby povolili uživatelům zrušit odběr jediným klepnutím.",
"settings.privacy.name": "Soukromí",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Restart",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Cynnwys y pennawd 'Dad-danysgrifio o'r rhestr'",
"settings.privacy.listUnsubHeaderHelp": "Cynnwys penynnau dad-danysgrifio sy'n caniatáu i ddefnyddwyr dad-danysgrifio drwy glicio un botwm.",
"settings.privacy.name": "Preifatrwydd",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Ailgychwyn",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Inkludiere `List-Unsubscribe` (von Liste abmelden) Header",
"settings.privacy.listUnsubHeaderHelp": "Inkludiere Header zum einfachen Abmelden in den E-Mails. Erlaubt es, den E-Mail Clients der Nutzer eine \",Ein Klick\"-Abmeldung anzubieten.",
"settings.privacy.name": "Privatsphäre",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Neustarten",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -488,6 +488,8 @@
"settings.privacy.listUnsubHeader": "Include `List-Unsubscribe` header",
"settings.privacy.listUnsubHeaderHelp": "Include unsubscription headers that allow e-mail clients to allow users to unsubscribe in a single click.",
"settings.privacy.name": "Privacy",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Restart",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -493,6 +493,8 @@
"settings.privacy.listUnsubHeader": "Incluir el encabezado para `darse de baja` de la lista",
"settings.privacy.listUnsubHeaderHelp": "Incluye los encabezados de darse de baja para habilitar a los clientes de correo para permitir a los usuarios darse de baja con un solo clic.",
"settings.privacy.name": "Privacidad",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Reiniciar",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visite www.hcaptcha.com para conseguir la SiteKey y el secret.",

View file

@ -493,6 +493,8 @@
"settings.privacy.listUnsubHeader": "Include `List-Unsubscribe` header",
"settings.privacy.listUnsubHeaderHelp": "Include unsubscription headers that allow e-mail clients to allow users to unsubscribe in a single click.",
"settings.privacy.name": "Yksityisyys",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Restart",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -493,6 +493,8 @@
"settings.privacy.listUnsubHeader": "Inclure l'en-tête de désabonnement simplifié (via certaines messageries)",
"settings.privacy.listUnsubHeaderHelp": "Inclure des en-têtes de désabonnement qui permettent aux utilisateurs de se désabonner en un seul clic depuis leur client de messagerie.",
"settings.privacy.name": "Vie privée",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Redémarrer",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Allez sur www.hcaptcha.com pour obtenir une clef et son secret.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "`List-Unsubscribe` fejléc",
"settings.privacy.listUnsubHeaderHelp": "Ha be van kapcsolva, egyes e-mail kliensek lehetővé teszik az egykattintásos leiratkozást.",
"settings.privacy.name": "Adatvédelem",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Újraindítás",
"settings.security.captchaKey": "hCaptcha.com kulcs",
"settings.security.captchaKeyHelp": "Kulcs és jelszó igénylése a hcaptcha.com oldalon.",

View file

@ -493,6 +493,8 @@
"settings.privacy.listUnsubHeader": "Includere l'intestazione `List-Unsubscribe`",
"settings.privacy.listUnsubHeaderHelp": "Includere intestazioni di annullamento dell'iscrizione che consentono agli utenti di annullare l'iscrizione con un clic dal proprio client di posta elettronica.",
"settings.privacy.name": "Privacy",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Riavviare",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visita www.hcaptcha.com per ottenere la SiteKey anche il secret.",

View file

@ -493,6 +493,8 @@
"settings.privacy.listUnsubHeader": "`リスト-登録解除` ヘッダー",
"settings.privacy.listUnsubHeaderHelp": "メールクライアントがワンクリックで登録解除をできるように登録解除用のヘッダーを含める。",
"settings.privacy.name": "プライバシー",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "再起動",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "`List-Unsubscribe` തലക്കെട്ട് കൂട്ടിച്ചേർക്കുക",
"settings.privacy.listUnsubHeaderHelp": "ഒറ്റ ക്ലിക്കിലൂടെ വരിക്കാനല്ലാതാക്കാൻ ഇ-മെയിൽ ക്ലൈന്റിൽ വരിക്കാരനല്ലാതാക്കാനുള്ള തലക്കെട്ട് കൂട്ടിച്ചേർക്കുക.",
"settings.privacy.name": "സ്വകാര്യത",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "പുനരാരംഭിയ്ക്കുക",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Voeg `List-Unsubscribe` header toe",
"settings.privacy.listUnsubHeaderHelp": "Voeg header toe zodat e-mailprogramma's gebruikers zich kunnen laten uitschrijven in een klik.",
"settings.privacy.name": "Privacy",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Herstarten",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Dodawaj nagłówek `List-Unsubscribe`",
"settings.privacy.listUnsubHeaderHelp": "Dodaj nagłówki do wypisania się z subskrypcji. Niektóre programy pocztowe umożliwiają wypisanie się jednym kliknięciem.",
"settings.privacy.name": "Prywatność",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Restart",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Wejdź na www.hcaptcha.com w celu pobrania klucza i sekretu.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Incluir cabeçalho `List-Unsubscribe`",
"settings.privacy.listUnsubHeaderHelp": "Incluir cabeçalhos de desinscrição que permitem aos clientes de e-mail cancelem a inscrição em um único clique.",
"settings.privacy.name": "Privacidade",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Reiniciar",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Incluir header `List-Unsubscribe`",
"settings.privacy.listUnsubHeaderHelp": "Incluir headers de cancelamento de subscrição que permite aos clientes de email permitir ao utilizadores cancelar a subscrição num único clique.",
"settings.privacy.name": "Privacidade",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Reiniciar",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visite www.hcaptcha.com para obter a chave e o segredo.",

View file

@ -493,6 +493,8 @@
"settings.privacy.listUnsubHeader": "Includeți antetul \"Listă-Dezabonare\"",
"settings.privacy.listUnsubHeaderHelp": "Include anteturi de dezabonare care permit clienților de e-mail să permită utilizatorilor să se dezaboneze printr-un singur clic.",
"settings.privacy.name": "Confidențialitate",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Repornește",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Vizitați www.hcaptcha.com pentru a obține cheia și secretul.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Включать заголовок `List-Unsubscribe`",
"settings.privacy.listUnsubHeaderHelp": "Включать заголовок отписки",
"settings.privacy.name": "Конфиденциальност",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Перезапустить",
"settings.security.captchaKey": "hCaptcha.com ключ сайта",
"settings.security.captchaKeyHelp": "Посетите www.hcaptcha.com для получения ключа сайта и секретного ключа.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Include `List-Unsubscribe` header",
"settings.privacy.listUnsubHeaderHelp": "Include unsubscription headers that allow e-mail clients to allow users to unsubscribe in a single click.",
"settings.privacy.name": "Privacy",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Restart",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "Nastaviť hlavičku `List-Unsubscribe`",
"settings.privacy.listUnsubHeaderHelp": "Nastaví hlavičku zrušenia odberov, ktorá umožňuje e-mailovým klientom, aby povolili používateľom zrušiť odber jedným kliknutím.",
"settings.privacy.name": "Súkromie",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Restarť",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -493,6 +493,8 @@
"settings.privacy.listUnsubHeader": " `List-Unsubscribe` Başlık bilgisini ekle",
"settings.privacy.listUnsubHeaderHelp": "E-posta istemcilerinin kullanıcıların tek bir tıklamayla abonelikten çıkmalarına olanak tanıyan abonelik iptal başlıklarını ekleyin.",
"settings.privacy.name": "Gizlilik",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Yeniden başlat",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -493,6 +493,8 @@
"settings.privacy.listUnsubHeader": "Bao gồm tiêu đề `Danh sách-Hủy đăng ký`",
"settings.privacy.listUnsubHeaderHelp": "Bao gồm các tiêu đề hủy đăng ký cho phép ứng dụng e-mail cho phép người dùng hủy đăng ký chỉ bằng một cú nhấp chuột.",
"settings.privacy.name": "Sự riêng tư",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "Khởi động lại",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -492,6 +492,8 @@
"settings.privacy.listUnsubHeader": "包括 `List-Unsubscribe` 标头",
"settings.privacy.listUnsubHeaderHelp": "包括允许电子邮件客户端允许用户通过单击取消订阅的取消订阅标题",
"settings.privacy.name": "隐私",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "重新开始",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -493,6 +493,8 @@
"settings.privacy.listUnsubHeader": "包括`List-Unsubscribe` 標頭",
"settings.privacy.listUnsubHeaderHelp": "包括允許電子郵件客戶端允許用戶通過單擊取消訂閱的取消訂閱標題",
"settings.privacy.name": "隱私",
"settings.privacy.recordOptinIP": "Record opt-in IP address",
"settings.privacy.recordOptinIPHelp": "Record IP address of double opt-ins in subscriber attributes.",
"settings.restart": "重新開始",
"settings.security.captchaKey": "hCaptcha.com SiteKey",
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",

View file

@ -451,8 +451,12 @@ func (c *Core) UnsubscribeByCampaign(subUUID, campUUID string, blocklist bool) e
}
// ConfirmOptionSubscription confirms a subscriber's optin subscription.
func (c *Core) ConfirmOptionSubscription(subUUID string, listUUIDs []string) error {
if _, err := c.q.ConfirmSubscriptionOptin.Exec(subUUID, pq.Array(listUUIDs)); err != nil {
func (c *Core) ConfirmOptionSubscription(subUUID string, listUUIDs []string, meta models.JSON) error {
if meta == nil {
meta = models.JSON{}
}
if _, err := c.q.ConfirmSubscriptionOptin.Exec(subUUID, pq.Array(listUUIDs), meta); err != nil {
c.log.Printf("error confirming subscription: %v", err)
return echo.NewHTTPError(http.StatusInternalServerError,
c.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.subscribers}", "error", pqErrMsg(err)))

View file

@ -13,7 +13,8 @@ func V2_5_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
INSERT INTO settings (key, value) VALUES
('upload.extensions', '["jpg","jpeg","png","gif","svg","*"]'),
('app.enable_public_archive_rss_content', 'false'),
('bounce.actions', '{"soft": {"count": 2, "action": "none"}, "hard": {"count": 2, "action": "blocklist"}, "complaint" : {"count": 2, "action": "blocklist"}}')
('bounce.actions', '{"soft": {"count": 2, "action": "none"}, "hard": {"count": 2, "action": "blocklist"}, "complaint" : {"count": 2, "action": "blocklist"}}'),
('privacy.record_optin_ip', 'false')
ON CONFLICT DO NOTHING;
`); err != nil {
return err
@ -25,12 +26,14 @@ func V2_5_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
-- Add the content_type column.
ALTER TABLE media ADD COLUMN IF NOT EXISTS content_type TEXT NOT NULL DEFAULT 'application/octet-stream';
-- Add meta column to subscriptions.
ALTER TABLE subscriber_lists ADD COLUMN IF NOT EXISTS meta JSONB NOT NULL DEFAULT '{}';
-- Fill the content type column for existing files (which would only be images at this point).
UPDATE media SET content_type = CASE
WHEN LOWER(SUBSTRING(filename FROM '.([^.]+)$')) = 'svg' THEN 'image/svg+xml'
ELSE 'image/' || LOWER(SUBSTRING(filename FROM '.([^.]+)$'))
END;
`); err != nil {
return err
}

View file

@ -174,8 +174,9 @@ type subLists struct {
// Subscription represents a list attached to a subscriber.
type Subscription struct {
List
SubscriptionStatus null.String `db:"subscription_status" json:"subscription_status"`
SubscriptionCreatedAt null.String `db:"subscription_created_at" json:"subscription_created_at"`
SubscriptionStatus null.String `db:"subscription_status" json:"subscription_status"`
SubscriptionCreatedAt null.String `db:"subscription_created_at" json:"subscription_created_at"`
Meta json.RawMessage `db:"meta" json:"meta"`
}
// SubscriberExportProfile represents a subscriber's collated data in JSON for export.

View file

@ -31,6 +31,7 @@ type Settings struct {
PrivacyAllowExport bool `json:"privacy.allow_export"`
PrivacyAllowWipe bool `json:"privacy.allow_wipe"`
PrivacyExportable []string `json:"privacy.exportable"`
PrivacyRecordOptinIP bool `json:"privacy.record_optin_ip"`
DomainBlocklist []string `json:"privacy.domain_blocklist"`
SecurityEnableCaptcha bool `json:"security.enable_captcha"`

View file

@ -43,6 +43,7 @@ WITH subs AS (
subscriber_lists.status AS subscription_status,
subscriber_lists.created_at AS subscription_created_at,
subscriber_lists.updated_at AS subscription_updated_at,
subscriber_lists.meta AS subscription_meta,
lists.*
) l)
)
@ -64,7 +65,10 @@ SELECT id as subscriber_id,
WITH sub AS (
SELECT id FROM subscribers WHERE CASE WHEN $1 > 0 THEN id = $1 ELSE uuid = $2 END
)
SELECT lists.*, subscriber_lists.status as subscription_status, subscriber_lists.created_at as subscription_created_at
SELECT lists.*,
subscriber_lists.status as subscription_status,
subscriber_lists.created_at as subscription_created_at,
subscriber_lists.meta as subscription_meta
FROM lists LEFT JOIN subscriber_lists
ON (subscriber_lists.list_id = lists.id AND subscriber_lists.subscriber_id = (SELECT id FROM sub))
WHERE CASE WHEN $3 = TRUE THEN TRUE ELSE subscriber_lists.status IS NOT NULL END
@ -214,7 +218,7 @@ WITH subID AS (
listIDs AS (
SELECT id FROM lists WHERE uuid = ANY($2::UUID[])
)
UPDATE subscriber_lists SET status='confirmed', updated_at=NOW()
UPDATE subscriber_lists SET status='confirmed', meta=meta || $3, updated_at=NOW()
WHERE subscriber_id = (SELECT id FROM subID) AND list_id = ANY(SELECT id FROM listIDs);
-- name: unsubscribe-subscribers-from-lists

View file

@ -43,6 +43,7 @@ DROP TABLE IF EXISTS subscriber_lists CASCADE;
CREATE TABLE subscriber_lists (
subscriber_id INTEGER REFERENCES subscribers(id) ON DELETE CASCADE ON UPDATE CASCADE,
list_id INTEGER NULL REFERENCES lists(id) ON DELETE CASCADE ON UPDATE CASCADE,
meta JSONB NOT NULL DEFAULT '{}',
status subscription_status NOT NULL DEFAULT 'unconfirmed',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
@ -225,6 +226,7 @@ INSERT INTO settings (key, value) VALUES
('privacy.allow_preferences', 'true'),
('privacy.exportable', '["profile", "subscriptions", "campaign_views", "link_clicks"]'),
('privacy.domain_blocklist', '[]'),
('privacy.record_optin_ip', 'false'),
('security.enable_captcha', 'false'),
('security.captcha_key', '""'),
('security.captcha_secret', '""'),