effective_mailchimp 0.2.5 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bfe4116a697ecf8159efedc227cef01e8ba67e6a66af101df8f13939145ab3b
4
- data.tar.gz: 6ec420f5b33544b57d355cb924eb116312df32a7f43cf21f5846d328e09bddf1
3
+ metadata.gz: 6828c1db2915c26b5aa9d2401496925b0898ff71edb6436d9283c895942967f7
4
+ data.tar.gz: 33db597cfe2e2ae408fc05b106ef4354b63bdfee2fa286bc10e8ccf5ed8e68df
5
5
  SHA512:
6
- metadata.gz: c1510628c55b5bb1d50848b6a0ab1d5c3e486b00dd809ba97a7c85e33cee9803bdf3a07c75e3190b8744b4d2245c304918dc267a40c42da955ffb0097b74296a
7
- data.tar.gz: 47e46744ee8e61be5662b976053c820df9832af56546a4b822a811044cc2843badc1209c1f0bc4fe3e993d895a43b815078b42fe218a44f40564996a955297a5
6
+ metadata.gz: 4e2eceb6d94dd48f78b43090bd51742f092b22b10c028636f2ab522562aa295bd361d6629d238c5ef99b5fe2ef0a7abb5dca9f72b2cfabd89c5c1bf4878cf072
7
+ data.tar.gz: 2b13409e3e4ef8b84f8541fda2d386649d149e6e52de6c41d8888894be2ad25f375dc5100443e224a9f596b12bbeafad4df883f8f96fd7eada54030a3faf3a16
@@ -3,11 +3,18 @@ module Admin
3
3
  before_action(:authenticate_user!) if defined?(Devise)
4
4
  before_action { EffectiveResources.authorize!(self, :admin, :effective_mailchimp) }
5
5
 
6
- # Calls Sync
7
- before_action(only: :index) { Effective::MailchimpList.sync! }
8
-
9
6
  include Effective::CrudController
10
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
+
11
18
  private
12
19
 
13
20
  def permitted_params
@@ -0,0 +1,18 @@
1
+ module Effective
2
+ class MailchimpController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+
5
+ def mailchimp_sync_user
6
+ resource = current_user
7
+
8
+ EffectiveResources.authorize!(self, :mailchimp_sync_user, current_user)
9
+
10
+ resource.mailchimp_sync!
11
+
12
+ flash[:success] = "Successfully synced mailchimp"
13
+
14
+ redirect_back(fallback_location: '/settings')
15
+ end
16
+
17
+ end
18
+ end
@@ -1,15 +1,2 @@
1
1
  module EffectiveMailchimpHelper
2
-
3
- def mailchimp_user_fields(form)
4
- raise('expected a form') unless form.respond_to?(:object)
5
-
6
- resource = form.object
7
- raise('expected an effective_mailchimp_user resource') unless resource.class.respond_to?(:effective_mailchimp_user?)
8
-
9
- resource.reload
10
- resource.mailchimp_sync!(force: false)
11
-
12
- render('effective/mailchimp_user/fields', form: form, f: form, resource: resource, mailchimp_user: resource)
13
- end
14
-
15
2
  end
@@ -2,7 +2,7 @@ class EffectiveMailchimpUpdateJob < ApplicationJob
2
2
 
3
3
  def perform(user)
4
4
  raise('expected an effective_mailchimp_user') unless user.class.try(:effective_mailchimp_user?)
5
- user.mailchimp_update!(force: true)
5
+ user.mailchimp_update!
6
6
  end
7
7
 
8
8
  end
@@ -14,6 +14,10 @@ module EffectiveMailchimpUser
14
14
 
15
15
  module ClassMethods
16
16
  def effective_mailchimp_user?; true; end
17
+
18
+ def require_mailchimp_update_fields
19
+ ['email', 'last_name', 'first_name']
20
+ end
17
21
  end
18
22
 
19
23
  included do
@@ -26,11 +30,54 @@ module EffectiveMailchimpUser
26
30
  accepts_nested_attributes_for :mailchimp_lists, allow_destroy: true
27
31
 
28
32
  # The user updated the form
29
- after_commit(if: -> { mailchimp_user_form_action }) do
33
+ after_commit(if: -> { mailchimp_member_update_required? }) do
30
34
  EffectiveMailchimpUpdateJob.perform_later(self)
31
35
  end
32
36
  end
33
37
 
38
+ def mailchimp_subscribed?(mailchimp_list)
39
+ raise('expected a MailchimpList') unless mailchimp_lists.all? { |list| list.kind_of?(Effective::MailchimpList) }
40
+
41
+ member = mailchimp_list_member(mailchimp_list: mailchimp_list)
42
+ return false if member.blank?
43
+
44
+ member.subscribed? && member.synced?
45
+ end
46
+
47
+ # Api method to just subscribe this user to this list right now
48
+ # Pass one list or an Array of lists
49
+ def mailchimp_subscribe!(mailchimp_list)
50
+ mailchimp_lists = Array(mailchimp_list)
51
+ raise('expected a MailchimpList') unless mailchimp_lists.all? { |list| list.kind_of?(Effective::MailchimpList) }
52
+
53
+ mailchimp_lists.each do |mailchimp_list|
54
+ member = build_mailchimp_list_member(mailchimp_list: mailchimp_list)
55
+ member.assign_attributes(subscribed: true)
56
+ end
57
+
58
+ # This sets up the after_commit to run the mailchimp_update! job
59
+ assign_attributes(mailchimp_user_form_action: true)
60
+
61
+ save!
62
+ end
63
+
64
+ # Api method to just unsubscribe this user to this list right now
65
+ # Pass one list or an Array of lists
66
+ def mailchimp_unsubscribe!(mailchimp_list)
67
+ mailchimp_lists = Array(mailchimp_list)
68
+ raise('expected a MailchimpList') unless mailchimp_lists.all? { |list| list.kind_of?(Effective::MailchimpList) }
69
+
70
+ mailchimp_lists.each do |mailchimp_list|
71
+ member = build_mailchimp_list_member(mailchimp_list: mailchimp_list)
72
+ member.assign_attributes(subscribed: false)
73
+ end
74
+
75
+ # This sets up the after_commit to run the mailchimp_update! job
76
+ assign_attributes(mailchimp_user_form_action: true)
77
+
78
+ save!
79
+ end
80
+
34
81
  # Intended for app to extend
35
82
  def mailchimp_merge_fields
36
83
  default_mailchimp_merge_fields()
@@ -88,26 +135,29 @@ module EffectiveMailchimpUser
88
135
  mailchimp_list_member(mailchimp_list: mailchimp_list) || mailchimp_list_members.build(mailchimp_list: mailchimp_list)
89
136
  end
90
137
 
91
- def mailchimp_list_members_changed?
92
- mailchimp_list_members.any? { |mlm| mlm.changes.present? || mlm.marked_for_destruction? }
93
- end
94
-
95
138
  def mailchimp_last_synced_at
96
- mailchimp_list_members.map(&:last_synced_at).min
139
+ mailchimp_list_members.map(&:last_synced_at).compact.min
97
140
  end
98
141
 
99
- def mailchimp_sync_required?
100
- return true if mailchimp_last_synced_at.blank?
101
- mailchimp_last_synced_at < (Time.zone.now - 1.day)
142
+ # Used by the form to set it up for all lists
143
+ def build_mailchimp_list_members
144
+ mailchimp_lists = Effective::MailchimpList.subscribable.sorted.to_a
145
+
146
+ mailchimp_lists.each do |mailchimp_list|
147
+ build_mailchimp_list_member(mailchimp_list: mailchimp_list)
148
+ end
149
+
150
+ mailchimp_list_members
102
151
  end
103
152
 
104
153
  # Pulls the current status from Mailchimp API into the Mailchimp List Member objects
105
154
  # Run before the mailchimp fields are displayed
106
- def mailchimp_sync!(force: true)
155
+ # Only run in the background when a user or admin clicks sync now
156
+ def mailchimp_sync!
107
157
  api = EffectiveMailchimp.api
108
158
  lists = Effective::MailchimpList.subscribable.sorted.to_a
109
159
 
110
- return if lists.length == mailchimp_list_members.length && !(force || mailchimp_sync_required?)
160
+ assign_attributes(mailchimp_user_form_action: nil)
111
161
 
112
162
  Timeout::timeout(lists.length * 2) do
113
163
  lists.each do |mailchimp_list|
@@ -123,38 +173,42 @@ module EffectiveMailchimpUser
123
173
  member.mark_for_destruction unless list.present?
124
174
  end
125
175
 
126
- save! if mailchimp_list_members_changed?
127
- true
176
+ save!
128
177
  end
129
178
 
130
179
  # Pushes the current Mailchimp List Member objects to Mailchimp when needed
131
- def mailchimp_update!(force: true)
180
+ # Called in the background after a form submission that changes the user email/last_name/first_name or mailchimp subscriptions
181
+ def mailchimp_update!
132
182
  api = EffectiveMailchimp.api
133
183
 
134
184
  assign_attributes(mailchimp_user_form_action: nil)
135
185
 
136
- mailchimp_list_members.map do |member|
186
+ mailchimp_list_members.each do |member|
137
187
  if member.mailchimp_id.blank? && member.subscribed?
138
188
  list_member = api.list_member_add(member)
139
- member.assign_mailchimp_attributes(list_member)
140
- elsif member.mailchimp_id.present? && (force || mailchimp_member_update_required?(member))
189
+ member.assign_mailchimp_attributes(list_member) if list_member.present?
190
+ elsif member.mailchimp_id.present?
141
191
  list_member = api.list_member_update(member)
142
- member.assign_mailchimp_attributes(list_member)
192
+ member.assign_mailchimp_attributes(list_member) if list_member.present?
143
193
  end
144
194
  end
145
195
 
146
- save! if mailchimp_list_members_changed?
147
- true
196
+ save!
148
197
  end
149
198
 
150
- def mailchimp_member_update_required?(member)
151
- require_update = ['email', 'last_name', 'first_name']
199
+ private
200
+
201
+ def mailchimp_member_update_required?
202
+ return false unless mailchimp_user_form_action
152
203
 
204
+ # Update if my email first name or last name change
205
+ require_update = self.class.require_mailchimp_update_fields()
153
206
  return true if (changes.keys & require_update).present?
154
207
  return true if (previous_changes.keys & require_update).present?
155
208
 
156
- return true if member.changes.present?
157
- return true if member.previous_changes.present?
209
+ # Update if any of my mailchimp list members changed
210
+ # which happens when I submit a form and change the Mailchimp values
211
+ return true if mailchimp_list_members.any? { |m| m.changes.present? || m.previous_changes.present? || m.marked_for_destruction? }
158
212
 
159
213
  false
160
214
  end
@@ -21,10 +21,18 @@ module Effective
21
21
  @client.set_config(api_key: @api_key, server: @server)
22
22
  end
23
23
 
24
+ def debug?
25
+ Rails.env.development?
26
+ end
27
+
24
28
  def admin_url
25
29
  "https://#{server}.admin.mailchimp.com"
26
30
  end
27
31
 
32
+ def public_url
33
+ "https://mailchimp.com"
34
+ end
35
+
28
36
  def ping
29
37
  client.ping.get()
30
38
  end
@@ -32,11 +40,15 @@ module Effective
32
40
  # Returns an Array of Lists, which are each Hash
33
41
  # Like this [{ ...}, { ... }]
34
42
  def lists
43
+ Rails.logger.info "[effective_mailchimp] Index Lists..." if debug?
44
+
35
45
  response = client.lists.get_all_lists(count: 250)
36
46
  Array(response['lists']) - [nil, '', {}]
37
47
  end
38
48
 
39
49
  def list(id)
50
+ Rails.logger.info "[effective_mailchimp] Show List..." if debug?
51
+
40
52
  client.lists.get_list(id.try(:mailchimp_id) || id)
41
53
  end
42
54
 
@@ -70,6 +82,9 @@ module Effective
70
82
  def list_member_add(member)
71
83
  raise('expected an Effective::MailchimpListMember') unless member.kind_of?(Effective::MailchimpListMember)
72
84
 
85
+ existing = list_member(member.mailchimp_list, member.user.email)
86
+ return existing if existing.present?
87
+
73
88
  merge_fields = member.user.mailchimp_merge_fields
74
89
  raise('expected user mailchimp_merge_fields to be a Hash') unless merge_fields.kind_of?(Hash)
75
90
 
@@ -79,7 +94,13 @@ module Effective
79
94
  merge_fields: merge_fields.delete_if { |k, v| v.blank? }
80
95
  }
81
96
 
82
- client.lists.add_list_member(member.mailchimp_list.mailchimp_id, payload)
97
+ begin
98
+ client.lists.add_list_member(member.mailchimp_list.mailchimp_id, payload)
99
+ rescue MailchimpMarketing::ApiError => e
100
+ return false if e.status == 400 && e.to_s.downcase.include?("member in compliance state")
101
+ raise(e)
102
+ end
103
+
83
104
  end
84
105
 
85
106
  def list_member_update(member)
@@ -21,6 +21,8 @@ module Effective
21
21
  timestamps
22
22
  end
23
23
 
24
+ validates :mailchimp_list_id, uniqueness: { scope: [:user_type, :user_id] }
25
+
24
26
  scope :deep, -> { includes(:mailchimp_list, :user) }
25
27
  scope :sorted, -> { order(:id) }
26
28
 
@@ -43,5 +45,9 @@ module Effective
43
45
  )
44
46
  end
45
47
 
48
+ def synced?
49
+ last_synced_at.present?
50
+ end
51
+
46
52
  end
47
53
  end
@@ -1,16 +1,20 @@
1
1
  %h1.effective-admin-heading= @page_title
2
2
 
3
- .card
4
- .card-body
5
- = collapse('Show merge field settings') do
6
- %p The following Merge fields are sent to Mailchimp when a user subscribes:
3
+ - resource = (@_effective_resource || Effective::Resource.new(controller_path))
7
4
 
8
- %ul
9
- - current_user.mailchimp_merge_fields.keys.each do |key|
10
- %li= key
5
+ .resource-buttons
6
+ = render_resource_buttons(resource.klass, (action ||= :index) => false)
11
7
 
12
- %p To have these fields displayed in Mailchimp, please configure each campaign with any of these merge fields.
8
+ = card do
9
+ = collapse('Show merge field settings') do
10
+ %p The following Merge fields are sent to Mailchimp when a user subscribes:
13
11
 
14
- .mb-4
12
+ %ul
13
+ - current_user.mailchimp_merge_fields.keys.each do |key|
14
+ %li= key
15
15
 
16
- = render_datatable @datatable
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
@@ -12,18 +12,7 @@
12
12
  = effective_form_with model: [:admin, user] do |f|
13
13
  = f.hidden_field :id
14
14
 
15
- = mailchimp_user_fields(f)
16
-
17
- %p.text-muted
18
- %small
19
- last synced with
20
- = link_to 'Mailchimp', EffectiveMailchimp.api.admin_url
21
- - if user.mailchimp_last_synced_at.present?
22
- = time_ago_in_words(user.mailchimp_last_synced_at)
23
- ago.
24
- - else
25
- never.
26
-
27
- = link_to 'sync now', effective_mailchimp.mailchimp_sync_user_admin_mailchimp_path(f.object), 'data-method': :post
15
+ = render('effective/mailchimp_user/fields', f: f)
16
+ = render('admin/mailchimp_user/sync', f: f)
28
17
 
29
18
  = f.submit
@@ -0,0 +1,15 @@
1
+ - user = f.object
2
+
3
+ - if user.persisted?
4
+ %p.text-muted
5
+ %small
6
+ last synced with
7
+ = link_to 'Mailchimp', EffectiveMailchimp.api.admin_url, target: '_blank'
8
+ - if user.mailchimp_last_synced_at.present?
9
+ = time_ago_in_words(user.mailchimp_last_synced_at)
10
+ ago.
11
+ - else
12
+ never.
13
+
14
+ - if EffectiveResources.authorized?(self, :mailchimp_sync_user, user)
15
+ = link_to 'sync now', effective_mailchimp.mailchimp_sync_user_admin_mailchimp_path(user), 'data-method': :post
@@ -1,12 +1,15 @@
1
1
  = f.hidden_field :mailchimp_user_form_action, value: true
2
2
 
3
- = f.fields_for :mailchimp_list_members do |fmlm|
3
+ = f.fields_for :mailchimp_list_members, f.object.build_mailchimp_list_members do |fmlm|
4
4
  - mailchimp_list = fmlm.object.mailchimp_list
5
5
  - next if mailchimp_list.blank?
6
6
 
7
+ = fmlm.hidden_field :id
8
+ = fmlm.hidden_field :mailchimp_list_id
9
+
7
10
  - if mailchimp_list.force_subscribe?
8
11
  %p
9
- = fmlm.check_box :subscribed, label: fmlm.object.to_s, disabled: true, hint: 'required'
12
+ = fmlm.check_box :subscribed, label: fmlm.object.to_s, disabled: true, hint: 'required', checked: true
10
13
  = fmlm.hidden_field :subscribed, value: true
11
14
 
12
15
  - elsif mailchimp_list.can_subscribe?
@@ -0,0 +1,15 @@
1
+ - user = f.object
2
+
3
+ - if user.persisted?
4
+ %p.text-muted
5
+ %small
6
+ last synced with
7
+ = link_to 'Mailchimp', EffectiveMailchimp.api.public_url, target: '_blank'
8
+ - if user.mailchimp_last_synced_at.present?
9
+ = time_ago_in_words(user.mailchimp_last_synced_at)
10
+ ago.
11
+ - else
12
+ never.
13
+
14
+ - if EffectiveResources.authorized?(self, :mailchimp_sync_user, user)
15
+ = link_to 'sync now', effective_mailchimp.mailchimp_sync_user_mailchimp_path(user), 'data-method': :post
data/config/routes.rb CHANGED
@@ -7,6 +7,9 @@ end
7
7
  EffectiveMailchimp::Engine.routes.draw do
8
8
  # Public routes
9
9
  scope module: 'effective' do
10
+ resources :mailchimp, only: [] do
11
+ post :mailchimp_sync_user, on: :member
12
+ end
10
13
  end
11
14
 
12
15
  namespace :admin do
@@ -16,12 +19,13 @@ EffectiveMailchimp::Engine.routes.draw do
16
19
 
17
20
  post :force_subscribe, on: :member
18
21
  post :unforce_subscribe, on: :member
22
+
23
+ get :mailchimp_sync, on: :collection
19
24
  end
20
25
 
21
26
  resources :mailchimp, only: [] do
22
27
  post :mailchimp_sync_user, on: :member
23
28
  end
24
-
25
29
  end
26
30
 
27
31
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveMailchimp
2
- VERSION = '0.2.5'.freeze
2
+ VERSION = '0.3.1'.freeze
3
3
  end
@@ -24,7 +24,7 @@ module EffectiveMailchimp
24
24
  end
25
25
 
26
26
  def self.permitted_params
27
- [ :mailchimp_user_form_action, mailchimp_list_members_attributes: [:id, :subscribed] ]
27
+ [ :mailchimp_user_form_action, mailchimp_list_members_attributes: [:id, :mailchimp_list_id, :subscribed] ]
28
28
  end
29
29
 
30
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_mailchimp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-24 00:00:00.000000000 Z
11
+ date: 2023-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -195,6 +195,7 @@ files:
195
195
  - app/assets/stylesheets/effective_mailchimp/base.scss
196
196
  - app/controllers/admin/mailchimp_controller.rb
197
197
  - app/controllers/admin/mailchimp_lists_controller.rb
198
+ - app/controllers/effective/mailchimp_controller.rb
198
199
  - app/datatables/admin/effective_mailchimp_lists_datatable.rb
199
200
  - app/helpers/effective_mailchimp_helper.rb
200
201
  - app/jobs/effective_mailchimp_update_job.rb
@@ -205,7 +206,9 @@ files:
205
206
  - app/views/admin/mailchimp_lists/_form.html.haml
206
207
  - app/views/admin/mailchimp_lists/index.html.haml
207
208
  - app/views/admin/mailchimp_user/_form.html.haml
209
+ - app/views/admin/mailchimp_user/_sync.html.haml
208
210
  - app/views/effective/mailchimp_user/_fields.html.haml
211
+ - app/views/effective/mailchimp_user/_sync.html.haml
209
212
  - config/effective_mailchimp.rb
210
213
  - config/routes.rb
211
214
  - db/migrate/01_create_effective_mailchimp.rb.erb
@@ -235,7 +238,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
235
238
  - !ruby/object:Gem::Version
236
239
  version: '0'
237
240
  requirements: []
238
- rubygems_version: 3.1.2
241
+ rubygems_version: 3.4.10
239
242
  signing_key:
240
243
  specification_version: 4
241
244
  summary: Subscribe and unsubscribe to mailchimp lists.