effective_mailchimp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|