Unify controller action for destroying an action into the todo controller and lay some groundwork for similar requests coming from different views. Fixes #343.

git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@300 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
lukemelia 2006-08-01 07:03:31 +00:00
parent 1f6a99a27f
commit 9392d4d696
23 changed files with 152 additions and 149 deletions

View file

@ -2,6 +2,7 @@
# Likewise will all the methods added be available for all controllers.
require_dependency "login_system"
require_dependency "source_view"
require "redcloth"
require 'date'
@ -73,6 +74,7 @@ class ApplicationController < ActionController::Base
end
def parse_date_per_user_prefs( s )
return nil if s == ''
Date.strptime(s, @user.preferences["date_format"])
end

View file

@ -29,7 +29,6 @@ class ContextController < ApplicationController
init
check_user_set_context
init_todos
@on_page = "context"
@page_title = "TRACKS::Context: #{@context.name}"
end
@ -58,7 +57,6 @@ class ContextController < ApplicationController
end
@saved = @item.save
@on_page = "context"
if @saved
# This reports real count +1 for some reason that I don't understand
# Almost identical code for add_item in projects reports correct num
@ -85,36 +83,6 @@ class ContextController < ApplicationController
end
end
# Delete a next action
#
def destroy_action
self.init
@item = check_user_return_item
@saved = @item.destroy
if @saved
@down_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = ? and todos.context_id IN (?)", @user.id, false, @item.context_id]).size.to_s
end
return if request.xhr?
# fallback for standard requests
if @saved
flash["notice"] = 'Successfully deleted next action'
redirect_to :controller => 'todo', :action => 'list'
else
render :controller => 'todo', :action => 'list'
end
rescue
if request.xhr? # be sure to include an error.rjs
render :action => 'error'
else
flash["warning"] = 'An error occurred on the server.'
render :controller => 'todo', :action => 'list'
end
end
# Toggles the 'done' status of the action
#
def toggle_check
@ -217,6 +185,7 @@ class ContextController < ApplicationController
end
def init
@source_view = params['_source_view'] || 'context'
@projects = @user.projects.collect { |x| x.done? ? nil:x }.compact
@contexts = @user.contexts
@todos = @user.todos

View file

@ -11,6 +11,7 @@ class DeferredController < ApplicationController
def index
@source_view = 'deferred'
init_projects_and_contexts
init_not_done_counts
@page_title = "TRACKS::Tickler"
@ -19,6 +20,7 @@ class DeferredController < ApplicationController
end
def create
@source_view = 'deferred'
@item = Deferred.new
@item.attributes = params["todo"]
if params["todo"]["show_from"]
@ -46,12 +48,14 @@ class DeferredController < ApplicationController
end
def edit
@source_view = 'deferred'
init_projects_and_contexts
@item = check_user_return_item
render :layout => false
end
def update
@source_view = 'deferred'
@item = check_user_return_item
@original_item_context_id = @item.context_id
@item.attributes = params["item"]

View file

@ -31,7 +31,6 @@ class ProjectController < ApplicationController
init
init_todos
@notes = @project.notes
@on_page = "project"
@page_title = "TRACKS::Project: #{@project.name}"
if @contexts.empty?
@ -80,7 +79,6 @@ class ProjectController < ApplicationController
end
@saved = @item.save
@on_page = "project"
if @saved
@up_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = ? and todos.project_id IN (?)", @user.id, false, @item.project_id]).size.to_s
end
@ -104,37 +102,6 @@ class ProjectController < ApplicationController
redirect_to :controller => 'todo', :action => 'index'
end
end
# Delete a next action
#
def destroy_action
self.init
@item = check_user_return_item
@saved = @item.destroy
@on_page = "project"
if @saved
@down_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = ? and todos.project_id IN (?)", @user.id, false, @item.project_id]).size.to_s
end
return if request.xhr?
# fallback for standard requests
if @saved
flash["notice"] = 'Successfully deleted next action'
redirect_to :controller => 'todo', :action => 'index'
else
render :controller => 'todo', :action => 'index'
end
rescue
if request.xhr? # be sure to include an error.rjs
render :action => 'error'
else
flash["warning"] = 'An error occurred on the server.'
render :controller => 'todo', :action => 'index'
end
end
# Toggles the 'done' status of the action
#
@ -145,7 +112,6 @@ class ProjectController < ApplicationController
@item.toggle!('done')
@item.completed = Time.now() # For some reason, the before_save in todo.rb stopped working
@saved = @item.save
@on_page = "project"
if @saved
@down_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = ? and todos.project_id IN (?)", @user.id, false, @item.project_id]).size.to_s
@done_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = ? and todos.project_id IN (?)", @user.id, true, @item.project_id]).size.to_s
@ -249,6 +215,7 @@ class ProjectController < ApplicationController
end
def init
@source_view = params['_source_view'] || 'project'
@projects = @user.projects
@contexts = @user.contexts
@todos = @user.todos

View file

@ -17,7 +17,6 @@ class TodoController < ApplicationController
@projects = @user.projects.find(:all, :include => [ :todos ])
@contexts = @user.contexts.find(:all, :include => [ :todos ])
@on_page = "home"
@page_title = "TRACKS::List tasks"
# If you've set no_completed to zero, the completed items box
@ -54,7 +53,6 @@ class TodoController < ApplicationController
self.init
@item = @user.todos.build
@item.attributes = params["todo"]
@on_page = "home"
if @item.due?
@item.due = parse_date_per_user_prefs(params["todo"]["due"])
@ -82,13 +80,13 @@ class TodoController < ApplicationController
wants.html do
flash["warning"] = 'An error occurred on the server.'
render :action => "index"
end
end
wants.js { render :action => 'error' }
wants.xml { render :text => 'An error occurred on the server.' + $! }
end
end
def edit_action
def edit
self.init
@item = check_user_return_item
render :layout => false
@ -111,7 +109,6 @@ class TodoController < ApplicationController
@item.toggle!('done')
@item.completed = Time.now() # For some reason, the before_save in todo.rb stopped working
@saved = @item.save
@on_page = "home"
@remaining_undone_in_context = Todo.count(:conditions => ['user_id = ? and context_id = ? and type = ? and done = ?', @user.id, @item.context_id, "Immediate", false])
if @saved
@down_count = @todos.collect { |x| ( !x.done? and !x.context.hide? ) ? x:nil }.compact.size.to_s
@ -130,9 +127,6 @@ class TodoController < ApplicationController
#
def update_action
self.init
if params["on_project_page"] == true
@on_page = "project"
end
@item = check_user_return_item
@original_item_context_id = @item.context_id
@item.attributes = params["item"]
@ -180,38 +174,61 @@ class TodoController < ApplicationController
end
end
# Delete a next action
#
def destroy_action
self.init
def destroy
@item = check_user_return_item
context_id = @item.context_id
project_id = @item.project_id
@saved = @item.destroy
@on_page = "home"
@remaining_undone_in_context = Todo.count(:conditions => ['user_id = ? and context_id = ? and type = ? and done = ?', @user.id, context_id, "Immediate", false])
if @saved
self.init
@down_count = @todos.reject { |x| x.done? or x.context.hide? }.size.to_s
end
return if request.xhr?
respond_to do |wants|
wants.html do
if @saved
flash["notice"] = 'Successfully deleted next action'
redirect_to :action => 'index'
else
flash["warning"] = 'Failed to delete the action.'
redirect_to :action => 'index'
end
end
wants.js do
if @saved
@down_count = 0
source_view do |from|
from.todo do
@down_count = Todo.count(:conditions => ['todos.user_id = ? and todos.type = ? and todos.done = ? and contexts.hide = ?',
@user.id, "Immediate", false, false],
:include => [ :context ])
@remaining_undone_in_context = Todo.count(:conditions => ['user_id = ? and context_id = ? and type = ? and done = ?',
@user.id, context_id, "Immediate", false])
end
from.context do
@down_count = Todo.count(:conditions => ['todos.user_id = ? and todos.type = ? and todos.done = ? and todos.context_id = ?',
@user.id, "Immediate", false, context_id])
end
from.project do
@down_count = Todo.count(:conditions => ['todos.user_id = ? and todos.type = ? and todos.done = ? and todos.project_id = ?',
@user.id, "Immediate", false, project_id]) unless project_id == nil
end
end
end
render
end
wants.xml { render :text => '200 OK. Action deleted.', :status => 200 }
# fallback for standard requests
if @saved
flash["notice"] = 'Successfully deleted next action'
redirect_to :action => 'index'
else
render :action => 'index'
end
rescue
if request.xhr? # be sure to include an error.rjs
render :action => 'error'
else
flash["warning"] = 'An error occurred on the server.'
render :action => 'index'
end
respond_to do |wants|
wants.html do
flash["warning"] = 'An error occurred on the server.'
redirect_to :action => 'index'
end
wants.js { render :action => 'error' }
wants.xml { render :text => 'An error occurred on the server.' + $! }
end
end
# List the completed tasks, sorted by completion date
@ -250,12 +267,20 @@ class TodoController < ApplicationController
if @user == item.user
return item
else
flash["warning"] = "Item and session user mis-match: #{item.user.name} and #{@user.name}!"
render_text ""
@error_message = 'Item and session user mis-match: #{item.user.name} and #{@user.name}!'
respond_to do |wants|
wants.html do
flash["warning"] = @error_message
render :action => "index"
end
wants.js { render :action => 'error' }
wants.xml { render :text => @error_message, :status => 403 }
end
end
end
def init
@source_view = params['_source_view'] || 'todo'
@projects = @user.projects
@contexts = @user.contexts
init_todos

View file

@ -15,19 +15,19 @@ module TodoHelper
)
end
def link_to_remote_todo( item, handled_by, type)
(type == "deferred") ? destroy_act = 'destroy' : destroy_act = 'destroy_action'
def link_to_remote_todo( item, options = {})
(options[:type] == "deferred") ? controller_name = 'deferred' : controller_name = 'todo'
url_options = { :controller => controller_name, :action => 'destroy', :id => item.id, :_source_view => @source_view }
str = link_to_remote( image_tag("blank", :title =>"Delete action", :class=>"delete_item"),
{:url => { :controller => handled_by, :action => destroy_act, :id => item.id },
:confirm => "Are you sure that you want to delete the action, \'#{item.description}\'?"},
{:class => "icon"}) + "\n"
{ :url => url_options, :confirm => "Are you sure that you want to delete the action, \'#{item.description}\'?" },
{ :class => "icon" }
) + "\n"
if !item.done?
(type == "deferred") ? edit_act = 'edit' : edit_act = 'edit_action'
str << link_to_remote( image_tag("blank", :title =>"Edit action", :class=>"edit_item", :id=>"action-#{item.id}-edit-icon"),
{
:update => "form-action-#{item.id}",
:loading => visual_effect(:pulsate, "action-#{item.id}-edit-icon"),
:url => { :controller => handled_by, :action => edit_act, :id => item.id },
:url => { :controller => 'todo', :action => 'edit', :id => item.id, :_source_view => @source_view },
:success => "Element.toggle('item-#{item.id}','action-#{item.id}-edit-form'); new Effect.Appear('action-#{item.id}-edit-form', { duration: .2 }); Form.focusFirstElement('form-action-#{item.id}')"
},
{

View file

@ -16,7 +16,7 @@
link_to_remote( image_tag("blank", :title =>"Delete this action", :class=>"delete_item"),
:update => "item-#{item.id}-container",
:loading => visual_effect(:fade, "item-#{item.id}-container"),
:url => { :controller => "todo", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " +
:url => { :controller => "todo", :action => "destroy", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " +
link_to_function(image_tag( "blank", :title => "Edit item", :class=>"edit_item"), "Element.toggle('item-#{item.id}','action-#{item.id}-edit-form'); new Effect.Appear('action-#{item.id}-edit-form'); Form.focusFirstElement('form-action-#{item.id}');" ) + " "
%>
<!-- begin div.checkbox -->
@ -58,7 +58,7 @@
</div><!-- [end:item-item.id-container] -->
<% else %>
<div id="done-item-<%= item.id %>-container">
<%= form_remote_tag( :url => url_for( :controller => "context", :action => "toggle_check", :id => item.id ),
<%= form_remote_tag( :url => url_for( :controller => "context", :action => "toggle_check", :id => item.id, :_source_view => source_view),
:html => { :id=> "checkbox-done-#{item.id}", :class => "inline-form" },
:update => "next_actions",
:position => "bottom",
@ -72,7 +72,7 @@
link_to_remote( image_tag("blank", :title =>"Delete this action", :class=>"delete_item"),
:update => "done-item-#{item.id}-container",
:loading => visual_effect(:fade, "done-item-#{item.id}-container"),
:url => { :controller => "todo", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " "
:url => { :controller => "todo", :action => "destroy", :id => item.id, :_source_view => source_view }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " "
%>
<%= image_tag("blank") %>
</div><!-- [end:big-box] -->

View file

@ -5,7 +5,7 @@ if @saved
page['badge_count'].replace_html @up_count
page.send :record, "Form.reset('todo-form-new-action');Form.focusFirstElement('todo-form-new-action')"
page.send :record, "Form.reset('todo-form-new-action-lightbox');Form.focusFirstElement('todo-form-new-action-lightbox')"
page.insert_html :bottom, "c#{@item.context_id}", :partial => 'todo/item', :locals => { :parent_container_type => "context" }
page.insert_html :bottom, "c#{@item.context_id}", :partial => 'todo/item', :locals => { :parent_container_type => "context", :source_view => 'context' }
page.visual_effect :highlight, "item-#{@item.id}-container", :duration => 3
page["c#{@item.context_id}empty-nd"].hide # If we are adding an new action, the uncompleted actions must be > 0
else

View file

@ -1,9 +0,0 @@
if @saved
page.visual_effect :fade, "item-#{@item.id}-container", :duration => 0.4
page.replace_html "badge_count", @down_count
if @down_count == "0"
page["c#{@item.context_id}empty-nd").show
end
else
page.replace_html "status", content_tag("div", content_tag("h2", "#{pluralize(@item.errors.count, "error")} prohibited this record from being saved") + content_tag("p", "There were problems with the following fields:") + content_tag("ul", @item.errors.each_full { |msg| content_tag("li", msg) }), "id" => "ErrorExplanation", "class" => "ErrorExplanation")
end

View file

@ -1,6 +1,6 @@
<div id="item-<%= item.id %>-container" class="item-container">
<div id="item-<%= item.id %>">
<%= link_to_remote_todo( item, controller.controller_name, "deferred" ) %>
<%= link_to_remote_todo item, { :type => 'deferred' } %>
<div class="description">
<%= show_date( item.show_from ) -%>

View file

@ -16,7 +16,7 @@
link_to_remote( image_tag("blank", :title =>"Delete action", :class=>"delete_item"),
:update => "item-#{item.id}-container",
:loading => visual_effect(:fade, "item-#{item.id}-container"),
:url => { :controller => "todo", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " +
:url => { :controller => "todo", :action => "destroy", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " +
link_to_function(image_tag( "blank", :title => "Edit action", :class => "edit_item"), "Element.toggle('item-#{item.id}','action-#{item.id}-edit-form'); new Effect.Appear('action-#{item.id}-edit-form'); Form.focusFirstElement('form-action-#{item.id}');" ) + " "
%>
</div>
@ -73,7 +73,7 @@
link_to_remote( image_tag("blank", :title =>"Delete action", :class=>"delete_item"),
:update => "done-item-#{item.id}-container",
:loading => visual_effect(:fade, "done-item-#{item.id}-container"),
:url => { :controller => "todo", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " "
:url => { :controller => "todo", :action => "destroy", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " "
%>
</div><!-- [end:big-box] -->
<!-- begin div.checkbox -->

View file

@ -1,9 +0,0 @@
if @saved
page.visual_effect :fade, "item-#{@item.id}-container", :duration => 0.4
page.replace_html "badge_count", @down_count
if @down_count == "0"
page["p#{@item.project_id}empty-nd"].show
end
else
page.replace_html "status", content_tag("div", content_tag("h2", "#{pluralize(@item.errors.count, "error")} prohibited this record from being saved") + content_tag("p", "There were problems with the following fields:") + content_tag("ul", @item.errors.each_full { |msg| content_tag("li", msg) }), "id" => "ErrorExplanation", "class" => "ErrorExplanation")
end

View file

@ -1,7 +1,7 @@
<div id="item-<%= item.id %>-container" class="item-container">
<div id="item-<%= item.id %>">
<%= link_to_remote_todo( item, controller.controller_name, "immediate" ) %>
<%= link_to_remote_todo item %>
<input type="checkbox" class="item-checkbox"
onclick="new Ajax.Request('<%= url_for :controller => controller.controller_name, :action => 'toggle_check', :id => item.id %>', {asynchronous:true, evalScripts:true});"
name="item_id" value="<%= item.id %>"<% if item.done? %> checked="checked" <% end %> />

View file

@ -1,9 +1,11 @@
if @saved
page["item-#{@item.id}-container"].remove
page['badge_count'].replace_html @down_count
if @remaining_undone_in_context == 0
page.visual_effect :fade, "c#{@item.context_id}", :duration => 0.4
end
source_view do |from|
from.todo { page.visual_effect :fade, "c#{@item.context_id}", :duration => 0.4 if @remaining_undone_in_context == 0 }
from.context { page["c#{@item.context_id}empty-nd"].show if @down_count == 0 }
from.project { page["p#{@item.project_id}empty-nd"].show if @down_count == 0 }
end
else
page["status"].replace_html content_tag("div", content_tag("h2", "#{pluralize(@item.errors.count, "error")} prohibited this record from being saved") + content_tag("p", "There were problems with the following fields:") + content_tag("ul", @item.errors.each_full { |msg| content_tag("li", msg) }), "id" => "ErrorExplanation", "class" => "ErrorExplanation")
end

View file

@ -1,6 +1,8 @@
<%= error_messages_for("item") %>
<%= hidden_field( "item", "id" ) %>
<%= source_view_tag( @source_view ) %>
<table>
<tr>
<td class="label"><label for="item_description">Next action</label></td>

View file

@ -1 +1 @@
page["status"].replace_html "An error occurred on the server."
page["status"].replace_html @error_message || "An error occurred on the server."

48
tracks/lib/source_view.rb Normal file
View file

@ -0,0 +1,48 @@
# Inspiration from Bruce Williams [http://codefluency.com/articles/2006/07/01/rails-views-getting-in-context/]
module Tracks
module SourceViewSwitching
class Responder
def initialize(source_view)
@source_view = source_view.underscore.gsub(/\s+/,'_').to_sym rescue nil
end
def nil?
yield if @source_view.nil? && block_given?
end
def method_missing(check_source_view,*args)
yield if check_source_view == @source_view && block_given?
end
end
module Controller
def self.included(base)
base.send(:helper, Tracks::SourceViewSwitching::Helper)
base.send(:helper_method, :source_view)
end
def source_view
responder = Tracks::SourceViewSwitching::Responder.new(params[:_source_view])
block_given? ? yield(responder) : responder
end
end
module Helper
def source_view_tag(name)
hidden_field_tag :_source_view, name.underscore.gsub(/\s+/,'_')
end
end
end
end
ActionController::Base.send(:include, Tracks::SourceViewSwitching::Controller)

View file

@ -42,9 +42,9 @@ class LoginControllerTest < Test::Unit::TestCase
user = login('jane','sesame', 'off')
assert_equal user.id, @response.session['user_id']
assert_equal user.login, "jane"
assert !user.is_admin
assert user.is_admin == false || user.is_admin == 0
assert_equal "Login successful: session will expire after 1 hour of inactivity.", flash['notice']
assert_redirected_to :controller => 'todo', :action => 'list'
assert_redirected_to :controller => 'todo', :action => 'index'
end
def test_logout
@ -82,16 +82,16 @@ class LoginControllerTest < Test::Unit::TestCase
assert admin.is_admin
newbie = create('newbie', 'newbiepass')
assert_equal "Signup successful for user newbie.", flash['notice']
assert_redirected_to :controller => 'todo', :action => 'list'
assert_redirected_to :controller => 'todo', :action => 'index'
assert_valid newbie
get :logout # logout the admin user
assert_equal newbie.login, "newbie"
assert !newbie.is_admin
assert newbie.is_admin == false || newbie.is_admin == 0
assert_not_nil newbie.preferences # have user preferences been created?
user = login('newbie', 'newbiepass', 'on') # log in the new user
assert_redirected_to :controller => 'todo', :action => 'list'
assert_redirected_to :controller => 'todo', :action => 'index'
assert_equal 'newbie', user.login
assert !user.is_admin
assert user.is_admin == false || user.is_admin == 0
num_users = User.find(:all)
assert_equal num_users.length, 3
end
@ -100,7 +100,7 @@ class LoginControllerTest < Test::Unit::TestCase
#
def test_create_by_non_admin
non_admin = login('jane', 'sesame', 'on')
assert !non_admin.is_admin
assert non_admin.is_admin == false || non_admin.is_admin == 0
post :signup, :user => {:login => 'newbie2', :password => 'newbiepass2', :password_confirmation => 'newbiepass2'}
assert_template 'login/nosignup'

View file

@ -37,7 +37,7 @@ class StoriesTest < ActionController::IntegrationTest
assert_response :redirect
follow_redirect!
assert_response :success
assert_template "todo/list"
assert_template "todo/index"
end
def goes_to_login
@ -63,7 +63,7 @@ class StoriesTest < ActionController::IntegrationTest
assert_response :redirect
follow_redirect!
assert_response :success
assert_template "todo/list"
assert_template "todo/index"
end
end

View file

@ -25,6 +25,7 @@ class ContextTest < Test::Unit::TestCase
def test_validate_name_is_unique
newcontext = Context.new
newcontext.name = contexts(:agenda).name
newcontext.user_id = contexts(:agenda).user_id
assert !newcontext.save
assert_equal 1, newcontext.errors.count
assert_equal "already exists", newcontext.errors.on(:name)

View file

@ -24,7 +24,8 @@ class ProjectTest < Test::Unit::TestCase
def test_validate_name_is_unique
newproj = Project.new
newproj.name = "Build a working time machine"
newproj.name = projects(:timemachine).name
newproj.user_id = projects(:timemachine).user_id
assert !newproj.save
assert_equal 1, newproj.errors.count
assert_equal "already exists", newproj.errors.on(:name)

View file

@ -18,7 +18,7 @@ class TodoTest < Test::Unit::TestCase
assert_equal 2, @not_completed1.project_id
assert_equal "Call Bill Gates to find out how much he makes per day", @not_completed1.description
assert_nil @not_completed1.notes
assert !@not_completed1.done
assert @not_completed1.done == false || @not_completed1.done == 0
assert_equal 1.week.ago.strftime("%Y-%m-%d %H:%M"), @not_completed1.created_at.strftime("%Y-%m-%d %H:%M")
assert_equal 2.week.from_now.strftime("%Y-%m-%d"), @not_completed1.due.strftime("%Y-%m-%d")
assert_nil @not_completed1.completed

View file

@ -28,7 +28,7 @@ class UserTest < Test::Unit::TestCase
assert_equal "jane", @other_user.login
assert_equal "#{Digest::SHA1.hexdigest("#{SALT}--sesame--")}", @other_user.password
assert_not_nil @other_user.word
assert !@other_user.is_admin
assert @other_user.is_admin == false || @other_user.is_admin == 0
end
# ============================================