Skip to content

Commit

Permalink
Add new privacy option 'Record opt-in IP' to record IP address of opt…
Browse files Browse the repository at this point in the history
…in 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.
  • Loading branch information
knadh committed Jul 26, 2023
1 parent b26950c commit ad80c71
Show file tree
Hide file tree
Showing 34 changed files with 122 additions and 41 deletions.
1 change: 1 addition & 0 deletions cmd/init.go
Expand Up @@ -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"`
Expand Down
11 changes: 10 additions & 1 deletion cmd/public.go
Expand Up @@ -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")))
Expand Down
66 changes: 34 additions & 32 deletions frontend/src/views/SubscriberForm.vue
Expand Up @@ -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">
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/views/settings/privacy.vue
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions i18n/ca.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/cs-cz.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/cy.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/de.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/en.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/es.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/fi.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/fr.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/hu.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/it.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/jp.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/ml.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/nl.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/pl.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/pt-BR.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/pt.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/ro.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/ru.json
Expand Up @@ -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 для получения ключа сайта и секретного ключа.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/se.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/sk.json
Expand Up @@ -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.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/tr.json
Expand Up @@ -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.",
Expand Down

0 comments on commit ad80c71

Please sign in to comment.