Merge pull request #3952 from gdpelican/unsubscribe-via-email
Unsubscribe via email
This commit is contained in:
commit
f61537bc16
|
@ -11,6 +11,7 @@ class EmailController < ApplicationController
|
|||
|
||||
def unsubscribe
|
||||
@user = DigestUnsubscribeKey.user_for_key(params[:key])
|
||||
RateLimiter.new(@user, "unsubscribe_via_email", 3, 1.day).performed! unless @user && @user.staff?
|
||||
|
||||
# Don't allow the use of a key while logged in as a different user
|
||||
if current_user.present? && (@user != current_user)
|
||||
|
@ -23,7 +24,12 @@ class EmailController < ApplicationController
|
|||
return
|
||||
end
|
||||
|
||||
@user.update_column(:email_digests, false)
|
||||
if params[:from_all]
|
||||
@user.update_columns(email_digests: false, email_direct: false, email_private_messages: false, email_always: false)
|
||||
else
|
||||
@user.update_column(:email_digests, false)
|
||||
end
|
||||
|
||||
@success = true
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
require_dependency 'email/message_builder'
|
||||
|
||||
class SubscriptionMailer < ActionMailer::Base
|
||||
include Email::BuildEmailHelper
|
||||
|
||||
def confirm_unsubscribe(user, opts={})
|
||||
unsubscribe_key = DigestUnsubscribeKey.create_key_for(user)
|
||||
build_email user.email,
|
||||
template: "unsubscribe_mailer",
|
||||
site_title: SiteSetting.title,
|
||||
site_domain_name: Discourse.current_hostname,
|
||||
confirm_unsubscribe_link: "#{Discourse.base_url}/unsubscribe/#{unsubscribe_key}?from_all=true"
|
||||
end
|
||||
end
|
|
@ -307,6 +307,7 @@ class UserNotifications < ActionMailer::Base
|
|||
context: context,
|
||||
username: username,
|
||||
add_unsubscribe_link: !user.staged,
|
||||
add_unsubscribe_via_email_link: user.mailing_list_mode,
|
||||
unsubscribe_url: post.topic.unsubscribe_url,
|
||||
allow_reply_by_email: allow_reply_by_email,
|
||||
use_site_subject: use_site_subject,
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<hr>
|
||||
|
||||
<div class='footer'>%{respond_instructions}</div>
|
||||
<div class='footer'>%{unsubscribe_link}</div>
|
||||
<div class='footer'>%{unsubscribe_link}%{unsubscribe_via_email_link}</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1104,6 +1104,9 @@ en:
|
|||
short_email_length: "Short email length in Bytes"
|
||||
display_name_on_email_from: "Display full names on email from fields"
|
||||
|
||||
unsubscribe_via_email: "Allow users to unsubscribe from emails by sending an email with 'unsubscribe' in the subject or body"
|
||||
unsubscribe_via_email_footer: "Attach an unsubscribe link to the footer of sent emails"
|
||||
|
||||
pop3_polling_enabled: "Poll via POP3 for email replies."
|
||||
pop3_polling_ssl: "Use SSL while connecting to the POP3 server. (Recommended)"
|
||||
pop3_polling_period_mins: "The period in minutes between checking the POP3 account for email. NOTE: requires restart."
|
||||
|
@ -1379,6 +1382,17 @@ en:
|
|||
blocked: "New registrations are not allowed from your IP address."
|
||||
max_new_accounts_per_registration_ip: "New registrations are not allowed from your IP address (maximum limit reached). Contact a staff member."
|
||||
|
||||
unsubscribe_mailer:
|
||||
subject_template: "Confirm you no longer want to receive email updates from %{site_title}"
|
||||
text_body_template: |
|
||||
Someone (possibly you?) requested to no longer send email updates from %{site_domain_name} to this address.
|
||||
If you with to confirm this, please click this link:
|
||||
|
||||
%{confirm_unsubscribe_link}
|
||||
|
||||
|
||||
I you want to continue receiving email updates, you may ignore this email.
|
||||
|
||||
invite_mailer:
|
||||
subject_template: "%{invitee_name} invited you to '%{topic_title}' on %{site_domain_name}"
|
||||
text_body_template: |
|
||||
|
@ -1940,7 +1954,10 @@ en:
|
|||
text_body_template: "The `download_remote_images_to_local` setting was disabled because the disk space limit at `download_remote_images_threshold` was reached."
|
||||
|
||||
unsubscribe_link: |
|
||||
To stop receiving notifications for this particular topic, [click here](%{unsubscribe_url}). To unsubscribe from these emails, change your [user preferences](%{user_preferences_url}).
|
||||
To stop receiving notifications for this particular topic, [click here](%{unsubscribe_url}). To unsubscribe from these emails, change your [user preferences](%{user_preferences_url})
|
||||
|
||||
unsubscribe_via_email_link: |
|
||||
or, [click here](mailto:reply@%{hostname}?subject=unsubscribe) to unsubscribe via email.
|
||||
|
||||
subject_re: "Re: "
|
||||
subject_pm: "[PM] "
|
||||
|
|
|
@ -535,6 +535,10 @@ email:
|
|||
short_email_length: 2800
|
||||
display_name_on_email_from:
|
||||
default: true
|
||||
unsubscribe_via_email:
|
||||
default: true
|
||||
unsubscribe_via_email_footer:
|
||||
default: false
|
||||
|
||||
files:
|
||||
max_image_size_kb: 3072
|
||||
|
|
|
@ -63,6 +63,13 @@ module Email
|
|||
if @opts[:add_unsubscribe_link]
|
||||
unsubscribe_link = PrettyText.cook(I18n.t('unsubscribe_link', template_args), sanitize: false).html_safe
|
||||
html_override.gsub!("%{unsubscribe_link}", unsubscribe_link)
|
||||
|
||||
if SiteSetting.unsubscribe_via_email_footer && @opts[:add_unsubscribe_via_email_link]
|
||||
unsubscribe_via_email_link = PrettyText.cook(I18n.t('unsubscribe_via_email_link', hostname: Discourse.current_hostname), sanitize: false).html_safe
|
||||
html_override.gsub!("%{unsubscribe_via_email_link}", unsubscribe_via_email_link)
|
||||
else
|
||||
html_override.gsub!("%{unsubscribe_via_email_link}", "")
|
||||
end
|
||||
else
|
||||
html_override.gsub!("%{unsubscribe_link}", "")
|
||||
end
|
||||
|
@ -103,6 +110,9 @@ module Email
|
|||
if @opts[:add_unsubscribe_link]
|
||||
body << "\n"
|
||||
body << I18n.t('unsubscribe_link', template_args)
|
||||
if SiteSetting.unsubscribe_via_email_footer && @opts[:add_unsubscribe_via_email_link]
|
||||
body << I18n.t('unsubscribe_via_email_link', hostname: Discourse.current_hostname)
|
||||
end
|
||||
end
|
||||
|
||||
body
|
||||
|
|
|
@ -59,7 +59,10 @@ module Email
|
|||
|
||||
raise InactiveUserError if !user.active && !user.staged
|
||||
|
||||
if post = find_related_post
|
||||
if action = subscription_action_for(body, @mail.subject)
|
||||
message = SubscriptionMailer.send(action, user)
|
||||
Email::Sender.new(message, :subscription).send
|
||||
elsif post = find_related_post
|
||||
create_reply(user: user, raw: body, post: post, topic: post.topic)
|
||||
else
|
||||
destination = destinations.first
|
||||
|
@ -226,6 +229,13 @@ module Email
|
|||
@likes ||= Set.new ["+1", I18n.t('post_action_types.like.title').downcase]
|
||||
end
|
||||
|
||||
def subscription_action_for(body, subject)
|
||||
return unless SiteSetting.unsubscribe_via_email
|
||||
if ([subject, body].compact.map(&:to_s).map(&:downcase) & ['unsubscribe']).any?
|
||||
:confirm_unsubscribe
|
||||
end
|
||||
end
|
||||
|
||||
def post_action_for(body)
|
||||
if likes.include?(body.strip.downcase)
|
||||
PostActionType.types[:like]
|
||||
|
|
|
@ -181,6 +181,23 @@ describe Email::MessageBuilder do
|
|||
|
||||
end
|
||||
|
||||
context "with unsubscribe_via_email_link true" do
|
||||
let(:message_with_unsubscribe_via_email) { Email::MessageBuilder.new(to_address,
|
||||
body: 'hello world',
|
||||
add_unsubscribe_link: true,
|
||||
add_unsubscribe_via_email_link: true,
|
||||
unsubscribe_url: "/t/1234/unsubscribe") }
|
||||
|
||||
it "can add an unsubscribe via email link" do
|
||||
SiteSetting.stubs(:unsubscribe_via_email_footer).returns(true)
|
||||
expect(message_with_unsubscribe_via_email.body).to match(/mailto:reply@#{Discourse.current_hostname}\?subject=unsubscribe/)
|
||||
end
|
||||
|
||||
it "does not add unsubscribe via email link without site setting set" do
|
||||
expect(message_with_unsubscribe_via_email.body).to_not match(/mailto:reply@#{Discourse.current_hostname}\?subject=unsubscribe/)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "template_args" do
|
||||
|
|
|
@ -130,6 +130,46 @@ describe Email::Receiver do
|
|||
expect(topic.posts.last.raw).to eq("Do you like liquorice?\n\nI really like them. One could even say that I am *addicted* to liquorice. Anf if\nyou can mix it up with some anise, then I'm in heaven ;)")
|
||||
end
|
||||
|
||||
describe 'Unsubscribing via email' do
|
||||
let(:last_email) { ActionMailer::Base.deliveries.last }
|
||||
|
||||
describe 'unsubscribe_subject.eml' do
|
||||
it 'sends an email asking the user to confirm the unsubscription' do
|
||||
expect { process("unsubscribe_subject") }.to change { ActionMailer::Base.deliveries.count }.by(1)
|
||||
expect(last_email.to.length).to eq 1
|
||||
expect(last_email.from.length).to eq 1
|
||||
expect(last_email.from).to include "noreply@#{Discourse.current_hostname}"
|
||||
expect(last_email.to).to include "discourse@bar.com"
|
||||
expect(last_email.subject).to eq I18n.t(:"unsubscribe_mailer.subject_template").gsub("%{site_title}", SiteSetting.title)
|
||||
end
|
||||
|
||||
it 'does nothing unless unsubscribe_via_email is turned on' do
|
||||
SiteSetting.stubs("unsubscribe_via_email").returns(false)
|
||||
before_deliveries = ActionMailer::Base.deliveries.count
|
||||
expect { process("unsubscribe_subject") }.to raise_error { Email::Receiver::BadDestinationAddress }
|
||||
expect(before_deliveries).to eq ActionMailer::Base.deliveries.count
|
||||
end
|
||||
end
|
||||
|
||||
describe 'unsubscribe_body.eml' do
|
||||
it 'sends an email asking the user to confirm the unsubscription' do
|
||||
expect { process("unsubscribe_body") }.to change { ActionMailer::Base.deliveries.count }.by(1)
|
||||
expect(last_email.to.length).to eq 1
|
||||
expect(last_email.from.length).to eq 1
|
||||
expect(last_email.from).to include "noreply@#{Discourse.current_hostname}"
|
||||
expect(last_email.to).to include "discourse@bar.com"
|
||||
expect(last_email.subject).to eq I18n.t(:"unsubscribe_mailer.subject_template").gsub("%{site_title}", SiteSetting.title)
|
||||
end
|
||||
|
||||
it 'does nothing unless unsubscribe_via_email is turned on' do
|
||||
SiteSetting.stubs(:unsubscribe_via_email).returns(false)
|
||||
before_deliveries = ActionMailer::Base.deliveries.count
|
||||
expect { process("unsubscribe_body") }.to raise_error { Email::Receiver::InvalidPost }
|
||||
expect(before_deliveries).to eq ActionMailer::Base.deliveries.count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "handles inline reply" do
|
||||
expect { process(:inline_reply) }.to change { topic.posts.count }
|
||||
expect(topic.posts.last.raw).to eq("On Tue, Jan 15, 2016 at 11:12 AM, Bar Foo <info@unconfigured.discourse.org> wrote:\n\n> WAT <https://bar.com/users/wat> November 28\n>\n> This is the previous post.\n\nAnd this is *my* reply :+1:")
|
||||
|
|
|
@ -39,9 +39,23 @@ describe EmailController do
|
|||
|
||||
context '.unsubscribe' do
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:user) { Fabricate(:user, email_digests: true, email_direct: true, email_private_messages: true, email_always: true) }
|
||||
let(:key) { DigestUnsubscribeKey.create_key_for(user) }
|
||||
|
||||
context 'from confirm unsubscribe email' do
|
||||
before do
|
||||
get :unsubscribe, key: key, from_all: true
|
||||
user.reload
|
||||
end
|
||||
|
||||
it 'unsubscribes from all emails' do
|
||||
expect(user.email_digests).to eq false
|
||||
expect(user.email_direct).to eq false
|
||||
expect(user.email_private_messages).to eq false
|
||||
expect(user.email_always).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a valid key' do
|
||||
before do
|
||||
get :unsubscribe, key: key
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
Return-Path: <discourse@bar.com>
|
||||
From: Foo Bar <discourse@bar.com>
|
||||
To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com
|
||||
Date: Thu, 13 Jun 2013 17:03:48 -0400
|
||||
Message-ID: <55@foo.bar.mail>
|
||||
Mime-Version: 1.0
|
||||
Content-Type: text/plain;
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
UNSUBSCRIBE
|
|
@ -0,0 +1,11 @@
|
|||
Return-Path: <discourse@bar.com>
|
||||
From: Foo Bar <discourse@bar.com>
|
||||
To: reply@bar.com
|
||||
Date: Thu, 13 Jun 2013 17:03:48 -0400
|
||||
Message-ID: <56@foo.bar.mail>
|
||||
Subject: UnSuBScRiBe
|
||||
Mime-Version: 1.0
|
||||
Content-Type: text/plain;
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
I've basically had enough of your mailing list and would very much like it if you went away.
|
Loading…
Reference in New Issue