msg 0.1.0
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/MIT-LICENSE +20 -0
- data/README.md +9 -0
- data/Rakefile +35 -0
- data/app/assets/images/msg/message.png +0 -0
- data/app/assets/images/msg/ping.png +0 -0
- data/app/assets/images/msg/sending.png +0 -0
- data/app/assets/javascripts/lib/jquery.peity.js +261 -0
- data/app/assets/javascripts/msg.js.coffee +6 -0
- data/app/assets/stylesheets/msg.css.sass +218 -0
- data/app/controllers/msg/bounces_controller.rb +67 -0
- data/app/controllers/msg/engine_controller.rb +23 -0
- data/app/controllers/msg/envelopes_controller.rb +22 -0
- data/app/controllers/msg/messages_controller.rb +85 -0
- data/app/controllers/msg/sendings_controller.rb +58 -0
- data/app/helpers/msg/msg_helper.rb +40 -0
- data/app/mailers/msg/devise_mailer.rb +20 -0
- data/app/mailers/msg/msg_mailer.rb +23 -0
- data/app/models/msg/bounce.rb +19 -0
- data/app/models/msg/envelope.rb +56 -0
- data/app/models/msg/message.rb +26 -0
- data/app/models/msg/sending.rb +54 -0
- data/app/views/layouts/msg/default.html.haml +30 -0
- data/app/views/layouts/msg/email.html.haml +10 -0
- data/app/views/msg/bounces/_listing.html.haml +5 -0
- data/app/views/msg/bounces/_report.html.haml +10 -0
- data/app/views/msg/bounces/index.html.haml +5 -0
- data/app/views/msg/devise_mailer/confirmation_instructions.en.html.haml +1 -0
- data/app/views/msg/devise_mailer/reset_password_instructions.en.html.haml +1 -0
- data/app/views/msg/envelopes/_listing.html.haml +5 -0
- data/app/views/msg/messages/_action_menu.html.haml +10 -0
- data/app/views/msg/messages/_form.html.haml +38 -0
- data/app/views/msg/messages/_instructions.html.haml +28 -0
- data/app/views/msg/messages/_message.html.haml +11 -0
- data/app/views/msg/messages/_saved_messages.html.haml +2 -0
- data/app/views/msg/messages/_transactional_messages.html.haml +3 -0
- data/app/views/msg/messages/edit.html.haml +7 -0
- data/app/views/msg/messages/index.html.haml +23 -0
- data/app/views/msg/messages/new.html.haml +7 -0
- data/app/views/msg/messages/preview.html.haml +4 -0
- data/app/views/msg/messages/show.html.haml +25 -0
- data/app/views/msg/msg_mailer/message_in_envelope.html.haml +2 -0
- data/app/views/msg/sendings/_form.html.haml +45 -0
- data/app/views/msg/sendings/_listing.html.haml +17 -0
- data/app/views/msg/sendings/_report.html.haml +8 -0
- data/app/views/msg/sendings/_sending.html.haml +22 -0
- data/app/views/msg/sendings/_sendings.html.haml +2 -0
- data/app/views/msg/sendings/index.html.haml +8 -0
- data/app/views/msg/sendings/new.html.haml +9 -0
- data/app/views/msg/sendings/review.html.haml +2 -0
- data/app/views/msg/sendings/show.html.haml +50 -0
- data/app/views/msg/shared/_toolbar.html.haml +17 -0
- data/config/cucumber.yml +8 -0
- data/config/locales/en.yml +76 -0
- data/config/routes.rb +26 -0
- data/db/migrate/20130320141926_msg_data.rb +54 -0
- data/db/migrate/20130327134754_sending_circumstances.rb +6 -0
- data/lib/msg.rb +88 -0
- data/lib/msg/engine.rb +23 -0
- data/lib/msg/receivers.rb +64 -0
- data/lib/msg/version.rb +3 -0
- data/lib/tasks/cucumber.rake +65 -0
- data/lib/tasks/msg_tasks.rake +4 -0
- data/spec/controllers/msg/bounces_controller_spec.rb +48 -0
- data/spec/controllers/msg/envelopes_controller_spec.rb +22 -0
- data/spec/controllers/msg/messages_controller_spec.rb +9 -0
- data/spec/controllers/msg/sendings_controller_spec.rb +47 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/user.rb +14 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +59 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +41 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/devise.rb +285 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +9 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/2013032210540_users.rb +49 -0
- data/spec/dummy/db/migrate/20130326093048_msg_data.msg.rb +55 -0
- data/spec/dummy/db/schema.rb +96 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +143 -0
- data/spec/dummy/log/test.log +1022 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/factories/msg/bounces.rb +9 -0
- data/spec/factories/msg/envelopes.rb +10 -0
- data/spec/factories/msg/messages.rb +10 -0
- data/spec/factories/msg/sendings.rb +7 -0
- data/spec/factories/users.rb +11 -0
- data/spec/helpers/msg/msg_helper_spec.rb +17 -0
- data/spec/lib/msg_spec.rb +15 -0
- data/spec/mailers/msg/devise_mailer_spec.rb +21 -0
- data/spec/mailers/msg/msg_mailer_spec.rb +36 -0
- data/spec/models/msg/bounce_spec.rb +19 -0
- data/spec/models/msg/envelope_spec.rb +62 -0
- data/spec/models/msg/message_spec.rb +19 -0
- data/spec/models/msg/sending_spec.rb +10 -0
- data/spec/routing/sendings_routing_spec.rb +31 -0
- data/spec/spec_helper.rb +70 -0
- metadata +446 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require 'open-uri'
|
|
2
|
+
|
|
3
|
+
module Msg
|
|
4
|
+
class BouncesController < Msg::EngineController
|
|
5
|
+
respond_to :json
|
|
6
|
+
|
|
7
|
+
# this is pinged from Amazon SNS with a JSON body something like this:
|
|
8
|
+
#
|
|
9
|
+
# {
|
|
10
|
+
# "notificationType":"Bounce",
|
|
11
|
+
# "bounce":{
|
|
12
|
+
# "bounceType":"Permanent",
|
|
13
|
+
# "reportingMTA":"dns; email.example.com",
|
|
14
|
+
# "bouncedRecipients":[
|
|
15
|
+
# {
|
|
16
|
+
# "emailAddress":"username@example.com",
|
|
17
|
+
# "status":"5.1.1",
|
|
18
|
+
# "action":"failed",
|
|
19
|
+
# "diagnosticCode":"smtp; 550 5.1.1 <username@example.com>... User"
|
|
20
|
+
# }
|
|
21
|
+
# ],
|
|
22
|
+
# "bounceSubType":"General",
|
|
23
|
+
# "timestamp":"2012-06-19T01:07:52.000Z",
|
|
24
|
+
# "feedbackId":"00000138111222aa-33322211-cccc-cccc-cccc-ddddaaaa068a-000000"
|
|
25
|
+
# },
|
|
26
|
+
# "mail":{
|
|
27
|
+
# "timestamp":"2012-06-19T01:05:45.000Z",
|
|
28
|
+
# "source":"sender@example.com",
|
|
29
|
+
# "messageId":"00000138111222aa-33322211-cccc-cccc-cccc-ddddaaaa0680-000000",
|
|
30
|
+
# "destination":[
|
|
31
|
+
# "username@example.com"
|
|
32
|
+
# ]
|
|
33
|
+
# }
|
|
34
|
+
# }
|
|
35
|
+
#
|
|
36
|
+
#
|
|
37
|
+
# When the subscription is first set up they will also POST us a SubscriptionConfirmation,
|
|
38
|
+
# upon which we have to GET the given url to confirm that we can follow orders.
|
|
39
|
+
#
|
|
40
|
+
def create
|
|
41
|
+
if request.headers['x-amz-sns-message-type'] == 'SubscriptionConfirmation'
|
|
42
|
+
response = HTTParty.get(params['SubscribeURL'])
|
|
43
|
+
head :ok
|
|
44
|
+
|
|
45
|
+
else
|
|
46
|
+
mail = params['Message']['mail']
|
|
47
|
+
bounce = params['Message']['bounce']
|
|
48
|
+
if envelope = Msg::Envelope.find_by_email_id(mail['messageId'])
|
|
49
|
+
envelope.bounces.create({
|
|
50
|
+
:bounce_type => bounce['bounceType'],
|
|
51
|
+
:bounce_subtype => bounce['bounceSubType'],
|
|
52
|
+
:reporter => bounce['reportingMTA'],
|
|
53
|
+
:raw_message => JSON.dump(bounce)
|
|
54
|
+
})
|
|
55
|
+
end
|
|
56
|
+
head :ok
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
protected
|
|
61
|
+
|
|
62
|
+
def build_bounce
|
|
63
|
+
@bounce = Msg::Bounce.new(params[:bounce])
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Msg
|
|
2
|
+
class EngineController < ::ApplicationController
|
|
3
|
+
|
|
4
|
+
before_filter :authenticate_user!
|
|
5
|
+
before_filter :require_admin!
|
|
6
|
+
layout :no_layout_if_pjax
|
|
7
|
+
|
|
8
|
+
protected
|
|
9
|
+
|
|
10
|
+
def require_admin!
|
|
11
|
+
raise Msg::PermissionDenied unless current_user.admin?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def no_layout_if_pjax
|
|
15
|
+
if request.headers['X-PJAX']
|
|
16
|
+
false
|
|
17
|
+
else
|
|
18
|
+
Msg.layout
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Msg
|
|
2
|
+
class EnvelopesController < Msg::EngineController
|
|
3
|
+
respond_to :png
|
|
4
|
+
skip_before_filter :authenticate_user!, :only => [:show]
|
|
5
|
+
skip_before_filter :require_admin!, :only => [:show]
|
|
6
|
+
before_filter :get_envelope, :only => [:show]
|
|
7
|
+
|
|
8
|
+
# envelopes#show is pinged when a message is opened.
|
|
9
|
+
#
|
|
10
|
+
def show
|
|
11
|
+
@envelope.open!
|
|
12
|
+
redirect_to view_context.image_path('msg/ping.png')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
protected
|
|
16
|
+
|
|
17
|
+
def get_envelope
|
|
18
|
+
@envelope = Msg::Envelope.find(params[:id])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Msg
|
|
2
|
+
class MessagesController < Msg::EngineController
|
|
3
|
+
respond_to :html, :js
|
|
4
|
+
|
|
5
|
+
before_filter :get_message, :only => [:show, :edit, :update, :destroy, :preview]
|
|
6
|
+
before_filter :build_message, :only => [:new, :create]
|
|
7
|
+
before_filter :get_transactional_messages, :only => [:index, :transactional]
|
|
8
|
+
before_filter :get_saved_messages, :only => [:index, :saved]
|
|
9
|
+
before_filter :get_receiver, :only => [:show, :preview]
|
|
10
|
+
|
|
11
|
+
def index
|
|
12
|
+
respond_with @saved_messages, @transactional_messages
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def saved
|
|
16
|
+
respond_with @saved_messages
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def transactional
|
|
20
|
+
respond_with @transactional_messages
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def show
|
|
24
|
+
respond_with @message
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def preview
|
|
28
|
+
respond_with @message, :layout => Msg.email_layout
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def new
|
|
32
|
+
respond_with @message
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create
|
|
36
|
+
@message.update_attributes(params[:message])
|
|
37
|
+
respond_with @message
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def edit
|
|
41
|
+
respond_with @message
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def update
|
|
45
|
+
@message.update_attributes(params[:message])
|
|
46
|
+
respond_with @message
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def destroy
|
|
50
|
+
@message.destroy
|
|
51
|
+
head :ok
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
protected
|
|
55
|
+
|
|
56
|
+
def build_message
|
|
57
|
+
@message = Msg::Message.new(params[:message])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def get_message
|
|
61
|
+
@message = Msg::Message.find(params[:id])
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def get_transactional_messages
|
|
65
|
+
@transactional_messages = Msg::Message.transactional
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def get_saved_messages
|
|
69
|
+
@show = params[:show] || 10
|
|
70
|
+
@page = params[:page] || 1
|
|
71
|
+
@saved_messages = Msg::Message.saved.page(@page).per(@show)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def get_receiver
|
|
75
|
+
klass = params[:receiver_type] || Msg.receiving_classes.first
|
|
76
|
+
if id = params[:receiver_id]
|
|
77
|
+
@receiver = klass.classify.constantize.find(id)
|
|
78
|
+
else
|
|
79
|
+
@receiver = klass.classify.constantize.first
|
|
80
|
+
end
|
|
81
|
+
raise ActiveRecord::RecordNotFound, "Cannot find a valid receiver for whom to preview message." unless @receiver
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Msg
|
|
2
|
+
class SendingsController < Msg::EngineController
|
|
3
|
+
respond_to :html, :js
|
|
4
|
+
|
|
5
|
+
before_filter :get_message, :only => [:new, :create]
|
|
6
|
+
before_filter :get_sending, :only => [:show, :review]
|
|
7
|
+
before_filter :build_sending, :only => [:new, :create]
|
|
8
|
+
before_filter :get_sendings, :only => [:index]
|
|
9
|
+
|
|
10
|
+
def index
|
|
11
|
+
respond_with @sendings do |format|
|
|
12
|
+
format.js { render :partial => "report"}
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def show
|
|
17
|
+
respond_with @sending
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def review
|
|
21
|
+
respond_with @sending, :layout => Msg.email_layout
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def new
|
|
25
|
+
respond_with @sending
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def create
|
|
29
|
+
@sending.update_attributes(params[:sending])
|
|
30
|
+
respond_with @sending
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def destroy
|
|
34
|
+
@sending.destroy
|
|
35
|
+
head :ok
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
protected
|
|
39
|
+
|
|
40
|
+
def get_message
|
|
41
|
+
@message = Msg::Message.find(params[:message_id])
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def build_sending
|
|
45
|
+
@sending = @message.sendings.build
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def get_sending
|
|
49
|
+
@sending = Msg::Sending.find(params[:id])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def get_sendings
|
|
53
|
+
@show = params[:show] || 10
|
|
54
|
+
@page = params[:page] || 1
|
|
55
|
+
@sendings = Msg::Sending.order("created_at DESC").page(@page).per(@show)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Msg
|
|
2
|
+
module MsgHelper
|
|
3
|
+
def shorten(text, length=64)
|
|
4
|
+
length = length[:length] if length.is_a?(Hash)
|
|
5
|
+
truncate(strip_tags(text), {:length => length, :separator => " "})
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def action_menulink(thing, html_options={})
|
|
9
|
+
classname = thing.class.to_s.downcase.underscore.split('/').last
|
|
10
|
+
html_options.reverse_merge!({
|
|
11
|
+
:class => "",
|
|
12
|
+
:data => {:menu => "#{classname}_#{thing.id}"}
|
|
13
|
+
})
|
|
14
|
+
html_options[:class] << " menu"
|
|
15
|
+
link_to t(:edit), "#", html_options if editable?(thing)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def action_menu(thing, locals={})
|
|
19
|
+
if editable?(thing)
|
|
20
|
+
type = thing.class.to_s.downcase.underscore
|
|
21
|
+
classname = type.split('/').last
|
|
22
|
+
locals[classname.to_sym] = thing
|
|
23
|
+
render :partial => "#{type.pluralize}/action_menu", :locals => locals
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def visible?(thing)
|
|
28
|
+
admin? || privileged? || current_user.can_see?(thing)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def editable?(thing)
|
|
32
|
+
admin? || current_user == thing.created_by
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def deletable?(thing)
|
|
36
|
+
admin? || current_user == thing.created_by
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Msg
|
|
2
|
+
class DeviseMailer < Devise::Mailer
|
|
3
|
+
default from: Msg.default_from_address, :bcc => Msg.email_bcc
|
|
4
|
+
layout Msg.email_layout
|
|
5
|
+
|
|
6
|
+
def confirmation_instructions(receiver, opts={})
|
|
7
|
+
@message = Msg::Message.find_by_function('confirmation_instructions')
|
|
8
|
+
@contents = @message.render_for(receiver)
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def reset_password_instructions(receiver, opts={})
|
|
13
|
+
@message = Msg::Message.find_by_function('reset_password_instructions')
|
|
14
|
+
@contents = @message.render_for(receiver)
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Msg
|
|
2
|
+
class MsgMailer < ActionMailer::Base
|
|
3
|
+
default from: Msg.default_from, :bcc => Msg.email_bcc
|
|
4
|
+
layout Msg.email_layout
|
|
5
|
+
|
|
6
|
+
# we send all messages through SES and use SNS to notify of bounces, so all the
|
|
7
|
+
# return-path and VERP and other gubbins that we would normally need can be omitted
|
|
8
|
+
#
|
|
9
|
+
def message_in_envelope(envelope)
|
|
10
|
+
headers['Message-ID'] = envelope.email_id
|
|
11
|
+
@envelope = envelope
|
|
12
|
+
@message = envelope.message
|
|
13
|
+
@receiver = envelope.receiver
|
|
14
|
+
@contents = envelope.contents
|
|
15
|
+
mail({
|
|
16
|
+
:to => %{"#{@receiver.name}" <#{@receiver.email}>},
|
|
17
|
+
:from => envelope.from_address,
|
|
18
|
+
:subject => envelope.subject
|
|
19
|
+
})
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Msg
|
|
2
|
+
class Bounce < ActiveRecord::Base
|
|
3
|
+
attr_accessible :envelope, :bounce_type, :bounce_subtype, :raw_message, :reporter
|
|
4
|
+
belongs_to :envelope
|
|
5
|
+
validates :envelope, :presence => true
|
|
6
|
+
|
|
7
|
+
scope :fatal, where(:bounce_type => "Permanent")
|
|
8
|
+
scope :transient, where(:bounce_type => "Transient")
|
|
9
|
+
|
|
10
|
+
def fatal?
|
|
11
|
+
bounce_type == 'Permanent'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def transient?
|
|
15
|
+
bounce_type == 'Transient'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Msg
|
|
2
|
+
class Envelope < ActiveRecord::Base
|
|
3
|
+
attr_accessible :receiver, :sending
|
|
4
|
+
|
|
5
|
+
belongs_to :sending
|
|
6
|
+
belongs_to :receiver, :polymorphic => true
|
|
7
|
+
has_many :bounces
|
|
8
|
+
|
|
9
|
+
validates :receiver, :presence => true
|
|
10
|
+
validates :sending, :presence => true
|
|
11
|
+
after_create :send_email
|
|
12
|
+
|
|
13
|
+
scope :opened, where("opened_at IS NOT NULL")
|
|
14
|
+
|
|
15
|
+
scope :bounced, select("msg_envelopes.*")
|
|
16
|
+
.joins("INNER JOIN msg_bounces as mb ON mb.envelope_id = msg_envelopes.id")
|
|
17
|
+
.having("count(mb.id) > 0")
|
|
18
|
+
|
|
19
|
+
def message
|
|
20
|
+
sending.message
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def open!
|
|
24
|
+
update_column(:opened_at, Time.now)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def status
|
|
28
|
+
if opened_at?
|
|
29
|
+
"read"
|
|
30
|
+
elsif bounces.any?
|
|
31
|
+
"bounced"
|
|
32
|
+
else
|
|
33
|
+
"unread"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
protected
|
|
38
|
+
|
|
39
|
+
def send_email
|
|
40
|
+
# Record sending parameters for later reference. The message or receiver properties might change after this is sent.
|
|
41
|
+
self.email_id = "#{self.id}@#{Msg.sending_domain}"
|
|
42
|
+
self.subject = message.subject
|
|
43
|
+
self.from_address = message.from
|
|
44
|
+
self.to_address = receiver.email
|
|
45
|
+
# render the message for our receiver
|
|
46
|
+
self.contents = message.render_for(receiver)
|
|
47
|
+
# send it
|
|
48
|
+
Msg::MsgMailer.message_in_envelope(self).deliver
|
|
49
|
+
# note sending time
|
|
50
|
+
self.sent_at = Time.now
|
|
51
|
+
# and save our changes
|
|
52
|
+
self.save!
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'mustache'
|
|
2
|
+
|
|
3
|
+
module Msg
|
|
4
|
+
class Message < ActiveRecord::Base
|
|
5
|
+
attr_accessible :subject, :body, :function, :description, :transactional, :saved, :from_name, :from_address
|
|
6
|
+
|
|
7
|
+
has_many :sendings
|
|
8
|
+
belongs_to :created_by, :class_name => Msg.user_class
|
|
9
|
+
|
|
10
|
+
scope :transactional, where(:transactional => true)
|
|
11
|
+
scope :saved, where(:saved => true, :transactional => false)
|
|
12
|
+
scope :unsaved, where(:saved => false)
|
|
13
|
+
|
|
14
|
+
def from
|
|
15
|
+
name = from_name? ? from_name : Msg.default_from_name
|
|
16
|
+
address = from_address? ? from_address : Msg.default_from_address
|
|
17
|
+
"#{name} <#{address}>"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render_for(receiver)
|
|
21
|
+
values = receiver.for_email.reverse_merge(Msg.email_values)
|
|
22
|
+
Mustache.render(body, values)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|