From 1f67d2a603e7d4c6155deea5575d4e077d4fc907 Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Sat, 12 Feb 2011 10:38:15 +0100 Subject: [PATCH] get dependency add/remove from new_action_form running --- app/controllers/application_controller.rb | 5 +- app/controllers/todos_controller.rb | 11 +-- app/helpers/todos_helper.rb | 3 +- app/models/todo.rb | 97 ++++++++--------------- app/views/todos/_new_todo_form.rhtml | 10 +-- config/locales/en.yml | 1 + public/javascripts/application.js | 61 +++++++++++--- 7 files changed, 93 insertions(+), 95 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 779f9031..1b65ecd9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -36,7 +36,6 @@ class ApplicationController < ActionController::Base before_filter :set_locale prepend_before_filter :login_required prepend_before_filter :enable_mobile_content_negotiation - # after_filter :set_locale after_filter :set_charset include ActionView::Helpers::TextHelper @@ -137,8 +136,8 @@ class ApplicationController < ActionController::Base end end - def auto_complete_result2(entries, phrase = nil) - json_elems = "[{" + entries.map {|item| "\"id\" : \"#{item.id}\", \"value\" : \"#{item.specification()}\""}.join("},{") + "}]" + def format_dependencies_as_json_for_auto_complete(entries) + json_elems = "[{" + entries.map {|item| "\"value\" : \"#{item.id}\", \"label\" : \"#{item.specification()}\""}.join("},{") + "}]" return json_elems == "[{}]" ? "" : json_elems end diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index 880c0de7..ca1e87e9 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -49,7 +49,7 @@ class TodosController < ApplicationController def create @source_view = params['_source_view'] || 'todo' @default_context = current_user.contexts.find_by_name(params['default_context_name']) - @default_project = current_user.projects.find_by_name(params['default_project_name']) + @default_project = current_user.projects.find_by_name(params['default_project_name']) unless params['default_project_name'].blank? @tag_name = params['_tag_name'] @@ -248,6 +248,7 @@ class TodosController < ApplicationController end def remove_predecessor + puts "@@@ start remove_predecessor" @source_view = params['_source_view'] || 'todo' @todo = current_user.todos.find(params['id']) @predecessor = current_user.todos.find(params['predecessor']) @@ -648,7 +649,7 @@ class TodosController < ApplicationController get_todo_from_params # Begin matching todos in current project @items = current_user.todos.find(:all, - :select => 'description, project_id, context_id, created_at', + :include => [:context, :project], :conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND ' + 'NOT (id = ?) AND lower(description) LIKE ? AND project_id = ?', 'active', 'pending', 'deferred', @@ -660,7 +661,7 @@ class TodosController < ApplicationController ) if @items.empty? # Match todos in other projects @items = current_user.todos.find(:all, - :select => 'description, project_id, context_id, created_at', + :include => [:context, :project], :conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND ' + 'NOT (id = ?) AND lower(description) LIKE ?', 'active', 'pending', 'deferred', @@ -672,7 +673,7 @@ class TodosController < ApplicationController else # New todo - TODO: Filter on project @items = current_user.todos.find(:all, - :select => 'description, project_id, context_id, created_at', + :include => [:context, :project], :conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND lower(description) LIKE ?', 'active', 'pending', 'deferred', '%' + params[:term].downcase + '%' ], @@ -680,7 +681,7 @@ class TodosController < ApplicationController :limit => 10 ) end - render :inline => auto_complete_result2(@items) + render :inline => format_dependencies_as_json_for_auto_complete(@items) end def convert_to_project diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 7f7bbc4d..27502a25 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -225,7 +225,6 @@ module TodosHelper page.todo { return !@todo.hidden? } page.deferred { return @todo.deferred? || @todo.pending? } page.context { - logger.debug "ci=#{@todo.context_id} dci=#{@default_context.id} th=#{@todo.hidden?} tch=#{@todo.context.hidden?}" return @todo.context_id==@default_context.id && ( (@todo.hidden? && @todo.context.hidden?) || (!@todo.hidden?) ) } page.tag { @@ -266,7 +265,7 @@ module TodosHelper end def default_contexts_for_autocomplete - projects = current_user.projects.find(:all, :conditions => ['default_context_id is not null']) + projects = current_user.projects.find(:all, :include => [:context], :conditions => ['default_context_id is not null']) Hash[*projects.map{ |p| [p.name, p.default_context.name] }.flatten].to_json end diff --git a/app/models/todo.rb b/app/models/todo.rb index 3456f55c..974616b3 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -108,84 +108,41 @@ class Todo < ActiveRecord::Base # Returns a string with description def specification - project_name = project.is_a?(NullProject) ? "(none)" : project.name - return "\'#{description}\' <\'#{context.title}\'; \'#{project_name}\'>" + project_name = self.project.is_a?(NullProject) ? "(none)" : self.project.name + return "\'#{self.description}\' <\'#{self.context.title}\'; \'#{project_name}\'>" end - - def todo_from_specification(specification) - # Split specification into parts: description - 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] - project_name = parts[0][2] - - # find the project - project_id = nil; - unless project_name == "(none)" - project = Project.first(:conditions => { - :user_id => self.user.id, - :name => project_name - }) - project_id = project.id unless project.nil? - end - - todos = Todo.all( - :joins => :context, - :conditions => { - :description => todo_description, - :user_id => self.user.id, - :contexts => {:name => context_name}, - :project_id => project_id - } - ) - - return nil if todos.empty? - - # TODO: what todo if there are more than one todo that fit the specification - return todos[0] - end - + def validate if !show_from.blank? && show_from < user.date errors.add("show_from", I18n.t('models.todo.error_date_must_be_future')) end - errors.add(:description, "may not contain \" characters") if /\"/.match(description) + errors.add(:description, "may not contain \" characters") if /\"/.match(self.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 + @predecessor_array.each do |todo| + errors.add("Depends on:", "Adding '#{h(todo.specification)}' would create a circular dependency") if is_successor?(todo) end end end def save_predecessors unless @predecessor_array.nil? # Only save predecessors if they changed - current_array = predecessors.map{|p| p.specification} + current_array = self.predecessors remove_array = current_array - @predecessor_array add_array = @predecessor_array - current_array @removed_predecessors = [] - # This is probably a bit naive code... - remove_array.each do |specification| - t = todo_from_specification(specification) - unless t.nil? - @removed_predecessors << t - self.predecessors.delete(t) + remove_array.each do |todo| + unless todo.nil? + @removed_predecessors << todo + self.predecessors.delete(todo) end 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) + + add_array.each do |todo| + unless todo.nil? + self.predecessors << todo unless self.predecessors.include?(todo) else - logger.error "Could not find #{specification}" # Unexpected since validation passed + logger.error "Could not find #{todo.description}" # Unexpected since validation passed end end end @@ -196,20 +153,22 @@ class Todo < ActiveRecord::Base end def remove_predecessor(predecessor) + puts "@@@ before delete" # remove predecessor and activate myself - predecessors.delete(predecessor) + self.predecessors.delete(predecessor) + puts "@@@ before activate" self.activate! end # Returns true if t is equal to self or a successor of self - def is_successor?(t) - if self == t + def is_successor?(todo) + if self == todo return true elsif self.successors.empty? return false else self.successors.each do |item| - if item.is_successor?(t) + if item.is_successor?(todo) return true end end @@ -314,12 +273,20 @@ class Todo < ActiveRecord::Base def add_predecessor_list(predecessor_list) return unless predecessor_list.kind_of? String - @predecessor_array = predecessor_list.scan(RE_SPEC) + + @predecessor_array=[] + + predecessor_ids_array = predecessor_list.split(", ") + predecessor_ids_array.each do |todo_id| + predecessor = self.user.todos.find_by_id( todo_id.to_i ) unless todo_id.blank? + @predecessor_array << predecessor unless predecessor.nil? + end + return @predecessor_array end def add_predecessor(t) - @predecessor_array = predecessors.map{|p| p.specification} + @predecessor_array = predecessors @predecessor_array << t.specification end diff --git a/app/views/todos/_new_todo_form.rhtml b/app/views/todos/_new_todo_form.rhtml index a870a39a..ba6d3c80 100644 --- a/app/views/todos/_new_todo_form.rhtml +++ b/app/views/todos/_new_todo_form.rhtml @@ -34,17 +34,13 @@ <%= t.text_field("show_from", "size" => 12, "class" => "Date", "tabindex" => 7, "autocomplete" => "off") %> - - <%= text_field_tag "predecessor_list", nil, :size => 30, :tabindex => 8 %> - -
- -
- +
+ <%= t('common.add')%>: <%= text_field_tag "predecessor_input", nil, :size => 30, :tabindex => 8 %> + <%= hidden_field_tag "predecessor_list", ""%> <%= source_view_tag( @source_view ) %> <%= hidden_field_tag :_tag_name, @tag_name.underscore.gsub(/\s+/,'_') if source_view_is :tag %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 0844acff..fd28562e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -39,6 +39,7 @@ en: logout: "Logout" cancel: "Cancel" ok: "Ok" + add: "Add" project: "Project" projects: "Projects" context: "Context" diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 6daaffb0..39097c8a 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -89,7 +89,7 @@ var TracksForm = { }); $('input[class=predecessor_add_button]').live('click', function(){ - var text = $('input[name=predecessor]').val(); + var text = $('input[name=predecessor_list]').val(); if (text.length > 0) { $('ul#predecessor_ul').show(); if (text.length > 35) { @@ -844,7 +844,7 @@ $(document).ajaxSend(function(event, request, settings) { } request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); } - request.setRequestHeader("X-CSRF-Token", $('meta[name=csrf-token]').attr('content')); + request.setRequestHeader('X-CSRF-Token', $('meta[name=csrf-token]').attr('content')); request.setRequestHeader("Accept", "text/javascript"); }); @@ -992,10 +992,10 @@ function enable_rich_interaction(){ .autocomplete({ minLength: 0, source: function( request, response ) { - var last_term = extractLast( request.term ); - if (last_term != "" && last_term != " ") + term = request.term; + if (term != "" && term != " ") $.getJSON( relative_to_root('auto_complete_for_predecessor'), { - term: last_term + term: term }, response ); }, focus: function() { @@ -1003,18 +1003,53 @@ function enable_rich_interaction(){ return false; }, select: function( event, ui ) { - var terms = split( this.value ); - // remove the current input - terms.pop(); - // add the selected item - terms.push( ui.item.value ); - // add placeholder to get the comma-and-space at the end - //terms.push( "" ); - this.value = terms.join( ", " ); + // retrieve values from input fields + todo_spec = ui.item.label + todo_id = ui.item.value + predecessor_list = $('input[name=predecessor_list]') + id_list = split( predecessor_list.val() ); + + // add the dependency to id list + id_list.push( todo_id ); + predecessor_list.val( id_list.join( ", " ) ); + + // show the html for the list of deps + $('ul#predecessor_ul').show(); + if (todo_spec.length > 35) { // cut off string + todo_spec = todo_spec.substring(0,35)+"..."; + } + // show the new dep in list + var html = $('ul#predecessor_ul').html(); + var new_li = "
  • "+ todo_spec + "
  • "; + $('ul#predecessor_ul').html(html + new_li); + $('li#pred_'+todo_id).slideDown(500); + + $('input[name=predecessor_input]').val(''); + $('input[name=predecessor_input]').focus(); return false; } }); + $('input[class=pred_remove_button]').live('click', function() { + predecessor_list = $('input[name=predecessor_list]'); + id_list = split( predecessor_list.val() ); + $("li#pred_"+this.id).slideUp(500).remove(); // remove from ul + + // remove from array + new_list = new Array(); + while (id_list.length > 0) { + elem = id_list.pop(); + if (elem != this.id && elem != '' && elem != ' ') { + new_list.push ( elem ); + } + } + + // update id list + predecessor_list.val( new_list.join(", ") ); + + return false; // prevent submit + }) + /* have to bind on keypress because of limitations of live() */ $('input[name=project_name]').live('keypress', function(){ $(this).bind('blur', project_defaults);