mail_spy 0.0.2 → 0.1.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.rdoc +86 -24
- data/app/assets/images/mail_spy/background_tile.gif +0 -0
- data/app/assets/images/mail_spy/mailspy_sprite.png +0 -0
- data/app/assets/images/mail_spy/spy.ico +0 -0
- data/app/assets/javascripts/mail_spy/lib/backbone.js +1256 -0
- data/app/assets/javascripts/mail_spy/lib/underscore.js +999 -0
- data/app/assets/javascripts/mail_spy/reports.js +2 -0
- data/app/assets/javascripts/mail_spy/sendgrid.js +2 -0
- data/app/assets/stylesheets/mail_spy/application.css +62 -1
- data/app/assets/stylesheets/mail_spy/reports.css.scss +128 -0
- data/app/assets/stylesheets/mail_spy/sendgrid.css +4 -0
- data/app/controllers/mail_spy/reports_controller.rb +29 -0
- data/app/controllers/mail_spy/sendgrid_controller.rb +26 -0
- data/app/controllers/mail_spy/tracking_controller.rb +19 -11
- data/app/helpers/mail_spy/email_helper.rb +21 -3
- data/app/helpers/mail_spy/reports_helper.rb +4 -0
- data/app/helpers/mail_spy/sendgrid_helper.rb +4 -0
- data/app/mailers/mail_spy/core_mailer.rb +15 -18
- data/app/models/mail_spy/email.rb +127 -19
- data/app/models/mail_spy/process_log.rb +25 -0
- data/app/views/layouts/mail_spy/application.html.erb +3 -0
- data/app/views/mail_spy/reports/campaigns.html.erb +138 -0
- data/config/routes.rb +6 -0
- data/lib/generators/mail_spy/templates/mail_spy.rb +25 -1
- data/lib/mail_spy/manager.rb +100 -54
- data/lib/mail_spy/sendgrid/smtp_api_header.rb +68 -0
- data/lib/mail_spy/version.rb +1 -1
- data/lib/mail_spy.rb +39 -5
- data/test/dummy/app/views/email_templates/helper_test.html.erb +15 -0
- data/test/dummy/app/views/email_templates/helper_test.text.erb +11 -0
- data/test/dummy/config/environments/development.rb +1 -1
- data/test/dummy/config/environments/production.rb +5 -1
- data/test/dummy/config/environments/test.rb +4 -1
- data/test/dummy/config/initializers/mail_spy.rb +42 -0
- data/test/dummy/config/routes.rb +1 -2
- data/test/dummy/db/schema.rb +16 -0
- data/test/dummy/db/seeds.rb +16 -13
- data/test/dummy/lib/tasks/emails.rake +8 -0
- data/test/dummy/log/development.log +782 -0
- data/test/dummy/log/test.log +9952 -0
- data/test/fixtures/mail_spy/process_logs.yml +15 -0
- data/test/functional/mail_spy/core_mailer_test.rb +16 -9
- data/test/functional/mail_spy/reports_controller_test.rb +9 -0
- data/test/functional/mail_spy/sendgrid_controller_test.rb +9 -0
- data/test/functional/mail_spy/tracking_controller_test.rb +16 -11
- data/test/test_email_credentials.yml +2 -2
- data/test/test_email_credentials.yml.sample +0 -8
- data/test/test_helper.rb +15 -41
- data/test/unit/helpers/mail_spy/reports_helper_test.rb +6 -0
- data/test/unit/helpers/mail_spy/sendgrid_helper_test.rb +6 -0
- data/test/unit/mail_spy/manager_test.rb +101 -122
- data/test/unit/mail_spy/{email_template_test.rb → process_log_test.rb} +1 -1
- metadata +122 -34
- data/app/models/mail_spy/email_template.rb +0 -17
- data/test/fixtures/mail_spy/email_templates.yml +0 -9
- /data/lib/generators/mail_spy/{initialize_generator.rb → initializer_generator.rb} +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<% @campaigns = [{
|
|
2
|
+
:name=>"Valentine's Day",
|
|
3
|
+
:first_sent => Date.today()-14,
|
|
4
|
+
:last_sent => Date.today(),
|
|
5
|
+
:kpis=>{
|
|
6
|
+
"Population"=> {
|
|
7
|
+
:icon=>"People",
|
|
8
|
+
:figure=>1000000
|
|
9
|
+
},
|
|
10
|
+
"Emails Sent"=>{
|
|
11
|
+
:icon=>"Mail",
|
|
12
|
+
:figure=>66478
|
|
13
|
+
},
|
|
14
|
+
"Click-Throughs"=>{
|
|
15
|
+
:icon=>"Pointer",
|
|
16
|
+
:figure=>1549
|
|
17
|
+
},
|
|
18
|
+
"Total Purchases"=>{
|
|
19
|
+
:icon=>"Money",
|
|
20
|
+
:figure=>1214
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
:purchase_funnel=>{
|
|
24
|
+
"delivered"=>66478,
|
|
25
|
+
"opened"=>7643,
|
|
26
|
+
"clicked"=>1549,
|
|
27
|
+
"purchased"=>5
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
},{
|
|
31
|
+
:name=>"Mother's Day",
|
|
32
|
+
:last_sent => Date.today(),
|
|
33
|
+
:sent=>1221,
|
|
34
|
+
:kpis=>{},
|
|
35
|
+
:purchase_funnel=>{}
|
|
36
|
+
}] %>
|
|
37
|
+
|
|
38
|
+
<script type="text/javascript">
|
|
39
|
+
$(function(){
|
|
40
|
+
|
|
41
|
+
getValue = function(_o){
|
|
42
|
+
try{
|
|
43
|
+
if(typeof(_o) === 'number' ||
|
|
44
|
+
typeof(parseInt(_o,10) === 'number')){
|
|
45
|
+
return _o;
|
|
46
|
+
} else if (_o.r && _o.c){
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
} catch(e){
|
|
50
|
+
console.log("Can't evaluate:");
|
|
51
|
+
console.log(_o);
|
|
52
|
+
console.log(e);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
$.each($("[data-operator]"),function(i,e){
|
|
57
|
+
var lhs, rhs,
|
|
58
|
+
result = "NA",
|
|
59
|
+
op = $(this).attr("data-operator"),
|
|
60
|
+
ops = $(this).attr("data-operands");
|
|
61
|
+
|
|
62
|
+
try{
|
|
63
|
+
ops = ops.split("|");
|
|
64
|
+
lhs = $.parseJSON(ops[0]);
|
|
65
|
+
rhs = $.parseJSON(ops[1]);
|
|
66
|
+
console.log(lhs + " : " + rhs);
|
|
67
|
+
|
|
68
|
+
//Switch operators
|
|
69
|
+
if(op == "+"){
|
|
70
|
+
result = getValue(lhs) + getValue(rhs);
|
|
71
|
+
} else if(op == "%"){
|
|
72
|
+
result = (lhs + "/" + rhs);
|
|
73
|
+
}
|
|
74
|
+
} catch(e) {
|
|
75
|
+
console.log(e);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
$(this).html(result);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
$.each($("[data-graph]"),function(i,e){
|
|
83
|
+
$(this).html($(this).attr("data-value"));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
});
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<%# ------------------------------------- Start actual view %>
|
|
90
|
+
|
|
91
|
+
<div class="MsSprite Logo">Mail Spy</div>
|
|
92
|
+
|
|
93
|
+
<h1 class="_active">
|
|
94
|
+
<div class="MsSprite Nav Campaign _active"></div> All Campaigns
|
|
95
|
+
</h1>
|
|
96
|
+
|
|
97
|
+
<div class="CampaignReport">
|
|
98
|
+
<%# For each campaign %>
|
|
99
|
+
<% @campaigns.each do |c| %>
|
|
100
|
+
<div class="CampaignContainer">
|
|
101
|
+
<h1 ><%= c[:name] %></h1>
|
|
102
|
+
|
|
103
|
+
<div class="KpiContainer">
|
|
104
|
+
<% c[:kpis].each do |kpi, kpi_data|%>
|
|
105
|
+
<div class="Kpi">
|
|
106
|
+
<div class="MsSprite <%= kpi_data[:icon] %> Icon"></div>
|
|
107
|
+
<kpi><%= kpi %></kpi>
|
|
108
|
+
<figure><%= kpi_data[:figure] %></figure>
|
|
109
|
+
</div>
|
|
110
|
+
<% end %>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="FunnelContainer">
|
|
113
|
+
<h2>
|
|
114
|
+
Purchase Funnel
|
|
115
|
+
</h2>
|
|
116
|
+
<table>
|
|
117
|
+
<thead>
|
|
118
|
+
<tr>
|
|
119
|
+
<td>Action</td>
|
|
120
|
+
<td>Raw</td>
|
|
121
|
+
<td>%</td>
|
|
122
|
+
<td></td>
|
|
123
|
+
</tr>
|
|
124
|
+
</thead>
|
|
125
|
+
<% c[:purchase_funnel].each do |step, step_data|%>
|
|
126
|
+
<tr>
|
|
127
|
+
<td><%= step %></td>
|
|
128
|
+
<td><%=step_data%></funnelCell>
|
|
129
|
+
<td data-operator="%" data-operands='{"r":-0,"c":-1}|{"r":-1,"c":-1}'></td>
|
|
130
|
+
<td data-operator="+" data-operands='6|9'></td>
|
|
131
|
+
<td data-graph="bar" data-value="<%=step_data%>"></td>
|
|
132
|
+
</tr>
|
|
133
|
+
<% end %>
|
|
134
|
+
</table>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
<% end %>
|
|
138
|
+
</div>
|
data/config/routes.rb
CHANGED
|
@@ -4,4 +4,10 @@ MailSpy::Engine.routes.draw do
|
|
|
4
4
|
match "b", :controller => :tracking, :action => :bug
|
|
5
5
|
match "a", :controller => :tracking, :action => :action
|
|
6
6
|
|
|
7
|
+
match "reports", :controller => :reports, :action => :campaigns
|
|
8
|
+
match "email", :controller => :reports, :action => :email
|
|
9
|
+
|
|
10
|
+
# Sendgrid notification api
|
|
11
|
+
post "sendgrid/notification", :controller => :sendgrid, :action => :notification
|
|
12
|
+
|
|
7
13
|
end
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
# MailSpy configuration
|
|
2
|
+
# tracking_host: The hostname and port (if necessary) of the server MailSpy
|
|
3
|
+
# will make requests to for tracking.
|
|
4
|
+
#
|
|
5
|
+
# aws credentials: We use aws to store the templates for the emails.
|
|
6
|
+
#
|
|
7
|
+
# If you have delayed job and want to use it to clear the outstanding emails
|
|
8
|
+
# toggle it on here
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
MailSpy.configure do |config|
|
|
12
|
+
config.tracker_host = "myapp.herokuapp.com"
|
|
13
|
+
config.aws_access_key_id = "YOUR ACCESS KEY"
|
|
14
|
+
config.aws_secret_access_key = "YOUR SECRET ACCESS KEY"
|
|
15
|
+
config.aws_campaign_bucket = "campaigns-yourco-com"
|
|
16
|
+
config.using_delayed_job = true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
|
|
1
20
|
# MailSpy allows you to add multiple email service providers simply
|
|
2
21
|
# call this method and specify the following options
|
|
3
22
|
#
|
|
@@ -12,14 +31,19 @@
|
|
|
12
31
|
# :password,
|
|
13
32
|
# :domain,
|
|
14
33
|
# :enable_starttls_auto
|
|
34
|
+
# :options (esp specific options)
|
|
15
35
|
|
|
16
36
|
MailSpy.add_email_service_provider do |esp|
|
|
17
37
|
esp.name = "sendgrid"
|
|
18
38
|
esp.address = "smtp.sendgrid.net"
|
|
19
|
-
esp.port = "587"
|
|
39
|
+
esp.port = "587"
|
|
20
40
|
esp.domain = "herokuapp.com"
|
|
21
41
|
esp.user_name = ENV['SENDGRID_USERNAME']
|
|
22
42
|
esp.password = ENV['SENDGRID_PASSWORD']
|
|
23
43
|
esp.authentication = :plain
|
|
24
44
|
esp.enable_starttls_auto = true
|
|
45
|
+
esp.options = {
|
|
46
|
+
:enable_sendgrid_event_tracking => true,
|
|
47
|
+
:enable_auto_google_analytics => true
|
|
48
|
+
}
|
|
25
49
|
end
|
data/lib/mail_spy/manager.rb
CHANGED
|
@@ -1,96 +1,142 @@
|
|
|
1
1
|
module MailSpy
|
|
2
2
|
module Manager
|
|
3
3
|
|
|
4
|
-
# -------------------------------------------
|
|
5
|
-
# Programmatically add email templates with a schema of search and
|
|
6
|
-
# replaceable text
|
|
7
|
-
#
|
|
8
|
-
def add_template(name, html_erb, text_erb, template_values_definition)
|
|
9
|
-
MailSpy::EmailTemplate.create!(
|
|
10
|
-
{
|
|
11
|
-
:name => name,
|
|
12
|
-
:html_erb => html_erb,
|
|
13
|
-
:text_erb => text_erb,
|
|
14
|
-
:template_values_definition => template_values_definition
|
|
15
|
-
})
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# ------------------------------------------- ADD INSTANCE
|
|
4
|
+
# ------------------------------------------- CREATE EMAIL
|
|
19
5
|
# Adds a instance of a email template to the queue to send
|
|
20
6
|
#
|
|
21
|
-
def
|
|
7
|
+
def create_email(options)
|
|
22
8
|
options.to_options!
|
|
23
9
|
|
|
24
10
|
required_options = [
|
|
25
|
-
:
|
|
11
|
+
:campaign, :stream, :component, :schedule_at, :subject,
|
|
26
12
|
:template_values, :from, :reply_to]
|
|
27
13
|
to_options = [:to, :cc, :bcc]
|
|
28
14
|
|
|
29
15
|
# Ensure that we have the required options
|
|
30
|
-
required_options.each { |ro| raise "
|
|
16
|
+
required_options.each { |ro| raise "create_email call missing #{ro}" unless options.include? ro }
|
|
31
17
|
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
18
|
+
# Ensure that the campaign, stream and component are not blank
|
|
19
|
+
# These keys are used to define the s3 paths for the templates
|
|
20
|
+
[:campaign, :stream, :component].each { |key| raise "##{key} can't be blank" if options[key].blank? }
|
|
35
21
|
|
|
36
|
-
# Make sure we have
|
|
37
|
-
options[
|
|
38
|
-
|
|
39
|
-
raise "No template: #{options[:template_name]} found, try add_template first" unless template
|
|
22
|
+
# Make sure we have someone to send to and its not blank
|
|
23
|
+
has_sender = to_options.select { |option| options[option].present? }.present?
|
|
24
|
+
raise "Email instance has no sender (to,cc,bcc were all blank)" unless has_sender
|
|
40
25
|
|
|
41
26
|
|
|
42
|
-
# Make sure that
|
|
43
|
-
|
|
27
|
+
# Make sure that all options passed map to a accessor so we don't errantly
|
|
28
|
+
# think we are passing something correctly and really its getting silently
|
|
29
|
+
# ignored
|
|
30
|
+
options.keys.each do |option|
|
|
44
31
|
unless MailSpy::Email.method_defined? "#{option}=".intern
|
|
45
32
|
raise "MailSpy::Email doesn't have #{option} as a setter '"
|
|
46
33
|
end
|
|
47
34
|
end
|
|
48
35
|
|
|
36
|
+
# Ensure that a esp (forced or random) exists for the email
|
|
37
|
+
forced_esp = options[:email_service_provider]
|
|
38
|
+
if forced_esp.present?
|
|
39
|
+
raise "No esp configured with name: #{forced_esp}" if MailSpy.esps[forced_esp].blank?
|
|
40
|
+
else
|
|
41
|
+
raise "No esps configured" if MailSpy.esps.blank?
|
|
42
|
+
esp_key = MailSpy.esps.keys[rand(MailSpy.esps.keys.count)]
|
|
43
|
+
options[:email_service_provider] = esp_key
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Google Analytics aupport for automatic population of utm_tokens
|
|
47
|
+
esp = MailSpy.esps[options[:email_service_provider]]
|
|
48
|
+
if esp.options[:enable_auto_google_analytics].present?
|
|
49
|
+
options[:utm_source] ||= 'mailspy'
|
|
50
|
+
options[:utm_medium] ||= 'email'
|
|
51
|
+
options[:utm_campaign] ||= options[:campaign]
|
|
52
|
+
options[:utm_term] ||= options[:stream]
|
|
53
|
+
options[:utm_content] ||= options[:component]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
#Create the email
|
|
57
|
+
email = MailSpy::Email.new(options)
|
|
58
|
+
|
|
59
|
+
# Ensure we have the template for the email on s3
|
|
60
|
+
raise "Missing html template" unless email.html_erb.present?
|
|
61
|
+
raise "Missing text template" unless email.text_erb.present?
|
|
49
62
|
|
|
50
|
-
email = MailSpy::Email.create!(options)
|
|
51
|
-
email.email_template = template
|
|
52
63
|
email.save!
|
|
64
|
+
|
|
65
|
+
# Enable sendgrid specific enhancements
|
|
66
|
+
# Must happen after the email has been saved for the id
|
|
67
|
+
# TODO test this works
|
|
68
|
+
if esp.options[:enable_sendgrid_event_tracking].present?
|
|
69
|
+
header = MailSpy::Sendgrid::SmtpApiHeader.new
|
|
70
|
+
header.setUniqueArgs({:eid => email.id.to_s})
|
|
71
|
+
email.headers = {'X-SMTPAPI' => header.asJSON}.merge(email.headers)
|
|
72
|
+
email.save!
|
|
73
|
+
end
|
|
74
|
+
|
|
53
75
|
email
|
|
54
76
|
end
|
|
55
77
|
|
|
56
78
|
# ------------------------------------------- SEND OUTSTANDING EMAILS
|
|
57
79
|
# Batches through all the emails that were scheduled and have come due
|
|
58
|
-
# sends them out (step many at a time)
|
|
59
|
-
|
|
80
|
+
# sends them out (step many at a time). Don't thread this method, instead
|
|
81
|
+
# use the parameters to control concurrency
|
|
82
|
+
def send_outstanding_emails(step=200, num_threads=100, num_workers=1)
|
|
83
|
+
success = false
|
|
60
84
|
raise "No Email service providers installed" unless MailSpy.esps.present?
|
|
61
85
|
|
|
62
|
-
|
|
63
|
-
|
|
86
|
+
return if MailSpy::ProcessLog.currently_processing?
|
|
87
|
+
current_time = DateTime.now
|
|
88
|
+
current_process = MailSpy::ProcessLog.create!(
|
|
89
|
+
{
|
|
90
|
+
:start => current_time,
|
|
91
|
+
:running => true,
|
|
92
|
+
})
|
|
64
93
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
value = email.send("#{email_key}")
|
|
69
|
-
pony_hash[pony_key] = value if value.present?
|
|
70
|
-
end
|
|
94
|
+
count = MailSpy::Email.where(:schedule_at.lte => current_time, :sent => false, :failed => false).count
|
|
95
|
+
first = MailSpy::Email.where(:schedule_at.lte => current_time, :sent => false, :failed => false).first
|
|
96
|
+
division = (count / num_workers).to_i
|
|
71
97
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
mails.each do |email|
|
|
78
|
-
MailSpy::CoreMailer.template(email).deliver
|
|
79
|
-
email.update_attribute(:sent, true)
|
|
80
|
-
sent += 1
|
|
98
|
+
num_workers.times do |i|
|
|
99
|
+
if MailSpy.using_delayed_job
|
|
100
|
+
Email.delay.deliver_batch(current_time, first._id, (i - 1) * division, division, step, num_threads)
|
|
101
|
+
else
|
|
102
|
+
Email.deliver_batch(current_time, first._id, (i - 1) * division, division, step, num_threads)
|
|
81
103
|
end
|
|
82
|
-
offset += step
|
|
83
104
|
end
|
|
84
105
|
|
|
85
|
-
|
|
106
|
+
success = true
|
|
107
|
+
return count
|
|
108
|
+
ensure
|
|
109
|
+
if current_process
|
|
110
|
+
end_time = Time.now
|
|
111
|
+
current_process.end = end_time
|
|
112
|
+
current_process.seconds_elapsed = end_time.to_i - current_process.start.to_i
|
|
113
|
+
current_process.running = false
|
|
114
|
+
current_process.success = success
|
|
115
|
+
current_process.save!
|
|
116
|
+
end
|
|
86
117
|
end
|
|
87
118
|
|
|
88
119
|
|
|
89
120
|
# ------------------------------------------- TRACKING
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
121
|
+
# MailSpy will automatically track opens and clicks if the tracking bugs
|
|
122
|
+
# and track_link helpers are used. This action allows tracking of
|
|
123
|
+
# arbitrary actions. Please be careful to standardize on action names
|
|
124
|
+
# email_id: The id from the MailSpy::Email record
|
|
125
|
+
# action_type: String denoting the action that occured
|
|
126
|
+
# details: Hash of any details of that action (again strive for standards)
|
|
127
|
+
# count: how many of this action occurred (defaults to 1)
|
|
128
|
+
def track_action(email_id, action_type, details={}, count=1)
|
|
129
|
+
raise "track_action missing email_id" if email_id.blank?
|
|
130
|
+
raise "track_Action missing action_type" if action_type.blank?
|
|
131
|
+
|
|
132
|
+
hash ={}
|
|
133
|
+
hash[:action_type] = action_type
|
|
134
|
+
hash[:count] = count
|
|
135
|
+
hash[:details] = details if details.present? && details.kind_of?(Hash)
|
|
136
|
+
|
|
137
|
+
# Save it up
|
|
138
|
+
email = MailSpy::Email.find(email_id)
|
|
139
|
+
email.actions.create!(hash)
|
|
94
140
|
end
|
|
95
141
|
|
|
96
142
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Version 1.0
|
|
2
|
+
# Last Updated 6/22/2009
|
|
3
|
+
# Header support for send grids smtp api
|
|
4
|
+
#
|
|
5
|
+
#
|
|
6
|
+
require 'json'
|
|
7
|
+
|
|
8
|
+
module MailSpy
|
|
9
|
+
module Sendgrid
|
|
10
|
+
class SmtpApiHeader
|
|
11
|
+
|
|
12
|
+
def initialize()
|
|
13
|
+
@data = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def addTo(to)
|
|
17
|
+
@data['to'] ||= []
|
|
18
|
+
@data['to'] += to.kind_of?(Array) ? to : [to]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def addSubVal(var, val)
|
|
22
|
+
if not @data['sub']
|
|
23
|
+
@data['sub'] = {}
|
|
24
|
+
end
|
|
25
|
+
if val.instance_of?(Array)
|
|
26
|
+
@data['sub'][var] = val
|
|
27
|
+
else
|
|
28
|
+
@data['sub'][var] = [val]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def setUniqueArgs(val)
|
|
33
|
+
if val.instance_of?(Hash)
|
|
34
|
+
@data['unique_args'] = val
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def setCategory(cat)
|
|
39
|
+
@data['category'] = cat
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def addFilterSetting(fltr, setting, val)
|
|
43
|
+
if not @data['filters']
|
|
44
|
+
@data['filters'] = {}
|
|
45
|
+
end
|
|
46
|
+
if not @data['filters'][fltr]
|
|
47
|
+
@data['filters'][fltr] = {}
|
|
48
|
+
end
|
|
49
|
+
if not @data['filters'][fltr]['settings']
|
|
50
|
+
@data['filters'][fltr]['settings'] = {}
|
|
51
|
+
end
|
|
52
|
+
@data['filters'][fltr]['settings'][setting] = val
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def asJSON()
|
|
56
|
+
json = JSON.generate @data
|
|
57
|
+
return json.gsub(/(["\]}])([,:])(["\[{])/, '\\1\\2 \\3')
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def as_string()
|
|
61
|
+
json = asJSON()
|
|
62
|
+
str = 'X-SMTPAPI: %s' % json.gsub(/(.{1,72})( +|$\n?)|(.{1,72})/, "\\1\\3\n")
|
|
63
|
+
return str
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/mail_spy/version.rb
CHANGED
data/lib/mail_spy.rb
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
require 'pp' #For debugging
|
|
2
|
+
require 'aws-sdk'
|
|
3
|
+
require 'mail_spy/sendgrid/smtp_api_header'
|
|
4
|
+
require 'work_queue'
|
|
2
5
|
require "mongoid"
|
|
3
6
|
require "mail_spy/engine"
|
|
4
7
|
require "mail_spy/manager"
|
|
8
|
+
require "zurb-foundation"
|
|
5
9
|
module MailSpy
|
|
6
10
|
|
|
7
11
|
extend MailSpy::Manager
|
|
@@ -10,21 +14,28 @@ module MailSpy
|
|
|
10
14
|
# Allows the client to configure and add esps to MailSpy
|
|
11
15
|
MailSpyESP = Struct.new(
|
|
12
16
|
:address, :port, :authentication, :user_name, :password, :domain,
|
|
13
|
-
:enable_starttls_auto, :name
|
|
17
|
+
:enable_starttls_auto, :name, :options
|
|
14
18
|
)
|
|
15
19
|
@@esps = {}
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
MailSpyConfig = Struct.new(
|
|
22
|
+
:tracker_host, :aws_access_key_id,
|
|
23
|
+
:aws_secret_access_key, :aws_campaign_bucket, :using_delayed_job
|
|
24
|
+
|
|
25
|
+
)
|
|
26
|
+
@@config = MailSpyConfig.new
|
|
18
27
|
|
|
19
28
|
# Allows the initializer to set the configuration
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
def self.configure(&block)
|
|
30
|
+
block.call(@@config)
|
|
31
|
+
end
|
|
23
32
|
|
|
24
33
|
#TODO eventually have this be a view with a interface
|
|
25
34
|
def self.add_email_service_provider(&block)
|
|
26
35
|
esp = MailSpyESP.new
|
|
36
|
+
esp.options = {} # Important! provides default options list for internals
|
|
27
37
|
block.call(esp)
|
|
38
|
+
esp.options.to_options!
|
|
28
39
|
@@esps[esp.name] = esp
|
|
29
40
|
end
|
|
30
41
|
|
|
@@ -32,5 +43,28 @@ module MailSpy
|
|
|
32
43
|
@@esps
|
|
33
44
|
end
|
|
34
45
|
|
|
46
|
+
def self.tracker_host
|
|
47
|
+
@@config.tracker_host
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.template_directory
|
|
51
|
+
@@config.template_directory
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.aws_access_key_id
|
|
55
|
+
@@config.aws_access_key_id
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.aws_secret_access_key
|
|
59
|
+
@@config.aws_secret_access_key
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.aws_campaign_bucket
|
|
63
|
+
@@config.aws_campaign_bucket
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.using_delayed_job
|
|
67
|
+
@@config.using_delayed_job
|
|
68
|
+
end
|
|
35
69
|
|
|
36
70
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<p>
|
|
2
|
+
You should be able to click on the link and have the server record the even
|
|
3
|
+
and forward you to the correct place.
|
|
4
|
+
|
|
5
|
+
A link : <%= track_link 'My home', 'www.google.com' %>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<br>
|
|
9
|
+
|
|
10
|
+
<p>
|
|
11
|
+
You should be able to show images in the email client and have the server
|
|
12
|
+
track the open
|
|
13
|
+
A Bug is in the parenthesis (<%= tracking_bug %>)
|
|
14
|
+
</p>
|
|
15
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
You should be able to click on the link and have the server record the even
|
|
2
|
+
and forward you to the correct place.
|
|
3
|
+
|
|
4
|
+
A link : <%= track_link 'My home', 'www.google.com' %>
|
|
5
|
+
---------------------------
|
|
6
|
+
|
|
7
|
+
You should be able to show images in the email client and have the server
|
|
8
|
+
track the open
|
|
9
|
+
A Bug is in the parenthesis (<%= tracking_bug %>)
|
|
10
|
+
|
|
11
|
+
|
|
@@ -14,7 +14,7 @@ Dummy::Application.configure do
|
|
|
14
14
|
config.action_controller.perform_caching = false
|
|
15
15
|
|
|
16
16
|
# Don't care if the mailer can't send
|
|
17
|
-
config.action_mailer.raise_delivery_errors =
|
|
17
|
+
config.action_mailer.raise_delivery_errors = true
|
|
18
18
|
|
|
19
19
|
# Print deprecation notices to the Rails logger
|
|
20
20
|
config.active_support.deprecation = :log
|
|
@@ -5,7 +5,7 @@ Dummy::Application.configure do
|
|
|
5
5
|
config.cache_classes = true
|
|
6
6
|
|
|
7
7
|
# Full error reports are disabled and caching is turned on
|
|
8
|
-
config.consider_all_requests_local
|
|
8
|
+
config.consider_all_requests_local = false
|
|
9
9
|
config.action_controller.perform_caching = true
|
|
10
10
|
|
|
11
11
|
# Disable Rails's static asset server (Apache or nginx will already do this)
|
|
@@ -64,4 +64,8 @@ Dummy::Application.configure do
|
|
|
64
64
|
# Log the query plan for queries taking more than this (works
|
|
65
65
|
# with SQLite, MySQL, and PostgreSQL)
|
|
66
66
|
# config.active_record.auto_explain_threshold_in_seconds = 0.5
|
|
67
|
+
|
|
68
|
+
# Raise errors on mailer failure
|
|
69
|
+
config.action_mailer.raise_delivery_errors = true
|
|
70
|
+
|
|
67
71
|
end
|
|
@@ -27,11 +27,14 @@ Dummy::Application.configure do
|
|
|
27
27
|
# Tell Action Mailer not to deliver emails to the real world.
|
|
28
28
|
# The :test delivery method accumulates sent emails in the
|
|
29
29
|
# ActionMailer::Base.deliveries array.
|
|
30
|
-
config.action_mailer.delivery_method = :
|
|
30
|
+
config.action_mailer.delivery_method = :smtp
|
|
31
31
|
|
|
32
32
|
# Raise exception on mass assignment protection for Active Record models
|
|
33
33
|
config.active_record.mass_assignment_sanitizer = :strict
|
|
34
34
|
|
|
35
35
|
# Print deprecation notices to the stderr
|
|
36
36
|
config.active_support.deprecation = :stderr
|
|
37
|
+
|
|
38
|
+
# Raise errors on mailer failure
|
|
39
|
+
config.action_mailer.raise_delivery_errors = true
|
|
37
40
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# MailSpy configuration
|
|
2
|
+
# tracking_host: The hostname and port (if necessary) of the server MailSpy
|
|
3
|
+
# will make requests to for tracking.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
MailSpy.configure do |config|
|
|
7
|
+
config.tracker_host = "localhost:5000"
|
|
8
|
+
config.aws_access_key_id = "AKIAIF6DRMAH3BR47AZQ"
|
|
9
|
+
config.aws_secret_access_key = "iy3wBOLSX0a7clbfYPSqvDprVnUzQURokZUazMpZ"
|
|
10
|
+
config.aws_campaign_bucket = "daviacalendar-mailspy"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# MailSpy allows you to add multiple email service providers simply
|
|
15
|
+
# call this method and specify the following options
|
|
16
|
+
#
|
|
17
|
+
# :name (must be unique for all esps you add)
|
|
18
|
+
# :via (stmp or sendmail)
|
|
19
|
+
#
|
|
20
|
+
# SMTP OPTIONS
|
|
21
|
+
# :address,
|
|
22
|
+
# :port,
|
|
23
|
+
# :authentication,
|
|
24
|
+
# :user_name,
|
|
25
|
+
# :password,
|
|
26
|
+
# :domain,
|
|
27
|
+
# :enable_starttls_auto
|
|
28
|
+
|
|
29
|
+
MailSpy.add_email_service_provider do |esp|
|
|
30
|
+
esp.name = "sendgrid"
|
|
31
|
+
esp.address = "smtp.sendgrid.net"
|
|
32
|
+
esp.port = "587"
|
|
33
|
+
esp.domain = "herokuapp.com"
|
|
34
|
+
esp.user_name = "app2602840@heroku.com"
|
|
35
|
+
esp.password = "qb6qnim0"
|
|
36
|
+
esp.authentication = :plain
|
|
37
|
+
esp.enable_starttls_auto = true
|
|
38
|
+
esp.options = {
|
|
39
|
+
:enable_sendgrid_event_tracking => true,
|
|
40
|
+
:enable_auto_google_analytics => true
|
|
41
|
+
}
|
|
42
|
+
end
|
data/test/dummy/config/routes.rb
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
# This file is auto-generated from the current state of the database. Instead
|
|
3
|
+
# of editing this file, please use the migrations feature of Active Record to
|
|
4
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
|
5
|
+
#
|
|
6
|
+
# Note that this schema.rb definition is the authoritative source for your
|
|
7
|
+
# database schema. If you need to create the application database on another
|
|
8
|
+
# system, you should be using db:schema:load, not running all the migrations
|
|
9
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
|
10
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
|
11
|
+
#
|
|
12
|
+
# It's strongly recommended to check this file into your version control system.
|
|
13
|
+
|
|
14
|
+
ActiveRecord::Schema.define(:version => 0) do
|
|
15
|
+
|
|
16
|
+
end
|