Permalink
Browse files

Got basic delivery actions working

  • Loading branch information...
1 parent 2b60bad commit fb1b4e54ab34aaa7ecc3ca1aa65c565a3f49c2f3 @mikel committed Sep 5, 2009
Showing with 2,544 additions and 217 deletions.
  1. +15 −11 app/controllers/application_controller.rb
  2. +5 −0 app/models/addressee.rb
  3. +7 −0 app/models/delivery.rb
  4. +4 −0 app/models/group.rb
  5. +44 −0 app/models/mailer.rb
  6. +36 −1 app/models/mailout.rb
  7. +4 −4 app/models/message.rb
  8. +11 −0 app/models/recipient.rb
  9. +6 −0 app/models/sender.rb
  10. +5 −0 app/models/subscription.rb
  11. +3 −0 app/models/user.rb
  12. +3 −33 app/views/home/index.html.erb
  13. +22 −0 app/views/layouts/_head.html.erb
  14. +37 −0 app/views/layouts/_header.html.erb
  15. +23 −6 app/views/layouts/application.html.erb
  16. 0 app/views/mailer/mailout.erb
  17. +4 −0 app/views/mailouts/new.html.erb
  18. +1 −1 app/views/user_sessions/new.html.erb
  19. +6 −0 config/environment.rb
  20. +3 −3 config/environments/test.rb
  21. +1 −0 db/migrate/20090502124658_create_users.rb
  22. +3 −0 db/migrate/20090519081741_create_recipients.rb
  23. +0 −11 db/migrate/20090520090847_add_organization_to_users_and_recipients.rb
  24. +0 −9 db/migrate/20090521234537_add_state_to_recipients.rb
  25. +2 −1 db/migrate/20090530112907_create_messages.rb
  26. +1 −1 db/migrate/20090606215023_create_addressees.rb
  27. +0 −11 db/migrate/20090610221548_add_scheduled_time_and_date_sent_to_messages.rb
  28. +0 −11 db/migrate/20090726030640_change_state_columns_to_aasm_states.rb
  29. +4 −14 db/migrate/20090729011053_create_mailouts.rb
  30. +18 −0 db/migrate/20090802124038_create_deliveries.rb
  31. +16 −0 db/migrate/20090905122340_create_senders.rb
  32. +28 −8 db/schema.rb
  33. +1,501 −0 doc/ERD.graffle
  34. +1 −1 features/manage_email_templates.feature
  35. +1 −0 features/manage_groups.feature
  36. +2 −0 features/manage_mailouts.feature
  37. +2 −0 features/manage_recipients.feature
  38. +47 −0 features/recipient_viewing_tracking.feature
  39. +43 −0 features/recipients_view_email_in_browser.feature
  40. +58 −0 features/sending_mailout.feature
  41. +5 −0 features/step_definitions/manage_mailouts_steps.rb
  42. +70 −0 features/step_definitions/sending_mailout_steps.rb
  43. +0 −1 features/support/env.rb
  44. +23 −0 features/unsubscribe.feature
  45. +30 −13 lib/tasks/rspec.rake
  46. 0 public/stylesheets/buttons.css
  47. 0 public/stylesheets/content.css
  48. 0 public/stylesheets/defaults.css
  49. 0 public/stylesheets/footer.css
  50. 0 public/stylesheets/forms.css
  51. +3 −0 public/stylesheets/header.css
  52. +6 −0 public/stylesheets/layouts.css
  53. 0 public/stylesheets/sass/buttons.sass
  54. 0 public/stylesheets/sass/content.sass
  55. 0 public/stylesheets/sass/defaults.sass
  56. 0 public/stylesheets/sass/footer.sass
  57. 0 public/stylesheets/sass/forms.sass
  58. +5 −0 public/stylesheets/sass/header.sass
  59. +5 −0 public/stylesheets/sass/layouts.sass
  60. 0 public/stylesheets/sass/sidebar.sass
  61. 0 public/stylesheets/sass/tables.sass
  62. 0 public/stylesheets/sidebar.css
  63. +7 −0 public/stylesheets/static/reset-fonts-grids.css
  64. 0 public/stylesheets/tables.css
  65. +3 −0 spec/factories/sender_factory.rb
  66. +7 −0 spec/fixtures/deliveries.yml
  67. +13 −0 spec/fixtures/senders.yml
  68. +36 −0 spec/models/delivery_spec.rb
  69. +25 −0 spec/models/mailer_spec.rb
  70. +143 −0 spec/models/mailout_spec.rb
  71. +7 −0 spec/models/message_spec.rb
  72. +37 −1 spec/models/recipient_spec.rb
  73. +33 −0 spec/models/sender_spec.rb
  74. +9 −0 spec/models/user_spec.rb
  75. +1 −1 spec/rcov.opts
  76. +1 −1 spec/resources
  77. +1 −0 spec/spec.opts
  78. +80 −47 spec/spec_helper.rb
  79. +16 −16 vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb
  80. +8 −8 vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb
  81. +1 −1 vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb
  82. +2 −2 vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb
@@ -5,9 +5,23 @@ class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details
- helper_method :current_user_session, :current_user
+ helper_method :current_user_session, :current_user, :current_user_admin?
filter_parameter_logging :password, :password_confirmation
+ def current_user
+ return @current_user if defined?(@current_user)
+ @current_user = current_user_session && current_user_session.record
+ end
+
+ def current_user_admin?
+ current_user && current_user.admin?
+ end
+
+ def current_user_session
+ return @current_user_session if defined?(@current_user_session)
+ @current_user_session = UserSession.find
+ end
+
private
def admin_only
@@ -18,16 +32,6 @@ def admin_only
end
end
- def current_user_session
- return @current_user_session if defined?(@current_user_session)
- @current_user_session = UserSession.find
- end
-
- def current_user
- return @current_user if defined?(@current_user)
- @current_user = current_user_session && current_user_session.record
- end
-
def require_user
unless current_user
store_location
View
@@ -1,3 +1,8 @@
+# The Addressee class is an explicit join table between Mailout, Group & Address
+#
+# This way a mailout can have one or more groups and one or more recipients,
+#
+# When a group is added to a mailout
class Addressee < ActiveRecord::Base
belongs_to :group
View
@@ -0,0 +1,7 @@
+class Delivery < ActiveRecord::Base
+
+ belongs_to :recipient
+ belongs_to :user
+ belongs_to :mailout
+
+end
View
@@ -1,3 +1,7 @@
+# A Group is a collection of recipients (through the subscriptions class)
+# this is a many to many association, additionally it has many mailouts,
+# that is, mailouts that have been sent to this group, through a many to many
+# association via addressee.
class Group < ActiveRecord::Base
belongs_to :user
View
@@ -0,0 +1,44 @@
+class Mailer < ActionMailer::Base
+
+ def self.send!
+ scheduled_mailouts.each do |mailout|
+ send_mailout(mailout)
+ end
+ end
+
+ private
+
+ def self.send_mailout(mailout)
+ mailout.subscribers.each do |recipient|
+ deliver_mailout(recipient, mailout)
+ Delivery.create!(:recipient_id => recipient.id,
+ :mailout_id => mailout.id,
+ :sent_at => Time.now,
+ :user_id => mailout.user_id)
+ end
+ mailout.next!
+ end
+
+ def mailout(recipient, mailout)
+ recipients recipient.email
+ subject mailout.title
+ from mailout.from
+ reply_to mailout.reply_to
+ if mailout.multipart?
+ content_type "multipart/alternative"
+ part "text/html" do |p|
+ p.body = render_message("welcome_email_html", :message => mailout.html_part)
+ end
+ part "text/plain" do |p|
+ p.body = render_message("welcome_email_plain", :message => mailout.plain_part)
+ end
+ else
+ body = mailout.plain_part
+ end
+ end
+
+ def self.scheduled_mailouts
+ Mailout.ready_to_send
+ end
+
+end
View
@@ -8,16 +8,19 @@ class Mailout < ActiveRecord::Base
aasm_state :schedule_mailout
aasm_state :confirm_mailout
aasm_state :confirmed
+ aasm_state :sent
aasm_event :next do
transitions :to => :select_recipients, :from => [:new]
transitions :to => :schedule_mailout, :from => [:select_recipients],
:guard => Proc.new { |m| m.must_have_recipients }
transitions :to => :confirm_mailout, :from => [:schedule_mailout]
transitions :to => :confirmed, :from => [:confirm_mailout]
+ transitions :to => :sent, :from => [:confirmed]
end
aasm_event :previous do
+ transitions :to => :confirmed, :from => [:sent]
transitions :to => :confirm_mailout, :from => [:confirmed]
transitions :to => :schedule_mailout, :from => [:confirm_mailout]
transitions :to => :select_recipients, :from => [:schedule_mailout]
@@ -30,10 +33,13 @@ class Mailout < ActiveRecord::Base
belongs_to :user
belongs_to :message
+ belongs_to :sender
validates_presence_of :title
validates_presence_of :message_id, :message => "must be selected"
+ named_scope :ready_to_send, {:conditions => ["date_scheduled < ? AND aasm_state = ?", Time.now, 'confirmed']}
+
def state
aasm_state
end
@@ -65,7 +71,6 @@ def must_have_recipients
end
end
-
def add_group_id
@group_selected
end
@@ -84,6 +89,36 @@ def add_recipient=(recipient_selected)
do_add_recipients
end
+ def subscribers
+ recipient_ids = Addressee.find(:all, :select => 'recipient_id',
+ :conditions => {:mailout_id => self.id}).map { |a| a.recipient_id }
+ recipient_ids << Subscription.recipient_ids_for(self.group_ids)
+ recipient_ids.flatten!
+ recipient_ids.uniq!
+ delivered_ids = Delivery.find(:all, :conditions => {:mailout_id => self.id}, :select => 'recipient_id').map { |a| a.recipient_id}
+ Recipient.find(:all, :conditions => ['id IN (?) and id NOT IN (?)', recipient_ids, delivered_ids], :order => "domain")
+ end
+
+ def from
+ sender.from if sender
+ end
+
+ def reply_to
+ sender.reply_to if sender
+ end
+
+ def multipart?
+ message.multipart if message
+ end
+
+ def html_part
+ message.html_part if message
+ end
+
+ def plain_part
+ message.plain_part if message
+ end
+
private
def do_add_group
View
@@ -15,11 +15,11 @@ class Message < ActiveRecord::Base
aasm_event :next do
transitions :to => :edit_content, :from => [:new],
- :guard => Proc.new { |m| m.source == 'plain' }
+ :guard => Proc.new { |m| m.source == 'plain' && m.valid? }
transitions :to => :select_html, :from => [:new],
- :guard => Proc.new { |m| m.source == 'html' }
+ :guard => Proc.new { |m| m.source == 'html' && m.valid? }
transitions :to => :select_template, :from => [:new],
- :guard => Proc.new { |m| m.source == 'template' }
+ :guard => Proc.new { |m| m.source == 'template' && m.valid? }
transitions :to => :edit_content, :from => [:select_html],
:on_transition => Proc.new { |m| m.multipart = true }
transitions :to => :edit_content, :from => [:select_template],
@@ -38,7 +38,7 @@ class Message < ActiveRecord::Base
transitions :to => :new, :from => [:edit_content]
end
- validates_presence_of :title
+ validates_format_of :title, :with => /^.+$/i, :message => "can't be blank"
has_many :attachments
has_many :mailouts
View
@@ -1,3 +1,4 @@
+# Recipient is an addressee or a group that is associated with a mailout
class Recipient < ActiveRecord::Base
# Use state machine for state transitions
@@ -11,6 +12,8 @@ class Recipient < ActiveRecord::Base
belongs_to :organization
has_many :subscriptions
+ has_many :deliveries
+
has_many :groups, :through => :subscriptions
has_many :addressees
@@ -19,6 +22,14 @@ class Recipient < ActiveRecord::Base
validates_presence_of :organization_id
validates_associated :organization
+ validates_each :email do |record, attr, value|
+ record.errors.add attr, 'is invalid' unless (TMail::Address.parse(value) rescue false)
+ end
+
+ def before_save
+ self.domain = TMail::Address.parse(email.to_s).domain
+ end
+
def name
"#{given_name} #{family_name}"
end
View
@@ -0,0 +1,6 @@
+class Sender < ActiveRecord::Base
+
+ belongs_to :organization
+ has_many :mailouts
+
+end
@@ -3,4 +3,9 @@ class Subscription < ActiveRecord::Base
belongs_to :group
belongs_to :recipient
+ def self.recipient_ids_for(group_ids)
+ subscriptions = find(:all, :conditions => {:group_id => group_ids}, :select => 'recipient_id')
+ subscriptions.map { |s| s.recipient_id }
+ end
+
end
View
@@ -5,6 +5,7 @@ class User < ActiveRecord::Base
has_many :memberships
has_many :recipients, :through => :organization
has_many :roles, :through => :memberships
+ has_many :deliveries
belongs_to :organization
@@ -36,6 +37,8 @@ def admin
member_of?(:admin)
end
+ alias :admin? :admin
+
def last_admin?
@last_admin ||= !User.find(:first,
:conditions => ["users.id != ? AND roles.name = 'admin'", self.id],
@@ -1,34 +1,4 @@
+<%- content_for :sidebar do -%>
+Sidebar
+<%- end -%>
<h1>Welcome to Contact</h1>
-
-<h2>Email Templates</h2>
-
-<ul>
- <li>
- <%= link_to "Email Templates", email_templates_path %>
- </li>
-
- <li>
- <%= link_to "Recipients", recipients_path %>
- </li>
-
- <li>
- <%= link_to "Messages", messages_path %>
- </li>
-
- <li>
- <%= link_to "Groups", groups_path %>
- </li>
-
- <li>
- <%= link_to "Mailouts", mailouts_path %>
- </li>
-
- <li>
- <%- if current_user.admin -%>
- <%= link_to "Users", users_path %><br />
- <%- else -%>
- <%= link_to "Profile", edit_user_path(current_user) %><br />
- <%- end -%>
- </li>
-
-</ul>
@@ -0,0 +1,22 @@
+<meta content="text/html;charset=utf-8" http-equiv="content-type" />
+<meta content="text/css" http-equiv="Content-Style-Type" />
+
+<%= javascript_include_tag 'prototype',
+ 'effects',
+ 'controls', :cache => 'base' %>
+
+<%= javascript_include_tag 'application',
+ :cache => 'app' %>
+
+<%= stylesheet_link_tag 'static/reset-fonts-grids.css',
+ 'layouts.css',
+ 'defaults.css',
+ 'forms.css',
+ 'tables.css',
+ 'buttons.css',
+ 'header.css',
+ 'footer.css',
+ 'content.css',
+ 'sidebar.css', :cache => 'all' %>
+
+<%= stylesheet_link_tag "#{controller.controller_name}.css" %>
@@ -0,0 +1,37 @@
+<div id="head_text">
+ Contact Mailing System
+</div>
+
+<% if current_user_session %>
+ <div id="navigation">
+ <ul>
+ <li>
+ <%= link_to "Recipients", recipients_path %>
+ </li>
+
+ <li>
+ <%= link_to "Groups", groups_path %>
+ </li>
+
+ <li>
+ <%= link_to "Messages", messages_path %>
+ </li>
+
+ <li>
+ <%= link_to "Templates", email_templates_path %>
+ </li>
+
+ <li>
+ <%= link_to "Mailouts", mailouts_path %>
+ </li>
+
+ <li>
+ <%- if current_user_admin? -%>
+ <%= link_to "Users", users_path %><br />
+ <%- else -%>
+ <%= link_to "Profile", edit_user_path(current_user) %><br />
+ <%- end -%>
+ </li>
+ </ul>
+ </div>
+<%- end -%>
Oops, something went wrong.

0 comments on commit fb1b4e5

Please sign in to comment.