Merge branch 'bsag'

Conflicts:
	app/views/todos/_todo.html.erb
This commit is contained in:
Hans de Graaff 2009-12-06 09:40:16 +01:00
commit 28de00791f
220 changed files with 4775 additions and 14363 deletions

View file

@ -27,6 +27,7 @@ class ApplicationController < ActionController::Base
helper_method :current_user, :prefs
layout proc{ |controller| controller.mobile? ? "mobile" : "standard" }
exempt_from_layout /\.js\.erb$/
before_filter :set_session_expiration
before_filter :set_time_zone
@ -129,14 +130,6 @@ class ApplicationController < ActionController::Base
RedCloth.new(text).to_html
end
def build_default_project_context_name_map(projects)
Hash[*projects.reject{ |p| p.default_context.nil? }.map{ |p| [p.name, p.default_context.name] }.flatten].to_json
end
def build_default_project_tags_map(projects)
Hash[*projects.reject{ |p| p.default_tags.nil? }.map{ |p| [p.name, p.default_tags] }.flatten].to_json
end
# Here's the concept behind this "mobile content negotiation" hack: In
# addition to the main, AJAXy Web UI, Tracks has a lightweight low-feature
# 'mobile' version designed to be suitablef or use from a phone or PDA. It

View file

@ -92,7 +92,7 @@ class ContextsController < ApplicationController
if @context.save
if boolean_param('wants_render')
@context_state_changed = (@orgininal_context_hidden != @context.hidden?)
@context_state_changed = (@original_context_hidden != @context.hidden?)
@new_state = (@context.hidden? ? "hidden" : "active") if @context_state_changed
respond_to do |format|
format.js
@ -110,6 +110,13 @@ class ContextsController < ApplicationController
end
end
def edit
@context = Context.find(params[:id])
respond_to do |format|
format.js
end
end
# Fairly self-explanatory; deletes the context If the context contains
# actions, you'll get a warning dialogue. If you choose to go ahead, any
# actions in the context will also be deleted.
@ -124,11 +131,12 @@ class ContextsController < ApplicationController
# Methods for changing the sort order of the contexts in the list
#
def order
list = params["list-contexts-hidden"] || params["list-contexts-active"]
list.each_with_index do |id, position|
current_user.contexts.update(id, :position => position + 1)
end
context_ids = params["container_context"]
@projects = current_user.contexts.update_positions( context_ids )
render :nothing => true
rescue
notify :error, $!
redirect_to :action => 'index'
end
protected
@ -218,8 +226,6 @@ class ContextsController < ApplicationController
@projects = current_user.projects
@count = @not_done_todos.size
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
@default_project_tags_map = build_default_project_tags_map(@projects).to_json
end
end

View file

@ -1,6 +1,6 @@
class IntegrationsController < ApplicationController
skip_before_filter :login_required, :only => :search_plugin
skip_before_filter :login_required, :only => [:search_plugin, :google_gadget]
def index
@page_title = 'TRACKS::Integrations'
@ -32,4 +32,8 @@ class IntegrationsController < ApplicationController
render :layout => false
end
def google_gadget
render :layout => false, :content_type => Mime::XML
end
end

View file

@ -47,14 +47,14 @@ 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_project_context_name_map = build_default_project_context_name_map(current_user.projects).to_json
@default_project_tags_map = build_default_project_tags_map(current_user.projects).to_json
@default_tags = @project.default_tags
respond_to do |format|
format.html
format.m &render_project_mobile
@ -92,7 +92,7 @@ class ProjectsController < ApplicationController
elsif @project.new_record?
render_failure @project.errors.full_messages.join(', ')
else
head :created, :location => project_url(@project)
head :created, :location => project_url(@project), :text => @project.id
end
end
end
@ -127,6 +127,7 @@ class ProjectsController < ApplicationController
@active_projects_count = current_user.projects.active.count
@hidden_projects_count = current_user.projects.hidden.count
@completed_projects_count = current_user.projects.completed.count
init_data_for_sidebar
render :template => 'projects/update.js.rjs'
return
elsif boolean_param('update_status')
@ -173,7 +174,7 @@ class ProjectsController < ApplicationController
end
def order
project_ids = params["list-active-projects"] || params["list-hidden-projects"] || params["list-completed-projects"]
project_ids = params["container_project"]
@projects = current_user.projects.update_positions( project_ids )
render :nothing => true
rescue
@ -186,6 +187,7 @@ class ProjectsController < ApplicationController
@projects = current_user.projects.alphabetize(:state => @state) if @state
@contexts = current_user.contexts
init_not_done_counts(['project'])
init_project_hidden_todo_counts(['project']) if @state == 'hidden'
end
def actionize
@ -193,6 +195,7 @@ class ProjectsController < ApplicationController
@projects = current_user.projects.actionize(current_user.id, :state => @state) if @state
@contexts = current_user.contexts
init_not_done_counts(['project'])
init_project_hidden_todo_counts(['project']) if @state == 'hidden'
end
protected
@ -293,4 +296,4 @@ class ProjectsController < ApplicationController
project_description
end
end
end

View file

@ -251,8 +251,6 @@ class RecurringTodosController < ApplicationController
@xth_day = [['first',1],['second',2],['third',3],['fourth',4],['last',5]]
@projects = current_user.projects.find(:all, :include => [:default_context])
@contexts = current_user.contexts.find(:all)
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
@default_project_tags_map = build_default_project_tags_map(@projects).to_json
end
def get_recurring_todo_from_param
@ -265,4 +263,4 @@ class RecurringTodosController < ApplicationController
recurring_todos.each { |rt| rt.toggle_completion! if rt.todos.not_completed.count == 0}
end
end
end

View file

@ -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) }
@ -50,9 +50,11 @@ 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
@todo = current_user.todos.build(p.attributes)
@ -69,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
@ -94,6 +104,7 @@ class TodosController < ApplicationController
@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
@ -128,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
#
@ -140,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
@ -188,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'
@ -229,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?
@ -295,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
@ -354,11 +441,9 @@ 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
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
@default_project_tags_map = build_default_project_tags_map(@projects).to_json
respond_to do |format|
format.html
@ -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
@ -421,16 +509,19 @@ class TodosController < ApplicationController
@projects = current_user.projects
@contexts = current_user.contexts
@contexts_to_show = @contexts.reject {|x| x.hide? }
# Set defaults for new_action
@initial_tag_name = @tag_name
unless @not_done_todos.empty?
@context = current_user.contexts.find_by_id(@not_done_todos[0].context_id)
end
# Set count badge to number of items with this tag
@not_done_todos.empty? ? @count = 0 : @count = @not_done_todos.size
@down_count = @count
respond_to do |format|
format.html {
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
@default_project_tags_map = build_default_project_tags_map(@projects).to_json
}
format.html
format.m {
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
render :action => "mobile_tag"
@ -464,8 +555,6 @@ class TodosController < ApplicationController
@page_title = "TRACKS::Calendar"
@projects = current_user.projects.find(:all)
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
@default_project_tags_map = build_default_project_tags_map(@projects).to_json
due_today_date = Time.zone.now
due_this_week_date = Time.zone.now.end_of_week
@ -506,12 +595,52 @@ class TodosController < ApplicationController
def auto_complete_for_tag
@items = Tag.find(:all,
:conditions => [ "name LIKE ?", '%' + params['tag_list'] + '%' ],
:order => "name ASC",
:limit => 10)
:conditions => [ "name LIKE ?", '%' + params['tag_list'] + '%' ],
:order => "name ASC",
:limit => 10)
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
@ -669,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
@ -718,10 +848,15 @@ class TodosController < ApplicationController
end
def determine_deferred_tag_count(tag)
tag_collection = Tag.find_by_name(tag).todos
@deferred_tag_count = tag_collection.count(:all,
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'deferred'],
:order => 'show_from ASC, todos.created_at DESC')
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
end
def render_todos_html
@ -745,9 +880,6 @@ class TodosController < ApplicationController
end
end
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
@default_project_tags_map = build_default_project_tags_map(@projects).to_json
render
end
end
@ -953,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)

View file

@ -3,8 +3,7 @@ class UsersController < ApplicationController
skip_before_filter :login_required, :only => [ :new, :create ]
prepend_before_filter :login_optional, :only => [ :new, :create ]
# GET /users
# GET /users.xml
# GET /users GET /users.xml
def index
@users = User.find(:all, :order => 'login')
respond_to do |format|
@ -12,18 +11,17 @@ class UsersController < ApplicationController
@page_title = "TRACKS::Manage Users"
@users = User.paginate :page => params[:page], :order => 'login ASC'
@total_users = User.count
# When we call users/signup from the admin page
# we store the URL so that we get returned here when signup is successful
# When we call users/signup from the admin page we store the URL so that
# we get returned here when signup is successful
store_location
end
format.xml { render :xml => @users.to_xml(:except => [ :password ]) }
end
end
# GET /users/somelogin
# GET /users/somelogin.xml
# GET /users/id GET /users/id.xml
def show
@user = User.find_by_login(params[:id])
@user = User.find_by_id(params[:id])
render :xml => @user.to_xml(:except => [ :password ])
end
@ -46,13 +44,13 @@ class UsersController < ApplicationController
render :layout => "login"
end
# Example usage: curl -H 'Accept: application/xml' -H 'Content-Type: application/xml'
# Example usage: curl -H 'Accept: application/xml' -H 'Content-Type:
# application/xml'
# -u admin:up2n0g00d
# -d '<request><login>username</login><password>abc123</password></request>'
# http://our.tracks.host/users
#
# POST /users
# POST /users.xml
# POST /users POST /users.xml
def create
if params['exception']
render_failure "Expected post format is valid xml like so: <request><login>username</login><password>abc123</password></request>."
@ -107,10 +105,9 @@ class UsersController < ApplicationController
end
end
# DELETE /users/somelogin
# DELETE /users/somelogin.xml
# DELETE /users/id DELETE /users/id.xml
def destroy
@deleted_user = User.find_by_login(params[:id])
@deleted_user = User.find_by_id(params[:id])
@saved = @deleted_user.destroy
@total_users = User.find(:all).size
@ -150,9 +147,8 @@ class UsersController < ApplicationController
if (params[:open_id_complete] || (params[:user][:auth_type] == 'open_id')) && openid_enabled?
authenticate_with_open_id do |result, identity_url|
if result.successful?
# Success means that the transaction completed without
# error. If info is nil, it means that the user cancelled
# the verification.
# Success means that the transaction completed without error. If info
# is nil, it means that the user cancelled the verification.
@user.auth_type = 'open_id'
@user.open_id_url = identity_url
if @user.save
@ -207,5 +203,4 @@ class UsersController < ApplicationController
return true
end
end
end

View file

@ -166,4 +166,17 @@ module ApplicationHelper
return rt+rp+rts
end
def date_format_for_date_picker()
standard_format = current_user.prefs.date_format
translations = [
['%m', 'mm'],
['%d', 'dd'],
['%Y', 'yy'],
['%y', 'y']
]
translations.inject(standard_format) do |str, translation|
str.gsub(*translation)
end
end
end

View file

@ -11,19 +11,15 @@ module RecurringTodosHelper
end
def recurring_todo_remote_delete_icon
str = link_to( image_tag_for_delete,
link_to( image_tag_for_delete,
recurring_todo_path(@recurring_todo), :id => "delete_icon_"+@recurring_todo.id.to_s,
:class => "icon delete_icon", :title => "delete the recurring action '#{@recurring_todo.description}'")
set_behavior_for_delete_icon
str
end
def recurring_todo_remote_star_icon
str = link_to( image_tag_for_star(@recurring_todo),
link_to( image_tag_for_star(@recurring_todo),
toggle_star_recurring_todo_path(@recurring_todo),
:class => "icon star_item", :title => "star the action '#{@recurring_todo.description}'")
set_behavior_for_star_icon
str
end
def recurring_todo_remote_edit_icon
@ -31,7 +27,6 @@ module RecurringTodosHelper
str = link_to( image_tag_for_edit(@recurring_todo),
edit_recurring_todo_path(@recurring_todo),
:class => "icon edit_icon")
set_behavior_for_edit_icon
else
str = '<a class="icon">' + image_tag("blank.png") + "</a> "
end
@ -40,7 +35,6 @@ module RecurringTodosHelper
def recurring_todo_remote_toggle_checkbox
str = check_box_tag('item_id', toggle_check_recurring_todo_path(@recurring_todo), @recurring_todo.completed?, :class => 'item-checkbox')
set_behavior_for_toggle_checkbox
str
end
@ -53,26 +47,4 @@ module RecurringTodosHelper
def image_tag_for_edit(todo)
image_tag("blank.png", :title =>"Edit action", :class=>"edit_item", :id=> dom_id(todo, 'edit_icon'))
end
def set_behavior_for_delete_icon
parameters = "_source_view=#{@source_view}"
parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
apply_behavior '.item-container a.delete_icon:click', :prevent_default => true do |page|
page.confirming "'Are you sure that you want to ' + this.title + '?'" do
page << "itemContainer = this.up('.item-container'); itemContainer.startWaiting();"
page << remote_to_href(:method => 'delete', :with => "'#{parameters}'", :complete => "itemContainer.stopWaiting();")
end
end
end
def set_behavior_for_edit_icon
parameters = "_source_view=#{@source_view}"
parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
apply_behavior '.item-container a.edit_icon:click', :prevent_default => true do |page|
page << "Effect.Pulsate(this);"
page << remote_to_href(:method => 'get', :with => "'#{parameters}'")
end
end
end
end

View file

@ -8,52 +8,41 @@ module TodosHelper
end
def form_remote_tag_edit_todo( &block )
form_tag(
todo_path(@todo), {
form_remote_tag(
:url => todo_path(@todo),
:loading => "$('#submit_todo_#{@todo.id}').block({message: null})",
:html => {
:method => :put,
:id => dom_id(@todo, 'form'),
:class => dom_id(@todo, 'form') + " inline-form edit_todo_form" },
&block )
apply_behavior 'form.edit_todo_form', make_remote_form(
:method => :put,
:before => "todoSpinner = this.down('button.positive'); todoSpinner.startWaiting()",
:loaded => "todoSpinner.stopWaiting()",
:condition => "!(this.down('button.positive').isWaiting())"),
:prevent_default => true
end
def set_behavior_for_star_icon
apply_behavior '.item-container a.star_item:click',
remote_to_href(:method => 'put', :with => "{ _source_view : '#{@source_view}' }"),
:prevent_default => true
end
def remote_star_icon
str = link_to( image_tag_for_star(@todo),
link_to( image_tag_for_star(@todo),
toggle_star_todo_path(@todo),
:class => "icon star_item", :title => "star the action '#{@todo.description}'")
set_behavior_for_star_icon
str
end
def remote_edit_menu_item(parameters, todo)
return link_to_remote(
image_tag("edit_off.png", :mouseover => "edit_on.png", :alt => "", :align => "absmiddle")+" Edit",
image_tag("edit_off.png", :mouseover => "edit_on.png", :alt => "Edit", :align => "absmiddle", :id => 'edit_icon_todo_'+todo.id.to_s)+" Edit",
:url => {:controller => 'todos', :action => 'edit', :id => todo.id},
:method => 'get',
:with => "'#{parameters}'",
:before => todo_start_waiting_js(todo),
:complete => todo_stop_waiting_js)
:complete => todo_stop_waiting_js(todo))
end
def remote_delete_menu_item(parameters, todo)
return link_to_remote(
image_tag("delete_off.png", :mouseover => "delete_on.png", :alt => "", :align => "absmiddle")+" Delete",
image_tag("delete_off.png", :mouseover => "delete_on.png", :alt => "Delete", :align => "absmiddle")+" Delete",
:url => {:controller => 'todos', :action => 'destroy', :id => todo.id},
:method => 'delete',
:with => "'#{parameters}'",
:before => todo_start_waiting_js(todo),
:complete => todo_stop_waiting_js)
:complete => todo_stop_waiting_js(todo),
:confirm => "Are you sure that you want to delete the action '#{todo.description}'?")
end
def remote_defer_menu_item(days, todo)
@ -64,24 +53,28 @@ module TodosHelper
futuredate = (@todo.show_from || @todo.user.date) + days.days
if @todo.due && futuredate > @todo.due
return link_to_function(
image_tag("defer_#{days}_off.png", :mouseover => "defer_#{days}.png", :alt => "", :align => "absmiddle")+" Defer #{pluralize(days, "day")}",
image_tag("defer_#{days}_off.png", :mouseover => "defer_#{days}.png", :alt => "Defer #{pluralize(days, "day")}", :align => "absmiddle")+" Defer #{pluralize(days, "day")}",
"alert('Defer date is after due date. Please edit and adjust due date before deferring.')"
)
else
return link_to_remote(
image_tag("defer_#{days}_off.png", :mouseover => "defer_#{days}.png", :alt => "", :align => "absmiddle")+" Defer #{pluralize(days, "day")}",
image_tag("defer_#{days}_off.png", :mouseover => "defer_#{days}.png", :alt => "Defer #{pluralize(days, "day")}", :align => "absmiddle")+" Defer #{pluralize(days, "day")}",
:url => url,
:before => todo_start_waiting_js(todo),
:complete => todo_stop_waiting_js)
:complete => todo_stop_waiting_js(todo))
end
end
def todo_start_waiting_js(todo)
return "$('ul#{dom_id(todo)}').hide(); itemContainer = $('#{dom_id(todo)}'); itemContainer.startWaiting()"
return "$('#ul#{dom_id(todo)}').css('visibility', 'hidden'); $('##{dom_id(todo)}').block({message: null})"
end
def todo_stop_waiting_js
return "itemContainer.stopWaiting();"
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
def image_tag_for_recurring_todo(todo)
@ -91,23 +84,17 @@ module TodosHelper
:class => "recurring_icon", :title => recurrence_pattern_as_text(todo.recurring_todo))
end
def set_behavior_for_toggle_checkbox
parameters = "_source_view=#{@source_view}"
parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
apply_behavior '.item-container input.item-checkbox:click',
remote_function(:url => javascript_variable('this.value'), :method => 'put',
:with => "'#{parameters}'")
end
def remote_toggle_checkbox
str = check_box_tag('item_id', toggle_check_todo_path(@todo), @todo.completed?, :class => 'item-checkbox')
set_behavior_for_toggle_checkbox
str
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?
"<span class=\"grey\">#{format_date( @todo.completed_at )}</span>"
elsif @todo.pending?
"<a title='Depends on: #{@todo.uncompleted_predecessors.map(&:description).join(', ')}'><span class=\"orange\">Pending</span></a> "
elsif @todo.deferred?
show_date( @todo.show_from )
else
@ -115,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
@ -135,6 +138,10 @@ module TodosHelper
if tag_list.empty? then "" else "<span class=\"tags\">#{tag_list}</span>" 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)})"
@ -213,23 +220,25 @@ module TodosHelper
end
def calendar_setup( input_field )
str = "Calendar.setup({ ifFormat:\"#{prefs.date_format}\""
str << ",firstDay:#{prefs.week_starts},showOthers:true,range:[2004, 2010]"
str << ",step:1,inputField:\"" + input_field + "\",cache:true,align:\"TR\" })\n"
javascript_tag str
# TODO:jQuery
#str = "Calendar.setup({ ifFormat:\"#{prefs.date_format}\""
#str << ",firstDay:#{prefs.week_starts},showOthers:true,range:[2004, 2010]"
#str << ",step:1,inputField:\"" + input_field + "\",cache:true,align:\"TR\" })\n"
#javascript_tag str
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}"
return "c#{todo.context_id}items"
end
def should_show_new_item
if @todo.project.nil? == false
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?)
@ -240,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
@ -266,6 +277,20 @@ module TodosHelper
array_or_string_for_javascript( current_user.contexts.collect{|c| escape_javascript(c.name) } )
end
def tag_names_for_autocomplete
array_or_string_for_javascript( Tag.all.collect{|c| escape_javascript(c.name) } )
end
def default_contexts_for_autocomplete
projects = current_user.projects.find(:all, :conditions => ['default_context_id is not null'])
Hash[*projects.map{ |p| [p.name, p.default_context.name] }.flatten].to_json
end
def default_tags_for_autocomplete
projects = current_user.projects.find(:all, :conditions => ["default_tags != ''"])
Hash[*projects.map{ |p| [p.name, p.default_tags] }.flatten].to_json
end
def format_ical_notes(notes)
split_notes = notes.split(/\n/)
joined_notes = split_notes.join("\\n")
@ -286,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

7
app/models/dependency.rb Normal file
View file

@ -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

View file

@ -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"

View file

@ -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 <context, project>
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 <context, project>
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

View file

@ -10,6 +10,13 @@ class User < ActiveRecord::Base
def find_by_params(params)
find(params['id'] || params['context_id']) || nil
end
def update_positions(context_ids)
context_ids.each_with_index do |id, position|
context = self.detect { |c| c.id == id.to_i }
raise "Context id #{id} not associated with user id #{@user.id}." if context.nil?
context.update_attribute(:position, position + 1)
end
end
end
has_many :projects,
:order => 'projects.position ASC',
@ -91,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'],

View file

@ -3,31 +3,9 @@
<h2>
<% if collapsible -%>
<a href="#" class="container_toggle" id="toggle_c<%= context.id %>"><%= image_tag("collapse.png") %></a>
<% apply_behavior '.container_toggle:click', :prevent_default => true do |page|
page << " /* only handle the click if a previous click had finished its animation */
if (todoItems.lastEffect == null || todoItems.lastEffect.state=='finished') {
containerElem = this.up('.container')
toggleTarget = containerElem.down('.toggle_target')
if (Element.visible(toggleTarget))
{
todoItems.collapseNextActionListing(this, toggleTarget);
todoItems.contextCollapseCookieManager.setCookie(todoItems.buildCookieName(containerElem), true)
}
else
{
todoItems.expandNextActionListing(this, toggleTarget);
todoItems.contextCollapseCookieManager.clearCookie(todoItems.buildCookieName(containerElem))
}
}
"
end
-%>
<% end -%>
<% if source_view_is :context %>
<span class="in_place_editor_field" id="context_name_in_place_editor"><%= context.name %></span>
<%= in_place_editor 'context_name_in_place_editor', {
:url => { :controller => 'contexts', :action => 'update', :id => context.id, :field => 'name', :update_context_name => true, :escape => false},
:options=>"{method:'put'}", :script => true } %>
<span id="context_name"><%= context.name %></span>
<% else %>
<%= link_to_context( context ) %>
<% end %>
@ -37,11 +15,5 @@
<div class="message"><p>Currently there are no incomplete actions in this context</p></div>
</div>
<%= render :partial => "todos/todo", :collection => @not_done, :locals => { :parent_container_type => "context" } %>
<% if @not_done.empty?
# fix (hack) for #713
set_behavior_for_star_icon
set_behavior_for_toggle_checkbox
end
-%>
</div><!-- [end:items] -->
</div><!-- [end:c<%= context.id %>] -->
</div><!-- [end:c<%= context.id %>] -->

View file

@ -1,35 +1,28 @@
<% context = context_form
@context = context-%>
<div id="<%= dom_id(context, 'edit') %>" class="edit-form" style="display:none;">
<% form_tag(context_path(context), {:id => dom_id(context, 'edit_form'), :class => "inline-form "+dom_id(context, 'edit_form')+"-edit-context-form edit-context-form", :method => :put}) do -%>
<%= error_messages_for 'context' %>
<label for="context_name">Context name</label><br/>
<%= text_field('context', 'name', :class => 'context-name') %><br/>
<% form_remote_tag(:url => context_path(context), :html => {:id => dom_id(context, 'edit_form'), :class => "inline-form "+dom_id(context, 'edit_form')+"-edit-context-form edit-context-form", :method => :put}) do -%>
<%= error_messages_for 'context' %>
<label for="context_hide">Hide from front page?</label>
<%= check_box('context', 'hide', :class => 'context-hide') %>
<input type="hidden" name="wants_render" value="true" />
<div class="submit_box">
<div class="widgets">
<button type="submit" class="positive" id="<%= dom_id(context, 'submit') %>" tabindex="15">
<%=image_tag("accept.png", :alt => "") %>
Update
</button>
<a href="javascript:void(0);" onclick="Element.toggle('<%= dom_id(context) %>');Element.toggle('<%= dom_id(context, 'edit') %>');" class="negative">
<%=image_tag("cancel.png", :alt => "") %>
Cancel
</a>
</div>
<label for="context_name">Context name</label><br/>
<%= text_field('context', 'name', :class => 'context-name') %><br/>
<label for="context_hide">Hide from front page?</label>
<%= check_box('context', 'hide', :class => 'context-hide') %>
<input type="hidden" name="wants_render" value="true" />
<div class="submit_box">
<div class="widgets">
<button type="submit" class="positive" id="<%= dom_id(context, 'submit') %>" tabindex="15">
<%=image_tag("accept.png", :alt => "") %>
Update
</button>
<a href="" onclick="" class="negative">
<%=image_tag("cancel.png", :alt => "") %>
Cancel
</a>
</div>
<br/><br/>
<% end %>
<%= apply_behavior ".edit-context-form", make_remote_form(
:before => "this.up('div.edit-form').down('button.positive').startWaiting()",
:condition => "!(this.up('div.edit-form').down('button.positive')).isWaiting()"),
:external => true
@context = nil %>
</div>
</div>
<br/><br/>
<% end %>

View file

@ -19,23 +19,25 @@
<% else %>
<span class="grey">VISIBLE</span>
<% end %>
<a class="delete_context_button" href="<%= formatted_context_path(context, :js) %>" title="delete the context '<%= context.name %>'"><%= image_tag( "blank.png", :title => "Delete context", :class=>"delete_item") %></a>
<%= apply_behavior "a.delete_context_button:click", { :prevent_default => true, :external => true} do |page, element|
page.confirming "'Are you sure that you want to ' + this.title + '?'" do
element.up('.context').start_waiting
page << remote_to_href(:method => 'delete')
end
end -%>
<a class="edit_context_button" href="#"><%= image_tag( "blank.png", :title => "Edit context", :class=>"edit_item") %></a>
<%= apply_behavior 'a.edit_context_button:click', :prevent_default => true do |page, element|
element.up('.context').toggle
editform = element.up('.list').down('.edit-form')
editform.toggle
editform.visual_effect(:appear)
editform.down('input').focus
end
-%>
<%= link_to_remote(
image_tag( "blank.png", :title => "Delete context", :class=>"delete_item"),
:url => {:controller => 'contexts', :action => 'destroy', :id => context.id},
:method => 'delete',
:with => "'_source_view=#{@source_view}'",
:before => "$('#{dom_id(context)}').block({message:null});",
:complete => "$('#{dom_id(context)}').unblock();",
:confirm => "Are you sure that you want to delete the context '#{context.name}'?"
) %>
<%= link_to_remote(
image_tag( "blank.png", :title => "Edit context", :class=>"edit_item"),
:url => {:controller => 'contexts', :action => 'edit', :id => context.id},
:method => 'get',
:with => "'_source_view=#{@source_view}'",
:before => "$('#{dom_id(context)}').block({message:null});",
:complete => "$('#{dom_id(context)}').unblock();"
) %>
</div>
</div>
<%= render :partial => 'contexts/context_form', :object => context %>
</div>
<div id="<%= dom_id(context, 'edit') %>" class="edit-form" style="display:none;">
</div>
</div>

View file

@ -1,12 +1,12 @@
if @saved
container_name = 'list-contexts-' + (@context.hidden? ? 'hidden' : 'active')
page.hide 'contexts-empty-nd'
page.insert_html :bottom, "list-contexts", :partial => 'context_listing', :locals => { :context_listing => @context }
page.sortable "list-contexts", get_listing_sortable_options
page.insert_html :bottom, container_name, :partial => 'context_listing', :locals => { :context_listing => @context }
page.hide 'status'
page['badge_count'].replace_html @down_count
page.call "Form.reset", "context-form"
page.call "Form.focusFirstElement", "context-form"
page << '$("#context-form").clearForm();'
page << '$("#context-form input:text:first").focus();'
else
page.show 'status'
page.replace_html 'status', "#{error_messages_for('context')}"
end
end

View file

@ -0,0 +1,4 @@
page[dom_id(@context, 'edit')].replace_html :partial => 'context_form', :locals => { :context_form => @context }
page[@context].hide
page[dom_id(@context, 'edit')].show
page[dom_id(@context, 'edit_form')].find('input.context-name').focus

View file

@ -8,12 +8,6 @@
<div id="toggle_context_new" class="hide_form">
<a title="Hide new context form" accesskey="n">&laquo; Hide form</a>
<% apply_behavior '#toggle_context_new a:click', :prevent_default => true do |page|
page << "TracksForm.toggle('toggle_context_new', 'context_new', 'context-form',
'&laquo; Hide form', 'Hide new context form',
'Create a new context &#187;', 'Add a context');"
end
%>
</div>
<div id="context_new" class="context_new" style="display:block">
@ -21,9 +15,8 @@
:url => contexts_path,
:method => :post,
:html=> { :id => 'context-form', :name => 'context', :class => 'inline-form'},
:before => "$('context_new_submit').startWaiting()",
:complete => "$('context_new_submit').stopWaiting()",
:condition => "!$('context_new_submit').isWaiting()") do -%>
:before => "$('#context_new_submit').block({message: null})",
:complete => "$('#context_new_submit').unblock()") do -%>
<div id="status"><%= error_messages_for('context') %></div>
@ -49,8 +42,3 @@
sortable_element 'list-contexts-active', get_listing_sortable_options
sortable_element 'list-contexts-hidden', get_listing_sortable_options
-%>
<script type="text/javascript">
window.onload=function(){
Nifty("div#context_new_container","normal");
}
</script>

View file

@ -1,12 +1,9 @@
status_message = 'Context saved'
page.notify :notice, status_message, 5.0
if @context_state_changed
page << "jQuery('##{dom_id(@context, 'edit')}').hide();"
page.remove dom_id(@context, 'container')
page.insert_html :bottom, "list-contexts-#{@new_state}", :partial => 'context_listing', :object => @context
else
page.replace_html dom_id(@context, 'container'), :partial => 'context_listing', :object => @context
end
page.sortable "list-contexts-active", get_listing_sortable_options
page.sortable "list-contexts-hidden", get_listing_sortable_options
page.hide "busy"
page.visual_effect :highlight, dom_id(@context), :duration => 3

View file

@ -34,9 +34,3 @@
</table>
</div><!-- End of feeds -->
</div>
<script type="text/javascript">
window.onload=function(){
Nifty("div#feedlegend","normal");
}
</script>

View file

@ -15,9 +15,3 @@
</p>
</div><!-- End of feeds -->
</div><!-- End of display_box -->
<script type="text/javascript">
window.onload=function(){
Nifty("div#feedlegend","normal");
}
</script>

View file

@ -125,11 +125,3 @@
<div id="input_box">
<%= render :file => "sidebar/sidebar.html.erb" %>
</div><!-- End of input box -->
<script type="text/javascript">
window.onload=function(){
Nifty("div#feedicons-project","normal");
Nifty("div#feedicons-context","normal");
Nifty("div#feedlegend","normal");
}
</script>

View file

@ -0,0 +1,5 @@
<Module>
<ModulePrefs title="Tracks" directory_title="Tracks" description="Gadget to add Tracks to Gmail as a gadget" author="Tracks" author_email="butshesagirl@rousette.org.uk" author_affiliation="Tracks" author_location="UK" title_url="http://www.getontracks.org/" screenshot="http://www.getontracks.org/images/uploads/tracks_home_thumb.png" thumbnail="http://www.getontracks.org/images/uploads/tracks_tickler.png" category="communication" category2="tools" height="300">
</ModulePrefs>
<Content type="url" href="<%= home_url %>mobile"/>
</Module>

View file

@ -1,27 +1,23 @@
<% has_contexts = !current_user.contexts.empty? -%>
<h1>Integrations</h1>
<p>Tracks can be integrated with a number of other tools... whatever it takes to help you get things done! This page has information on setting up some of these. Not all of these are applicable to all platforms, and some require more technical knowledge than others. See also <%= link_to "developer documentation for Tracks' REST API", url_for(:action => 'rest_api') %>.</p>
<p>Contents:
<ol>
<p>Tracks can be integrated with a number of other tools...
whatever it takes to help you get things done!
This page has information on setting up some of these.
Not all of these are applicable to all platforms, and some require more
technical knowledge than others.
See also <%= link_to "developer documentation for Tracks' REST API", url_for(:action => 'rest_api') %>.</p>
<br/><p>Contents:</p>
<ul>
<li><a href="#applescript1-section">Add an Action with Applescript</a></li>
<li><a href="#applescript2-section">Add an Action with Applescript based on the currently selected Email in Mail.app</a></li>
<li><a href="#quicksilver-applescript-section">Add Actions with Quicksilver and Applescript</a></li>
<li><a href="#email-cron-section">Automatically Email Yourself Upcoming Actions</a></li>
</ol><br />
</p>
<p>Do you have one of your own to add? <a href="http://www.rousette.org.uk/projects/forums/viewforum/10/" title="Tracks | Tips and Tricks">Tell us about it in our Tips and Tricks forum
</a> and we may include it on this page in a future versions of Tracks.</p>
<a name="message_gateway"> </a>
<h2>Integrated email/SMS receiver</h2>
<p>
If Tracks is running on the same server as your mail server, you can use the integrated mail handler built into tracks. Steps to set it up:
<ul>
<li>Go to <%= link_to "Preferences", preferences_url %> and set your "From email" and "default email context" for todos sent in via email (which could come from an SMS message)</li>
<li>In sendmail/qmail/postfix/whatever, set up an email address alias to pipe messages to <pre style="font-size:1.5em">/PATH/TO/RUBY/ruby /PATH/TO/TRACKS/script/runner -e production 'MessageGateway.receive(STDIN.read)'</pre></li>
<li>Send an email to your newly configured address!</li>
</ul>
You can also use the Rich Todo API to send in tasks like "do laundry @ Home" or "Call Bill > project X". The subject of the message will fill description, context, and project, while the body will populate the tasks's note.
<li><a href="#message_gateway">Integrate Tracks with an email server to be able to send an action through email to Tracks</a></li>
<li><a href="#google_gadget">Add Tracks as a Google Gmail gadget</a></li>
</ul><br/>
<p>Do you have one of your own to add?
<a href="http://www.getontracks.org/forums/viewforum/10/" title="Tracks | Tips and Tricks">Tell us about
it in our Tips and Tricks forum</a> and we may include it on this page in a future versions of Tracks.
</p>
<a name="applescript1-section"> </a>
@ -29,22 +25,22 @@ You can also use the Rich Todo API to send in tasks like "do laundry @ Home" or
<p>This is a simple script that pops up a dialog box asking for a description, and then sends that to Tracks with a hard-coded context.</p>
<% if has_contexts -%>
<ol>
<li>Choose the context you want to add actions to: <select name="applescript1-contexts" id="applescript1-contexts"><%= options_from_collection_for_select(current_user.contexts, "id", "name", current_user.contexts.first.id) %></select>
<%= observe_field "applescript1-contexts", :update => "applescript1",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_applescript1" },
:before => "$('applescript1').startWaiting()",
:complete => "$('applescript1').stopWaiting()"
%>
</li>
<li>Copy the Applescript below to the clipboard.<br />
<textarea id="applescript1" name="applescript1" rows="15"><%= render :partial => 'applescript1', :locals => { :context => current_user.contexts.first } %></textarea>
</li>
<li>Open Script Editor and paste the script into a new document.</li>
<li>Compile and save the script. Run it as necessary.</li>
</ol>
<ol>
<li>Choose the context you want to add actions to: <select name="applescript1-contexts" id="applescript1-contexts"><%= options_from_collection_for_select(current_user.contexts, "id", "name", current_user.contexts.first.id) %></select>
<%= observe_field "applescript1-contexts", :update => "applescript1",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_applescript1" },
:before => "$('applescript1').startWaiting()",
:complete => "$('applescript1').stopWaiting()"
%>
</li>
<li>Copy the Applescript below to the clipboard.<br />
<textarea id="applescript1" name="applescript1" rows="15"><%= render :partial => 'applescript1', :locals => { :context => current_user.contexts.first } %></textarea>
</li>
<li>Open Script Editor and paste the script into a new document.</li>
<li>Compile and save the script. Run it as necessary.</li>
</ol>
<% else %>
<br/><p id="no_context_msg"><i>You do not have any context yet. The script will be available after you add your first context</i></p>
<% end %>
@ -54,23 +50,23 @@ You can also use the Rich Todo API to send in tasks like "do laundry @ Home" or
<p>This script takes the sender and subject of the selected email(s) in Mail and creates a new action for each one, with the description, "Email [sender] about [subject]". The description gets truncated to 100 characters (the validation limit for the field) if it is longer than that. It also has Growl notifications if you have Growl installed.</p>
<% if has_contexts -%>
<ol>
<li>Choose the context you want to add actions to: <select name="applescript2-contexts" id="applescript2-contexts"><%= options_from_collection_for_select(current_user.contexts, "id", "name", current_user.contexts.first.id) %></select>
<%= observe_field "applescript2-contexts", :update => "applescript2",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_applescript2" },
:before => "$('applescript2').startWaiting()",
:complete => "$('applescript2').stopWaiting()"
%>
</li>
<li>Copy the Applescript below to the clipboard.<br />
<textarea id="applescript2" name="applescript2" rows="15"><%= render :partial => 'applescript2', :locals => { :context => current_user.contexts.first } %></textarea>
</li>
<li>Open Script Editor and paste the script into a new document.</li>
<li>Compile and save the script to the ~/Library/Scriipts/Mail Scripts directory.</li>
<li>For more information on using AppleScript with Mail.app, see <a href="http://www.apple.com/applescript/mail/" title="Scriptable Applications: Mail">this overview</a>.
</ol>
<ol>
<li>Choose the context you want to add actions to: <select name="applescript2-contexts" id="applescript2-contexts"><%= options_from_collection_for_select(current_user.contexts, "id", "name", current_user.contexts.first.id) %></select>
<%= observe_field "applescript2-contexts", :update => "applescript2",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_applescript2" },
:before => "$('applescript2').startWaiting()",
:complete => "$('applescript2').stopWaiting()"
%>
</li>
<li>Copy the Applescript below to the clipboard.<br />
<textarea id="applescript2" name="applescript2" rows="15"><%= render :partial => 'applescript2', :locals => { :context => current_user.contexts.first } %></textarea>
</li>
<li>Open Script Editor and paste the script into a new document.</li>
<li>Compile and save the script to the ~/Library/Scriipts/Mail Scripts directory.</li>
<li>For more information on using AppleScript with Mail.app, see <a href="http://www.apple.com/applescript/mail/" title="Scriptable Applications: Mail">this overview</a>.
</ol>
<% else %>
<br/><p><i>You do not have any context yet. The script will be available after you add your first context</i></p>
<% end %>
@ -81,28 +77,28 @@ You can also use the Rich Todo API to send in tasks like "do laundry @ Home" or
<p>This integration will allow you to add actions to Tracks via <a href="http://quicksilver.blacktree.com/">Quicksilver</a>.</p>
<% if has_contexts -%>
<ol>
<li>Choose the context you want to add actions to: <select name="quicksilver-contexts" id="quicksilver-contexts"><%= options_from_collection_for_select(current_user.contexts, "id", "name", current_user.contexts.first.id) %></select>
<%= observe_field "quicksilver-contexts", :update => "quicksilver",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_quicksilver_applescript" },
:before => "$('quicksilver').startWaiting()",
:complete => "$('quicksilver').stopWaiting()"
%>
</li>
<li>Copy the Applescript below to the clipboard.<br />
<textarea id="quicksilver" name="quicksilver" rows="15"><%= render :partial => 'quicksilver_applescript', :locals => { :context => current_user.contexts.first } %></textarea>
</li>
<li>Open Script Editor and paste the script into a new document.</li>
<li>Compile and save the script as "Add to Tracks.scpt" in ~/Library/Application Support/Quicksilver/Actions/ (you may need to create the Actions directory)</li>
<li>Restart Quicksilver</li>
<li>Activate Quicksilver (Ctrl+Space by default)</li>
<li>Press "." to put quicksilver into text mode</li>
<li>Type the description of the next action you want to add</li>
<li>Press tab to switch to the action pane.</li>
<li>By typing or scrolling, choose the "Add to Tracks" action.</li>
</ol>
<ol>
<li>Choose the context you want to add actions to: <select name="quicksilver-contexts" id="quicksilver-contexts"><%= options_from_collection_for_select(current_user.contexts, "id", "name", current_user.contexts.first.id) %></select>
<%= observe_field "quicksilver-contexts", :update => "quicksilver",
:with => 'context_id',
:url => { :controller => "integrations", :action => "get_quicksilver_applescript" },
:before => "$('quicksilver').startWaiting()",
:complete => "$('quicksilver').stopWaiting()"
%>
</li>
<li>Copy the Applescript below to the clipboard.<br />
<textarea id="quicksilver" name="quicksilver" rows="15"><%= render :partial => 'quicksilver_applescript', :locals => { :context => current_user.contexts.first } %></textarea>
</li>
<li>Open Script Editor and paste the script into a new document.</li>
<li>Compile and save the script as "Add to Tracks.scpt" in ~/Library/Application Support/Quicksilver/Actions/ (you may need to create the Actions directory)</li>
<li>Restart Quicksilver</li>
<li>Activate Quicksilver (Ctrl+Space by default)</li>
<li>Press "." to put quicksilver into text mode</li>
<li>Type the description of the next action you want to add</li>
<li>Press tab to switch to the action pane.</li>
<li>By typing or scrolling, choose the "Add to Tracks" action.</li>
</ol>
<% else %>
<br/><p><i>You do not have any context yet. The script will be available after you add your first context</i></p>
<% end %>
@ -115,3 +111,38 @@ You can also use the Rich Todo API to send in tasks like "do laundry @ Home" or
<textarea id="cron" name="cron">0 5 * * * /usr/bin/curl -0 "<%= home_url %>todos.txt?due=6&token=<%= current_user.token %>" | /usr/bin/mail -e -s 'Tracks actions due in the next 7 days' youremail@yourdomain.com</textarea>
<p>You can of course use other text <%= link_to 'feeds provided by Tracks', feeds_path %> -- why not email a list of next actions in a particular project to a group of colleagues who are working on the project?</p>
<a name="message_gateway"> </a>
<h2>Integrated email/SMS receiver</h2>
<p>
If Tracks is running on the same server as your mail server, you can use the integrated mail handler built into tracks. Steps to set it up:
<ul>
<li>Go to <%= link_to "Preferences", preferences_url %> and set your "From email" and "default email context" for todos sent in via email (which could come from an SMS message)</li>
<li>In sendmail/qmail/postfix/whatever, set up an email address alias to pipe messages to <pre >/PATH/TO/RUBY/ruby /PATH/TO/TRACKS/script/runner -e production 'MessageGateway.receive(STDIN.read)'</pre></li>
<li>Send an email to your newly configured address!</li>
</ul>
<p>You can also use the Rich Todo API to send in tasks like "do laundry @ Home"
or "Call Bill > project X". The subject of the message will fill description,
context, and project, while the body will populate the tasks's note.
</p>
<a name="google_gadget"> </a>
<h2>Add Tracks as a Google Gmail gadget</h2>
<p>
You can now manage your projects/actions inside Gmail using Tracks Gmail Gadget.
Add Tracks Gmail gadget to the sidebar of Gmail and track your next actions
or add new action without explicitly open new browser tab for Tracks. Steps to set it up:
</p>
<ul>
<li>Sign in to Gmail and click Settings in the top right of your Gmail page. In Gmail setting page, click Labs tab</li>
<li>Enable the "Add any gadget by URL" feature. You will find it at bottom of the list. Select Enable radio button and click Save Changes button.</li>
<li>Now you can see Gadgets tab added to Gmail Settings. Go to the Gadgets tab</li>
<li>Paste following link to the Add a gadget by its URL: and then click Add button:<br/>
<pre><%= integrations_url + "/google_gadget" %></pre></li>
</ul>
<Module>
<ModulePrefs title="GTDify" directory_title="GTDify" description="Official gadget for GTDify service." author="GTDify" author_email="support@gtdify.com" author_affiliation="GTDify" author_location="NJ, USA" title_url="http://www.gtdify.com/" screenshot="http://www.gtdify.com/modules/gmail/ss.png" thumbnail="http://www.gtdify.com/modules/gmail/tn.png" category="communication" category2="tools" height="300">
</ModulePrefs>
<Content type="url" href="http://my.gtdify.com/mobile/"/>
</Module>

View file

@ -3,7 +3,8 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<%= stylesheet_link_tag "scaffold" %>
<%= javascript_include_tag :defaults %>
<%= javascript_include_tag 'jquery' %>
<%= javascript_include_tag 'jquery.cookie' %>
<title><%= @page_title -%></title>

View file

@ -6,22 +6,35 @@
<meta http-equiv="Refresh" content="<%= @prefs["refresh"].to_i*60 %>;url=<%= request.request_uri %>">
<% end -%>
<% bundle :name => "tracks_css" do %>
<%= stylesheet_link_tag *%w[ standard superfish calendar-system niftyCorners] %>
<%= stylesheet_link_tag *%w[ standard superfish niftyCorners jquery-ui jquery.autocomplete] %>
<% end %>
<%= stylesheet_link_tag "print", :media => "print" %>
<% bundle :name => "jquery" do %>
<%= javascript_include_tag "jquery-1.2.6.min" %>
<%= javascript_include_tag 'jquery' %>
<%= javascript_include_tag 'jquery-ui' %>
<%= javascript_include_tag 'jquery.cookie' %>
<%= javascript_include_tag 'jquery.blockUI' %>
<%= javascript_include_tag 'jquery.jeditable' %>
<%= javascript_include_tag 'jquery.autocomplete' %>
<% end %>
<% bundle :name => "tracks_js" do %>
<%= javascript_include_tag *%w[
hoverIntent superfish prototype
effects dragdrop controls application
calendar calendar-en calendar-setup
accesskey-hints todo-items niftycube
protoload flashobject lowpro
] %>
hoverIntent superfish application
accesskey-hints niftycube flashobject ] %>
<% end %>
<%= javascript_include_tag :unobtrusive %>
<%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %>
<%= javascript_tag "var SOURCE_VIEW = '#{@source_view}';" %>
<%= javascript_tag "var TAG_NAME = '#{@tag_name}';" if @tag_name %>
<script type="text/javascript">
<% if defined? context_names_for_autocomplete -%>
var contextNames = <%= context_names_for_autocomplete %>;
var projectNames = <%= project_names_for_autocomplete %>;
var defaultContexts = <%= default_contexts_for_autocomplete %>;
var defaultTags = <%= default_tags_for_autocomplete %>;
var tagNames = <%= tag_names_for_autocomplete %>;
var dateFormat = '<%= date_format_for_date_picker %>';
<% end -%>
</script>
<link rel="shortcut icon" href="<%= url_for(:controller => 'favicon.ico') %>" />
<%= auto_discovery_link_tag(:rss, {:controller => "todos", :action => "index", :format => 'rss', :token => "#{current_user.token}"}, {:title => "RSS feed of next actions"}) %>
<link rel="search" type="application/opensearchdescription+xml" title="Tracks" href="<%= search_plugin_path %>" />
@ -54,14 +67,16 @@
<li><%= navigation_link( "Contexts", contexts_path, {:accesskey=>"c", :title=>"Contexts"} ) %></li>
<li><%= navigation_link( "Notes", notes_path, {:accesskey => "o", :title => "Show all notes"} ) %></li>
<li><%= navigation_link( "Repeating todos", {:controller => "recurring_todos", :action => "index"}, :title => "Manage recurring actions" ) %></li>
</ul></li>
</ul>
</li>
<li><a href="#">View</a>
<ul>
<li><%= navigation_link( "Calendar", calendar_path, :title => "Calendar of due actions" ) %></li>
<li><%= navigation_link( "Done", done_path, {:accesskey=>"d", :title=>"Completed"} ) %></li>
<li><%= navigation_link( "Feeds", {:controller => "feedlist", :action => "index"}, :title => "See a list of available feeds" ) %></li>
<li><%= navigation_link( "Statistics", {:controller => "stats", :action => "index"}, :title => "See your statistics" ) %></li>
</ul>
<ul>
<li><%= navigation_link( "Calendar", calendar_path, :title => "Calendar of due actions" ) %></li>
<li><%= navigation_link( "Done", done_path, {:accesskey=>"d", :title=>"Completed"} ) %></li>
<li><%= navigation_link( "Feeds", {:controller => "feedlist", :action => "index"}, :title => "See a list of available feeds" ) %></li>
<li><%= navigation_link( "Statistics", {:controller => "stats", :action => "index"}, :title => "See your statistics" ) %></li>
</ul>
</li>
<li><a href="#">Admin</a>
<ul>
<li><%= navigation_link( "Preferences", preferences_path, {:accesskey => "u", :title => "Show my preferences"} ) %></li>
@ -69,19 +84,21 @@
<% if current_user.is_admin? -%>
<li><%= navigation_link("Manage users", users_path, {:accesskey => "a", :title => "Add or delete users"} ) %></li>
<% end -%>
</ul></li>
</ul>
</li>
<li><a href="#">?</a>
<ul>
<li><%= link_to 'Integrate Tracks', integrations_path %></li>
<li><%= link_to 'REST API Docs', url_for(:controller => 'integrations', :action => 'rest_api') %></li>
</ul></li>
</ul>
</li>
<li><%= navigation_link(image_tag("system-search.png", :size => "16X16", :border => 0), {:controller => "search", :action => "index"}, :title => "Search All Items" ) %></li>
</ul>
</div>
<%= render_flash %>
</div>
<div id="content">
<div id="content" class="<%= @controller.controller_name %>">
<% unless @controller_name == 'feed' or session['noexpiry'] == "on" -%>
<%= periodically_call_remote( :url => {:controller => "login", :action => "check_expiry"},
:frequency => (5*60)) %>
@ -93,36 +110,5 @@
</div>
<%= render :partial => "shared/footer" %>
<script type="text/javascript">
jQuery(document).ready(function() { /* main menu */
jQuery('ul.sf-menu').superfish({
delay: 250,
animation: {opacity:'show',height:'show'},
autoArrows: false,
dropShadows: false,
speed: 'fast'
});
jQuery('ul.sf-item-menu').superfish({ /* context menu */
delay: 100,
animation: {opacity:'show',height:'show'},
autoArrows: false,
dropShadows: false,
speed: 'fast',
onBeforeShow: function() { /* highlight todo */
$(this.parent().parent().parent()).addClass("sf-item-selected");
},
onHide: function() { /* remove hightlight from todo */
$(this.parent().parent().parent()).removeClass("sf-item-selected");
}
});
/* for toggle notes link in mininav */
jQuery("#toggle-notes-nav").click(function () { jQuery(".todo_notes").toggle(); });
/* show the notes of a todo */
TodoBehavior.enableToggleNotes();
Nifty("div#todo_new_action_container","normal");
if ($('flash').visible()) { new Effect.Fade("flash",{duration:5.0}); }
});
</script>
</body>
</html>

View file

@ -61,7 +61,7 @@
<script type="text/javascript">
function showPreferredAuth() {
var preferredAuth = new CookieManager().getCookie('preferred_auth');
var preferredAuth = $.cookie('preferred_auth');
var databaseEnabled = <%= show_database_form ? 'true' : 'false' %>;
var openidEnabled = <%= show_openid_form ? 'true' : 'false' %>;
if (preferredAuth && preferredAuth == 'openid' && openidEnabled) {
@ -74,5 +74,28 @@ function showPreferredAuth() {
Login.showOpenid();
}
}
Event.observe(window, 'load', showPreferredAuth);
</script>
$(document).ready(showPreferredAuth);
var Login = {
showOpenid: function() {
$('#database_auth_form').hide();
$('#openid_auth_form').show();
$('#alternate_auth_openid').hide();
$('#alternate_auth_database').show();
$('#openid_url').focus();
$('#openid_url').select();
$.cookie('preferred_auth', 'openid');
},
showDatabase: function(container) {
$('#openid_auth_form').hide();
$('#database_auth_form').show();
$('#alternate_auth_database').hide();
$('#alternate_auth_openid').show();
$('#user_login').focus();
$('#user_login').select();
$.cookie('preferred_auth', 'database');
}
}
</script>

View file

@ -12,13 +12,13 @@
:id => "delete_note_"+note.id.to_s),
{:update => dom_id(note),
:loading => visual_effect(:fade, dom_id(note, 'container')),
:complete => "Element.remove('#{dom_id(note, 'container')}');",
:complete => "$('#{dom_id(note, 'container')}').remove();",
:url => note_path(note),
:method => :delete,
:confirm => "Are you sure that you want to delete the note \'#{note.id.to_s}\'?" },
{ :class => 'delete_note' }) -%>&nbsp;
<%= link_to_function(image_tag( "blank.png", :title => "Edit item", :class=>"edit_item"),
"Element.toggle('#{dom_id(note)}'); Element.toggle('#{dom_id(note, 'edit')}'); Effect.Appear('#{dom_id(note, 'edit')}'); Form.focusFirstElement('#{dom_id(note, 'edit_form')}');" ) + " | " %>
"$('##{dom_id(note)}').toggle(); $('##{dom_id(note, 'edit')}').show(); $('##{dom_id(note, 'edit_form')} textarea').focus();" ) + " | " %>
<%= link_to("In: " + note.project.name, project_path(note.project), :class=>"footer_link" ) %>&nbsp;|&nbsp;
Created: <%= format_date(note.created_at) %>
<% if note.updated_at? -%>
@ -37,4 +37,4 @@
<% end -%>
</div>
</div>
<% note = nil -%>
<% note = nil -%>

View file

@ -1,6 +0,0 @@
<div class="page_name_auto_complete" id="default_context_list" style="display:none;z-index:9999"></div>
<script type="text/javascript">
defaultContextAutoCompleter = new Autocompleter.Local('project_default_context_name', 'default_context_list', <%= context_names_for_autocomplete %>, {choices:100,autoSelect:false});
Event.observe($('project_default_context_name'), "focus", defaultContextAutoCompleter.activate.bind(defaultContextAutoCompleter));
Event.observe($('project_default_context_name'), "click", defaultContextAutoCompleter.activate.bind(defaultContextAutoCompleter));
</script>

View file

@ -1,10 +1,5 @@
<div class="container">
<h2>
<% if collapsible -%>
<a href="#" class="container_toggle" id="toggle_p<%= project.id %>"><%= image_tag("collapse.png") %></a>
<% end -%>
<%= project.name -%>
</h2>
<h2 id="project_name"><% if collapsible %><a href="#" class="container_toggle" id="toggle_p<%= project.id %>"><%= image_tag("collapse.png") %></a><% end %><%= project.name -%></h2>
<div id="<%= dom_id(project, "container")%>">
<%= render :partial => "projects/project_settings", :locals => { :project => project, :collapsible => collapsible } %>
</div>
@ -17,11 +12,5 @@
<div class="message"><p>Currently there are no incomplete actions in this project</p></div>
</div>
<%= render :partial => "todos/todo", :collection => @not_done, :locals => { :parent_container_type => "project" } %>
<% if @not_done.empty?
# fix (hack) for #713
set_behavior_for_star_icon
set_behavior_for_toggle_checkbox
end
-%>
</div>
</div>

View file

@ -1,7 +1,8 @@
<%
project = project_form
%>
<% form_tag project_path(project), { :id => dom_id(project, 'edit_form'), :class => "inline-form "+dom_id(project, 'edit_form')+"-edit-project-form", :method => :put } do -%>
<% form_remote_tag(:url => project_path(project), :html => { :id => dom_id(project, 'edit_form'), :class => "inline-form "+dom_id(project, 'edit_form')+"-edit-project-form", :method => :put }) do -%>
<%= source_view_tag( @source_view ) -%>
@ -18,7 +19,6 @@ project = project_form
<label for="project[default_context_name]">Default Context</label><br/>
<%= text_field_tag("project[default_context_name]", project.default_context.name, {:tabindex=>1,:size=> 25}) %>
<%= render :partial => 'default_context_autocomplete' %>
<br/>
<label for="project[default_tags]">Default Tags</label><br/>
@ -32,7 +32,7 @@ project = project_form
<%=image_tag("accept.png", :alt => "") %>
Update
</button>
<a href="javascript:void(0);" id="<%= dom_id(project, 'cancel') %>" onclick="Element.toggle('<%= dom_id(project) %>');Element.toggle('<%= dom_id(project, 'edit') %>');" class="negative">
<a href="#" id="<%= dom_id(project, 'cancel') %>" class="negative">
<%=image_tag("cancel.png", :alt => "") %>
Cancel
</a>
@ -42,7 +42,3 @@ project = project_form
<% end -%>
<%= apply_behavior "."+dom_id(project, 'edit_form')+"-edit-project-form", make_remote_form(
:before => "$('"+dom_id(project, 'submit')+"').startWaiting();",
:condition => "!$('"+dom_id(project, 'submit')+"').isWaiting()",
:external => false) %>

View file

@ -19,20 +19,15 @@ suppress_edit_button ||= false
title="delete the project '<%= project.name %>'"><%= image_tag( "blank.png",
:title => "Delete project",
:class=>"delete_item") %></a>
<%= apply_behavior "a.delete_project_button:click", { :prevent_default => true, :external => true } do |page, element|
page.confirming "'Are you sure that you want to ' + this.title + '?'" do
element.up('.project').start_waiting
page << remote_to_href(:method => 'delete')
end
end -%>
<% unless suppress_edit_button -%>
<%= link_to_remote(
image_tag( "blank.png", :title => "Edit project", :class=>"edit_item"),
:url => {:controller => 'projects', :action => 'edit', :id => project.id},
:method => 'get',
:with => "'_source_view=#{@source_view}'",
:before => "$('#{dom_id(project)}').startWaiting();",
:complete => "$('#{dom_id(project)}').stopWaiting();"
:before => "$('#{dom_id(project)}').block({message:null});",
:complete => "$('#{dom_id(project)}').unblock();enable_rich_interaction();"
) %>
<% end -%>
@ -42,8 +37,3 @@ suppress_edit_button ||= false
<div id="<%= dom_id(project, 'edit') %>" class="edit-form" style="display:none;">
</div>
</div>
<% if controller.action_name == 'create' %>
<script>
new Effect.Appear('<%= dom_id(project) %>');
</script>
<% end %>

View file

@ -19,8 +19,8 @@
:url => {:controller => 'projects', :action => 'edit', :id => project.id},
:method => 'get',
:with => "'_source_view=#{@source_view}'",
:before => "$('#{dom_id(project)}').startWaiting();",
:complete => "$('#{dom_id(project)}').stopWaiting();"
:before => "$('#{dom_id(project)}').block({message: null});",
:complete => "$('#{dom_id(project)}').unblock();enable_rich_interaction();"
) %>
</div>
<% unless project.description.blank? -%>
@ -28,4 +28,4 @@
<% end -%>
</div>
<div id="<%= dom_id(project, 'edit') %>" class="edit-form" style="display:none;">
</div>
</div>

View file

@ -4,28 +4,13 @@
<div class="alpha_sort">
<%= link_to("Alphabetically", alphabetize_projects_path(:state => state),
:class => "alphabetize_link", :title => "Sort these projects alphabetically") %>
<% apply_behavior '.alphabetize_link:click', :prevent_default => true do |page, element|
page.confirming 'Are you sure that you want to sort these projects alphabetically? This will replace the existing sort order.' do
page << "alphaSort = this.up('.alpha_sort');
alphaSort.startWaiting();"
page << remote_to_href(:complete => "alphaSort.stopWaiting()")
end
end
%></div><span class="sort_separator">&nbsp;|&nbsp;</span><div class="tasks_sort">
</div><span class="sort_separator">&nbsp;|&nbsp;</span><div class="tasks_sort">
<%= link_to("By number of tasks", actionize_projects_path(:state => state),
:class => "actionize_link", :title => "Sort these projects by number of tasks") %>
<% apply_behavior '.actionize_link:click', :prevent_default => true do |page, element|
page.confirming 'Are you sure that you want to sort these projects by the number of tasks? This will replace the existing sort order.' do
page << "tasksSort = this.up('.tasks_sort');
tasksSort.startWaiting();"
page << remote_to_href(:complete => "tasksSort.stopWaiting()")
end
end
%></div>
</div>
</div>
<div id="list-<%= state %>-projects" class="project-list">
<%= render :partial => 'project_listing', :collection => project_state_group %>
</div>
<%= sortable_element "list-#{state}-projects", get_listing_sortable_options("list-#{state}-projects") %>
</div>

View file

@ -8,10 +8,10 @@ elsif @saved
page.replace_html "active-projects-count", @active_projects_count
page.insert_html :bottom, "list-active-projects", :partial => 'project_listing', :locals => { :project_listing => @project }
page.sortable "list-active-projects", get_listing_sortable_options('list-active-projects')
page.call "Form.reset", "project-form"
page.call "Form.focusFirstElement", "project-form"
page << "$('#project-form').clearForm();"
page << "$('#project-form input:text:first').focus();"
else
page.show 'status'
page.replace_html 'status', "#{error_messages_for('project')}"
end
page.hide "busy"
page.hide "busy"

View file

@ -1,4 +1,4 @@
page[dom_id(@project, 'edit')].replace_html :partial => 'project_form', :locals => { :project_form => @project }
page[@project].hide
page[dom_id(@project, 'edit')].show
page[dom_id(@project, 'edit_form')].down('input.project-name').focus
page[dom_id(@project, 'edit_form')].find('input.project-name').focus

View file

@ -14,20 +14,13 @@
<div id="toggle_project_new" class="hide_form">
<a title="Hide new project form" accesskey="n">&laquo; Hide form</a>
<% apply_behavior '#toggle_project_new a:click', :prevent_default => true do |page|
page << "TracksForm.toggle('toggle_project_new', 'project_new', 'project-form',
'&laquo; Hide form', 'Hide new project form',
'Create a new project &#187;', 'Add a project');"
end
%>
</div>
<div id="project_new" class="project_new" style="display:block">
<% form_remote_tag(:url => projects_path, :method => :post,
:html=> { :id=>'project-form', :name=>'project', :class => 'inline-form'},
:before => "$('project_new_project_submit').startWaiting()",
:complete => "$('project_new_project_submit').stopWaiting()",
:condition => "!$('project_new_project_submit').isWaiting()") do -%>
:before => "$('#project_new_project_submit').block({message:null})",
:complete => "$('#project_new_project_submit').unblock()") do -%>
<div id="status"><%= error_messages_for('project') %></div>
@ -40,7 +33,6 @@
<% unless @contexts.empty? -%>
<label for="default_context_name">Default Context (optional):</label><br />
<%= text_field_tag("project[default_context_name]", @project.default_context.name, :tabindex => 3) %>
<%= render :partial => 'default_context_autocomplete' %>
<br />
<% end -%>

View file

@ -4,14 +4,14 @@
</div>
<%= 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 -%>
<div class="container">
<div id="notes">
<div class="add_note_link"><%= link_to_function( "Add a note", "Element.toggle('new-note'); Form.focusFirstElement('form-new-note');", :id=>"add_note_href") %></div>
<div class="add_note_link"><%= link_to 'Add a note', '#' %> </div>
<h2>Notes</h2>
<div id="empty-n" style="display:<%= @project.notes.empty? ? 'block' : 'none'%>;">
<%= render :partial => "shared/empty",
@ -26,7 +26,7 @@
:method => :post,
:update => "notes",
:position => "bottom",
:complete => "new Effect.Highlight('notes');$('empty-n').hide();Form.reset('form-new-note');",
:complete => "$('#notes').effect('highlight', 1000);$('#empty-n').hide();$('#new-note form').clearForm();",
:html => {:id=>'form-new-note', :class => 'inline-form'} do %>
<%= hidden_field( "new_note", "project_id", "value" => "#{@project.id}" ) %>
<%= text_area( "new_note", "body", "cols" => 50, "rows" => 3, "tabindex" => 1 ) %>
@ -39,4 +39,4 @@
<div id="input_box">
<%= render :partial => "shared/add_new_item_form" %>
<%= render :file => "sidebar/sidebar.html.erb" %>
</div><!-- End of input box -->
</div><!-- End of input box -->

View file

@ -19,4 +19,14 @@ else
page[dom_id(@project, 'edit')].hide
page.replace_html dom_id(@project, 'container'), :partial => 'project_settings', :locals => { :project => @project }
page[dom_id(@project)].show
page['todo_context_name'].value = @project.default_context.name if @project.default_context
page['#todo_project_name'].value = @project.name
page['tag_list'].value = @project.default_tags if @project.default_tags
page << "$('input[name=default_context_name]').val('#{@project.default_context.name}');" if @project.default_context
page << "defaultContexts = #{default_contexts_for_autocomplete};"
page << "defaultTags = #{default_tags_for_autocomplete};"
end
page.replace_html "sidebar", :file => 'sidebar/sidebar.html.erb'

View file

@ -1,10 +1,8 @@
page['project_name_in_place_editor'].replace_html @project.name
page['default_project_name_id'].value = @project.name
page['todo_project_name'].value = @project.name
# renew project auto complete array
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}; projectAutoCompleter.changed = true"
page << "var projectNames = #{project_names_for_autocomplete};"
status_message = "Name of project was changed"
page.notify :notice, status_message, 5.0

View file

@ -3,9 +3,9 @@
<% form_remote_tag(
:url => recurring_todo_path(@recurring_todo), :method => :put,
:html=> { :id=>'recurring-todo-form-edit-action', :name=>'recurring_todo', :class => 'inline-form' },
:before => "$('recurring_todo_edit_action_submit').startWaiting()",
:complete => "$('recurring_todo_edit_action_submit').stopWaiting();",
:condition => "!$('recurring_todo_edit_action_submit').isWaiting()") do
:before => "$('#recurring_todo_edit_action_submit').block({message: null})",
:complete => "$('#recurring_todo_edit_action_submit').unblock();$('#recurring-todo-form-edit-action').clearForm();") do
-%>
<div id="edit_status"><%= error_messages_for("item", :object_name => 'action') %></div>
@ -20,59 +20,13 @@
<label for="edit_recurring_todo_project_name">Project</label>
<input id="edit_recurring_todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="<%= @recurring_todo.project.nil? ? 'None' : @recurring_todo.project.name.gsub(/"/,"&quot;") %>" />
<div class="page_name_auto_complete" id="edit_project_list" style="display:none"></div>
<script type="text/javascript">
projectAutoCompleter = new Autocompleter.Local('edit_recurring_todo_project_name', 'edit_project_list', <%= project_names_for_autocomplete %>, {choices:100,autoSelect:false});
function selectDefaultContext() {
todoContextNameElement = $('edit_recurring_todo_context_name');
defaultContextName = todoContextNameElement.projectDefaultContextsMap[this.value];
if (defaultContextName && !todoContextNameElement.editedByTracksUser) {
todoContextNameElement.value = defaultContextName;
}
}
function selectDefaultTags() {
todoTagListElement = $('edit_recurring_todo_tag_list');
defaultTagList = todoTagListElement.projectDefaultTagsMap[this.value];
if (defaultTagList && !todoTagListElement.editedByTracksUser) {
todoTagListElement.value = defaultTagList;
}
}
Event.observe($('edit_recurring_todo_project_name'), "focus", projectAutoCompleter.activate.bind(projectAutoCompleter));
Event.observe($('edit_recurring_todo_project_name'), "click", projectAutoCompleter.activate.bind(projectAutoCompleter));
Event.observe($('edit_recurring_todo_project_name'), "blur", selectDefaultContext.bind($('edit_recurring_todo_project_name')));
Event.observe($('edit_recurring_todo_project_name'), "blur", selectDefaultTags.bind($('edit_recurring_todo_project_name')));
</script>
<label for="edit_recurring_todo_context_name">Context</label>
<input id="edit_recurring_todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="<%= @recurring_todo.context.name %>" />
<div class="page_name_auto_complete" id="edit_context_list" style="display:none"></div>
<script type="text/javascript">
var contextAutoCompleter;
function initializeNamesForAutoComplete(contextNamesForAutoComplete) {
if (contextNamesForAutoComplete.length == 0 || contextNamesForAutoComplete[0].length == 0) {
return;
}
contextAutoCompleter = new Autocompleter.Local('edit_recurring_todo_context_name', 'edit_context_list', contextNamesForAutoComplete, {choices:100,autoSelect:false});
Event.observe($('edit_recurring_todo_context_name'), "focus", function(){ $('edit_recurring_todo_context_name').editedByTracksUser = true; });
Event.observe($('edit_recurring_todo_context_name'), "focus", contextAutoCompleter.activate.bind(contextAutoCompleter));
Event.observe($('edit_recurring_todo_context_name'), "click", contextAutoCompleter.activate.bind(contextAutoCompleter));
}
function updateContextNamesForAutoComplete(contextNamesForAutoComplete) {
if (contextAutoCompleter) { // i.e. if we're already initialized
contextAutoCompleter.options.array = contextNamesForAutoComplete
} else {
initializeNamesForAutoComplete(contextNamesForAutoComplete)
}
}
initializeNamesForAutoComplete(<%= context_names_for_autocomplete %>);
$('edit_recurring_todo_context_name').projectDefaultContextsMap = eval('(' + <%= @default_project_context_name_map %> + ')');
</script>
<label for="edit_recurring_todo_tag_list">Tags (separate with commas)</label>
<%= text_field_tag "edit_recurring_todo_tag_list", @recurring_todo.tag_list, :size => 30, :tabindex => 5 -%>
<script type="text/javascript">
$('edit_recurring_todo_tag_list').projectDefaultTagsMap = eval('(' + <%= @default_project_tags_map %> + ')');
Event.observe($('edit_recurring_todo_tag_list'), "focus", function(){ $('edit_recurring_todo_tag_list').editedByTracksUser = true; });
</script>
</div>
</div>
<div id="recurring_edit_period_id">
@ -87,13 +41,13 @@
<div id="recurring_timespan">
<br/>
<label for="recurring_todo[start_from]">Starts on </label><%=
text_field_tag("recurring_todo_edit_start_from", format_date(@recurring_todo.start_from), "size" => 12, "class" => "Date", "onfocus" => "Calendar.setup", "tabindex" => 6, "autocomplete" => "off") %><br/>
text_field_tag("recurring_todo_edit_start_from", format_date(@recurring_todo.start_from), "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %><br/>
<br/>
<label for="recurring_todo[ends_on]">Ends on:</label><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', @recurring_todo.ends_on == 'no_end_date')%> No end date<br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times', @recurring_todo.ends_on == 'ends_on_number_of_times')%> Ends after <%= text_field_tag("recurring_todo[number_of_occurences]", @recurring_todo.number_of_occurences, "size" => 3, "tabindex" => 7) %> times<br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date', @recurring_todo.ends_on == 'ends_on_end_date')%> Ends on <%=
text_field_tag('recurring_todo_edit_end_date', format_date(@recurring_todo.end_date), "size" => 12, "class" => "Date", "onfocus" => "Calendar.setup", "tabindex" => 8, "autocomplete" => "off") %><br/>
text_field_tag('recurring_todo_edit_end_date', format_date(@recurring_todo.end_date), "size" => 12, "class" => "Date", "tabindex" => 8, "autocomplete" => "off") %><br/>
</div></div>
<div id="recurring_edit_daily" style="display:<%= @recurring_todo.recurring_period == 'daily' ? 'block' : 'none' %> ">
<label>Settings for daily recurring actions</label><br/>
@ -149,13 +103,11 @@
<%=image_tag("accept.png", :alt => "") %>
Update
</button>
<button type="button" class="positive" id="recurring_todo_edit_action_cancel" tabindex="15" onclick="TracksForm.toggle_overlay();">
<button type="button" class="positive" id="recurring_todo_edit_action_cancel" tabindex="15">
<%=image_tag("cancel.png", :alt => "") %>
Cancel
</button>
</div>
</div>
<% end %>
<%= calendar_setup( "recurring_todo_edit_start_from" ) %>
<%= calendar_setup( "recurring_todo_edit_end_date" ) %>
</div>

View file

@ -2,9 +2,8 @@
<% form_remote_tag(
:url => recurring_todos_path, :method => :post,
:html=> { :id=>'recurring-todo-form-new-action', :name=>'recurring_todo', :class => 'inline-form' },
:before => "$('recurring_todo_new_action_submit').startWaiting()",
:complete => "$('recurring_todo_new_action_submit').stopWaiting();",
:condition => "!$('recurring_todo_new_action_submit').isWaiting()") do
:before => "$('#recurring_todo_new_action_submit').block({message: null})",
:complete => "$('#recurring_todo_new_action_submit').unblock();$('#recurring-todo-form-new-action').clearForm();") do
-%>
<div id="new_status"><%= error_messages_for("item", :object_name => 'action') %></div>
@ -17,59 +16,12 @@
<label for="recurring_todo_project_name">Project</label>
<input id="recurring_todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="" />
<div class="page_name_auto_complete" id="project_list" style="display:none"></div>
<script type="text/javascript">
projectAutoCompleter = new Autocompleter.Local('recurring_todo_project_name', 'project_list', <%= project_names_for_autocomplete %>, {choices:100,autoSelect:false});
function selectDefaultContext() {
todoContextNameElement = $('recurring_todo_context_name');
defaultContextName = todoContextNameElement.projectDefaultContextsMap[this.value];
if (defaultContextName && !todoContextNameElement.editedByTracksUser) {
todoContextNameElement.value = defaultContextName;
}
}
Event.observe($('recurring_todo_project_name'), "focus", projectAutoCompleter.activate.bind(projectAutoCompleter));
Event.observe($('recurring_todo_project_name'), "click", projectAutoCompleter.activate.bind(projectAutoCompleter));
Event.observe($('recurring_todo_project_name'), "blur", selectDefaultContext.bind($('recurring_todo_project_name')));
</script>
<label for="recurring_todo_context_name">Context</label>
<input id="recurring_todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="" />
<div class="page_name_auto_complete" id="context_list" style="display:none"></div>
<script type="text/javascript">
var contextAutoCompleter;
function initializeNamesForAutoComplete(contextNamesForAutoComplete) {
if (contextNamesForAutoComplete.length == 0 || contextNamesForAutoComplete[0].length == 0) {
return;
}
contextAutoCompleter = new Autocompleter.Local('recurring_todo_context_name', 'context_list', contextNamesForAutoComplete, {choices:100,autoSelect:false});
Event.observe($('recurring_todo_context_name'), "focus", function(){ $('recurring_todo_context_name').editedByTracksUser = true; });
Event.observe($('recurring_todo_context_name'), "focus", contextAutoCompleter.activate.bind(contextAutoCompleter));
Event.observe($('recurring_todo_context_name'), "click", contextAutoCompleter.activate.bind(contextAutoCompleter));
}
function updateContextNamesForAutoComplete(contextNamesForAutoComplete) {
if (contextAutoCompleter) { // i.e. if we're already initialized
contextAutoCompleter.options.array = contextNamesForAutoComplete
} else {
initializeNamesForAutoComplete(contextNamesForAutoComplete)
}
}
initializeNamesForAutoComplete(<%= context_names_for_autocomplete %>);
$('recurring_todo_context_name').projectDefaultContextsMap = eval('(' + <%= @default_project_context_name_map %> + ')');
</script>
<label for="tag_list">Tags (separate with commas)</label>
<%= text_field_tag "tag_list", nil, :size => 30, :tabindex => 5 -%>
<script type="text/javascript">
$('tag_list').projectDefaultTagsMap = eval('(' + <%= @default_project_tags_map %> + ')');
function selectDefaultTags() {
todoTagListElement = $('tag_list');
defaultTagList = todoTagListElement.projectDefaultTagsMap[this.value];
if (defaultTagList && !todoTagListElement.editedByTracksUser) {
todoTagListElement.value = defaultTagList;
}
}
Event.observe($('recurring_todo_project_name'), "blur", selectDefaultTags.bind($('recurring_todo_project_name')));
Event.observe($('tag_list'), "focus", function(){ $('tag_list').editedByTracksUser = true; });
</script>
</div>
</div>
<div id="recurring_period_id">
@ -79,18 +31,16 @@
<%= radio_button_tag('recurring_todo[recurring_period]', 'weekly')%> Weekly<br/>
<%= radio_button_tag('recurring_todo[recurring_period]', 'monthly')%> Monthly<br/>
<%= radio_button_tag('recurring_todo[recurring_period]', 'yearly')%> Yearly<br/>
<% apply_behaviour "#recurring_period:click",
"TracksForm.hide_all_recurring(); $('recurring_'+TracksForm.get_period()).show();" %>
</div>
<div id="recurring_timespan">
<br/>
<label for="recurring_todo[start_from]">Starts on </label><%=
text_field(:recurring_todo, :start_from, "value" => format_date(current_user.time), "size" => 12, "class" => "Date", "onfocus" => "Calendar.setup", "tabindex" => 6, "autocomplete" => "off") %><br/>
text_field(:recurring_todo, :start_from, "value" => format_date(current_user.time), "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %><br/>
<br/>
<label for="recurring_todo[ends_on]">Ends on:</label><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', true)%> No end date<br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times')%> Ends after <%= text_field( :recurring_todo, :number_of_occurences, "size" => 3, "tabindex" => 7) %> times<br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date')%> Ends on <%= text_field(:recurring_todo, :end_date, "size" => 12, "class" => "Date", "onfocus" => "Calendar.setup", "tabindex" => 8, "autocomplete" => "off", "value" => "") %><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date')%> Ends on <%= text_field(:recurring_todo, :end_date, "size" => 12, "class" => "Date", "tabindex" => 8, "autocomplete" => "off", "value" => "") %><br/>
</div></div>
<div id="recurring_daily" style="display:block">
<label>Settings for daily recurring actions</label><br/>
@ -147,13 +97,11 @@
<%=image_tag("accept.png", :alt => "") %>
Create
</button>
<button type="button" class="positive" id="recurring_todo_new_action_cancel" tabindex="15" onclick="Form.reset('recurring-todo-form-new-action');Form.focusFirstElement('recurring-todo-form-new-action');TracksForm.hide_all_recurring(); $('recurring_daily').show();TracksForm.toggle_overlay();">
<button type="button" class="positive" id="recurring_todo_new_action_cancel" tabindex="15">
<%=image_tag("cancel.png", :alt => "") %>
Cancel
</button>
</div>
</div>
<% end %>
<%= calendar_setup( "recurring_todo_start_from" ) %>
<%= calendar_setup( "recurring_todo_end_date" ) %>
</div>

View file

@ -4,9 +4,8 @@ page.replace_html 'new_status', "#{error_messages_for('recurring_todo')}"
page.notify :notice, @message, 5.0
if @recurring_saved
# reset form
page << "TracksForm.hide_all_recurring(); $('recurring_daily').show();"
page << "Form.reset('recurring-todo-form-new-action');"
page << "Form.focusFirstElement('recurring-todo-form-new-action');"
page << "TracksForm.hide_all_recurring(); $('#recurring_daily').show();"
page << "$('#recurring_todo_new_action_submit').unblock();$('#recurring-todo-form-new-action').clearForm();"
# hide overlayed edit form
page << "TracksForm.toggle_overlay();"
# insert new recurring todo

View file

@ -33,16 +33,4 @@
<div id="edit-recurring-todo" class="edit-form" style="display:none">
<div class='placeholder'>This should not be visible</div>
</div>
</div><%
# need to add behaviour for edit form here. Behaviour defined in partials are
# not generated for
apply_behaviour "#recurring_edit_period:click",
"TracksForm.hide_all_edit_recurring(); $('recurring_edit_'+TracksForm.get_edit_period()).show();"
-%>
<script type="text/javascript">
window.onload=function(){
Nifty("div#recurring_new_container","normal");
}
</script>
</div>

View file

@ -0,0 +1,3 @@
<% if @saved -%>
$('div#recurring_todo_<%= @recurring_todo.id %> a.star_item img').toggleClass('starred_todo').toggleClass('unstarred_todo');
<% end -%>

View file

@ -1,3 +0,0 @@
if @saved
page[@recurring_todo].down('a.star_item').down('img').toggleClassName('starred_todo').toggleClassName('unstarred_todo')
end

View file

@ -4,6 +4,3 @@
<%= submit_tag "Search" %>
<% end %>
</div>
<script type="text/javascript">
Form.focusFirstElement('search-form')
</script>

View file

@ -9,21 +9,15 @@
<div id="toggle_action_new" class="hide_form">
<a title="Hide new action form" accesskey="n" href="#">&laquo; Hide form</a>
<% apply_behavior '#toggle_action_new a:click', :prevent_default => true do |page|
page << "TracksForm.toggle('toggle_action_new', 'todo_new_action', 'todo-form-new-action',
'&laquo; Hide form', 'Hide next action form',
'Add a next action &#187;', 'Add a next action');"
end
-%>
</div>
<div id="todo_new_action" style="display:block">
<% form_remote_tag(
:url => todos_path, :method => :post,
:html=> { :id=>'todo-form-new-action', :name=>'todo', :class => 'inline-form' },
:before => "$('todo_new_action_submit').startWaiting()",
:complete => "$('todo_new_action_submit').stopWaiting()",
:condition => "!$('todo_new_action_submit').isWaiting() && askIfNewContextProvided()") do -%>
:before => "$('#todo_new_action_submit').block({message:null})",
:complete => "$('#todo_new_action_submit').unblock()",
:condition => "askIfNewContextProvided()") do -%>
<div id="status"><%= error_messages_for("item", :object_name => 'action') %></div>
@ -44,24 +38,23 @@
<div class="page_name_auto_complete" id="context_list" style="display:none"></div>
<label for="tag_list">Tags (separate with commas)</label>
<%= text_field_tag "tag_list", nil, :size => 30, :tabindex => 5 %>
<%= text_field_tag "tag_list", @default_tags, :size => 30, :tabindex => 5 %>
<%= content_tag("div", "", :id => "tag_list_auto_complete", :class => "auto_complete") %>
<%= auto_complete_field 'tag_list', {
:url => {:controller => 'todos', :action => 'auto_complete_for_tag'},
:tokens => [',']
} %>
<div class="due_input">
<label for="todo_due">Due</label>
<%= text_field("todo", "due", "size" => 12, "class" => "Date", "onfocus" => "Calendar.setup", "tabindex" => 6, "autocomplete" => "off") %>
<%= text_field("todo", "due", "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %>
</div>
<div class="show_from_input">
<label for="todo_show_from">Show from</label>
<%= text_field("todo", "show_from", "size" => 12, "class" => "Date", "onfocus" => "Calendar.setup", "tabindex" => 7, "autocomplete" => "off") %>
<%= text_field("todo", "show_from", "size" => 12, "class" => "Date", "tabindex" => 7, "autocomplete" => "off") %>
</div>
<label for="predecessor_list">Depends on</label>
<%= 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 %>
<div class="submit_box">
<div class="widgets">
@ -71,67 +64,5 @@
</div>
</div>
<% end -%>
<script type="text/javascript" charset="utf-8">
var contextNames = <%= context_names_for_autocomplete %>;
var projectNames = <%= project_names_for_autocomplete %>;
function askIfNewContextProvided() {
var givenContextName = $('todo_context_name').value;
if (givenContextName.length == 0) return true; // do nothing and depend on rails validation error
for (var i = 0; i < contextNames.length; ++i) {
if (contextNames[i] == givenContextName) return true;
}
return confirm('New context "' + givenContextName + '" will be also created. Are you sure?');
}
var projectAutoCompleter = new Autocompleter.Local('todo_project_name', 'project_list', projectNames, {choices:100,autoSelect:false});
function selectDefaultContext() {
todoContextNameElement = $('todo_context_name');
defaultContextName = todoContextNameElement.projectDefaultContextsMap[this.value];
if (defaultContextName && !todoContextNameElement.editedByTracksUser) {
todoContextNameElement.value = defaultContextName;
}
}
function selectDefaultTags() {
todoTagListElement = $('tag_list');
defaultTags = todoTagListElement.projectDefaultTagsMap[this.value];
if (defaultTags && !todoTagListElement.editedByTracksUser) {
todoTagListElement.value = defaultTags;
}
}
Event.observe($('todo_project_name'), "focus", projectAutoCompleter.activate.bind(projectAutoCompleter));
Event.observe($('todo_project_name'), "click", projectAutoCompleter.activate.bind(projectAutoCompleter));
Event.observe($('todo_project_name'), "blur", selectDefaultContext.bind($('todo_project_name')));
Event.observe($('todo_project_name'), "blur", selectDefaultTags.bind($('todo_project_name')));
var contextAutoCompleter;
function initializeNamesForAutoComplete(contextNamesForAutoComplete) {
if (contextNamesForAutoComplete.length == 0 || contextNamesForAutoComplete[0].length == 0) {
return;
}
contextAutoCompleter = new Autocompleter.Local('todo_context_name', 'context_list', contextNamesForAutoComplete, {choices:100,autoSelect:false});
Event.observe($('todo_context_name'), "focus", function(){ $('todo_context_name').editedByTracksUser = true; });
Event.observe($('tag_list'), "focus", function(){ $('tag_list').editedByTracksUser = true; });
Event.observe($('todo_context_name'), "focus", contextAutoCompleter.activate.bind(contextAutoCompleter));
Event.observe($('todo_context_name'), "click", contextAutoCompleter.activate.bind(contextAutoCompleter));
}
function updateContextNamesForAutoComplete(contextNamesForAutoComplete) {
if (contextAutoCompleter) { // i.e. if we're already initialized
contextAutoCompleter.options.array = contextNamesForAutoComplete
} else {
initializeNamesForAutoComplete(contextNamesForAutoComplete)
}
}
initializeNamesForAutoComplete(contextNames);
$('todo_context_name').projectDefaultContextsMap = eval('(' + <%= @default_project_context_name_map %> + ')');
$('tag_list').projectDefaultTagsMap = eval('(' + <%= @default_project_tags_map %> + ')');
</script>
<%= calendar_setup( "todo_due" ) %>
<%= calendar_setup( "todo_show_from" ) %>
</div>
</div>
</div>

View file

@ -1,3 +1,3 @@
<div id="footer">
<p>Send feedback on <%= TRACKS_VERSION %>: <a href="http://www.assembla.com/spaces/tracks-tickets/tickets">Bugs</a> | <a href="http://www.getontracks.org/forums/">Forum</a> | <a href="http://www.getontracks.org/wiki/">Wiki</a> | <a href="mailto:butshesagirl@rousette.org.uk?subject=Tracks feedback">Email</a> | <a href="http://www.getontracks.org/">Website</a> | <a href="http://www.getontracks.org/development/">Contribute</a></p>
<p>Send feedback on <%= TRACKS_VERSION %>: <a href="http://www.assembla.com/spaces/tracks-tickets/tickets">Bugs</a> | <a href="http://www.getontracks.org/forums/">Forum</a> | <a href="http://www.getontracks.org/wiki/">Wiki</a> | <a href="mailto:butshesagirl@rousette.org.uk?subject=Tracks feedback">Email</a> | <a href="http://www.getontracks.org/">Website</a> | <a href="http://getontracks.org/tracks/contribute">Contribute</a></p>
</div>

View file

@ -1 +1 @@
<div class="footer"><p>Mobile Tracks <%= TRACKS_VERSION %>: <a href="mailto:butshesagirl@rousette.org.uk?subject=Tracks feedback">Email</a> | <a href="http://www.rousette.org.uk/projects/">Website</a> | <a href="http://www.rousette.org.uk/projects/tracks/contribute">Contribute</a></p></div>
<div class="footer"><p>Mobile Tracks <%= TRACKS_VERSION %>: <a href="mailto:butshesagirl@rousette.org.uk?subject=Tracks feedback">Email</a> | <a href="http://www.getontracks.org/">Website</a> | <a href="http://getontracks.org/tracks/contribute">Contribute</a></p></div>

View file

@ -3,15 +3,16 @@
<% if collapsible %>
<a href="#" class="container_toggle" id="toggle_deferred"><%= image_tag("collapse.png") %></a>
<% end %>
Deferred actions <%= append_descriptor ? append_descriptor : '' %>
Deferred/pending actions <%= append_descriptor ? append_descriptor : '' %>
</h2>
<div id="tickleritems" class="items toggle_target">
<div id="tickler-empty-nd" style="display:<%= deferred.empty? ? 'block' : 'none'%>;">
<div class="message"><p>Currently there are no deferred actions</p></div>
<div id="tickler-empty-nd" style="display:<%= deferred.empty? && pending.empty? ? 'block' : 'none'%>;">
<div class="message"><p>Currently there are no deferred or pending actions</p></div>
</div>
<%= 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 } %>
</div><!-- [end:items] -->
</div><!-- [end:tickler] -->
</div><!-- [end:tickler] -->

View file

@ -14,42 +14,12 @@
<label for="<%= dom_id(@todo, 'project_name') %>">Project</label>
<input id="<%= dom_id(@todo, 'project_name') %>" name="project_name" autocomplete="off" tabindex="10" size="30" type="text" value="<%= @todo.project.nil? ? 'None' : @todo.project.name.gsub(/"/,"&quot;") %>" />
<div class="page_name_auto_complete" id="<%= dom_id(@todo, 'project_list') %>" style="display:none"></div>
<script type="text/javascript">
<%= dom_id(@todo, 'project_autocompleter') %> = new Autocompleter.Local('<%= dom_id(@todo, 'project_name') %>', '<%= dom_id(@todo, 'project_list') %>', projectNames, {choices:100,autoSelect:false});
function selectDefaultContext() {
todoContextNameElement = $('<%= dom_id(@todo, 'context_name') %>');
defaultContextName = $('todo_context_name').projectDefaultContextsMap[this.value];
if (defaultContextName && !todoContextNameElement.editedByTracksUser) {
todoContextNameElement.value = defaultContextName;
}
}
function selectDefaultTags() {
todoTagListElement = $('<%= dom_id(@todo, 'tag_list') %>');
defaultTagList = $('tag_list').projectDefaultTagsMap[this.value];
if (defaultTagList && !todoTagListElement.editedByTracksUser) {
todoTagListElement.value = defaultTagList;
}
}
Event.observe($('<%= dom_id(@todo, 'project_name') %>'), "focus", <%= dom_id(@todo, 'project_autocompleter') %>.activate.bind(<%= dom_id(@todo, 'project_autocompleter') %>));
Event.observe($('<%= dom_id(@todo, 'project_name') %>'), "click", <%= dom_id(@todo, 'project_autocompleter') %>.activate.bind(<%= dom_id(@todo, 'project_autocompleter') %>));
Event.observe($('<%= dom_id(@todo, 'project_name') %>'), "blur", selectDefaultContext.bind($('<%= dom_id(@todo, 'project_name') %>')));
Event.observe($('<%= dom_id(@todo, 'project_name') %>'), "blur", selectDefaultTags.bind($('<%= dom_id(@todo, 'project_name') %>')));
</script>
</div>
<div class="context_input">
<label for="<%= dom_id(@todo, 'context_name') %>">Context</label>
<input id="<%= dom_id(@todo, 'context_name') %>" name="context_name" autocomplete="off" tabindex="11" size="30" type="text" value="<%= @todo.context.name %>" />
<div class="page_name_auto_complete" id="<%= dom_id(@todo, 'context_list') %>" style="display:none"></div>
<script type="text/javascript">
<%= dom_id(@todo, 'context_autocompleter') %> = new Autocompleter.Local('<%= dom_id(@todo, 'context_name') %>', '<%= dom_id(@todo, 'context_list') %>', contextNames, {choices:100,autoSelect:false});
Event.observe($('<%= dom_id(@todo, 'context_name') %>'), "focus", function(){ $('<%= dom_id(@todo, 'context_name') %>').editedByTracksUser = true; });
Event.observe($('<%= dom_id(@todo, 'tag_list') %>'), "focus", function(){ $('<%= dom_id(@todo, 'tag_list') %>').editedByTracksUser = true; });
Event.observe($('<%= dom_id(@todo, 'context_name') %>'), "focus", <%= dom_id(@todo, 'context_autocompleter') %>.activate.bind(<%= dom_id(@todo, 'context_autocompleter') %>));
Event.observe($('<%= dom_id(@todo, 'context_name') %>'), "click", <%= dom_id(@todo, 'context_autocompleter') %>.activate.bind(<%= dom_id(@todo, 'context_autocompleter') %>));
</script>
</div>
<label class="tag_list_label" for="<%= dom_id(@todo, 'tag_list') %>">Tags (separate with commas)</label>
@ -76,30 +46,27 @@
</a>
</div>
<label class="predecessor_list_label" for="<%= dom_id(@todo, 'predecessor_list') %>">Depends on (separate with commas)</label>
<%= 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? -%>
<input type="hidden" name="on_project_page" value="true" />
<% end -%>
<div class="submit_box">
<div class="widgets">
<button type="submit" class="positive" id="<%= dom_id(@todo, 'submit') %>" tabindex="15">
<button type="submit" class="positive" id="<%= dom_id(@todo, 'submit') %>" tabindex="16">
<%=image_tag("accept.png", :alt => "") %>
Update
</button>
<a href="javascript:void(0);" onclick="Element.toggle('<%= dom_id(@todo, 'line') %>');Element.toggle('<%= dom_id(@todo, 'edit') %>');" class="negative">
<a href="#" class="negative">
<%=image_tag("cancel.png", :alt => "") %>
Cancel
</a>
</div>
</div>
<%= calendar_setup( dom_id(@todo, 'due') ) %>
<%= calendar_setup( dom_id(@todo, 'show_from') ) %>
<script>
jQuery(function(){
jQuery(".date_clear").click(function() {
/* add behavior to clear the date both buttons for show_from and due */
jQuery(this).prev().val('');
})})
</script>

View file

@ -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
%>
<div id="<%= dom_id(successor, 'successor') %>" class="item-container">
<div id="<%= dom_id(successor, 'successor_line') %>">
<div class="description<%= staleness_class( successor ) %>" style="margin-left: 20px">
<span class="todo.descr"><%= h sanitize(successor.description) %></span>
<%= 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? %>
</div>
</div>
</div>

View file

@ -8,27 +8,35 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
@z_index_counter = @z_index_counter - 1 # for IE z-index bug
%>
<div id="<%= dom_id(todo) %>" class="item-container">
<div id="<%= dom_id(todo, 'line') %>">
<div id="<%= dom_id(todo, 'line') %>" class="item-show">
<%= remote_star_icon %>
<%= remote_toggle_checkbox unless source_view_is :deferred %>
<ul class="sf-menu sf-item-menu"><li style="z-index:<%=@z_index_counter%>"><%= image_tag "downarrow.png", :alt=> "" %><ul id="ul<%= dom_id(todo) %>">
<% unless suppress_edit_button %>
<li><%= remote_edit_menu_item(parameters, todo) %></li>
<% end %>
<li><%= remote_delete_menu_item(parameters, todo) %></li>
<% unless todo.completed? || todo.deferred? %>
<li><%= remote_defer_menu_item(1, todo) %></li>
<li><%= remote_defer_menu_item(7, todo) %></li>
<% end %>
</ul></ul>
<ul class="sf-menu sf-item-menu">
<li style="z-index:<%=@z_index_counter%>"><%= image_tag "downarrow.png", :alt=> "" %>
<ul id="ul<%= dom_id(todo) %>">
<% unless suppress_edit_button %>
<li><%= remote_edit_menu_item(parameters, todo) %></li>
<% end %>
<li><%= remote_delete_menu_item(parameters, todo) %></li>
<% unless todo.completed? || todo.deferred? %>
<li><%= remote_defer_menu_item(1, todo) %></li>
<li><%= remote_defer_menu_item(7, todo) %></li>
<% end %>
</ul>
</li>
</ul>
<div class="description<%= staleness_class( todo ) %>">
<%= grip_span %>
<%= date_span -%>
<span class="todo.descr"><%= sanitize(todo.description) %></span>
<span class="todo.descr"><%= h sanitize(todo.description) %></span>
<% #= 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? %>
</div>
</div>
<div id="<%= dom_id(todo, 'edit') %>" class="edit-form" style="display:none">
@ -37,9 +45,5 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
<div class="placeholder"> </div>
<% end -%>
</div>
<<<<<<< HEAD:app/views/todos/_todo.html.erb
</div>
=======
</div>
<%= apply_behaviour ".date_clear:click","var selector_x = this.getAttribute('id').replace('_x', ''); $(selector_x).value='';" %>
>>>>>>> Don't run double sanitation on a string.:app/views/todos/_todo.html.erb

View file

@ -0,0 +1,15 @@
<%
suppress_button ||= false
%>
<%= link_to(image_tag( 'blank.png', :width=>'16', :height=>'16', :border=>'0' ), "#", {:class => 'show_successors', :title => 'Show successors'}) unless suppress_button %>
<div class="todo_successors" id="<%= dom_id(item, 'successors') %>" style=<%= suppress_button ? "display:display" : "display:none" %> >
<%= render :partial => "todos/successor",
:collection => item.pending_successors,
:locals => { :todo => item,
:parent_container_type => parent_container_type,
:suppress_dependencies => true,
:predecessor => item }
%>
</div>

View file

@ -0,0 +1,27 @@
if @saved
# show update message
status_message = "Added #{@predecessor.description} as dependency."
unless @original_state == 'pending'
status_message += " #{@todo.description} set to pending"
end
# remove successor from page
page[@todo].remove
# regenerate predecessor to add arrow
page[@predecessor].replace_html :partial => 'todos/todo', :locals => { :todo => @predecessor, :parent_container_type => parent_container_type }
# show in tickler box in project view
if source_view_is_one_of :project, :tag
page['tickler-empty-nd'].hide
page.replace "tickler", :partial => 'todos/deferred', :locals => { :deferred => @todo.project.deferred_todos,
:collapsible => false,
:append_descriptor => "in this project",
:parent_container_type => 'project',
:pending => @todo.project.pending_todos }
end
page << 'enable_rich_interaction();'
page.notify :notice, status_message, 5.0
else
page.replace_html "status", content_tag("div", content_tag("h2", "Unable to add dependency"), "id" => "errorExplanation", "class" => "errorExplanation")
end

View file

@ -59,17 +59,6 @@
var projectNames = <%= project_names_for_autocomplete %>;
$('todo_context_name').projectDefaultContextsMap = eval('(' + <%= @default_project_context_name_map %> + ')');
</script>
<!--
<input class="hide_tickler" id="hide_tickler" type="checkbox" tabindex="5" name="hide_tickler" checked="true"/>
<label for="hide_tickler"> Show actions in tickler</label>
<br/><br/>
-->
<p><%= link_to('<span class="feed">iCal</span>', {:format => 'ics', :token => current_user.token}, :title => "iCal feed" ) %>
- Get this calendar in iCal format</p>
</div>
<%
apply_behavior 'input.hide_tickler:click', :prevent_default => true do |page|
page << "alert('hiding action in tickler from calendar is not yet implemented');"
end
%>

View file

@ -11,6 +11,7 @@ X-WR-CALNAME:Tracks
due_date = Time.zone.now
overdue_text = "Overdue: "
end
modified = todo.updated_at || todo.created_at
%>BEGIN:VEVENT
DTSTART;VALUE=DATE:<%= due_date.strftime("%Y%m%d") %>
DTEND;VALUE=DATE:<%= (due_date+1.day).strftime("%Y%m%d") %>
@ -20,7 +21,7 @@ CLASS:PUBLIC
CATEGORIES:Tracks
CREATED:<%= todo.created_at.strftime("%Y%m%dT%H%M%SZ") %>
DESCRIPTION:<%= format_ical_notes(todo.notes) %>
LAST-MODIFIED:<%= todo.updated_at.strftime("%Y%m%dT%H%M%SZ") %>
LAST-MODIFIED:<%= modified.strftime("%Y%m%dT%H%M%SZ") %>
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED

View file

@ -2,15 +2,17 @@ if @saved
page.hide 'status'
status_message = 'Added new next action'
status_message += ' to tickler' if @todo.deferred?
status_message += ' in pending state' if @todo.pending?
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
page.send :record, "Form.reset('todo-form-new-action');Form.focusFirstElement('todo-form-new-action')"
page.send :record, "$('#todo-form-new-action').clearForm();$('#todo-form-new-action input:text:first').focus();"
page['todo_context_name'].value = @initial_context_name
page['todo_project_name'].value = @initial_project_name
page << "updateContextNamesForAutoComplete(#{context_names_for_autocomplete})" if @new_context_created
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}" if @new_project_created
page['tag_list'].value = @default_tags
#page << "updateContextNamesForAutoComplete(#{context_names_for_autocomplete})" if @new_context_created
#page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}" if @new_project_created
if should_show_new_item()
if @new_context_created
page.insert_html :top, 'display_box', :partial => 'contexts/context', :locals => { :context => @todo.context, :collapsible => true }
@ -20,10 +22,19 @@ if @saved
page.visual_effect :highlight, dom_id(@todo), :duration => 3
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
end
# make sure the behavior of the new/updated todo is enabled
page << "TodoBehavior.enableToggleNotes()"
page['tickler-empty-nd'].hide if source_view_is :deferred
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
# Update predecessors (if they exist and are visible)
@todo.uncompleted_predecessors.each do |p|
page << "if ($(\'#{item_container_id(p)}\')) {"
page[p].replace_html :partial => 'todos/todo',
:locals => { :todo => p, :parent_container_type => parent_container_type }
page << "}"
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')}"

View file

@ -4,23 +4,33 @@ if @saved
page['badge_count'].replace_html @down_count
# remove context if empty
page.visual_effect :fade, item_container_id(@todo), :duration => 0.4 if source_view_is_one_of(:todo, :deferred) && @remaining_in_context == 0
page.visual_effect(:fade, "c#{@todo.context_id}", :duration => 0.4) if (@remaining_in_context == 0)
# show message if there are no actions
page[empty_container_msg_div_id].show if !empty_container_msg_div_id.nil? && @down_count == 0
page['tickler-empty-nd'].show if source_view_is(:deferred) && @down_count == 0
# show new todo if the completed todo was recurring
unless @new_recurring_todo.nil? || @new_recurring_todo.deferred?
page.call "todoItems.ensureVisibleWithEffectAppear", item_container_id(@new_recurring_todo)
page.insert_html :bottom, item_container_id(@new_recurring_todo), :partial => 'todos/todo', :locals => { :todo => @new_recurring_todo, :parent_container_type => parent_container_type }
page.visual_effect :highlight, dom_id(@new_recurring_todo, 'line'), {'startcolor' => "'#99ff99'"}
page.notify :notice, "Action was deleted. Because this action is recurring, a new action was added", 6.0
else
if @todo.recurring_todo.todos.active.count == 0
page.notify :notice, "There is no next action after the recurring action you just deleted. The recurrence is completed", 6.0 unless @recurring_todo.nil?
if @todo.from_recurring_todo?
unless @new_recurring_todo.nil? || @new_recurring_todo.deferred?
page.call "todoItems.ensureVisibleWithEffectAppear", item_container_id(@new_recurring_todo)
page.insert_html :bottom, item_container_id(@new_recurring_todo), :partial => 'todos/todo', :locals => { :todo => @new_recurring_todo, :parent_container_type => parent_container_type }
page.visual_effect :highlight, dom_id(@new_recurring_todo, 'line'), {'startcolor' => "'#99ff99'"}
page.notify :notice, "Action was deleted. Because this action is recurring, a new action was added", 6.0
else
if @todo.recurring_todo.todos.active.count == 0
page.notify :notice, "There is no next action after the recurring action you just deleted. The recurrence is completed", 6.0 if @new_recurring_todo.nil?
end
end
end
# Activate pending todos that are successors of the deleted
@pending_to_activate.each do |t|
logger.debug "#300: Removing #{t.description} from pending block and adding it to active"
page[t].remove if source_view_is(:project) or source_view_is(:tag)
page.insert_html :bottom, item_container_id(t), :partial => 'todos/todo', :locals => { :todo => t, :parent_container_type => parent_container_type }
page.visual_effect :highlight, dom_id(t, 'line'), {'startcolor' => "'#99ff99'", :duration => 2}
end
else
page.notify :error, "There was an error deleting the item #{@todo.description}", 8.0
end
end

View file

@ -1,4 +1,4 @@
page[dom_id(@todo, 'form')].down('.placeholder').replace_html :partial => 'todos/edit_form'
page[dom_id(@todo, 'line')].hide
page[dom_id(@todo, 'form')].find('.placeholder').show().replace_html :partial => 'todos/edit_form'
page[dom_id(@todo, 'edit')].show
page[dom_id(@todo, 'form')].down('input#todo_description').focus
page[dom_id(@todo, 'line')].hide
page[dom_id(@todo, 'form')].find('input#todo_description').show().focus

View file

@ -0,0 +1,25 @@
if @removed
status_message = "Removed #{@successor.description} as dependency from #{@predecessor.description}."
page.notify :notice, status_message, 5.0
# replace old predecessor with one without the successor
page.replace dom_id(@predecessor), :partial => 'todos/todo', :locals => {
:todo => @predecessor, :parent_container_type => parent_container_type }
# update display if pending->active
if @successor.active?
page[@successor].remove unless source_view_is_one_of(:todo, :context)
page.insert_html :bottom, item_container_id(@successor), :partial => 'todos/todo', :locals => {
:todo => @successor, :parent_container_type => parent_container_type }
page.visual_effect :highlight, dom_id(@successor, 'line'), {'startcolor' => "'#99ff99'"}
end
# update display if pending->deferred
if @successor.deferred?
page.replace dom_id(@successor), :partial => 'todos/todo', :locals => {
:todo => @successor, :parent_container_type => parent_container_type }
end
page << "enable_rich_interaction();"
else
page.notify :error, "There was an error removing the dependency", 8.0
end

View file

@ -8,7 +8,13 @@
:locals => { :collapsible => true } %>
<% unless @deferred.nil? -%>
<%= render :partial => "todos/deferred", :locals => { :deferred => @deferred, :collapsible => true, :append_descriptor => "tagged with &lsquo;#{@tag_name}&rsquo;", :parent_container_type => 'tag' } %>
<%= render :partial => "todos/deferred", :locals => {
:deferred => @deferred,
:pending => @pending,
:collapsible => true,
:append_descriptor => "tagged with &lsquo;#{@tag_name}&rsquo;",
:parent_container_type => 'tag'
} %>
<% end -%>
<% unless @hidden_todos.nil? -%>

View file

@ -8,13 +8,20 @@ if @saved
page.insert_html :top, "completed_containeritems", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => "completed" }
page.visual_effect :highlight, dom_id(@todo, 'line'), {'startcolor' => "'#99ff99'"}
page[empty_container_msg_div_id].show if @down_count == 0 && !empty_container_msg_div_id.nil?
page.show 'tickler-empty-nd' if source_view_is(:project) && @deferred_count == 0
page.show 'tickler-empty-nd' if source_view_is(:project) && @deferred_count == 0 && @pending_count == 0
page.hide 'empty-d' # If we've checked something as done, completed items can't be empty
end
# Activate pending todos that are successors of the completed
@pending_to_activate.each do |t|
logger.debug "#300: Removing #{t.description} from pending block and adding it to active"
page[t].remove if source_view_is(:project) or source_view_is(:tag)
page.insert_html :bottom, item_container_id(t), :partial => 'todos/todo', :locals => { :todo => t, :parent_container_type => parent_container_type }
page.visual_effect :highlight, dom_id(t, 'line'), {'startcolor' => "'#99ff99'"}
end
# remove container if empty
if @remaining_in_context == 0 && source_view_is(:todo)
page.visual_effect :fade, item_container_id(@todo), :duration => 0.4
page.visual_effect :fade, "c"+@todo.context.id.to_s, :duration => 0.4
end
if @original_item_was_deferred && source_view_is(:tag)
@ -42,7 +49,17 @@ if @saved
page.insert_html :bottom, item_container_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
page.visual_effect :highlight, dom_id(@todo, 'line'), {'startcolor' => "'#99ff99'"}
page.show "empty-d" if @completed_count == 0
page.show "c"+@todo.context.id.to_s
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? # If we've checked something as undone, incomplete items can't be empty
# If active todos are successors of the reactivated todo they will be blocked
@active_to_block.each do |t|
logger.debug "#300: Block #{t.description} that are a successor of #{@todo.description}"
page[t].remove # Remove it from active
if source_view_is(:project) or source_view_is(:tag) # Insert it in deferred/pending block if existing
logger.debug "Insert #{t.description} in #{item_container_id(t)} block"
page.insert_html :bottom, item_container_id(t), :partial => 'todos/todo', :locals => { :todo => t, :parent_container_type => parent_container_type }
end
end
end
page.hide "status"
@ -53,5 +70,3 @@ if @saved
else
page.replace_html "status", content_tag("div", content_tag("h2", "#{pluralize(@todo.errors.count, "error")} prohibited this action from being saved") + content_tag("p", "There were problems with the following fields:") + content_tag("ul", @todo.errors.each_full { |msg| content_tag("li", msg) }), "id" => "errorExplanation", "class" => "errorExplanation")
end
# make sure the behavior of the new/updated todo is enabled
page << "TodoBehavior.enableToggleNotes()"

View file

@ -0,0 +1,3 @@
<% if @saved -%>
$('div#line_todo_<%= @todo.id %> a.star_item img').toggleClass('starred_todo').toggleClass('unstarred_todo');
<% end -%>

View file

@ -1,3 +0,0 @@
if @saved
page[@todo].down('a.star_item').down('img').toggleClassName('starred_todo').toggleClassName('unstarred_todo')
end

View file

@ -6,15 +6,8 @@ if @saved
status_message = 'Added new context / ' + status_message if @new_context_created
page.notify :notice, status_message, 5.0
# update auto completer arrays for edit form in right column, only for pages
# with that form
unless source_view_is_one_of(:calendar)
page << "contextAutoCompleter.options.array = #{context_names_for_autocomplete}; contextAutoCompleter.changed = true" if @new_context_created
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}; projectAutoCompleter.changed = true" if @new_project_created
end
if source_view_is_one_of(:todo, :context, :tag)
if @context_changed || @todo.deferred?
if @context_changed || @todo.deferred? || @todo.pending?
page[@todo].remove
if (@remaining_in_context == 0)
@ -27,8 +20,8 @@ if @saved
end
else
source_view do |from|
from.todo { page.visual_effect :fade, item_container_id(@todo), :duration => 0.4 }
from.tag { page.visual_effect :fade, item_container_id(@todo), :duration => 0.4 }
from.todo { page.visual_effect :fade, "c#{@todo.context.id}", :duration => 0.4 }
from.tag { page.visual_effect :fade, "c#{@todo.context.id}", :duration => 0.4 }
from.context { page.show "c#{@original_item_context_id}empty-nd" }
end
end
@ -89,11 +82,19 @@ if @saved
elsif @todo_was_activated_from_deferred_state
page[@todo].remove
page['tickler-empty-nd'].show if (@deferred_count == 0)
page.insert_html :bottom, "p#{@todo.project_id}", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
page.insert_html :bottom, "p#{@todo.project_id}items", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
page.visual_effect :highlight, dom_id(@todo), :duration => 3
page["p#{@todo.project_id}empty-nd"].hide
page.replace_html "badge_count", @down_count
else
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
page.replace_html "p#{@todo.project_id}items", :partial => 'todos/todo', :collection => @todo.project.not_done_todos,
:locals => { :parent_container_type => parent_container_type }
page.replace "tickler", :partial => 'todos/deferred', :locals => { :deferred => @todo.project.deferred_todos,
:collapsible => false,
:append_descriptor => "in this project",
:parent_container_type => 'project',
:pending => @todo.project.pending_todos }
page['tickler-empty-nd'].show if (@deferred_count == 0 and @pending_count == 0)
page.visual_effect :highlight, dom_id(@todo), :duration => 3
end
elsif source_view_is :deferred
@ -141,9 +142,14 @@ if @saved
else
logger.error "unexpected source_view '#{params[:_source_view]}'"
end
# make sure the behavior of the new/updated todo is enabled
page << "TodoBehavior.enableToggleNotes()"
# Update predecessors (if they exist and are visible)
@todo.uncompleted_predecessors.each do |p|
page << "if ($(\'#{item_container_id(p)}\')) {"
page[p].replace_html :partial => 'todos/todo',
:locals => { :todo => p, :parent_container_type => parent_container_type }
page << "}"
end
else
page.show 'error_status'
page.replace_html 'error_status', "#{error_messages_for('todo')}"
end
end

View file

@ -2,34 +2,38 @@
<p>You have a total of <span id="user_count"><%= @total_users %></span> users</p>
<table class="users_table">
<table class="users_table">
<tr>
<th>Login</th>
<th>Full name</th>
<th>Authorization type</th>
<th>Open ID URL</th>
<th>Total actions</th>
<th>Total contexts</th>
<th>Total projects</th>
<th>Total notes</th>
<th>&nbsp;</th>
<th>Login</th>
<th>Full name</th>
<th>Authorization type</th>
<th>Open ID URL</th>
<th>Total actions</th>
<th>Total contexts</th>
<th>Total projects</th>
<th>Total notes</th>
<th>&nbsp;</th>
</tr>
<% for user in @users %>
<tr <%= "class=\"highlight\"" if user.is_admin? %> id="user-<%= user.id %>">
<td><%=h user.login %></td>
<td><%=h user.last_name? ? user.display_name : '-' %></td>
<td><%= h user.auth_type %></td>
<td><%= h user.open_id_url || '-' %></td>
<td><%= h user.todos.size %></td>
<td><%= h user.contexts.size %></td>
<td><%= h user.projects.size %></td>
<td><%= h user.notes.size %></td>
<td><%= !user.is_admin? ? link_to_remote( image_tag("blank.png", :title =>"Destroy user", :class=>"delete_item"), {:url => user_path(user), :method => :delete, :confirm => "Warning: this will delete user \'#{user.login}\', all their actions, contexts, project and notes. Are you sure that you want to continue?" }, { :class => "icon" } ) : "&nbsp;" %></td>
</tr>
<% end %>
</table>
<p>
<%= will_paginate @users %>
</p>
<p><%= link_to 'Signup new user', signup_path %></p>
<tr <%= "class=\"highlight\"" if user.is_admin? %> id="user-<%= user.id %>">
<td><%=h user.login %></td>
<td><%=h user.last_name? ? user.display_name : '-' %></td>
<td><%= h user.auth_type %></td>
<td><%= h user.open_id_url || '-' %></td>
<td><%= h user.todos.size %></td>
<td><%= h user.contexts.size %></td>
<td><%= h user.projects.size %></td>
<td><%= h user.notes.size %></td>
<td><%= !user.is_admin? ? link_to_remote(
image_tag("blank.png", :title =>"Destroy user", :class=>"delete_item"),
{ :url => user_path(user.id), :method => :delete,
:confirm => "Warning: this will delete user \'#{user.login}\', all their actions, contexts, project and notes. Are you sure that you want to continue?" },
{ :class => "icon" } ) : "&nbsp;" %></td>
</tr>
<% end %>
</table>
<p>
<%= will_paginate @users %>
</p>
<p><%= link_to 'Signup new user', signup_path %></p>