diff --git a/app/controllers/email_controller.rb b/app/controllers/email_controller.rb index b4aebf910cc..de0c1cc2413 100644 --- a/app/controllers/email_controller.rb +++ b/app/controllers/email_controller.rb @@ -6,38 +6,17 @@ class EmailController < ApplicationController skip_before_action :check_xhr, :preload_json, :redirect_to_login_if_required def unsubscribe - @not_found = true - @watched_count = nil + key = UnsubscribeKey.find_by(key: params[:key]) + @found = key.present? - if key = UnsubscribeKey.find_by(key: params[:key]) - if @user = key.user - post = key.post - @topic = post&.topic || key.topic - @digest_unsubscribe = !@topic && !SiteSetting.disable_digest_emails - @type = key.unsubscribe_key_type - @not_found = false + if @found + UnsubscribeKey + .get_unsubscribe_strategy_for(key) + &.prepare_unsubscribe_options(self) - if current_user.present? && (@user != current_user) - @different_user = @user.name - @return_url = request.original_url - end - - watching = TopicUser.notification_levels[:watching] - - @unsubscribed_from_all = @user.user_option.unsubscribed_from_all? - - if @topic - @watching_topic = TopicUser.exists?(user_id: @user.id, notification_level: watching, topic_id: @topic.id) - if @topic.category_id - if CategoryUser.exists?(user_id: @user.id, notification_level: CategoryUser.watching_levels, category_id: @topic.category_id) - @watched_count = TopicUser.joins(:topic) - .where(user: @user, notification_level: watching, "topics.category_id" => @topic.category_id) - .count - end - end - else - @digest_frequencies = digest_frequencies(@user) - end + if current_user.present? && (@user != current_user) + @different_user = @user.name + @return_url = request.original_url end end end @@ -46,80 +25,24 @@ class EmailController < ApplicationController RateLimiter.new(nil, "unsubscribe_#{request.ip}", 10, 1.minute).performed! key = UnsubscribeKey.find_by(key: params[:key]) - raise Discourse::NotFound unless key && key.user - - topic = key&.post&.topic || key.topic + raise Discourse::NotFound if key.nil? || key.user.nil? user = key.user + updated = UnsubscribeKey.get_unsubscribe_strategy_for(key) + &.unsubscribe(params) - updated = false + if updated + cache_key = "unsub_#{SecureRandom.hex}" + Discourse.cache.write cache_key, user.email, expires_in: 1.hour - if topic - if params["unwatch_topic"] - TopicUser.where(topic_id: topic.id, user_id: user.id) - .update_all(notification_level: TopicUser.notification_levels[:tracking]) - updated = true - end - - if params["unwatch_category"] && topic.category_id - TopicUser.joins(:topic) - .where(:user => user, - :notification_level => TopicUser.notification_levels[:watching], - "topics.category_id" => topic.category_id) - .update_all(notification_level: TopicUser.notification_levels[:tracking]) - - CategoryUser.where(user_id: user.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: user.id) - .update_all(notification_level: TopicUser.notification_levels[:muted]) - updated = true - end - end - - if params["disable_mailing_list"] - user.user_option.update_columns(mailing_list_mode: false) - updated = true - end - - if params['digest_after_minutes'] - digest_frequency = params['digest_after_minutes'].to_i - - user.user_option.update_columns( - digest_after_minutes: digest_frequency, - email_digests: digest_frequency.positive? - ) - updated = true - end - - if params["unsubscribe_all"] - user.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 - - unless updated - redirect_back fallback_location: path("/") - else - - key = "unsub_#{SecureRandom.hex}" - Discourse.cache.write key, user.email, expires_in: 1.hour - - url = path("/email/unsubscribed?key=#{key}") - if topic - url += "&topic_id=#{topic.id}" + url = path("/email/unsubscribed?key=#{cache_key}") + if key.associated_topic + url += "&topic_id=#{key.associated_topic.id}" end redirect_to url + else + redirect_back fallback_location: path("/") end - end def unsubscribed @@ -130,31 +53,4 @@ class EmailController < ApplicationController topic = Topic.find_by(id: params[:topic_id].to_i) if @topic_id @topic = topic if topic && Guardian.new(nil).can_see?(topic) end - - private - - def digest_frequencies(user) - frequency_in_minutes = user.user_option.digest_after_minutes - email_digests = user.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 - - 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 - end end diff --git a/app/mailers/subscription_mailer.rb b/app/mailers/subscription_mailer.rb index a8c4234f6bd..c2c40752c84 100644 --- a/app/mailers/subscription_mailer.rb +++ b/app/mailers/subscription_mailer.rb @@ -4,7 +4,7 @@ class SubscriptionMailer < ActionMailer::Base include Email::BuildEmailHelper def confirm_unsubscribe(user, opts = {}) - unsubscribe_key = UnsubscribeKey.create_key_for(user, "all") + unsubscribe_key = UnsubscribeKey.create_key_for(user, UnsubscribeKey::ALL_TYPE) build_email user.email, template: "unsubscribe_mailer", site_title: SiteSetting.title, diff --git a/app/mailers/user_notifications.rb b/app/mailers/user_notifications.rb index d3bc5e0bb45..0c36b6aeea8 100644 --- a/app/mailers/user_notifications.rb +++ b/app/mailers/user_notifications.rb @@ -216,6 +216,8 @@ class UserNotifications < ActionMailer::Base def digest(user, opts = {}) build_summary_for(user) + @unsubscribe_key = UnsubscribeKey.create_key_for(@user, UnsubscribeKey::DIGEST_TYPE) + min_date = opts[:since] || user.last_emailed_at || user.last_seen_at || 1.month.ago # Fetch some topics and posts to show @@ -753,7 +755,6 @@ class UserNotifications < ActionMailer::Base @header_bgcolor = ColorScheme.hex_for_name('header_background') @anchor_color = ColorScheme.hex_for_name('tertiary') @markdown_linker = MarkdownLinker.new(@base_url) - @unsubscribe_key = UnsubscribeKey.create_key_for(@user, "digest") @disable_email_custom_styles = !SiteSetting.apply_custom_styles_to_digest end diff --git a/app/models/post.rb b/app/models/post.rb index 5cfccffe554..185fd941f56 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -616,7 +616,9 @@ class Post < ActiveRecord::Base end def unsubscribe_url(user) - "#{Discourse.base_url}/email/unsubscribe/#{UnsubscribeKey.create_key_for(user, self)}" + key_value = UnsubscribeKey.create_key_for(user, UnsubscribeKey::TOPIC_TYPE, post: self) + + "#{Discourse.base_url}/email/unsubscribe/#{key_value}" end def self.url(slug, topic_id, post_number, opts = nil) diff --git a/app/models/unsubscribe_key.rb b/app/models/unsubscribe_key.rb index 308d2d5205f..da81fdfb33c 100644 --- a/app/models/unsubscribe_key.rb +++ b/app/models/unsubscribe_key.rb @@ -7,16 +7,44 @@ class UnsubscribeKey < ActiveRecord::Base before_create :generate_random_key - def self.create_key_for(user, type) - if Post === type - create(user_id: user.id, unsubscribe_key_type: "topic", topic_id: type.topic_id, post_id: type.id).key - else - create(user_id: user.id, unsubscribe_key_type: type).key + ALL_TYPE = 'all' + DIGEST_TYPE = 'digest' + TOPIC_TYPE = 'topic' + + class << self + def create_key_for(user, type, post: nil) + unsubscribe_key = new(user_id: user.id, unsubscribe_key_type: type) + + if type == TOPIC_TYPE + unsubscribe_key.topic_id = post.topic_id + unsubscribe_key.post_id = post.id + end + + unsubscribe_key.save! + unsubscribe_key.key + end + + def user_for_key(key) + where(key: key).first&.user + end + + def get_unsubscribe_strategy_for(key) + strategies = { + DIGEST_TYPE => EmailControllerHelper::DigestEmailUnsubscriber, + TOPIC_TYPE => EmailControllerHelper::TopicEmailUnsubscriber, + ALL_TYPE => EmailControllerHelper::BaseEmailUnsubscriber + } + + DiscoursePluginRegistry.email_unsubscribers.each do |unsubcriber| + strategies.merge!(unsubcriber) + end + + strategies[key.unsubscribe_key_type]&.new(key) end end - def self.user_for_key(key) - where(key: key).first.try(:user) + def associated_topic + @associated_topic ||= topic || post&.topic end private diff --git a/app/views/email/unsubscribe.html.erb b/app/views/email/unsubscribe.html.erb index 6351b5ef999..522b27d59a1 100644 --- a/app/views/email/unsubscribe.html.erb +++ b/app/views/email/unsubscribe.html.erb @@ -1,7 +1,7 @@
- <%- if @not_found || @different_user %> + <%- if !@found || @different_user %> - <%if @not_found%> + <%if !@found %>

<%= t "unsubscribe.not_found_description" %>

<%- else %>

<%= t("unsubscribe.different_user_description").html_safe %>

@@ -78,6 +78,8 @@

<% end %> + <%= server_plugin_outlet "unsubscribe_options" %> + <% unless @unsubscribed_from_all %>