effective_mailchimp 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +128 -0
- data/Rakefile +18 -0
- data/app/assets/config/effective_mailchimp_manifest.js +3 -0
- data/app/assets/javascripts/effective_mailchimp/base.js +0 -0
- data/app/assets/javascripts/effective_mailchimp.js +1 -0
- data/app/assets/stylesheets/effective_mailchimp/base.scss +0 -0
- data/app/assets/stylesheets/effective_mailchimp.scss +1 -0
- data/app/controllers/admin/mailchimp_controller.rb +19 -0
- data/app/controllers/admin/mailchimp_lists_controller.rb +18 -0
- data/app/datatables/admin/effective_mailchimp_lists_datatable.rb +36 -0
- data/app/helpers/effective_mailchimp_helper.rb +14 -0
- data/app/models/concerns/effective_mailchimp_user.rb +116 -0
- data/app/models/effective/mailchimp_api.rb +84 -0
- data/app/models/effective/mailchimp_list.rb +69 -0
- data/app/models/effective/mailchimp_list_member.rb +47 -0
- data/app/views/admin/mailchimp_lists/_form.html.haml +8 -0
- data/app/views/admin/mailchimp_user/_form.html.haml +25 -0
- data/app/views/effective/mailchimp_user/_fields.html.haml +4 -0
- data/config/effective_mailchimp.rb +12 -0
- data/config/routes.rb +24 -0
- data/db/migrate/01_create_effective_mailchimp.rb.erb +34 -0
- data/db/seeds.rb +1 -0
- data/lib/effective_mailchimp/engine.rb +18 -0
- data/lib/effective_mailchimp/version.rb +3 -0
- data/lib/effective_mailchimp.rb +26 -0
- data/lib/generators/effective_mailchimp/install_generator.rb +28 -0
- data/lib/generators/templates/effective_mailchimp_mailer_preview.rb +4 -0
- data/lib/tasks/effective_mailchimp_tasks.rake +8 -0
- metadata +240 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fc851b003ffad568b1b32a435b75022875e3c12be043f4a298d929927142aab1
|
4
|
+
data.tar.gz: 7560d0e76bef241e59c40ca93b2c607b45b4e4757ccbe2d8b64f575b0e9eb9c8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 787fbc005508da120aaac6103c21c5ff31b107ae80c311b7ad0eed91765fc85ff5a2c4537fdf22e41d8d78e95d6de5cade9e1bd76570165d63e217ac8909696c
|
7
|
+
data.tar.gz: 2dd3834383a2352a093928e95fd77423734c214d5061377ccf9fc2b1b4e51ce18e5e82e0af6e1acca928a576efdc8215cc9b8bb814af1a1a5cb63cd7393340d5
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2023 Code and Effect Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# Effective Mailchimp
|
2
|
+
|
3
|
+
Subscribe and unsubscribe users to individual mailchimp campaigns.
|
4
|
+
|
5
|
+
Update Mailchimp whenever a user changes their demographics
|
6
|
+
|
7
|
+
## Getting Started
|
8
|
+
|
9
|
+
This requires Rails 6+ and Twitter Bootstrap 4 and just works with Devise.
|
10
|
+
|
11
|
+
Please first install the [effective_datatables](https://github.com/code-and-effect/effective_datatables) gem.
|
12
|
+
|
13
|
+
Please download and install the [Twitter Bootstrap4](http://getbootstrap.com)
|
14
|
+
|
15
|
+
Add to your Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'haml'
|
19
|
+
gem 'effective_mailchimp'
|
20
|
+
```
|
21
|
+
|
22
|
+
Run the bundle command to install it:
|
23
|
+
|
24
|
+
```console
|
25
|
+
bundle install
|
26
|
+
```
|
27
|
+
|
28
|
+
Then run the generator:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
rails generate effective_mailchimp:install
|
32
|
+
```
|
33
|
+
|
34
|
+
The generator will install an initializer which describes all configuration options and creates a database migration.
|
35
|
+
|
36
|
+
If you want to tweak the table names, manually adjust both the configuration file and the migration now.
|
37
|
+
|
38
|
+
Then migrate the database:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
rake db:migrate
|
42
|
+
```
|
43
|
+
|
44
|
+
Please add the following to your User model:
|
45
|
+
|
46
|
+
```
|
47
|
+
effective_mailchimp_user
|
48
|
+
```
|
49
|
+
|
50
|
+
and
|
51
|
+
|
52
|
+
```
|
53
|
+
Add a link to the admin menu:
|
54
|
+
|
55
|
+
```haml
|
56
|
+
- if can? :admin, :effective_mailchimp
|
57
|
+
- if can? :index, Effective::MailchimpList
|
58
|
+
= nav_link_to 'Mailchimp lists', effective_mailchimp.admin_mailchimp_lists_path
|
59
|
+
```
|
60
|
+
|
61
|
+
To add the fields to your existing users form
|
62
|
+
|
63
|
+
```
|
64
|
+
= form_with(model: user) do |f|
|
65
|
+
= f.text_field :first_name
|
66
|
+
= f.text_field :last_name
|
67
|
+
= mailchimp_user_fields(f)
|
68
|
+
= f.submit
|
69
|
+
|
70
|
+
## Configuring Mailchimp Account
|
71
|
+
|
72
|
+
Refer to the Marketing API Quick Start
|
73
|
+
|
74
|
+
`https://mailchimp.com/developer/marketing/guides/quick-start/`
|
75
|
+
|
76
|
+
Sign up for a Mailchimp account. Create a campaign.
|
77
|
+
|
78
|
+
Get API key from:
|
79
|
+
|
80
|
+
Visit `https://us7.admin.mailchimp.com/account/api/`
|
81
|
+
|
82
|
+
Click Create an API Key
|
83
|
+
|
84
|
+
Get your server from the domain. us7 in the above example.
|
85
|
+
|
86
|
+
## Configuration
|
87
|
+
|
88
|
+
## Authorization
|
89
|
+
|
90
|
+
All authorization checks are handled via the effective_resources gem found in the `config/initializers/effective_resources.rb` file.
|
91
|
+
|
92
|
+
## Permissions
|
93
|
+
|
94
|
+
The permissions you actually want to define are as follows (using CanCan):
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
if user.persisted?
|
98
|
+
end
|
99
|
+
|
100
|
+
if user.admin?
|
101
|
+
can :admin, :effective_mailchimp
|
102
|
+
|
103
|
+
can([:index, :edit, :update], Effective::MailchimpList)
|
104
|
+
can(:can_subscribe, Effective::MailchimpList) { |list| !list.can_subscribe? }
|
105
|
+
can(:cannot_subscribe, Effective::MailchimpList) { |list| list.can_subscribe? }
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
## License
|
110
|
+
|
111
|
+
MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
|
112
|
+
|
113
|
+
## Testing
|
114
|
+
|
115
|
+
Run tests by:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
rails test
|
119
|
+
```
|
120
|
+
|
121
|
+
## Contributing
|
122
|
+
|
123
|
+
1. Fork it
|
124
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
125
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
126
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
127
|
+
5. Bonus points for test coverage
|
128
|
+
6. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
|
3
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
4
|
+
load "rails/tasks/engine.rake"
|
5
|
+
|
6
|
+
load "rails/tasks/statistics.rake"
|
7
|
+
|
8
|
+
require "bundler/gem_tasks"
|
9
|
+
|
10
|
+
require "rake/testtask"
|
11
|
+
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = false
|
16
|
+
end
|
17
|
+
|
18
|
+
task default: :test
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require_tree ./effective_mailchimp
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
@import 'effective_mailchimp/base';
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Admin
|
2
|
+
class MailchimpController < ApplicationController
|
3
|
+
before_action(:authenticate_user!) if defined?(Devise)
|
4
|
+
before_action { EffectiveResources.authorize!(self, :admin, :effective_mailchimp) }
|
5
|
+
|
6
|
+
def mailchimp_sync_user
|
7
|
+
resource = current_user.class.find(params[:id])
|
8
|
+
|
9
|
+
EffectiveResources.authorize!(self, :update, resource)
|
10
|
+
|
11
|
+
resource.mailchimp_sync!
|
12
|
+
|
13
|
+
flash[:success] = "Successfully synced mailchimp"
|
14
|
+
|
15
|
+
redirect_back(fallback_location: "/admin/users/#{params[:id]}/edit")
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Admin
|
2
|
+
class MailchimpListsController < ApplicationController
|
3
|
+
before_action(:authenticate_user!) if defined?(Devise)
|
4
|
+
before_action { EffectiveResources.authorize!(self, :admin, :effective_mailchimp) }
|
5
|
+
|
6
|
+
# Calls Sync
|
7
|
+
before_action(only: :index) { Effective::MailchimpList.sync! }
|
8
|
+
|
9
|
+
include Effective::CrudController
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def permitted_params
|
14
|
+
params.require(:effective_mailchimp_list).permit!
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Admin
|
2
|
+
class EffectiveMailchimpListsDatatable < Effective::Datatable
|
3
|
+
filters do
|
4
|
+
scope :all
|
5
|
+
scope :subscribable
|
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_id, visible: false
|
16
|
+
col :web_id, visible: false
|
17
|
+
|
18
|
+
col :name
|
19
|
+
col :can_subscribe
|
20
|
+
|
21
|
+
col :url, label: 'Campaign' do |ml|
|
22
|
+
link_to('View Campaign', ml.url, target: '_blank')
|
23
|
+
end
|
24
|
+
|
25
|
+
col :members_url, label: 'Members' do |ml|
|
26
|
+
link_to('View Members', ml.members_url, target: '_blank')
|
27
|
+
end
|
28
|
+
|
29
|
+
actions_col
|
30
|
+
end
|
31
|
+
|
32
|
+
collection do
|
33
|
+
Effective::MailchimpList.deep.all
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
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.mailchimp_sync!(force: false)
|
10
|
+
|
11
|
+
render('effective/mailchimp_user/fields', form: form, f: form, resource: resource, mailchimp_user: resource)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# EffectiveMailchimpUser
|
2
|
+
#
|
3
|
+
# Mark your user model with effective_mailchimp_user to get a few helpers
|
4
|
+
# And user specific point required scores
|
5
|
+
|
6
|
+
module EffectiveMailchimpUser
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module Base
|
10
|
+
def effective_mailchimp_user
|
11
|
+
include ::EffectiveMailchimpUser
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def effective_mailchimp_user?; true; end
|
17
|
+
end
|
18
|
+
|
19
|
+
included do
|
20
|
+
attr_accessor :mailchimp_user_form_action
|
21
|
+
|
22
|
+
has_many :mailchimp_list_members, -> { Effective::MailchimpListMember.sorted }, as: :user, class_name: 'Effective::MailchimpListMember', dependent: :destroy
|
23
|
+
accepts_nested_attributes_for :mailchimp_list_members, allow_destroy: true
|
24
|
+
|
25
|
+
has_many :mailchimp_lists, -> { Effective::MailchimpList.sorted }, through: :mailchimp_list_members, class_name: 'Effective::MailchimpList'
|
26
|
+
accepts_nested_attributes_for :mailchimp_lists, allow_destroy: true
|
27
|
+
|
28
|
+
# The user updated the form
|
29
|
+
after_commit(if: -> { mailchimp_user_form_action }) { mailchimp_update!(force: false) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def mailchimp_subscribed_lists
|
33
|
+
mailchimp_list_members.select(&:subscribed?).map(&:mailchimp_list)
|
34
|
+
end
|
35
|
+
|
36
|
+
def mailchimp_list_member(mailchimp_list:)
|
37
|
+
raise('expected a MailchimpList') unless mailchimp_list.kind_of?(Effective::MailchimpList)
|
38
|
+
mailchimp_list_members.find { |mlm| mlm.mailchimp_list_id == mailchimp_list.id }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Find or build
|
42
|
+
def build_mailchimp_list_member(mailchimp_list:)
|
43
|
+
raise('expected a MailchimpList') unless mailchimp_list.kind_of?(Effective::MailchimpList)
|
44
|
+
mailchimp_list_member(mailchimp_list: mailchimp_list) || mailchimp_list_members.build(mailchimp_list: mailchimp_list)
|
45
|
+
end
|
46
|
+
|
47
|
+
def mailchimp_list_members_changed?
|
48
|
+
mailchimp_list_members.any? { |mlm| mlm.changes.present? || mlm.marked_for_destruction? }
|
49
|
+
end
|
50
|
+
|
51
|
+
def mailchimp_last_synced_at
|
52
|
+
mailchimp_list_members.map(&:last_synced_at).min
|
53
|
+
end
|
54
|
+
|
55
|
+
def mailchimp_sync_required?
|
56
|
+
return true if mailchimp_last_synced_at.blank?
|
57
|
+
mailchimp_last_synced_at < (Time.zone.now - 1.day)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Pulls the current status from Mailchimp API into the Mailchimp List Member objects
|
61
|
+
# Run before the mailchimp fields are displayed
|
62
|
+
def mailchimp_sync!(force: true)
|
63
|
+
api = EffectiveMailchimp.api
|
64
|
+
lists = Effective::MailchimpList.subscribable.sorted.to_a
|
65
|
+
|
66
|
+
return if lists.length == mailchimp_list_members.length && !(force || mailchimp_sync_required?)
|
67
|
+
|
68
|
+
lists.each do |mailchimp_list|
|
69
|
+
member = build_mailchimp_list_member(mailchimp_list: mailchimp_list)
|
70
|
+
|
71
|
+
list_member = api.list_member(mailchimp_list, email) || {}
|
72
|
+
member.assign_mailchimp_attributes(list_member)
|
73
|
+
end
|
74
|
+
|
75
|
+
mailchimp_list_members.each do |member|
|
76
|
+
list = lists.find { |list| list.id == member.mailchimp_list_id }
|
77
|
+
member.mark_for_destruction unless list.present?
|
78
|
+
end
|
79
|
+
|
80
|
+
save! if mailchimp_list_members_changed?
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
84
|
+
# Pushes the current Mailchimp List Member objects to Mailchimp when needed
|
85
|
+
def mailchimp_update!(force: true)
|
86
|
+
api = EffectiveMailchimp.api
|
87
|
+
|
88
|
+
assign_attributes(mailchimp_user_form_action: nil)
|
89
|
+
|
90
|
+
mailchimp_list_members.map do |member|
|
91
|
+
if member.mailchimp_id.blank? && member.subscribed?
|
92
|
+
list_member = api.list_member_add(member)
|
93
|
+
member.assign_mailchimp_attributes(list_member)
|
94
|
+
elsif member.mailchimp_id.present? && (force || mailchimp_member_update_required?(member))
|
95
|
+
list_member = api.list_member_update(member)
|
96
|
+
member.assign_mailchimp_attributes(list_member)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
save! if mailchimp_list_members_changed?
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
104
|
+
def mailchimp_member_update_required?(member)
|
105
|
+
require_update = ['email', 'last_name', 'first_name']
|
106
|
+
|
107
|
+
return true if (changes.keys & require_update).present?
|
108
|
+
return true if (previous_changes.keys & require_update).present?
|
109
|
+
|
110
|
+
return true if member.changes.present?
|
111
|
+
return true if member.previous_changes.present?
|
112
|
+
|
113
|
+
false
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# The Mailchimp API object
|
2
|
+
# https://github.com/mailchimp/mailchimp-marketing-ruby
|
3
|
+
# https://mailchimp.com/developer/marketing/api/
|
4
|
+
|
5
|
+
require 'MailchimpMarketing'
|
6
|
+
|
7
|
+
module Effective
|
8
|
+
class MailchimpApi
|
9
|
+
attr_accessor :api_key
|
10
|
+
attr_accessor :server
|
11
|
+
attr_accessor :client
|
12
|
+
|
13
|
+
def initialize(api_key:, server:)
|
14
|
+
raise('expected an api key') unless api_key.present?
|
15
|
+
raise('expected a server') unless server.present?
|
16
|
+
|
17
|
+
@api_key = api_key
|
18
|
+
@server = server
|
19
|
+
|
20
|
+
@client = ::MailchimpMarketing::Client.new()
|
21
|
+
@client.set_config(api_key: api_key, server: server)
|
22
|
+
end
|
23
|
+
|
24
|
+
def admin_url
|
25
|
+
"https://#{server}.admin.mailchimp.com"
|
26
|
+
end
|
27
|
+
|
28
|
+
def ping
|
29
|
+
client.ping.get()
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns an Array of Lists, which are each Hash
|
33
|
+
# Like this [{ ...}, { ... }]
|
34
|
+
def lists
|
35
|
+
response = client.lists.get_all_lists()
|
36
|
+
Array(response['lists']) - [nil, '', {}]
|
37
|
+
end
|
38
|
+
|
39
|
+
def list(id)
|
40
|
+
client.lists.get_list(id.try(:mailchimp_id) || id)
|
41
|
+
end
|
42
|
+
|
43
|
+
def list_member(id, email)
|
44
|
+
raise('expected an email') unless email.to_s.include?('@')
|
45
|
+
|
46
|
+
begin
|
47
|
+
client.lists.get_list_member(id.try(:mailchimp_id) || id, email)
|
48
|
+
rescue MailchimpMarketing::ApiError => e
|
49
|
+
{}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def list_member_add(member)
|
54
|
+
raise('expected an Effective::MailchimpListMember') unless member.kind_of?(Effective::MailchimpListMember)
|
55
|
+
|
56
|
+
payload = {
|
57
|
+
email_address: member.user.email,
|
58
|
+
status: (member.subscribed ? 'subscribed' : 'unsubscribed'),
|
59
|
+
merge_fields: {
|
60
|
+
'FNAME': member.user.try(:first_name),
|
61
|
+
'LNAME': member.user.try(:last_name)
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
client.lists.add_list_member(member.mailchimp_list.mailchimp_id, payload)
|
66
|
+
end
|
67
|
+
|
68
|
+
def list_member_update(member)
|
69
|
+
raise('expected an Effective::MailchimpListMember') unless member.kind_of?(Effective::MailchimpListMember)
|
70
|
+
|
71
|
+
payload = {
|
72
|
+
email_address: member.user.email,
|
73
|
+
status: (member.subscribed ? 'subscribed' : 'unsubscribed'),
|
74
|
+
merge_fields: {
|
75
|
+
'FNAME': member.user.try(:first_name),
|
76
|
+
'LNAME': member.user.try(:last_name)
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
client.lists.update_list_member(member.mailchimp_list.mailchimp_id, member.email, payload)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Effective
|
2
|
+
class MailchimpList < ActiveRecord::Base
|
3
|
+
|
4
|
+
self.table_name = (EffectiveMailchimp.mailchimp_lists_table_name || :mailchimp_lists).to_s
|
5
|
+
|
6
|
+
effective_resource do
|
7
|
+
mailchimp_id :string
|
8
|
+
web_id :string
|
9
|
+
|
10
|
+
name :string
|
11
|
+
can_subscribe :boolean
|
12
|
+
|
13
|
+
timestamps
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
name.presence || model_name.human
|
18
|
+
end
|
19
|
+
|
20
|
+
scope :deep, -> { all }
|
21
|
+
scope :sorted, -> { order(:name) }
|
22
|
+
scope :subscribable, -> { where(can_subscribe: true) }
|
23
|
+
|
24
|
+
# Creates or builds all the Lists
|
25
|
+
def self.sync!
|
26
|
+
# All the Lists from Mailchimp
|
27
|
+
lists = EffectiveMailchimp.api.lists
|
28
|
+
|
29
|
+
# Get all our existing Effective::MailchimpList records
|
30
|
+
mailchimp_lists = all()
|
31
|
+
|
32
|
+
# Find or create Effective::Mailchimp based on existing lists
|
33
|
+
lists.each do |list|
|
34
|
+
mailchimp_id = list['id']
|
35
|
+
web_id = list['web_id']
|
36
|
+
name = list['name']
|
37
|
+
|
38
|
+
mailchimp_list = mailchimp_lists.find { |ml| ml.mailchimp_id == mailchimp_id } || new()
|
39
|
+
mailchimp_list.assign_attributes(mailchimp_id: mailchimp_id, web_id: web_id, name: name)
|
40
|
+
mailchimp_list.save!
|
41
|
+
end
|
42
|
+
|
43
|
+
# Destroy any Effective::Mailchimp resources if they no longer returned by lists
|
44
|
+
mailchimp_lists.each do |mailchimp_list|
|
45
|
+
list = lists.find { |list| list['id'] == mailchimp_list.mailchimp_id }
|
46
|
+
mailchimp_list.destroy! unless list.present?
|
47
|
+
end
|
48
|
+
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def url
|
53
|
+
EffectiveMailchimp.api.admin_url + "/campaigns/#f_list:#{web_id}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def members_url
|
57
|
+
EffectiveMailchimp.api.admin_url + "/lists/members?id=#{web_id}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def can_subscribe!
|
61
|
+
update!(can_subscribe: true)
|
62
|
+
end
|
63
|
+
|
64
|
+
def cannot_subscribe!
|
65
|
+
update!(can_subscribe: false)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Effective
|
2
|
+
class MailchimpListMember < ActiveRecord::Base
|
3
|
+
self.table_name = (EffectiveMailchimp.mailchimp_list_members_table_name || :mailchimp_list_members).to_s
|
4
|
+
|
5
|
+
belongs_to :user, polymorphic: true
|
6
|
+
belongs_to :mailchimp_list
|
7
|
+
|
8
|
+
log_changes(to: :user, except: :last_synced_at) if respond_to?(:log_changes)
|
9
|
+
|
10
|
+
effective_resource do
|
11
|
+
mailchimp_id :string
|
12
|
+
web_id :string
|
13
|
+
|
14
|
+
email_address :string
|
15
|
+
full_name :string
|
16
|
+
|
17
|
+
subscribed :boolean
|
18
|
+
|
19
|
+
last_synced_at :datetime
|
20
|
+
|
21
|
+
timestamps
|
22
|
+
end
|
23
|
+
|
24
|
+
scope :deep, -> { includes(:mailchimp_list, :user) }
|
25
|
+
scope :sorted, -> { order(:id) }
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
mailchimp_list&.to_s || model_name.human
|
29
|
+
end
|
30
|
+
|
31
|
+
def email
|
32
|
+
email_address.presence || user.email
|
33
|
+
end
|
34
|
+
|
35
|
+
def assign_mailchimp_attributes(atts)
|
36
|
+
assign_attributes(
|
37
|
+
mailchimp_id: atts['id'],
|
38
|
+
web_id: atts['web_id'],
|
39
|
+
email_address: atts['email_address'],
|
40
|
+
full_name: atts['full_name'],
|
41
|
+
subscribed: (atts['status'] == 'subscribed'),
|
42
|
+
last_synced_at: Time.zone.now
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
= effective_form_with(model: [:admin, mailchimp_list], engine: true) do |f|
|
2
|
+
= f.static_field :mailchimp_id
|
3
|
+
= f.static_field :web_id
|
4
|
+
= f.static_field :name
|
5
|
+
|
6
|
+
= f.check_box :can_subscribe, label: 'Yes, display this list to users and allow them to subscribe'
|
7
|
+
|
8
|
+
= effective_submit(f)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
= card(ets(Effective::MailchimpList)) do
|
2
|
+
%p.text-muted #{user} is subscribed to #{pluralize(user.mailchimp_subscribed_lists.count, et(Effective::MailchimpList))}.
|
3
|
+
|
4
|
+
%p.text-muted
|
5
|
+
Please visit
|
6
|
+
= link_to 'All ' + ets(Effective::MailchimpList), effective_mailchimp.admin_mailchimp_lists_path, target: '_blank'
|
7
|
+
to configure which lists are displayed.
|
8
|
+
|
9
|
+
%p
|
10
|
+
%strong Subscribed
|
11
|
+
|
12
|
+
= effective_form_with model: [:admin, user] do |f|
|
13
|
+
= f.hidden_field :id
|
14
|
+
= mailchimp_user_fields(f)
|
15
|
+
|
16
|
+
%p.text-muted
|
17
|
+
%small
|
18
|
+
last synced with
|
19
|
+
= link_to 'Mailchimp', EffectiveMailchimp.api.admin_url
|
20
|
+
= time_ago_in_words(user.mailchimp_last_synced_at)
|
21
|
+
ago.
|
22
|
+
= link_to 'sync now', effective_mailchimp.mailchimp_sync_user_admin_mailchimp_path(f.object), 'data-method': :post
|
23
|
+
|
24
|
+
|
25
|
+
= f.submit
|
@@ -0,0 +1,12 @@
|
|
1
|
+
EffectiveMailchimp.setup do |config|
|
2
|
+
# config.mailchimp_lists_table_name = :mailchimp_lists
|
3
|
+
# config.mailchimp_list_members_table_name = :mailchimp_list_members
|
4
|
+
|
5
|
+
# Layout Settings
|
6
|
+
# Configure the Layout per controller, or all at once
|
7
|
+
# config.layout = { application: 'application', admin: 'admin' }
|
8
|
+
|
9
|
+
# Mailchimp Settings
|
10
|
+
config.api_key = '' # From mailchimp's /account/api/ screen
|
11
|
+
config.server = '' # Determine from your mailchimp account URL. Something like us1
|
12
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Rails.application.routes.draw do
|
4
|
+
mount EffectiveMailchimp::Engine => '/', as: 'effective_mailchimp'
|
5
|
+
end
|
6
|
+
|
7
|
+
EffectiveMailchimp::Engine.routes.draw do
|
8
|
+
# Public routes
|
9
|
+
scope module: 'effective' do
|
10
|
+
end
|
11
|
+
|
12
|
+
namespace :admin do
|
13
|
+
resources :mailchimp_lists, only: [:index, :edit, :update] do
|
14
|
+
post :can_subscribe, on: :member
|
15
|
+
post :cannot_subscribe, on: :member
|
16
|
+
end
|
17
|
+
|
18
|
+
resources :mailchimp, only: [] do
|
19
|
+
post :mailchimp_sync_user, on: :member
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class CreateEffectiveMailchimp < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
|
4
|
+
create_table :mailchimp_lists do |t|
|
5
|
+
t.string :mailchimp_id
|
6
|
+
t.string :web_id
|
7
|
+
t.string :name
|
8
|
+
|
9
|
+
t.boolean :can_subscribe
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :mailchimp_list_members do |t|
|
15
|
+
t.integer :user_id
|
16
|
+
t.string :user_type
|
17
|
+
|
18
|
+
t.integer :mailchimp_list_id
|
19
|
+
|
20
|
+
t.string :mailchimp_id
|
21
|
+
t.string :web_id
|
22
|
+
|
23
|
+
t.string :email_address
|
24
|
+
t.string :full_name
|
25
|
+
|
26
|
+
t.boolean :subscribed
|
27
|
+
|
28
|
+
t.datetime :last_synced_at
|
29
|
+
|
30
|
+
t.timestamps
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/db/seeds.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts "Running effective_mailchimp seeds"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module EffectiveMailchimp
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
engine_name 'effective_mailchimp'
|
4
|
+
|
5
|
+
# Set up our default configuration options.
|
6
|
+
initializer 'effective_mailchimp.defaults', before: :load_config_initializers do |app|
|
7
|
+
eval File.read("#{config.root}/config/effective_mailchimp.rb")
|
8
|
+
end
|
9
|
+
|
10
|
+
# Include acts_as_mailchimp concern and allow any ActiveRecord object to call it
|
11
|
+
initializer 'effective_mailchimp.active_record' do |app|
|
12
|
+
app.config.to_prepare do
|
13
|
+
ActiveRecord::Base.extend(EffectiveMailchimpUser::Base)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'effective_resources'
|
2
|
+
require 'effective_datatables'
|
3
|
+
require 'effective_mailchimp/engine'
|
4
|
+
require 'effective_mailchimp/version'
|
5
|
+
|
6
|
+
module EffectiveMailchimp
|
7
|
+
|
8
|
+
def self.config_keys
|
9
|
+
[
|
10
|
+
:mailchimp_lists_table_name, :mailchimp_list_members_table_name,
|
11
|
+
:layout,
|
12
|
+
:api_key, :server
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
include EffectiveGem
|
17
|
+
|
18
|
+
def self.api
|
19
|
+
Effective::MailchimpApi.new(api_key: api_key, server: server)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.permitted_params
|
23
|
+
[ :mailchimp_user_form_action, mailchimp_list_members_attributes: [:id, :subscribed] ]
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module EffectiveMailchimp
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
include Rails::Generators::Migration
|
5
|
+
|
6
|
+
desc 'Creates an EffectiveMailchimp initializer in your application.'
|
7
|
+
|
8
|
+
source_root File.expand_path('../../templates', __FILE__)
|
9
|
+
|
10
|
+
def self.next_migration_number(dirname)
|
11
|
+
if not ActiveRecord::Base.timestamped_migrations
|
12
|
+
Time.new.utc.strftime("%Y%m%d%H%M%S")
|
13
|
+
else
|
14
|
+
'%.3d' % (current_migration_number(dirname) + 1)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def copy_initializer
|
19
|
+
template ('../' * 3) + 'config/effective_mailchimp.rb', 'config/initializers/effective_mailchimp.rb'
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_migration_file
|
23
|
+
migration_template ('../' * 3) + 'db/migrate/01_create_effective_mailchimp.rb.erb', 'db/migrate/create_effective_mailchimp.rb'
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,240 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: effective_mailchimp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Code and Effect
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-02-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 6.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: MailchimpMarketing
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: effective_bootstrap
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: effective_datatables
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.0.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 4.0.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: effective_resources
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sqlite3
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: devise
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: haml-rails
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: pry-byebug
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: effective_test_bot
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: effective_developer
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: effective_email_templates
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: Subscribe and unsubscribe to mailchimp lists.
|
182
|
+
email:
|
183
|
+
- info@codeandeffect.com
|
184
|
+
executables: []
|
185
|
+
extensions: []
|
186
|
+
extra_rdoc_files: []
|
187
|
+
files:
|
188
|
+
- MIT-LICENSE
|
189
|
+
- README.md
|
190
|
+
- Rakefile
|
191
|
+
- app/assets/config/effective_mailchimp_manifest.js
|
192
|
+
- app/assets/javascripts/effective_mailchimp.js
|
193
|
+
- app/assets/javascripts/effective_mailchimp/base.js
|
194
|
+
- app/assets/stylesheets/effective_mailchimp.scss
|
195
|
+
- app/assets/stylesheets/effective_mailchimp/base.scss
|
196
|
+
- app/controllers/admin/mailchimp_controller.rb
|
197
|
+
- app/controllers/admin/mailchimp_lists_controller.rb
|
198
|
+
- app/datatables/admin/effective_mailchimp_lists_datatable.rb
|
199
|
+
- app/helpers/effective_mailchimp_helper.rb
|
200
|
+
- app/models/concerns/effective_mailchimp_user.rb
|
201
|
+
- app/models/effective/mailchimp_api.rb
|
202
|
+
- app/models/effective/mailchimp_list.rb
|
203
|
+
- app/models/effective/mailchimp_list_member.rb
|
204
|
+
- app/views/admin/mailchimp_lists/_form.html.haml
|
205
|
+
- app/views/admin/mailchimp_user/_form.html.haml
|
206
|
+
- app/views/effective/mailchimp_user/_fields.html.haml
|
207
|
+
- config/effective_mailchimp.rb
|
208
|
+
- config/routes.rb
|
209
|
+
- db/migrate/01_create_effective_mailchimp.rb.erb
|
210
|
+
- db/seeds.rb
|
211
|
+
- lib/effective_mailchimp.rb
|
212
|
+
- lib/effective_mailchimp/engine.rb
|
213
|
+
- lib/effective_mailchimp/version.rb
|
214
|
+
- lib/generators/effective_mailchimp/install_generator.rb
|
215
|
+
- lib/generators/templates/effective_mailchimp_mailer_preview.rb
|
216
|
+
- lib/tasks/effective_mailchimp_tasks.rake
|
217
|
+
homepage: https://github.com/code-and-effect/effective_mailchimp
|
218
|
+
licenses:
|
219
|
+
- MIT
|
220
|
+
metadata: {}
|
221
|
+
post_install_message:
|
222
|
+
rdoc_options: []
|
223
|
+
require_paths:
|
224
|
+
- lib
|
225
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - ">="
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0'
|
230
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
231
|
+
requirements:
|
232
|
+
- - ">="
|
233
|
+
- !ruby/object:Gem::Version
|
234
|
+
version: '0'
|
235
|
+
requirements: []
|
236
|
+
rubygems_version: 3.1.2
|
237
|
+
signing_key:
|
238
|
+
specification_version: 4
|
239
|
+
summary: Subscribe and unsubscribe to mailchimp lists.
|
240
|
+
test_files: []
|