diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index d62f6aa0..56da269e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -47,10 +47,11 @@ class ProjectsController < ApplicationController @not_done = @project.not_done_todos_including_hidden @deferred = @project.deferred_todos + @pending = @project.pending_todos @done = @project.todos.find_in_state(:all, :completed, :order => "todos.completed_at DESC", :limit => current_user.prefs.show_number_completed, :include => [:context]) @count = @not_done.size - @down_count = @count + @deferred.size + @down_count = @count + @deferred.size + @pending.size @next_project = current_user.projects.next_from(@project) @previous_project = current_user.projects.previous_from(@project) @default_tags = @project.default_tags diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index 949d94bf..95e29b8c 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -6,9 +6,9 @@ class TodosController < ApplicationController prepend_before_filter :login_or_feed_token_required, :only => [:index, :calendar] append_before_filter :init, :except => [ :destroy, :completed, :completed_archive, :check_deferred, :toggle_check, :toggle_star, - :edit, :update, :create, :calendar, :auto_complete_for_tag] - append_before_filter :get_todo_from_params, :only => [ :edit, :toggle_check, :toggle_star, :show, :update, :destroy ] - protect_from_forgery :except => [:auto_complete_for_tag] + :edit, :update, :create, :calendar, :auto_complete_for_tag, :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] + protect_from_forgery :except => [:auto_complete_for_tag, :auto_complete_for_predecessor] session :off, :only => :index, :if => Proc.new { |req| is_feed_request(req) } @@ -54,6 +54,7 @@ class TodosController < ApplicationController p = TodoCreateParamsHelper.new(params, prefs) p.parse_dates() unless mobile? tag_list = p.tag_list + predecessor_list = p.predecessor_list @todo = current_user.todos.build(p.attributes) @@ -70,13 +71,21 @@ class TodosController < ApplicationController @todo.context_id = context.id end + @todo.add_predecessor_list(predecessor_list) @todo.update_state_from_project @saved = @todo.save unless (@saved == false) || tag_list.blank? @todo.tag_with(tag_list) @todo.tags.reload end - + + unless (@aved == false) + unless @todo.uncompleted_predecessors.empty? || @todo.state == 'project_hidden' + @todo.state = 'pending' + end + @todo.save + end + respond_to do |format| format.html { redirect_to :action => "index" } format.m do @@ -130,6 +139,30 @@ class TodosController < ApplicationController format.xml { render :xml => @todo.to_xml( :root => 'todo', :except => :user_id ) } end end + + def add_predecessor + @source_view = params['_source_view'] || 'todo' + @predecessor = Todo.find(params['predecessor']) + @todo = Todo.find(params['successor']) + @original_state = @todo.state + # Add predecessor + @todo.add_predecessor(@predecessor) + @todo.state = 'pending' + @saved = @todo.save + respond_to do |format| + format.js + end + end + + def remove_predecessor + @source_view = params['_source_view'] || 'todo' + @predecessor = Todo.find(params['predecessor']) + @successor = @todo + @removed = @successor.remove_predecessor(@predecessor) + respond_to do |format| + format.js + end + end # Toggles the 'done' status of the action # @@ -142,6 +175,18 @@ class TodosController < ApplicationController # check if this todo has a related recurring_todo. If so, create next todo @new_recurring_todo = check_for_next_todo(@todo) if @saved + if @todo.completed? + @pending_to_activate = @todo.pending_to_activate + @pending_to_activate.each do |t| + t.activate! + end + else + @active_to_block = @todo.active_to_block + @active_to_block.each do |t| + t.block! + end + end + respond_to do |format| format.js do if @saved @@ -190,7 +235,8 @@ class TodosController < ApplicationController @original_item_project_id = @todo.project_id @original_item_was_deferred = @todo.deferred? @original_item_due = @todo.due - @original_item_due_id = get_due_id_for_calendar(@todo.due) + @original_item_due_id = get_due_id_for_calendar(@todo.due) + @original_item_predecessor_list = @todo.predecessors.map{|t| t.specification}.join(', ') if params['todo']['project_id'].blank? && !params['project_name'].nil? if params['project_name'] == 'None' @@ -231,15 +277,38 @@ class TodosController < ApplicationController if params['done'] == '1' && !@todo.completed? @todo.complete! + @todo.pending_to_activate.each do |t| + t.activate! + end end # strange. if checkbox is not checked, there is no 'done' in params. # Therefore I've used the negation if !(params['done'] == '1') && @todo.completed? @todo.activate! + @todo.active_to_block.each do |t| + t.block! + end end @todo.attributes = params["todo"] + + @todo.add_predecessor_list(params[:predecessor_list]) @saved = @todo.save + if @saved && 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 + else + if @todo.state == 'active' + @todo.block! # Block active if we got uncompleted predecessors + end + end + end + @todo.save! + end @context_changed = @original_item_context_id != @todo.context_id @todo_was_activated_from_deferred_state = @original_item_was_deferred && @todo.active? @@ -297,16 +366,32 @@ class TodosController < ApplicationController @context_id = @todo.context_id @project_id = @todo.project_id + # activate successors if they only depend on this todo + activated_successor_count = 0 + @pending_to_activate = [] + @todo.pending_successors.each do |successor| + successor.uncompleted_predecessors.delete(@todo) + if successor.uncompleted_predecessors.empty? + successor.activate! + @pending_to_activate << successor + activated_successor_count += 1 + end + end + @saved = @todo.destroy # check if this todo has a related recurring_todo. If so, create next todo @new_recurring_todo = check_for_next_todo(@todo) if @saved - + respond_to do |format| format.html do if @saved - notify :notice, "Successfully deleted next action", 2.0 + message = "Successfully deleted next action" + if activated_successor_count > 0 + message += " activated #{pluralize(activated_successor_count, 'pending action')}" + end + notify :notice, message, 2.0 redirect_to :action => 'index' else notify :error, "Failed to delete the action", 2.0 @@ -356,7 +441,7 @@ class TodosController < ApplicationController @contexts_to_show = @contexts = current_user.contexts.find(:all, :include => [ :todos ]) current_user.deferred_todos.find_and_activate_ready - @not_done_todos = current_user.deferred_todos + @not_done_todos = current_user.deferred_todos + current_user.pending_todos @count = @not_done_todos.size @down_count = @count @@ -409,6 +494,9 @@ class TodosController < ApplicationController @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') # If you've set no_completed to zero, the completed items box isn't shown on # the tag page @@ -513,6 +601,46 @@ class TodosController < ApplicationController render :inline => "<%= auto_complete_result(@items, :name) %>" end + def auto_complete_for_predecessor + unless params['id'].nil? + get_todo_from_params + # Begin matching todos in current project + @items = current_user.todos.find(:all, + :select => 'description, project_id, context_id, created_at', + :conditions => [ '(todos.state = ? OR todos.state = ?) AND ' + + 'NOT (id = ?) AND lower(description) LIKE ? AND project_id = ?', + 'active', 'pending', + @todo.id, + '%' + params[:predecessor_list].downcase + '%', + @todo.project_id ], + :order => 'description ASC', + :limit => 10 + ) + if @items.empty? # Match todos in other projects + @items = current_user.todos.find(:all, + :select => 'description, project_id, context_id, created_at', + :conditions => [ '(todos.state = ? OR todos.state = ?) AND ' + + 'NOT (id = ?) AND lower(description) LIKE ?', + 'active', 'pending', + params[:id], '%' + params[:q].downcase + '%' ], + :order => 'description ASC', + :limit => 10 + ) + end + else + # New todo - TODO: Filter on project + @items = current_user.todos.find(:all, + :select => 'description, project_id, context_id, created_at', + :conditions => [ '(todos.state = ? OR todos.state = ?) AND lower(description) LIKE ?', + 'active', 'pending', + '%' + params[:q].downcase + '%' ], + :order => 'description ASC', + :limit => 10 + ) + end + render :inline => "<%= auto_complete_result2(@items) %>" + end + private def get_todo_from_params @@ -670,6 +798,7 @@ class TodosController < ApplicationController 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 @@ -956,6 +1085,10 @@ class TodosController < ApplicationController def tag_list @params['tag_list'] end + + def predecessor_list + @params['predecessor_list'] + end def parse_dates() @attributes['show_from'] = @prefs.parse_date(show_from) diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index e0272708..d8be9d1f 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -64,11 +64,15 @@ module TodosHelper :complete => todo_stop_waiting_js(todo)) end end - + def todo_start_waiting_js(todo) return "$('#ul#{dom_id(todo)}').css('visibility', 'hidden'); $('##{dom_id(todo)}').block({message: null})" end + def successor_start_waiting_js(successor) + return "$('##{dom_id(successor, "successor")}').block({message: null})" + end + def todo_stop_waiting_js(todo) return "$('##{dom_id(todo)}').unblock();enable_rich_interaction();" end @@ -82,12 +86,15 @@ module TodosHelper def remote_toggle_checkbox - check_box_tag('item_id', toggle_check_todo_path(@todo), @todo.completed?, :class => 'item-checkbox') + check_box_tag('item_id', toggle_check_todo_path(@todo), @todo.completed?, :class => 'item-checkbox', + :title => @todo.pending? ? 'Blocked by ' + @todo.uncompleted_predecessors.map(&:description).join(', ') : "", :readonly => @todo.pending?) end def date_span if @todo.completed? "#{format_date( @todo.completed_at )}" + elsif @todo.pending? + "Pending " elsif @todo.deferred? show_date( @todo.show_from ) else @@ -95,6 +102,22 @@ module TodosHelper end end + def successors_span + unless @todo.pending_successors.empty? + pending_count = @todo.pending_successors.length + title = "Has #{pluralize(pending_count, 'pending action')}: #{@todo.pending_successors.map(&:description).join(', ')}" + image_tag( 'successor_off.png', :width=>'10', :height=>'16', :border=>'0', :title => title ) + end + end + + def grip_span + unless @todo.completed? + image_tag('grip.png', :width => '7', :height => '16', :border => '0', + :title => 'Drag onto another action to make it depend on that action', + :class => 'grip') + end + end + def tag_list_text @todo.tags.collect{|t| t.name}.join(', ') end @@ -115,6 +138,10 @@ module TodosHelper if tag_list.empty? then "" else "#{tag_list}" end end + def predecessor_list_text + @todo.predecessors.map{|t| t.specification}.join(', ') + end + def deferred_due_date if @todo.deferred? && @todo.due "(action due on #{format_date(@todo.due)})" @@ -201,9 +228,10 @@ module TodosHelper end def item_container_id (todo) - if source_view_is :project - return "p#{todo.project_id}items" if todo.active? - return "tickler" if todo.deferred? + if todo.deferred? or todo.pending? + return "tickleritems" + elsif source_view_is :project + return "p#{todo.project_id}items" end return "c#{todo.context_id}items" end @@ -221,6 +249,8 @@ module TodosHelper 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(:tag) && @todo.pending? return false end @@ -281,4 +311,9 @@ module TodosHelper class_str = todo.starred? ? "starred_todo" : "unstarred_todo" image_tag("blank.png", :title =>"Star action", :class => class_str) end + + def auto_complete_result2(entries, phrase = nil) + return entries.map{|e| e.specification()}.join("\n") rescue '' + end + end diff --git a/app/models/dependency.rb b/app/models/dependency.rb new file mode 100644 index 00000000..dd347c1f --- /dev/null +++ b/app/models/dependency.rb @@ -0,0 +1,7 @@ +class Dependency < ActiveRecord::Base + + belongs_to :predecessor, :foreign_key => 'predecessor_id', :class_name => 'Todo' + belongs_to :successor, :foreign_key => 'successor_id', :class_name => 'Todo' + +end + diff --git a/app/models/project.rb b/app/models/project.rb index b5dbd3c1..88053a1c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -20,6 +20,11 @@ class Project < ActiveRecord::Base :class_name => 'Todo', :conditions => ["todos.state = ? ", "deferred"], :order => "show_from" + has_many :pending_todos, + :include => [:context,:tags,:project], + :class_name => 'Todo', + :conditions => ["todos.state = ? ", "pending"], + :order => "show_from" has_many :notes, :dependent => :delete_all, :order => "created_at DESC" diff --git a/app/models/todo.rb b/app/models/todo.rb index d608e56f..df7aca4f 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -4,12 +4,26 @@ class Todo < ActiveRecord::Base belongs_to :project belongs_to :user belongs_to :recurring_todo + + has_many :predecessor_dependencies, :foreign_key => 'predecessor_id', :class_name => 'Dependency', :dependent => :destroy + has_many :successor_dependencies, :foreign_key => 'successor_id', :class_name => 'Dependency', :dependent => :destroy + has_many :predecessors, :through => :successor_dependencies + has_many :successors, :through => :predecessor_dependencies + has_many :uncompleted_predecessors, :through => :successor_dependencies, + :source => :predecessor, :conditions => ['NOT (state = ?)', 'completed'] + has_many :pending_successors, :through => :predecessor_dependencies, + :source => :successor, :conditions => ['state = ?', 'pending'] + + after_save :save_predecessors named_scope :active, :conditions => { :state => 'active' } named_scope :not_completed, :conditions => ['NOT (state = ? )', 'completed'] named_scope :are_due, :conditions => ['NOT (todos.due IS NULL)'] STARRED_TAG_NAME = "starred" + RE_TODO = /[^"]+/ + RE_CONTEXT = /[^"]+/ + RE_PROJECT = /[^"]+/ acts_as_state_machine :initial => :active, :column => 'state' @@ -19,6 +33,7 @@ class Todo < ActiveRecord::Base state :project_hidden state :completed, :enter => Proc.new { |t| t.completed_at = Time.zone.now }, :exit => Proc.new { |t| t.completed_at = nil } state :deferred + state :pending event :defer do transitions :to => :deferred, :from => [:active] @@ -30,6 +45,8 @@ class Todo < ActiveRecord::Base event :activate do transitions :to => :active, :from => [:project_hidden, :completed, :deferred] + transitions :to => :active, :from => [:pending], :guard => :no_uncompleted_predecessors_or_deferral? + transitions :to => :deferred, :from => [:pending], :guard => :no_uncompleted_predecessors? end event :hide do @@ -40,6 +57,10 @@ class Todo < ActiveRecord::Base transitions :to => :deferred, :from => [:project_hidden], :guard => Proc.new{|t| !t.show_from.blank? } transitions :to => :active, :from => [:project_hidden] end + + event :block do + transitions :to => :pending, :from => [:active, :deferred] + end attr_protected :user @@ -51,15 +72,120 @@ class Todo < ActiveRecord::Base validates_presence_of :show_from, :if => :deferred? validates_presence_of :context + def initialize(*args) + super(*args) + @predecessor_array = nil # Used for deferred save of predecessors + end + + def no_uncompleted_predecessors_or_deferral? + return (show_from.blank? or Time.zone.now > show_from and uncompleted_predecessors.empty?) + end + + def no_uncompleted_predecessors? + return uncompleted_predecessors.empty? + end + + + # Returns a string with description + def specification + project_name = project.is_a?(NullProject) ? "(none)" : project.name + return "\"#{description}\" <\"#{context.title}\"; \"#{project_name}\">" + end + + def todo_from_specification(specification) + # Split specification into parts: description + re_parts = /"(#{RE_TODO})"\s<"(#{RE_CONTEXT})";\s"(#{RE_PROJECT})">/ + parts = specification.scan(re_parts) + return nil unless parts.length == 1 + return nil unless parts[0].length == 3 + todo_description = parts[0][0] + context_name = parts[0][1] + todos = Todo.all( + :joins => :context, + :conditions => { + :description => todo_description, + :contexts => {:name => context_name} + } + ) + return nil if todos.empty? + # todos now contains all todos with matching description and context + # TODO: Is this possible to do with a single query? + todos.each do |todo| + project_name = todo.project.is_a?(NullProject) ? "(none)" : todo.project.name + return todo if project_name == parts[0][2] + end + return nil + end + def validate if !show_from.blank? && show_from < user.date errors.add("show_from", "must be a date in the future") end + errors.add(:description, "may not contain \" characters") if /\"/.match(description) + unless @predecessor_array.nil? # Only validate predecessors if they changed + @predecessor_array.each do |specification| + t = todo_from_specification(specification) + if t.nil? + errors.add("Depends on:", "Could not find action '#{h(specification)}'") + else + errors.add("Depends on:", "Adding '#{h(specification)}' would create a circular dependency") if is_successor?(t) + end + end + end + end + + def save_predecessors + unless @predecessor_array.nil? # Only save predecessors if they changed + current_array = predecessors.map{|p| p.specification} + remove_array = current_array - @predecessor_array + add_array = @predecessor_array - current_array + + # This is probably a bit naive code... + remove_array.each do |specification| + t = todo_from_specification(specification) + self.predecessors.delete(t) unless t.nil? + end + # ... as is this? + add_array.each do |specification| + t = todo_from_specification(specification) + unless t.nil? + self.predecessors << t unless self.predecessors.include?(t) + else + logger.error "Could not find #{specification}" # Unexpected since validation passed + end + end + end + end + + def remove_predecessor(predecessor) + # remove predecessor and activate myself + predecessors.delete(predecessor) + self.activate! + end + + # Returns true if t is equal to self or a successor of self + def is_successor?(t) + if self == t + return true + elsif self.successors.empty? + return false + else + self.successors.each do |item| + if item.is_successor?(t) + return true + end + end + end + return false end def update_state_from_project if state == 'project_hidden' and !project.hidden? - self.state = 'active' + if self.uncompleted_predecessors.empty? + self.state = 'active' + else + self.state = 'pending' + end elsif state == 'active' and project.hidden? self.state = 'project_hidden' end @@ -92,7 +218,7 @@ class Todo < ActiveRecord::Base def project original_project.nil? ? Project.null_object : original_project end - + alias_method :original_set_initial_state, :set_initial_state def set_initial_state @@ -138,6 +264,28 @@ class Todo < ActiveRecord::Base def from_recurring_todo? return self.recurring_todo_id != nil end + + def add_predecessor_list(predecessor_list) + return unless predecessor_list.kind_of? String + # Split into list + re_specification = /"#{RE_TODO}"\s<"#{RE_CONTEXT}";\s"#{RE_PROJECT}">/ + @predecessor_array = predecessor_list.scan(re_specification) + end + + def add_predecessor(t) + @predecessor_array = predecessors.map{|p| p.specification} + @predecessor_array << t.specification + end + + # Return todos that should be activated if the current todo is completed + def pending_to_activate + return successors.find_all {|t| t.uncompleted_predecessors.empty?} + end + + # Return todos that should be blocked if the current todo is undone + def active_to_block + return successors.find_all {|t| t.active? or t.deferred?} + end # Rich Todo API diff --git a/app/models/user.rb b/app/models/user.rb index 6ce58aa8..30c5a6fb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -98,6 +98,10 @@ class User < ActiveRecord::Base find(:all, :conditions => ['show_from <= ?', Time.zone.now ]).collect { |t| t.activate! } end end + has_many :pending_todos, + :class_name => 'Todo', + :conditions => [ 'state = ?', 'pending' ], + :order => 'show_from ASC, todos.created_at DESC' has_many :completed_todos, :class_name => 'Todo', :conditions => ['todos.state = ? AND NOT(todos.completed_at IS NULL)', 'completed'], diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb index 0ed68cbd..efa84ab9 100644 --- a/app/views/projects/show.html.erb +++ b/app/views/projects/show.html.erb @@ -4,7 +4,7 @@ <%= render :partial => "projects/project", :locals => { :project => @project, :collapsible => false } %> - <%= render :partial => "todos/deferred", :locals => { :deferred => @deferred, :collapsible => false, :append_descriptor => "in this project", :parent_container_type => 'project' } %> + <%= render :partial => "todos/deferred", :locals => { :deferred => @deferred, :collapsible => false, :append_descriptor => "in this project", :parent_container_type => 'project', :pending => @pending } %> <% unless @max_completed==0 -%> <%= render :partial => "todos/completed", :locals => { :done => @done, :collapsible => false, :suppress_project => true, :append_descriptor => "in this project" } %> <% end -%> diff --git a/app/views/shared/_add_new_item_form.rhtml b/app/views/shared/_add_new_item_form.rhtml index ca2c2032..e8226fb8 100644 --- a/app/views/shared/_add_new_item_form.rhtml +++ b/app/views/shared/_add_new_item_form.rhtml @@ -51,6 +51,8 @@ <%= text_field("todo", "show_from", "size" => 12, "class" => "Date", "tabindex" => 7, "autocomplete" => "off") %> + + <%= text_field_tag "predecessor_list", nil, :size => 30, :tabindex => 8 %> <%= source_view_tag( @source_view ) %> <%= hidden_field_tag :_tag_name, @tag_name.underscore.gsub(/\s+/,'_') if source_view_is :tag %> diff --git a/app/views/todos/_deferred.rhtml b/app/views/todos/_deferred.rhtml index 58c16756..c38e1561 100644 --- a/app/views/todos/_deferred.rhtml +++ b/app/views/todos/_deferred.rhtml @@ -3,15 +3,16 @@ <% if collapsible %> <%= image_tag("collapse.png") %> <% end %> - Deferred actions <%= append_descriptor ? append_descriptor : '' %> + Deferred/pending actions <%= append_descriptor ? append_descriptor : '' %>
-
-

Currently there are no deferred actions

+
+

Currently there are no deferred or pending actions

<%= render :partial => "todos/todo", :collection => deferred, :locals => { :parent_container_type => parent_container_type } %> + <%= render :partial => "todos/todo", :collection => pending, :locals => { :parent_container_type => parent_container_type } %>
-
\ No newline at end of file + diff --git a/app/views/todos/_edit_form.rhtml b/app/views/todos/_edit_form.rhtml index e471984e..2f8e4bc5 100644 --- a/app/views/todos/_edit_form.rhtml +++ b/app/views/todos/_edit_form.rhtml @@ -46,13 +46,21 @@ + +<%= text_field_tag 'predecessor_list', predecessor_list_text, :id => dom_id(@todo, 'predecessor_list'), :size => 30, :tabindex => 15 %> +<%= content_tag("div", "", :id => dom_id(@todo, 'predecessor_list')+"_auto_complete", :class => "auto_complete") %> +<%= auto_complete_field dom_id(@todo, 'predecessor_list'), { + :url => {:controller => 'todos', :action => 'auto_complete_for_predecessor', :id => @todo.id}, + :tokens => [','] +} %> + <% if controller.controller_name == "project" || @todo.deferred? -%> <% end -%>
- diff --git a/app/views/todos/_successor.html.erb b/app/views/todos/_successor.html.erb new file mode 100644 index 00000000..83f1fc54 --- /dev/null +++ b/app/views/todos/_successor.html.erb @@ -0,0 +1,26 @@ +<% +suppress_context ||= false +suppress_project ||= false +suppress_dependencies ||= false +parameters = "_source_view=#{@source_view}" +parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag' +@z_index_counter = @z_index_counter - 1 # for IE z-index bug +%> +
+
+
+ <%= h sanitize(successor.description) %> + + <%= link_to_remote( + image_tag("blank.png", :title => "Remove dependency (does not delete the action)", :align => "absmiddle", :class => "delete_item"), + {:url => {:controller => 'todos', :action => 'remove_predecessor', :id => successor.id}, + :method => 'delete', + :with => "'#{parameters}&predecessor=#{predecessor.id}'", + :before => successor_start_waiting_js(successor)}, + {:style => "background: transparent;"}) %> + + <%= render(:partial => "todos/toggle_successors", :locals => { :item => successor, :suppress_button => true }) unless successor.pending_successors.empty? %> +
+
+
+ diff --git a/app/views/todos/_todo.html.erb b/app/views/todos/_todo.html.erb index 25f8d372..8a042ae6 100644 --- a/app/views/todos/_todo.html.erb +++ b/app/views/todos/_todo.html.erb @@ -26,13 +26,16 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
+ <%= grip_span %> <%= date_span -%> <%= h sanitize(todo.description) %> + <% #= successors_span %> <%= image_tag_for_recurring_todo(todo) if @todo.from_recurring_todo? %> <%= tag_list %> <%= deferred_due_date %> <%= project_and_context_links( parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %> <%= render(:partial => "todos/toggle_notes", :locals => { :item => todo }) if todo.notes? %> + <%= render(:partial => "todos/toggle_successors", :locals => { :item => todo }) unless todo.pending_successors.empty? %>