spree_mail 0.40.0.1

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 (41) hide show
  1. data/README.md +19 -0
  2. data/Rakefile +94 -0
  3. data/app/controllers/admin/emails_controller.rb +61 -0
  4. data/app/controllers/admin/subscribers_controller.rb +78 -0
  5. data/app/controllers/emails_controller.rb +19 -0
  6. data/app/controllers/subscribers_controller.rb +42 -0
  7. data/app/mailers/email_mailer.rb +24 -0
  8. data/app/model/email.rb +77 -0
  9. data/app/model/subscriber.rb +39 -0
  10. data/app/views/admin/emails/_form.html.erb +78 -0
  11. data/app/views/admin/emails/edit.html.erb +15 -0
  12. data/app/views/admin/emails/index.html.erb +64 -0
  13. data/app/views/admin/emails/new.html.erb +15 -0
  14. data/app/views/admin/emails/show.html.erb +20 -0
  15. data/app/views/admin/hooks/_subscribers_tab.html.erb +1 -0
  16. data/app/views/admin/shared/_spree_mail_sub_nav.html.erb +8 -0
  17. data/app/views/admin/subscribers/_form.html.erb +19 -0
  18. data/app/views/admin/subscribers/_options.html.erb +5 -0
  19. data/app/views/admin/subscribers/edit.html.erb +15 -0
  20. data/app/views/admin/subscribers/index.html.erb +76 -0
  21. data/app/views/admin/subscribers/new.html.erb +15 -0
  22. data/app/views/admin/subscribers/show.html.erb +19 -0
  23. data/app/views/hooks/_footer_left.html.erb +1 -0
  24. data/app/views/hooks/_subscriber_sign_up_form.html.erb +4 -0
  25. data/app/views/hooks/_subscriber_static_content.html.erb +3 -0
  26. data/app/views/layouts/email.html.erb +81 -0
  27. data/app/views/subscribers/_fields.html.erb +11 -0
  28. data/app/views/subscribers/new.html.erb +17 -0
  29. data/app/views/subscribers/show.html.erb +16 -0
  30. data/config/locales/en.yml +33 -0
  31. data/config/routes.rb +20 -0
  32. data/db/migrate/install_spree_mail.rb +25 -0
  33. data/lib/spree_mail.rb +24 -0
  34. data/lib/spree_mail/custom_hooks.rb +20 -0
  35. data/lib/spree_mail/version.rb +3 -0
  36. data/lib/tasks/install.rake +36 -0
  37. data/public/images/mailer/airmail.gif +0 -0
  38. data/public/images/mailer/background.jpg +0 -0
  39. data/public/images/mailer/postmark.png +0 -0
  40. data/public/stylesheets/admin/spree_mail.css +38 -0
  41. metadata +149 -0
@@ -0,0 +1,19 @@
1
+ Spree Mail
2
+ ----------
3
+
4
+ Spree Mail extends Spree by adding a mailing list subscriber model, sign up forms and an admin to send messages.
5
+
6
+
7
+
8
+ Demo
9
+ ----
10
+
11
+ rails new spree_mail_example; cd spree_mail_example; echo "gem 'spree', '0.40.2'" >> Gemfile; echo "gem 'spree_mail', '0.40.0.1'" >> Gemfile; rm public/index.html; bundle install; rake spree:install spree_mail:install db:migrate db:seed; rails s
12
+
13
+
14
+
15
+
16
+ License
17
+ -------
18
+
19
+ Copyright (c) 2011 Spencer Steffen, released under the New BSD License All rights reserved.
@@ -0,0 +1,94 @@
1
+ require 'bundler'
2
+ #require 'rake/testtask'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ #Rake::TestTask.new do |t|
6
+ # t.libs << "lib"
7
+ # t.pattern = 'test/**/*_test.rb'
8
+ # t.verbose = true
9
+ #end
10
+
11
+
12
+ task :test do
13
+
14
+ root = ENV["RAILS_ROOT"] || File.expand_path('../spec/test_app', __FILE__)
15
+ env = File.join(root, 'config', 'environment.rb')
16
+ puts "(Rails Root: #{root})"
17
+
18
+ require env
19
+ require File.expand_path('../test/test_helper', __FILE__)
20
+ Dir["test/**/*.rb"].reject{|file| file.match(/test_helper/) != nil }.each do |file|
21
+ puts "Loading #{file}"
22
+ load file
23
+ end
24
+
25
+ end
26
+
27
+
28
+
29
+
30
+ desc "Default Task"
31
+ task :default => [ :test ]
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+ # TODO: pull in the spree/core/Rakefile bits that set up for testing
41
+ desc "Regenerates a Rails 3 app for testing"
42
+ task :test_app do
43
+ # TODO - this path requires a certain directory structure -- need
44
+ # to think about how to refactor
45
+
46
+
47
+ files = `gem contents spree`.split("\n").select{|file| file.match("test_app_generator")}
48
+ if files.length == 1
49
+ require files.first
50
+ class SpreeMailTestAppGenerator < Spree::Generators::TestAppGenerator
51
+ def tweak_gemfile
52
+ append_file "Gemfile" ,
53
+ <<-gems
54
+ gem 'activemerchant'
55
+ gem 'spree_core', '>=0.40.2'
56
+ gem 'spree_auth', '>=0.40.2'
57
+ gem 'spree_mail', :path => "#{File.dirname(__FILE__)}"
58
+ gems
59
+ end
60
+
61
+ def install_spree_gems
62
+
63
+ puts "-----------------------------------------"
64
+ puts "Installing gems..."
65
+ `bundle install --gemfile=spec/test_app/Gemfile`
66
+ puts "-----------------------------------------"
67
+
68
+ inside "test_app" do
69
+ run 'rake spree_core:install'
70
+ run 'rake spree_auth:install'
71
+ run 'rake spree_mail:install'
72
+ end
73
+ end
74
+
75
+ def migrate_db
76
+ run_migrations
77
+ end
78
+ end
79
+
80
+ SpreeMailTestAppGenerator.start
81
+
82
+ puts "spec/test_app created. "
83
+
84
+ else
85
+ puts "Failed: Could not find lib/generators/spree/test_app_generator.rb"
86
+ end
87
+ end
88
+
89
+ namespace :test_app do
90
+ desc 'Rebuild test database'
91
+ task :rebuild_db do
92
+ system("cd spec/test_app && rake db:drop db:migrate RAILS_ENV=test")
93
+ end
94
+ end
@@ -0,0 +1,61 @@
1
+ class Admin::EmailsController < Admin::BaseController
2
+
3
+ resource_controller
4
+
5
+ before_filter :check_json_authenticity, :only => :index
6
+ before_filter :get_subscribers, :only => [:new, :create, :edit, :update]
7
+
8
+ index.response do |wants|
9
+ wants.html { render :action => :index }
10
+ wants.json { render :json => json_data }
11
+ end
12
+ destroy.success.wants.js { render_js_for_destroy }
13
+
14
+
15
+ def deliver
16
+ @email = Email.find(params[:id])
17
+ sent, count = @email.deliver!
18
+ if sent
19
+ flash[:notice] = t('delivery_success', count)
20
+ else
21
+ flash[:error] = t('delivery_failed', count)
22
+ end
23
+ redirect_to admin_email_path(@email)
24
+ end
25
+
26
+
27
+
28
+ private
29
+
30
+ ## Allow different formats of json data to suit different ajax calls
31
+ #def json_data
32
+ # json_format = params[:json_format] or 'default'
33
+ # case json_format
34
+ # when 'basic'
35
+ # collection.map {|u| {'id' => u.id, 'name' => u.email}}.to_json
36
+ # else
37
+ # collection.to_json(:include =>
38
+ # {:bill_address => {:include => [:state, :country]},
39
+ # :ship_address => {:include => [:state, :country]}})
40
+ # end
41
+ #end
42
+
43
+ def get_subscribers
44
+ @subscribers = Subscriber.active
45
+ end
46
+
47
+ def collection
48
+ return @collection if @collection.present?
49
+ unless request.xhr?
50
+ @search = Email.searchlogic(params[:search])
51
+
52
+ #set order by to default or form result
53
+ @search.order ||= "ascend_by_name"
54
+
55
+ @collection = @search.do_search.paginate(:per_page => Spree::Config[:admin_products_per_page], :page => params[:page])
56
+
57
+ else
58
+ @collection = Email.where("wholesalers.name like :search", {:search => "#{params[:q].strip}%"}).limit(params[:limit] || 100)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,78 @@
1
+ class Admin::SubscribersController < Admin::BaseController
2
+
3
+ resource_controller
4
+
5
+ before_filter :check_json_authenticity, :only => :index
6
+
7
+ #index.response do |wants|
8
+ # wants.html { render :action => :index }
9
+ # wants.json { render :json => json_data }
10
+ #end
11
+
12
+ destroy.success.wants.js { render_js_for_destroy }
13
+
14
+ create.response do |wants|
15
+ wants.html { redirect_to admin_subscribers_path }
16
+ end
17
+
18
+ update.response do |wants|
19
+ wants.html { redirect_to admin_subscribers_path }
20
+ end
21
+
22
+ def resubscribe
23
+ @subscriber = object
24
+ if @subscriber.resubscribe!
25
+ flash[:notice] = t("resubscribe_success")
26
+ else
27
+ flash[:error] = t("resubscribe_failed")
28
+ end
29
+ redirect_to request.referer
30
+ end
31
+
32
+ def unsubscribe
33
+ @subscriber = object
34
+ if @subscriber.unsubscribe!
35
+ flash[:notice] = t("unsubscribe_success")
36
+ else
37
+ flash[:error] = t("unsubscribe_failed")
38
+ end
39
+ redirect_to request.referer
40
+ end
41
+
42
+ def unsubscribed
43
+ @search = Subscriber.searchlogic(params[:search])
44
+
45
+ #set order by to default or form result
46
+ @search.order ||= "ascend_by_name"
47
+
48
+ @subscribers = @collection = @search.do_search.unsubscribed.paginate(:per_page => Spree::Config[:admin_products_per_page], :page => params[:page])
49
+ render :template => 'admin/subscribers/index'
50
+ end
51
+
52
+
53
+ private
54
+
55
+ # Allow different formats of json data to suit different ajax calls
56
+ #def json_data
57
+ # json_format = params[:json_format] or 'default'
58
+ # case json_format
59
+ # when 'basic'
60
+ # collection.map {|u| {'id' => u.id, 'name' => u.email}}.to_json
61
+ # else
62
+ # collection.to_json(:include =>
63
+ # {:bill_address => {:include => [:state, :country]},
64
+ # :ship_address => {:include => [:state, :country]}})
65
+ # end
66
+ #end
67
+
68
+ def collection
69
+ return @collection if @collection.present?
70
+ @search = Subscriber.searchlogic(params[:search])
71
+
72
+ #set order by to default or form result
73
+ @search.order ||= "ascend_by_name"
74
+
75
+ @collection = @search.do_search.active.paginate(:per_page => Spree::Config[:admin_products_per_page], :page => params[:page])
76
+
77
+ end
78
+ end
@@ -0,0 +1,19 @@
1
+ class EmailsController < Spree::BaseController
2
+
3
+ include ActionView::Helpers::TagHelper
4
+ include ActionView::Helpers::TextHelper
5
+
6
+ def show
7
+ @subscriber = Subscriber.find_by_token(params[:token])
8
+ @email = Email.find_by_token(params[:id])
9
+
10
+ return redirect_to new_subscriber_path unless @email.recipients.include?(@subscriber.email)
11
+
12
+ @email_subject = @email.render(:subject, @subscriber)
13
+ @text = @email.render(:body, @subscriber)
14
+ @base_url = "http://#{Spree::Config[:site_url]}"
15
+
16
+ render :layout => 'email', :text => simple_format(@text)
17
+ end
18
+
19
+ end
@@ -0,0 +1,42 @@
1
+ class SubscribersController < Spree::BaseController
2
+
3
+ before_filter :get_subscriber, :only => [:show, :unsubscribe]
4
+
5
+ def index
6
+ redirect_to new_subscriber_path
7
+ end
8
+
9
+ def new
10
+ @subscriber = Subscriber.new
11
+ end
12
+
13
+ def show
14
+ end
15
+
16
+ def create
17
+ @subscriber = Subscriber.new(params[:subscriber])
18
+ if @subscriber.valid? && @subscriber.save
19
+ flash[:notice] = "Thanks for signing up for our newsletter!"
20
+ redirect_to new_subscriber_path
21
+ else
22
+ flash[:error] = "Sorry, we could not sign you up."
23
+ render :action => 'new'
24
+ end
25
+ end
26
+
27
+ def unsubscribe
28
+ if @subscriber.email == params[:subscriber][:email] && @subscriber.unsubscribe!
29
+ flash[:notice] = "You were successfully unsubscribed from the mailing list."
30
+ else
31
+ flash[:error] = "We're sorry, you could not be unsubscribed at this time."
32
+ end
33
+ redirect_to new_subscriber_path
34
+ end
35
+
36
+ private
37
+
38
+ def get_subscriber
39
+ @subscriber = Subscriber.find_by_token(params[:id])
40
+ end
41
+
42
+ end
@@ -0,0 +1,24 @@
1
+ class EmailMailer < ActionMailer::Base
2
+
3
+ include ActionView::Helpers::TagHelper
4
+ include ActionView::Helpers::TextHelper
5
+
6
+ default_url_options[:host] = "10.0.1.10:3000"
7
+
8
+ def with_layout(email, subscriber)
9
+ @email = email
10
+ @subscriber = subscriber
11
+ @email_subject = @email.render(:subject, @subscriber)
12
+ @text = @email.render(:body, @subscriber)
13
+ @base_url = "http://10.0.1.10:3000" ##{Spree::Config[:site_url]}
14
+ @link_to_browser = read_email_url(@subscriber.token, @email.token)
15
+
16
+ unless @email.nil? || @email_subject.empty? || @text.empty?
17
+ mail(:to => @subscriber.email, :from => @email.from, :subject => @email_subject) do |format|
18
+ format.text { render :text => @text }
19
+ format.html { render :layout => 'email', :text => simple_format(@text) }
20
+ end
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,77 @@
1
+ class Email < ActiveRecord::Base
2
+
3
+ validates :to, :presence => true
4
+ validates :subject, :presence => true
5
+ validates :body, :presence => true
6
+
7
+ before_create :set_token
8
+
9
+ def to=(value)
10
+ value = {} unless value.is_a? Hash
11
+ value.delete("0")
12
+ return false if value.empty?
13
+ write_attribute :to, value.inspect
14
+ end
15
+
16
+ def from
17
+ MailMethod.current.preferred_mails_from rescue "no-reply@spree-mail-example.com"
18
+ end
19
+
20
+ def recipients
21
+ hash = eval(read_attribute(:to)) rescue {}
22
+ hash.values
23
+ end
24
+
25
+ def recipient_list
26
+ recipients.join(", ")
27
+ end
28
+
29
+ def deliver!
30
+ count = 0
31
+ recipients.each do |email|
32
+ subscriber = Subscriber.find_by_email(email) rescue nil
33
+ if subscriber
34
+ mail = EmailMailer.with_layout(self, subscriber)
35
+ count += 1 if mail && mail.deliver!
36
+ end
37
+ end
38
+ return 0 < count, count
39
+ end
40
+
41
+
42
+
43
+ def render(attribute, subscriber)
44
+ Mustache.render(self.send(attribute), subscriber.attributes)
45
+ end
46
+
47
+ private
48
+
49
+ def set_token
50
+ write_attribute :token, Digest::SHA1.hexdigest(Time.now.to_s)
51
+ end
52
+
53
+
54
+ class << self
55
+
56
+ def new(parameters={})
57
+ parameters ||= {}
58
+ super(parameters.reverse_merge!(:body => template))
59
+ end
60
+
61
+ def template
62
+ txt=<<TXT
63
+ Hello {{name}},
64
+
65
+
66
+
67
+ Regards,
68
+
69
+ #{Spree::Config[:site_name]}
70
+
71
+ TXT
72
+ end
73
+
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,39 @@
1
+ class Subscriber < ActiveRecord::Base
2
+
3
+ attr_protected :token
4
+
5
+ scope :active, where("unsubscribed_at IS NULL").order(:name)
6
+ scope :unsubscribed, where("unsubscribed_at IS NOT NULL").order(:name)
7
+
8
+ validates :name, :presence => true
9
+ validates :email, :format => Devise.email_regexp, :uniqueness => true
10
+
11
+ before_create :set_token
12
+
13
+ def active?
14
+ unsubscribed_at.to_s.empty?
15
+ end
16
+
17
+ def resubscribe!
18
+ return true if active?
19
+ self.unsubscribed_at = nil
20
+ save
21
+ end
22
+
23
+ def unsubscribe!
24
+ return true unless active?
25
+ self.unsubscribed_at = Time.now
26
+ save
27
+ end
28
+
29
+ def email=(value)
30
+ write_attribute :email, value.strip.downcase
31
+ end
32
+
33
+ private
34
+
35
+ def set_token
36
+ write_attribute :token, Digest::SHA1.hexdigest(Time.now.to_s)
37
+ end
38
+
39
+ end