* Converted Todo to acts_as_state_machine. It's states are active, deferred, completed, and project_hidden. This replaces the old single inheritance model of Immediate and Deferred. Also renamed todo.completed to todo.completed_at for clarity

* Consolidated toggle_check handling to todo_controller and rjs  
* Introduced user preference to show/hide hidden projects section in sidebar
* Fixed a bug in parse_date_per_user_prefs that was causing due dates to be set in the todo model as Times and not Dates
* Upgraded ARTS plugin
* This changeset includes migrations, so remember to db:migrate.
* Lots of code changes here, so bug reports will be gratefully accepted!



git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@343 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
lukemelia 2006-11-15 09:05:07 +00:00
parent 883bcb30bb
commit c51587e422
47 changed files with 487 additions and 301 deletions

View file

@ -83,23 +83,28 @@ class ApplicationController < ActionController::Base
def parse_date_per_user_prefs( s )
return nil if s == ''
Chronic.parse(s)
Chronic.parse(s).to_date
end
def init_data_for_sidebar
@projects = @user.projects
@contexts = @user.contexts
init_not_done_counts
if @prefs.show_hidden_projects_in_sidebar
init_project_hidden_todo_counts(['project'])
end
end
def init_not_done_counts(parents = ['project','context'])
parents.each do |parent|
eval("@#{parent}_not_done_counts = Todo.count(:all,
:conditions => ['todos.user_id = ? and todos.type = ? and todos.done = ? and (projects.state != ? or todos.project_id is ?)',
@user.id, \"Immediate\", false, \"hidden\", nil],
:joins => 'LEFT JOIN projects on projects.id = todos.project_id',
:group => :#{parent}_id)")
eval("@#{parent}_not_done_counts = Todo.count(:conditions => ['user_id = ? and state = ?', @user.id, 'active'], :group => :#{parent}_id)")
end
end
end
def init_project_hidden_todo_counts(parents = ['project','context'])
parents.each do |parent|
eval("@#{parent}_project_hidden_todo_counts = Todo.count(:conditions => ['user_id = ? and state = ?', @user.id, 'project_hidden'], :group => :#{parent}_id)")
end
end
end

View file

@ -102,29 +102,6 @@ class ContextController < ApplicationController
redirect_to :controller => 'todo', :action => 'list'
end
end
# Toggles the 'done' status of the action
#
def toggle_check
self.init
@item = check_user_return_item
@item.toggle!('done')
@item.completed = Time.now() # For some reason, the before_save in todo.rb stopped working
@saved = @item.save
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
@done_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = ? and todos.context_id IN (?)", @user.id, true, @item.context_id]).size.to_s
end
return if request.xhr?
if @saved
flash[:notice] = "The action <strong>'#{@item.description}'</strong> was marked as <strong>#{@item.done? ? 'complete' : 'incomplete' }</strong>"
else
flash[:notice] = "The action <strong>'#{@item.description}'</strong> was NOT marked as <strong>#{@item.done? ? 'complete' : 'incomplete' } due to an error on the server.</strong>"
end
redirect_to :action => "list"
end
# Edit the details of the context
#
@ -210,11 +187,9 @@ class ContextController < ApplicationController
# If we exclude completed projects, then we can't display them in the sidebar
# if the user sets the preference for them to be shown
# @projects = @user.projects.reject { |x| x.completed? }
@projects = @user.projects
@contexts = @user.contexts
init_data_for_sidebar
@todos = @user.todos
@done = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = ?", @user.id, true], :include => [:project], :order => "completed DESC")
init_not_done_counts
@done = @user.todos.find_in_state(:all, :completed, :order => "todos.completed_at DESC")
end
def init_todos
@ -224,10 +199,7 @@ class ContextController < ApplicationController
# TODO: Temporarily doing this search manually until I can work out a way
# to do the same thing using not_done_todos acts_as_todo_container method
# Hides actions in hidden projects from context.
@not_done_todos = Todo.find(:all,
:conditions => ["todos.context_id = ? and todos.done = ? and todos.type = ? and (projects.state != ? or todos.project_id is ?)", @context.id, false, "Immediate", "hidden", nil],
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
:include => [:project])
@not_done_todos = @context.todos.find_in_state(:all, :active, :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC")
@count = @not_done_todos.size
end

View file

@ -12,16 +12,16 @@ class DeferredController < ApplicationController
def index
@source_view = 'deferred'
init_projects_and_contexts
init_not_done_counts
init_data_for_sidebar
@page_title = "TRACKS::Tickler"
@tickles = @user.todos.find(:all, :conditions => ['type = ?', "Deferred"], :order => "show_from ASC")
@tickles = @user.todos.find_in_state(:all, :deferred, :order => "show_from ASC")
@count = @tickles.size
end
def create
@source_view = 'deferred'
@item = Deferred.new
@item = Todo.new
@item.defer!
@item.attributes = params["todo"]
if params["todo"]["show_from"]
@item.show_from = parse_date_per_user_prefs(params["todo"]["show_from"])
@ -37,7 +37,7 @@ class DeferredController < ApplicationController
@saved = @item.save
if @saved
@up_count = @user.todos.count(['type = ?', "Deferred"])
@up_count = @user.todos.count_in_state(:deferred)
end
respond_to do |wants|
@ -84,7 +84,7 @@ class DeferredController < ApplicationController
redirect_to :action => "index"
end
wants.js do
@down_count = @user.todos.count(['type = ?', "Deferred"]) if @saved
@down_count = @user.todos.count_in_state(:deferred) if @saved
render
end
wants.xml { render :xml => @item.to_xml( :root => 'todo', :except => :user_id ) }
@ -101,30 +101,22 @@ class DeferredController < ApplicationController
end
end
# Check for any due tickler items, change them to type Immediate.
# Check for any due tickler items, activate them
# Called by periodically_call_remote
def check_tickler
now = Date.today()
@due_tickles = @user.todos.find(:all, :conditions => ['type = ? AND (show_from < ? OR show_from = ?)', "Deferred", now, now ], :order => "show_from ASC")
# Change the due tickles to type "Immediate"
@due_tickles = @user.todos.find_in_state(:all, :deferred, :conditions => ['show_from < ? OR show_from = ?', now, now ], :order => "show_from ASC")
# Change the due tickles to active
@due_tickles.each do |t|
t[:type] = "Immediate"
t.show_from = nil
t.save_with_validation(false)
t.activate!
t.save
end
respond_to do |wants|
wants.html { redirect_to :controller => 'todo', :action => 'index' }
wants.js
end
end
protected
def init_projects_and_contexts
@projects = @user.projects
@contexts = @user.contexts
end
private
def check_user_return_item

View file

@ -84,9 +84,9 @@ protected
options = Hash.new
if params.key?('done')
condition_builder.add 'todos.done = ?', true
condition_builder.add 'todos.state = ?', 'completed'
else
condition_builder.add 'todos.done = ?', false
condition_builder.add 'todos.state = ?', 'active'
end
if params.key?('limit')
@ -107,7 +107,7 @@ protected
if params.key?('done')
done_in_last = params['done'].to_i
condition_builder.add('todos.completed >= ?', done_in_last.days.ago)
condition_builder.add('todos.completed_at >= ?', done_in_last.days.ago)
@title << " actions completed"
@description << " in the last #{done_in_last.to_s} days"
end

View file

@ -16,9 +16,9 @@ class MobileController < ApplicationController
self.init
@page_title = @desc = "All actions"
@todos_pages, @todos = paginate( :todos, :order => 'due IS NULL, due ASC, created_at ASC',
:conditions => ['user_id = ? and type = ? and done = ?', @user.id, "Immediate", false],
:conditions => ['user_id = ? and state = ?', @user.id, "active"],
:per_page => 6 )
@count = @all_todos.reject { |x| x.done? || x.context.hide? }.size
@count = @all_todos.reject { |x| !x.active? || x.context.hide? }.size
end
def detail
@ -32,10 +32,11 @@ class MobileController < ApplicationController
@item = check_user_return_item
else
if params[:item][:"show_from(1i)"] == ""
@item = Immediate.create(params[:item]) if params[:item]
@item = Todo.create(params[:item]) if params[:item]
else
@item = Deferred.create(params[:item]) if params[:item]
end
@item = Todo.create(params[:item]) if params[:item]
@item.defer!
end
end
@item.user_id = @user.id
@ -64,14 +65,14 @@ class MobileController < ApplicationController
@context = Context.find( params[:context][:id] )
@page_title = @desc = "#{@context.name}"
@todos_pages, @todos = paginate( :todos, :order => 'due IS NULL, due ASC, created_at ASC',
:conditions => ['user_id = ? and type = ? and done = ? and context_id = ?', @user.id, "Immediate", false, @context.id], :per_page => 6 )
@count = @all_todos.reject { |x| x.done? || x.context_id != @context.id }.size
:conditions => ['user_id = ? and state = ? and context_id = ?', @user.id, "active", @context.id], :per_page => 6 )
@count = @all_todos.reject { |x| x.completed? || x.context_id != @context.id }.size
when 'project'
@project = Project.find( params[:project][:id] )
@page_title = @desc = "#{@project.name}"
@todos_pages, @todos = paginate( :todos, :order => 'due IS NULL, due ASC, created_at ASC',
:conditions => ['user_id = ? and type = ? and done = ? and project_id = ?', @user.id, "Immediate", false, @project.id], :per_page => 6 )
@count = @all_todos.reject { |x| x.done? || x.project_id != @project.id }.size
:conditions => ['user_id = ? and state = ? and project_id = ?', @user.id, "active", @project.id], :per_page => 6 )
@count = @all_todos.reject { |x| x.completed? || x.project_id != @project.id }.size
end
end
@ -88,11 +89,9 @@ class MobileController < ApplicationController
end
def init
@contexts = Context.find :all, :order => 'position ASC',
:conditions => ['user_id = ?', @user.id]
@projects = Project.find :all, :order => 'position ASC',
:conditions => ['user_id = ? and state = ?', @user.id, "active"]
@all_todos = Todo.find(:all, :conditions => ['user_id = ? and type = ?', @user.id, "Immediate"])
@contexts = @user.contexts.find(:all, :order => 'position ASC')
@projects = @user.projects.find_in_state(:all, :active, :order => 'position ASC')
@all_todos = @user.todos.find(:all, :conditions => ['state = ? or state =?', "active", "completed"])
end
end

View file

@ -17,6 +17,7 @@ class ProjectController < ApplicationController
#
def list
init
init_project_hidden_todo_counts
@page_title = "TRACKS::List Projects"
respond_to do |wants|
wants.html
@ -125,38 +126,27 @@ class ProjectController < ApplicationController
end
end
# Toggles the 'done' status of the action
#
def toggle_check
self.init
@item = check_user_return_item
@item.toggle!('done')
@item.completed = Time.now() # For some reason, the before_save in todo.rb stopped working
@saved = @item.save
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
end
return if request.xhr?
if @saved
flash[:notice] = "The action <strong>'#{@item.description}'</strong> was marked as <strong>#{@item.done? ? 'complete' : 'incomplete' }</strong>"
else
flash[:notice] = "The action <strong>'#{@item.description}'</strong> was NOT marked as <strong>#{@item.done? ? 'complete' : 'incomplete' } due to an error on the server.</strong>"
end
redirect_to :action => "list"
end
# Edit the details of the project
#
def update
self.init
check_user_set_project
@project.transition_to(params['project']['state'])
params['project'].delete('state')
@project.attributes = params['project']
@project.name = deurlize(@project.name)
if @project.save
render :partial => 'project_listing', :object => @project
if params['wants_render']
if (@project.hidden?)
@project_project_hidden_todo_counts = Hash.new
@project_project_hidden_todo_counts[@project.id] = @project.reload().not_done_todo_count(:include_project_hidden_todos => true)
else
@project_not_done_counts[@project.id] = @project.reload().not_done_todo_count(:include_project_hidden_todos => true)
end
render :partial => 'project_listing', :object => @project
else
render :text => 'Success'
end
else
flash[:warning] = "Couldn't update project"
render :text => ''
@ -242,14 +232,14 @@ class ProjectController < ApplicationController
@projects = @user.projects
@contexts = @user.contexts
@todos = @user.todos
@done = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = ?", @user.id, true], :include => [:project], :order => "completed DESC")
init_not_done_counts
@done = @user.todos.find_in_state(:all, :completed, :order => "completed_at DESC")
init_data_for_sidebar
end
def init_todos
check_user_set_project
@done = @project.done_todos
@not_done = @project.not_done_todos
@not_done = @project.not_done_todos(:include_project_hidden_todos => true)
@count = @not_done.size
end

View file

@ -26,8 +26,8 @@ class TodoController < ApplicationController
@done = nil
if max_completed > 0
@done = Todo.find(:all,
:conditions => ['todos.user_id = ? and todos.done = ?', @user.id, true],
:order => 'todos.completed DESC',
:conditions => ['todos.user_id = ? and todos.state = ?', @user.id, 'completed'],
:order => 'todos.completed_at DESC',
:limit => max_completed,
:include => [ :project, :context ])
end
@ -39,7 +39,7 @@ class TodoController < ApplicationController
end
# Set count badge to number of not-done, not hidden context items
@count = @todos.reject { |x| x.done? || x.context.hide? }.size
@count = @todos.reject { |x| !x.active? || x.context.hide? }.size
respond_to do |wants|
wants.html
@ -79,7 +79,7 @@ class TodoController < ApplicationController
wants.js do
if @saved
init_todos
@up_count = @todos.reject { |x| x.done? or x.context.hide? }.size.to_s
@up_count = @todos.reject { |x| !x.active? or x.context.hide? }.size.to_s
end
render :action => 'create'
end
@ -119,21 +119,20 @@ class TodoController < ApplicationController
#
def toggle_check
init
logger.info "source view is " + @source_view
@item = check_user_return_item
@item.toggle!('done')
@item.completed = Time.now() # For some reason, the before_save in todo.rb stopped working
@item.toggle_completion()
@saved = @item.save
@remaining_undone_in_context = @user.contexts.find(@item.context_id).not_done_todos.length
if @saved
@down_count = @todos.reject { |x| x.done? || x.context.hide? }.size.to_s
@remaining_undone_in_context = @user.contexts.find(@item.context_id).not_done_todo_count
determine_down_count
end
return if request.xhr?
if @saved
redirect_with_notice "The action <strong>'#{@item.description}'</strong> was marked as <strong>#{@item.done? ? 'complete' : 'incomplete' }</strong>", :action => "index"
redirect_with_notice "The action <strong>'#{@item.description}'</strong> was marked as <strong>#{@item.completed? ? 'complete' : 'incomplete' }</strong>", :action => "index"
else
redirect_with_notice "The action <strong>'#{@item.description}'</strong> was NOT marked as <strong>#{@item.done? ? 'complete' : 'incomplete' } due to an error on the server.</strong>", :action => "index"
redirect_with_notice "The action <strong>'#{@item.description}'</strong> was NOT marked as <strong>#{@item.completed? ? 'complete' : 'incomplete' } due to an error on the server.</strong>", :action => "index"
end
end
@ -207,7 +206,7 @@ class TodoController < ApplicationController
wants.js do
if @saved
@down_count = determine_down_count
determine_down_count
source_view do |from|
from.todo do
@remaining_undone_in_context = @user.contexts.find(@context_id).not_done_todos.length
@ -249,7 +248,7 @@ class TodoController < ApplicationController
private
def check_user_return_item
item = Todo.find( params['id'] )
item = Todo.find( params['id'].to_i )
if @user == item.user
return item
else
@ -267,39 +266,30 @@ class TodoController < ApplicationController
def init
@source_view = params['_source_view'] || 'todo'
@projects = @user.projects
@contexts = @user.contexts
init_todos
init_not_done_counts
init_data_for_sidebar
init_todos
end
def init_todos
# Exclude hidden projects from count on home page
@todos = Todo.find(:all,
:conditions => ['todos.user_id = ? and todos.type = ? and (projects.state != ? or todos.project_id is ?)', @user.id, "Immediate", "hidden", nil],
:include => [ :project, :context ])
@todos = @user.todos.find(:all, :conditions => ['todos.state = ? or todos.state = ?', 'active', 'complete'], :include => [ :project, :context ])
# Exclude hidden projects from the home page
@not_done_todos = Todo.find(:all,
:conditions => ['todos.user_id = ? and todos.type = ? and todos.done = ? and (projects.state != ? or todos.project_id is ?)', @user.id, "Immediate", false, "hidden", nil],
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
:include => [ :project, :context ])
@not_done_todos = @user.todos.find(:all, :conditions => ['todos.state = ?', 'active'], :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC", :include => [ :project, :context ])
end
def determine_down_count
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 ])
@down_count = Todo.count_by_sql(['SELECT COUNT(*) FROM todos, contexts WHERE todos.context_id = contexts.id and todos.user_id = ? and todos.state = ? and contexts.hide = ?', @user.id, 'active', 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])
@down_count = @user.contexts.find(@item.context_id).todos.count_in_state(:active)
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
unless @item.project_id == nil
@down_count = @user.projects.find(@item.project_id).todos.count_in_state(:active)
end
end
end
end