adding error emails and invitation mailing

git-svn-id: http://svn.barleysodas.com/barleysodas/trunk@126 0f7b21a7-9e3a-4941-bbeb-ce5c7c368fa7
master
andrew 2008-02-03 20:16:53 +00:00
parent 52c36c3891
commit 11e3356f55
28 changed files with 340 additions and 10 deletions

View File

@ -127,6 +127,23 @@ class ApplicationController < ActionController::Base
true true
end end
##
# Log the error and mail it out if the request is not a local request.
# Usually only used in production mode.
#
def log_error(exception)
super(exception)
begin
unless local_request? or RAILS_ENV == 'development' or
exception.class == ActiveRecord::RecordNotFound
ErrorMailer.deliver_snapshot(exception, clean_backtrace(exception),
session.instance_variable_get("@data"), params, request.env)
end
rescue => e
logger.info e.to_s
end
end
private private
## ##

View File

@ -0,0 +1,63 @@
class InvitationsController < ApplicationController
append_before_filter :ensure_xhr, :only => [ :create, :send ]
# GET /invitations
# GET /invitations.xml
def index
@invitations = Invitation.find(:all, :include => [ 'people' ],
:order => 'peoples.title ASC')
respond_to do |format|
format.html # index.rhtml
format.xml { render :xml => @invitations.to_xml }
end
end
# POST /invitations
def create
@invitation = Invitation.new
@invitation.people_id = params[:people_id].to_i if params[:people_id]
@people = @invitation.people
if @invitation.save
render :partial => 'invitations/invitations'
else
render :inline => "<%= error_messages_for('invitation') -%>",
:status => 500
end
end
# DELETE /invitations/1
# DELETE /invitations/1.xml
def destroy
@invitation = Invitation.find(params[:id])
@invitation.destroy
respond_to do |format|
format.html { redirect_to invitations_url }
format.xml { head :ok }
end
end
def send_invitation
@invitation = Invitation.find(:first, :include => [ 'people' ],
:conditions => [ "people_id = ?", session[:people_id] ])
@invitation ||= Invitation.new :people_id => session[:people_id]
@people = @invitation.people
recipient = params[:email].to_s
begin
if @invitation.new_record?
@invitation.errors.add(:you, "have no invitations")
end
if recipient.to_s.empty?
@invitation.errors.add(:recipient, "is missing")
end
raise "Misconfigured invitation!" unless @invitation.errors.empty?
InvitationMailer.deliver_invitation(@invitation, recipient)
rescue
logger.info("Failed to deliver invitation: #{$!}")
render :inline => "<%= error_messages_for('invitation') -%>",
:status => 500
return
end
@invitation.toggle! :sent
render :partial => 'invitations/invitations'
end
end

View File

@ -50,8 +50,10 @@ class PeoplesController < ApplicationController
set_people_role set_people_role
@page = Page.new(params[:page]) @page = Page.new(params[:page])
@people.page = @page @people.page = @page
invitation = Invitation.find_by_code(params[:code])
respond_to do |format| respond_to do |format|
if @people.save if invitation and @people.save
invitation.destroy
flash[:notice] = 'People was successfully created.' flash[:notice] = 'People was successfully created.'
format.html { redirect_to people_url(@people.page.title_for_url) } format.html { redirect_to people_url(@people.page.title_for_url) }
format.xml { head :created, format.xml { head :created,

View File

@ -116,7 +116,6 @@ module ApplicationHelper
# Renders everything you need to display the TagImage browser. # Renders everything you need to display the TagImage browser.
# #
def tagged_image_browser(obj) def tagged_image_browser(obj)
javascript_include_tag('control.modal.js') +
render(:partial => 'shared/tagged_image_browser', render(:partial => 'shared/tagged_image_browser',
:locals => { :obj => obj }) :locals => { :obj => obj })
end end

View File

@ -0,0 +1,2 @@
module InvitationsHelper
end

View File

@ -0,0 +1,14 @@
class ErrorMailer < ActionMailer::Base
def snapshot(exception, trace, session, params, env)
@headers["Content-Type"] = "text/plain"
@recipients = 'penguincoder@gmail.com'
@from = 'BarleySodas <error@barleysodas.com>'
@subject = "[BarleySodas Exception: #{env['REQUEST_URI']}]"
@sent_on = Time.now
@body["exception"] = exception
@body["trace"] = trace
@body["session"] = session
@body["params"] = params
@body["env"] = env
end
end

17
app/models/invitation.rb Normal file
View File

@ -0,0 +1,17 @@
class Invitation < ActiveRecord::Base
belongs_to :people
validates_presence_of :people_id
before_create :set_invitation_code
protected
def set_invitation_code
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
check = nil
begin
check = ''
1.upto(32) { |k| check << chars[rand(chars.size - 1)] }
end while Invitation.find_by_code(check)
self.code = check
end
end

View File

@ -0,0 +1,10 @@
class InvitationMailer < ActionMailer::Base
def invitation(invitation, recipient)
@headers["Content-Type"] = "text/plain"
@recipients = recipient
@from = 'BarleySodas <no-reply@barleysodas.com>'
@subject = "You've been invited to BarleySodas!"
@body["invitation"] = invitation
@body["people_title"] = invitation.people.title
end
end

View File

@ -14,6 +14,8 @@ class People < ActiveRecord::Base
has_many :actual_friends, :through => :friends, :source => :destination has_many :actual_friends, :through => :friends, :source => :destination
has_many :experiences, :dependent => :destroy has_many :experiences, :dependent => :destroy
has_many :beers, :through => :experiences has_many :beers, :through => :experiences
has_many :invitations, :dependent => :destroy,
:conditions => [ 'sent IS NULL or sent = ?', false ]
make_authenticatable make_authenticatable
validates_length_of :password, :minimum => 8, :if => :password_required?, validates_length_of :password, :minimum => 8, :if => :password_required?,

View File

@ -0,0 +1,38 @@
Error report from <%= Time.now %>
* Message: <%= @exception.message %>
* Location: <%= @env['REQUEST_URI'] %>
* Action: <%= @params.delete('action') %>
* Controller: <%= @params.delete('controller') %>
* Query: <%= @env['QUERY_STRING'] %>
* Method: <%= @env['REQUEST_METHOD'] %>
* SSL: <%= @env['SERVER_PORT'].to_i == 443 ? "true" : "false" %>
* Agent: <%= @env['HTTP_USER_AGENT'] %>
Backtrace
<%= @trace.to_a.join("\n") -%>
<% if @params -%>
Params
<% for key, val in @params -%>
* <%= key %>
<%= val.to_yaml.to_a.join("\n ") %>
<% end -%>
<% end -%>
<% if @session -%>
Session
<% for key, val in @session -%>
<% next if key.to_s == 'permissions' -%>
* <%= key %>
<%= val.to_yaml.to_a.join("\n ") %>
<% end -%>
<% end -%>
<% if @env -%>
Environment
<% for key, val in @env -%>
* <%= key %>
<%= val.to_yaml.to_a.join("\n ") %>
<% end -%>
<% end -%>

View File

@ -0,0 +1,13 @@
Hello!
You have been nice enough that someone has given you an invitation to join
the best beer site. We have a wiki, style guidelines, beers, breweries,
and sweet image tagging that lets you build a network of things and places
experienced in your journey through inebriation.
Sign up today at:
<%= new_people_url(:code => @invitation.code, :host => "barleysodas.com") %>
Thanks,
The BarleySodas Society for the preservation of ancient and modern techniques regarding both the intellectual and philisophical management of varied sizes animals vice the expected volume of celestial entities older than, but not equal to, sixteen billion years in age.

View File

@ -0,0 +1,2 @@
You have <strong><%= pluralize(@people.invitations.size, 'Invitations') rescue '0 Invitations' -%></strong>.
<% if @people and !@people.invitations.empty? -%><br /><%= link_to_function 'Send Invitation', "lightboxes['invitation_dialog'].open()" -%><% end -%>

View File

@ -0,0 +1,12 @@
<h1>Editing invitation</h1>
<%= error_messages_for :invitation %>
<% form_for(:invitation, :url => invitation_path(@invitation), :html => { :method => :put }) do |f| %>
<p>
<%= submit_tag "Update" %>
</p>
<% end %>
<%= link_to 'Show', invitation_path(@invitation) %> |
<%= link_to 'Back', invitations_path %>

View File

@ -0,0 +1,18 @@
<h1>Listing invitations</h1>
<table>
<tr>
</tr>
<% for invitation in @invitations %>
<tr>
<td><%= link_to 'Show', invitation_path(invitation) %></td>
<td><%= link_to 'Edit', edit_invitation_path(invitation) %></td>
<td><%= link_to 'Destroy', invitation_path(invitation), :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New invitation', new_invitation_path %>

View File

@ -0,0 +1,11 @@
<h1>New invitation</h1>
<%= error_messages_for :invitation %>
<% form_for(:invitation, :url => invitations_path) do |f| %>
<p>
<%= submit_tag "Create" %>
</p>
<% end %>
<%= link_to 'Back', invitations_path %>

View File

@ -0,0 +1,3 @@
<%= link_to 'Edit', edit_invitation_path(@invitation) %> |
<%= link_to 'Back', invitations_path %>

View File

@ -5,6 +5,8 @@
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<%= stylesheet_link_tag 'application', :media => 'all' %> <%= stylesheet_link_tag 'application', :media => 'all' %>
<%= javascript_include_tag :defaults %> <%= javascript_include_tag :defaults %>
<%= javascript_include_tag 'control.modal.js' %>
<%= javascript_include_tag 'control.rating.js' %>
<script type="text/javascript"> <script type="text/javascript">
<%= yield :script %> <%= yield :script %>
</script> </script>

View File

@ -2,9 +2,9 @@
<label for="people_title">Name</label> <%= text_field 'people', 'title' %> <label for="people_title">Name</label> <%= text_field 'people', 'title' %>
</p> </p>
<p> <p>
<label for="people_password">Password</label> <%= text_field 'people', 'password' %> <label for="people_password">Password</label> <%= password_field 'people', 'password' %>
</p> </p>
<p> <p>
<label for="people_password_confirmation">Password Confirmation</label> <%= text_field 'people', 'password_confirmation' %> <label for="people_password_confirmation">Password Confirmation</label> <%= password_field 'people', 'password_confirmation' %>
</p> </p>
<%= render :partial => 'pages/page_form' %> <%= render :partial => 'pages/page_form' %>

View File

@ -1,6 +1,7 @@
<%= error_messages_for :people %> <%= error_messages_for :people %>
<% form_for(:people, :url => peoples_path) do |f| %> <% form_for(:people, :url => peoples_path, :code => params[:code]) do |f| %>
<p>Invitation Code: <strong><%= params[:code] -%></strong><%= hidden_field_tag 'code', params[:code] -%></p>
<%= render :partial => 'people_form' %> <%= render :partial => 'people_form' %>
<p> <p>
<%= submit_tag "Create" %> <%= submit_tag "Create" %>

View File

@ -2,6 +2,16 @@
<%= render :partial => 'pages/page' %> <%= render :partial => 'pages/page' %>
<% lightbox :title => 'Send An Invitation', :window_id => 'invitation_dialog' do -%>
<div id="invitation_errors"></div>
<div class="centered">
<p>Enter the recipient's email address: <%= text_field_tag 'email' -%></p>
</div>
<div class="dialogControls">
<%= link_to_remote "Send", :url => { :controller => :invitations, :action => :send_invitation }, :with => "'email='+escape($('email').value)", :update => { :failure => 'invitation_errors', :success => 'person_invitations' }, :success => "Control.Modal.close()" -%>
</div>
<% end -%>
<% content_for :sidebar do -%> <% content_for :sidebar do -%>
<%= new_people_link -%><br /> <%= new_people_link -%><br />
<%= edit_people_link(@people) %><br /> <%= edit_people_link(@people) %><br />
@ -11,4 +21,6 @@
<% unless @people.id == session[:people_id] or @people.friend_of?(session[:people_id]) -%><%= add_friend_link(@people) -%><% end -%> <% unless @people.id == session[:people_id] or @people.friend_of?(session[:people_id]) -%><%= add_friend_link(@people) -%><% end -%>
<% if @people.friend_of?(session[:people_id]) -%><%= remove_friend_link(@people) -%><br /><% end -%> <% if @people.friend_of?(session[:people_id]) -%><%= remove_friend_link(@people) -%><br /><% end -%>
<%= link_to "#{pluralize(@people.beers.size, 'Beer')} Experience", experience_path(:id => @people.page.title_for_url) -%><br /> <%= link_to "#{pluralize(@people.beers.size, 'Beer')} Experience", experience_path(:id => @people.page.title_for_url) -%><br />
<span id="person_invitations"><%= render :partial => 'invitations/invitations' -%></span><br />
<% if has_permission_for_action?(:create, :invitations) -%><%= link_to_remote "#{image_tag('list-add.png')} Invitation", :update => 'person_invitations', :url => invitations_path, :with => "'people_id=#{@people.id}'", :success => "new Effect.Highlight('person_invitations', { duration: 2.0 })" -%><br /><% end -%>
<% end -%> <% end -%>

View File

@ -2,9 +2,12 @@ RAILS_GEM_VERSION = '1.2.3' unless defined? RAILS_GEM_VERSION
require File.join(File.dirname(__FILE__), 'boot') require File.join(File.dirname(__FILE__), 'boot')
Rails::Initializer.run do |config| Rails::Initializer.run do |config|
config.frameworks -= [ :action_web_service, :action_mailer ] config.frameworks -= [ :action_web_service ]
config.action_controller.session_store = :active_record_store config.action_controller.session_store = :active_record_store
config.active_record.default_timezone = :utc config.active_record.default_timezone = :utc
config.action_mailer.delivery_method = :sendmail
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
end end
require 'redcloth' require 'redcloth'

View File

@ -1,6 +1,7 @@
ActionController::Routing::Routes.draw do |map| ActionController::Routing::Routes.draw do |map|
map.resources :beers, :breweries, :pages, :discussions, :peoples, :roles, map.resources :beers, :breweries, :pages, :discussions, :peoples, :roles,
:sessions, :styles, :galleries, :tag_images, :friends, :experiences :sessions, :styles, :galleries, :tag_images, :friends, :experiences,
:invitations
map.connect ':controller/:action/:id.:format' map.connect ':controller/:action/:id.:format'
map.connect ':controller/:action/:id' map.connect ':controller/:action/:id'
map.connect '/', :controller => 'pages', :action => 'default_action' map.connect '/', :controller => 'pages', :action => 'default_action'

View File

@ -10,6 +10,8 @@ class CreateRoles < ActiveRecord::Migration
br = Role.create :code => 'base', :name => 'Base Role' br = Role.create :code => 'base', :name => 'Base Role'
ar = Role.create :code => 'admin', :name => 'Administrative Role', ar = Role.create :code => 'admin', :name => 'Administrative Role',
:parent_id => br.id :parent_id => br.id
nr = Role.create :code => 'normal', :name => 'Normal User Role',
:parent_id => br.id
end end
def self.down def self.down

View File

@ -0,0 +1,14 @@
class CreateInvitations < ActiveRecord::Migration
def self.up
create_table :invitations do |t|
t.column :code, :string, :limit => 32
t.column :people_id, :integer
t.column :sent, :boolean, :default => false
end
add_index :invitations, :people_id
end
def self.down
drop_table :invitations
end
end

View File

@ -6,7 +6,7 @@ base_actions = ApplicationController.action_methods
controllers = [ PagesController, DiscussionsController, StylesController, controllers = [ PagesController, DiscussionsController, StylesController,
PeoplesController, BeersController, BreweriesController, RolesController, PeoplesController, BeersController, BreweriesController, RolesController,
GalleriesController, TagImagesController, FriendsController, GalleriesController, TagImagesController, FriendsController,
ExperiencesController ] ExperiencesController, InvitationsController ]
controllers.each do |c| controllers.each do |c|
actions = c.action_methods - base_actions actions = c.action_methods - base_actions
cname = c.controller_name cname = c.controller_name

5
test/fixtures/invitations.yml vendored Normal file
View File

@ -0,0 +1,5 @@
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
id: 1
two:
id: 2

View File

@ -0,0 +1,57 @@
require File.dirname(__FILE__) + '/../test_helper'
require 'invitations_controller'
# Re-raise errors caught by the controller.
class InvitationsController; def rescue_action(e) raise e end; end
class InvitationsControllerTest < Test::Unit::TestCase
fixtures :invitations
def setup
@controller = InvitationsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
def test_should_get_index
get :index
assert_response :success
assert assigns(:invitations)
end
def test_should_get_new
get :new
assert_response :success
end
def test_should_create_invitation
old_count = Invitation.count
post :create, :invitation => { }
assert_equal old_count+1, Invitation.count
assert_redirected_to invitation_path(assigns(:invitation))
end
def test_should_show_invitation
get :show, :id => 1
assert_response :success
end
def test_should_get_edit
get :edit, :id => 1
assert_response :success
end
def test_should_update_invitation
put :update, :id => 1, :invitation => { }
assert_redirected_to invitation_path(assigns(:invitation))
end
def test_should_destroy_invitation
old_count = Invitation.count
delete :destroy, :id => 1
assert_equal old_count-1, Invitation.count
assert_redirected_to invitations_path
end
end

View File

@ -0,0 +1,10 @@
require File.dirname(__FILE__) + '/../test_helper'
class InvitationTest < Test::Unit::TestCase
fixtures :invitations
# Replace this with your real tests.
def test_truth
assert true
end
end