From f637d9e02be3dbb06b47a21dea3b825c898238f3 Mon Sep 17 00:00:00 2001 From: Jess Lee Date: Wed, 10 Apr 2019 14:45:06 -0400 Subject: [PATCH] Manage tag and community mod lists in Mailchimp (#2362) * add moderation email list columns * automate tag moderator mailing list * reorder logic for tag mod list * add community mod list * remove trusted privileges when reverting a user to a regular member * automatically make tag mods community mods * fix merge conflicts * add community view * make methods less complex based on code climate sugggestion * add specs * refactor tag moderator module * fix spec --- Envfile | 2 + app/controllers/internal/tags_controller.rb | 14 ++- app/labor/assign_tag_moderator.rb | 27 +++++- app/labor/mailchimp_bot.rb | 96 +++++++++++++++++-- app/models/user.rb | 4 + app/policies/user_policy.rb | 2 + .../moderator/manage_activity_and_roles.rb | 57 ++++++++--- app/views/users/_notifications.html.erb | 12 +++ config/routes.rb | 1 - ...0405190915_add_mailchimp_lists_to_users.rb | 6 ++ db/schema.rb | 4 +- spec/labor/mailchimp_bot_spec.rb | 47 +++++++++ 12 files changed, 244 insertions(+), 28 deletions(-) create mode 100644 db/migrate/20190405190915_add_mailchimp_lists_to_users.rb diff --git a/Envfile b/Envfile index eead8e4f2..508bfa8df 100644 --- a/Envfile +++ b/Envfile @@ -109,6 +109,8 @@ variable :HONEYCOMB_API_KEY, :String, default: "Optional" variable :MAILCHIMP_API_KEY, :String, default: "Optional-valid" variable :MAILCHIMP_NEWSLETTER_ID, :String, default: "Optional" variable :MAILCHIMP_SUSTAINING_MEMBERS_ID, :String, default: "Optional" +variable :MAILCHIMP_TAG_MODERATORS_ID, :String, default: "Optional" +variable :MAILCHIMP_COMMUNITY_MODERATORS_ID, :String, default: "Optional" # Email digest frequency variable :PERIODIC_EMAIL_DIGEST_MAX, :Integer, default: 0 diff --git a/app/controllers/internal/tags_controller.rb b/app/controllers/internal/tags_controller.rb index 5e8e123b6..09c60c776 100644 --- a/app/controllers/internal/tags_controller.rb +++ b/app/controllers/internal/tags_controller.rb @@ -22,8 +22,10 @@ class Internal::TagsController < Internal::ApplicationController def update @tag = Tag.find(params[:id]) - add_moderator if params[:tag][:tag_moderator_id] - remove_moderator if params[:tag][:remove_moderator_id] + @add_user_id = params[:tag][:tag_moderator_id] + @remove_user_id = params[:tag][:remove_moderator_id] + add_moderator if @add_user_id + remove_moderator if @remove_user_id @tag.update!(tag_params) redirect_to "/internal/tags/#{params[:id]}" end @@ -31,12 +33,14 @@ class Internal::TagsController < Internal::ApplicationController private def remove_moderator - User.find(params[:tag][:remove_moderator_id]).remove_role :tag_moderator, @tag + user = User.find(@remove_user_id) + user.update(email_tag_mod_newsletter: false) + AssignTagModerator.remove_tag_moderator(user, @tag) end def add_moderator - user_id = params[:tag][:tag_moderator_id] - AssignTagModerator.add_tag_moderators([user_id], [@tag.id]) + User.find(@add_user_id).update(email_tag_mod_newsletter: true) + AssignTagModerator.add_tag_moderators([@add_user_id], [@tag.id]) end def tag_params diff --git a/app/labor/assign_tag_moderator.rb b/app/labor/assign_tag_moderator.rb index 2dbd650f9..b583c32fb 100644 --- a/app/labor/assign_tag_moderator.rb +++ b/app/labor/assign_tag_moderator.rb @@ -1,11 +1,34 @@ module AssignTagModerator + def self.add_trusted_role(user) + user.add_role :trusted + user.update(email_community_mod_newsletter: true) + MailchimpBot.new(user).manage_community_moderator_list + end + + def self.add_to_chat_channel(user) + ChatChannel.find_by(slug: "tag-moderators").add_users(user) if user.chat_channels.find_by(slug: "tag-moderators").blank? + end + + def self.add_tag_mod_role(user, tag) + user.update(email_tag_mod_newsletter: true) if user.email_tag_mod_newsletter == false + user.add_role(:tag_moderator, tag) + MailchimpBot.new(user).manage_tag_moderator_list + end + def self.add_tag_moderators(user_ids, tag_ids) user_ids.each_with_index do |user_id, index| user = User.find(user_id) tag = Tag.find(tag_ids[index]) - user.add_role(:tag_moderator, tag) - ChatChannel.find_by(slug: "tag-moderators").add_users(user) if user.chat_channels.find_by(slug: "tag-moderators").blank? + add_tag_mod_role(user, tag) + add_trusted_role(user) + add_to_chat_channel(user) NotifyMailer.tag_moderator_confirmation_email(user, tag.name).deliver unless tag.name == "go" end end + + def self.remove_tag_moderator(user, tag) + user.remove_role(:tag_moderator, tag) + user.update(email_tag_mod_newsletter: false) if user.email_tag_mod_newsletter == true + MailchimpBot.new(user).manage_tag_moderator_list + end end diff --git a/app/labor/mailchimp_bot.rb b/app/labor/mailchimp_bot.rb index 8bc06148e..2b33deceb 100644 --- a/app/labor/mailchimp_bot.rb +++ b/app/labor/mailchimp_bot.rb @@ -11,6 +11,8 @@ class MailchimpBot return true unless Rails.env.production? || Rails.env.test? upsert_to_membership_newsletter + manage_community_moderator_list + manage_tag_moderator_list upsert_to_newsletter end @@ -45,6 +47,60 @@ class MailchimpBot success end + def manage_community_moderator_list + return false unless user.has_role?(:trusted) + + success = false + status = user.email_community_mod_newsletter ? "subscribed" : "unsubscribed" + begin + gibbon.lists(ApplicationConfig["MAILCHIMP_COMMUNITY_MODERATORS_ID"]).members(target_md5_email).upsert( + body: { + email_address: user.email, + status: status, + merge_fields: { + NAME: user.name.to_s, + USERNAME: user.username.to_s, + TWITTER: user.twitter_username.to_s, + GITHUB: user.github_username.to_s, + IMAGE_URL: user.profile_image_url.to_s + } + }, + ) + success = true + rescue Gibbon::MailChimpError => e + report_error(e) + end + success + end + + def manage_tag_moderator_list + return false unless user.tag_moderator? + + success = false + tags = user.roles.where(name: "tag_moderator").map { |t| Tag.find(t.resource_id).name } + status = user.email_tag_mod_newsletter ? "subscribed" : "unsubscribed" + begin + gibbon.lists(ApplicationConfig["MAILCHIMP_TAG_MODERATORS_ID"]).members(target_md5_email).upsert( + body: { + email_address: user.email, + status: status, + merge_fields: { + NAME: user.name.to_s, + USERNAME: user.username.to_s, + TWITTER: user.twitter_username.to_s, + GITHUB: user.github_username.to_s, + IMAGE_URL: user.profile_image_url.to_s, + TAGS: tags.join(", ") + } + }, + ) + success = true + rescue Gibbon::MailChimpError => e + report_error(e) + end + success + end + def upsert_to_membership_newsletter return false unless a_sustaining_member? @@ -80,6 +136,36 @@ class MailchimpBot success end + def unsub_sustaining_member + return unless user.tag_moderator? + + gibbon.lists(ApplicationConfig["MAILCHIMP_TAG_MODERATORS_ID"]).members(target_md5_email).update( + body: { + status: "unsubscribed" + }, + ) + end + + def unsub_community_mod + return unless user.has_role?(:trusted) + + gibbon.lists(ApplicationConfig["MAILCHIMP_COMMUNITY_MODERATORS_ID"]).members(target_md5_email).update( + body: { + status: "unsubscribed" + }, + ) + end + + def unsub_tag_mod + return unless a_sustaining_member? + + gibbon.lists(ApplicationConfig["MAILCHIMP_SUSTAINING_MEMBERS_ID"]).members(target_md5_email).update( + body: { + status: "unsubscribed" + }, + ) + end + def unsubscribe_all_newsletters success = false begin @@ -88,13 +174,9 @@ class MailchimpBot status: "unsubscribed" }, ) - if a_sustaining_member? - gibbon.lists(ApplicationConfig["MAILCHIMP_SUSTAINING_MEMBERS_ID"]).members(target_md5_email).update( - body: { - status: "unsubscribed" - }, - ) - end + unsub_tag_mod + unsub_sustaining_member + unsub_community_mod success = true rescue Gibbon::MailChimpError => e report_error(e) diff --git a/app/models/user.rb b/app/models/user.rb index e3b1fa6a5..40446055e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -406,6 +406,10 @@ class User < ApplicationRecord MailchimpBot.new(self).unsubscribe_all_newsletters end + def tag_moderator? + roles.where(name: "tag_moderator").any? + end + private def send_welcome_notification diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index 7ee2beda7..50408eede 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -69,6 +69,8 @@ class UserPolicy < ApplicationPolicy email_digest_periodic email_follower_notifications email_membership_newsletter + email_tag_mod_newsletter + email_community_mod_newsletter email_mention_notifications email_connect_messages email_newsletter diff --git a/app/services/moderator/manage_activity_and_roles.rb b/app/services/moderator/manage_activity_and_roles.rb index 9a1b3e6b8..ee54b51c4 100644 --- a/app/services/moderator/manage_activity_and_roles.rb +++ b/app/services/moderator/manage_activity_and_roles.rb @@ -53,11 +53,25 @@ module Moderator end def remove_privileges - @user.remove_role :trusted @user.remove_role :video_permission - @user.remove_role :tag_moderator @user.remove_role :workshop_pass @user.remove_role :pro + remove_mod_roles + remove_tag_moderator_role + end + + def remove_mod_roles + @user.remove_role :trusted + @user.remove_role :tag_moderator + @user.update(email_tag_mod_newsletter: false) + MailchimpBot.new(user).manage_tag_moderator_list + @user.update(email_community_mod_newsletter: false) + MailchimpBot.new(user).manage_community_moderator_list + end + + def remove_tag_moderator_role + @user.remove_role :tag_moderator + MailchimpBot.new(user).manage_tag_moderator_list end def create_note(reason, content) @@ -76,28 +90,47 @@ module Moderator user.add_role :banned remove_privileges when "Warn" - user.add_role :warned - user.remove_role :banned - remove_privileges + warned when "Comment Ban" - user.add_role :comment_banned - user.remove_role :banned - remove_privileges + comment_banned when "Regular Member" - remove_negative_roles - user.remove_role :pro + regular_member when "Trusted" remove_negative_roles user.remove_role :pro - user.add_role :trusted + add_trusted_role when "Pro" remove_negative_roles - user.add_role :trusted + add_trusted_role user.add_role :pro end create_note(role, note) end + def comment_banned + user.add_role :comment_banned + user.remove_role :banned + remove_privileges + end + + def regular_member + remove_negative_roles + user.remove_role :pro + remove_mod_roles + end + + def warned + user.add_role :warned + user.remove_role :banned + remove_privileges + end + + def add_trusted_role + user.add_role :trusted + user.update(email_community_mod_newsletter: true) + MailchimpBot.new(user).manage_community_moderator_list + end + def remove_negative_roles user.remove_role :banned if user.banned user.remove_role :warned if user.warned diff --git a/app/views/users/_notifications.html.erb b/app/views/users/_notifications.html.erb index 30ac5f26c..5d8df1008 100644 --- a/app/views/users/_notifications.html.erb +++ b/app/views/users/_notifications.html.erb @@ -15,6 +15,18 @@ <%= f.label :email_membership_newsletter, "Send me sustaining membership newsletter emails" %> <% end %> + <% if current_user.tag_moderator? %> +
+ <%= f.check_box :email_tag_mod_newsletter %> + <%= f.label :email_tag_mod_newsletter, "Send me tag moderator newsletter emails" %> +
+ <% end %> + <% if current_user.has_role?(:trusted) %> +
+ <%= f.check_box :email_community_mod_newsletter %> + <%= f.label :email_community_mod_newsletter, "Send me community moderator newsletter emails" %> +
+ <% end %>
<%= f.check_box :email_comment_notifications %> <%= f.label :email_comment_notifications, "Send me an email when someone replies to me in a comment thread" %> diff --git a/config/routes.rb b/config/routes.rb index d617179d9..506b5d503 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -139,7 +139,6 @@ Rails.application.routes.draw do resources :rating_votes, only: [:create] resources :page_views, only: %i[create update] resources :buffer_updates, only: [:create] - get "/notifications/:filter" => "notifications#index" get "/notifications/:filter/:org_id" => "notifications#index" patch "/onboarding_update" => "users#onboarding_update" diff --git a/db/migrate/20190405190915_add_mailchimp_lists_to_users.rb b/db/migrate/20190405190915_add_mailchimp_lists_to_users.rb new file mode 100644 index 000000000..e1cef9562 --- /dev/null +++ b/db/migrate/20190405190915_add_mailchimp_lists_to_users.rb @@ -0,0 +1,6 @@ +class AddMailchimpListsToUsers < ActiveRecord::Migration[5.2] + def change + add_column :users, :email_tag_mod_newsletter, :boolean, default: false + add_column :users, :email_community_mod_newsletter, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 0d66ac43d..bbf57dd19 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_04_04_102732) do +ActiveRecord::Schema.define(version: 2019_04_05_190915) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -779,6 +779,7 @@ ActiveRecord::Schema.define(version: 2019_04_04_102732) do t.string "email" t.boolean "email_badge_notifications", default: true t.boolean "email_comment_notifications", default: true + t.boolean "email_community_mod_newsletter", default: false t.boolean "email_connect_messages", default: true t.boolean "email_digest_periodic", default: true, null: false t.boolean "email_follower_notifications", default: true @@ -786,6 +787,7 @@ ActiveRecord::Schema.define(version: 2019_04_04_102732) do t.boolean "email_mention_notifications", default: true t.boolean "email_newsletter", default: true t.boolean "email_public", default: false + t.boolean "email_tag_mod_newsletter", default: false t.boolean "email_unread_notifications", default: true t.string "employer_name" t.string "employer_url" diff --git a/spec/labor/mailchimp_bot_spec.rb b/spec/labor/mailchimp_bot_spec.rb index f0ca1f415..b78b8201d 100644 --- a/spec/labor/mailchimp_bot_spec.rb +++ b/spec/labor/mailchimp_bot_spec.rb @@ -8,16 +8,27 @@ class FakeGibbonRequest < Gibbon::Request def members(*args) super end + + def tag_mods(*args) + super + end + + def community_mods(*args) + super + end end RSpec.describe MailchimpBot do let(:user) { create(:user, :ignore_after_callback) } let(:article) { create(:article, user_id: user.id) } let(:my_gibbon_client) { instance_double(FakeGibbonRequest) } + let(:tag) { create(:tag, name: "tag name", bg_color_hex: Faker::Color.hex_color, text_color_hex: Faker::Color.hex_color, supported: true) } before do allow(Gibbon::Request).to receive(:new) { my_gibbon_client } allow(my_gibbon_client).to receive(:lists) { my_gibbon_client } + allow(my_gibbon_client).to receive(:tag_mods) { my_gibbon_client } + allow(my_gibbon_client).to receive(:community_mods) { my_gibbon_client } allow(my_gibbon_client).to receive(:members) { my_gibbon_client } allow(my_gibbon_client).to receive(:upsert).and_return(true) end @@ -82,6 +93,42 @@ RSpec.describe MailchimpBot do end end + describe "manage community moderator list" do + it "returns false if user isn't a community moderator" do + expect(described_class.new(user).manage_community_moderator_list).to be(false) + end + + it "sends proper information" do + user.update(email_community_mod_newsletter: true) + user.add_role :trusted + described_class.new(user).manage_community_moderator_list + expect(my_gibbon_client).to have_received(:upsert). + with(hash_including( + body: hash_including( + status: "subscribed", + ), + )) + end + end + + describe "manage tag moderator list" do + it "returns false if user isn't a tag moderator" do + expect(described_class.new(user).manage_community_moderator_list).to be(false) + end + + it "sends proper information" do + user.update(email_tag_mod_newsletter: true) + user.add_role(:tag_moderator, tag) + described_class.new(user).manage_tag_moderator_list + expect(my_gibbon_client).to have_received(:upsert). + with(hash_including( + body: hash_including( + status: "subscribed", + ), + )) + end + end + describe "#upsert_to_membership_newsletter" do it "returns false if user isn't a sustaining member" do expect(described_class.new(user).upsert_to_membership_newsletter).to be(false)