get tag view working for updating todos. Refactored update a lot

This commit is contained in:
Reinier Balt 2011-01-01 18:55:53 +01:00
parent 1cea27ccc9
commit f923a40a40
15 changed files with 314 additions and 215 deletions

View file

@ -124,16 +124,9 @@ class ApplicationController < ActionController::Base
# config/settings.yml
#
def format_date(date)
if date
date_format = prefs.date_format
formatted_date = date.in_time_zone(prefs.time_zone).strftime("#{date_format}")
else
formatted_date = ''
end
formatted_date
return date ? date.in_time_zone(prefs.time_zone).strftime("#{prefs.date_format}") : ''
end
def for_autocomplete(coll, substr)
filtered = coll.find_all{|item| item.name.downcase.include? substr.downcase}
json_elems = "[{" + filtered.map {|item| "\"value\" : \"#{item.name}\", \"id\" : \"#{item.id}\""}.join("},{") + "}]"

View file

@ -27,7 +27,9 @@ class ProjectsController < ApplicationController
format.rss &render_rss_feed
format.atom &render_atom_feed
format.text &render_text_feed
format.autocomplete { render :text => for_autocomplete(@projects.reject(&:completed?), params[:q]) }
format.autocomplete {
uncompleted_projects = current_user.projects.uncompleted(true)
render :text => for_autocomplete(uncompleted_projects, params[:term]) }
end
end
end

View file

@ -5,7 +5,7 @@ class TodosController < ApplicationController
skip_before_filter :login_required, :only => [:index, :calendar]
prepend_before_filter :login_or_feed_token_required, :only => [:index, :calendar]
append_before_filter :find_and_activate_ready, :only => [:index, :list_deferred]
append_before_filter :init, :except => [ :destroy, :completed,
append_before_filter :init, :except => [ :tag, :destroy, :completed,
:completed_archive, :check_deferred, :toggle_check, :toggle_star,
:edit, :update, :create, :calendar, :auto_complete_for_predecessor, :remove_predecessor, :add_predecessor]
append_before_filter :get_todo_from_params, :only => [ :edit, :toggle_check, :toggle_star, :show, :update, :destroy, :remove_predecessor, :show_notes]
@ -80,25 +80,27 @@ class TodosController < ApplicationController
# Fix for #977 because AASM overrides @state on creation
specified_state = @todo.state
@todo.update_state_from_project
@saved = @todo.save
# Fix for #977 because AASM overrides @state on creation
@todo.update_attribute('state', specified_state) unless specified_state == "immediate"
@todo.update_state_from_project
@saved = @todo.save
unless (@saved == false) || tag_list.blank?
@todo.tag_with(tag_list)
@todo.tags.reload
end
unless (@saved == false)
if @saved
unless @todo.uncompleted_predecessors.empty? || @todo.state == 'project_hidden'
@todo.state = 'pending'
end
@todo.save
end
@todo.reload
respond_to do |format|
format.html { redirect_to :action => "index" }
format.m do
@ -121,6 +123,7 @@ class TodosController < ApplicationController
@status_message = 'Added new next action'
@status_message += ' to tickler' if @todo.deferred?
@status_message += ' in pending state' if @todo.pending?
@status_message += ' in hidden state' if @todo.hidden?
@status_message = 'Added new project / ' + status_message if @new_project_created
@status_message = 'Added new context / ' + status_message if @new_context_created
render :action => 'create'
@ -328,20 +331,20 @@ class TodosController < ApplicationController
cache_attributes_from_before_update
update_tags
update_project if params['todo']['project_id'].blank? && !params['project_name'].nil?
update_context if params['todo']['context_id'].blank? && !params['context_name'].blank?
update_project
update_context
update_due_and_show_from_dates
update_completed_state
update_dependencies
update_attributes_of_todo
@todo.attributes = params["todo"]
@saved = @todo.save
@todo.reload # refresh context and project object too (not only their id's)
@todo.add_predecessor_list(params[:predecessor_list])
update_pending_state if @saved && params[:predecessor_list]
update_todo_state_if_project_changed
determine_changes_by_this_update
determine_remaining_in_context_count(@context_changed ? @original_item_context_id : @todo.context_id)
update_todo_state_if_project_changed
determine_down_count
determine_deferred_tag_count(params['_tag_name']) if @source_view == 'tag'
@ -479,6 +482,7 @@ class TodosController < ApplicationController
# /todos/tag/[tag_name] shows all the actions tagged with tag_name
def tag
init_data_for_sidebar unless mobile?
@source_view = params['_source_view'] || 'tag'
@tag_name = params[:name]
@page_title = t('todos.tagged_page_title', :tag_name => @tag_name)
@ -488,28 +492,22 @@ class TodosController < ApplicationController
@tag = Tag.find_by_name(@tag_name)
@tag = Tag.new(:name => @tag_name) if @tag.nil?
tag_collection = @tag.todos
@not_done_todos = tag_collection.find(:all,
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'active'],
:order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC')
@hidden_todos = current_user.todos.find(:all,
@not_done_todos = current_user.todos.with_tag(@tag).active.find(:all,
:order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC', :include => [:context])
@hidden_todos = current_user.todos.with_tag(@tag).hidden.find(:all,
:include => [:taggings, :tags, :context],
:conditions => ['tags.name = ? AND (todos.state = ? OR (contexts.hide = ? AND todos.state = ?))', @tag_name, 'project_hidden', true, 'active'],
:order => 'todos.completed_at DESC, todos.created_at DESC')
@deferred = tag_collection.find(:all,
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'deferred'],
:order => 'show_from ASC, todos.created_at DESC')
@pending = tag_collection.find(:all,
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'pending'],
:order => 'show_from ASC, todos.created_at DESC')
@deferred = current_user.todos.with_tag(@tag).deferred.find(:all,
:order => 'show_from ASC, todos.created_at DESC', :include => [:context])
@pending = current_user.todos.with_tag(@tag).blocked.find(:all,
:order => 'show_from ASC, todos.created_at DESC', :include => [:context])
# If you've set no_completed to zero, the completed items box isn't shown on
# the tag page
max_completed = current_user.prefs.show_number_completed
@done = tag_collection.find(:all,
@done = current_user.todos.with_tag(@tag).completed.find(:all,
:limit => max_completed,
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'completed'],
:order => 'todos.completed_at DESC')
@projects = current_user.projects
@ -538,7 +536,7 @@ class TodosController < ApplicationController
def tags
@tags = Tag.all
respond_to do |format|
format.autocomplete { render :text => for_autocomplete(@tags, params[:q]) }
format.autocomplete { render :text => for_autocomplete(@tags, params[:term]) }
end
end
@ -810,9 +808,7 @@ class TodosController < ApplicationController
def determine_down_count
source_view do |from|
from.todo do
@down_count = current_user.todos.active.count(
:all,
:conditions => ['contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', false, 'active'], :include => [:project,:context])
@down_count = current_user.todos.not_hidden.count(:all)
end
from.context do
@down_count = current_user.contexts.find(@todo.context_id).todos.not_completed.count(:all)
@ -820,8 +816,6 @@ class TodosController < ApplicationController
from.project do
unless @todo.project_id == nil
@down_count = current_user.projects.find(@todo.project_id).not_done_todos_including_hidden.count
@deferred_count = current_user.projects.find(@todo.project_id).deferred_todos.count
@pending_count = current_user.projects.find(@todo.project_id).pending_todos.count
end
end
from.deferred do
@ -833,8 +827,7 @@ class TodosController < ApplicationController
if @tag.nil?
@tag = Tag.new(:name => @tag_name)
end
tag_collection = @tag.todos
@not_done_todos = tag_collection.find(:all, :conditions => ['todos.user_id = ? and state = ?', current_user.id, 'active'])
@not_done_todos = current_user.todos.with_tag(@tag).active.find(:all)
@not_done_todos.empty? ? @down_count = 0 : @down_count = @not_done_todos.size
end
end
@ -844,18 +837,35 @@ class TodosController < ApplicationController
source_view do |from|
from.deferred {
# force reload to todos to get correct count and not a cached one
@remaining_in_context = current_user.contexts.find(context_id).todos(true).deferred_or_blocked.count(:all)
@target_context_count = current_user.contexts.find(@todo.context_id).todos(true).deferred_or_blocked.count(:all)
@remaining_in_context = current_user.contexts.find(context_id).todos(true).deferred_or_blocked.count
@target_context_count = current_user.contexts.find(@todo.context_id).todos(true).deferred_or_blocked.count
}
from.tag {
from.tag {
tag = Tag.find_by_name(params['_tag_name'])
if tag.nil?
tag = Tag.new(:name => params['tag'])
end
@remaining_in_context = current_user.contexts.find(context_id).not_done_todo_count({:tag => tag.id})
@remaining_in_context = current_user.contexts.find(context_id).todos.not_hidden.with_tag(tag).count
@target_context_count = current_user.contexts.find(@todo.context_id).todos.not_hidden.with_tag(tag).count
if !@todo.hidden? && @todo_hidden_state_changed
@remaining_hidden_count = current_user.todos.hidden.with_tag(tag).count
end
}
from.project {
@remaining_deferred_or_pending_count = current_user.projects.find(@todo.project_id).todos.deferred_or_blocked.count
@remaining_in_context = current_user.projects.find(@todo.project_id).todos.active.count
@target_context_count = current_user.projects.find(@todo.project_id).todos.active.count
}
from.calendar {
@target_context_count = count_old_due_empty(@new_due_id)
}
end
@remaining_in_context = current_user.contexts.find(context_id).not_done_todo_count if @remaining_in_context.nil?
@remaining_in_context = current_user.contexts.find(context_id).not_done_todo_count if !@remaining_in_context
@target_context_count = current_user.contexts.find(@todo.context_id).not_done_todo_count if !@target_context_count
puts "@remaining_in_context = #{@remaining_in_context}"
puts "@target_context_count = #{@target_context_count}"
puts "@remaining_hidden_count = #{@remaining_hidden_count}"
puts "@remaining_deferred_or_pending_count = #{@remaining_deferred_or_pending_count}"
end
def determine_completed_count
@ -874,16 +884,10 @@ class TodosController < ApplicationController
end
end
def determine_deferred_tag_count(tag)
tags = Tag.find_by_name(tag)
if tags.nil?
# should normally not happen, but is a workaround for #929
@deferred_tag_count = 0
else
@deferred_tag_count = tags.todos.count(:all,
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'deferred'],
:order => 'show_from ASC, todos.created_at DESC')
end
def determine_deferred_tag_count(tag_name)
tag = Tag.find_by_name(tag_name)
# tag.nil? should normally not happen, but is a workaround for #929
@remaining_deferred_or_pending_count = tag.nil? ? 0 : current_user.todos.deferred.with_tag(tag).count
end
def render_todos_html
@ -1039,25 +1043,29 @@ class TodosController < ApplicationController
end
def is_old_due_empty(id)
return 0 == count_old_due_empty(id)
end
def count_old_due_empty(id)
due_today_date = Time.zone.now
due_this_week_date = Time.zone.now.end_of_week
due_next_week_date = due_this_week_date + 7.days
due_this_month_date = Time.zone.now.end_of_month
case id
when "due_today"
return 0 == current_user.todos.not_completed.count(:all,
return current_user.todos.not_completed.count(:all,
:conditions => ['todos.due <= ?', due_today_date])
when "due_this_week"
return 0 == current_user.todos.not_completed.count(:all,
return current_user.todos.not_completed.count(:all,
:conditions => ['todos.due > ? AND todos.due <= ?', due_today_date, due_this_week_date])
when "due_next_week"
return 0 == current_user.todos.not_completed.count(:all,
return current_user.todos.not_completed.count(:all,
:conditions => ['todos.due > ? AND todos.due <= ?', due_this_week_date, due_next_week_date])
when "due_this_month"
return 0 == current_user.todos.not_completed.count(:all,
return current_user.todos.not_completed.count(:all,
:conditions => ['todos.due > ? AND todos.due <= ?', due_next_week_date, due_this_month_date])
when "due_after_this_month"
return 0 == current_user.todos.not_completed.count(:all,
return current_user.todos.not_completed.count(:all,
:conditions => ['todos.due > ?', due_this_month_date])
else
raise Exception.new, "unknown due id for calendar: '#{id}'"
@ -1144,45 +1152,53 @@ class TodosController < ApplicationController
@original_item_context_id = @todo.context_id
@original_item_project_id = @todo.project_id
@original_item_was_deferred = @todo.deferred?
@original_item_was_hidden = @todo.hidden?
@original_item_due = @todo.due
@original_item_due_id = get_due_id_for_calendar(@todo.due)
@original_item_predecessor_list = @todo.predecessors.map{|t| t.specification}.join(', ')
puts "wh = #{@original_item_was_hidden}"
end
def update_project
if params['project_name'] == 'None'
project = Project.null_object
else
project = current_user.projects.find_by_name(params['project_name'].strip)
unless project
project = current_user.projects.build
project.name = params['project_name'].strip
project.save
@new_project_created = true
if params['todo']['project_id'].blank? && !params['project_name'].nil?
if params['project_name'] == 'None'
project = Project.null_object
else
project = current_user.projects.find_by_name(params['project_name'].strip)
unless project
project = current_user.projects.build
project.name = params['project_name'].strip
project.save
@new_project_created = true
end
end
params["todo"]["project_id"] = project.id
end
params["todo"]["project_id"] = project.id
@project_changed = @original_item_project_id != params["todo"]["project_id"] = project.id
end
def update_todo_state_if_project_changed
if (@project_changed && !@original_item_project_id.nil?) then
if ( @project_changed ) then
@todo.update_state_from_project
@todo.save!
@remaining_undone_in_project = current_user.projects.find(@original_item_project_id).not_done_todos.count
@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 = current_user.contexts.find_by_name(params['context_name'].strip)
unless context
@new_context = current_user.contexts.build
@new_context.name = params['context_name'].strip
@new_context.save
@new_context_created = true
@not_done_todos = [@todo]
context = @new_context
if params['todo']['context_id'].blank? && !params['context_name'].blank?
context = current_user.contexts.find_by_name(params['context_name'].strip)
unless context
@new_context = current_user.contexts.build
@new_context.name = params['context_name'].strip
@new_context.save
@new_context_created = true
@not_done_todos = [@todo]
context = @new_context
end
params["todo"]["context_id"] = context.id
end
params["todo"]["context_id"] = context.id
@context_changed = @original_item_context_id != params["todo"]["context_id"] = context.id
puts "context changed into '#{context.name}'"
end
def update_tags
@ -1220,34 +1236,47 @@ class TodosController < ApplicationController
end
end
def update_pending_state
def update_dependencies
@todo.add_predecessor_list(params[:predecessor_list])
if @original_item_predecessor_list != params[:predecessor_list]
# Possible state change with new dependencies
if @todo.uncompleted_predecessors.empty?
if @todo.state == 'pending'
@todo.activate! # Activate pending if no uncompleted predecessors
end
if @todo.uncompleted_predecessors.empty?
@todo.activate! if @todo.state == 'pending' # Activate pending if no uncompleted predecessors
else
if @todo.state == 'active'
@todo.block! # Block active if we got uncompleted predecessors
end
@todo.block! if @todo.state == 'active' # Block active if we got uncompleted predecessors
end
end
@todo.save!
end
def update_attributes_of_todo
@todo.attributes = params["todo"]
end
def determine_changes_by_this_update
@context_changed = @original_item_context_id != @todo.context_id
@todo_was_activated_from_deferred_state = @original_item_was_deferred && @todo.active?
@todo_was_deferred = !@original_item_was_deferred && @todo.deferred?
@todo_was_deferred_from_active_state = @todo.deferred? && !@original_item_was_deferred
@todo_deferred_state_changed = @todo_was_deferred_from_active_state || @todo_was_activated_from_deferred_state
@due_date_changed = @original_item_due != @todo.due
@todo_hidden_state_changed = @original_item_was_hidden != @todo.hidden?
if source_view_is :calendar
@due_date_changed = ((@original_item_due != @todo.due) && @todo.due) || !@todo.due
@old_due_empty = is_old_due_empty(@original_item_due_id)
@new_due_id = get_due_id_for_calendar(@todo.due) if @due_date_changed
puts "@context_changed = #{@context_changed}"
puts "@project_changed = #{@project_changed}"
puts "@todo_was_activated_from_deferred_state #{@todo_was_activated_from_deferred_state}"
puts "@todo_was_deferred_from_active_state = #{@todo_was_deferred_from_active_state}"
puts "@due_date_changed = #{@due_date_changed}"
puts "@todo_hidden_state_changed = #{@todo_hidden_state_changed} @todo.hidden?=#{@todo.hidden?}"
source_view do |page|
page.calendar do
@old_due_empty = is_old_due_empty(@original_item_due_id)
@new_due_id = get_due_id_for_calendar(@todo.due) if @due_date_changed
end
page.tag do
@tag_name = params['_tag_name']
@tag_was_removed = !@todo.has_tag?(@tag_name)
puts "@tag_was_removed = #{@tag_was_removed}"
end
end
@project_changed = @original_item_project_id != @todo.project_id
end
def project_specified_by_name(project_name)

View file

@ -205,22 +205,9 @@ module TodosHelper
end
end
def item_container_id (todo)
return "c#{todo.context_id}items" if source_view_is :deferred
return "tickleritems" if todo.deferred? or todo.pending?
return "p#{todo.project_id}items" if source_view_is :project
return @new_due_id if source_view_is :calendar
return "c#{todo.context_id}items"
end
def should_show_new_item
unless @todo.project.nil?
# do not show new actions that were added to hidden or completed projects
# on home page and context page
return false if source_view_is(:todo) && (@todo.project.hidden? || @todo.project.completed?)
return false if source_view_is(:context) && (@todo.project.hidden? || @todo.project.completed?)
end
return false if source_view_is(:todo) && @todo.hidden?
return false if source_view_is(:context) && @todo.hidden? && !@todo.context.hidden?
return false if (source_view_is(:tag) && !@todo.tags.include?(@tag_name))
return false if (source_view_is(:context) && !(@todo.context_id==@default_context.id) )
@ -228,8 +215,8 @@ module TodosHelper
return true if source_view_is(:deferred) && @todo.deferred?
return true if source_view_is(:project) && @todo.project.hidden? && @todo.project_hidden?
return true if source_view_is(:project) && @todo.deferred?
return true if !source_view_is(:deferred) && @todo.active?
return true if source_view_is(:project) && @todo.pending?
return true if !source_view_is(:deferred) && @todo.active?
return true if source_view_is(:tag) && @todo.pending?
return false
@ -239,20 +226,13 @@ module TodosHelper
return 'tickler' if source_view_is :deferred
return 'project' if source_view_is :project
return 'stats' if source_view_is :stats
return 'tag' if source_view_is :tag
return 'context'
end
def empty_container_msg_div_id
todo = @todo || @successor
return "" unless todo # empty id if no todo or successor given
return "tickler-empty-nd" if source_view_is_one_of(:project, :tag) && todo.deferred?
return "p#{todo.project_id}empty-nd" if source_view_is :project
return "c#{todo.context_id}empty-nd"
end
def todo_container_is_empty
default_container_empty = ( @down_count == 0 )
deferred_container_empty = ( @todo.deferred? && @deferred_count == 0)
deferred_container_empty = ( @todo.deferred? && @remaining_deferred_count == 0)
return default_container_empty || deferred_container_empty
end
@ -284,15 +264,18 @@ module TodosHelper
end
def update_needs_to_hide_context
return false if source_view_is(:tag) && (@remaining_in_context == 0) && (@todo_hidden_state_changed && !@todo.hidden?)
return (@remaining_in_context == 0) && !source_view_is(:context)
end
def update_needs_to_remove_todo_from_container
source_view do |page|
page.context { return @context_changed || @todo.deferred? || @todo.pending?}
page.project { return updated_todo_changed_deferred_state }
page.context { return @context_changed || @todo.deferred? || @todo.pending?}
page.project { return @todo_deferred_state_changed }
page.deferred { return @context_changed || !(@todo.deferred? || @todo.pending?) }
page.calendar { return @due_date_changed || !@todo.due }
page.stats { return @todo.completed? }
page.tag { return (@context_changed && !@todo.hidden?) || @tag_was_removed || @todo_hidden_state_changed || @todo_deferred_state_changed }
end
return false
end
@ -300,35 +283,67 @@ module TodosHelper
def replace_with_updated_todo
source_view do |page|
page.context { return !update_needs_to_remove_todo_from_container }
page.project { return !updated_todo_changed_deferred_state}
page.project { return !@todo_deferred_state_changed}
page.deferred { return !@context_changed && (@todo.deferred? || @todo.pending?) }
page.calendar { return !@due_date_changed && @todo.due }
end
end
def append_updated_todo
source_view do |page|
page.context { return false }
page.project { return updated_todo_changed_deferred_state }
page.deferred { return @context_changed && (@todo.deferred? || @todo.pending?) }
page.calendar { return @due_date_changed && @todo.due }
page.stats { return !@todo.completed? }
page.tag { return !update_needs_to_remove_todo_from_container && !@tag_was_removed }
end
return false
end
def updated_todo_changed_deferred_state
return (@todo.deferred? && !@original_item_was_deferred) || @todo_was_activated_from_deferred_state
def append_updated_todo
source_view do |page|
page.context { return false }
page.project { return @todo_deferred_state_changed }
page.deferred { return @context_changed && (@todo.deferred? || @todo.pending?) }
page.calendar { return @due_date_changed && @todo.due }
page.stats { return false }
page.tag { return update_needs_to_remove_todo_from_container && !@tag_was_removed}
end
return false
end
def item_container_id (todo)
puts "todo.hidden?=#{todo.hidden?} en source_view=#{@source_view}"
return "hiddenitems" if source_view_is(:tag) && todo.hidden?
return "c#{todo.context_id}items" if source_view_is :deferred
return @new_due_id if source_view_is :calendar
return "tickleritems" if todo.deferred? || todo.pending?
return "p#{todo.project_id}items" if source_view_is :project
return "c#{todo.context_id}items"
end
def empty_container_msg_div_id(todo = @todo || @successor)
raise Exception.new, "no @todo or @successor set" if !todo
source_view do |page|
page.project {
return "tickler-empty-nd" if @todo_was_deferred_from_active_state
return "p#{todo.project_id}empty-nd"
}
page.tag {
return "tickler-empty-nd" if @todo_was_deferred_from_active_state
return "hidden-empty-nd" if @todo.hidden?
return "c#{todo.context_id}empty-nd"
}
page.calendar {
return "empty_#{@new_due_id}"
}
end
return "c#{todo.context_id}empty-nd"
end
def render_animation(animation)
html = ""
animation.each do |step|
puts "step='#{step}'"
unless step.blank?
html += step + "({ go: function() {\r\n"
end
end
html += "}})" * animation.count
html += "}}) " * animation.count
return html
end

View file

@ -1,5 +1,7 @@
class Project < ActiveRecord::Base
has_many :todos, :dependent => :delete_all, :include => [:context,:tags]
# TODO: remove these scopes. Can be replaced by the named scopes on the todo relation
has_many :not_done_todos,
:include => [:context,:tags,:project],
:class_name => 'Todo',
@ -35,6 +37,7 @@ class Project < ActiveRecord::Base
named_scope :active, :conditions => { :state => 'active' }
named_scope :hidden, :conditions => { :state => 'hidden' }
named_scope :completed, :conditions => { :state => 'completed'}
named_scope :uncompleted, :conditions => ["NOT state = ?", 'completed']
validates_presence_of :name
validates_length_of :name, :maximum => 255

View file

@ -23,6 +23,16 @@ class Todo < ActiveRecord::Base
named_scope :deferred, :conditions => ["todos.completed_at IS NULL AND NOT todos.show_from IS NULL"]
named_scope :blocked, :conditions => ['todos.state = ?', 'pending']
named_scope :deferred_or_blocked, :conditions => ["(todos.completed_at IS NULL AND NOT todos.show_from IS NULL) OR (todos.state = ?)", "pending"]
named_scope :not_deferred_or_blocked, :conditions => ["todos.completed_at IS NULL AND todos.show_from IS NULL AND NOT todos.state = ?", "pending"]
named_scope :with_tag, lambda { |tag| {:joins => :taggings, :conditions => ["taggings.tag_id = ? ", tag.id] } }
named_scope :of_user, lambda { |user_id| {:conditions => ["todos.user_id = ? ", user_id] } }
named_scope :hidden,
:joins => :context,
:conditions => ["todos.state = ? OR (contexts.hide = ? AND (todos.state = ? OR todos.state = ?))",
'project_hidden', true, 'active', 'deferred']
named_scope :not_hidden,
:joins => [:context, :project],
:conditions => ['contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', false, 'active']
STARRED_TAG_NAME = "starred"
@ -92,7 +102,6 @@ class Todo < ActiveRecord::Base
def no_uncompleted_predecessors?
return uncompleted_predecessors.empty?
end
# Returns a string with description <context, project>
def specification
@ -196,14 +205,24 @@ class Todo < ActiveRecord::Base
return false
end
def has_tag?(tag)
return self.tags.select{|t| t.name==tag }.size > 0
end
def hidden?
puts "hidden => state = #{self.state} context(#{self.context.name}).hidden=#{self.context.hidden?}"
return self.state == 'project_hidden' || ( self.context.hidden? && (self.state == 'active' || self.state == 'deferred'))
end
def update_state_from_project
if state == 'project_hidden' and !project.hidden?
puts "state was #{self.state}; project.hidden?=#{self.project.hidden?}"
if state == 'project_hidden' and !self.project.hidden?
if self.uncompleted_predecessors.empty?
self.state = 'active'
else
self.state = 'pending'
end
elsif state == 'active' and project.hidden?
elsif self.state == 'active' and self.project.hidden?
self.state = 'project_hidden'
end
end

View file

@ -12,8 +12,6 @@
<%= javascript_tag "var SOURCE_VIEW = '#{@source_view}';" %>
<%= javascript_tag "var TAG_NAME = '#{@tag_name}';" if @tag_name %>
<script type="text/javascript">
var defaultContexts = <%= default_contexts_for_autocomplete rescue '{}' %>;
var defaultTags = <%= default_tags_for_autocomplete rescue '{}' %>;
var dateFormat = '<%= date_format_for_date_picker %>';
var weekStart = '<%= current_user.prefs.week_starts %>';
function relative_to_root(path) { return '<%= root_url %>'+path; };

View file

@ -14,29 +14,6 @@
animation << (@new_context_created ? "insert_new_context_with_updated_todo" : "add_to_existing_container")
end
animation << "hide_context" if update_needs_to_hide_context
# update relevant empty messaged
source_view do |page|
page.context {
animation << "show_empty_message_source_container" if (@remaining_in_context == 0)
}
page.project {
animation << "show_empty_message_project" if (@down_count == 0)
if @todo_was_activated_from_deferred_state
animation << "show_empty_message_tickler" if @deferred_count == 0
animation << "hide_empty_message_project"
end
animation << "hide_empty_message_tickler" if @todo_was_deferred && (@deferred_count == 1)
}
page.deferred {
animation << "show_empty_message_source_container" if (@remaining_in_context == 0)
animation << "hide_empty_message_target_container" if (@context_changed && @target_context_count==1)
}
page.calendar {
animation << "show_empty_message_source_container" if @old_due_empty
animation << "hide_empty_message_target_container" if @todo.due
}
end
animation << "highlight_updated_todo"
%>
@ -48,10 +25,54 @@
function remove_todo(next_steps) {
$('#<%= dom_id(@todo) %>').fadeOut(400, function() {
$('#<%= dom_id(@todo) %>').remove();
<% # TODO: to helper function: show_empty_message_source_container
container_id = ""
source_view do |page|
page.project {
container_id = "p#{@original_item_project_id}empty-nd" if @remaining_in_context == 0
container_id = "tickler-empty-nd" if @todo_was_activated_from_deferred_state && @remaining_deferred_or_pending_count == 0
}
page.deferred { container_id = "c#{@original_item_context_id}empty-nd" if @remaining_in_context == 0 }
page.calendar { container_id = "empty_#{@original_item_due_id}" if @old_due_empty }
page.tag {
container_id = "hidden-empty-nd" if !@todo.hidden? && @todo_hidden_state_changed && @remaining_hidden_count == 0
container_id = "tickler-empty-nd" if @todo_was_activated_from_deferred_state && @remaining_deferred_or_pending_count == 0
}
page.context { container_id = "c#{@original_item_context_id}empty-nd" if @remaining_in_context == 0 }
end
unless container_id.blank?
-%>
$("#<%= container_id%>").slideDown(100);
<% end -%>
next_steps.go();
});
}
function add_to_existing_container(next_steps) {
$('#<%= item_container_id(@todo) %>').append(html_for_todo());
<% if source_view_is_one_of(:project,:calendar) -%>
next_steps.go();
<% if (@target_context_count==1) || (@todo.deferred? && @remaining_deferred_or_pending_count == 1) -%>
$("#<%= empty_container_msg_div_id %>").slideUp(100);
<% end -%>
<% else -%>
<% unless (@todo_hidden_state_changed && @todo.hidden?) || @todo_was_deferred_from_active_state -%>
$('#c<%= @todo.context_id %>').fadeIn(500, function() {
next_steps.go();
<% if @target_context_count==1 -%>
$("#<%= empty_container_msg_div_id %>").slideUp(100);
<% end -%>
});
<% else -%>
next_steps.go();
<% if (@target_context_count==1) || (@todo.deferred? && @remaining_deferred_or_pending_count == 1) -%>
$("#<%= empty_container_msg_div_id %>").slideUp(100);
<% end -%>
<% end -%>
<% end -%>
}
function replace_todo(next_steps) {
$('#<%= dom_id(@todo) %>').html(html_for_todo());
next_steps.go();
@ -66,15 +87,6 @@ function highlight_updated_todo(next_steps) {
$('#<%= dom_id(@todo)%>').effect('highlight', {}, 2000, function(){ next_steps.go(); });
}
function add_to_existing_container(next_steps) {
$('#<%= item_container_id(@todo) %>').append(html_for_todo());
<% if source_view_is_one_of(:project,:calendar) -%>
next_steps.go();
<% else -%>
$('#c<%= @todo.context_id %>').fadeIn(500, function() { next_steps.go(); });
<% end -%>
}
function update_badge_count() {
<%
count = source_view_is(:context) ? @remaining_in_context : @down_count
@ -83,33 +95,6 @@ function update_badge_count() {
TracksPages.set_page_badge(<%= count %>);
}
function show_empty_message_source_container(next_steps) {
<% container_id = source_view_is(:calendar) ? "empty_#{@original_item_due_id}" : "c#{@original_item_context_id}empty-nd" -%>
$("#<%= container_id%>").slideDown(100, function() { next_steps.go(); });
}
function hide_empty_message_target_container(next_steps) {
<% container_id = source_view_is(:calendar) ? "empty_#{@new_due_id}" : "c#{@todo.context_id}empty-nd" -%>
$("#<%=container_id%>").slideUp(100, function() { next_steps.go(); });
}
function show_empty_message_project(next_steps) {<%
id = updated_todo_changed_deferred_state ? "p#{@todo.project_id}empty-nd" : empty_container_msg_div_id %>
$('#<%= id %>').slideDown(100, function() { next_steps.go(); });
}
function hide_empty_message_project(next_steps) {
$('#<%= empty_container_msg_div_id %>').slideUp(100, function() { next_steps.go(); });
}
function show_empty_message_tickler(next_steps) {
$('#tickler-empty-nd').slideDown(100, function() { next_steps.go(); });
}
function hide_empty_message_tickler(next_steps) {
$('#tickler-empty-nd').slideUp(100, function(){ next_steps.go(); });
}
function insert_new_context_with_updated_todo(next_steps) {
$('#display_box').prepend(html_for_new_context());
$('#c<%= @todo.context_id %>').fadeIn(500, function() { next_steps.go(); });

View file

@ -325,8 +325,8 @@ en:
months_ago: "Months ago"
other_actions_label: "(others)"
action_selection_title: "TRACKS::Action selection"
actions_selected_from_week: "Actions selected from week"
actions_further: "and further"
actions_selected_from_week: "Actions selected from week "
actions_further: " and further"
totals_project_count: "You have {{count}} projects."
totals_active_project_count: "Of those {{count}} are active projects"
totals_hidden_project_count: "{{count}} are hidden"

View file

@ -27,3 +27,9 @@ Feature: Edit a context
Scenario: Editing the context of the last todo will remove the todo and show empty message
Given this scenario is pending
Scenario: Adding a todo to a hidden project will not show the todo
Given this scenario is pending
Scenario: Adding a todo to a hidden context will show that todo
Given this scenario is pending

View file

@ -62,3 +62,9 @@ Feature: Show statistics
And I should see 5 todos
And I should see "to return to the statistics page"
And I should see "to show the actions from week 0 and further"
Scenario: I can edit the todos selected from a chart
Given this scenario is pending
Scenario: Marking a todo selected from a chart as complete will remove it from the page
Given this scenario is pending

View file

@ -0,0 +1,40 @@
Feature: Tagging todos
In order to organise my todos in various lists
As a Tracks user
I want to to be able to add or edit one or more tags to todos
Background:
Given the following user record
| login | password | is_admin |
| testuser | secret | false |
And I have logged in as "testuser" with password "secret"
Scenario: I can edit a todo to add tags to that todo
Given this is a pending scenario
Scenario: I can add a new todo with tags
Given this is a pending scenario
Scenario: I can show all todos tagged with a specific tag
Given this is a pending scenario
Scenario: I can remove a tag from a todo from the tag view and the tag will be removed
Given this is a pending scenario
Scenario: I can add a new todo from tag view with that tag and it will be added to the page
Given this is a pending scenario
Scenario: I can add a new todo from tag view with a different tag and it will not be added to the page
Given this is a pending scenario
Scenario: I can change the context of a tagged todo in tag view and it will move the tag on the page
Given this is a pending scenario
Scenario: I can defer a tagged todo in tag view and it will move the todo on the page to the deferred container
Given this is a pending scenario
Scenario: I can move a tagged todo in tag view to a hidden project and it will move the todo on the page to the hidden container
Given this is a pending scenario
Scenario: I can move a tagged todo in tag view to a hidden context and it will move the todo on the page to the hidden container
Given this is a pending scenario

View file

@ -124,7 +124,7 @@ module LoginSystem
def get_current_user
if @user.nil? && session['user_id']
@user = User.find session['user_id'], :include => :preference
@user = User.find(session['user_id'], :include => [:preference])
end
@prefs = @user.prefs unless @user.nil?
@user

View file

@ -1,5 +1,6 @@
module Tracks
module TodoList
# TODO: this module should be deprecated. This could mostly (all?) be replaced by named scopes)
def not_done_todos(opts={})
@not_done_todos ||= self.find_not_done_todos(opts)

View file

@ -743,7 +743,7 @@ function default_ajax_options_for_submit(ajax_type, element_to_block) {
type: ajax_type,
async: true,
context: element_to_block,
data: "_source_view=" + encodeURIComponent( SOURCE_VIEW ),
data: "_source_view=" + SOURCE_VIEW,
beforeSend: function() {
$(this).block({
message: null
@ -758,7 +758,7 @@ function default_ajax_options_for_submit(ajax_type, element_to_block) {
}
}
if(typeof(TAG_NAME) !== 'undefined')
options.data += "&_tag_name="+ encodeURIComponent (TAG_NAME);
options.data += "&_tag_name="+ TAG_NAME;
return options;
}
@ -865,8 +865,8 @@ function enable_rich_interaction(){
/* called after completion of all AJAX calls */
/* fix for #1036 where closing a edit form before the autocomplete was filled
* resulted in a dropdown box that could not be removed. We remove all
* autocomplete boxes the hard way */
* resulted in a dropdown box that could not be removed. We remove all
* autocomplete boxes the hard way */
$('.ac_results').remove();
$('input.Date').datepicker({
@ -879,10 +879,12 @@ function enable_rich_interaction(){
$('input[name=context_name]').autocomplete({
source: relative_to_root('contexts.autocomplete')
});
$('input[name=project_name]').autocomplete({
source: relative_to_root('projects.autocomplete')
});
/* $('input[name=project[default_context_name]]').autocomplete(
relative_to_root('contexts.autocomplete'), {matchContains: true});
$('input[name=project_name]').autocomplete(
relative_to_root('projects.autocomplete'), {matchContains: true});
$('input[name=tag_list]:not(.ac_input)').autocomplete(
relative_to_root('tags.autocomplete'), {multiple: true,multipleSeparator:',',matchContains:true});
$('input[name=predecessor_list]:not(.ac_input)').autocomplete(
@ -971,7 +973,7 @@ function enable_rich_interaction(){
field_touched = false;
/* shrink the notes on the project pages. This is not live(), so this needs
* to be run after ajax adding of a new note */
* to be run after ajax adding of a new note */
$('.note_wrapper').truncate({
max_length: 90,
more: '',