diff --git a/app/controllers/notifications/unsubscribes_controller.rb b/app/controllers/notifications/unsubscribes_controller.rb new file mode 100644 index 000000000..f7486d91e --- /dev/null +++ b/app/controllers/notifications/unsubscribes_controller.rb @@ -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 diff --git a/app/mailers/concerns/mailers/unsubscribable.rb b/app/mailers/concerns/mailers/unsubscribable.rb new file mode 100644 index 000000000..fb9278df7 --- /dev/null +++ b/app/mailers/concerns/mailers/unsubscribable.rb @@ -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 diff --git a/app/mailers/notification/bundle_mailer.rb b/app/mailers/notification/bundle_mailer.rb index 683c83ec8..06bce5aa0 100644 --- a/app/mailers/notification/bundle_mailer.rb +++ b/app/mailers/notification/bundle_mailer.rb @@ -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, diff --git a/app/models/user/notifiable.rb b/app/models/user/notifiable.rb index 015186d15..b0e4ffcad 100644 --- a/app/models/user/notifiable.rb +++ b/app/models/user/notifiable.rb @@ -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) diff --git a/app/views/mailers/notification/bundle_mailer/notification.html.erb b/app/views/mailers/notification/bundle_mailer/notification.html.erb index 8dcb6e731..f051cf426 100644 --- a/app/views/mailers/notification/bundle_mailer/notification.html.erb +++ b/app/views/mailers/notification/bundle_mailer/notification.html.erb @@ -5,7 +5,7 @@ <%= render partial: "notification/bundle_mailer/notification", collection: @notifications, as: :notification %> diff --git a/app/views/mailers/notification/bundle_mailer/notification.text.erb b/app/views/mailers/notification/bundle_mailer/notification.text.erb index d447d2f05..0149767e6 100644 --- a/app/views/mailers/notification/bundle_mailer/notification.text.erb +++ b/app/views/mailers/notification/bundle_mailer/notification.text.erb @@ -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) %> diff --git a/app/views/notifications/unsubscribes/new.html.erb b/app/views/notifications/unsubscribes/new.html.erb new file mode 100644 index 000000000..7baaf8e9a --- /dev/null +++ b/app/views/notifications/unsubscribes/new.html.erb @@ -0,0 +1,7 @@ +

Unsubscribing from all email notifications as <%= @user.name %>…

+ +
+ <%= 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 %> +
diff --git a/app/views/notifications/unsubscribes/show.html.erb b/app/views/notifications/unsubscribes/show.html.erb new file mode 100644 index 000000000..3610e92e2 --- /dev/null +++ b/app/views/notifications/unsubscribes/show.html.erb @@ -0,0 +1,3 @@ +

Unsubscribed

+

Thank you <%= @user.name %>, you will no longer receive bundled email notifications.

+

You can <%= link_to "re-enable notifications here", notifications_settings_path %>.

diff --git a/config/routes.rb b/config/routes.rb index dca643a9b..5392868c9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,6 +56,7 @@ Rails.application.routes.draw do namespace :notifications do resource :settings + resource :unsubscribe end resources :notifications do diff --git a/test/controllers/notifications/unsubscribes_controller_test.rb b/test/controllers/notifications/unsubscribes_controller_test.rb new file mode 100644 index 000000000..08998d6c6 --- /dev/null +++ b/test/controllers/notifications/unsubscribes_controller_test.rb @@ -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