dm_newsletter 4.2.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +33 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/stylesheets/dm_newsletter/application.css +13 -0
  6. data/app/controllers/dm_newsletter/admin/admin_controller.rb +16 -0
  7. data/app/controllers/dm_newsletter/admin/newsletters_controller.rb +81 -0
  8. data/app/controllers/dm_newsletter/application_controller.rb +3 -0
  9. data/app/controllers/dm_newsletter/newsletters_controller.rb +50 -0
  10. data/app/helpers/dm_newsletter/newsletters_helper.rb +8 -0
  11. data/app/models/dm_newsletter/concerns/ability.rb +37 -0
  12. data/app/models/dm_newsletter/permitted_params.rb +20 -0
  13. data/app/models/mailchimp_newsletter.rb +217 -0
  14. data/app/models/mailchimp_newsletter_subscriber.rb +43 -0
  15. data/app/models/newsletter.rb +46 -0
  16. data/app/models/newsletter_subscriber.rb +257 -0
  17. data/app/models/standard_newsletter.rb +19 -0
  18. data/app/views/dm_newsletter/admin/newsletters/_form.html.erb +17 -0
  19. data/app/views/dm_newsletter/admin/newsletters/edit.html.erb +2 -0
  20. data/app/views/dm_newsletter/admin/newsletters/index.html.erb +32 -0
  21. data/app/views/dm_newsletter/admin/newsletters/new.html.erb +2 -0
  22. data/app/views/dm_newsletter/admin/newsletters/show.html.erb +46 -0
  23. data/config/locales/nms.cs.yml +15 -0
  24. data/config/locales/nms.de.yml +15 -0
  25. data/config/locales/nms.en.yml +15 -0
  26. data/config/locales/nms.fi.yml +15 -0
  27. data/config/locales/nms.ja.yml +15 -0
  28. data/config/routes.rb +15 -0
  29. data/db/20130730120724_create_subscribers.rb +32 -0
  30. data/db/migrate/20130729075442_create_newsletter.rb +24 -0
  31. data/lib/dm_newsletter.rb +4 -0
  32. data/lib/dm_newsletter/engine.rb +8 -0
  33. data/lib/tasks/dm_newsletter_tasks.rake +4 -0
  34. data/spec/dummy/README.rdoc +28 -0
  35. data/spec/dummy/Rakefile +6 -0
  36. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  37. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  38. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  39. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  40. data/spec/dummy/app/models/ability.rb +12 -0
  41. data/spec/dummy/app/models/user.rb +6 -0
  42. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  43. data/spec/dummy/bin/bundle +3 -0
  44. data/spec/dummy/bin/rails +4 -0
  45. data/spec/dummy/bin/rake +4 -0
  46. data/spec/dummy/config.ru +4 -0
  47. data/spec/dummy/config/application.rb +26 -0
  48. data/spec/dummy/config/boot.rb +5 -0
  49. data/spec/dummy/config/database.yml +25 -0
  50. data/spec/dummy/config/environment.rb +5 -0
  51. data/spec/dummy/config/environments/development.rb +37 -0
  52. data/spec/dummy/config/environments/production.rb +82 -0
  53. data/spec/dummy/config/environments/test.rb +39 -0
  54. data/spec/dummy/config/initializers/assets.rb +8 -0
  55. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  56. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  57. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  58. data/spec/dummy/config/initializers/inflections.rb +16 -0
  59. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  60. data/spec/dummy/config/initializers/session_store.rb +3 -0
  61. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  62. data/spec/dummy/config/locales/en.yml +23 -0
  63. data/spec/dummy/config/routes.rb +12 -0
  64. data/spec/dummy/config/secrets.yml +22 -0
  65. data/spec/dummy/db/development.sqlite3 +0 -0
  66. data/spec/dummy/db/migrate/20141114170927_add_globalize_countries.dm_core.rb +50 -0
  67. data/spec/dummy/db/migrate/20141114170928_devise_create_users.dm_core.rb +46 -0
  68. data/spec/dummy/db/migrate/20141114170929_add_user_fields.dm_core.rb +14 -0
  69. data/spec/dummy/db/migrate/20141114170930_rolify_create_roles.dm_core.rb +20 -0
  70. data/spec/dummy/db/migrate/20141114170931_add_last_access.dm_core.rb +10 -0
  71. data/spec/dummy/db/migrate/20141114170932_create_versions.dm_core.rb +19 -0
  72. data/spec/dummy/db/migrate/20141114170933_add_object_changes_column_to_versions.dm_core.rb +12 -0
  73. data/spec/dummy/db/migrate/20141114170934_create_dm_core_accounts.dm_core.rb +13 -0
  74. data/spec/dummy/db/migrate/20141114170935_add_account_to_users.dm_core.rb +9 -0
  75. data/spec/dummy/db/migrate/20141114170936_create_preferences.dm_core.rb +13 -0
  76. data/spec/dummy/db/migrate/20141114170937_create_comments.dm_core.rb +22 -0
  77. data/spec/dummy/db/migrate/20141114170938_add_activity.dm_core.rb +21 -0
  78. data/spec/dummy/db/migrate/20141114170939_add_type_to_comments.dm_core.rb +9 -0
  79. data/spec/dummy/db/migrate/20141114170940_add_category.dm_core.rb +28 -0
  80. data/spec/dummy/db/migrate/20141114170941_create_email_table.dm_core.rb +26 -0
  81. data/spec/dummy/db/migrate/20141114170942_add_user_profile.dm_core.rb +46 -0
  82. data/spec/dummy/db/migrate/20141114170943_add_profile_email.dm_core.rb +14 -0
  83. data/spec/dummy/db/migrate/20141114170944_create_payment_history.dm_core.rb +37 -0
  84. data/spec/dummy/db/migrate/20141114170945_change_anchor_field.dm_core.rb +10 -0
  85. data/spec/dummy/db/migrate/20141114170946_create_user_site_profile.dm_core.rb +27 -0
  86. data/spec/dummy/db/migrate/20141114170947_add_avatar.dm_core.rb +12 -0
  87. data/spec/dummy/db/migrate/20141114170948_add_notify_to_payment_history.dm_core.rb +8 -0
  88. data/spec/dummy/db/migrate/20141114170949_acts_as_votable_migration.dm_core.rb +28 -0
  89. data/spec/dummy/db/migrate/20141114170950_add_user_site_profile_uuid.dm_core.rb +19 -0
  90. data/spec/dummy/db/migrate/20141114170951_add_invoice_id.dm_core.rb +7 -0
  91. data/spec/dummy/db/migrate/20141114170952_acts_as_follower_migration.dm_core.rb +18 -0
  92. data/spec/dummy/db/migrate/20141114170953_rename_invoice_id.dm_core.rb +12 -0
  93. data/spec/dummy/db/migrate/20141114170954_add_core_addresses.dm_core.rb +18 -0
  94. data/spec/dummy/db/migrate/20141114170955_papertrail_increase_column.dm_core.rb +9 -0
  95. data/spec/dummy/db/migrate/20141114170956_acts_as_taggable_on_migration.dm_core.rb +32 -0
  96. data/spec/dummy/db/migrate/20141114170957_add_missing_unique_indices.dm_core.rb +23 -0
  97. data/spec/dummy/db/migrate/20141114170958_add_taggings_counter_cache_to_tags.dm_core.rb +16 -0
  98. data/spec/dummy/db/migrate/20141114170959_create_custom_fields.dm_core.rb +40 -0
  99. data/spec/dummy/db/migrate/20141114170960_add_missing_taggable_index.dm_core.rb +11 -0
  100. data/spec/dummy/db/migrate/20141119112030_create_cms.dm_cms.rb +92 -0
  101. data/spec/dummy/db/migrate/20141119112031_add_account_to_cms.dm_cms.rb +11 -0
  102. data/spec/dummy/db/migrate/20141119112032_create_blog.dm_cms.rb +62 -0
  103. data/spec/dummy/db/migrate/20141119112033_add_notification_sent.dm_cms.rb +6 -0
  104. data/spec/dummy/db/migrate/20141119112034_add_blog_comment.dm_cms.rb +8 -0
  105. data/spec/dummy/db/migrate/20141119112035_add_blog_image.dm_cms.rb +6 -0
  106. data/spec/dummy/db/migrate/20141119112036_rename_snippet_slug.dm_cms.rb +14 -0
  107. data/spec/dummy/db/migrate/20141119112037_add_requires_subscription_blog.dm_cms.rb +8 -0
  108. data/spec/dummy/db/migrate/20141119112038_add_pages_ranked_model.dm_cms.rb +23 -0
  109. data/spec/dummy/db/migrate/20141119112039_add_blog_owner.dm_cms.rb +7 -0
  110. data/spec/dummy/db/migrate/20141119112040_create_media_files.dm_cms.rb +30 -0
  111. data/spec/dummy/db/migrate/20141119112041_add_cmspage_summary.dm_cms.rb +7 -0
  112. data/spec/dummy/db/migrate/20141119112042_add_blog_image_email_header.dm_cms.rb +6 -0
  113. data/spec/dummy/db/migrate/20141119112043_add_header_image.dm_cms.rb +10 -0
  114. data/spec/dummy/db/migrate/20160128145239_add_favored_locale.dm_core.rb +17 -0
  115. data/spec/dummy/db/migrate/20160128145240_update_papertrail_v4.dm_core.rb +41 -0
  116. data/spec/dummy/db/schema.rb +593 -0
  117. data/spec/dummy/public/404.html +67 -0
  118. data/spec/dummy/public/422.html +67 -0
  119. data/spec/dummy/public/500.html +66 -0
  120. data/spec/dummy/public/favicon.ico +0 -0
  121. data/spec/factories/accounts.rb +9 -0
  122. data/spec/factories/user_profiles.rb +10 -0
  123. data/spec/factories/users.rb +19 -0
  124. data/spec/models/newsletter_spec.rb +9 -0
  125. data/spec/rails_helper.rb +70 -0
  126. data/spec/spec_helper.rb +85 -0
  127. data/spec/support/accounts.rb +17 -0
  128. data/spec/support/devise.rb +44 -0
  129. data/spec/support/fix_locale.rb +57 -0
  130. metadata +296 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9a3b03f31653026403d126ca9b3abf692aac4819
4
+ data.tar.gz: 90255c7249b1d731919e056aaab344ae76f62442
5
+ SHA512:
6
+ metadata.gz: c260c52c8739df5137ebb2f7cb7484326d699a101a307a207270162658db99b4ec1042a6146858b715e17fb69b99146f1ac3ff7f69c74e3a408e64c48905aa00
7
+ data.tar.gz: bf3be1368f65dbc081072d03345b36975f6c0e659dbbbc4a9061af33e57c9549025e1e038b4acde530def16438d9ea2b70d92c7d1e1de8037625064a883ca62f
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 digitalMoksha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # dm_newsletter
2
+
3
+ **dm_newsletter** provides the newsletter management system for the MokshaCMS collection of gems.
4
+
5
+ _The MokshaCMS collection of gems provides an integrated system of services for content, event, forum, learning, and newsletter management. It supports sites with multiple languages and mutliple distinct sites per installation. Administration is built in. Additional services/engines can be written to provide additional functionality._
6
+
7
+ - core foundation ([dm_core](https://github.com/digitalmoksha/dm_core))
8
+ - content management ([dm_cms](https://github.com/digitalmoksha/dm_cms))
9
+ - event management ([dm_event](https://github.com/digitalmoksha/dm_event))
10
+ - forum management ([dm_forum](https://github.com/digitalmoksha/dm_forum))
11
+ - learning management ([dm_lms](https://github.com/digitalmoksha/dm_lms))
12
+ - newsletter management ([dm_newsletter](https://github.com/digitalmoksha/dm_newsletter))
13
+
14
+ ## Installation
15
+
16
+ Add the following to your Gem file:
17
+
18
+ ```
19
+ gem 'dm_preferences', '~> 1.0'
20
+ gem 'dm_core', git: 'https://github.com/digitalmoksha/dm_core.git', branch: '4-2-stable'
21
+ gem 'dm_cms', git: 'https://github.com/digitalmoksha/dm_cms.git', branch: '4-2-stable'
22
+ gem 'dm_newsletter', git: 'https://github.com/digitalmoksha/dm_newsletter.git', branch: '4-2-stable'
23
+ gem 'themes_for_rails', git: 'git://github.com/digitalmoksha/themes_for_rails.git'
24
+ gem 'aced_rails', git: 'git://github.com/digitalmoksha/aced_rails.git'
25
+ ```
26
+
27
+ After running `bundle install`, run
28
+
29
+ ```
30
+ rake dm_core:install:migrations
31
+ rake dm_cms:install:migrations
32
+ rake dm_newsletter:install:migrations
33
+ ```
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'DmNewsletter'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ require 'rspec/core'
25
+ require 'rspec/core/rake_task'
26
+
27
+ desc "Run all specs in spec directory (excluding plugin specs)"
28
+ RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
29
+
30
+ task :default => :spec
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,16 @@
1
+ class DmNewsletter::Admin::AdminController < DmCore::Admin::AdminController
2
+ include DmNewsletter::NewslettersHelper
3
+ helper 'dm_newsletter/newsletters'
4
+
5
+ before_filter :authorize_access
6
+
7
+ protected
8
+
9
+ #------------------------------------------------------------------------------
10
+ def authorize_access
11
+ unless can?(:manage_newsletters, :all)
12
+ flash[:alert] = "Unauthorized Access!"
13
+ redirect_to current_account.index_path
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,81 @@
1
+ class DmNewsletter::Admin::NewslettersController < DmNewsletter::Admin::AdminController
2
+ include DmNewsletter::PermittedParams
3
+
4
+ #before_filter :mailchimp_guard, only: [:new, :edit, :create, :update]
5
+ before_filter :newsletter_lookup, except: [:index, :new, :create, :synchronize_lists]
6
+
7
+ #------------------------------------------------------------------------------
8
+ def index
9
+ @newsletters = using_mailchimp? ? MailchimpNewsletter.all : StandardNewsletter.all
10
+ @newsletters.each { |newsletter| newsletter.update_list_stats }
11
+ end
12
+
13
+ #------------------------------------------------------------------------------
14
+ def new
15
+ @newsletter = using_mailchimp? ? MailchimpNewsletter.new : StandardNewsletter.new
16
+ end
17
+
18
+ #------------------------------------------------------------------------------
19
+ def edit
20
+ end
21
+
22
+ #------------------------------------------------------------------------------
23
+ def create
24
+ @newsletter = using_mailchimp? ? MailchimpNewsletter.new(mailchimp_newsletter_params) : StandardNewsletter.new(standard_newsletter_params)
25
+ if @newsletter.save
26
+ redirect_to admin_newsletters_url, notice: 'Newsletter was successfully created.'
27
+ else
28
+ render action: :new
29
+ end
30
+ end
31
+
32
+ #------------------------------------------------------------------------------
33
+ def update
34
+ if @newsletter.update_attributes(newsletter_params)
35
+ redirect_to admin_newsletters_url, notice: 'Newsletter was successfully updated.'
36
+ else
37
+ render action: :edit
38
+ end
39
+ end
40
+
41
+ #------------------------------------------------------------------------------
42
+ def destroy
43
+ @newsletter.destroy
44
+
45
+ redirect_to admin_newsletters_url
46
+ end
47
+
48
+ #------------------------------------------------------------------------------
49
+ def show
50
+ # @subscriptions = @newsletter.subscriptions
51
+ @folder_list = @newsletter.folder_list
52
+ end
53
+
54
+ #------------------------------------------------------------------------------
55
+ def synchronize_lists
56
+ if using_mailchimp?
57
+ MailchimpNewsletter.synchronize
58
+ redirect_to(admin_newsletters_url, notice: 'Synchronized with Mailchimp') and return
59
+ else
60
+ redirect_to(admin_newsletters_url) and return
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ #------------------------------------------------------------------------------
67
+ def newsletter_lookup
68
+ @newsletter = Newsletter.find(params[:id])
69
+ end
70
+
71
+ # Protects certain actions from being run if we're using mailchimp integration
72
+ #------------------------------------------------------------------------------
73
+ def mailchimp_guard
74
+ if using_mailchimp?
75
+ redirect_to(admin_newsletters_url, error: 'Action not supported when using Mailchimp') and return false
76
+ else
77
+ true
78
+ end
79
+ end
80
+
81
+ end
@@ -0,0 +1,3 @@
1
+ class DmNewsletter::ApplicationController < ::ApplicationController
2
+ include ApplicationHelper
3
+ end
@@ -0,0 +1,50 @@
1
+ class DmNewsletter::NewslettersController < DmNewsletter::ApplicationController
2
+
3
+ # Handle a newsletter signup. By submitting using a Rails form
4
+ # and then adding via the MailChimp API, it should cut out automated signups
5
+ # from spam bots, because the authenticity token will be validated first.
6
+ #------------------------------------------------------------------------------
7
+ def subscribe_to_newsletter
8
+ subscription_params = params['subscription'] || {}
9
+ user_or_email = current_user ? current_user : subscription_params['email']
10
+ @newsletter = Newsletter.find_newsletter(params['token'])
11
+
12
+ if @newsletter
13
+ result = @newsletter.subscribe(user_or_email, subscription_params)
14
+ respond_to do |format|
15
+ if result[:success]
16
+ msg = I18n.t('nms.subscription_successful')
17
+ format.html { redirect_to (request.env['HTTP_REFERER'].blank? ? main_app.index_url : :back), notice: msg }
18
+ format.json { render json: { success: true, msg: msg } }
19
+ else
20
+ msg = I18n.t(@newsletter.map_error_to_msg(result[:code]))
21
+ format.html { redirect_to :back, alert: msg }
22
+ format.json { render json: { success: false, msg: msg } }
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ # Unsubscribe current user from newsletter. We only support direct unsubscribing
29
+ # for a logged in user
30
+ #------------------------------------------------------------------------------
31
+ def unsubscribe_from_newsletter
32
+ email = current_user ? current_user.email : ''
33
+ @newsletter = Newsletter.find_newsletter(params['token'])
34
+
35
+ if (@newsletter)
36
+ result = @newsletter.unsubscribe(email)
37
+ respond_to do |format|
38
+ if result
39
+ msg = I18n.t('nms.unsubscribe_successuful')
40
+ format.html { redirect_to (request.env['HTTP_REFERER'].blank? ? main_app.index_url : :back), notice: msg }
41
+ format.js { render json: { success: true, msg: msg } }
42
+ else
43
+ msg = I18n.t('nms.Email_NotExists')
44
+ format.html { redirect_to :back, alert: msg }
45
+ format.js { render json: { success: false, msg: msg } }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,8 @@
1
+ module DmNewsletter::NewslettersHelper
2
+
3
+ #------------------------------------------------------------------------------
4
+ def using_mailchimp?
5
+ Account.current.preferred_nms_use_mailchimp?
6
+ end
7
+
8
+ end
@@ -0,0 +1,37 @@
1
+ # Wrap lms specific CanCan rules. Should be included in the main app's
2
+ # Ability class.
3
+ # NOTE: When checking abilities, don't check for Class level abilities,
4
+ # unless you don't care about the instance level. For example, don't
5
+ # use both styles
6
+ # can? :moderate, Forum
7
+ # can? :moderate, @forum
8
+ # In this case, if you need to check the class level, then use specific
9
+ # current_user.has_role? :moderator, Forum
10
+ #------------------------------------------------------------------------------
11
+
12
+ module DmNewsletter
13
+ module Concerns
14
+ module Ability
15
+ def dm_newsletter_abilities(user)
16
+ #--- Admin
17
+ if user && user.has_role?(:newsletter_manager)
18
+ can :manage_newsletters, :all
19
+ can :access_admin, :all
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ #------------------------------------------------------------------------------
27
+ # The abilities get basically compiled. So if you use
28
+ #
29
+ # can :moderate, Forum, :id => Forum.with_role(:moderator, user).map(&:id)
30
+ #
31
+ # this will execute the Forum.with_role query once during Ability.new. However
32
+ #
33
+ # can :moderate, Forum do |forum|
34
+ # user.has_role? :moderator, forum
35
+ # end
36
+ #
37
+ # this will execute the has_role? block on each call to can?
@@ -0,0 +1,20 @@
1
+ module DmNewsletter
2
+ module PermittedParams
3
+
4
+ #------------------------------------------------------------------------------
5
+ def newsletter_params
6
+ params.require(:newsletter).permit! if can? :manage_newsletters, :all
7
+ end
8
+
9
+ #------------------------------------------------------------------------------
10
+ def standard_newsletter_params
11
+ params.require(:standard_newsletter).permit! if can? :manage_newsletters, :all
12
+ end
13
+
14
+ #------------------------------------------------------------------------------
15
+ def mailchimp_newsletter_params
16
+ params.require(:mailchimp_newsletter).permit! if can? :manage_newsletters, :all
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,217 @@
1
+ # Integrates MailChimp into our system. Using the 'gibbon' gem to interface
2
+ # with MailChimp API V2
3
+ #------------------------------------------------------------------------------
4
+ class MailchimpNewsletter < Newsletter
5
+
6
+ MAILCHIMP_ERRORS = { 200 => 'List_DoesNotExist',
7
+ 214 => 'List_AlreadySubscribed',
8
+ 232 => 'Email_NotExists'
9
+ }.freeze
10
+
11
+ validate :validate_list_id
12
+ after_create :update_list_stats
13
+
14
+ #------------------------------------------------------------------------------
15
+ def self.signup_information(token, options = {})
16
+ information = { newsletter: self.find_newsletter(token, options) }
17
+ if information[:newsletter]
18
+ information[:grouping] = information[:newsletter].groupings[0]
19
+ if options[:current_user]
20
+ subscriber = MailchimpNewsletterSubscriber.subscriber_info(information[:newsletter], options[:current_user].email)
21
+ information[:subscriber] = (subscriber && subscriber.subscribed? ? subscriber : nil)
22
+ end
23
+ end
24
+ return information
25
+ end
26
+
27
+ # Make sure list id is specified, and it's valid with MailChimp
28
+ #------------------------------------------------------------------------------
29
+ def validate_list_id
30
+ unless self.mc_id.blank?
31
+ api = MailchimpNewsletter.api
32
+ list_info = api.lists.list(filters: {list_id: self.mc_id, exact: true})
33
+ if !list_info['errors'].empty?
34
+ errors[:mc_id] << (list_info['errors'][0]['error'])
35
+ end
36
+ else
37
+ errors[:mc_id] << ("list id must be provided")
38
+ end
39
+ end
40
+
41
+ # Retrieve list of groupings from Mailchimp
42
+ #------------------------------------------------------------------------------
43
+ def groupings
44
+ begin
45
+ api = MailchimpNewsletter.api
46
+ groupings = api.lists.interest_groupings(id: self.mc_id)
47
+ rescue Gibbon::MailChimpError => exception
48
+ #--- groupings are not enabled for this list
49
+ return []
50
+ end
51
+ end
52
+
53
+ # subscribe user or email to the newsletter
54
+ # Setting the Accept-Language will cause MC to send the confirmation in the users
55
+ # language if the list auto-translate is turned on
56
+ #------------------------------------------------------------------------------
57
+ def subscribe(user_or_email, options = {FNAME: '', LNAME: ''})
58
+ return { success: false, code: 232 } if user_or_email.blank?
59
+ api = MailchimpNewsletter.api
60
+ headers = {'Accept-Language' => I18n.locale.to_s}
61
+
62
+ #--- update data if user logged in. Don't for an unprotected subscribe. but honor value if passed in
63
+ options.reverse_merge! update_existing: user_or_email.is_a?(User)
64
+
65
+ #--- remove any invalid merge vars or other options
66
+ merge_vars = options.except('new-email', :email, :optin_ip, :optin_time, :mc_location, :mc_notes,
67
+ :update_existing, :mc_language, :headers)
68
+
69
+ #--- groupings needs to be an Array, but the form usually sends it as a Hash
70
+ merge_vars['GROUPINGS'] = [merge_vars['GROUPINGS']] if merge_vars['GROUPINGS'] && !merge_vars['GROUPINGS'].is_a?(Array)
71
+
72
+ if user_or_email.is_a?(User)
73
+ email = {email: user_or_email.email}
74
+ merge_vars[:FNAME] = user_or_email.first_name
75
+ merge_vars[:LNAME] = user_or_email.last_name
76
+ merge_vars[:COUNTRY] = user_or_email.country.english_name if user_or_email.country
77
+ else
78
+ email = {email: user_or_email}
79
+ end
80
+ merge_vars[:SPAMAPI] = 1
81
+ merge_vars[:MC_LANGUAGE] = I18n.locale # set the language to the current locale they are using
82
+ api.lists.subscribe(id: self.mc_id, email: email, merge_vars: merge_vars,
83
+ double_optin: true, update_existing: options[:update_existing], replace_interests: true,
84
+ headers: headers)
85
+ return { success: true, code: 0 }
86
+ rescue Gibbon::MailChimpError => exception
87
+ Rails.logger.info "=== Error Subscribing #{email} : #{exception.to_s}"
88
+ return { success: false, code: exception.code }
89
+ end
90
+
91
+ # unsubscribe email from the newsletter
92
+ #------------------------------------------------------------------------------
93
+ def unsubscribe(email)
94
+ return false if email.blank?
95
+
96
+ api = MailchimpNewsletter.api
97
+ api.lists.unsubscribe(id: self.mc_id, email: {email: email}, delete_member: false, send_goodbye: true, send_notify: true)
98
+ return true
99
+ rescue Gibbon::MailChimpError => exception
100
+ Rails.logger.info "=== Error Unsubscribing #{email} : #{exception.to_s}"
101
+ return false
102
+ end
103
+
104
+ #------------------------------------------------------------------------------
105
+ def update_list_stats
106
+ api = MailchimpNewsletter.api
107
+ list_info = api.lists.list(filters: {list_id: self.mc_id, exact: true})
108
+
109
+ #--- update the newsletter
110
+ if list_info['errors'].empty?
111
+ self.update_attributes(
112
+ name: list_info['data'][0]['name'],
113
+ subscribed_count: list_info['data'][0]['stats']['member_count'],
114
+ unsubscribed_count: list_info['data'][0]['stats']['unsubscribe_count'],
115
+ cleaned_count: list_info['data'][0]['stats']['cleaned_count'],
116
+ created_at: list_info['data'][0]['date_created'])
117
+ else
118
+ #--- looks like the list was deleted at MailChimp, mark as deleted
119
+ self.update_attribute(:deleted, true)
120
+ end
121
+ end
122
+
123
+ # get a list of sent campaigns. Can specify :folder_id to get only those
124
+ # in a specfic folder
125
+ #------------------------------------------------------------------------------
126
+ def sent_campaign_list(options = {start: 0, limit: 100})
127
+ api = MailchimpNewsletter.api
128
+ list_params = {sort_field: 'send_time', sort_dir: 'DESC',
129
+ filters: {list_id: self.mc_id}}
130
+ list_params[:start] = options[:start] ? options[:start] : 0
131
+ list_params[:limit] = options[:limit] ? options[:limit] : 100
132
+ list_params[:filters][:folder_id] = options[:folder_id] if options[:folder_id]
133
+
134
+ campaigns = api.campaigns.list(list_params)
135
+ end
136
+
137
+ # Get simplified list of sent campaigns. Useful for showing archived
138
+ # campaigns on the front end
139
+ #------------------------------------------------------------------------------
140
+ def sent_campaign_list_simple(options = {start: 0, limit: 100})
141
+ list = sent_campaign_list(options)
142
+ campaigns = list['data'].map {|item| {subject: item['subject'],
143
+ sent_on: item['send_time'].to_datetime,
144
+ archive_url: item['archive_url']} }
145
+ end
146
+
147
+ #------------------------------------------------------------------------------
148
+ def folder_list(type = 'campaign')
149
+ api = MailchimpNewsletter.api
150
+ folder_list = api.folders.list(type: type)
151
+ folder_list.sort! {|x, y| x['name'] <=> y['name']}
152
+ end
153
+
154
+ #------------------------------------------------------------------------------
155
+ def map_error_to_msg(code)
156
+ return MAILCHIMP_ERRORS[code] ? "nms.#{MAILCHIMP_ERRORS[code]}" : 'nms.mc_unknown_error'
157
+ end
158
+
159
+ # Grab an API object to work with
160
+ #------------------------------------------------------------------------------
161
+ def self.api
162
+ if !Account.current.preferred_nms_api_key.blank?
163
+ Gibbon::API.new(Account.current.preferred_nms_api_key)
164
+ else
165
+ nil
166
+ end
167
+ end
168
+
169
+ # is the email already subscribed to this list
170
+ #------------------------------------------------------------------------------
171
+ def email_subscribed?(email)
172
+ subscriber = MailchimpNewsletterSubscriber.subscriber_info(self, email)
173
+ return subscriber.present? && subscriber.subscribed?
174
+ end
175
+
176
+ # Query the lists in MailChimp, and create / update what we have in the database.
177
+ # If a list no longer exists, we will delete it and any attached information
178
+ # Note: [todo] This function is currently not used. Still determining if it's needed.
179
+ # Keep here until final decision.
180
+ #------------------------------------------------------------------------------
181
+ # def self.synchronize
182
+ # newsletters = MailchimpNewsletter.all
183
+ # api = MailchimpNewsletter.api
184
+ # lists = api.lists.list
185
+ #
186
+ # unless lists['data'].nil?
187
+ # lists['data'].each do |list|
188
+ # index = newsletters.find_index { |item| item.mc_id == list['id'] }
189
+ # if index
190
+ # #--- update the newsletter
191
+ # newsletters[index].update_attributes(
192
+ # name: list['name'],
193
+ # subscribed_count: list['stats']['member_count'],
194
+ # unsubscribed_count: list['stats']['unsubscribe_count'],
195
+ # cleaned_count: list['stats']['cleaned_count'])
196
+ # newsletters.delete_at(index)
197
+ # else
198
+ # #--- create a new newsletter
199
+ # MailchimpNewsletter.create!(
200
+ # name: list['name'],
201
+ # mc_id: list['id'],
202
+ # created_at: list['date_created'])
203
+ # end
204
+ # end
205
+ #
206
+ # #--- if any left, then mark them as deleted
207
+ # newsletters.each { |old_list| old_list.update_attribute(:deleted, true) }
208
+ #
209
+ # #--- update the lists_synced_on time
210
+ # account = Account.current
211
+ # account.preferred_nms_lists_synced_on = Time.now
212
+ # account.save
213
+ # end
214
+ # rescue Gibbon::MailChimpError
215
+ # end
216
+
217
+ end