Add unsubscribe links and headers for emails

https://3.basecamp.com/2914079/buckets/37331921/todos/9002504172
This commit is contained in:
Jorge Manrubia
2025-08-28 16:58:51 +02:00
parent 243779dbcf
commit e4ab1366be
10 changed files with 95 additions and 2 deletions
@@ -0,0 +1,24 @@
class Notifications::UnsubscribesController < ApplicationController
allow_unauthenticated_access
skip_before_action :verify_authenticity_token
before_action :set_user
def new
end
def create
@user.settings.bundle_email_never!
redirect_to notifications_unsubscribe_path(access_token: params[:access_token])
end
def show
end
private
def set_user
unless @user = User.find_by_token_for(:unsubscribe, params[:access_token])
redirect_to root_path, alert: "Invalid unsubscribe link"
end
end
end
@@ -0,0 +1,12 @@
module Mailers::Unsubscribable
extend ActiveSupport::Concern
included do
after_action :set_unsubscribe_headers
end
def set_unsubscribe_headers
headers["List-Unsubscribe-Post"] = "List-Unsubscribe=One-Click"
headers["List-Unsubscribe"] = "<#{notifications_unsubscribe_url(access_token: @unsubscribe_token)}>"
end
end
@@ -1,9 +1,13 @@
class Notification::BundleMailer < ApplicationMailer
include Mailers::Unsubscribable
helper NotificationsHelper
def notification(bundle)
@user = bundle.user
@bundle = bundle
@notifications = bundle.notifications
@unsubscribe_token = @user.generate_token_for(:unsubscribe)
mail \
to: bundle.user.email_address,
+2
View File
@@ -4,6 +4,8 @@ module User::Notifiable
included do
has_many :notifications, dependent: :destroy
has_many :notification_bundles, class_name: "Notification::Bundle", dependent: :destroy
generates_token_for :unsubscribe, expires_in: 1.month
end
def bundle(notification)
@@ -5,7 +5,7 @@
<%= render partial: "notification/bundle_mailer/notification", collection: @notifications, as: :notification %>
<p class="footer">Fizzy emails you about new notifications every few hours. <br>
<%= link_to "Change how often you get these", notifications_settings_url %>
or <%= link_to "unsubscribe from all email notifications", root_url %>.
or <%= link_to "unsubscribe from all email notifications", new_notifications_unsubscribe_url(access_token: @unsubscribe_token) %>.
</p>
</td>
</tr>
@@ -10,4 +10,4 @@ Change how often you get these:
<%= notifications_settings_url %>
Unsubscribe from all email notifications:
<%= root_url %>
<%= new_notifications_unsubscribe_url(access_token: @unsubscribe_token) %>
@@ -0,0 +1,7 @@
<p>Unsubscribing from all email notifications as <%= @user.name %>…</p>
<div class="push_double--top centered delayed-fade-in">
<%= auto_submit_form_with model: @user, url: notifications_unsubscribe_path(access_token: params[:access_token]), method: :post do |form| %>
<%= form.submit "Unsubscribe now", class: "btn" %>
<% end %>
</div>
@@ -0,0 +1,3 @@
<h1>Unsubscribed</h1>
<p>Thank you <%= @user.name %>, you will no longer receive bundled email notifications.</p>
<p>You can <%= link_to "re-enable notifications here", notifications_settings_path %>.</p>
+1
View File
@@ -56,6 +56,7 @@ Rails.application.routes.draw do
namespace :notifications do
resource :settings
resource :unsubscribe
end
resources :notifications do
@@ -0,0 +1,40 @@
require "test_helper"
class Notifications::UnsubscribesControllerTest < ActionDispatch::IntegrationTest
setup do
@user = users(:david)
@access_token = @user.generate_token_for(:unsubscribe)
sign_in_as @user
end
test "new" do
get new_notifications_unsubscribe_path(access_token: @access_token)
assert_response :success
end
test "new with bad token" do
get new_notifications_unsubscribe_path(access_token: "bad")
assert_redirected_to root_path
end
test "create" do
@user.reload.settings.bundle_email_every_few_hours!
assert_changes -> { @user.reload.settings.bundle_email_frequency }, to: "never" do
post notifications_unsubscribe_path(access_token: @access_token)
assert_redirected_to notifications_unsubscribe_path(access_token: @access_token)
end
end
test "create with bad token" do
assert_no_changes -> { @user.reload.settings.bundle_email_frequency } do
post notifications_unsubscribe_path(access_token: "bad")
assert_redirected_to root_path
end
end
test "show" do
get notifications_unsubscribe_path(access_token: @access_token)
assert_response :success
end
end