effective_mailchimp 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/admin/mailchimp_categories_controller.rb +15 -0
  3. data/app/controllers/admin/mailchimp_controller.rb +25 -0
  4. data/app/controllers/admin/mailchimp_interests_controller.rb +15 -0
  5. data/app/controllers/admin/mailchimp_list_members_controller.rb +15 -0
  6. data/app/controllers/admin/mailchimp_lists_controller.rb +0 -10
  7. data/app/datatables/admin/effective_mailchimp_categories_datatable.rb +32 -0
  8. data/app/datatables/admin/effective_mailchimp_interests_datatable.rb +47 -0
  9. data/app/datatables/admin/effective_mailchimp_list_members_datatable.rb +39 -0
  10. data/app/datatables/admin/effective_mailchimp_lists_datatable.rb +5 -3
  11. data/app/helpers/effective_mailchimp_helper.rb +10 -0
  12. data/app/models/concerns/effective_mailchimp_user.rb +5 -1
  13. data/app/models/effective/mailchimp_api.rb +43 -15
  14. data/app/models/effective/mailchimp_category.rb +73 -0
  15. data/app/models/effective/mailchimp_interest.rb +92 -0
  16. data/app/models/effective/mailchimp_list.rb +35 -37
  17. data/app/models/effective/mailchimp_list_member.rb +42 -1
  18. data/app/views/admin/mailchimp/_sync.html.haml +15 -0
  19. data/app/views/admin/mailchimp/index.html.haml +80 -0
  20. data/app/views/admin/mailchimp_interests/_form.html.haml +10 -0
  21. data/app/views/admin/mailchimp_lists/_form.html.haml +1 -0
  22. data/app/views/admin/mailchimp_user/_form.html.haml +10 -4
  23. data/app/views/effective/mailchimp_user/_fields.html.haml +36 -9
  24. data/config/locales/effective_mailchimp.en.yml +12 -0
  25. data/config/routes.rb +7 -9
  26. data/db/migrate/101_create_effective_mailchimp.rb +37 -0
  27. data/lib/effective_mailchimp/version.rb +1 -1
  28. data/lib/effective_mailchimp.rb +6 -2
  29. data/lib/tasks/effective_mailchimp_tasks.rake +0 -16
  30. metadata +13 -2
  31. data/app/views/admin/mailchimp_lists/index.html.haml +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3502aae94186923697f39763042a537f6adf5426396e576b2ce17821cc892c70
4
- data.tar.gz: dac06ef19961ed0e8eab2a548f2d7a810c50fbfccdd7219d0d198247f69353b2
3
+ metadata.gz: d3b3306349bc71e4dcd895a1be96411b94e82c76155972d5ee73ad302487fa5d
4
+ data.tar.gz: 590439bc94cc48d34586b14eeba77476e5fc715ee32118a973c640fdc25e6def
5
5
  SHA512:
6
- metadata.gz: 73df6b018f47d66c0d5ba1fa27a02124c65c9544e6322e37bad2240cc21e2f73baae853bb68bb3e3c6ac8ef691ba6d627958988f2521884cf21244526b15da6f
7
- data.tar.gz: 021eca9c105c49ee522df6c0b60f3d64e608ec9a8adc3e0930a018e6ff4ef56c8965796efdee1e0c9e1b3d988bb8b4f7578e022ffcc665a49a14c087683dfbe4
6
+ metadata.gz: 59eab329289651cd579828198bafbad8093aa94e452866f6dc2a51b5a787a7b077e57d644e5caa7cf91b868d12db82bdb28284680e7a3608f216c7bbbd4e73ad
7
+ data.tar.gz: fc0bbcadfffd0caae215be540ad150bf588264ae7ee013b4a4cbe8d0ea7064fe603d4f9bd4abfe70c6ef341d931d5e1ec1bed60f15735f5603f777e54c827d1a
@@ -0,0 +1,15 @@
1
+ module Admin
2
+ class MailchimpCategoriesController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_mailchimp) }
5
+
6
+ include Effective::CrudController
7
+
8
+ private
9
+
10
+ def permitted_params
11
+ params.require(:effective_mailchimp_category).permit!
12
+ end
13
+
14
+ end
15
+ end
@@ -3,6 +3,31 @@ module Admin
3
3
  before_action(:authenticate_user!) if defined?(Devise)
4
4
  before_action { EffectiveResources.authorize!(self, :admin, :effective_mailchimp) }
5
5
 
6
+ include Effective::CrudController
7
+
8
+ page_title 'Mailchimp'
9
+
10
+ # /admin/mailchimp
11
+ def index
12
+ end
13
+
14
+ # Sync All
15
+ def mailchimp_sync
16
+ EffectiveResources.authorize!(self, :admin, :mailchimp_sync)
17
+
18
+ api = EffectiveMailchimp.api
19
+ merge_fields = current_user.class.new().mailchimp_merge_fields
20
+
21
+ Effective::MailchimpList.sync!(api: api, merge_fields: merge_fields)
22
+ Effective::MailchimpCategory.sync!(api: api)
23
+ Effective::MailchimpInterest.sync!(api: api)
24
+
25
+ flash[:success] = "Successfully synced mailchimp data"
26
+
27
+ redirect_back(fallback_location: effective_mailchimp.admin_mailchimp_path)
28
+ end
29
+
30
+ # Sync one user
6
31
  def mailchimp_sync_user
7
32
  resource = current_user.class.find(params[:id])
8
33
 
@@ -0,0 +1,15 @@
1
+ module Admin
2
+ class MailchimpInterestsController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_mailchimp) }
5
+
6
+ include Effective::CrudController
7
+
8
+ private
9
+
10
+ def permitted_params
11
+ params.require(:effective_mailchimp_interest).permit!
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Admin
2
+ class MailchimpListMembersController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_mailchimp) }
5
+
6
+ include Effective::CrudController
7
+
8
+ private
9
+
10
+ def permitted_params
11
+ params.require(:effective_mailchimp_list_member).permit!
12
+ end
13
+
14
+ end
15
+ end
@@ -5,16 +5,6 @@ module Admin
5
5
 
6
6
  include Effective::CrudController
7
7
 
8
- def mailchimp_sync
9
- EffectiveResources.authorize!(self, :mailchimp_sync, Effective::MailchimpList)
10
-
11
- Effective::MailchimpList.sync!
12
-
13
- flash[:success] = "Successfully synced mailchimp lists"
14
-
15
- redirect_back(fallback_location: effective_mailchimp.admin_mailchimp_lists_path)
16
- end
17
-
18
8
  private
19
9
 
20
10
  def permitted_params
@@ -0,0 +1,32 @@
1
+ module Admin
2
+ class EffectiveMailchimpCategoriesDatatable < Effective::Datatable
3
+
4
+ datatable do
5
+ length :all
6
+
7
+ order :name
8
+
9
+ col :updated_at, visible: false
10
+ col :created_at, visible: false
11
+ col :id, visible: false
12
+
13
+ col :mailchimp_list
14
+
15
+ col :list_id, visible: false
16
+ col :mailchimp_id, visible: false
17
+
18
+ col :name
19
+ col :list_name, visible: false
20
+
21
+ col :display_type
22
+
23
+ col :mailchimp_interests
24
+
25
+ actions_col
26
+ end
27
+
28
+ collection do
29
+ Effective::MailchimpCategory.deep.all
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,47 @@
1
+ module Admin
2
+ class EffectiveMailchimpInterestsDatatable < Effective::Datatable
3
+ filters do
4
+ scope :all
5
+ scope :subscribable
6
+ end
7
+
8
+ datatable do
9
+ length :all
10
+ order :display_order
11
+
12
+ col :updated_at, visible: false
13
+ col :created_at, visible: false
14
+ col :id, visible: false
15
+
16
+ col :mailchimp_list
17
+ col :mailchimp_category
18
+
19
+ col :list_id, visible: false
20
+ col :category_id, visible: false
21
+ col :mailchimp_id, visible: false
22
+
23
+ col :name
24
+ col :can_subscribe
25
+ col :force_subscribe
26
+
27
+ col :list_name, visible: false
28
+ col :category_name, visible: false
29
+ col :display_order, visible: false
30
+
31
+ col :subscriber_count
32
+
33
+ actions_col
34
+ end
35
+
36
+ collection do
37
+ scope = Effective::MailchimpInterest.deep
38
+
39
+ if attributes[:mailchimp_category_id].present?
40
+ scope = scope.sorted
41
+ end
42
+
43
+ scope
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,39 @@
1
+ module Admin
2
+ class EffectiveMailchimpListMembersDatatable < Effective::Datatable
3
+ filters do
4
+ scope :all
5
+ scope :subscribed
6
+ end
7
+
8
+ datatable do
9
+ order :updated_at
10
+
11
+ col :updated_at, visible: false
12
+ col :created_at, visible: false
13
+ col :id, visible: false
14
+
15
+ col :mailchimp_list
16
+ col :user
17
+
18
+ col :mailchimp_id, visible: false
19
+ col :web_id, visible: false
20
+
21
+ col :email_address
22
+ col :full_name
23
+ col :subscribed
24
+
25
+ col :interests, visible: false
26
+ col :mailchimp_interests, search: { collection: Effective::MailchimpInterest.order(:name), fuzzy: true }
27
+
28
+ col :last_synced_at
29
+
30
+ actions_col do |member|
31
+ dropdown_link_to('Edit', "/admin/users/#{member.user.to_param}/edit#tab-mailchimp", 'data-turbolinks': false)
32
+ end
33
+ end
34
+
35
+ collection do
36
+ Effective::MailchimpListMember.deep.all
37
+ end
38
+ end
39
+ end
@@ -6,7 +6,8 @@ module Admin
6
6
  end
7
7
 
8
8
  datatable do
9
- order :updated_at
9
+ length :all
10
+ order :name
10
11
 
11
12
  col :updated_at, visible: false
12
13
  col :created_at, visible: false
@@ -21,13 +22,14 @@ module Admin
21
22
 
22
23
  col :url, label: 'Mailchimp' do |ml|
23
24
  [
24
- link_to('View Campaign', ml.url, target: '_blank'),
25
25
  link_to('View Members', ml.members_url, target: '_blank'),
26
26
  link_to('View Merge Fields', ml.merge_fields_url, target: '_blank')
27
27
  ].join('<br>').html_safe
28
28
  end
29
29
 
30
- col :merge_fields
30
+ col :merge_fields, visible: false do |ml|
31
+ ml.merge_fields.join(', ')
32
+ end
31
33
 
32
34
  actions_col
33
35
  end
@@ -1,2 +1,12 @@
1
1
  module EffectiveMailchimpHelper
2
+
3
+ def mailchimp_list_member_interests_collection(mailchimp_interests)
4
+ interests = mailchimp_interests.select { |interest| interest.can_subscribe? || interest.force_subscribe? }
5
+
6
+ interests.map do |interest|
7
+ label = (interest.force_subscribe? ? (interest.to_s + ' ' + content_tag(:small, 'required', class: 'text-hint')) : interest.to_s).html_safe
8
+ [label, interest.mailchimp_id, disabled: (interest.force_subscribe || !interest.can_subscribe?)]
9
+ end
10
+ end
11
+
2
12
  end
@@ -35,7 +35,7 @@ module EffectiveMailchimpUser
35
35
 
36
36
  # The user updated the form
37
37
  after_commit(if: -> { mailchimp_member_update_required? }) do
38
- EffectiveMailchimpUpdateJob.perform_later(self)
38
+ EffectiveMailchimpUpdateJob.perform_later(self) # This calls user.mailchimp_update! on the background
39
39
  end
40
40
  end
41
41
 
@@ -140,6 +140,10 @@ module EffectiveMailchimpUser
140
140
  mailchimp_list_members.select(&:subscribed?).map(&:mailchimp_list)
141
141
  end
142
142
 
143
+ def mailchimp_subscribed_interests
144
+ mailchimp_list_members.select(&:subscribed?).flat_map(&:interests)
145
+ end
146
+
143
147
  def mailchimp_list_member(mailchimp_list:)
144
148
  raise('expected a MailchimpList') unless mailchimp_list.kind_of?(Effective::MailchimpList)
145
149
  mailchimp_list_members.find { |mlm| mlm.mailchimp_list_id == mailchimp_list.id }
@@ -29,6 +29,22 @@ module Effective
29
29
  "https://#{server}.admin.mailchimp.com"
30
30
  end
31
31
 
32
+ def audience_url
33
+ "https://#{server}.admin.mailchimp.com/audience/"
34
+ end
35
+
36
+ def groups_url
37
+ "https://#{server}.admin.mailchimp.com/lists/dashboard/groups/"
38
+ end
39
+
40
+ def contacts_url
41
+ "https://#{server}.admin.mailchimp.com/audience/contacts"
42
+ end
43
+
44
+ def campaigns_url
45
+ "https://#{server}.admin.mailchimp.com/campaigns/"
46
+ end
47
+
32
48
  def public_url
33
49
  "https://mailchimp.com"
34
50
  end
@@ -52,6 +68,20 @@ module Effective
52
68
  client.lists.get_list(id.try(:mailchimp_id) || id)
53
69
  end
54
70
 
71
+ def categories(list_id)
72
+ Rails.logger.info "[effective_mailchimp] Index Interest Categories..." if debug?
73
+
74
+ response = client.lists.get_list_interest_categories(list_id.try(:mailchimp_id) || list_id)
75
+ Array(response['categories']) - [nil, '', {}]
76
+ end
77
+
78
+ def interests(list_id, category_id)
79
+ Rails.logger.info "[effective_mailchimp] Index Interest Category Interests..." if debug?
80
+
81
+ response = client.lists.list_interest_category_interests(list_id, category_id)
82
+ Array(response['interests']) - [nil, '', {}]
83
+ end
84
+
55
85
  def list_member(id, email)
56
86
  raise('expected an email') unless email.to_s.include?('@')
57
87
 
@@ -63,7 +93,7 @@ module Effective
63
93
  end
64
94
 
65
95
  def list_merge_fields(id)
66
- response = client.lists.get_list_merge_fields(id, count: 100)
96
+ response = client.lists.get_list_merge_fields(id.try(:mailchimp_id) || id, count: 100)
67
97
  Array(response['merge_fields']) - [nil, '', {}]
68
98
  end
69
99
 
@@ -73,7 +103,7 @@ module Effective
73
103
  payload = { name: name.to_s.titleize, tag: name.to_s, type: type }
74
104
 
75
105
  begin
76
- client.lists.add_list_merge_field(id, payload)
106
+ client.lists.add_list_merge_field(id.try(:mailchimp_id) || id, payload)
77
107
  rescue MailchimpMarketing::ApiError => e
78
108
  false
79
109
  end
@@ -85,31 +115,29 @@ module Effective
85
115
  existing = list_member(member.mailchimp_list, member.user.email)
86
116
  return existing if existing.present?
87
117
 
88
- merge_fields = member.user.mailchimp_merge_fields
89
- raise('expected user mailchimp_merge_fields to be a Hash') unless merge_fields.kind_of?(Hash)
90
-
91
- payload = {
92
- email_address: member.user.email,
93
- status: (member.subscribed ? 'subscribed' : 'unsubscribed'),
94
- merge_fields: merge_fields.delete_if { |k, v| v.blank? }
95
- }
96
-
118
+ payload = list_member_payload(member)
97
119
  client.lists.add_list_member(member.mailchimp_list.mailchimp_id, payload)
98
120
  end
99
121
 
100
122
  def list_member_update(member)
101
123
  raise('expected an Effective::MailchimpListMember') unless member.kind_of?(Effective::MailchimpListMember)
102
124
 
125
+ payload = list_member_payload(member)
126
+ client.lists.update_list_member(member.mailchimp_list.mailchimp_id, member.email, payload)
127
+ end
128
+
129
+ def list_member_payload(member)
130
+ raise('expected an Effective::MailchimpListMember') unless member.kind_of?(Effective::MailchimpListMember)
131
+
103
132
  merge_fields = member.user.mailchimp_merge_fields
104
133
  raise('expected user mailchimp_merge_fields to be a Hash') unless merge_fields.kind_of?(Hash)
105
134
 
106
135
  payload = {
107
136
  email_address: member.user.email,
108
137
  status: (member.subscribed ? 'subscribed' : 'unsubscribed'),
109
- merge_fields: merge_fields.delete_if { |k, v| v.blank? }
110
- }
111
-
112
- client.lists.update_list_member(member.mailchimp_list.mailchimp_id, member.email, payload)
138
+ merge_fields: merge_fields.delete_if { |k, v| v.blank? },
139
+ interests: member.interests_hash.presence
140
+ }.compact
113
141
  end
114
142
 
115
143
  end
@@ -0,0 +1,73 @@
1
+ module Effective
2
+ class MailchimpCategory < ActiveRecord::Base
3
+ self.table_name = (EffectiveMailchimp.mailchimp_categories_table_name || :mailchimp_categories).to_s
4
+
5
+ belongs_to :mailchimp_list
6
+ has_many :mailchimp_interests
7
+
8
+ effective_resource do
9
+ mailchimp_id :string # ID of this Mailchimp InterestCategory
10
+ list_id :string # Mailchimp list ID
11
+
12
+ name :string # Title
13
+ list_name :string
14
+
15
+ display_type :string # Type
16
+
17
+ timestamps
18
+ end
19
+
20
+ validates :mailchimp_id, presence: true
21
+ validates :list_id, presence: true
22
+ validates :name, presence: true
23
+
24
+ scope :deep, -> { all }
25
+ scope :sorted, -> { order(:name) }
26
+ scope :subscribable, -> { all }
27
+
28
+ # Creates or builds all the Lists
29
+ def self.sync!(api: EffectiveMailchimp.api)
30
+ # For every mailchimp_list, get all the categories
31
+ mailchimp_lists = Effective::MailchimpList.all
32
+
33
+ mailchimp_lists.each do |mailchimp_list|
34
+ # All the Groups from Mailchimp
35
+ categories = api.categories(mailchimp_list.mailchimp_id)
36
+
37
+ # Get all our existing Effective::MailchimpCategory records
38
+ mailchimp_categories = where(mailchimp_list: mailchimp_list)
39
+
40
+ # Find or create Effective::MailchimpGroups based on existing groups
41
+ categories.each do |category|
42
+ mailchimp_id = category['id']
43
+ mailchimp_category = mailchimp_categories.find { |mc| mc.mailchimp_id == mailchimp_id } || new()
44
+
45
+ mailchimp_category.assign_attributes(
46
+ mailchimp_list: mailchimp_list,
47
+
48
+ mailchimp_id: mailchimp_id,
49
+ list_id: category['list_id'],
50
+ list_name: mailchimp_list.name,
51
+ name: (category['title'] || category['name']),
52
+ display_type: category['type']
53
+ )
54
+
55
+ mailchimp_category.save!
56
+ end
57
+
58
+ # Destroy any Effective::MailchimpGroups resources if they no longer returned by groups
59
+ mailchimp_categories.each do |mailchimp_category|
60
+ category = categories.find { |category| category['id'] == mailchimp_category.mailchimp_id }
61
+ mailchimp_category.destroy! unless category.present?
62
+ end
63
+ end
64
+
65
+ true
66
+ end
67
+
68
+ def to_s
69
+ name.presence || model_name.human
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,92 @@
1
+ module Effective
2
+ class MailchimpInterest < ActiveRecord::Base
3
+ self.table_name = (EffectiveMailchimp.mailchimp_interests_table_name || :mailchimp_interests).to_s
4
+
5
+ belongs_to :mailchimp_list
6
+ belongs_to :mailchimp_category
7
+
8
+ effective_resource do
9
+ mailchimp_id :string # ID of this interest on Mailchimp
10
+
11
+ list_id :string # Mailchimp list ID
12
+ category_id :string # Interest Category
13
+
14
+ name :string
15
+ list_name :string
16
+ category_name :string
17
+
18
+ subscriber_count :integer
19
+ display_order :integer
20
+
21
+ can_subscribe :boolean
22
+ force_subscribe :boolean
23
+
24
+ timestamps
25
+ end
26
+
27
+ validates :mailchimp_id, presence: true
28
+ validates :list_id, presence: true
29
+ validates :category_id, presence: true
30
+ validates :name, presence: true
31
+
32
+ scope :deep, -> { all }
33
+ scope :sorted, -> { order(:display_order) }
34
+ scope :subscribable, -> { where(can_subscribe: true) }
35
+
36
+ # Creates or builds all the Lists
37
+ def self.sync!(api: EffectiveMailchimp.api)
38
+ # For every mailchimp_list, get all the interests
39
+ mailchimp_lists = Effective::MailchimpList.deep.all
40
+
41
+ mailchimp_lists.each do |mailchimp_list|
42
+ mailchimp_list.mailchimp_categories.each do |mailchimp_category|
43
+
44
+ # All the Interests from Mailchimp
45
+ interests = api.interests(mailchimp_list.mailchimp_id, mailchimp_category.mailchimp_id)
46
+
47
+ # Get all our existing Effective::MailchimpGroup records
48
+ mailchimp_interests = where(mailchimp_list: mailchimp_list, mailchimp_category: mailchimp_category)
49
+
50
+ # Find or create Effective::MailchimpInterests based on existing lists and categories
51
+ interests.each do |interest|
52
+ mailchimp_id = interest['id']
53
+ mailchimp_interest = mailchimp_interests.find { |mi| mi.mailchimp_id == mailchimp_id } || new()
54
+
55
+ mailchimp_interest.assign_attributes(
56
+ mailchimp_list: mailchimp_list,
57
+ mailchimp_category: mailchimp_category,
58
+
59
+ mailchimp_id: mailchimp_id,
60
+
61
+ list_id: interest['list_id'],
62
+ list_name: mailchimp_list.name,
63
+
64
+ category_id: interest['category_id'],
65
+ category_name: mailchimp_category.name,
66
+
67
+ name: interest['name'],
68
+ display_order: interest['display_order'],
69
+ subscriber_count: interest['subscriber_count']
70
+ )
71
+
72
+ mailchimp_interest.assign_attributes(can_subscribe: true) if mailchimp_interest.new_record?
73
+ mailchimp_interest.save!
74
+ end
75
+
76
+ # Destroy any Effective::MailchimpGroups resources if they no longer returned by interests
77
+ mailchimp_interests.each do |mailchimp_interest|
78
+ interest = interests.find { |interest| interest['id'] == mailchimp_interest.mailchimp_id }
79
+ mailchimp_interest.destroy! unless interest.present?
80
+ end
81
+ end
82
+ end
83
+
84
+ true
85
+ end
86
+
87
+ def to_s
88
+ name.presence || model_name.human
89
+ end
90
+
91
+ end
92
+ end
@@ -3,6 +3,8 @@ module Effective
3
3
  self.table_name = (EffectiveMailchimp.mailchimp_lists_table_name || :mailchimp_lists).to_s
4
4
 
5
5
  has_many :mailchimp_list_members, dependent: :delete_all
6
+ has_many :mailchimp_categories, dependent: :delete_all
7
+ has_many :mailchimp_interests, dependent: :delete_all
6
8
 
7
9
  effective_resource do
8
10
  mailchimp_id :string
@@ -16,18 +18,14 @@ module Effective
16
18
  timestamps
17
19
  end
18
20
 
19
- def to_s
20
- name.presence || model_name.human
21
- end
22
-
23
- scope :deep, -> { all }
21
+ scope :deep, -> { includes(:mailchimp_categories, :mailchimp_interests) }
24
22
  scope :sorted, -> { order(:name) }
25
23
  scope :subscribable, -> { where(can_subscribe: true) }
26
24
 
27
25
  # Creates or builds all the Lists
28
- def self.sync!
26
+ def self.sync!(api: EffectiveMailchimp.api, merge_fields: nil)
29
27
  # All the Lists from Mailchimp
30
- lists = EffectiveMailchimp.api.lists
28
+ lists = api.lists()
31
29
 
32
30
  # Get all our existing Effective::MailchimpList records
33
31
  mailchimp_lists = all()
@@ -35,11 +33,17 @@ module Effective
35
33
  # Find or create Effective::Mailchimp based on existing lists
36
34
  lists.each do |list|
37
35
  mailchimp_id = list['id']
38
- web_id = list['web_id']
39
- name = list['name']
40
36
 
41
37
  mailchimp_list = mailchimp_lists.find { |ml| ml.mailchimp_id == mailchimp_id } || new()
42
- mailchimp_list.assign_attributes(mailchimp_id: mailchimp_id, web_id: web_id, name: name)
38
+ mailchimp_list.assign_attributes(
39
+ mailchimp_id: mailchimp_id,
40
+
41
+ web_id: list['web_id'],
42
+ name: list['name'],
43
+ updated_at: Time.zone.now
44
+ )
45
+
46
+ mailchimp_list.assign_attributes(can_subscribe: true) if mailchimp_list.new_record?
43
47
  mailchimp_list.save!
44
48
  end
45
49
 
@@ -49,27 +53,37 @@ module Effective
49
53
  mailchimp_list.destroy! unless list.present?
50
54
  end
51
55
 
52
- true
53
- end
54
-
55
- # This creates our local merge fields ON Mailchimp
56
- def create_mailchimp_merge_fields!(merge_fields)
57
- raise('expected a Hash of merge fields') unless merge_fields.kind_of?(Hash)
58
-
59
- merge_fields.keys.each do |name|
60
- EffectiveMailchimp.api.add_merge_field(mailchimp_id, name: name)
56
+ # Sync merge fields
57
+ if merge_fields.present?
58
+ merge_field_keys = merge_fields.keys.map(&:to_s)
59
+
60
+ mailchimp_lists.reject(&:destroyed?).each do |mailchimp_list|
61
+ existing = api.list_merge_fields(mailchimp_list).map { |hash| hash['tag'] }
62
+ (merge_field_keys - existing).each do |name|
63
+ puts "Adding merge field #{name} to #{mailchimp_list}"
64
+ api.add_merge_field(mailchimp_list, name: name)
65
+ end
66
+ end
61
67
  end
62
68
 
63
69
  true
64
70
  end
65
71
 
72
+ def to_s
73
+ name.presence || model_name.human
74
+ end
75
+
66
76
  def merge_fields
67
77
  return [] unless mailchimp_id
68
78
  EffectiveMailchimp.api.list_merge_fields(mailchimp_id).map { |hash| hash['tag'] }.sort
69
79
  end
70
80
 
71
- def url
72
- EffectiveMailchimp.api.admin_url + "/campaigns/#f_list:#{web_id}"
81
+ def grouped?
82
+ mailchimp_categories.present? && mailchimp_categories.any? { |category| category.mailchimp_interests.present? }
83
+ end
84
+
85
+ def ungrouped?
86
+ !grouped?
73
87
  end
74
88
 
75
89
  def members_url
@@ -80,21 +94,5 @@ module Effective
80
94
  EffectiveMailchimp.api.admin_url + "/lists/settings/merge-tags?id=#{web_id}"
81
95
  end
82
96
 
83
- def can_subscribe!
84
- update!(can_subscribe: true)
85
- end
86
-
87
- def cannot_subscribe!
88
- update!(can_subscribe: false)
89
- end
90
-
91
- def force_subscribe!
92
- update!(force_subscribe: true)
93
- end
94
-
95
- def unforce_subscribe!
96
- update!(force_subscribe: false)
97
- end
98
-
99
97
  end
100
98
  end
@@ -14,6 +14,8 @@ module Effective
14
14
  email_address :string
15
15
  full_name :string
16
16
 
17
+ interests :text # Array of mailchimp_interest mailchimp_ids
18
+
17
19
  # We set this on our side to update mailchimp and subscribe the user
18
20
  subscribed :boolean
19
21
 
@@ -26,10 +28,17 @@ module Effective
26
28
  timestamps
27
29
  end
28
30
 
31
+ if EffectiveResources.serialize_with_coder?
32
+ serialize :interests, type: Array, coder: YAML
33
+ else
34
+ serialize :interests, Array
35
+ end
36
+
29
37
  validates :mailchimp_list_id, uniqueness: { scope: [:user_type, :user_id] }
30
38
 
31
39
  scope :deep, -> { includes(:mailchimp_list, :user) }
32
40
  scope :sorted, -> { order(:id) }
41
+ scope :subscribed, -> { where(subscribed: true) }
33
42
 
34
43
  def to_s
35
44
  mailchimp_list&.to_s || model_name.human
@@ -39,6 +48,37 @@ module Effective
39
48
  email_address.presence || user.email
40
49
  end
41
50
 
51
+ # Array of MailchimpInterest mailchimp_ids
52
+ def interests
53
+ Array(self[:interests]) - [nil, '']
54
+ end
55
+
56
+ # Array of MailchimpInterests
57
+ def mailchimp_interests
58
+ all_mailchimp_interests.select { |interest| interests.include?(interest.mailchimp_id) }
59
+ end
60
+
61
+ # We use this to add the force_subscribed interests
62
+ def build_interests(mailchimp_interest)
63
+ mailchimp_ids = Array(mailchimp_interest).map { |interest| interest.try(:mailchimp_id) || interest }
64
+ raise('expected an array of MailchimpInterests or mailchimp_ids') unless mailchimp_ids.all? { |id| id.kind_of?(String) && id.length > 1 }
65
+
66
+ assign_attributes(interests: interests | mailchimp_ids)
67
+ end
68
+
69
+ # {"25a38426c9" => false, "9b826db370" => true }
70
+ def interests_hash
71
+ all_mailchimp_interests.each_with_object({}) do |interest, hash|
72
+ hash[interest.mailchimp_id] = interests.include?(interest.mailchimp_id)
73
+ end
74
+ end
75
+
76
+ # From the mailchimp list
77
+ def all_mailchimp_interests
78
+ return [] if mailchimp_list.blank?
79
+ mailchimp_list.mailchimp_categories.flat_map(&:mailchimp_interests)
80
+ end
81
+
42
82
  def assign_mailchimp_attributes(atts)
43
83
  assign_attributes(
44
84
  mailchimp_id: atts['id'],
@@ -46,7 +86,8 @@ module Effective
46
86
  email_address: atts['email_address'],
47
87
  full_name: atts['full_name'],
48
88
  subscribed: (atts['status'] == 'subscribed'),
49
- last_synced_at: Time.zone.now
89
+ last_synced_at: Time.zone.now,
90
+ interests: Hash(atts['interests']).select { |_, subscribed| subscribed == true }.keys
50
91
  )
51
92
  end
52
93
 
@@ -0,0 +1,15 @@
1
+ - if EffectiveResources.authorized?(self, :admin, :mailchimp_sync)
2
+ %p= link_to 'Sync changes from Mailchimp', effective_mailchimp.mailchimp_sync_admin_mailchimp_index_path, 'data-method': :post, class: 'btn btn-primary'
3
+
4
+ %p.text-muted
5
+ %small
6
+ - mailchimp_last_synced_at = Effective::MailchimpList.maximum(:updated_at)
7
+
8
+ last synced with
9
+ = link_to 'Mailchimp', EffectiveMailchimp.api.admin_url, target: '_blank'
10
+
11
+ - if mailchimp_last_synced_at.present?
12
+ = time_ago_in_words(mailchimp_last_synced_at)
13
+ ago.
14
+ - else
15
+ never.
@@ -0,0 +1,80 @@
1
+ %h1.effective-admin-heading= @page_title
2
+
3
+ = card('Mailchimp Settings') do
4
+ - if EffectiveMailchimp.api_blank?
5
+ .alert.alert-danger You are not connected to Mailchimp. Please set your API key.
6
+
7
+ - if EffectiveMailchimp.api_present?
8
+ %p
9
+ = succeed('.') do
10
+ Please configure your
11
+ = link_to 'audiences', EffectiveMailchimp.api.audience_url, target: '_blank'
12
+ and
13
+ = link_to 'groups', EffectiveMailchimp.api.groups_url, target: '_blank'
14
+ on the Mailchimp website then sync the changes so they can be updated below
15
+
16
+ %p
17
+ You can also visit the
18
+ = link_to('contacts', EffectiveMailchimp.api.contacts_url, target: '_blank')
19
+ and
20
+ = link_to('campaigns', EffectiveMailchimp.api.campaigns_url, target: '_blank')
21
+ at anytime.
22
+
23
+ %p.text-muted
24
+ %small
25
+ To change the names or display order of items below, please visit the Mailchimp website then sync changes.
26
+ %br
27
+ This operation also creates the audience merge fields and updates the subscriber counts for each group below.
28
+
29
+ = render('admin/mailchimp/sync')
30
+
31
+ = collapse('More settings') do
32
+ %p
33
+ For more information see
34
+ = succeed(',') do
35
+ = link_to(etsd(Effective::MailchimpList), effective_mailchimp.admin_mailchimp_lists_path, target: '_blank')
36
+ = succeed(',') do
37
+ = link_to(etsd(Effective::MailchimpListMember), effective_mailchimp.admin_mailchimp_list_members_path, target: '_blank')
38
+ = succeed(',') do
39
+ = link_to(etsd(Effective::MailchimpCategory), effective_mailchimp.admin_mailchimp_categories_path, target: '_blank')
40
+ and
41
+ = succeed('.') do
42
+ = link_to(etsd(Effective::MailchimpInterest), effective_mailchimp.admin_mailchimp_interests_path, target: '_blank')
43
+
44
+ %p The following merge fields are sent to Mailchimp when a user subscribes:
45
+
46
+ %ul
47
+ - current_user.mailchimp_merge_fields.keys.sort.each do |key|
48
+ %li= key
49
+
50
+ .mb-4
51
+
52
+ = card(Effective::MailchimpList) do
53
+ %p
54
+ Audiences contain a list of your contacts.
55
+ View all #{link_to ets(Effective::MailchimpList), EffectiveMailchimp.api.audience_url, target: '_blank'}.
56
+
57
+ %p
58
+ %small
59
+ To change the name, please visit the Mailchimp website then sync changes.
60
+ Press the Edit button to change the Can Subscribe and Force Subscribe settings.
61
+
62
+ - datatable = Admin::EffectiveMailchimpListsDatatable.new
63
+ = render_datatable(datatable, simple: true, inline: true)
64
+
65
+ = card(Effective::MailchimpCategory) do
66
+ %p
67
+ The following groups are displayed to the user where they can opt-in to each interest.
68
+ View all #{link_to ets(Effective::MailchimpCategory), EffectiveMailchimp.api.groups_url, target: '_blank'}.
69
+
70
+ %p
71
+ %small
72
+ To change the name, please visit the Mailchimp website then sync changes.
73
+ Press the Edit button to change the Can Subscribe and Force Subscribe settings.
74
+ Sync changes to update the subscriber counts.
75
+
76
+ - Effective::MailchimpCategory.all.each do |mailchimp_category|
77
+ = card(mailchimp_category.to_s) do
78
+ - datatable = Admin::EffectiveMailchimpInterestsDatatable.new(mailchimp_category: mailchimp_category)
79
+ = render_datatable(datatable, simple: true, inline: true)
80
+
@@ -0,0 +1,10 @@
1
+ = effective_form_with(model: [:admin, mailchimp_interest], engine: true) do |f|
2
+ = f.static_field :name, label: EffectiveResources.et(mailchimp_interest)
3
+
4
+ = f.check_box :can_subscribe, label: "Yes, display this interest and allow them to subscribe"
5
+
6
+ = f.check_box :force_subscribe,
7
+ label: "Yes, force users to subscribe. Subscribe them automatically and do not allow unsubscribe from the website",
8
+ hint: "They can still unsubscribe from the email link to unsubscribe"
9
+
10
+ = effective_submit(f)
@@ -1,5 +1,6 @@
1
1
  = effective_form_with(model: [:admin, mailchimp_list], engine: true) do |f|
2
2
  = f.static_field :name, label: EffectiveResources.et(mailchimp_list)
3
+ = f.static_field :merge_fields
3
4
 
4
5
  = f.check_box :can_subscribe, label: "Yes, display users and allow them to subscribe"
5
6
 
@@ -1,13 +1,19 @@
1
- = card(ets(Effective::MailchimpList)) do
2
- %p.text-muted #{user} is subscribed to #{pluralize(user.mailchimp_subscribed_lists.count, et(Effective::MailchimpList))}.
1
+ = card('Mailchimp') do
2
+ %p.text-muted
3
+ = succeed('.') do
4
+ = user
5
+ is subscribed to
6
+ = pluralize(user.mailchimp_subscribed_lists.count, etd(Effective::MailchimpList))
7
+ and
8
+ = pluralize(user.mailchimp_subscribed_interests.count, etd(Effective::MailchimpInterest))
3
9
 
4
10
  %p.text-muted
5
11
  Please visit
6
- = link_to 'All ' + ets(Effective::MailchimpList), effective_mailchimp.admin_mailchimp_lists_path, target: '_blank'
12
+ = link_to 'Mailchimp Settings', effective_mailchimp.admin_mailchimp_path, target: '_blank'
7
13
  to configure which are displayed.
8
14
 
9
15
  %p
10
- %strong Subscribed
16
+ %strong Subscribed to the following
11
17
 
12
18
  = effective_form_with model: [:admin, user] do |f|
13
19
  = f.hidden_field :id
@@ -7,14 +7,41 @@
7
7
  = fmlm.hidden_field :id
8
8
  = fmlm.hidden_field :mailchimp_list_id
9
9
 
10
- - if mailchimp_list.force_subscribe?
11
- %p
12
- - if fmlm.object.cannot_be_subscribed?
13
- = fmlm.check_box :subscribed, label: fmlm.object.to_s, disabled: true, hint: 'required but unsubscribed', checked: false
10
+ - # With groups, probably single audience implementation
11
+ - if mailchimp_list.grouped?
12
+ = fmlm.hidden_field :subscribed, value: true
13
+
14
+ - # For each group / mailchimp_category
15
+ - mailchimp_list.mailchimp_categories.each do |mailchimp_category|
16
+ - mailchimp_interests = mailchimp_category.mailchimp_interests
17
+
18
+ - # Force subscription of each interest
19
+ - forced = mailchimp_interests.select { |mi| mi.force_subscribe? }
20
+ - fmlm.object.build_interests(forced)
21
+
22
+ - forced.each do |mailchimp_interest|
23
+ = fmlm.hidden_field :interests, name: "#{fmlm.object_name}[interests][]", value: mailchimp_interest.mailchimp_id
24
+
25
+ - # Add control for mailchimp interests
26
+ - mailchimp_interests_collection = mailchimp_list_member_interests_collection(mailchimp_interests)
27
+
28
+ - if mailchimp_category.display_type == 'select'
29
+ = fmlm.select :interests, mailchimp_interests_collection, label: mailchimp_category.to_s, name: "#{fmlm.object_name}[interests][]"
30
+ - elsif mailchimp_category.display_type == 'radio'
31
+ = fmlm.radios :interests, mailchimp_interests_collection, label: mailchimp_category.to_s, name: "#{fmlm.object_name}[interests][]"
14
32
  - else
15
- = fmlm.check_box :subscribed, label: fmlm.object.to_s, disabled: true, hint: 'required', checked: true
16
- = fmlm.hidden_field :subscribed, value: true
33
+ = fmlm.checks :interests, mailchimp_interests_collection, label: mailchimp_category.to_s, name: "#{fmlm.object_name}[interests][]"
34
+
35
+ - # No groups, multiple audiences implementation
36
+ - if mailchimp_list.ungrouped?
37
+ - if mailchimp_list.force_subscribe?
38
+ %p
39
+ - if fmlm.object.cannot_be_subscribed?
40
+ = fmlm.check_box :subscribed, label: fmlm.object.to_s, disabled: true, hint: 'required but unsubscribed', checked: false
41
+ - else
42
+ = fmlm.check_box :subscribed, label: fmlm.object.to_s, disabled: true, hint: 'required', checked: true
43
+ = fmlm.hidden_field :subscribed, value: true
17
44
 
18
- - elsif mailchimp_list.can_subscribe?
19
- %p
20
- = fmlm.check_box :subscribed, label: fmlm.object.to_s
45
+ - elsif mailchimp_list.can_subscribe?
46
+ %p
47
+ = fmlm.check_box :subscribed, label: fmlm.object.to_s
@@ -0,0 +1,12 @@
1
+ en:
2
+ effective_mailchimp:
3
+ name: 'Effective Mailchimp'
4
+ acronym: 'Mailchimp'
5
+
6
+ activerecord:
7
+ models:
8
+ # These ones should stay effective
9
+ effective/mailchimp_list: 'Mailchimp Audience'
10
+ effective/mailchimp_list_member: 'Mailchimp Audience Member'
11
+ effective/mailchimp_category: 'Mailchimp Group'
12
+ effective/mailchimp_interest: 'Mailchimp Group Interest'
data/config/routes.rb CHANGED
@@ -13,19 +13,17 @@ EffectiveMailchimp::Engine.routes.draw do
13
13
  end
14
14
 
15
15
  namespace :admin do
16
- resources :mailchimp_lists, only: [:index, :edit, :update] do
17
- post :can_subscribe, on: :member
18
- post :cannot_subscribe, on: :member
19
-
20
- post :force_subscribe, on: :member
21
- post :unforce_subscribe, on: :member
22
-
23
- get :mailchimp_sync, on: :collection
24
- end
16
+ resources :mailchimp_lists, only: [:index, :edit, :update]
17
+ resources :mailchimp_interests, only: [:index, :edit, :update]
18
+ resources :mailchimp_categories, only: :index
19
+ resources :mailchimp_list_members, only: :index
25
20
 
26
21
  resources :mailchimp, only: [] do
22
+ post :mailchimp_sync, on: :collection
27
23
  post :mailchimp_sync_user, on: :member
28
24
  end
25
+
26
+ get '/mailchimp', to: 'mailchimp#index', as: :mailchimp
29
27
  end
30
28
 
31
29
  end
@@ -11,6 +11,41 @@ class CreateEffectiveMailchimp < ActiveRecord::Migration[6.0]
11
11
  t.timestamps
12
12
  end
13
13
 
14
+ create_table :mailchimp_categories do |t|
15
+ t.integer :mailchimp_list_id
16
+
17
+ t.string :mailchimp_id
18
+ t.string :list_id
19
+
20
+ t.string :name
21
+ t.string :list_name
22
+
23
+ t.string :display_type
24
+
25
+ t.timestamps
26
+ end
27
+
28
+ create_table :mailchimp_interests do |t|
29
+ t.integer :mailchimp_list_id
30
+ t.integer :mailchimp_category_id
31
+
32
+ t.string :mailchimp_id
33
+ t.string :list_id
34
+ t.string :category_id
35
+
36
+ t.string :name
37
+ t.string :list_name
38
+ t.string :category_name
39
+
40
+ t.integer :display_order
41
+ t.integer :subscriber_count
42
+
43
+ t.boolean :can_subscribe
44
+ t.boolean :force_subscribe
45
+
46
+ t.timestamps
47
+ end
48
+
14
49
  create_table :mailchimp_list_members do |t|
15
50
  t.integer :user_id
16
51
  t.string :user_type
@@ -26,6 +61,8 @@ class CreateEffectiveMailchimp < ActiveRecord::Migration[6.0]
26
61
  t.boolean :subscribed, default: false
27
62
  t.boolean :cannot_be_subscribed, default: false
28
63
 
64
+ t.text :interests
65
+
29
66
  t.datetime :last_synced_at
30
67
 
31
68
  t.timestamps
@@ -1,3 +1,3 @@
1
1
  module EffectiveMailchimp
2
- VERSION = '0.6.0'.freeze
2
+ VERSION = '0.7.0'.freeze
3
3
  end
@@ -7,7 +7,7 @@ module EffectiveMailchimp
7
7
 
8
8
  def self.config_keys
9
9
  [
10
- :mailchimp_lists_table_name, :mailchimp_list_members_table_name,
10
+ :mailchimp_lists_table_name, :mailchimp_list_members_table_name, :mailchimp_categories_table_name, :mailchimp_interests_table_name,
11
11
  :layout,
12
12
  :api_key
13
13
  ]
@@ -23,8 +23,12 @@ module EffectiveMailchimp
23
23
  api_key.present?
24
24
  end
25
25
 
26
+ def self.api_blank?
27
+ api_key.blank?
28
+ end
29
+
26
30
  def self.permitted_params
27
- [ :mailchimp_user_form_action, mailchimp_list_members_attributes: [:id, :mailchimp_list_id, :subscribed] ]
31
+ [ :mailchimp_user_form_action, mailchimp_list_members_attributes: [:id, :mailchimp_list_id, :subscribed, interests: []] ]
28
32
  end
29
33
 
30
34
  end
@@ -5,20 +5,4 @@ namespace :effective_mailchimp do
5
5
  load "#{__dir__}/../../db/seeds.rb"
6
6
  end
7
7
 
8
- # bundle exec rake effective_mailchimp:create_mailchimp_merge_fields
9
- task create_mailchimp_merge_fields: :environment do
10
- merge_fields = User.new.mailchimp_merge_fields()
11
-
12
- Effective::MailchimpList.sync!
13
-
14
- collection = Effective::MailchimpList.all
15
-
16
- collection.find_each do |list|
17
- puts "Creating #{list} merge fields"
18
- list.create_mailchimp_merge_fields!(merge_fields)
19
- end
20
-
21
- puts 'All done'
22
- end
23
-
24
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_mailchimp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
@@ -193,23 +193,34 @@ files:
193
193
  - app/assets/javascripts/effective_mailchimp/base.js
194
194
  - app/assets/stylesheets/effective_mailchimp.scss
195
195
  - app/assets/stylesheets/effective_mailchimp/base.scss
196
+ - app/controllers/admin/mailchimp_categories_controller.rb
196
197
  - app/controllers/admin/mailchimp_controller.rb
198
+ - app/controllers/admin/mailchimp_interests_controller.rb
199
+ - app/controllers/admin/mailchimp_list_members_controller.rb
197
200
  - app/controllers/admin/mailchimp_lists_controller.rb
198
201
  - app/controllers/effective/mailchimp_controller.rb
202
+ - app/datatables/admin/effective_mailchimp_categories_datatable.rb
203
+ - app/datatables/admin/effective_mailchimp_interests_datatable.rb
204
+ - app/datatables/admin/effective_mailchimp_list_members_datatable.rb
199
205
  - app/datatables/admin/effective_mailchimp_lists_datatable.rb
200
206
  - app/helpers/effective_mailchimp_helper.rb
201
207
  - app/jobs/effective_mailchimp_update_job.rb
202
208
  - app/models/concerns/effective_mailchimp_user.rb
203
209
  - app/models/effective/mailchimp_api.rb
210
+ - app/models/effective/mailchimp_category.rb
211
+ - app/models/effective/mailchimp_interest.rb
204
212
  - app/models/effective/mailchimp_list.rb
205
213
  - app/models/effective/mailchimp_list_member.rb
214
+ - app/views/admin/mailchimp/_sync.html.haml
215
+ - app/views/admin/mailchimp/index.html.haml
216
+ - app/views/admin/mailchimp_interests/_form.html.haml
206
217
  - app/views/admin/mailchimp_lists/_form.html.haml
207
- - app/views/admin/mailchimp_lists/index.html.haml
208
218
  - app/views/admin/mailchimp_user/_form.html.haml
209
219
  - app/views/admin/mailchimp_user/_sync.html.haml
210
220
  - app/views/effective/mailchimp_user/_fields.html.haml
211
221
  - app/views/effective/mailchimp_user/_sync.html.haml
212
222
  - config/effective_mailchimp.rb
223
+ - config/locales/effective_mailchimp.en.yml
213
224
  - config/routes.rb
214
225
  - db/migrate/101_create_effective_mailchimp.rb
215
226
  - db/seeds.rb
@@ -1,20 +0,0 @@
1
- %h1.effective-admin-heading= @page_title
2
-
3
- - resource = (@_effective_resource || Effective::Resource.new(controller_path))
4
-
5
- .resource-buttons
6
- = render_resource_buttons(resource.klass, (action ||= :index) => false)
7
-
8
- = card do
9
- = collapse('Show merge field settings') do
10
- %p The following Merge fields are sent to Mailchimp when a user subscribes:
11
-
12
- %ul
13
- - current_user.mailchimp_merge_fields.keys.each do |key|
14
- %li= key
15
-
16
- %p To have these fields displayed in Mailchimp, please configure each campaign with any of these merge fields.
17
-
18
- .mb-4
19
-
20
- = render_datatable @datatable