modify todo controller to store multiple next actions

This commit is contained in:
Reinier Balt 2010-07-14 20:20:55 +02:00
parent c9ce82d533
commit 4400c42d7c
5 changed files with 246 additions and 121 deletions

View file

@ -48,76 +48,135 @@ class TodosController < ApplicationController
def create
@source_view = params['_source_view'] || 'todo'
@tag_name = params['_tag_name']
p = TodoCreateParamsHelper.new(params, prefs)
p.parse_dates() unless mobile?
tag_list = p.tag_list
predecessor_list = p.predecessor_list
@tag_name = params['_tag_name']
unless params[:todo][:multiple_todos].nil?
create_multiple
else
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)
@todo = current_user.todos.build(p.attributes)
if p.project_specified_by_name?
project = current_user.projects.find_or_create_by_name(p.project_name)
@new_project_created = project.new_record_before_save?
@todo.project_id = project.id
end
if p.context_specified_by_name?
context = current_user.contexts.find_or_create_by_name(p.context_name)
@new_context_created = context.new_record_before_save?
@not_done_todos = [@todo] if @new_context_created
@todo.context_id = context.id
end
@todo.add_predecessor_list(predecessor_list)
# 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"
unless (@saved == false) || tag_list.blank?
@todo.tag_with(tag_list)
@todo.tags.reload
end
unless (@saved == false)
unless @todo.uncompleted_predecessors.empty? || @todo.state == 'project_hidden'
@todo.state = 'pending'
if p.project_specified_by_name?
project = current_user.projects.find_or_create_by_name(p.project_name)
@new_project_created = project.new_record_before_save?
@todo.project_id = project.id
end
@todo.save
if p.context_specified_by_name?
context = current_user.contexts.find_or_create_by_name(p.context_name)
@new_context_created = context.new_record_before_save?
@not_done_todos = [@todo] if @new_context_created
@todo.context_id = context.id
end
@todo.add_predecessor_list(predecessor_list)
# 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"
unless (@saved == false) || tag_list.blank?
@todo.tag_with(tag_list)
@todo.tags.reload
end
unless (@saved == 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
@return_path=cookies[:mobile_url] ? cookies[:mobile_url] : mobile_path
if @saved
redirect_to @return_path
else
@projects = current_user.projects.find(:all)
@contexts = current_user.contexts.find(:all)
render :action => "new"
end
end
format.js do
determine_down_count if @saved
@contexts = current_user.contexts.find(:all) if @new_context_created
@projects = current_user.projects.find(:all) if @new_project_created
@initial_context_name = params['default_context_name']
@initial_project_name = params['default_project_name']
@default_tags = @todo.project.default_tags unless @todo.project.nil?
render :action => 'create'
end
format.xml do
if @saved
head :created, :location => todo_url(@todo)
else
render :xml => @todo.errors.to_xml, :status => 422
end
end
end
end
end
def create_multiple
if project_specified_by_name(params[:project_name])
project = current_user.projects.find_or_create_by_name(params[:project_name])
@new_project_created = project.new_record_before_save?
@project_id = project.id
end
if context_specified_by_name(params[:context_name])
context = current_user.contexts.find_or_create_by_name(params[:context_name])
@new_context_created = context.new_record_before_save?
@not_done_todos = [] if @new_context_created
@context_id = context.id
end
tag_list = params[:tag_list]
@todos = []
params[:todo][:multiple_todos].split("\n").map do |line|
@todo = current_user.todos.build(
:description => line)
@todo.project_id = @project_id
@todo.context_id = @context_id
puts "TODO: #{@todo.description}, #{@todo.project_id}, #{@todo.context_id}"
@saved = @todo.save
puts "NOT SAVED" unless @saved
unless (@saved == false) || tag_list.blank?
@todo.tag_with(tag_list)
@todo.tags.reload
end
@todos << @todo
@not_done_todos << @todo if @new_context_created
end
respond_to do |format|
format.html { redirect_to :action => "index" }
format.m do
@return_path=cookies[:mobile_url] ? cookies[:mobile_url] : mobile_path
if @saved
redirect_to @return_path
else
@projects = current_user.projects.find(:all)
@contexts = current_user.contexts.find(:all)
render :action => "new"
end
end
format.js do
determine_down_count if @saved
@contexts = current_user.contexts.find(:all) if @new_context_created
@projects = current_user.projects.find(:all) if @new_project_created
@initial_context_name = params['default_context_name']
@initial_project_name = params['default_project_name']
@default_tags = @todo.project.default_tags unless @todo.project.nil?
render :action => 'create'
@default_tags = @todos[0].project.default_tags unless @todos[0].project.nil?
render :action => 'create_multiple'
end
format.xml do
if @saved
head :created, :location => todo_url(@todo)
head :created, :location => context_url(@todos[0].context)
else
render :xml => @todo.errors.to_xml, :status => 422
render :xml => @todos[0].errors.to_xml, :status => 422
end
end
end
@ -184,12 +243,12 @@ class TodosController < ApplicationController
if @todo.completed?
@pending_to_activate = @todo.pending_to_activate
@pending_to_activate.each do |t|
t.activate!
t.activate!
end
else
@active_to_block = @todo.active_to_block
@active_to_block.each do |t|
t.block!
t.block!
end
end
@ -198,7 +257,7 @@ class TodosController < ApplicationController
if @saved
determine_remaining_in_context_count(@todo.context_id)
determine_down_count
determine_completed_count
determine_completed_count
determine_deferred_tag_count(params['_tag_name']) if @source_view == 'tag'
if source_view_is :calendar
@original_item_due_id = get_due_id_for_calendar(@original_item_due)
@ -301,7 +360,7 @@ class TodosController < ApplicationController
if params['done'] == '1' && !@todo.completed?
@todo.complete!
@todo.pending_to_activate.each do |t|
t.activate!
t.activate!
end
end
# strange. if checkbox is not checked, there is no 'done' in params.
@ -309,7 +368,7 @@ class TodosController < ApplicationController
if !(params['done'] == '1') && @todo.completed?
@todo.activate!
@todo.active_to_block.each do |t|
t.block!
t.block!
end
end
@ -420,7 +479,7 @@ class TodosController < ApplicationController
notify :error, "Failed to delete the action", 2.0
redirect_to :action => 'index'
end
end
end
format.js do
if @saved
@ -428,7 +487,7 @@ class TodosController < ApplicationController
if source_view_is_one_of(:todo, :deferred)
determine_remaining_in_context_count(@context_id)
elsif source_view_is :calendar
@original_item_due_id = get_due_id_for_calendar(@original_item_due)
@original_item_due_id = get_due_id_for_calendar(@original_item_due)
@old_due_empty = is_old_due_empty(@original_item_due_id)
end
end
@ -506,25 +565,25 @@ class TodosController < ApplicationController
@tag = Tag.new(:name => @tag_name) if @tag.nil?
tag_collection = @tag.todos
@not_done_todos = tag_collection.find(:all,
@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,
:include => [:taggings, :tags, :context],
@hidden_todos = current_user.todos.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,
@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,
@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
max_completed = current_user.prefs.show_number_completed
@done = tag_collection.find(:all,
:limit => max_completed,
@done = tag_collection.find(:all,
:limit => max_completed,
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'completed'],
:order => 'todos.completed_at DESC')
@ -540,13 +599,13 @@ class TodosController < ApplicationController
# Set count badge to number of items with this tag
@not_done_todos.empty? ? @count = 0 : @count = @not_done_todos.size
@down_count = @count
@down_count = @count
respond_to do |format|
format.html
format.m {
format.m {
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
render :action => "mobile_tag"
render :action => "mobile_tag"
}
end
end
@ -591,23 +650,23 @@ class TodosController < ApplicationController
due_this_month_date = Time.zone.now.end_of_month
@due_today = current_user.todos.not_completed.find(:all,
:include => [:taggings, :tags],
:include => [:taggings, :tags],
:conditions => ['todos.due <= ?', due_today_date],
:order => "due")
@due_this_week = current_user.todos.not_completed.find(:all,
:include => [:taggings, :tags],
:include => [:taggings, :tags],
:conditions => ['todos.due > ? AND todos.due <= ?', due_today_date, due_this_week_date],
:order => "due")
@due_next_week = current_user.todos.not_completed.find(:all,
:include => [:taggings, :tags],
:include => [:taggings, :tags],
:conditions => ['todos.due > ? AND todos.due <= ?', due_this_week_date, due_next_week_date],
:order => "due")
@due_this_month = current_user.todos.not_completed.find(:all,
:include => [:taggings, :tags],
:include => [:taggings, :tags],
:conditions => ['todos.due > ? AND todos.due <= ?', due_next_week_date, due_this_month_date],
:order => "due")
@due_after_this_month = current_user.todos.not_completed.find(:all,
:include => [:taggings, :tags],
:include => [:taggings, :tags],
:conditions => ['todos.due > ?', due_this_month_date],
:order => "due")
@ -626,35 +685,35 @@ class TodosController < ApplicationController
unless params['id'].nil?
get_todo_from_params
# Begin matching todos in current project
@items = current_user.todos.find(:all,
@items = current_user.todos.find(:all,
:select => 'description, project_id, context_id, created_at',
:conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND ' +
'NOT (id = ?) AND lower(description) LIKE ? AND project_id = ?',
'active', 'pending', 'deferred',
@todo.id,
'%' + params[:predecessor_list].downcase + '%',
@todo.project_id ],
'NOT (id = ?) AND lower(description) LIKE ? AND project_id = ?',
'active', 'pending', 'deferred',
@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',
@items = current_user.todos.find(:all,
:select => 'description, project_id, context_id, created_at',
:conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND ' +
'NOT (id = ?) AND lower(description) LIKE ?',
'active', 'pending', 'deferred',
params[:id], '%' + params[:q].downcase + '%' ],
'NOT (id = ?) AND lower(description) LIKE ?',
'active', 'pending', 'deferred',
params[:id], '%' + params[:q].downcase + '%' ],
:order => 'description ASC',
:limit => 10
)
end
else
# New todo - TODO: Filter on project
@items = current_user.todos.find(:all,
@items = current_user.todos.find(:all,
:select => 'description, project_id, context_id, created_at',
:conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND lower(description) LIKE ?',
'active', 'pending', 'deferred',
'%' + params[:q].downcase + '%' ],
:conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND lower(description) LIKE ?',
'active', 'pending', 'deferred',
'%' + params[:q].downcase + '%' ],
:order => 'description ASC',
:limit => 10
)
@ -665,7 +724,7 @@ class TodosController < ApplicationController
def convert_to_project
@todo = current_user.todos.find(params[:id])
@project = current_user.projects.new(:name => @todo.description, :description => @todo.notes,
:default_context => @todo.context)
:default_context => @todo.context)
@todo.destroy
@project.save!
redirect_to project_url(@project)
@ -753,7 +812,7 @@ class TodosController < ApplicationController
end
else
yield
end
end
end
def with_limit_scope(&block)
@ -779,7 +838,7 @@ class TodosController < ApplicationController
with_limit_scope do
if mobile?
init_todos_for_mobile_view
init_todos_for_mobile_view
else
# Note: these next two finds were previously using
@ -789,10 +848,10 @@ class TodosController < ApplicationController
@todos = Todo.find(:all, :conditions => ['todos.user_id = ?', current_user.id], :include => [ :project, :context, :tags ])
# Exclude hidden projects from the home page
@not_done_todos = Todo.find(:all,
:conditions => ['todos.user_id = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)',
current_user.id, false, 'active'],
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
@not_done_todos = Todo.find(:all,
:conditions => ['todos.user_id = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)',
current_user.id, false, 'active'],
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
:include => [ :project, :context, :tags ])
end
@ -806,10 +865,10 @@ class TodosController < ApplicationController
# but that broke with_scope for :limit
# Exclude hidden projects from the home page
@not_done_todos = Todo.find(:all,
:conditions => ['todos.user_id = ? AND todos.state = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)',
current_user.id, 'active', false, 'active'],
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
@not_done_todos = Todo.find(:all,
:conditions => ['todos.user_id = ? AND todos.state = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)',
current_user.id, 'active', false, 'active'],
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
:include => [ :project, :context, :tags ])
end
@ -817,8 +876,8 @@ class TodosController < ApplicationController
source_view do |from|
from.todo do
@down_count = Todo.count(
:all,
:conditions => ['todos.user_id = ? and todos.state = ? and contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', current_user.id, 'active', false, 'active'],
:all,
:conditions => ['todos.user_id = ? and todos.state = ? and contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', current_user.id, 'active', false, 'active'],
:include => [ :project, :context ])
# #@down_count = Todo.count_by_sql(['SELECT COUNT(*) FROM todos,
# contexts WHERE todos.context_id = contexts.id and todos.user_id = ?
@ -849,12 +908,12 @@ class TodosController < ApplicationController
@not_done_todos.empty? ? @down_count = 0 : @down_count = @not_done_todos.size
end
end
end
end
def determine_remaining_in_context_count(context_id = @todo.context_id)
source_view do |from|
from.deferred { @remaining_in_context = current_user.contexts.find(context_id).deferred_todo_count }
from.tag {
from.tag {
tag = Tag.find_by_name(params['_tag_name'])
if tag.nil?
tag = Tag.new(:name => params['tag'])
@ -863,7 +922,7 @@ class TodosController < ApplicationController
}
end
@remaining_in_context = current_user.contexts.find(context_id).not_done_todo_count if @remaining_in_context.nil?
end
end
def determine_completed_count
source_view do |from|
@ -1022,7 +1081,7 @@ class TodosController < ApplicationController
end
end
end
return new_recurring_todo
return new_recurring_todo
end
def get_due_id_for_calendar(due)
@ -1063,11 +1122,11 @@ class TodosController < ApplicationController
when "due_this_month"
return 0 == 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"
when "due_after_this_month"
return 0 == current_user.todos.not_completed.count(:all,
:conditions => ['todos.due > ?', due_this_month_date])
else
raise Exception.new, "unknown due id for calendar: '#{id}'"
raise Exception.new, "unknown due id for calendar: '#{id}'"
end
end
@ -1144,4 +1203,20 @@ class TodosController < ApplicationController
end
end
private
def project_specified_by_name(project_name)
return false unless params['project_id'].blank?
return false if project_name.blank?
return false if project_name == 'None'
true
end
def context_specified_by_name(context_name)
return false unless params['context_id'].blank?
return false if context_name.blank?
true
end
end

View file

@ -18,7 +18,7 @@
:html=> { :id=>'todo-form-new-action', :name=>'todo', :class => 'inline-form' },
:before => "$('#todo_new_action_submit').block({message:null})",
:complete => "$('#todo_new_action_submit').unblock()",
:condition => "askIfNewContextProvided()") do -%>
:condition => "askIfNewContextProvided('')") do -%>
<div id="status"><%= error_messages_for("item", :object_name => 'action') %></div>
@ -71,34 +71,42 @@
</div>
<div id="todo_multi_add" style="display:none">
<form id="todo-form-multi-new-acion" class="inline-form">
<% form_remote_tag(
:url => todos_path, :method => :post,
:html=> { :id=>'todo-form-multi-new-action', :name=>'todo', :class => 'inline-form' },
:before => "$('#todo_multi_new_action_submit').block({message:null})",
:complete => "$('#todo_multi_new_action_submit').unblock()",
:condition => "askIfNewContextProvided('multi_')") do -%>
<label for="todo_notes">Multiple next actions (one on each line)</label>
<%= text_area( "todo", "multiple_todos", "cols" => 29, "rows" => 6, "tabindex" => 2) %>
<input id="default_project_name_id" name="default_project_name" type="hidden" value="<%=@initial_project_name-%>" />
<label for="todo_project_name">Project for all actions</label>
<input id="todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="<%= @initial_project_name %>" />
<input id="multi_todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="<%= @initial_project_name %>" />
<div class="page_name_auto_complete" id="project_list" style="display:none"></div>
<input id="default_context_name_id" name="default_context_name" type="hidden" value="<%=@initial_context_name-%>" />
<label for="todo_context_name">Context for all actions</label>
<input id="todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="<%= @initial_context_name %>" />
<input id="multi_todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="<%= @initial_context_name %>" />
<div class="page_name_auto_complete" id="context_list" style="display:none"></div>
<label for="tag_list">Tags for all actions (sep. with commas)</label>
<%= text_field_tag "tag_list", @default_tags, :size => 30, :tabindex => 5 %>
<%= text_field_tag "multi_tag_list", @default_tags, :name=>:tag_list, :size => 30, :tabindex => 5 %>
<%= content_tag("div", "", :id => "tag_list_auto_complete", :class => "auto_complete") %>
<div class="submit_box">
<div class="widgets">
<button type="submit" class="positive" id="todo_multi_new_action_submit" tabindex="8">
<%= image_tag("accept.png", :alt => "") %>Add action
<%= image_tag("accept.png", :alt => "") %>Add actions
</button>
</div>
</div>
</form>
<% end -%>
</div>
</div>

View file

@ -0,0 +1,42 @@
if @saved
page.hide 'status'
status_message = 'Added new next action'
status_message += 's' if @todos.size > 1
status_message = 'Added new project / ' + status_message if @new_project_created
status_message = 'Added new context / ' + status_message if @new_context_created
page.notify :notice, status_message, 5.0
page['badge_count'].replace_html @down_count
# reset form and set focus to first field
page.send :record, "$('#todo-form-multi-new-action').clearForm();$('#todo-form-multi-new-action input:text:first').focus();"
# set defaults of form
page.send :record, "$('#multi_todo_context_name').val('#{@initial_context_name}');"
page.send :record, "$('#multi_todo_project_name').val('#{@initial_project_name}');"
page.send :record, "$('#multi_tag_list').val('#{@default_tags}');"
if should_show_new_item()
if @new_context_created
page.insert_html :top, 'display_box', :partial => 'contexts/context', :locals => { :context => @todo.context, :collapsible => true }
else
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" if source_view_is_one_of(:todo, :deferred, :tag)
@todos.each do |todo|
page.insert_html :bottom, item_container_id(todo), :partial => 'todos/todo', :locals => { :todo => todo, :parent_container_type => parent_container_type, :source_view => @source_view }
page.visual_effect :highlight, dom_id(todo), :duration => 3
end
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
end
if (source_view_is :project and @todo.pending?) or (source_view_is :deferred)
page['tickler-empty-nd'].hide # For some reason this does not work: page['tickler-empty-nd'].hide if (@todo.pending? or (source_view_is :deferred))
end
end
# make sure the behavior of the new/updated todo is enabled
page << "enable_rich_interaction();"
else
page.show 'status'
page.replace_html 'status', "#{error_messages_for('todo', :object_name => 'action')}"
end

View file

@ -11,7 +11,7 @@ var TracksForm = {
toggleLink.text(hideLinkText).attr('title', hideLinkTitle);
$('#'+formId+' input:text:first').focus();
}
toggleLink.parent.toggleClass('hide_form');
toggleLink.parent().toggleClass('hide_form');
},
hide_all_recurring: function () {
$.each(['daily', 'weekly', 'monthly', 'yearly'], function(){
@ -137,8 +137,8 @@ function setup_container_toggles(){
});
}
function askIfNewContextProvided() {
var givenContextName = $('#todo_context_name').val();
function askIfNewContextProvided(source) {
var givenContextName = $('#'+source+'todo_context_name').val();
var contextNames = [];
var contextNamesRequest = $.ajax({url: relative_to_root('contexts.autocomplete'),
async: false,

View file

@ -825,7 +825,7 @@ input#go_to_project, input#context_hide {
float: right;
}
#todo-form-new-action .submit_box, #project_form .submit_box, #context_form .submit_box, #todo-form-multi-new-acion .submit_box {
#todo-form-new-action .submit_box, #project_form .submit_box, #context_form .submit_box, #todo-form-multi-new-action .submit_box {
height: 25px;
padding: 5px 0;
text-align: center;