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.
- 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
|