From c51587e4229f7dad7876ae8be33f1e8af0f5c487 Mon Sep 17 00:00:00 2001 From: lukemelia Date: Wed, 15 Nov 2006 09:05:07 +0000 Subject: [PATCH] * 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 --- tracks/app/controllers/application.rb | 19 ++++--- tracks/app/controllers/context_controller.rb | 34 +---------- tracks/app/controllers/deferred_controller.rb | 32 ++++------- tracks/app/controllers/feed_controller.rb | 6 +- tracks/app/controllers/mobile_controller.rb | 27 +++++---- tracks/app/controllers/project_controller.rb | 44 ++++++--------- tracks/app/controllers/todo_controller.rb | 52 +++++++---------- tracks/app/helpers/application_helper.rb | 6 +- tracks/app/helpers/feed_helper.rb | 4 +- tracks/app/helpers/todo_helper.rb | 4 +- tracks/app/models/context.rb | 2 +- tracks/app/models/deferred.rb | 9 --- tracks/app/models/immediate.rb | 2 - tracks/app/models/project.rb | 29 +++++++++- tracks/app/models/todo.rb | 51 +++++++++++++++-- tracks/app/models/user.rb | 2 +- tracks/app/views/context/_show_items.rhtml | 4 +- tracks/app/views/context/toggle_check.rjs | 26 --------- tracks/app/views/feed/rss.rxml | 2 +- tracks/app/views/layouts/standard.rhtml | 2 +- .../app/views/project/_project_listing.rhtml | 2 +- tracks/app/views/project/_show_items.rhtml | 4 +- tracks/app/views/project/show.rhtml | 2 +- tracks/app/views/project/toggle_check.rjs | 26 --------- tracks/app/views/shared/sidebar.rhtml | 3 +- tracks/app/views/todo/_done.rhtml | 4 +- tracks/app/views/todo/_edit_form.rhtml | 6 +- tracks/app/views/todo/_item.rhtml | 10 ++-- tracks/app/views/todo/toggle_check.rjs | 28 +++++++--- .../app/views/user/preference_edit_form.rhtml | 1 + tracks/app/views/user/preferences.rhtml | 1 + .../019_convert_todo_to_state_machine.rb | 44 +++++++++++++++ ...pref_to_show_hidden_projects_in_sidebar.rb | 13 +++++ tracks/db/schema.rb | 26 ++++----- tracks/lib/acts_as_todo_container.rb | 36 +++++++++--- tracks/test/fixtures/preferences.yml | 2 + tracks/test/fixtures/todos.yml | 56 +++++++++---------- .../functional/context_controller_test.rb | 7 ++- .../functional/project_controller_test.rb | 31 +++++++++- .../test/functional/todo_controller_test.rb | 39 +++++++++++-- .../test/functional/user_controller_test.rb | 3 - tracks/test/unit/todo_test.rb | 8 +-- tracks/vendor/plugins/arts/README | 8 ++- .../plugins/arts/{meta.yml => about.yml} | 2 +- tracks/vendor/plugins/arts/init.rb | 3 +- tracks/vendor/plugins/arts/lib/arts.rb | 25 ++++++++- tracks/vendor/plugins/arts/test/arts_test.rb | 41 ++++++++++++++ 47 files changed, 487 insertions(+), 301 deletions(-) delete mode 100644 tracks/app/models/deferred.rb delete mode 100644 tracks/app/models/immediate.rb delete mode 100644 tracks/app/views/context/toggle_check.rjs delete mode 100644 tracks/app/views/project/toggle_check.rjs create mode 100644 tracks/db/migrate/019_convert_todo_to_state_machine.rb create mode 100644 tracks/db/migrate/020_pref_to_show_hidden_projects_in_sidebar.rb rename tracks/vendor/plugins/arts/{meta.yml => about.yml} (90%) diff --git a/tracks/app/controllers/application.rb b/tracks/app/controllers/application.rb index 78773ff7..a9e39020 100644 --- a/tracks/app/controllers/application.rb +++ b/tracks/app/controllers/application.rb @@ -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 diff --git a/tracks/app/controllers/context_controller.rb b/tracks/app/controllers/context_controller.rb index ce5c9b46..621e4ace 100644 --- a/tracks/app/controllers/context_controller.rb +++ b/tracks/app/controllers/context_controller.rb @@ -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 '#{@item.description}' was marked as #{@item.done? ? 'complete' : 'incomplete' }" - else - flash[:notice] = "The action '#{@item.description}' was NOT marked as #{@item.done? ? 'complete' : 'incomplete' } due to an error on the server." - 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 diff --git a/tracks/app/controllers/deferred_controller.rb b/tracks/app/controllers/deferred_controller.rb index 1fad2a3f..4f191ff8 100644 --- a/tracks/app/controllers/deferred_controller.rb +++ b/tracks/app/controllers/deferred_controller.rb @@ -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 diff --git a/tracks/app/controllers/feed_controller.rb b/tracks/app/controllers/feed_controller.rb index ef8b7b35..9abfd48a 100644 --- a/tracks/app/controllers/feed_controller.rb +++ b/tracks/app/controllers/feed_controller.rb @@ -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 diff --git a/tracks/app/controllers/mobile_controller.rb b/tracks/app/controllers/mobile_controller.rb index 22b3700c..55f80ace 100644 --- a/tracks/app/controllers/mobile_controller.rb +++ b/tracks/app/controllers/mobile_controller.rb @@ -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 diff --git a/tracks/app/controllers/project_controller.rb b/tracks/app/controllers/project_controller.rb index addf7072..0e6db1c6 100644 --- a/tracks/app/controllers/project_controller.rb +++ b/tracks/app/controllers/project_controller.rb @@ -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 '#{@item.description}' was marked as #{@item.done? ? 'complete' : 'incomplete' }" - else - flash[:notice] = "The action '#{@item.description}' was NOT marked as #{@item.done? ? 'complete' : 'incomplete' } due to an error on the server." - 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 diff --git a/tracks/app/controllers/todo_controller.rb b/tracks/app/controllers/todo_controller.rb index f2565e50..0f46261e 100644 --- a/tracks/app/controllers/todo_controller.rb +++ b/tracks/app/controllers/todo_controller.rb @@ -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 '#{@item.description}' was marked as #{@item.done? ? 'complete' : 'incomplete' }", :action => "index" + redirect_with_notice "The action '#{@item.description}' was marked as #{@item.completed? ? 'complete' : 'incomplete' }", :action => "index" else - redirect_with_notice "The action '#{@item.description}' was NOT marked as #{@item.done? ? 'complete' : 'incomplete' } due to an error on the server.", :action => "index" + redirect_with_notice "The action '#{@item.description}' was NOT marked as #{@item.completed? ? 'complete' : 'incomplete' } due to an error on the server.", :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 diff --git a/tracks/app/helpers/application_helper.rb b/tracks/app/helpers/application_helper.rb index f32b36e5..2635e54c 100644 --- a/tracks/app/helpers/application_helper.rb +++ b/tracks/app/helpers/application_helper.rb @@ -117,7 +117,11 @@ module ApplicationHelper # actions or multiple actions # def count_undone_todos(todos_parent, string="actions") - count = eval "@#{todos_parent.class.to_s.downcase}_not_done_counts[#{todos_parent.id}]" + if (todos_parent.is_a?(Project) && todos_parent.hidden?) + count = eval "@project_project_hidden_todo_counts[#{todos_parent.id}]" + else + count = eval "@#{todos_parent.class.to_s.downcase}_not_done_counts[#{todos_parent.id}]" + end count = 0 if count == nil #count = todos_parent.todos.select{|t| !t.done }.size if count == 1 diff --git a/tracks/app/helpers/feed_helper.rb b/tracks/app/helpers/feed_helper.rb index cb73b6c4..2ebed443 100644 --- a/tracks/app/helpers/feed_helper.rb +++ b/tracks/app/helpers/feed_helper.rb @@ -11,8 +11,8 @@ module FeedHelper if item.context_id == context.id result_string << "\n" + context.name.upcase + ":\n" if result_string.empty? - if (item.done == 1 || item.done == true) && item.completed - result_string << " [Completed: " + format_date(item.completed) + "] " + if (item.completed?) && item.completed_at + result_string << " [Completed: " + format_date(item.completed_at) + "] " end if item.due diff --git a/tracks/app/helpers/todo_helper.rb b/tracks/app/helpers/todo_helper.rb index 108b9d86..b3e0eb69 100644 --- a/tracks/app/helpers/todo_helper.rb +++ b/tracks/app/helpers/todo_helper.rb @@ -22,7 +22,7 @@ module TodoHelper { :url => url_options, :confirm => "Are you sure that you want to delete the action, \'#{item.description}\'?" }, { :class => "icon" } ) + "\n" - if !item.done? + if !item.completed? url_options[:action] = 'edit' str << link_to_remote( image_tag_for_edit(item), { :url => url_options, :loading => visual_effect(:pulsate, "action-#{item.id}-edit-icon") }, @@ -42,7 +42,7 @@ module TodoHelper # * l3: created more than 3 x staleness_starts # def staleness_class(item) - if item.due || item.done? + if item.due || item.completed? return "" elsif item.created_at < (@user.preference.staleness_starts * 3).days.ago return " stale_l3" diff --git a/tracks/app/models/context.rb b/tracks/app/models/context.rb index cb3f4daa..8e4a2911 100644 --- a/tracks/app/models/context.rb +++ b/tracks/app/models/context.rb @@ -1,6 +1,6 @@ class Context < ActiveRecord::Base - has_many :todos, :dependent => true, :order => "completed DESC" + has_many :todos, :dependent => true, :order => "completed_at DESC" belongs_to :user acts_as_list :scope => :user diff --git a/tracks/app/models/deferred.rb b/tracks/app/models/deferred.rb deleted file mode 100644 index a6b5c884..00000000 --- a/tracks/app/models/deferred.rb +++ /dev/null @@ -1,9 +0,0 @@ -class Deferred < Todo - validates_presence_of :show_from - - def validate - if show_from != nil && show_from < Date.today() - errors.add("Show From", "must be a date in the future.") - end - end -end diff --git a/tracks/app/models/immediate.rb b/tracks/app/models/immediate.rb deleted file mode 100644 index bcd26694..00000000 --- a/tracks/app/models/immediate.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Immediate < Todo -end diff --git a/tracks/app/models/project.rb b/tracks/app/models/project.rb index 561b6a30..6a963a54 100644 --- a/tracks/app/models/project.rb +++ b/tracks/app/models/project.rb @@ -16,7 +16,7 @@ class Project < ActiveRecord::Base acts_as_todo_container :find_todos_include => :context state :active - state :hidden + state :hidden, :enter => :hide_todos, :exit => :unhide_todos state :completed event :activate do @@ -44,6 +44,33 @@ class Project < ActiveRecord::Base def linkurl_present? attribute_present?("linkurl") end + + def hide_todos + todos.each do |t| + t.hide! unless t.completed? + t.save + end + end + + def unhide_todos + todos.each do |t| + t.unhide! if t.project_hidden? + t.save + end + end + + def transition_to(candidate_state) + case candidate_state.to_sym + when current_state + return + when :hidden + hide! + when :active + activate! + when :completed + complete! + end + end end diff --git a/tracks/app/models/todo.rb b/tracks/app/models/todo.rb index 0f7ea2e0..82e51bae 100644 --- a/tracks/app/models/todo.rb +++ b/tracks/app/models/todo.rb @@ -5,6 +5,34 @@ class Todo < ActiveRecord::Base belongs_to :project belongs_to :user + acts_as_state_machine :initial => :active, :column => 'state' + + state :active, :enter => Proc.new { |t| t.show_from = nil } + state :project_hidden + state :completed, :enter => Proc.new { |t| t.completed_at = Time.now() }, :exit => Proc.new { |t| t.completed_at = nil } + state :deferred + + event :defer do + transitions :to => :deferred, :from => [:active] + end + + event :complete do + transitions :to => :completed, :from => [:active, :project_hidden, :deferred] + end + + event :activate do + transitions :to => :active, :from => [:project_hidden, :completed, :deferred] + end + + event :hide do + transitions :to => :project_hidden, :from => [:active, :deferred] + end + + event :unhide do + transitions :to => :deferred, :from => [:project_hidden], :guard => Proc.new{|t| t.show_from != nil} + transitions :to => :active, :from => [:project_hidden] + end + attr_protected :user # Description field can't be empty, and must be < 100 bytes @@ -13,6 +41,21 @@ class Todo < ActiveRecord::Base validates_length_of :description, :maximum => 100 validates_length_of :notes, :maximum => 60000, :allow_nil => true # validates_chronic_date :due, :allow_nil => true + validates_presence_of :show_from, :if => :deferred? + + def validate + if deferred? && show_from != nil && show_from < Date.today() + errors.add("Show From", "must be a date in the future.") + end + end + + def toggle_completion + if completed? + activate! + else + complete! + end + end alias_method :original_project, :project @@ -26,16 +69,16 @@ class Todo < ActiveRecord::Base def self.find_completed(user_id) done = self.find(:all, - :conditions => ['todos.user_id = ? and todos.done = ? and todos.completed is not null', user_id, true], - :order => 'todos.completed DESC', + :conditions => ['todos.user_id = ? and todos.state = ? and todos.completed_at is not null', user_id, 'completed'], + :order => 'todos.completed_at DESC', :include => [ :project, :context ]) def done.completed_within( date ) - reject { |x| x.completed < date } + reject { |x| x.completed_at < date } end def done.completed_more_than( date ) - reject { |x| x.completed > date } + reject { |x| x.completed_at > date } end done diff --git a/tracks/app/models/user.rb b/tracks/app/models/user.rb index 45f08a73..0a903e1f 100644 --- a/tracks/app/models/user.rb +++ b/tracks/app/models/user.rb @@ -3,7 +3,7 @@ require 'digest/sha1' class User < ActiveRecord::Base has_many :contexts, :order => "position ASC" has_many :projects, :order => "position ASC" - has_many :todos, :order => "completed DESC, created_at DESC" + has_many :todos, :order => "completed_at DESC, created_at DESC" has_many :notes, :order => "created_at DESC" has_one :preference diff --git a/tracks/app/views/context/_show_items.rhtml b/tracks/app/views/context/_show_items.rhtml index c8154136..1bb6feaf 100644 --- a/tracks/app/views/context/_show_items.rhtml +++ b/tracks/app/views/context/_show_items.rhtml @@ -1,6 +1,6 @@ <% item = show_items %> -<% if !item.done? %> +<% if !item.completed? %>
<%= form_remote_tag( :url => url_for( :controller => "context", :action => "toggle_check", :id => item.id ), :html => { :id=> "checkbox-notdone-#{item.id}", :class => "inline-form" }, @@ -82,7 +82,7 @@
- <%= format_date( item.completed ) %> + <%= format_date( item.completed_at ) %> <%= sanitize(item.description) %> <% if item.project_id %> <%= link_to( "[P]", { :controller => "project", :action => "show", :name => urlize(item.project.name) }, :title => "View project: #{item.project.name}" ) %> diff --git a/tracks/app/views/context/toggle_check.rjs b/tracks/app/views/context/toggle_check.rjs deleted file mode 100644 index bb25d3b8..00000000 --- a/tracks/app/views/context/toggle_check.rjs +++ /dev/null @@ -1,26 +0,0 @@ -if @saved - page.remove "item-#{@item.id}-container" - if @item.done? - # Don't try to insert contents into a non-existent container! - unless @user.preference.hide_completed_actions? - page.insert_html :top, "completed", :partial => 'todo/item', :locals => { :parent_container_type => "completed" } - page.visual_effect :highlight, "item-#{@item.id}", {'startcolor' => "'#99ff99'"} - if @down_count == '0' - page["c#{@item.context_id}empty-nd"].show - end - page.hide "empty-d" # If we've checked something as done, completed items can't be empty - end - else - page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@item.context_id}" - page.insert_html :bottom, "c#{@item.context_id}", :partial => 'todo/item', :locals => { :parent_container_type => "context" } - page.visual_effect :highlight, "item-#{@item.id}", {'startcolor' => "'#99ff99'"} - if @done_count == '0' - page.show "empty-d" - end - page["c#{@item.context_id}empty-nd"].hide # If we've checked something as undone, uncompleted items can't be empty - end - page.hide "status" - page.replace_html "badge_count", @down_count -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 \ No newline at end of file diff --git a/tracks/app/views/feed/rss.rxml b/tracks/app/views/feed/rss.rxml index c9e5ae5f..e73cd78b 100644 --- a/tracks/app/views/feed/rss.rxml +++ b/tracks/app/views/feed/rss.rxml @@ -11,7 +11,7 @@ xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do due = "
Due: #{format_date(i.due)}
\n" if i.due? toggle_link = link_to( "mark as done", {:only_path => false, :controller => "todo", :action => "toggle_check", :id => i.id}) done = "
#{toggle_link}
" unless i.completed? - done = "
Completed: #{format_date(i.completed)}
\n" if i.completed? + done = "
Completed: #{format_date(i.completed_at)}
\n" if i.completed? context_link = link_to( i.context.name, { :only_path => false, :controller => "context", :action => "show", :name => urlize(i.context.name) } ) if i.project_id? project_link = link_to (i.project.name, { :only_path => false, :controller => "project", :action => "show", :name => urlize(i.project.name)} ) diff --git a/tracks/app/views/layouts/standard.rhtml b/tracks/app/views/layouts/standard.rhtml index 8711d6ce..603f127c 100644 --- a/tracks/app/views/layouts/standard.rhtml +++ b/tracks/app/views/layouts/standard.rhtml @@ -2,7 +2,7 @@ - <% if @prefs["refresh"].to_i != 0 -%> + <% if @prefs.refresh != 0 -%> ;url=<%= request.request_uri %>"> <% end -%> <%= stylesheet_link_tag "standard" %> diff --git a/tracks/app/views/project/_project_listing.rhtml b/tracks/app/views/project/_project_listing.rhtml index c2f0c01e..642ccf4d 100644 --- a/tracks/app/views/project/_project_listing.rhtml +++ b/tracks/app/views/project/_project_listing.rhtml @@ -34,7 +34,7 @@ <%= render :partial => 'project_form', :object => project %> - +
    Cancel
diff --git a/tracks/app/views/project/_show_items.rhtml b/tracks/app/views/project/_show_items.rhtml index 747e800d..5989e818 100644 --- a/tracks/app/views/project/_show_items.rhtml +++ b/tracks/app/views/project/_show_items.rhtml @@ -1,5 +1,5 @@ <% item = show_items %> -<% if !item.done? %> +<% if !item.completed? %>
<%= form_remote_tag( :url => url_for( :controller => "project", :action => "toggle_check", :id => item.id ), :html => { :id=> "checkbox-notdone-#{item.id}", :class => "inline-form" }, @@ -82,7 +82,7 @@
- <%= format_date( item.completed ) %> + <%= format_date( item.completed_at ) %> <%= sanitize(item.description) %> <% if item.project_id %> <%= link_to( "[C]", { :controller => "context", :action => "show", :name => urlize(item.context.name) }, :title => "View context: #{item.context.name}" ) %> diff --git a/tracks/app/views/project/show.rhtml b/tracks/app/views/project/show.rhtml index 1e6fbd70..0cd19b0e 100644 --- a/tracks/app/views/project/show.rhtml +++ b/tracks/app/views/project/show.rhtml @@ -38,7 +38,7 @@

Status

<% ['active', 'hidden', 'completed'].each do | state | %> - <% js = "new Ajax.Request('#{ url_for :controller => 'project', :action => 'update', :id => @project.id, 'project[state]' => state }', {asynchronous:true, evalScripts:true});" %> + <% js = "new Ajax.Request('#{ url_for :controller => 'project', :action => 'update', :id => @project.id, :wants_render => false, 'project[state]' => state }', {asynchronous:true, evalScripts:true});" %> <%= radio_button(:project, 'state', state, {:class => 'project-done', :onclick => js}) %> <%= state.titlecase %> <% end %>
diff --git a/tracks/app/views/project/toggle_check.rjs b/tracks/app/views/project/toggle_check.rjs deleted file mode 100644 index 4ac8a403..00000000 --- a/tracks/app/views/project/toggle_check.rjs +++ /dev/null @@ -1,26 +0,0 @@ -if @saved - page.remove "item-#{@item.id}-container" - if @item.done? - # Don't try to insert contents into a non-existent container! - unless @user.preference.hide_completed_actions? - page.insert_html :top, "completed", :partial => 'todo/item', :locals => { :parent_container_type => "project" } - page.visual_effect :highlight, "item-#{@item.id}", {'startcolor' => "'#99ff99'"} - if @down_count == '0' - page["p#{@item.project_id}empty-nd"].show - end - page.hide "empty-d" # If we've checked something as done, completed items can't be empty - end - else - page.call "todoItems.ensureVisibleWithEffectAppear", "p#{@item.project_id}" - page.insert_html :bottom, "p#{@item.project_id}", :partial => 'todo/item', :locals => { :parent_container_type => "project" } - page.visual_effect :highlight, "item-#{@item.id}", {'startcolor' => "'#99ff99'"} - if @done_count == '0' - page.show "empty-d" - end - page["p#{@item.project_id}empty-nd"].hide # If we've checked something as undone, uncompleted items can't be empty - end - page.hide "status" - page.replace_html "badge_count", @down_count -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 \ No newline at end of file diff --git a/tracks/app/views/shared/sidebar.rhtml b/tracks/app/views/shared/sidebar.rhtml index 6f456e4e..f2805c4a 100644 --- a/tracks/app/views/shared/sidebar.rhtml +++ b/tracks/app/views/shared/sidebar.rhtml @@ -6,13 +6,14 @@ <% end -%> +<% if @user.preference.show_hidden_projects_in_sidebar %>

Hidden Projects:

    <% for project in @projects.select{|p| p.hidden? } -%> <% end -%>
- +<% end %> <% if @user.preference.show_completed_projects_in_sidebar %>

Completed Projects:

diff --git a/tracks/app/views/todo/_done.rhtml b/tracks/app/views/todo/_done.rhtml index c8a25c25..7126e5c9 100644 --- a/tracks/app/views/todo/_done.rhtml +++ b/tracks/app/views/todo/_done.rhtml @@ -1,7 +1,7 @@ -<% if done.completed %> +<% if done.completed_at %> <%= image_tag( "done", :width=>"16", :height=>"16", :border=>"0") %> - <%= format_date( done.completed ) %> + <%= format_date( done.completed_at ) %> <%= " " + sanitize(done.description) + " "%> <% if done.project_id %> diff --git a/tracks/app/views/todo/_edit_form.rhtml b/tracks/app/views/todo/_edit_form.rhtml index edd14b2f..266be72a 100644 --- a/tracks/app/views/todo/_edit_form.rhtml +++ b/tracks/app/views/todo/_edit_form.rhtml @@ -46,13 +46,13 @@