mirror of
https://github.com/TracksApp/tracks.git
synced 2026-02-22 23:24:07 +01:00
Merge branch 'bsag'
Conflicts: app/views/todos/_todo.html.erb
This commit is contained in:
commit
28de00791f
220 changed files with 4775 additions and 14363 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
7
app/models/dependency.rb
Normal 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
|
||||
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -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 %>] -->
|
||||
|
|
|
|||
|
|
@ -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 %>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
4
app/views/contexts/edit.js.rjs
Normal file
4
app/views/contexts/edit.js.rjs
Normal 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
|
||||
|
|
@ -8,12 +8,6 @@
|
|||
|
||||
<div id="toggle_context_new" class="hide_form">
|
||||
<a title="Hide new context form" accesskey="n">« 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',
|
||||
'« Hide form', 'Hide new context form',
|
||||
'Create a new context »', '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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -34,9 +34,3 @@
|
|||
</table>
|
||||
</div><!-- End of feeds -->
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload=function(){
|
||||
Nifty("div#feedlegend","normal");
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
5
app/views/integrations/google_gadget.erb
Normal file
5
app/views/integrations/google_gadget.erb
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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' }) -%>
|
||||
<%= 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" ) %> |
|
||||
Created: <%= format_date(note.created_at) %>
|
||||
<% if note.updated_at? -%>
|
||||
|
|
@ -37,4 +37,4 @@
|
|||
<% end -%>
|
||||
</div>
|
||||
</div>
|
||||
<% note = nil -%>
|
||||
<% note = nil -%>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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) %>
|
||||
|
|
@ -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 %>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"> | </span><div class="tasks_sort">
|
||||
</div><span class="sort_separator"> | </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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -14,20 +14,13 @@
|
|||
|
||||
<div id="toggle_project_new" class="hide_form">
|
||||
<a title="Hide new project form" accesskey="n">« 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',
|
||||
'« Hide form', 'Hide new project form',
|
||||
'Create a new project »', '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 -%>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(/"/,""") %>" />
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
3
app/views/recurring_todos/toggle_star.js.erb
Normal file
3
app/views/recurring_todos/toggle_star.js.erb
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<% if @saved -%>
|
||||
$('div#recurring_todo_<%= @recurring_todo.id %> a.star_item img').toggleClass('starred_todo').toggleClass('unstarred_todo');
|
||||
<% end -%>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
if @saved
|
||||
page[@recurring_todo].down('a.star_item').down('img').toggleClassName('starred_todo').toggleClassName('unstarred_todo')
|
||||
end
|
||||
|
|
@ -4,6 +4,3 @@
|
|||
<%= submit_tag "Search" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
Form.focusFirstElement('search-form')
|
||||
</script>
|
||||
|
|
@ -9,21 +9,15 @@
|
|||
|
||||
<div id="toggle_action_new" class="hide_form">
|
||||
<a title="Hide new action form" accesskey="n" href="#">« 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',
|
||||
'« Hide form', 'Hide next action form',
|
||||
'Add a next action »', '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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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] -->
|
||||
|
|
|
|||
|
|
@ -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(/"/,""") %>" />
|
||||
<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>
|
||||
26
app/views/todos/_successor.html.erb
Normal file
26
app/views/todos/_successor.html.erb
Normal 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>
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
15
app/views/todos/_toggle_successors.html.erb
Normal file
15
app/views/todos/_toggle_successors.html.erb
Normal 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>
|
||||
|
||||
27
app/views/todos/add_predecessor.js.rjs
Normal file
27
app/views/todos/add_predecessor.js.rjs
Normal 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
|
||||
|
||||
|
|
@ -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
|
||||
%>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')}"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
25
app/views/todos/remove_predecessor.js.rjs
Normal file
25
app/views/todos/remove_predecessor.js.rjs
Normal 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
|
||||
|
|
@ -8,7 +8,13 @@
|
|||
:locals => { :collapsible => true } %>
|
||||
|
||||
<% unless @deferred.nil? -%>
|
||||
<%= render :partial => "todos/deferred", :locals => { :deferred => @deferred, :collapsible => true, :append_descriptor => "tagged with ‘#{@tag_name}’", :parent_container_type => 'tag' } %>
|
||||
<%= render :partial => "todos/deferred", :locals => {
|
||||
:deferred => @deferred,
|
||||
:pending => @pending,
|
||||
:collapsible => true,
|
||||
:append_descriptor => "tagged with ‘#{@tag_name}’",
|
||||
:parent_container_type => 'tag'
|
||||
} %>
|
||||
<% end -%>
|
||||
|
||||
<% unless @hidden_todos.nil? -%>
|
||||
|
|
|
|||
|
|
@ -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()"
|
||||
3
app/views/todos/toggle_star.js.erb
Normal file
3
app/views/todos/toggle_star.js.erb
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<% if @saved -%>
|
||||
$('div#line_todo_<%= @todo.id %> a.star_item img').toggleClass('starred_todo').toggleClass('unstarred_todo');
|
||||
<% end -%>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
if @saved
|
||||
page[@todo].down('a.star_item').down('img').toggleClassName('starred_todo').toggleClassName('unstarred_todo')
|
||||
end
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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> </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> </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" } ) : " " %></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" } ) : " " %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
<p>
|
||||
<%= will_paginate @users %>
|
||||
</p>
|
||||
|
||||
<p><%= link_to 'Signup new user', signup_path %></p>
|
||||
Loading…
Add table
Add a link
Reference in a new issue