mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: Custom unsubscribe options (#17090)
With this change, plugins can create custom unsubscribe keys, extend the unsubscribe view with custom preferences, and decide how they are updated.
This commit is contained in:
@@ -95,6 +95,8 @@ class DiscoursePluginRegistry
|
||||
|
||||
define_filtered_register :notification_consolidation_plans
|
||||
|
||||
define_filtered_register :email_unsubscribers
|
||||
|
||||
def self.register_auth_provider(auth_provider)
|
||||
self.auth_providers << auth_provider
|
||||
end
|
||||
|
||||
57
lib/email_controller_helper/base_email_unsubscriber.rb
Normal file
57
lib/email_controller_helper/base_email_unsubscriber.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This class and its children are instantiated and used by the EmailController.
|
||||
module EmailControllerHelper
|
||||
class BaseEmailUnsubscriber
|
||||
def initialize(unsubscribe_key)
|
||||
@unsubscribe_key = unsubscribe_key
|
||||
end
|
||||
|
||||
attr_reader :unsubscribe_key
|
||||
|
||||
# Sets instance variables in the `EmailController#unsubscribe`, which are later available in the view.
|
||||
# Don't forget to call super when extending this method.
|
||||
def prepare_unsubscribe_options(controller)
|
||||
controller.instance_variable_set(:@digest_unsubscribe, false)
|
||||
controller.instance_variable_set(:@watched_count, nil)
|
||||
controller.instance_variable_set(:@type, unsubscribe_key.unsubscribe_key_type)
|
||||
|
||||
controller.instance_variable_set(:@user, key_owner)
|
||||
|
||||
controller.instance_variable_set(
|
||||
:@unsubscribed_from_all,
|
||||
key_owner.user_option.unsubscribed_from_all?
|
||||
)
|
||||
end
|
||||
|
||||
# Called by the `EmailController#perform_unsubscribe` and defines what unsubscribing means.
|
||||
#
|
||||
# Receives the request params and returns a boolean indicating if any preferences were updated.
|
||||
#
|
||||
# Don't forget to call super when extending this method.
|
||||
def unsubscribe(params)
|
||||
updated = false
|
||||
|
||||
if params[:disable_mailing_list]
|
||||
key_owner.user_option.update_columns(mailing_list_mode: false)
|
||||
updated = true
|
||||
end
|
||||
|
||||
if params[:unsubscribe_all]
|
||||
key_owner.user_option.update_columns(email_digests: false,
|
||||
email_level: UserOption.email_level_types[:never],
|
||||
email_messages_level: UserOption.email_level_types[:never],
|
||||
mailing_list_mode: false)
|
||||
updated = true
|
||||
end
|
||||
|
||||
updated
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def key_owner
|
||||
unsubscribe_key.user
|
||||
end
|
||||
end
|
||||
end
|
||||
51
lib/email_controller_helper/digest_email_unsubscriber.rb
Normal file
51
lib/email_controller_helper/digest_email_unsubscriber.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module EmailControllerHelper
|
||||
class DigestEmailUnsubscriber < BaseEmailUnsubscriber
|
||||
def prepare_unsubscribe_options(controller)
|
||||
super(controller)
|
||||
controller.instance_variable_set(:@digest_unsubscribe, !SiteSetting.disable_digest_emails)
|
||||
|
||||
frequency_in_minutes = key_owner.user_option.digest_after_minutes
|
||||
email_digests = key_owner.user_option.email_digests
|
||||
frequencies = DigestEmailSiteSetting.values.dup
|
||||
never = frequencies.delete_at(0)
|
||||
allowed_frequencies = %w[never weekly every_month every_six_months]
|
||||
|
||||
result = frequencies.reduce(frequencies: [], current: nil, selected: nil, take_next: false) do |memo, v|
|
||||
memo[:current] = v[:name] if v[:value] == frequency_in_minutes && email_digests
|
||||
next(memo) unless allowed_frequencies.include?(v[:name])
|
||||
|
||||
memo.tap do |m|
|
||||
m[:selected] = v[:value] if m[:take_next] && email_digests
|
||||
m[:frequencies] << [I18n.t("unsubscribe.digest_frequency.#{v[:name]}"), v[:value]]
|
||||
m[:take_next] = !m[:take_next] && m[:current]
|
||||
end
|
||||
end
|
||||
|
||||
digest_frequencies = result.slice(:frequencies, :current, :selected).tap do |r|
|
||||
r[:frequencies] << [I18n.t("unsubscribe.digest_frequency.#{never[:name]}"), never[:value]]
|
||||
r[:selected] ||= never[:value]
|
||||
r[:current] ||= never[:name]
|
||||
end
|
||||
|
||||
controller.instance_variable_set(:@digest_frequencies, digest_frequencies)
|
||||
end
|
||||
|
||||
def unsubscribe(params)
|
||||
updated = super(params)
|
||||
|
||||
if params[:digest_after_minutes]
|
||||
digest_frequency = params[:digest_after_minutes].to_i
|
||||
|
||||
unsubscribe_key.user.user_option.update_columns(
|
||||
digest_after_minutes: digest_frequency,
|
||||
email_digests: digest_frequency.positive?
|
||||
)
|
||||
updated = true
|
||||
end
|
||||
|
||||
updated
|
||||
end
|
||||
end
|
||||
end
|
||||
62
lib/email_controller_helper/topic_email_unsubscriber.rb
Normal file
62
lib/email_controller_helper/topic_email_unsubscriber.rb
Normal file
@@ -0,0 +1,62 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module EmailControllerHelper
|
||||
class TopicEmailUnsubscriber < BaseEmailUnsubscriber
|
||||
def prepare_unsubscribe_options(controller)
|
||||
super(controller)
|
||||
watching = TopicUser.notification_levels[:watching]
|
||||
|
||||
topic = unsubscribe_key.associated_topic
|
||||
|
||||
controller.instance_variable_set(:@topic, topic)
|
||||
controller.instance_variable_set(
|
||||
:@watching_topic,
|
||||
TopicUser.exists?(user: key_owner, notification_level: watching, topic_id: topic.id)
|
||||
)
|
||||
|
||||
return if topic.category_id.blank?
|
||||
return if !CategoryUser.exists?(user: key_owner, notification_level: CategoryUser.watching_levels, category_id: topic.category_id)
|
||||
|
||||
controller.instance_variable_set(
|
||||
:@watched_count,
|
||||
TopicUser.joins(:topic)
|
||||
.where(user: key_owner, notification_level: watching).where(topics: { category_id: topic.category_id }).count
|
||||
)
|
||||
end
|
||||
|
||||
def unsubscribe(params)
|
||||
updated = super(params)
|
||||
|
||||
topic = unsubscribe_key.associated_topic
|
||||
return updated if topic.nil?
|
||||
|
||||
if params[:unwatch_topic]
|
||||
TopicUser.where(topic_id: topic.id, user_id: key_owner.id)
|
||||
.update_all(notification_level: TopicUser.notification_levels[:tracking])
|
||||
|
||||
updated = true
|
||||
end
|
||||
|
||||
if params[:unwatch_category] && topic.category_id
|
||||
TopicUser.joins(:topic)
|
||||
.where(user: key_owner, notification_level: TopicUser.notification_levels[:watching])
|
||||
.where(topics: { category_id: topic.category_id })
|
||||
.update_all(notification_level: TopicUser.notification_levels[:tracking])
|
||||
|
||||
CategoryUser
|
||||
.where(user_id: key_owner.id, category_id: topic.category_id, notification_level: CategoryUser.watching_levels)
|
||||
.destroy_all
|
||||
|
||||
updated = true
|
||||
end
|
||||
|
||||
if params[:mute_topic]
|
||||
TopicUser.where(topic_id: topic.id, user_id: key_owner.id).update_all(notification_level: TopicUser.notification_levels[:muted])
|
||||
|
||||
updated = true
|
||||
end
|
||||
|
||||
updated
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1018,6 +1018,29 @@ class Plugin::Instance
|
||||
StaticController::CUSTOM_PAGES[page] = blk ? { topic_id: blk } : options
|
||||
end
|
||||
|
||||
# Let plugin define custom unsubscribe keys,
|
||||
# set custom instance variables on the `EmailController#unsubscribe` action,
|
||||
# and describe what unsubscribing for that key does.
|
||||
#
|
||||
# The method receives a class that inherits from `Email::BaseEmailUnsubscriber`.
|
||||
# Take a look at it to know how to implement your child class.
|
||||
#
|
||||
# In conjunction with this, you'll have to:
|
||||
#
|
||||
# - Register a new connector under app/views/connectors/unsubscribe_options.
|
||||
# We'll include the HTML inside the unsubscribe form, so you can add your fields using the
|
||||
# instance variables you set in the controller previously. When the form is submitted,
|
||||
# it sends the updated preferences to `EmailController#perform_unsubscribe`.
|
||||
#
|
||||
# - Your code is responsible for creating the custom key by calling `UnsubscribeKey#create_key_for`.
|
||||
def register_email_unsubscriber(type, unsubscriber)
|
||||
core_types = [UnsubscribeKey::ALL_TYPE, UnsubscribeKey::DIGEST_TYPE, UnsubscribeKey::TOPIC_TYPE]
|
||||
raise ArgumentError.new('Type already exists') if core_types.include?(type)
|
||||
raise ArgumentError.new('Not an email unsubscriber') if !unsubscriber.ancestors.include?(EmailControllerHelper::BaseEmailUnsubscriber)
|
||||
|
||||
DiscoursePluginRegistry.register_email_unsubscriber({ type => unsubscriber }, self)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.js_path
|
||||
|
||||
Reference in New Issue
Block a user