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.
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: []