spree_mail_chimp 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. data/LICENSE +20 -0
  2. data/README.textile +71 -0
  3. data/app/assets/javascripts/store/jquery.simplemodal.1.4.min.js +24 -0
  4. data/app/assets/javascripts/store/mailchimp_subscribe.js +46 -0
  5. data/app/assets/javascripts/store/spree_mail_chimp.js +1 -0
  6. data/app/assets/stylesheets/store/mail_chimp.css +21 -0
  7. data/app/assets/stylesheets/store/spree_mail_chimp.css +4 -0
  8. data/app/controllers/spree/admin/mail_chimp_settings_controller.rb +21 -0
  9. data/app/controllers/spree/subscriptions_controller.rb +39 -0
  10. data/app/models/app_configuration_decorator.rb +10 -0
  11. data/app/models/user_decorator.rb +121 -0
  12. data/app/overrides/add_mail_chimp_admin_menu_link.rb +4 -0
  13. data/app/overrides/add_newsletter_checkbox_to_create_account_page.rb +4 -0
  14. data/app/overrides/add_newsletter_subscribe_form_to_footer.rb +4 -0
  15. data/app/views/spree/admin/configurations/_spree_mail_chimp_configuration_link.html.erb +4 -0
  16. data/app/views/spree/admin/mail_chimp_settings/edit.html.erb +65 -0
  17. data/app/views/spree/admin/mail_chimp_settings/show.html.erb +26 -0
  18. data/app/views/spree/shared/_newsletter_subscribe_form.html.erb +14 -0
  19. data/app/views/spree/shared/_newsletter_subscribe_form_wo_butt.html.erb +5 -0
  20. data/app/views/spree/subscriptions/create.js.erb +20 -0
  21. data/app/views/spree/users/_subscribe_to_newsletter_field.html.erb +4 -0
  22. data/lib/generators/spree_mail_chimp/install/install_generator.rb +27 -0
  23. data/lib/spree/mail-chimp/config.rb +10 -0
  24. data/lib/spree_mail_chimp.rb +3 -0
  25. data/lib/spree_mail_chimp/engine.rb +15 -0
  26. data/lib/tasks/mail_chimp.rake +72 -0
  27. metadata +93 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Sam Beam
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.textile ADDED
@@ -0,0 +1,71 @@
1
+ h1. Spree + Mail Chimp
2
+
3
+ MailChimp API integration for your Spree store, using the hominid gem.
4
+
5
+ Adds a checkbox to the user signup form to allow customer to opt-in to your Mailchimp mailing list. Mailchimp API calls happen via the hominid gem.
6
+
7
+ Mailchimp subscription status is tracked with a boolean flag on the users table, we also fetch and store the unique MC record id when the subscription is created. This allows us to modify existing email addresses with minimal fuss.
8
+
9
+ This is a very simple extension as of now, *any ideas suggestions or improvements welcome!*
10
+
11
+ h2. Subscription form partial with JS
12
+
13
+ Also includes a partial at <code>shared/newsletter_subscribe_form</code> that can be included in your footer or sidebar anywhere on the site, has one field for the email address. jQuery code in <code>public/javascripts/mailchimp_subscribe.js</code> will POST to the SubscriptionsController which will relay to Mailchimp.
14
+
15
+ The SimpleModal plugin is used in the EJB to pop up a confirmation or error alert (because you won't be satisfied with a simple alert() box)
16
+
17
+ h3. Installation
18
+
19
+ Add to project's Gemfile:
20
+ <pre><code>
21
+ gem 'mail_chimp', '>=1.3', :git => 'git://github.com/sbeam/spree-mail-chimp.git'
22
+ </code> </pre>
23
+
24
+ Run from project's root:
25
+ <pre><code>
26
+ bundle install
27
+
28
+ rails g spree_mail_chimp:install
29
+ </code> </pre>
30
+
31
+ h3. Configuration
32
+
33
+ See the Mail Chimp Settings forms in the Admin under Configuration.
34
+
35
+ Note, using Mail Chimp "Merge Vars", you can have any callable methods on User sent
36
+ to the MC list, mapped to indentically named (but uppercased) fields in the MC list admin console.
37
+ Unfortunately these are only sent at the time the subscription is created, so there isn't much available.
38
+
39
+ h3. Changes
40
+
41
+ Version 3.0.0 - released 19 Apr 2012 - Spree 1.0 compat
42
+ Version 2.0.0 - released 6 Dec 2010 - Spree 0.30-stable
43
+ Version 1.0.1 - released 21 Nov 2010 - Spree 0.11-stable
44
+
45
+ h3. Requirements
46
+
47
+ Spree >= 1.0
48
+ hominid >= 2.2.0 http://rubygems.org/gems/hominid
49
+
50
+ Also uses the jQuery SimpleModal plugin, included.
51
+
52
+ h3. Credits
53
+
54
+ Authored by Sam Beam @sbeam
55
+ Migrated to Spree 0.30 by Bastian Schnurle @bzt
56
+ Migrated to Spree 1.0 (and extensively refactored) by James Kong @jkongie
57
+
58
+ Inspired originally by https://github.com/softa/mailee_spree
59
+
60
+ includes SimpleModal http://www.ericmmartin.com/projects/simplemodal/
61
+
62
+ h3. TODO
63
+
64
+ * make subscribe_to_newsletter_field work
65
+ * Send user address info to MC via merge vars, when address is given
66
+ * Export new orders to Mailchimp for full CRM gnarliness
67
+ * Utility to export existing users to Mailchimp
68
+ * Add admin controller to view lists and subscriptions
69
+ * Allow for multiple lists
70
+ * Tests :/
71
+
@@ -0,0 +1,24 @@
1
+ /*
2
+ * SimpleModal 1.4 - jQuery Plugin
3
+ * http://www.ericmmartin.com/projects/simplemodal/
4
+ * Copyright (c) 2010 Eric Martin (http://twitter.com/ericmmartin)
5
+ * Dual licensed under the MIT and GPL licenses
6
+ * Revision: $Id: jquery.simplemodal.js 256 2010-07-27 23:05:43Z emartin24 $
7
+ */
8
+ (function(d){var k=d.browser.msie&&parseInt(d.browser.version)===6&&typeof window.XMLHttpRequest!=="object",l=null,f=[];d.modal=function(a,b){return d.modal.impl.init(a,b)};d.modal.close=function(){d.modal.impl.close()};d.modal.focus=function(a){d.modal.impl.focus(a)};d.modal.setContainerDimensions=function(){d.modal.impl.setContainerDimensions()};d.modal.setPosition=function(){d.modal.impl.setPosition()};d.modal.update=function(a,b){d.modal.impl.update(a,b)};d.fn.modal=function(a){return d.modal.impl.init(this,
9
+ a)};d.modal.defaults={appendTo:"body",focus:true,opacity:50,overlayId:"simplemodal-overlay",overlayCss:{},containerId:"simplemodal-container",containerCss:{},dataId:"simplemodal-data",dataCss:{},minHeight:null,minWidth:null,maxHeight:null,maxWidth:null,autoResize:false,autoPosition:true,zIndex:1E3,close:true,closeHTML:'<a class="modalCloseImg" title="Close"></a>',closeClass:"simplemodal-close",escClose:true,overlayClose:false,position:null,persist:false,modal:true,onOpen:null,onShow:null,onClose:null};
10
+ d.modal.impl={d:{},init:function(a,b){var c=this;if(c.d.data)return false;l=d.browser.msie&&!d.boxModel;c.o=d.extend({},d.modal.defaults,b);c.zIndex=c.o.zIndex;c.occb=false;if(typeof a==="object"){a=a instanceof jQuery?a:d(a);c.d.placeholder=false;if(a.parent().parent().size()>0){a.before(d("<span></span>").attr("id","simplemodal-placeholder").css({display:"none"}));c.d.placeholder=true;c.display=a.css("display");if(!c.o.persist)c.d.orig=a.clone(true)}}else if(typeof a==="string"||typeof a==="number")a=
11
+ d("<div></div>").html(a);else{alert("SimpleModal Error: Unsupported data type: "+typeof a);return c}c.create(a);c.open();d.isFunction(c.o.onShow)&&c.o.onShow.apply(c,[c.d]);return c},create:function(a){var b=this;f=b.getDimensions();if(b.o.modal&&k)b.d.iframe=d('<iframe src="javascript:false;"></iframe>').css(d.extend(b.o.iframeCss,{display:"none",opacity:0,position:"fixed",height:f[0],width:f[1],zIndex:b.o.zIndex,top:0,left:0})).appendTo(b.o.appendTo);b.d.overlay=d("<div></div>").attr("id",b.o.overlayId).addClass("simplemodal-overlay").css(d.extend(b.o.overlayCss,
12
+ {display:"none",opacity:b.o.opacity/100,height:b.o.modal?f[0]:0,width:b.o.modal?f[1]:0,position:"fixed",left:0,top:0,zIndex:b.o.zIndex+1})).appendTo(b.o.appendTo);b.d.container=d("<div></div>").attr("id",b.o.containerId).addClass("simplemodal-container").css(d.extend(b.o.containerCss,{display:"none",position:"fixed",zIndex:b.o.zIndex+2})).append(b.o.close&&b.o.closeHTML?d(b.o.closeHTML).addClass(b.o.closeClass):"").appendTo(b.o.appendTo);b.d.wrap=d("<div></div>").attr("tabIndex",-1).addClass("simplemodal-wrap").css({height:"100%",
13
+ outline:0,width:"100%"}).appendTo(b.d.container);b.d.data=a.attr("id",a.attr("id")||b.o.dataId).addClass("simplemodal-data").css(d.extend(b.o.dataCss,{display:"none"})).appendTo("body");b.setContainerDimensions();b.d.data.appendTo(b.d.wrap);if(k||l)b.fixIE()},bindEvents:function(){var a=this;d("."+a.o.closeClass).bind("click.simplemodal",function(b){b.preventDefault();a.close()});a.o.modal&&a.o.close&&a.o.overlayClose&&a.d.overlay.bind("click.simplemodal",function(b){b.preventDefault();a.close()});
14
+ d(document).bind("keydown.simplemodal",function(b){if(a.o.modal&&b.keyCode===9)a.watchTab(b);else if(a.o.close&&a.o.escClose&&b.keyCode===27){b.preventDefault();a.close()}});d(window).bind("resize.simplemodal",function(){f=a.getDimensions();a.o.autoResize?a.setContainerDimensions():a.o.autoPosition&&a.setPosition();if(k||l)a.fixIE();else if(a.o.modal){a.d.iframe&&a.d.iframe.css({height:f[0],width:f[1]});a.d.overlay.css({height:f[0],width:f[1]})}})},unbindEvents:function(){d("."+this.o.closeClass).unbind("click.simplemodal");
15
+ d(document).unbind("keydown.simplemodal");d(window).unbind("resize.simplemodal");this.d.overlay.unbind("click.simplemodal")},fixIE:function(){var a=this,b=a.o.position;d.each([a.d.iframe||null,!a.o.modal?null:a.d.overlay,a.d.container],function(c,h){if(h){var g=h[0].style;g.position="absolute";if(c<2){g.removeExpression("height");g.removeExpression("width");g.setExpression("height",'document.body.scrollHeight > document.body.clientHeight ? document.body.scrollHeight : document.body.clientHeight + "px"');
16
+ g.setExpression("width",'document.body.scrollWidth > document.body.clientWidth ? document.body.scrollWidth : document.body.clientWidth + "px"')}else{var e;if(b&&b.constructor===Array){c=b[0]?typeof b[0]==="number"?b[0].toString():b[0].replace(/px/,""):h.css("top").replace(/px/,"");c=c.indexOf("%")===-1?c+' + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"':parseInt(c.replace(/%/,""))+' * ((document.documentElement.clientHeight || document.body.clientHeight) / 100) + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"';
17
+ if(b[1]){e=typeof b[1]==="number"?b[1].toString():b[1].replace(/px/,"");e=e.indexOf("%")===-1?e+' + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"':parseInt(e.replace(/%/,""))+' * ((document.documentElement.clientWidth || document.body.clientWidth) / 100) + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"'}}else{c='(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"';
18
+ e='(document.documentElement.clientWidth || document.body.clientWidth) / 2 - (this.offsetWidth / 2) + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"'}g.removeExpression("top");g.removeExpression("left");g.setExpression("top",c);g.setExpression("left",e)}}})},focus:function(a){var b=this;a=a&&d.inArray(a,["first","last"])!==-1?a:"first";var c=d(":input:enabled:visible:"+a,b.d.wrap);setTimeout(function(){c.length>0?c.focus():b.d.wrap.focus()},
19
+ 10)},getDimensions:function(){var a=d(window);return[d.browser.opera&&d.browser.version>"9.5"&&d.fn.jquery<"1.3"||d.browser.opera&&d.browser.version<"9.5"&&d.fn.jquery>"1.2.6"?a[0].innerHeight:a.height(),a.width()]},getVal:function(a,b){return a?typeof a==="number"?a:a==="auto"?0:a.indexOf("%")>0?parseInt(a.replace(/%/,""))/100*(b==="h"?f[0]:f[1]):parseInt(a.replace(/px/,"")):null},update:function(a,b){var c=this;if(!c.d.data)return false;c.d.origHeight=c.getVal(a,"h");c.d.origWidth=c.getVal(b,"w");
20
+ c.d.data.hide();a&&c.d.container.css("height",a);b&&c.d.container.css("width",b);c.setContainerDimensions();c.d.data.show();c.o.focus&&c.focus();c.unbindEvents();c.bindEvents()},setContainerDimensions:function(){var a=this,b=a.d.origHeight?a.d.origHeight:d.browser.opera?a.d.container.height():a.getVal(a.d.container.css("height"),"h"),c=a.d.origWidth?a.d.origWidth:d.browser.opera?a.d.container.width():a.getVal(a.d.container.css("width"),"w"),h=a.d.data.outerHeight(true),g=a.d.data.outerWidth(true);
21
+ a.d.origHeight=a.d.origHeight||b;a.d.origWidth=a.d.origWidth||c;var e=a.o.maxHeight?a.getVal(a.o.maxHeight,"h"):null,i=a.o.maxWidth?a.getVal(a.o.maxWidth,"w"):null;e=e&&e<f[0]?e:f[0];i=i&&i<f[1]?i:f[1];var j=a.o.minHeight?a.getVal(a.o.minHeight,"h"):"auto";b=b?a.o.autoResize&&b>e?e:b:h?h>e?e:a.o.minHeight&&j!=="auto"&&h<j?j:h:j;e=a.o.minWidth?a.getVal(a.o.minWidth,"w"):"auto";c=c?a.o.autoResize&&c>i?i:c:g?g>i?i:a.o.minWidth&&e!=="auto"&&g<e?e:g:e;a.d.container.css({height:b,width:c});a.d.wrap.css({overflow:h>
22
+ b||g>c?"auto":"visible"});a.o.autoPosition&&a.setPosition()},setPosition:function(){var a=this,b,c;b=f[0]/2-a.d.container.outerHeight(true)/2;c=f[1]/2-a.d.container.outerWidth(true)/2;if(a.o.position&&Object.prototype.toString.call(a.o.position)==="[object Array]"){b=a.o.position[0]||b;c=a.o.position[1]||c}else{b=b;c=c}a.d.container.css({left:c,top:b})},watchTab:function(a){var b=this;if(d(a.target).parents(".simplemodal-container").length>0){b.inputs=d(":input:enabled:visible:first, :input:enabled:visible:last",
23
+ b.d.data[0]);if(!a.shiftKey&&a.target===b.inputs[b.inputs.length-1]||a.shiftKey&&a.target===b.inputs[0]||b.inputs.length===0){a.preventDefault();b.focus(a.shiftKey?"last":"first")}}else{a.preventDefault();b.focus()}},open:function(){var a=this;a.d.iframe&&a.d.iframe.show();if(d.isFunction(a.o.onOpen))a.o.onOpen.apply(a,[a.d]);else{a.d.overlay.show();a.d.container.show();a.d.data.show()}a.o.focus&&a.focus();a.bindEvents()},close:function(){var a=this;if(!a.d.data)return false;a.unbindEvents();if(d.isFunction(a.o.onClose)&&
24
+ !a.occb){a.occb=true;a.o.onClose.apply(a,[a.d])}else{if(a.d.placeholder){var b=d("#simplemodal-placeholder");if(a.o.persist)b.replaceWith(a.d.data.removeClass("simplemodal-data").css("display",a.display));else{a.d.data.hide().remove();b.replaceWith(a.d.orig)}}else a.d.data.hide().remove();a.d.container.hide().remove();a.d.overlay.hide();a.d.iframe&&a.d.iframe.hide().remove();setTimeout(function(){a.d.overlay.remove();a.d={}},10)}}}})(jQuery);
@@ -0,0 +1,46 @@
1
+
2
+ var subscribe_email_default_txt = '';
3
+
4
+ jQuery.ajaxSetup({
5
+ 'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
6
+ });
7
+
8
+ var SpreeMailchimpApp = {
9
+
10
+ doSubmit: function(e) {
11
+ SpreeMailchimpApp.getBusy(null); // could really use $.proxy here but spree doesn't have 1.4
12
+ $.post(this.action+'.js', $(this).serialize(), SpreeMailchimpApp.getNotBusy, "script");
13
+ return false;
14
+ },
15
+
16
+ getBusy : function( fn ) {
17
+ $("body").css('cursor', 'progress');
18
+ $("#busy_indicator").fadeIn('fast', fn);
19
+ },
20
+
21
+ getNotBusy : function() {
22
+ $("body").css('cursor', 'default');
23
+ $("#busy_indicator").fadeOut('fast');
24
+ }
25
+
26
+ };
27
+
28
+
29
+
30
+ jQuery(document).ready( function() {
31
+
32
+ subscribe_email_default_txt = $('#subscribe_email').val();
33
+
34
+ $('#subscribe_email').bind('focus', function() {
35
+ if (this.value == subscribe_email_default_txt) this.value = '';
36
+ });
37
+ $('#subscribe_email').bind('blur', function() {
38
+ if (this.value == '') this.value = subscribe_email_default_txt;
39
+ });
40
+
41
+ $('#mailchimp_subscribe_wrap form').bind('submit', SpreeMailchimpApp.doSubmit);
42
+
43
+ });
44
+
45
+
46
+
@@ -0,0 +1 @@
1
+ //= require store/spree_core
@@ -0,0 +1,21 @@
1
+ /* basic styles for simplemodal dialog to match Spree sample store */
2
+ .simplemodal-overlay {
3
+ background: #333;
4
+ }
5
+ .simplemodal-container {
6
+ border: 2px solid #162f54;
7
+ background: #fff;
8
+ width: 45%;
9
+ }
10
+ .simplemodal-close {
11
+ border-bottom: 1px solid #fff;
12
+ text-align: right;
13
+ background: #162f54;
14
+ color: #fff;
15
+ }
16
+ .simplemodal-container h1 {
17
+ font-size: 110%;
18
+ }
19
+ .simplemodal-data {
20
+ padding: 0.7em;
21
+ }
@@ -0,0 +1,4 @@
1
+ /*
2
+ *= require store/spree_core
3
+ *= require_tree .
4
+ */
@@ -0,0 +1,21 @@
1
+ module Spree
2
+
3
+ class Admin::MailChimpSettingsController < Admin::BaseController
4
+
5
+ def show
6
+ end
7
+
8
+ def update
9
+ Spree::Config.set(params[:preferences])
10
+
11
+ respond_to do |format|
12
+ format.html {
13
+ redirect_to admin_mail_chimp_settings_path
14
+ }
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+
@@ -0,0 +1,39 @@
1
+ class Spree::SubscriptionsController < Spree::BaseController
2
+
3
+ def hominid
4
+ @hominid ||= Hominid::Base.new({ :api_key => Spree::Config.get(:mailchimp_api_key) })
5
+ end
6
+
7
+ def create
8
+ @errors = []
9
+
10
+ if params[:email].blank?
11
+ @errors << t('missing_email')
12
+ elsif params[:email] !~ /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}/i
13
+ @errors << t('invalid_email_address')
14
+ else
15
+ begin
16
+ self.class.benchmark "Checking if address exists and/or is valid" do
17
+ @mc_member = hominid.member_info(Spree::Config.get(:mailchimp_list_id), params[:email])
18
+ end
19
+ rescue Hominid::ListError => e
20
+ end
21
+
22
+ if @mc_member
23
+ @errors << t('that_address_is_already_subscribed')
24
+ else
25
+ begin
26
+ self.class.benchmark "Adding mailchimp subscriber" do
27
+ hominid.subscribe(Spree::Config.get(:mailchimp_list_id), params[:email], {}, MailChimpSync::Sync::mc_subscription_opts)
28
+ end
29
+ rescue Hominid::ValidationError => e
30
+ @errors << t('invalid_email_address')
31
+ end
32
+ end
33
+ end
34
+
35
+ respond_to do |wants|
36
+ wants.js
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ Spree::AppConfiguration.class_eval do
2
+
3
+ preference :mailchimp_double_opt_in, :boolean, :default => false
4
+ preference :mailchimp_send_welcome, :boolean, :default => false
5
+ preference :mailchimp_send_notify, :boolean, :default => false
6
+ preference :mailchimp_merge_vars, :string, :default => ''
7
+ preference :mailchimp_list_id, :string
8
+ preference :mailchimp_api_key, :string
9
+
10
+ end
@@ -0,0 +1,121 @@
1
+ Spree::User.class_eval do
2
+
3
+ before_create :mailchimp_add_to_mailing_list
4
+ before_update :mailchimp_update_in_mailing_list, :if => :is_mail_list_subscriber_changed?
5
+
6
+ attr_accessible :is_mail_list_subscriber
7
+
8
+ private
9
+
10
+ # Subscribes a user to the mailing list
11
+ #
12
+ # Returns ?
13
+ def mailchimp_add_to_mailing_list
14
+ if self.is_mail_list_subscriber?
15
+ begin
16
+ hominid.list_subscribe(mailchimp_list_id, self.email, mailchimp_merge_vars, 'html', *mailchimp_subscription_opts)
17
+ logger.debug "Fetching new mailchimp subscriber info"
18
+
19
+ assign_mailchimp_subscriber_id if self.mailchimp_subscriber_id.blank?
20
+ rescue Hominid::APIError => e
21
+ logger.warn "SpreeMailChimp: Failed to create contact in Mailchimp: #{e.message}"
22
+ end
23
+ end
24
+ end
25
+
26
+ # Removes the User from the Mailchimp mailing list
27
+ #
28
+ # Returns ?
29
+ def mailchimp_remove_from_mailing_list
30
+ if !self.is_mail_list_subscriber? && self.mailchimp_subscriber_id.present?
31
+ begin
32
+ # TODO: Get rid of those magic values. Maybe add them as Spree::Config options?
33
+ hominid.list_unsubscribe(mailchimp_list_id, self.email, false, false, true)
34
+ logger.debug "Removing mailchimp subscriber"
35
+ rescue Hominid::APIError => e
36
+ logger.warn "SpreeMailChimp: Failed to remove contact from Mailchimp: #{e.message}"
37
+ end
38
+ end
39
+ end
40
+
41
+ # Updates Mailchimp
42
+ #
43
+ # Returns nothing
44
+ # TODO: Update the user's email address in Mailchimp if it changes.
45
+ # Look at listMemberUpdate
46
+ def mailchimp_update_in_mailing_list
47
+ if self.is_mail_list_subscriber?
48
+ mailchimp_add_to_mailing_list
49
+ elsif !self.is_mail_list_subscriber?
50
+ mailchimp_remove_from_mailing_list
51
+ end
52
+ end
53
+
54
+ # Retrieves and stores the Mailchimp member id
55
+ #
56
+ # Returns the Mailchimp ID
57
+ def assign_mailchimp_subscriber_id
58
+ begin
59
+ response = hominid.list_member_info(mailchimp_list_id, [self.email]).with_indifferent_access
60
+
61
+ if response[:success] == 1
62
+ member = response[:data][0]
63
+
64
+ self.mailchimp_subscriber_id = member[:id]
65
+ end
66
+ rescue Hominid::APIError => e
67
+ logger.warn "SpreeMailChimp: Failed to retrieve and store Mailchimp ID: #{e.message}"
68
+ end
69
+ end
70
+
71
+ # Creates an instance of the Hominid::API
72
+ #
73
+ # Returns Hominid::API
74
+ def hominid
75
+ @hominid ||= Hominid::API.new(Spree::Config.get(:mailchimp_api_key))
76
+ end
77
+
78
+ # Gets the Mailchimp list ID that is stored in Spree::Config
79
+ #
80
+ # Returns a Mailchimp list ID String
81
+ def mailchimp_list_id
82
+ @mailchimp_list_id ||= Spree::Config.get(:mailchimp_list_id)
83
+ end
84
+
85
+ # Generates the subsubcription options for the application
86
+ #
87
+ # The option values are returned as an Array in the following order:
88
+ #
89
+ # double_optin - Flag to control whether a double opt-in confirmation
90
+ # message is sent, defaults to true. Abusing this may
91
+ # cause your account to be suspended. (default: false)
92
+ # update_existing - Flag to control whether existing subscribers should be
93
+ # updated instead of throwing an error. (default: true)
94
+ # ( MailChimp's default is false )
95
+ # replace_interests - Flag to determine whether we replace the interest
96
+ # groups with the groups provided or we add the provided
97
+ # groups to the member's interest groups. (default: true)
98
+ # send_welcome - If the double_optin is false and this is true, a welcome
99
+ # email is sent out. If double_optin is true, this has no
100
+ # effect. (default: false)
101
+ #
102
+ # Returns an Array of subscription options
103
+ #
104
+ # TODO: Add configuration options for 'update_existing' and 'replace_interests'
105
+ # TODO: Remove configuration options for :mailchimp_send_notify
106
+ def mailchimp_subscription_opts
107
+ [Spree::Config.get(:mailchimp_double_opt_in), true, true, Spree::Config.get(:mailchimp_send_welcome)]
108
+ end
109
+
110
+ # Generates the merge variables for subscribing a user
111
+ def mailchimp_merge_vars
112
+ merge_vars = {}
113
+ if mailchimp_merge_user_attribs = Spree::Config.get(:mailchimp_merge_vars)
114
+ mailchimp_merge_user_attribs.split(',').each do |method|
115
+ merge_vars[method.upcase] = self.send(method.downcase) if @user.respond_to? method.downcase
116
+ end
117
+ end
118
+ merge_vars
119
+ end
120
+
121
+ end
@@ -0,0 +1,4 @@
1
+ Deface::Override.new(:virtual_path => 'spree/admin/configurations/index',
2
+ :name => 'add_mail_chimp_admin_menu_link',
3
+ :insert_bottom => "[data-hook='admin_configurations_menu']",
4
+ :partial => 'spree/admin/configurations/spree_mail_chimp_configuration_link' )
@@ -0,0 +1,4 @@
1
+ Deface::Override.new(:virtual_path => "spree/shared/_user_form",
2
+ :name => "add_newsletter_checkbox_to_create_account_page",
3
+ :insert_top => "[data-hook='signup_below_password_fields']",
4
+ :partial => "spree/users/subscribe_to_newsletter_field" )
@@ -0,0 +1,4 @@
1
+ Deface::Override.new(:virtual_path => 'spree/shared/_footer',
2
+ :name => 'add_newsletter_form_to_footer',
3
+ :insert_top => 'div#footer_left',
4
+ :partial => 'shared/newsletter_subscribe_form' )
@@ -0,0 +1,4 @@
1
+ <tr>
2
+ <td><%= link_to t("mailchimp_settings"), admin_mail_chimp_settings_path %></td>
3
+ <td><%= t('mailchimp_settings_description') %></td>
4
+ </tr>
@@ -0,0 +1,65 @@
1
+ <%= content_for :head do %>
2
+ <%= javascript_include_tag "jquery.validate.min" %>
3
+ <% end -%>
4
+
5
+ <%= render :partial => 'spree/admin/shared/configuration_menu' %>
6
+
7
+ <h1><%= t('mailchimp_settings') %></h1>
8
+
9
+ <%= form_tag(admin_mail_chimp_settings_path, :method => :put, :id => :mail_chimp_settings_form) do %>
10
+
11
+ <div class="yui-g">
12
+ <div class="yui-u first">
13
+ <fieldset>
14
+ <legend><%= t('mailchimp_keys') %></legend>
15
+ <p>
16
+ <label><%= t('mailchimp_api_key') %></label><br />
17
+ <%= text_field_tag('preferences[mailchimp_api_key]', Spree::Config[:mailchimp_api_key], :size => 46, :maxlength => 256, :class => 'email') %>
18
+ </p>
19
+ <p>
20
+ <label><%= t('mailchimp_list_id') %></label><br />
21
+ <%= text_field_tag('preferences[mailchimp_list_id]', Spree::Config[:mailchimp_list_id], :maxlength => 256, :class => 'email') %>
22
+ </p>
23
+ </fieldset>
24
+
25
+ <fieldset>
26
+ <legend><%= t('preferences') %></legend>
27
+ <p>
28
+ <label>
29
+ <input name="preferences[mailchimp_double_opt_in]" type="hidden" value="0" />
30
+ <%= check_box_tag('preferences[mailchimp_double_opt_in]', "1", Spree::Config[:mailchimp_double_opt_in]) %>&nbsp;<%= t('mailchimp_double_opt_in') %>&nbsp;
31
+ </label>
32
+ </p>
33
+ <p>
34
+ <label>
35
+ <input name="preferences[mailchimp_send_welcome]" type="hidden" value="0" />
36
+ <%= check_box_tag('preferences[mailchimp_send_welcome]', "1", Spree::Config[:mailchimp_send_welcome]) %>&nbsp;<%= t('mailchimp_send_welcome') %>&nbsp;
37
+ </label>
38
+ </p>
39
+ <p>
40
+ <label>
41
+ <input name="preferences[mailchimp_send_notify]" type="hidden" value="0" />
42
+ <%= check_box_tag('preferences[mailchimp_send_notify]', "1", Spree::Config[:mailchimp_send_notify]) %>&nbsp;<%= t('mailchimp_send_notify') %>&nbsp;
43
+ </label>
44
+ </p>
45
+ <p>
46
+ <label>
47
+ &nbsp;<%= t('mailchimp_merge_vars') %>&nbsp;<br /><%= text_field_tag('preferences[mailchimp_merge_vars]', Spree::Config[:mailchimp_merge_vars], :size => 44) %>
48
+ </label>
49
+ <br />
50
+ <span class="note"><%= t('mailchimp_merge_vars_help') %></span>
51
+ </p>
52
+ </fieldset>
53
+
54
+ </div>
55
+ </div>
56
+
57
+ <p class="form-buttons"><%= button t('save_preferences') %></p>
58
+
59
+ <script>
60
+ <%# TODO add validation for email list rather than simple email %>
61
+ $(document).ready(function(){
62
+ $("#inquiries_settings_form").validate();
63
+ });
64
+ </script>
65
+ <% end -%>
@@ -0,0 +1,26 @@
1
+ <%= render :partial => 'spree/admin/shared/configuration_menu' %>
2
+
3
+ <h1><%= t('mailchimp_settings') %></h1>
4
+
5
+ <table>
6
+ <tr>
7
+ <th scope="row"><%= t("mailchimp_api_key") %>:</th>
8
+ <td><%= Spree::Config[:mailchimp_api_key] %></td>
9
+ </tr>
10
+ <tr>
11
+ <th scope="row"><%= t("mailchimp_list_id") %>:</th>
12
+ <td><%= Spree::Config[:mailchimp_list_id] %></td>
13
+ </tr>
14
+ </table>
15
+
16
+ <h4><%= t("mailchimp_double_opt_in") %>: <%= Spree::Config[:mailchimp_double_opt_in]? t('yes') : t('no') %></h4>
17
+ <h4><%= t("mailchimp_send_welcome") %>: <%= Spree::Config[:mailchimp_send_welcome]? t('yes') : t('no') %></h4>
18
+ <h4><%= t("mailchimp_send_notify") %>: <%= Spree::Config[:mailchimp_send_notify]? t('yes') : t('no') %></h4>
19
+ <% unless Spree::Config[:mailchimp_merge_vars].blank? %>
20
+ <h4><%= t("mailchimp_merge_vars") %></h4>
21
+ <p>
22
+ <%= raw Spree::Config[:mailchimp_merge_vars].gsub(/,/, '<br >') %>
23
+ </p>
24
+ <% end %>
25
+
26
+ <p><%= link_to_with_icon 'edit', t("edit"), edit_admin_mail_chimp_settings_path %></p>
@@ -0,0 +1,14 @@
1
+ <div id="mailchimp_subscribe_wrap">
2
+ <%= form_for :user, :url => :subscriptions do |f| %>
3
+ <table>
4
+ <tr>
5
+ <td>
6
+ <%= text_field_tag :email, t('enter_your_email'), :class => 'email', :id => 'subscribe_email' %>
7
+ </td>
8
+ <td>
9
+ <%= submit_tag t('subscribe'), :id => 'op_subscribe' %>
10
+ </td>
11
+ </tr>
12
+ </table>
13
+ <% end -%>
14
+ </div>
@@ -0,0 +1,5 @@
1
+ <div id="mailchimp_subscribe_wrap">
2
+ <%= form_for :user, :url => :subscriptions do |f| %>
3
+ <%= text_field_tag :email, t('enter_your_email'), :class=>'email', :id=>'subscribe_email' %>
4
+ <% end -%>
5
+ </div>
@@ -0,0 +1,20 @@
1
+ <% flash[:notice] = nil %>
2
+
3
+ var modal_opts = { closeHTML : '<div>[x] CLOSE</div>',
4
+
5
+ containerCss : { height:80 },
6
+
7
+ onOpen: function (dialog) {
8
+ dialog.overlay.fadeIn('fast', function() {
9
+ dialog.data.show();
10
+ dialog.container.fadeIn('fast');
11
+ });
12
+ }
13
+ };
14
+
15
+ <% if @errors.empty? %>
16
+ $.modal('<div id="modal-notice"><h1><%= t("you_have_been_subscribed") %></h1></div>', modal_opts);
17
+ <% else %>
18
+ $.modal('<div id="modal-notice"><h1>ERROR:</h1> <%= @errors.join(' / ') %></div>', modal_opts);
19
+ <% end %>
20
+
@@ -0,0 +1,4 @@
1
+ <p class="login_newsletter_subscribe">
2
+ <%= check_box :user, :is_mail_list_subscriber, :class => 'title' %>
3
+ <%= label :user, :is_mail_list_subscriber, t(:do_subscribe_to_our_mailing_list) %>
4
+ </p>
@@ -0,0 +1,27 @@
1
+ module SpreeMailChimp
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ def add_javascripts
6
+ append_file "app/assets/javascripts/store/all.js", "//= require store/spree_mail_chimp\n"
7
+ end
8
+
9
+ def add_stylesheets
10
+ inject_into_file "app/assets/stylesheets/store/all.css", " *= require store/spree_mail_chimp\n", :before => /\*\//, :verbose => true
11
+ end
12
+
13
+ def add_migrations
14
+ run 'rake railties:install:migrations FROM=spree_mail_chimp'
15
+ end
16
+
17
+ def run_migrations
18
+ res = ask "Would you like to run the migrations now? [Y/n]"
19
+ if res == "" || res.downcase == "y"
20
+ run 'rake db:migrate'
21
+ else
22
+ puts "Skiping rake db:migrate, don't forget to run it!"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,10 @@
1
+ module Spree::MailChimp
2
+ class Config < Spree::Config
3
+ class << self
4
+ def instance
5
+ return nil unless ActiveRecord::Base.connection.tables.include?('configurations')
6
+ MailChimpConfiguration.find_or_create_by_name("MailChimp configuration")
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ require 'spree_core'
2
+ require 'spree_mail_chimp/engine'
3
+ require 'hominid'
@@ -0,0 +1,15 @@
1
+ module SpreeMailChimp
2
+ class Engine < Rails::Engine
3
+ engine_name 'spree_mail_chimp'
4
+
5
+ config.autoload_paths += %W(#{config.root}/lib)
6
+
7
+ def self.activate
8
+ Dir.glob(File.join(File.dirname(__FILE__), "../../app/**/*_decorator*.rb")) do |c|
9
+ Rails.application.config.cache_classes ? require(c) : load(c)
10
+ end
11
+ end
12
+
13
+ config.to_prepare &method(:activate).to_proc
14
+ end
15
+ end
@@ -0,0 +1,72 @@
1
+ require 'fileutils'
2
+ include FileUtils::Verbose
3
+
4
+ namespace :mail_chimp do
5
+ namespace :db do
6
+ desc "Custom migrations"
7
+ task :migrate do
8
+ require 'erb'
9
+ require 'logger'
10
+ require 'active_record'
11
+ reference = YAML::load(ERB.new(IO.read("#{Rails.root}/config/database.yml")).result)
12
+ env = RAILS_ENV = ENV['RAILS_ENV'] || 'development'
13
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
14
+ ActiveRecord::Base.logger.level = Logger::WARN
15
+ ActiveRecord::Base.configurations = reference.dup
16
+ reference.each_key do |name|
17
+ next unless name.include? env
18
+ puts "Migrating #{name}"
19
+ ActiveRecord::Base.clear_active_connections!
20
+ ActiveRecord::Base.configurations[env] = reference[name]
21
+ ActiveRecord::Base.establish_connection RAILS_ENV
22
+ ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
23
+ ActiveRecord::Migrator.migrate("#{File.expand_path('../..', File.dirname(__FILE__))}/db/migrate", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
24
+ Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
25
+ end
26
+ end
27
+
28
+ desc "Loads a specified fixture using rake db:load_file[filename.rb]"
29
+ task :seed do
30
+ Rake::Task["mail_chimp:db:load_dir"].invoke("default")
31
+ end
32
+
33
+ desc "Loads a specified fixture using rake db:load_file[filename.rb]"
34
+ task :load_file, [:file] => :environment do |t, args|
35
+ file = args.file
36
+ ext = File.extname file
37
+ if ext == ".csv" or ext == ".yml"
38
+ puts "loading fixture " + file
39
+ Fixtures.create_fixtures(File.dirname(file), File.basename(file, '.*'))
40
+ else
41
+ if File.exists? file
42
+ puts "loading ruby " + file
43
+ require file
44
+ end
45
+ end
46
+ end
47
+
48
+ desc "Loads fixtures from the the dir you specify using rake db:load_dir[loadfrom]"
49
+ task :load_dir, [:dir] => :environment do |t, args|
50
+ dir = args.dir
51
+ fixtures = ActiveSupport::OrderedHash.new
52
+ ruby_files = ActiveSupport::OrderedHash.new
53
+ Dir.glob(File.join(File.expand_path('../..', File.dirname(__FILE__)), "db", dir, '*.{yml,csv,rb}')).each do |fixture_file|
54
+ ext = File.extname fixture_file
55
+ if ext == ".rb"
56
+ ruby_files[File.basename(fixture_file, '.*')] = fixture_file
57
+ else
58
+ fixtures[File.basename(fixture_file, '.*')] = fixture_file
59
+ end
60
+ end
61
+ fixtures.sort.each do |fixture, fixture_file|
62
+ # an invoke will only execute the task once
63
+ Rake::Task["db:load_file"].execute(Rake::TaskArguments.new([:file], [fixture_file]))
64
+ end
65
+ ruby_files.sort.each do |fixture, ruby_file|
66
+ # an invoke will only execute the task once
67
+ Rake::Task["db:load_file"].execute(Rake::TaskArguments.new([:file], [ruby_file]))
68
+ end
69
+ end
70
+ end
71
+ end
72
+
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spree_mail_chimp
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sam Beam
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: spree_core
16
+ requirement: &17066680 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *17066680
25
+ - !ruby/object:Gem::Dependency
26
+ name: hominid
27
+ requirement: &17064580 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *17064580
36
+ description:
37
+ email: sbeam@onsetcorps.net
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - README.textile
43
+ - LICENSE
44
+ - lib/spree_mail_chimp/engine.rb
45
+ - lib/spree_mail_chimp.rb
46
+ - lib/generators/spree_mail_chimp/install/install_generator.rb
47
+ - lib/tasks/mail_chimp.rake
48
+ - lib/spree/mail-chimp/config.rb
49
+ - app/controllers/spree/subscriptions_controller.rb
50
+ - app/controllers/spree/admin/mail_chimp_settings_controller.rb
51
+ - app/views/spree/users/_subscribe_to_newsletter_field.html.erb
52
+ - app/views/spree/shared/_newsletter_subscribe_form_wo_butt.html.erb
53
+ - app/views/spree/shared/_newsletter_subscribe_form.html.erb
54
+ - app/views/spree/subscriptions/create.js.erb
55
+ - app/views/spree/admin/configurations/_spree_mail_chimp_configuration_link.html.erb
56
+ - app/views/spree/admin/mail_chimp_settings/edit.html.erb
57
+ - app/views/spree/admin/mail_chimp_settings/show.html.erb
58
+ - app/overrides/add_newsletter_subscribe_form_to_footer.rb
59
+ - app/overrides/add_newsletter_checkbox_to_create_account_page.rb
60
+ - app/overrides/add_mail_chimp_admin_menu_link.rb
61
+ - app/assets/stylesheets/store/mail_chimp.css
62
+ - app/assets/stylesheets/store/spree_mail_chimp.css
63
+ - app/assets/javascripts/store/mailchimp_subscribe.js
64
+ - app/assets/javascripts/store/jquery.simplemodal.1.4.min.js
65
+ - app/assets/javascripts/store/spree_mail_chimp.js
66
+ - app/models/user_decorator.rb
67
+ - app/models/app_configuration_decorator.rb
68
+ homepage: https://github.com/sbeam/spree-mail-chimp
69
+ licenses: []
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: 1.8.7
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements:
87
+ - none
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.11
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Mail Chimp subscriptions for your Spree store using hominid
93
+ test_files: []