adding error emails and invitation mailing
git-svn-id: http://svn.barleysodas.com/barleysodas/trunk@126 0f7b21a7-9e3a-4941-bbeb-ce5c7c368fa7
This commit is contained in:
parent
52c36c3891
commit
11e3356f55
28 changed files with 340 additions and 10 deletions
app
controllers
helpers
models
views
error_mailer
invitation_mailer
invitations
layouts
peoples
config
db/migrate
generate_permissionstest
|
@ -127,6 +127,23 @@ class ApplicationController < ActionController::Base
|
|||
true
|
||||
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
|
||||
|
||||
##
|
||||
|
|
63
app/controllers/invitations_controller.rb
Normal file
63
app/controllers/invitations_controller.rb
Normal 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
|
|
@ -50,8 +50,10 @@ class PeoplesController < ApplicationController
|
|||
set_people_role
|
||||
@page = Page.new(params[:page])
|
||||
@people.page = @page
|
||||
invitation = Invitation.find_by_code(params[:code])
|
||||
respond_to do |format|
|
||||
if @people.save
|
||||
if invitation and @people.save
|
||||
invitation.destroy
|
||||
flash[:notice] = 'People was successfully created.'
|
||||
format.html { redirect_to people_url(@people.page.title_for_url) }
|
||||
format.xml { head :created,
|
||||
|
|
|
@ -116,9 +116,8 @@ module ApplicationHelper
|
|||
# Renders everything you need to display the TagImage browser.
|
||||
#
|
||||
def tagged_image_browser(obj)
|
||||
javascript_include_tag('control.modal.js') +
|
||||
render(:partial => 'shared/tagged_image_browser',
|
||||
:locals => { :obj => obj })
|
||||
render(:partial => 'shared/tagged_image_browser',
|
||||
:locals => { :obj => obj })
|
||||
end
|
||||
|
||||
##
|
||||
|
|
2
app/helpers/invitations_helper.rb
Normal file
2
app/helpers/invitations_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
module InvitationsHelper
|
||||
end
|
14
app/models/error_mailer.rb
Normal file
14
app/models/error_mailer.rb
Normal 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
17
app/models/invitation.rb
Normal 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
|
10
app/models/invitation_mailer.rb
Normal file
10
app/models/invitation_mailer.rb
Normal 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
|
|
@ -14,6 +14,8 @@ class People < ActiveRecord::Base
|
|||
has_many :actual_friends, :through => :friends, :source => :destination
|
||||
has_many :experiences, :dependent => :destroy
|
||||
has_many :beers, :through => :experiences
|
||||
has_many :invitations, :dependent => :destroy,
|
||||
:conditions => [ 'sent IS NULL or sent = ?', false ]
|
||||
|
||||
make_authenticatable
|
||||
validates_length_of :password, :minimum => 8, :if => :password_required?,
|
||||
|
|
38
app/views/error_mailer/snapshot.rhtml
Normal file
38
app/views/error_mailer/snapshot.rhtml
Normal 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 -%>
|
13
app/views/invitation_mailer/invitation.rhtml
Normal file
13
app/views/invitation_mailer/invitation.rhtml
Normal 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.
|
2
app/views/invitations/_invitations.rhtml
Normal file
2
app/views/invitations/_invitations.rhtml
Normal 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 -%>
|
12
app/views/invitations/edit.rhtml
Normal file
12
app/views/invitations/edit.rhtml
Normal 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 %>
|
18
app/views/invitations/index.rhtml
Normal file
18
app/views/invitations/index.rhtml
Normal 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 %>
|
11
app/views/invitations/new.rhtml
Normal file
11
app/views/invitations/new.rhtml
Normal 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 %>
|
3
app/views/invitations/show.rhtml
Normal file
3
app/views/invitations/show.rhtml
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
<%= link_to 'Edit', edit_invitation_path(@invitation) %> |
|
||||
<%= link_to 'Back', invitations_path %>
|
|
@ -5,6 +5,8 @@
|
|||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<%= stylesheet_link_tag 'application', :media => 'all' %>
|
||||
<%= javascript_include_tag :defaults %>
|
||||
<%= javascript_include_tag 'control.modal.js' %>
|
||||
<%= javascript_include_tag 'control.rating.js' %>
|
||||
<script type="text/javascript">
|
||||
<%= yield :script %>
|
||||
</script>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
<label for="people_title">Name</label> <%= text_field 'people', 'title' %>
|
||||
</p>
|
||||
<p>
|
||||
<label for="people_password">Password</label> <%= text_field 'people', 'password' %>
|
||||
<label for="people_password">Password</label> <%= password_field 'people', 'password' %>
|
||||
</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>
|
||||
<%= render :partial => 'pages/page_form' %>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<%= 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' %>
|
||||
<p>
|
||||
<%= submit_tag "Create" %>
|
||||
|
|
|
@ -2,6 +2,16 @@
|
|||
|
||||
<%= 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 -%>
|
||||
<%= new_people_link -%><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 -%>
|
||||
<% 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 />
|
||||
<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 -%>
|
|
@ -2,9 +2,12 @@ RAILS_GEM_VERSION = '1.2.3' unless defined? RAILS_GEM_VERSION
|
|||
require File.join(File.dirname(__FILE__), 'boot')
|
||||
|
||||
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.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
|
||||
|
||||
require 'redcloth'
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
ActionController::Routing::Routes.draw do |map|
|
||||
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'
|
||||
map.connect '/', :controller => 'pages', :action => 'default_action'
|
||||
|
|
|
@ -10,6 +10,8 @@ class CreateRoles < ActiveRecord::Migration
|
|||
br = Role.create :code => 'base', :name => 'Base Role'
|
||||
ar = Role.create :code => 'admin', :name => 'Administrative Role',
|
||||
:parent_id => br.id
|
||||
nr = Role.create :code => 'normal', :name => 'Normal User Role',
|
||||
:parent_id => br.id
|
||||
end
|
||||
|
||||
def self.down
|
||||
|
|
14
db/migrate/015_create_invitations.rb
Normal file
14
db/migrate/015_create_invitations.rb
Normal 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
|
|
@ -6,7 +6,7 @@ base_actions = ApplicationController.action_methods
|
|||
controllers = [ PagesController, DiscussionsController, StylesController,
|
||||
PeoplesController, BeersController, BreweriesController, RolesController,
|
||||
GalleriesController, TagImagesController, FriendsController,
|
||||
ExperiencesController ]
|
||||
ExperiencesController, InvitationsController ]
|
||||
controllers.each do |c|
|
||||
actions = c.action_methods - base_actions
|
||||
cname = c.controller_name
|
||||
|
|
5
test/fixtures/invitations.yml
vendored
Normal file
5
test/fixtures/invitations.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
|
||||
one:
|
||||
id: 1
|
||||
two:
|
||||
id: 2
|
57
test/functional/invitations_controller_test.rb
Normal file
57
test/functional/invitations_controller_test.rb
Normal 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
|
10
test/unit/invitation_test.rb
Normal file
10
test/unit/invitation_test.rb
Normal 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
|
Reference in a new issue