diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 20055838..80bca004 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -75,7 +75,9 @@ class ApplicationController < ActionController::Base if todos_parent.nil? count = 0 elsif (todos_parent.is_a?(Project) && todos_parent.hidden?) - count = @project_project_hidden_todo_counts[todos_parent.id] + count = @project_hidden_todo_counts[todos_parent.id] + elsif (todos_parent.is_a?(Context) && todos_parent.hidden?) + count = @context_hidden_todo_counts[todos_parent.id] else count = eval "@#{todos_parent.class.to_s.downcase}_not_done_counts[#{todos_parent.id}]" end @@ -200,7 +202,10 @@ class ApplicationController < ActionController::Base init_not_done_counts if prefs.show_hidden_projects_in_sidebar - init_project_hidden_todo_counts(['project']) + init_hidden_todo_counts(['project']) + end + if prefs.show_hidden_contexts_in_sidebar + init_hidden_todo_counts(['context']) end end @@ -211,9 +216,9 @@ class ApplicationController < ActionController::Base end end - def init_project_hidden_todo_counts(parents = ['project','context']) + def init_hidden_todo_counts(parents = ['project', 'context']) parents.each do |parent| - eval("@#{parent}_project_hidden_todo_counts ||= current_user.todos.active_or_hidden.count_by_group('#{parent}_id')") + eval("@#{parent}_hidden_todo_counts ||= current_user.todos.active_or_hidden.count_by_group('#{parent}_id')") end end diff --git a/app/controllers/contexts_controller.rb b/app/controllers/contexts_controller.rb index 306f686a..4e2e4448 100644 --- a/app/controllers/contexts_controller.rb +++ b/app/controllers/contexts_controller.rb @@ -12,7 +12,10 @@ class ContextsController < ApplicationController @active_contexts = current_user.contexts.active @hidden_contexts = current_user.contexts.hidden @closed_contexts = current_user.contexts.closed - init_not_done_counts(['context']) unless request.format == :autocomplete + unless request.format == :autocomplete + init_not_done_counts(['context']) + init_hidden_todo_counts(['context']) + end respond_to do |format| format.html &render_contexts_html @@ -37,7 +40,7 @@ class ContextsController < ApplicationController unless @context.nil? @max_completed = current_user.prefs.show_number_completed @done = @context.todos.completed.limit(@max_completed).reorder("todos.completed_at DESC, todos.created_at DESC").includes(Todo::DEFAULT_INCLUDES) - @not_done_todos = @context.todos.active.reorder("todos.due IS NULL, todos.due ASC, todos.created_at ASC").includes(Todo::DEFAULT_INCLUDES) + @not_done_todos = @context.todos.active_or_hidden.not_project_hidden.reorder('todos.due IS NULL, todos.due ASC, todos.created_at ASC').includes(Todo::DEFAULT_INCLUDES) @todos_without_project = @not_done_todos.select{|t| t.project.nil?} @deferred_todos = @context.todos.deferred.includes(Todo::DEFAULT_INCLUDES) @@ -77,6 +80,7 @@ class ContextsController < ApplicationController format.js do @down_count = current_user.contexts.size init_not_done_counts + init_hidden_todo_counts(['context']) end format.xml do if @context.new_record? diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 1e74c331..f2e4728d 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -14,7 +14,7 @@ class ProjectsController < ApplicationController else @contexts = current_user.contexts init_not_done_counts(['project']) - init_project_hidden_todo_counts(['project']) + init_hidden_todo_counts(['project']) if params[:only_active_with_no_next_actions] @projects = current_user.projects.active.select { |p| count_undone_todos(p) == 0 } else @@ -64,7 +64,7 @@ class ProjectsController < ApplicationController @current_projects = projects.uncompleted.select { |p| not (p.needs_review?(current_user)) }.sort_by { |p| p.last_reviewed || Time.zone.at(0) } init_not_done_counts(['project']) - init_project_hidden_todo_counts(['project']) + init_hidden_todo_counts(['project']) current_user.projects.cache_note_counts @page_title = t('projects.list_reviews') @@ -220,7 +220,7 @@ class ProjectsController < ApplicationController @contexts = current_user.contexts update_state_counts init_data_for_sidebar - init_project_hidden_todo_counts(['project']) + init_hidden_todo_counts(['project']) template = 'projects/update' @@ -292,7 +292,7 @@ class ProjectsController < ApplicationController @projects = current_user.projects.alphabetize(:state => @state) if @state @contexts = current_user.contexts init_not_done_counts(['project']) - init_project_hidden_todo_counts(['project']) if @state == 'hidden' + init_hidden_todo_counts(['project']) if @state == 'hidden' end def actionize @@ -300,7 +300,7 @@ class ProjectsController < ApplicationController @projects = current_user.projects.actionize(:state => @state) if @state @contexts = current_user.contexts init_not_done_counts(['project']) - init_project_hidden_todo_counts(['project']) if @state == 'hidden' + init_hidden_todo_counts(['project']) if @state == 'hidden' end def done_todos diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 6dfdc787..98fae72a 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -13,7 +13,7 @@ class SearchController < ApplicationController @count = searcher.number_of_finds init_not_done_counts - init_project_hidden_todo_counts + init_hidden_todo_counts end def index diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index 7a290667..dcbf31fa 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -99,8 +99,7 @@ class TodosController < ApplicationController @todo.add_predecessor_list(p.predecessor_list) @saved = @todo.save @todo.tag_with(tag_list) if @saved && tag_list.present? - @todo.update_state_from_project if @saved - @todo.block! if @todo.should_be_blocked? + @todo.block! if @todo.uncompleted_predecessors? else @saved = false end @@ -448,7 +447,9 @@ class TodosController < ApplicationController @todo.reload # refresh context and project object too (not only their id's) update_dependency_state - update_todo_state_if_project_changed + if @project_changed + @remaining_undone_in_project = current_user.projects.find(@original_item_project_id).todos.active.count if source_view_is :project + end determine_changes_by_this_update determine_remaining_in_container_count( (@context_changed || @project_changed) ? @original_item : @todo) @@ -588,7 +589,7 @@ class TodosController < ApplicationController respond_to do |format| format.html do init_not_done_counts - init_project_hidden_todo_counts + init_hidden_todo_counts init_data_for_sidebar unless mobile? end format.m @@ -1189,12 +1190,6 @@ end end end - def update_todo_state_if_project_changed - if @project_changed - @todo.update_state_from_project - @remaining_undone_in_project = current_user.projects.find(@original_item_project_id).todos.active.count if source_view_is :project - end - end def update_context @context_changed = false diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index ec1c9348..750ace39 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -442,21 +442,11 @@ module TodosHelper # === handle CRUD actions for todos def show_todo_on_current_context_page - return @todo.context_id==@default_context.id - end - - def todo_should_not_be_hidden_on_context_page - return !@todo.hidden? || # todo is not hidden --> show - (@todo.hidden? && @todo.context.hidden?) # todo is hidden, but context is hidden too --> show + @todo.context_id == @default_context.id && (!@todo.hidden? || @todo.context.hidden?) end def show_todo_on_current_project_page - return @todo.project.id == @default_project.id - end - - def todo_should_not_be_hidden_on_project_page - return !@todo.hidden? || - (@todo.project_hidden? && @todo.project.hidden?) + @todo.project.id == @default_project.id && (!@todo.hidden? || @todo.project.hidden?) end def should_show_new_item(todo = @todo) @@ -464,9 +454,9 @@ module TodosHelper source_view do |page| page.todo { return !todo.hidden? && !todo.deferred? } page.deferred { return todo.deferred? || todo.pending? } - page.context { return show_todo_on_current_context_page && todo_should_not_be_hidden_on_context_page } page.tag { return todo.has_tag?(@tag_name) } - page.project { return show_todo_on_current_project_page && todo_should_not_be_hidden_on_project_page } + page.context { return show_todo_on_current_context_page } + page.project { return show_todo_on_current_project_page } end return false end diff --git a/app/models/project.rb b/app/models/project.rb index ac4e7058..f702ce71 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -27,7 +27,7 @@ class Project < ActiveRecord::Base aasm :column => :state do state :active, :initial => true - state :hidden, :enter => :hide_todos, :exit => :unhide_todos + state :hidden state :completed, :enter => :set_completed_at_date, :exit => :clear_completed_at_date event :activate do @@ -53,24 +53,6 @@ class Project < ActiveRecord::Base self.last_reviewed = Time.now end - def hide_todos - todos.each do |t| - unless t.completed? || t.deferred? - t.hide! - t.save - end - end - end - - def unhide_todos - todos.each do |t| - if t.project_hidden? - t.unhide! - t.save - end - end - end - def set_completed_at_date self.completed_at = Time.zone.now end diff --git a/app/models/todo.rb b/app/models/todo.rb index fa6a7e90..f24c34dc 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -26,21 +26,25 @@ class Todo < ActiveRecord::Base has_many :attachments, dependent: :destroy # scopes for states of this todo - scope :active, -> { where state: 'active' } - scope :active_or_hidden, -> { where "todos.state = ? OR todos.state = ?", 'active', 'project_hidden' } - scope :not_completed, -> { where 'NOT (todos.state = ?)', 'completed' } - scope :completed, -> { where "todos.state = ?", 'completed' } - scope :deferred, -> { where "todos.state = ?", 'deferred' } + scope :active, -> { active_or_hidden.not_hidden } + scope :active_or_hidden, -> { where state: 'active' } + scope :context_hidden, -> { joins('INNER JOIN contexts c ON c.id = todos.context_id').where('c.state = ?', 'hidden') } + scope :project_hidden, -> { joins('LEFT OUTER JOIN projects p ON p.id = todos.project_id').where('p.state = ?', 'hidden') } + scope :completed, -> { where 'todos.state = ?', 'completed' } + scope :deferred, -> { where 'todos.state = ?', 'deferred' } scope :blocked, -> {where 'todos.state = ?', 'pending' } scope :pending, -> {where 'todos.state = ?', 'pending' } - scope :deferred_or_blocked, -> { where "(todos.state = ?) OR (todos.state = ?)", "deferred", "pending" } - scope :not_deferred_or_blocked, -> { where "(NOT todos.state=?) AND (NOT todos.state = ?)", "deferred", "pending" } + scope :deferred_or_blocked, -> { where '(todos.state = ?) OR (todos.state = ?)', 'deferred', 'pending' } scope :hidden, -> { - joins("INNER JOIN contexts c_hidden ON c_hidden.id = todos.context_id"). - where("todos.state = ? OR (c_hidden.state = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?))", 'project_hidden', 'hidden', 'active', 'deferred', 'pending') } - scope :not_hidden, -> { - joins("INNER JOIN contexts c_hidden ON c_hidden.id = todos.context_id"). - where('NOT(todos.state = ? OR (c_hidden.state = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?)))','project_hidden', 'hidden', 'active', 'deferred', 'pending') } + joins('INNER JOIN contexts c_hidden ON c_hidden.id = todos.context_id'). + joins('LEFT OUTER JOIN projects p_hidden ON p_hidden.id = todos.project_id'). + where('(c_hidden.state = ? OR p_hidden.state = ?)', 'hidden', 'hidden'). + where('NOT todos.state = ?', 'completed') } + scope :not_hidden, -> { not_context_hidden.not_project_hidden } + scope :not_deferred_or_blocked, -> { where '(NOT todos.state=?) AND (NOT todos.state = ?)', 'deferred', 'pending' } + scope :not_project_hidden, -> { joins('LEFT OUTER JOIN projects p ON p.id = todos.project_id').where('p.id IS NULL OR NOT(p.state = ?)', 'hidden') } + scope :not_context_hidden, -> { joins('INNER JOIN contexts c ON c.id = todos.context_id').where('NOT(c.state = ?)', 'hidden') } + scope :not_completed, -> { where 'NOT (todos.state = ?)', 'completed' } # other scopes scope :are_due, -> { where 'NOT (todos.due IS NULL)' } @@ -71,7 +75,6 @@ class Todo < ActiveRecord::Base aasm :column => :state do state :active - state :project_hidden state :completed, :before_enter => Proc.new { |t| t.completed_at = Time.zone.now }, :before_exit => Proc.new { |t| t.completed_at = nil} state :deferred, :before_exit => Proc.new { |t| t[:show_from] = nil } state :pending @@ -81,29 +84,19 @@ class Todo < ActiveRecord::Base end event :complete do - transitions :to => :completed, :from => [:active, :project_hidden, :deferred, :pending] + transitions :to => :completed, :from => [:active, :deferred, :pending] end event :activate do - transitions :to => :active, :from => [:project_hidden, :deferred] + transitions :to => :active, :from => [:deferred] transitions :to => :active, :from => [:completed], :guard => :no_uncompleted_predecessors? transitions :to => :active, :from => [:pending], :guard => :no_uncompleted_predecessors_or_deferral? transitions :to => :pending, :from => [:completed], :guard => :uncompleted_predecessors? transitions :to => :deferred, :from => [:pending], :guard => :no_uncompleted_predecessors? end - event :hide do - transitions :to => :project_hidden, :from => [:active, :deferred, :pending] - end - - event :unhide do - transitions :to => :deferred, :from => [:project_hidden], :guard => Proc.new{|t| t.show_from.present? } - transitions :to => :pending, :from => [:project_hidden], :guard => :uncompleted_predecessors? - transitions :to => :active, :from => [:project_hidden] - end - event :block do - transitions :to => :pending, :from => [:active, :deferred, :project_hidden] + transitions :to => :pending, :from => [:active, :deferred] end end @@ -143,10 +136,6 @@ class Todo < ActiveRecord::Base return !uncompleted_predecessors.empty? end - def should_be_blocked? - return !( uncompleted_predecessors.empty? || state == 'project_hidden' ) - end - def guard_for_transition_from_deferred_to_pending no_uncompleted_predecessors? && not_part_of_hidden_container? end @@ -199,7 +188,7 @@ class Todo < ActiveRecord::Base self.predecessors.delete(predecessor) if self.predecessors.empty? self.reload # reload predecessors - self.not_part_of_hidden_container? ? self.activate! : self.hide! + self.activate! else save! end @@ -226,20 +215,7 @@ class Todo < ActiveRecord::Base end def hidden? - return self.project_hidden? || ( self.context.hidden? && (self.active? || self.deferred?)) - end - - def update_state_from_project - if self.project_hidden? && (!self.project.hidden?) - if self.uncompleted_predecessors.empty? - self.activate! - else - self.block! - end - elsif self.active? && self.project.hidden? - self.hide! - end - self.save! + self.project.hidden? || self.context.hidden? end def toggle_completion! diff --git a/test/controllers/contexts_controller_test.rb b/test/controllers/contexts_controller_test.rb index 56492154..9b6b5c7d 100644 --- a/test/controllers/contexts_controller_test.rb +++ b/test/controllers/contexts_controller_test.rb @@ -13,7 +13,17 @@ class ContextsControllerTest < ActionController::TestCase get :show, { :id => "1" } assert_equal 'TRACKS::Context: agenda', assigns['page_title'] end - + + def test_shows_todos_when_hidden + c = contexts(:agenda) + todos = c.todos.active + assert_equal 6, todos.size + c.hide! + login_as :admin_user + get :show, { :id => '1'} + assert_equal 6, assigns['not_done_todos'].size + end + def test_show_renders_show_template login_as :admin_user get :show, { :id => "1" } diff --git a/test/controllers/projects_controller_test.rb b/test/controllers/projects_controller_test.rb index 9028d257..6af1cf15 100644 --- a/test/controllers/projects_controller_test.rb +++ b/test/controllers/projects_controller_test.rb @@ -52,13 +52,13 @@ class ProjectsControllerTest < ActionController::TestCase assert_ajax_create_increments_count 'My New Project' end - def test_todo_state_is_project_hidden_after_hiding_project + def test_todo_is_hidden_after_hiding_project p = projects(:timemachine) todos = p.todos.active login_as(:admin_user) xhr :post, :update, :id => 1, "project"=>{"name"=>p.name, "description"=>p.description, "state"=>"hidden"} todos.each do |t| - assert_equal :project_hidden, t.reload().aasm.current_state + assert t.reload().hidden? end assert p.reload().hidden? end diff --git a/test/controllers/todos_controller_test.rb b/test/controllers/todos_controller_test.rb index 2c33beb1..17f2557d 100644 --- a/test/controllers/todos_controller_test.rb +++ b/test/controllers/todos_controller_test.rb @@ -381,7 +381,7 @@ class TodosControllerTest < ActionController::TestCase assert p.reload().hidden? todo = p.todos.first - assert todo.project_hidden?, "todo should be project_hidden" + assert todo.hidden?, 'todo should be hidden' # clear project from todo: the todo should be unhidden xhr :post, :update, :id => todo.id, :_source_view => 'todo', "project_name"=>"", "todo"=>{} diff --git a/test/models/todo_test.rb b/test/models/todo_test.rb index f9791084..c257c0fe 100644 --- a/test/models/todo_test.rb +++ b/test/models/todo_test.rb @@ -216,8 +216,6 @@ class TodoTest < ActiveSupport::TestCase new_todo = @not_completed1.user.todos.build(description: "test", context: @not_completed1.context, project: project) new_todo.save! assert new_todo.active? - # And I update the state of the todo from its project - new_todo.update_state_from_project # Then the todo should be hidden assert new_todo.hidden? end