spree_mail 0.40.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +19 -0
- data/Rakefile +94 -0
- data/app/controllers/admin/emails_controller.rb +61 -0
- data/app/controllers/admin/subscribers_controller.rb +78 -0
- data/app/controllers/emails_controller.rb +19 -0
- data/app/controllers/subscribers_controller.rb +42 -0
- data/app/mailers/email_mailer.rb +24 -0
- data/app/model/email.rb +77 -0
- data/app/model/subscriber.rb +39 -0
- data/app/views/admin/emails/_form.html.erb +78 -0
- data/app/views/admin/emails/edit.html.erb +15 -0
- data/app/views/admin/emails/index.html.erb +64 -0
- data/app/views/admin/emails/new.html.erb +15 -0
- data/app/views/admin/emails/show.html.erb +20 -0
- data/app/views/admin/hooks/_subscribers_tab.html.erb +1 -0
- data/app/views/admin/shared/_spree_mail_sub_nav.html.erb +8 -0
- data/app/views/admin/subscribers/_form.html.erb +19 -0
- data/app/views/admin/subscribers/_options.html.erb +5 -0
- data/app/views/admin/subscribers/edit.html.erb +15 -0
- data/app/views/admin/subscribers/index.html.erb +76 -0
- data/app/views/admin/subscribers/new.html.erb +15 -0
- data/app/views/admin/subscribers/show.html.erb +19 -0
- data/app/views/hooks/_footer_left.html.erb +1 -0
- data/app/views/hooks/_subscriber_sign_up_form.html.erb +4 -0
- data/app/views/hooks/_subscriber_static_content.html.erb +3 -0
- data/app/views/layouts/email.html.erb +81 -0
- data/app/views/subscribers/_fields.html.erb +11 -0
- data/app/views/subscribers/new.html.erb +17 -0
- data/app/views/subscribers/show.html.erb +16 -0
- data/config/locales/en.yml +33 -0
- data/config/routes.rb +20 -0
- data/db/migrate/install_spree_mail.rb +25 -0
- data/lib/spree_mail.rb +24 -0
- data/lib/spree_mail/custom_hooks.rb +20 -0
- data/lib/spree_mail/version.rb +3 -0
- data/lib/tasks/install.rake +36 -0
- data/public/images/mailer/airmail.gif +0 -0
- data/public/images/mailer/background.jpg +0 -0
- data/public/images/mailer/postmark.png +0 -0
- data/public/stylesheets/admin/spree_mail.css +38 -0
- metadata +149 -0
data/README.md
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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
|
data/app/model/email.rb
ADDED
@@ -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
|