From be0267956bec0a5d1c8bb5cb875cfee2ebcf83a3 Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Wed, 7 Jan 2015 15:24:15 -0800 Subject: [PATCH] easy unsubscribe baked into emails --- app/settings.rb | 21 ++++++++++- models/site.rb | 8 ++++ tests/acceptance/settings/account_tests.rb | 43 ++++++++++++++++++++++ tests/workers/email_worker_tests.rb | 5 ++- views/settings/account/unsubscribe.erb | 11 ++++++ workers/email_worker.rb | 6 ++- 6 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 views/settings/account/unsubscribe.erb diff --git a/app/settings.rb b/app/settings.rb index 12373380..29387b36 100644 --- a/app/settings.rb +++ b/app/settings.rb @@ -15,7 +15,10 @@ def require_ownership_for_settings end end -get '/settings/:username/?' do +get '/settings/:username/?' do |username| + # This is for the email_unsubscribe below + pass if Site.select(:id).where(username: username).first.nil? + require_login require_ownership_for_settings erb :'settings/site' @@ -281,4 +284,20 @@ post '/settings/create_child' do flash[:error] = site.errors.first.last.first redirect '/settings#sites' end +end + +get '/settings/unsubscribe_email/?' do + redirect "/settings/#email" if signed_in? + + if params[:email] && params[:token] && params[:email] != '' && Site.valid_email_unsubscribe_token?(params[:email], params[:token]) + Site.where(email: params[:email]).all.each do |site| + site.send_emails = false + site.save_changes validate: false + end + + @message = "You have been successfully unsubscribed from future emails to #{params[:email]}. Our apologies for the inconvenience." + else + @message = 'There was an error unsubscribing your email address. Please contact support.' + end + erb :'settings/account/unsubscribe' end \ No newline at end of file diff --git a/models/site.rb b/models/site.rb index 5eba1a74..8ed6dfeb 100644 --- a/models/site.rb +++ b/models/site.rb @@ -206,6 +206,14 @@ class Site < Sequel::Model end class << self + def valid_email_unsubscribe_token?(email, token) + email_unsubscribe_token(email) == token + end + + def email_unsubscribe_token(email) + Digest::SHA2.hexdigest email+$config['email_unsubscribe_token'] + end + def valid_login?(username_or_email, plaintext) site = get_with_identifier username_or_email diff --git a/tests/acceptance/settings/account_tests.rb b/tests/acceptance/settings/account_tests.rb index f9d419c7..90a05483 100644 --- a/tests/acceptance/settings/account_tests.rb +++ b/tests/acceptance/settings/account_tests.rb @@ -66,6 +66,49 @@ describe 'site/settings' do end end + describe 'unsubscribe email' do + include Capybara::DSL + + before do + @email = "#{SecureRandom.uuid.gsub('-', '')}@example.com" + @site = Fabricate :site, email: @email + EmailWorker.jobs.clear + Mail::TestMailer.deliveries.clear + + @params = { + email: @site.email, + token: Site.email_unsubscribe_token(@site.email) + } + @params_query = Rack::Utils.build_query(@params) + + @email_unsubscribe_url = "https://neocities.org/settings/unsubscribe_email?"+@params_query + page.set_rack_session id: nil + end + + it 'should redirect to settings page if logged in' do + page.set_rack_session id: @site.id + + end + + it 'should unsubscribe for valid token' do + @site.send_email subject: 'Hello', body: 'Okay' + EmailWorker.drain + email = Mail::TestMailer.deliveries.first + + email.body.to_s.must_match @email_unsubscribe_url + @site.send_emails.must_equal true + visit '/settings/unsubscribe_email?'+@params_query + + page.body.must_match /You have been successfully unsubscribed.+#{@site.email}/i + + @site.reload.send_emails.must_equal false + end + + it 'should fail to subscribe for bad token' do + + end + end + describe 'change password' do include Capybara::DSL diff --git a/tests/workers/email_worker_tests.rb b/tests/workers/email_worker_tests.rb index 3b168429..05345ba3 100644 --- a/tests/workers/email_worker_tests.rb +++ b/tests/workers/email_worker_tests.rb @@ -18,6 +18,9 @@ describe EmailWorker do mail.from.first.must_equal 'from@example.com' mail.to.first.must_equal 'to@example.com' mail.subject.must_equal 'Hello World' - mail.body.to_s.must_equal 'testing' + body = mail.body.to_s + puts body + body.must_match /testing/ + body.must_match /unsubscribe/ end end \ No newline at end of file diff --git a/views/settings/account/unsubscribe.erb b/views/settings/account/unsubscribe.erb new file mode 100644 index 00000000..03157256 --- /dev/null +++ b/views/settings/account/unsubscribe.erb @@ -0,0 +1,11 @@ +
+
+

Email Unsubscribe

+
+
+ +
+
+ <%= @message %> +
+
\ No newline at end of file diff --git a/workers/email_worker.rb b/workers/email_worker.rb index e4980255..386f5c6c 100644 --- a/workers/email_worker.rb +++ b/workers/email_worker.rb @@ -3,13 +3,17 @@ class EmailWorker sidekiq_options queue: :emails, retry: 10, backtrace: true def perform(args={}) + unsubscribe_token = Site.email_unsubscribe_token args['to'] + + footer = "\n\n---\nYou are receiving this email because you have a Neocities site. If you would like to subscribe from Neocities emails, just visit this url:\nhttps://neocities.org/settings/unsubscribe_email?email=#{Rack::Utils.escape args['to']}&token=#{unsubscribe_token}" + Mail.deliver do # TODO this is not doing UTF-8 properly. from args['from'] reply_to args['reply_to'] to args['to'] subject args['subject'] - body args['body'] + body args['body']+footer end end end