effective_mailchimp 0.1.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 +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +128 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/effective_mailchimp_manifest.js +3 -0
  6. data/app/assets/javascripts/effective_mailchimp/base.js +0 -0
  7. data/app/assets/javascripts/effective_mailchimp.js +1 -0
  8. data/app/assets/stylesheets/effective_mailchimp/base.scss +0 -0
  9. data/app/assets/stylesheets/effective_mailchimp.scss +1 -0
  10. data/app/controllers/admin/mailchimp_controller.rb +19 -0
  11. data/app/controllers/admin/mailchimp_lists_controller.rb +18 -0
  12. data/app/datatables/admin/effective_mailchimp_lists_datatable.rb +36 -0
  13. data/app/helpers/effective_mailchimp_helper.rb +14 -0
  14. data/app/models/concerns/effective_mailchimp_user.rb +116 -0
  15. data/app/models/effective/mailchimp_api.rb +84 -0
  16. data/app/models/effective/mailchimp_list.rb +69 -0
  17. data/app/models/effective/mailchimp_list_member.rb +47 -0
  18. data/app/views/admin/mailchimp_lists/_form.html.haml +8 -0
  19. data/app/views/admin/mailchimp_user/_form.html.haml +25 -0
  20. data/app/views/effective/mailchimp_user/_fields.html.haml +4 -0
  21. data/config/effective_mailchimp.rb +12 -0
  22. data/config/routes.rb +24 -0
  23. data/db/migrate/01_create_effective_mailchimp.rb.erb +34 -0
  24. data/db/seeds.rb +1 -0
  25. data/lib/effective_mailchimp/engine.rb +18 -0
  26. data/lib/effective_mailchimp/version.rb +3 -0
  27. data/lib/effective_mailchimp.rb +26 -0
  28. data/lib/generators/effective_mailchimp/install_generator.rb +28 -0
  29. data/lib/generators/templates/effective_mailchimp_mailer_preview.rb +4 -0
  30. data/lib/tasks/effective_mailchimp_tasks.rake +8 -0
  31. 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
@@ -0,0 +1,3 @@
1
+ //= link_directory ../javascripts .js
2
+ //= link_directory ../stylesheets .css
3
+ //= link_tree ../images
@@ -0,0 +1 @@
1
+ //= require_tree ./effective_mailchimp
@@ -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,4 @@
1
+ = f.hidden_field :mailchimp_user_form_action, value: true
2
+
3
+ = f.fields_for :mailchimp_list_members do |fmlm|
4
+ %p= fmlm.check_box :subscribed, label: fmlm.object.to_s
@@ -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,3 @@
1
+ module EffectiveMailchimp
2
+ VERSION = '0.1.0'.freeze
3
+ 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
@@ -0,0 +1,4 @@
1
+ # Visit http://localhost:3000/rails/mailers
2
+
3
+ class EffectiveMailchimpMailerPreview < ActionMailer::Preview
4
+ end
@@ -0,0 +1,8 @@
1
+ namespace :effective_mailchimp do
2
+
3
+ # bundle exec rake effective_mailchimp:seed
4
+ task seed: :environment do
5
+ load "#{__dir__}/../../db/seeds.rb"
6
+ end
7
+
8
+ 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: []