mirror of
https://github.com/TracksApp/tracks.git
synced 2026-01-03 15:58:50 +01:00
Merge branch 'master' of git://github.com/bsag/tracks into bsag
This commit is contained in:
commit
ab9c00090b
110 changed files with 2366 additions and 1165 deletions
|
|
@ -54,7 +54,7 @@ class LoginController < ApplicationController
|
|||
end
|
||||
when :get
|
||||
if User.no_users_yet?
|
||||
redirect_to :controller => 'users', :action => 'new'
|
||||
redirect_to signup_path
|
||||
return
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class ProjectsController < ApplicationController
|
|||
format.rss &render_rss_feed
|
||||
format.atom &render_atom_feed
|
||||
format.text &render_text_feed
|
||||
format.autocomplete { render :text => for_autocomplete(@projects, params[:q]) }
|
||||
format.autocomplete { render :text => for_autocomplete(@projects.reject(&:completed?), params[:q]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -105,15 +105,16 @@ class ProjectsController < ApplicationController
|
|||
if params['project']['state']
|
||||
@new_state = params['project']['state']
|
||||
@state_changed = @project.state != @new_state
|
||||
logger.info "@state_changed: #{@project.state} == #{params['project']['state']} != #{@state_changed}"
|
||||
params['project'].delete('state')
|
||||
end
|
||||
success_text = if params['field'] == 'name' && params['value']
|
||||
params['project']['id'] = params['id']
|
||||
params['project']['name'] = params['value']
|
||||
end
|
||||
|
||||
@project.attributes = params['project']
|
||||
if @project.save
|
||||
@saved = @project.save
|
||||
if @saved
|
||||
@project.transition_to(@new_state) if @state_changed
|
||||
if boolean_param('wants_render')
|
||||
if (@project.hidden?)
|
||||
|
|
@ -149,8 +150,8 @@ class ProjectsController < ApplicationController
|
|||
return
|
||||
end
|
||||
else
|
||||
notify :warning, "Couldn't update project"
|
||||
render :text => ''
|
||||
init_data_for_sidebar
|
||||
render :template => 'projects/update.js.rjs'
|
||||
return
|
||||
end
|
||||
render :template => 'projects/update.js.rjs'
|
||||
|
|
@ -283,7 +284,7 @@ class ProjectsController < ApplicationController
|
|||
p.delete('default_context_name')
|
||||
|
||||
unless default_context_name.blank?
|
||||
default_context = Context.find_or_create_by_name(default_context_name)
|
||||
default_context = current_user.contexts.find_or_create_by_name(default_context_name)
|
||||
p['default_context_id'] = default_context.id
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -319,6 +319,7 @@ class StatsController < ApplicationController
|
|||
# - actions not part of a hidden project
|
||||
# - actions not part of a hidden context
|
||||
# - actions not deferred (show_from must be null)
|
||||
# - actions not pending/blocked
|
||||
|
||||
@actions_running_time = @actions.find_by_sql([
|
||||
"SELECT t.created_at "+
|
||||
|
|
@ -326,7 +327,7 @@ class StatsController < ApplicationController
|
|||
"WHERE t.user_id=? "+
|
||||
"AND t.completed_at IS NULL " +
|
||||
"AND t.show_from IS NULL " +
|
||||
"AND NOT (p.state='hidden' OR c.hide=?) " +
|
||||
"AND NOT (p.state='hidden' OR p.state='pending' OR c.hide=?) " +
|
||||
"ORDER BY t.created_at ASC", @user.id, true]
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ class TodosController < ApplicationController
|
|||
|
||||
skip_before_filter :login_required, :only => [:index, :calendar]
|
||||
prepend_before_filter :login_or_feed_token_required, :only => [:index, :calendar]
|
||||
append_before_filter :find_and_activate_ready, :only => [:index, :list_deferred]
|
||||
append_before_filter :init, :except => [ :destroy, :completed,
|
||||
:completed_archive, :check_deferred, :toggle_check, :toggle_star,
|
||||
:edit, :update, :create, :calendar, :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]
|
||||
append_before_filter :get_todo_from_params, :only => [ :edit, :toggle_check, :toggle_star, :show, :update, :destroy, :remove_predecessor, :show_notes]
|
||||
protect_from_forgery :except => [:auto_complete_for_predecessor]
|
||||
|
||||
def index
|
||||
current_user.deferred_todos.find_and_activate_ready
|
||||
@projects = current_user.projects.find(:all, :include => [:default_context])
|
||||
@contexts = current_user.contexts.find(:all)
|
||||
|
||||
|
|
@ -48,68 +48,142 @@ 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)
|
||||
|
||||
if p.project_specified_by_name?
|
||||
project = current_user.projects.find_or_create_by_name(p.project_name)
|
||||
@new_project_created = project.new_record_before_save?
|
||||
@todo.project_id = project.id
|
||||
end
|
||||
|
||||
if p.context_specified_by_name?
|
||||
context = current_user.contexts.find_or_create_by_name(p.context_name)
|
||||
@new_context_created = context.new_record_before_save?
|
||||
@not_done_todos = [@todo] if @new_context_created
|
||||
@todo.context_id = context.id
|
||||
end
|
||||
@tag_name = params['_tag_name']
|
||||
|
||||
@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
|
||||
is_multiple = params[:todo] && params[:todo][:multiple_todos] && !params[:todo][:multiple_todos].nil?
|
||||
if is_multiple
|
||||
create_multiple
|
||||
else
|
||||
p = TodoCreateParamsHelper.new(params, prefs)
|
||||
p.parse_dates() unless mobile?
|
||||
tag_list = p.tag_list
|
||||
predecessor_list = p.predecessor_list
|
||||
|
||||
@todo = current_user.todos.build(p.attributes)
|
||||
|
||||
unless (@saved == false)
|
||||
unless @todo.uncompleted_predecessors.empty? || @todo.state == 'project_hidden'
|
||||
@todo.state = 'pending'
|
||||
if p.project_specified_by_name?
|
||||
project = current_user.projects.find_or_create_by_name(p.project_name)
|
||||
@new_project_created = project.new_record_before_save?
|
||||
@todo.project_id = project.id
|
||||
end
|
||||
@todo.save
|
||||
|
||||
if p.context_specified_by_name?
|
||||
context = current_user.contexts.find_or_create_by_name(p.context_name)
|
||||
@new_context_created = context.new_record_before_save?
|
||||
@not_done_todos = [@todo] if @new_context_created
|
||||
@todo.context_id = context.id
|
||||
end
|
||||
|
||||
@todo.add_predecessor_list(predecessor_list)
|
||||
|
||||
# Fix for #977 because AASM overrides @state on creation
|
||||
specified_state = @todo.state
|
||||
|
||||
@todo.update_state_from_project
|
||||
@saved = @todo.save
|
||||
|
||||
# Fix for #977 because AASM overrides @state on creation
|
||||
@todo.update_attribute('state', specified_state) unless specified_state == "immediate"
|
||||
|
||||
unless (@saved == false) || tag_list.blank?
|
||||
@todo.tag_with(tag_list)
|
||||
@todo.tags.reload
|
||||
end
|
||||
|
||||
unless (@saved == false)
|
||||
unless @todo.uncompleted_predecessors.empty? || @todo.state == 'project_hidden'
|
||||
@todo.state = 'pending'
|
||||
end
|
||||
@todo.save
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to :action => "index" }
|
||||
format.m do
|
||||
@return_path=cookies[:mobile_url] ? cookies[:mobile_url] : mobile_path
|
||||
if @saved
|
||||
redirect_to @return_path
|
||||
else
|
||||
@projects = current_user.projects.find(:all)
|
||||
@contexts = current_user.contexts.find(:all)
|
||||
render :action => "new"
|
||||
end
|
||||
end
|
||||
format.js do
|
||||
determine_down_count if @saved
|
||||
@contexts = current_user.contexts.find(:all) if @new_context_created
|
||||
@projects = current_user.projects.find(:all) if @new_project_created
|
||||
@initial_context_name = params['default_context_name']
|
||||
@initial_project_name = params['default_project_name']
|
||||
@default_tags = @todo.project.default_tags unless @todo.project.nil?
|
||||
render :action => 'create'
|
||||
end
|
||||
format.xml do
|
||||
if @saved
|
||||
head :created, :location => todo_url(@todo)
|
||||
else
|
||||
render :xml => @todo.errors.to_xml, :status => 422
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_multiple
|
||||
if project_specified_by_name(params[:project_name])
|
||||
project = current_user.projects.find_or_create_by_name(params[:project_name])
|
||||
@new_project_created = project.new_record_before_save?
|
||||
@project_id = project.id
|
||||
end
|
||||
|
||||
if context_specified_by_name(params[:context_name])
|
||||
context = current_user.contexts.find_or_create_by_name(params[:context_name])
|
||||
@new_context_created = context.new_record_before_save?
|
||||
@not_done_todos = [] if @new_context_created
|
||||
@context_id = context.id
|
||||
end
|
||||
|
||||
tag_list = params[:tag_list]
|
||||
|
||||
@todos = []
|
||||
params[:todo][:multiple_todos].split("\n").map do |line|
|
||||
@todo = current_user.todos.build(
|
||||
:description => line)
|
||||
@todo.project_id = @project_id
|
||||
@todo.context_id = @context_id
|
||||
puts "TODO: #{@todo.description}, #{@todo.project_id}, #{@todo.context_id}"
|
||||
@saved = @todo.save
|
||||
puts "NOT SAVED" unless @saved
|
||||
unless (@saved == false) || tag_list.blank?
|
||||
@todo.tag_with(tag_list)
|
||||
@todo.tags.reload
|
||||
end
|
||||
@todos << @todo
|
||||
@not_done_todos << @todo if @new_context_created
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to :action => "index" }
|
||||
format.m do
|
||||
@return_path=cookies[:mobile_url] ? cookies[:mobile_url] : mobile_path
|
||||
if @saved
|
||||
redirect_to @return_path
|
||||
else
|
||||
@projects = current_user.projects.find(:all)
|
||||
@contexts = current_user.contexts.find(:all)
|
||||
render :action => "new"
|
||||
end
|
||||
end
|
||||
format.js do
|
||||
determine_down_count if @saved
|
||||
@contexts = current_user.contexts.find(:all) if @new_context_created
|
||||
@projects = current_user.projects.find(:all) if @new_project_created
|
||||
@initial_context_name = params['default_context_name']
|
||||
@initial_project_name = params['default_project_name']
|
||||
@default_tags = @todo.project.default_tags unless @todo.project.nil?
|
||||
render :action => 'create'
|
||||
if @todos.size > 0
|
||||
@default_tags = @todos[0].project.default_tags unless @todos[0].project.nil?
|
||||
else
|
||||
@multiple_error = "You need to submit at least one next action"
|
||||
@saved = false;
|
||||
@default_tags = current_user.projects.find_by_name(@initial_project_name).default_tags unless @initial_project_name.blank?
|
||||
end
|
||||
render :action => 'create_multiple'
|
||||
end
|
||||
format.xml do
|
||||
if @saved
|
||||
head :created, :location => todo_url(@todo)
|
||||
head :created, :location => context_url(@todos[0].context)
|
||||
else
|
||||
render :xml => @todo.errors.to_xml, :status => 422
|
||||
render :xml => @todos[0].errors.to_xml, :status => 422
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -176,12 +250,12 @@ class TodosController < ApplicationController
|
|||
if @todo.completed?
|
||||
@pending_to_activate = @todo.pending_to_activate
|
||||
@pending_to_activate.each do |t|
|
||||
t.activate!
|
||||
t.activate!
|
||||
end
|
||||
else
|
||||
@active_to_block = @todo.active_to_block
|
||||
@active_to_block.each do |t|
|
||||
t.block!
|
||||
t.block!
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -190,7 +264,7 @@ class TodosController < ApplicationController
|
|||
if @saved
|
||||
determine_remaining_in_context_count(@todo.context_id)
|
||||
determine_down_count
|
||||
determine_completed_count
|
||||
determine_completed_count
|
||||
determine_deferred_tag_count(params['_tag_name']) if @source_view == 'tag'
|
||||
if source_view_is :calendar
|
||||
@original_item_due_id = get_due_id_for_calendar(@original_item_due)
|
||||
|
|
@ -222,6 +296,23 @@ class TodosController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def change_context
|
||||
@todo = Todo.find(params[:todo][:id])
|
||||
@original_item_context_id = @todo.context_id
|
||||
@context = Context.find(params[:todo][:context_id])
|
||||
@todo.context = @context
|
||||
@saved = @todo.save
|
||||
|
||||
@context_changed = true
|
||||
@message = "Context changed to #{@context.name}"
|
||||
determine_remaining_in_context_count(@original_item_context_id)
|
||||
|
||||
respond_to do |format|
|
||||
format.js {render :action => :update }
|
||||
format.xml { render :xml => @todo.to_xml( :except => :user_id ) }
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@source_view = params['_source_view'] || 'todo'
|
||||
init_data_for_sidebar unless mobile?
|
||||
|
|
@ -276,7 +367,7 @@ class TodosController < ApplicationController
|
|||
if params['done'] == '1' && !@todo.completed?
|
||||
@todo.complete!
|
||||
@todo.pending_to_activate.each do |t|
|
||||
t.activate!
|
||||
t.activate!
|
||||
end
|
||||
end
|
||||
# strange. if checkbox is not checked, there is no 'done' in params.
|
||||
|
|
@ -284,7 +375,7 @@ class TodosController < ApplicationController
|
|||
if !(params['done'] == '1') && @todo.completed?
|
||||
@todo.activate!
|
||||
@todo.active_to_block.each do |t|
|
||||
t.block!
|
||||
t.block!
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -395,7 +486,7 @@ class TodosController < ApplicationController
|
|||
notify :error, "Failed to delete the action", 2.0
|
||||
redirect_to :action => 'index'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
format.js do
|
||||
if @saved
|
||||
|
|
@ -403,7 +494,7 @@ class TodosController < ApplicationController
|
|||
if source_view_is_one_of(:todo, :deferred)
|
||||
determine_remaining_in_context_count(@context_id)
|
||||
elsif source_view_is :calendar
|
||||
@original_item_due_id = get_due_id_for_calendar(@original_item_due)
|
||||
@original_item_due_id = get_due_id_for_calendar(@original_item_due)
|
||||
@old_due_empty = is_old_due_empty(@original_item_due_id)
|
||||
end
|
||||
end
|
||||
|
|
@ -435,13 +526,10 @@ class TodosController < ApplicationController
|
|||
@source_view = 'deferred'
|
||||
@page_title = "TRACKS::Tickler"
|
||||
|
||||
@projects = current_user.projects.find(:all, :include => [ :todos, :default_context ])
|
||||
@contexts_to_show = @contexts = current_user.contexts.find(:all, :include => [ :todos ])
|
||||
@contexts_to_show = @contexts = current_user.contexts.find(:all)
|
||||
|
||||
current_user.deferred_todos.find_and_activate_ready
|
||||
@not_done_todos = current_user.deferred_todos + current_user.pending_todos
|
||||
@count = @not_done_todos.size
|
||||
@down_count = @count
|
||||
@not_done_todos = current_user.deferred_todos(:include => [:tags, :taggings, :projects]) + current_user.pending_todos(:include => [:tags, :taggings, :projects])
|
||||
@down_count = @count = @not_done_todos.size
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
|
|
@ -482,25 +570,25 @@ class TodosController < ApplicationController
|
|||
@tag = Tag.new(:name => @tag_name) if @tag.nil?
|
||||
tag_collection = @tag.todos
|
||||
|
||||
@not_done_todos = tag_collection.find(:all,
|
||||
@not_done_todos = tag_collection.find(:all,
|
||||
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'active'],
|
||||
:order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC')
|
||||
@hidden_todos = current_user.todos.find(:all,
|
||||
:include => [:taggings, :tags, :context],
|
||||
@hidden_todos = current_user.todos.find(:all,
|
||||
:include => [:taggings, :tags, :context],
|
||||
:conditions => ['tags.name = ? AND (todos.state = ? OR (contexts.hide = ? AND todos.state = ?))', @tag_name, 'project_hidden', true, 'active'],
|
||||
:order => 'todos.completed_at DESC, todos.created_at DESC')
|
||||
@deferred = tag_collection.find(:all,
|
||||
@deferred = tag_collection.find(:all,
|
||||
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'deferred'],
|
||||
:order => 'show_from ASC, todos.created_at DESC')
|
||||
@pending = tag_collection.find(:all,
|
||||
@pending = tag_collection.find(:all,
|
||||
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'pending'],
|
||||
:order => 'show_from ASC, todos.created_at DESC')
|
||||
|
||||
# If you've set no_completed to zero, the completed items box isn't shown on
|
||||
# the tag page
|
||||
max_completed = current_user.prefs.show_number_completed
|
||||
@done = tag_collection.find(:all,
|
||||
:limit => max_completed,
|
||||
@done = tag_collection.find(:all,
|
||||
:limit => max_completed,
|
||||
:conditions => ['todos.user_id = ? and state = ?', current_user.id, 'completed'],
|
||||
:order => 'todos.completed_at DESC')
|
||||
|
||||
|
|
@ -516,13 +604,13 @@ class TodosController < ApplicationController
|
|||
|
||||
# Set count badge to number of items with this tag
|
||||
@not_done_todos.empty? ? @count = 0 : @count = @not_done_todos.size
|
||||
@down_count = @count
|
||||
@down_count = @count
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.m {
|
||||
format.m {
|
||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
|
||||
render :action => "mobile_tag"
|
||||
render :action => "mobile_tag"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -567,23 +655,23 @@ class TodosController < ApplicationController
|
|||
due_this_month_date = Time.zone.now.end_of_month
|
||||
|
||||
@due_today = current_user.todos.not_completed.find(:all,
|
||||
:include => [:taggings, :tags],
|
||||
:include => [:taggings, :tags],
|
||||
:conditions => ['todos.due <= ?', due_today_date],
|
||||
:order => "due")
|
||||
@due_this_week = current_user.todos.not_completed.find(:all,
|
||||
:include => [:taggings, :tags],
|
||||
:include => [:taggings, :tags],
|
||||
:conditions => ['todos.due > ? AND todos.due <= ?', due_today_date, due_this_week_date],
|
||||
:order => "due")
|
||||
@due_next_week = current_user.todos.not_completed.find(:all,
|
||||
:include => [:taggings, :tags],
|
||||
:include => [:taggings, :tags],
|
||||
:conditions => ['todos.due > ? AND todos.due <= ?', due_this_week_date, due_next_week_date],
|
||||
:order => "due")
|
||||
@due_this_month = current_user.todos.not_completed.find(:all,
|
||||
:include => [:taggings, :tags],
|
||||
:include => [:taggings, :tags],
|
||||
:conditions => ['todos.due > ? AND todos.due <= ?', due_next_week_date, due_this_month_date],
|
||||
:order => "due")
|
||||
@due_after_this_month = current_user.todos.not_completed.find(:all,
|
||||
:include => [:taggings, :tags],
|
||||
:include => [:taggings, :tags],
|
||||
:conditions => ['todos.due > ?', due_this_month_date],
|
||||
:order => "due")
|
||||
|
||||
|
|
@ -602,35 +690,35 @@ class TodosController < ApplicationController
|
|||
unless params['id'].nil?
|
||||
get_todo_from_params
|
||||
# Begin matching todos in current project
|
||||
@items = current_user.todos.find(:all,
|
||||
@items = current_user.todos.find(:all,
|
||||
:select => 'description, project_id, context_id, created_at',
|
||||
:conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND ' +
|
||||
'NOT (id = ?) AND lower(description) LIKE ? AND project_id = ?',
|
||||
'active', 'pending', 'deferred',
|
||||
@todo.id,
|
||||
'%' + params[:predecessor_list].downcase + '%',
|
||||
@todo.project_id ],
|
||||
'NOT (id = ?) AND lower(description) LIKE ? AND project_id = ?',
|
||||
'active', 'pending', 'deferred',
|
||||
@todo.id,
|
||||
'%' + params[:predecessor_list].downcase + '%',
|
||||
@todo.project_id ],
|
||||
:order => 'description ASC',
|
||||
:limit => 10
|
||||
)
|
||||
if @items.empty? # Match todos in other projects
|
||||
@items = current_user.todos.find(:all,
|
||||
:select => 'description, project_id, context_id, created_at',
|
||||
@items = current_user.todos.find(:all,
|
||||
:select => 'description, project_id, context_id, created_at',
|
||||
:conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND ' +
|
||||
'NOT (id = ?) AND lower(description) LIKE ?',
|
||||
'active', 'pending', 'deferred',
|
||||
params[:id], '%' + params[:q].downcase + '%' ],
|
||||
'NOT (id = ?) AND lower(description) LIKE ?',
|
||||
'active', 'pending', 'deferred',
|
||||
params[:id], '%' + params[:q].downcase + '%' ],
|
||||
:order => 'description ASC',
|
||||
:limit => 10
|
||||
)
|
||||
end
|
||||
else
|
||||
# New todo - TODO: Filter on project
|
||||
@items = current_user.todos.find(:all,
|
||||
@items = current_user.todos.find(:all,
|
||||
:select => 'description, project_id, context_id, created_at',
|
||||
:conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND lower(description) LIKE ?',
|
||||
'active', 'pending', 'deferred',
|
||||
'%' + params[:q].downcase + '%' ],
|
||||
:conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND lower(description) LIKE ?',
|
||||
'active', 'pending', 'deferred',
|
||||
'%' + params[:q].downcase + '%' ],
|
||||
:order => 'description ASC',
|
||||
:limit => 10
|
||||
)
|
||||
|
|
@ -641,22 +729,38 @@ class TodosController < ApplicationController
|
|||
def convert_to_project
|
||||
@todo = current_user.todos.find(params[:id])
|
||||
@project = current_user.projects.new(:name => @todo.description, :description => @todo.notes,
|
||||
:default_context => @todo.context)
|
||||
:default_context => @todo.context)
|
||||
@todo.destroy
|
||||
@project.save!
|
||||
redirect_to project_url(@project)
|
||||
end
|
||||
|
||||
|
||||
def show_notes
|
||||
@return_path=cookies[:mobile_url] ? cookies[:mobile_url] : mobile_path
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
redirect_to home_path, "Viewing note of todo is not implemented"
|
||||
}
|
||||
format.m {
|
||||
render:action => "mobile_show_notes"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_todo_from_params
|
||||
@todo = current_user.todos.find(params['id'])
|
||||
end
|
||||
|
||||
def find_and_activate_ready
|
||||
current_user.deferred_todos.find_and_activate_ready
|
||||
end
|
||||
|
||||
def init
|
||||
@source_view = params['_source_view'] || 'todo'
|
||||
init_data_for_sidebar unless mobile?
|
||||
init_todos
|
||||
init_todos
|
||||
end
|
||||
|
||||
def with_feed_query_scope(&block)
|
||||
|
|
@ -725,7 +829,7 @@ class TodosController < ApplicationController
|
|||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def with_limit_scope(&block)
|
||||
|
|
@ -751,7 +855,7 @@ class TodosController < ApplicationController
|
|||
with_limit_scope do
|
||||
|
||||
if mobile?
|
||||
init_todos_for_mobile_view
|
||||
init_todos_for_mobile_view
|
||||
else
|
||||
|
||||
# Note: these next two finds were previously using
|
||||
|
|
@ -761,10 +865,10 @@ class TodosController < ApplicationController
|
|||
@todos = Todo.find(:all, :conditions => ['todos.user_id = ?', current_user.id], :include => [ :project, :context, :tags ])
|
||||
|
||||
# Exclude hidden projects from the home page
|
||||
@not_done_todos = Todo.find(:all,
|
||||
:conditions => ['todos.user_id = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)',
|
||||
current_user.id, false, 'active'],
|
||||
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
|
||||
@not_done_todos = Todo.find(:all,
|
||||
:conditions => ['todos.user_id = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)',
|
||||
current_user.id, false, 'active'],
|
||||
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
|
||||
:include => [ :project, :context, :tags ])
|
||||
end
|
||||
|
||||
|
|
@ -778,10 +882,10 @@ class TodosController < ApplicationController
|
|||
# but that broke with_scope for :limit
|
||||
|
||||
# Exclude hidden projects from the home page
|
||||
@not_done_todos = Todo.find(:all,
|
||||
:conditions => ['todos.user_id = ? AND todos.state = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)',
|
||||
current_user.id, 'active', false, 'active'],
|
||||
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
|
||||
@not_done_todos = Todo.find(:all,
|
||||
:conditions => ['todos.user_id = ? AND todos.state = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)',
|
||||
current_user.id, 'active', false, 'active'],
|
||||
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
|
||||
:include => [ :project, :context, :tags ])
|
||||
end
|
||||
|
||||
|
|
@ -789,8 +893,8 @@ class TodosController < ApplicationController
|
|||
source_view do |from|
|
||||
from.todo do
|
||||
@down_count = Todo.count(
|
||||
:all,
|
||||
:conditions => ['todos.user_id = ? and todos.state = ? and contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', current_user.id, 'active', false, 'active'],
|
||||
:all,
|
||||
:conditions => ['todos.user_id = ? and todos.state = ? and contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', current_user.id, 'active', false, 'active'],
|
||||
:include => [ :project, :context ])
|
||||
# #@down_count = Todo.count_by_sql(['SELECT COUNT(*) FROM todos,
|
||||
# contexts WHERE todos.context_id = contexts.id and todos.user_id = ?
|
||||
|
|
@ -821,12 +925,12 @@ class TodosController < ApplicationController
|
|||
@not_done_todos.empty? ? @down_count = 0 : @down_count = @not_done_todos.size
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def determine_remaining_in_context_count(context_id = @todo.context_id)
|
||||
source_view do |from|
|
||||
from.deferred { @remaining_in_context = current_user.contexts.find(context_id).deferred_todo_count }
|
||||
from.tag {
|
||||
from.tag {
|
||||
tag = Tag.find_by_name(params['_tag_name'])
|
||||
if tag.nil?
|
||||
tag = Tag.new(:name => params['tag'])
|
||||
|
|
@ -835,7 +939,7 @@ class TodosController < ApplicationController
|
|||
}
|
||||
end
|
||||
@remaining_in_context = current_user.contexts.find(context_id).not_done_todo_count if @remaining_in_context.nil?
|
||||
end
|
||||
end
|
||||
|
||||
def determine_completed_count
|
||||
source_view do |from|
|
||||
|
|
@ -994,7 +1098,7 @@ class TodosController < ApplicationController
|
|||
end
|
||||
end
|
||||
end
|
||||
return new_recurring_todo
|
||||
return new_recurring_todo
|
||||
end
|
||||
|
||||
def get_due_id_for_calendar(due)
|
||||
|
|
@ -1035,11 +1139,11 @@ class TodosController < ApplicationController
|
|||
when "due_this_month"
|
||||
return 0 == current_user.todos.not_completed.count(:all,
|
||||
:conditions => ['todos.due > ? AND todos.due <= ?', due_next_week_date, due_this_month_date])
|
||||
when "due_after_this_month"
|
||||
when "due_after_this_month"
|
||||
return 0 == current_user.todos.not_completed.count(:all,
|
||||
:conditions => ['todos.due > ?', due_this_month_date])
|
||||
else
|
||||
raise Exception.new, "unknown due id for calendar: '#{id}'"
|
||||
raise Exception.new, "unknown due id for calendar: '#{id}'"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1116,4 +1220,20 @@ class TodosController < ApplicationController
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def project_specified_by_name(project_name)
|
||||
return false unless params['project_id'].blank?
|
||||
return false if project_name.blank?
|
||||
return false if project_name == 'None'
|
||||
true
|
||||
end
|
||||
|
||||
def context_specified_by_name(context_name)
|
||||
return false unless params['context_id'].blank?
|
||||
return false if context_name.blank?
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class UsersController < ApplicationController
|
|||
|
||||
unless user.valid?
|
||||
session['new_user'] = user
|
||||
redirect_to :action => 'new'
|
||||
redirect_to signup_path
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -119,10 +119,8 @@ module TodosHelper
|
|||
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') +
|
||||
image_tag('blank.png', :width => 16, :height => 16, :border => 0,
|
||||
:title => "Drop an action to make it depend on this action", :class => 'successor_target drop_target')
|
||||
:title => 'Drag onto another action to make it depend on that action',
|
||||
:class => 'grip')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -227,14 +225,6 @@ module TodosHelper
|
|||
end
|
||||
end
|
||||
|
||||
def calendar_setup( input_field )
|
||||
# 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 todo.deferred? or todo.pending?
|
||||
return "tickleritems"
|
||||
|
|
@ -253,11 +243,14 @@ module TodosHelper
|
|||
return false if source_view_is(:context) && (@todo.project.hidden? || @todo.project.completed?)
|
||||
end
|
||||
|
||||
return false if (source_view_is(:tag) && !@todo.tags.include?(@tag_name))
|
||||
|
||||
return true if source_view_is(:deferred) && @todo.deferred?
|
||||
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
|
||||
|
|
|
|||
|
|
@ -29,13 +29,16 @@ class RecurringTodo < ActiveRecord::Base
|
|||
|
||||
validates_presence_of :description
|
||||
validates_presence_of :recurring_period
|
||||
validates_presence_of :target
|
||||
validates_presence_of :ends_on
|
||||
validates_presence_of :context
|
||||
|
||||
validates_length_of :description, :maximum => 100
|
||||
validates_length_of :notes, :maximum => 60000, :allow_nil => true
|
||||
|
||||
validates_presence_of :context
|
||||
|
||||
validate :period_specific_validations
|
||||
validate :starts_and_ends_on_validations
|
||||
validate :set_recurrence_on_validations
|
||||
|
||||
def period_specific_validations
|
||||
periods = %W[daily weekly monthly yearly]
|
||||
|
|
@ -57,9 +60,8 @@ class RecurringTodo < ActiveRecord::Base
|
|||
errors.add_to_base("Every other nth week may not be empty for recurrence setting")
|
||||
end
|
||||
something_set = false
|
||||
%w{sunday monday tuesday wednesday thursday friday}.each do |day|
|
||||
%w{sunday monday tuesday wednesday thursday friday saturday}.each do |day|
|
||||
something_set ||= self.send("on_#{day}")
|
||||
|
||||
end
|
||||
errors.add_to_base("You must specify at least one day on which the todo recurs") if !something_set
|
||||
end
|
||||
|
|
@ -104,6 +106,21 @@ class RecurringTodo < ActiveRecord::Base
|
|||
errors.add_to_base("The end of the recurrence is not selected") unless ends_on == "no_end_date"
|
||||
end
|
||||
end
|
||||
|
||||
def set_recurrence_on_validations
|
||||
# show always or x days before due date. x not null
|
||||
case self.target
|
||||
when 'show_from_date'
|
||||
# no validations
|
||||
when 'due_date'
|
||||
errors.add_to_base("Please select when to show the action") if show_always.nil?
|
||||
unless show_always
|
||||
errors.add_to_base("Please fill in the number of days to show the todo before the due date") if show_from_delta.nil? || show_from_delta.blank?
|
||||
end
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence target selector '#{self.recurrence_target}'"
|
||||
end
|
||||
end
|
||||
|
||||
# the following recurrence patterns can be stored:
|
||||
#
|
||||
|
|
@ -388,9 +405,9 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def recurrence_pattern
|
||||
return "invalid repeat pattern" if every_other1.nil?
|
||||
case recurring_period
|
||||
when 'daily'
|
||||
return "invalid repeat pattern" if every_other1.nil?
|
||||
if only_work_days
|
||||
return "on work days"
|
||||
else
|
||||
|
|
@ -401,21 +418,19 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
when 'weekly'
|
||||
return "invalid repeat pattern" if every_other1.nil?
|
||||
if every_other1 > 1
|
||||
return "every #{every_other1} weeks"
|
||||
else
|
||||
return 'weekly'
|
||||
end
|
||||
when 'monthly'
|
||||
return "invalid repeat pattern" if every_other1.nil? || every_other2.nil?
|
||||
return "invalid repeat pattern" if every_other2.nil?
|
||||
if self.recurrence_selector == 0
|
||||
return "every #{self.every_other2} month#{self.every_other2>1?'s':''} on day #{self.every_other1}"
|
||||
else
|
||||
return "every #{self.xth} #{self.day_of_week} of every #{self.every_other2} month#{self.every_other2>1?'s':''}"
|
||||
end
|
||||
when 'yearly'
|
||||
return "invalid repeat pattern" if every_other1.nil?
|
||||
if self.recurrence_selector == 0
|
||||
return "every year on #{self.month_of_year} #{self.every_other1}"
|
||||
else
|
||||
|
|
@ -496,30 +511,12 @@ class RecurringTodo < ActiveRecord::Base
|
|||
#
|
||||
# assumes self.recurring_period == 'daily'
|
||||
|
||||
# determine start
|
||||
if previous.nil?
|
||||
start = self.start_from.nil? ? Time.zone.now : self.start_from
|
||||
else
|
||||
# use the next day
|
||||
start = previous + 1.day
|
||||
|
||||
unless self.start_from.nil?
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
# start_from as start to search for next date
|
||||
start = self.start_from if self.start_from > previous
|
||||
end
|
||||
end
|
||||
start = determine_start(previous, 1.day)
|
||||
|
||||
if self.only_work_days
|
||||
if start.wday() >= 1 && start.wday() <= 5 # 1=monday; 5=friday
|
||||
return start
|
||||
else
|
||||
if start.wday() == 0 # sunday
|
||||
return start + 1.day
|
||||
else # saturday
|
||||
return start + 2.day
|
||||
end
|
||||
end
|
||||
return start + 2.day if start.wday() == 6 # saturday
|
||||
return start + 1.day if start.wday() == 0 # sunday
|
||||
return start
|
||||
else # every nth day; n = every_other1
|
||||
# if there was no previous todo, do not add n: the first todo starts on
|
||||
# today or on start_from
|
||||
|
|
@ -535,8 +532,8 @@ class RecurringTodo < ActiveRecord::Base
|
|||
start = previous + 1.day
|
||||
if start.wday() == 0
|
||||
# we went to a new week , go to the nth next week and find first match
|
||||
# that week
|
||||
start += self.every_other1.week
|
||||
# that week. Note that we already went into the next week, so -1
|
||||
start += (self.every_other1-1).week
|
||||
end
|
||||
unless self.start_from.nil?
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
|
|
@ -726,16 +723,16 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
protected
|
||||
|
||||
def validate
|
||||
errors.add("", "At least one day must be selected in the weekly pattern") if self.every_day == ' '
|
||||
end
|
||||
|
||||
def determine_start(previous)
|
||||
|
||||
def determine_start(previous, offset=0.day)
|
||||
# offset needs to be 1.day for daily patterns
|
||||
|
||||
if previous.nil?
|
||||
start = self.start_from.nil? ? Time.zone.now : self.start_from
|
||||
# skip to present
|
||||
start = Time.zone.now if Time.zone.now > start
|
||||
else
|
||||
start = previous
|
||||
start = previous + offset
|
||||
|
||||
unless self.start_from.nil?
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
|
|
|
|||
|
|
@ -18,12 +18,19 @@ class Todo < ActiveRecord::Base
|
|||
|
||||
named_scope :active, :conditions => { :state => 'active' }
|
||||
named_scope :not_completed, :conditions => ['NOT (todos.state = ? )', 'completed']
|
||||
named_scope :completed, :conditions => ["NOT completed_at IS NULL"]
|
||||
named_scope :are_due, :conditions => ['NOT (todos.due IS NULL)']
|
||||
named_scope :deferred, :conditions => ["completed_at IS NULL AND NOT show_from IS NULL"]
|
||||
named_scope :blocked, :conditions => ['todos.state = ?', 'pending']
|
||||
|
||||
STARRED_TAG_NAME = "starred"
|
||||
|
||||
# regular expressions for dependencies
|
||||
RE_TODO = /[^"]+/
|
||||
RE_CONTEXT = /[^"]+/
|
||||
RE_PROJECT = /[^"]+/
|
||||
RE_PARTS = /"(#{RE_TODO})"\s<"(#{RE_CONTEXT})";\s"(#{RE_PROJECT})">/ # results in array
|
||||
RE_SPEC = /"#{RE_TODO}"\s<"#{RE_CONTEXT}";\s"#{RE_PROJECT}">/ # results in string
|
||||
|
||||
acts_as_state_machine :initial => :active, :column => 'state'
|
||||
|
||||
|
|
@ -94,27 +101,36 @@ class Todo < ActiveRecord::Base
|
|||
|
||||
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)
|
||||
parts = specification.scan(RE_PARTS)
|
||||
return nil unless parts.length == 1
|
||||
return nil unless parts[0].length == 3
|
||||
todo_description = parts[0][0]
|
||||
context_name = parts[0][1]
|
||||
project_name = parts[0][2]
|
||||
|
||||
# find the project
|
||||
project_id = nil;
|
||||
unless project_name == "(none)"
|
||||
project = Project.first(:conditions => {
|
||||
:user_id => self.user.id,
|
||||
:name => project_name
|
||||
})
|
||||
project_id = project.id unless project.nil?
|
||||
end
|
||||
|
||||
todos = Todo.all(
|
||||
:joins => :context,
|
||||
:conditions => {
|
||||
:description => todo_description,
|
||||
:contexts => {:name => context_name}
|
||||
:user_id => self.user.id,
|
||||
:contexts => {:name => context_name},
|
||||
:project_id => project_id
|
||||
}
|
||||
)
|
||||
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
|
||||
|
||||
# TODO: what todo if there are more than one todo that fit the specification
|
||||
return todos[0]
|
||||
end
|
||||
|
||||
def validate
|
||||
|
|
@ -267,9 +283,8 @@ class Todo < ActiveRecord::Base
|
|||
|
||||
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)
|
||||
@predecessor_array = predecessor_list.scan(RE_SPEC)
|
||||
return @predecessor_array
|
||||
end
|
||||
|
||||
def add_predecessor(t)
|
||||
|
|
@ -287,10 +302,6 @@ class Todo < ActiveRecord::Base
|
|||
return successors.find_all {|t| t.active? or t.deferred?}
|
||||
end
|
||||
|
||||
def notes=(value)
|
||||
super(value.try(:gsub, /</, '<').try(:gsub, />/, '>'))
|
||||
end
|
||||
|
||||
def raw_notes=(value)
|
||||
self[:notes] = value
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@
|
|||
<%= link_to_context( context ) %>
|
||||
<% end %>
|
||||
</h2>
|
||||
<div id="c_<%=context.id%>_target" class="context_target drop_target"></div>
|
||||
<div id="c<%= context.id %>items" class="items toggle_target">
|
||||
<div id="c<%= context.id %>empty-nd" style="display:<%= @not_done.empty? ? 'block' : 'none'%>;">
|
||||
<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" } %>
|
||||
</div><!-- [end:items] -->
|
||||
<div id="c_<%=context.id%>_target" class="context_target drop_target"></div>
|
||||
</div><!-- [end:c<%= context.id %>] -->
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
: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}'?",
|
||||
:confirm => "Are you sure that you want to delete the context '#{context.name}'? Be aware that this will also delete all actions in this context!",
|
||||
:html => { :id => dom_id(context, 'delete') }
|
||||
) %>
|
||||
<%= link_to_remote(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
if not @not_done.empty?
|
||||
# only show a context when there are actions in it
|
||||
-%>
|
||||
<h2><%=mobile_context.name%></h2>
|
||||
<h2><%= link_to mobile_context.name, context_path(mobile_context, :format => 'm') %></h2>
|
||||
<ul class="c">
|
||||
<%= render :partial => "todos/mobile_todo",
|
||||
:collection => @not_done,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<%
|
||||
new_todo_params = {}
|
||||
new_todo_params = {:format => :m}
|
||||
new_todo_params[:from_project] = @mobile_from_project if @mobile_from_project
|
||||
new_todo_params[:from_context] = @mobile_from_context if @mobile_from_context
|
||||
-%><?xml version="1.0"?>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<h1><span class="count"><%= @down_count %></span> <%=
|
||||
current_user.time.strftime(@prefs.title_date_format) -%></h1>
|
||||
<div class="nav">
|
||||
<%= (link_to("0-New action", new_todo_path(new_todo_params, :format => 'm'))+" | ") unless @new_mobile -%>
|
||||
<%= (link_to("0-New action", new_todo_path(new_todo_params))+" | ") unless @new_mobile -%>
|
||||
<%= (link_to("1-Home", todos_path(:format => 'm'))+" | ") unless @home -%>
|
||||
<%= (link_to("2-Contexts", contexts_path(:format => 'm'))+" | ") -%>
|
||||
<%= (link_to("3-Projects", projects_path(:format => 'm'))+" | ") -%>
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
<hr/><% if !@prefs.nil? -%>
|
||||
<div class="nav">
|
||||
<%= (link_to("Logout", logout_path(:format => 'm')) +" | ") -%>
|
||||
<%= (link_to("0-New action", new_todo_path(:format => 'm'), {:accesskey => "0"})+" | ") unless @new_mobile -%>
|
||||
<%= (link_to("0-New action", new_todo_path(new_todo_params), {:accesskey => "0"})+" | ") unless @new_mobile -%>
|
||||
<%= (link_to("1-Home", todos_path(:format => 'm'), {:accesskey => "1"})+" | ") unless @home -%>
|
||||
<%= (link_to("2-Contexts", contexts_path(:format => 'm'), {:accesskey => "2"})+" | ") -%>
|
||||
<%= (link_to("3-Projects", projects_path(:format => 'm'), {:accesskey => "3"})+" | ") -%>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
'jquery.blockUI','jquery.jeditable','jquery.autocomplete',
|
||||
'jquery.truncator', :cache => 'jquery-all' %>
|
||||
<%= javascript_include_tag 'hoverIntent','superfish','application',
|
||||
'accesskey-hints','niftycube','flashobject', :cache => 'tracks' %>
|
||||
'accesskey-hints','niftycube','swfobject', :cache => 'tracks' %>
|
||||
<%= 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 %>
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
<div id="minilinks">
|
||||
<%= link_to("Toggle notes", "#", {:accesskey => "S", :title => "Toggle all notes", :id => "toggle-notes-nav"}) %>
|
||||
|
|
||||
<%= link_to "Logout (#{current_user.display_name}) »", logout_path %> <p>
|
||||
<%= link_to "Logout (#{current_user.display_name}) »", logout_path %>
|
||||
</div>
|
||||
<div id="navcontainer">
|
||||
<ul class="sf-menu">
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
<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>
|
||||
<li><%= link_to 'REST API Docs', rest_api_docs_path %></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><%= navigation_link(image_tag("system-search.png", :size => "16X16", :border => 0), {:controller => "search", :action => "index"}, :title => "Search All Items" ) %></li>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<div class="container">
|
||||
<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>
|
||||
<% 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>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ project = project_form
|
|||
%>
|
||||
|
||||
<% 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 -%>
|
||||
|
||||
<div id="error_status"><%= error_messages_for("project") %></div>
|
||||
<%= source_view_tag( @source_view ) -%>
|
||||
|
||||
<label for="project_name">Name:</label><br/>
|
||||
|
|
|
|||
|
|
@ -15,22 +15,23 @@ suppress_edit_button ||= false
|
|||
<div class="buttons">
|
||||
<span class="grey"><%= project.current_state.to_s.upcase %></span>
|
||||
<a class="delete_project_button"
|
||||
href="<%= project_path(project, :format => 'js') %>"
|
||||
href="<%= project_path(project, :format => 'js') %>"
|
||||
title="delete the project '<%= project.name %>'"><%= image_tag( "blank.png",
|
||||
:title => "Delete project",
|
||||
:title => "Delete project",
|
||||
:class=>"delete_item") %></a>
|
||||
|
||||
<% 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)}').block({message:null});",
|
||||
:complete => "$('#{dom_id(project)}').unblock();enable_rich_interaction();"
|
||||
) %>
|
||||
|
||||
<% 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)}').block({message:null});",
|
||||
:complete => "$('#{dom_id(project)}').unblock();enable_rich_interaction();",
|
||||
:html => {:id => "link_edit_#{dom_id(project)}"}
|
||||
) %>
|
||||
|
||||
<% end -%>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
<div class="project-state-group" id="list-<%= state %>-projects-container" <%= " style=\"display:none\"" if project_state_group.empty? %>>
|
||||
<h2><span id="<%= state %>-projects-count" class="badge"><%= project_state_group.length %></span><%= state.titlecase %> Projects</h2>
|
||||
<div class="menu_sort"><span class="sort_separator">Sort </span>
|
||||
<div class="alpha_sort">
|
||||
<%= link_to("Alphabetically", alphabetize_projects_path(:state => state),
|
||||
:class => "alphabetize_link", :title => "Sort these projects alphabetically") %>
|
||||
<div class="alpha_sort">
|
||||
<%= link_to("Alphabetically", alphabetize_projects_path(:state => state),
|
||||
:class => "alphabetize_link", :title => "Sort these projects alphabetically") %>
|
||||
</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") %>
|
||||
<%= link_to("By number of tasks", actionize_projects_path(:state => state),
|
||||
:class => "actionize_link", :title => "Sort these projects by number of tasks") %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="list-<%= state %>-projects" class="project-list">
|
||||
<%= render :partial => 'project_listing', :collection => project_state_group %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<div id="input_box">
|
||||
<div id="project_new_project_filler">
|
||||
<div id="project_new_project_container">
|
||||
|
||||
|
||||
<div id="toggle_project_new" class="hide_form">
|
||||
<a title="Hide new project form" accesskey="n">« Hide form</a>
|
||||
</div>
|
||||
|
|
@ -21,27 +21,27 @@
|
|||
:html=> { :id=>'project-form', :name=>'project', :class => 'inline-form'},
|
||||
:before => "$('#project_new_project_submit').block({message:null})",
|
||||
:complete => "$('#project_new_project_submit').unblock()") do -%>
|
||||
|
||||
|
||||
<div id="status"><%= error_messages_for('project') %></div>
|
||||
|
||||
|
||||
<label for="project_name">Name:</label><br />
|
||||
<%= text_field 'project', 'name', "tabindex" => 1 %><br />
|
||||
|
||||
|
||||
<label for="project_description">Description (optional):</label><br />
|
||||
<%= text_area 'project', 'description', "cols" => 30, "rows" => 4, "tabindex" => 2 %><br />
|
||||
|
||||
|
||||
<% 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) %>
|
||||
<br />
|
||||
<% end -%>
|
||||
|
||||
|
||||
<label for="default_tags">Default Tags (optional):</label><br />
|
||||
<%= text_field_tag("project[default_tags]", @project.default_tags, :tabindex => 4) %>
|
||||
<br />
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
<div class="submit_box">
|
||||
<div class="widgets">
|
||||
<button type="submit" class="positive" id="project_new_project_submit">
|
||||
|
|
@ -49,10 +49,10 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<br/><br/>
|
||||
<input id="go_to_project" type="checkbox" tabindex="5" name="go_to_project"/><label for="go_to_project"> Take me to the new project page</label><br />
|
||||
|
||||
<input id="go_to_project" type="checkbox" tabindex="5" name="go_to_project"/><label for="go_to_project"> Take me to the new project page</label><br />
|
||||
|
||||
<% end -%>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload=function(){
|
||||
Nifty("div#project_new_project_container","normal");
|
||||
}
|
||||
window.onload=function(){
|
||||
Nifty("div#project_new_project_container","normal");
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,32 +1,39 @@
|
|||
status_message = 'Project saved'
|
||||
page.notify :notice, status_message, 5.0
|
||||
if source_view_is :project_list
|
||||
if @state_changed
|
||||
page[dom_id(@project, 'container')].remove
|
||||
page.insert_html :bottom, "list-#{@project.state}-projects", :partial => 'project_listing', :object => @project
|
||||
if @saved
|
||||
status_message = 'Project saved'
|
||||
page.notify :notice, status_message, 5.0
|
||||
if source_view_is :project_list
|
||||
if @state_changed
|
||||
page[dom_id(@project, 'container')].remove
|
||||
page.insert_html :bottom, "list-#{@project.state}-projects", :partial => 'project_listing', :object => @project
|
||||
else
|
||||
page.replace_html dom_id(@project, 'container'), :partial => 'project_listing', :object => @project
|
||||
end
|
||||
page.sortable "list-#{@project.state}-projects", get_listing_sortable_options("list-#{@project.state}-projects")
|
||||
page.replace_html "active-projects-count", @active_projects_count
|
||||
page.replace_html "hidden-projects-count", @hidden_projects_count
|
||||
page.replace_html "completed-projects-count", @completed_projects_count
|
||||
|
||||
page.set_element_visible("list-hidden-projects-container", @hidden_projects_count > 0)
|
||||
page.set_element_visible("list-active-projects-container", @active_projects_count > 0)
|
||||
page.set_element_visible("list-completed-projects-container", @completed_projects_count > 0)
|
||||
else
|
||||
page.replace_html dom_id(@project, 'container'), :partial => 'project_listing', :object => @project
|
||||
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
|
||||
end
|
||||
page.sortable "list-#{@project.state}-projects", get_listing_sortable_options("list-#{@project.state}-projects")
|
||||
page.replace_html "active-projects-count", @active_projects_count
|
||||
page.replace_html "hidden-projects-count", @hidden_projects_count
|
||||
page.replace_html "completed-projects-count", @completed_projects_count
|
||||
|
||||
page.set_element_visible("list-hidden-projects-container", @hidden_projects_count > 0)
|
||||
page.set_element_visible("list-active-projects-container", @active_projects_count > 0)
|
||||
page.set_element_visible("list-completed-projects-container", @completed_projects_count > 0)
|
||||
page['default_project_name_id'].value = @project.name
|
||||
page['todo_project_name'].value = @project.name
|
||||
page.replace_html "project_name", @project.name
|
||||
|
||||
page.replace_html "sidebar", :file => 'sidebar/sidebar.html.erb'
|
||||
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};"
|
||||
page.show 'error_status'
|
||||
page.replace_html 'error_status', "#{error_messages_for('project')}"
|
||||
end
|
||||
|
||||
page.replace_html "sidebar", :file => 'sidebar/sidebar.html.erb'
|
||||
|
||||
page << "enable_rich_interaction();"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
page['default_project_name_id'].value = @project.name
|
||||
page['todo_project_name'].value = @project.name
|
||||
|
||||
# renew project auto complete array
|
||||
page << "var projectNames = #{project_names_for_autocomplete};"
|
||||
page << "enable_rich_interaction();"
|
||||
|
||||
status_message = "Name of project was changed"
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ page << "TracksForm.toggle_overlay();"
|
|||
page['new-recurring-todo'].hide
|
||||
page['edit-recurring-todo'].replace_html :partial => 'recurring_todos/edit_form'
|
||||
page['edit-recurring-todo'].show
|
||||
page << "enable_rich_interaction();"
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@ 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 context and project
|
||||
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
|
||||
|
||||
# replace old recurring todo with updated todo
|
||||
page.replace dom_id(@recurring_todo), :partial => 'recurring_todos/recurring_todo', :locals => { :recurring_todo => @recurring_todo }
|
||||
page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
|
||||
|
|
@ -19,4 +15,4 @@ if @saved
|
|||
else
|
||||
page.show 'edit_status'
|
||||
page.replace_html 'edit_status', "#{error_messages_for('recurring_todo')}"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@
|
|||
-%>
|
||||
<div id="todo_new_action_container">
|
||||
|
||||
<div id="toggle_action_new" class="hide_form">
|
||||
<a title="Hide new action form" accesskey="n" href="#">« Hide form</a>
|
||||
<div id="toggle_forms" class="toggle_forms">
|
||||
<a title="Hide new action form" accesskey="n" href="#" id="toggle_action_new">« Hide form</a> |
|
||||
<a title="Toggle single/multi new action form" accesskey="m" href="#" id="toggle_multi">Add multiple next actions</a>
|
||||
</div>
|
||||
|
||||
<div id="todo_new_action" style="display:block">
|
||||
|
|
@ -17,7 +18,7 @@
|
|||
:html=> { :id=>'todo-form-new-action', :name=>'todo', :class => 'inline-form' },
|
||||
:before => "$('#todo_new_action_submit').block({message:null})",
|
||||
:complete => "$('#todo_new_action_submit').unblock()",
|
||||
:condition => "askIfNewContextProvided()") do -%>
|
||||
:condition => "askIfNewContextProvided('')") do -%>
|
||||
|
||||
<div id="status"><%= error_messages_for("item", :object_name => 'action') %></div>
|
||||
|
||||
|
|
@ -28,11 +29,13 @@
|
|||
<%= text_area( "todo", "notes", "cols" => 29, "rows" => 6, "tabindex" => 2) %>
|
||||
|
||||
<input id="default_project_name_id" name="default_project_name" type="hidden" value="<%=@initial_project_name-%>" />
|
||||
|
||||
<label for="todo_project_name">Project</label>
|
||||
<input id="todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="<%= @initial_project_name %>" />
|
||||
<div class="page_name_auto_complete" id="project_list" style="display:none"></div>
|
||||
|
||||
<input id="default_context_name_id" name="default_context_name" type="hidden" value="<%=@initial_context_name-%>" />
|
||||
|
||||
<label for="todo_context_name">Context</label>
|
||||
<input id="todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="<%= @initial_context_name %>" />
|
||||
<div class="page_name_auto_complete" id="context_list" style="display:none"></div>
|
||||
|
|
@ -63,6 +66,49 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<% end -%>
|
||||
|
||||
<% end # form-remote-tag -%>
|
||||
</div>
|
||||
|
||||
<div id="todo_multi_add" style="display:none">
|
||||
<% form_remote_tag(
|
||||
:url => todos_path, :method => :post,
|
||||
:html=> { :id=>'todo-form-multi-new-action', :name=>'todo', :class => 'inline-form' },
|
||||
:before => "$('#todo_multi_new_action_submit').block({message:null})",
|
||||
:complete => "$('#todo_multi_new_action_submit').unblock()",
|
||||
:condition => "askIfNewContextProvided('multi_')") do -%>
|
||||
|
||||
<div id="multiple_status"><%= error_messages_for("item", :object_name => 'action') %></div>
|
||||
|
||||
<label for="todo_notes">Multiple next actions (one on each line)</label>
|
||||
<%= text_area( "todo", "multiple_todos", "cols" => 29, "rows" => 6, "tabindex" => 2) %>
|
||||
|
||||
<input id="default_project_name_id" name="default_project_name" type="hidden" value="<%=@initial_project_name-%>" />
|
||||
|
||||
<label for="todo_project_name">Project for all actions</label>
|
||||
<input id="multi_todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="<%= @initial_project_name %>" />
|
||||
<div class="page_name_auto_complete" id="project_list" style="display:none"></div>
|
||||
|
||||
<input id="default_context_name_id" name="default_context_name" type="hidden" value="<%=@initial_context_name-%>" />
|
||||
|
||||
<label for="todo_context_name">Context for all actions</label>
|
||||
<input id="multi_todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="<%= @initial_context_name %>" />
|
||||
<div class="page_name_auto_complete" id="context_list" style="display:none"></div>
|
||||
|
||||
<label for="tag_list">Tags for all actions (sep. with commas)</label>
|
||||
<%= text_field_tag "multi_tag_list", @default_tags, :name=>:tag_list, :size => 30, :tabindex => 5 %>
|
||||
<%= content_tag("div", "", :id => "tag_list_auto_complete", :class => "auto_complete") %>
|
||||
|
||||
<div class="submit_box">
|
||||
<div class="widgets">
|
||||
<button type="submit" class="positive" id="todo_multi_new_action_submit" tabindex="8">
|
||||
<%= image_tag("accept.png", :alt => "") %>Add actions
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% end -%>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1 +1,6 @@
|
|||
<%= flashobject_tag "open-flash-chart.swf", :query_params => { 'width' => width, 'height' => height, 'data' => data}, :size => "#{width}x#{height}", :parameters => { 'allowScriptAccess' => 'sameDomain', 'wmode' => 'transparent'}, :class_name => 'open-flash-chart', :flash_id => 'chart' %>
|
||||
<% @swf_count ||= 0 -%>
|
||||
<div class="open-flash-chart"><%= swf_tag "open-flash-chart.swf",
|
||||
:flashvars => { 'width' => width, 'height' => height, 'data' => data},
|
||||
:parameters => { 'allowScriptAccess' => 'sameDomain', 'wmode' => 'transparent'},
|
||||
:div_id => "chart_#{@swf_count+=1}",
|
||||
:size => "#{width}x#{height}" %></div>
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
<%= link_to t.name,
|
||||
{:controller => "todos", :action => "tag", :id => t.name},
|
||||
{:style => "font-size: " + (9 + 2*(t.count.to_i-@tags_min)/@tags_divisor).to_s + "pt",
|
||||
:title => t.count+" actions"}
|
||||
:title => t.count.to_s+" actions"}
|
||||
-%> <%
|
||||
end
|
||||
end-%>
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
<%= link_to t.name,
|
||||
{:controller => "todos", :action => "tag", :id => t.name},
|
||||
{:style => "font-size: " + (9 + 2*(t.count.to_i-@tags_min_90days)/@tags_divisor_90days).to_s + "pt",
|
||||
:title => t.count+" actions"}
|
||||
:title => t.count.to_s+" actions"}
|
||||
-%> <%
|
||||
end
|
||||
end-%>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
<p>You have <%= @projects.count%> projects.
|
||||
Of those <%= @projects.count(:conditions => "state = 'active'")%> are active projects,
|
||||
<%= @projects.count(:conditions => "state = 'hidden'")%> hidden projects and
|
||||
<%= @projects.count(:conditions => "state = 'completed'")%> completed projects</p>
|
||||
Of those <%= @projects.active.count%> are active projects,
|
||||
<%= @projects.hidden.count%> hidden projects and
|
||||
<%= @projects.completed.count%> completed projects</p>
|
||||
|
||||
<p>You have <%= @contexts.count%> contexts.
|
||||
Of those <%= @contexts.count(:conditions => ["hide = ?", false])%> are visible contexts and
|
||||
<%= @contexts.count(:conditions => ["hide = ?", true]) %> are hidden contexts
|
||||
Of those <%= @contexts.active.count%> are visible contexts and
|
||||
<%= @contexts.hidden.count%> are hidden contexts
|
||||
|
||||
<% unless @actions.empty? -%>
|
||||
<p>You have <%= @actions.count(:conditions => "completed_at IS NULL") %> incomplete actions
|
||||
of which <%= @actions.count(:conditions => "completed_at IS NULL AND NOT show_from IS NULL") %> are deferred actions. </p>
|
||||
<p>Since your first action on <%= format_date(@first_action.created_at) %>
|
||||
you have a total of <%= @actions.count %> actions.
|
||||
<%= @actions.completed.count %> of these are completed.
|
||||
|
||||
<p>Since your first action on <%= format_date(@first_action.created_at) %>
|
||||
you have a total of <%= @actions.count %> actions.
|
||||
<%= @actions.count(:conditions => "NOT completed_at IS NULL") %> of these are completed.
|
||||
<p>You have <%= @actions.not_completed.count %> incomplete actions
|
||||
of which <%= @actions.deferred.count %> are deferred actions
|
||||
in the tickler and <%= @actions.blocked.count %> are dependent on the completion of other actions.
|
||||
. </p>
|
||||
|
||||
<p>You have <%= @tags_count-%> tags placed on actions. Of those tags,
|
||||
<%= @unique_tags_count -%> are unique.
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ end -%>
|
|||
-%><span class="m_t">
|
||||
<% end -%>
|
||||
<%= date_span -%> <%= link_to mobile_todo.description, todo_path(mobile_todo, :format => 'm') -%>
|
||||
<% if mobile_todo.notes? %>
|
||||
<%= link_to(image_tag("mobile_notes.png", :border => "0"), mobile_todo_show_notes_path(mobile_todo, :format => 'm')) -%>
|
||||
<% end %>
|
||||
<% if parent_container_type == 'context' or parent_container_type == 'tag' -%>
|
||||
<%= "<span class=\"prj\"> (" +
|
||||
link_to(mobile_todo.project.name, project_path(mobile_todo.project, :format => 'm')) +
|
||||
|
|
|
|||
50
app/views/todos/create_multiple.js.rjs
Normal file
50
app/views/todos/create_multiple.js.rjs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
if @saved
|
||||
page.hide 'multiple_status'
|
||||
|
||||
status_message = 'Added new next action'
|
||||
status_message += 's' if @todos.size > 1
|
||||
status_message = 'Added new project / ' + status_message if @new_project_created
|
||||
status_message = 'Added new context / ' + status_message if @new_context_created
|
||||
page.notify :notice, status_message, 5.0
|
||||
|
||||
page['badge_count'].replace_html @down_count
|
||||
|
||||
# reset form and set focus to first field
|
||||
page.send :record, "$('#todo-form-multi-new-action').clearForm();$('#todo-form-multi-new-action input:text:first').focus();"
|
||||
|
||||
# set defaults of form
|
||||
page.send :record, "$('#multi_todo_context_name').val('#{@initial_context_name}');"
|
||||
page.send :record, "$('#multi_todo_project_name').val('#{@initial_project_name}');"
|
||||
page.send :record, "$('#multi_tag_list').val('#{@default_tags}');"
|
||||
|
||||
if should_show_new_item()
|
||||
if @new_context_created
|
||||
page.insert_html :top, 'display_box', :partial => 'contexts/context', :locals => { :context => @todo.context, :collapsible => true }
|
||||
else
|
||||
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" if source_view_is_one_of(:todo, :deferred, :tag)
|
||||
|
||||
@todos.each do |todo|
|
||||
page.insert_html :bottom, item_container_id(todo), :partial => 'todos/todo', :locals => { :todo => todo, :parent_container_type => parent_container_type, :source_view => @source_view }
|
||||
page.visual_effect :highlight, dom_id(todo), :duration => 3
|
||||
end
|
||||
|
||||
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
|
||||
end
|
||||
if (source_view_is :project and @todo.pending?) or (source_view_is :deferred)
|
||||
page['tickler-empty-nd'].hide # For some reason this does not work: page['tickler-empty-nd'].hide if (@todo.pending? or (source_view_is :deferred))
|
||||
end
|
||||
end
|
||||
# make sure the behavior of the new/updated todo is enabled
|
||||
page << "enable_rich_interaction();"
|
||||
else
|
||||
page.show 'multiple_status'
|
||||
# add error about missing todo description that is not available in @todos
|
||||
@multiple_error = content_tag(:div, content_tag(:p, @multiple_error), {:class => 'errorExplanation', :id => 'errorExplanation'}) unless @multiple_error.blank?
|
||||
error_messages = @multiple_error || ""
|
||||
# add errors of individual @todos
|
||||
@todos.each do |todo|
|
||||
@todo_i = todo
|
||||
error_messages += error_messages_for('todo_i', :object_name => 'action')
|
||||
end
|
||||
page.replace_html 'multiple_status', error_messages
|
||||
end
|
||||
5
app/views/todos/mobile_show_notes.rhtml
Normal file
5
app/views/todos/mobile_show_notes.rhtml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<h2>Next action description (<%= link_to "Go back", @return_path %>)</h2>
|
||||
<%= link_to @todo.description, todo_path(@todo, :format => 'm') -%></h2>
|
||||
<h2>Notes</h2>
|
||||
<%= format_note(@todo.notes) %>
|
||||
<%= link_to "Back", @return_path %>
|
||||
|
|
@ -4,6 +4,7 @@ if @saved
|
|||
status_message += ' to tickler' if @todo.deferred?
|
||||
status_message = 'Added new project / ' + status_message if @new_project_created
|
||||
status_message = 'Added new context / ' + status_message if @new_context_created
|
||||
status_message = @message || status_message
|
||||
page.notify :notice, status_message, 5.0
|
||||
|
||||
if source_view_is_one_of(:todo, :context, :tag)
|
||||
|
|
@ -46,12 +47,18 @@ if @saved
|
|||
page.replace_html("badge_count", @down_count) if source_view_is :todo
|
||||
|
||||
# show todo in context
|
||||
page.delay(0.5) do
|
||||
page.delay(0.3) do
|
||||
page.call "todoItems.ensureContainerHeight", "c#{@original_item_context_id}items"
|
||||
if source_view_is_one_of(:todo, :tag) && @todo.active?
|
||||
page.call "todoItems.ensureContainerHeight", "c#{@todo.context_id}items"
|
||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||
end
|
||||
if @context_changed
|
||||
source_view do |from|
|
||||
from.todo {page << "$('#c#{@todo.context_id} h2').effect('highlight', {}, 3000)" }
|
||||
from.tag {page << "$('#c#{@todo.context_id} h2').effect('highlight')" }
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if @original_item_was_deferred && source_view_is(:tag)
|
||||
|
|
@ -88,12 +95,13 @@ if @saved
|
|||
page.replace_html "badge_count", @down_count
|
||||
else
|
||||
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 }
|
||||
: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
|
||||
|
|
@ -146,7 +154,7 @@ if @saved
|
|||
@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 }
|
||||
:locals => { :todo => p, :parent_container_type => parent_container_type }
|
||||
page << "}"
|
||||
end
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1,179 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.47pre4 r22446"
|
||||
sodipodi:docname="predecessor.svg"
|
||||
inkscape:export-filename="/Users/epall/code/tracks/public/images/add_successor_on.png"
|
||||
inkscape:export-xdpi="9.6245861"
|
||||
inkscape:export-ydpi="9.6245861">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient5258">
|
||||
<stop
|
||||
style="stop-color:#28343c;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5260" />
|
||||
<stop
|
||||
style="stop-color:#0096ff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop5262" />
|
||||
</linearGradient>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutM"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="TriangleOutM"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path3768"
|
||||
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||
transform="scale(0.4)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutS"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="TriangleOutS"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path3771"
|
||||
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||
transform="scale(0.2)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible;">
|
||||
<path
|
||||
id="path3649"
|
||||
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(0.6) rotate(180) translate(0,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Arrow1Mend"
|
||||
style="overflow:visible;">
|
||||
<path
|
||||
id="path3631"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
|
||||
transform="scale(0.4) rotate(180) translate(10,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path3765"
|
||||
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||
transform="scale(0.8)" />
|
||||
</marker>
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect2826"
|
||||
is_visible="true" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect2822"
|
||||
is_visible="true" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="29.11825 : 616.44767 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
id="perspective10" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5258"
|
||||
id="linearGradient5264"
|
||||
x1="142.18495"
|
||||
y1="261.65085"
|
||||
x2="142.54144"
|
||||
y2="174.1613"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter5282">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.63631671"
|
||||
id="feGaussianBlur5284" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.3579107"
|
||||
inkscape:cx="109.12131"
|
||||
inkscape:cy="846.66442"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g5238"
|
||||
showgrid="false"
|
||||
inkscape:window-width="675"
|
||||
inkscape:window-height="547"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g5238"
|
||||
transform="translate(-23.335007,-4.8042662)"
|
||||
style="fill:none;stroke:#ff0000;stroke-width:0.7;stroke-miterlimit:4;stroke-dasharray:none">
|
||||
<path
|
||||
id="path5244"
|
||||
style="fill:url(#linearGradient5264);fill-opacity:1;stroke:#3a3a3a;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;filter:url(#filter5282)"
|
||||
d="m 124.05478,198.95569 25.59428,-0.003 -38.08971,39.75365 -36.022918,-39.75365 23.518346,0.003 0,-11.2623 c 0,-11.56985 3.814362,-23.72799 12.843752,-32.8125 9.02939,-9.08451 22.71756,-14.28125 39.34375,-14.28125 15.89659,0 28.81237,4.90636 36.875,13.9375 8.06263,9.03114 10.40625,20.50418 10.40625,30.9375 l 0,97.6875 -25,0 0,-97.6875 c 0,-6.68688 -1.49995,-11.41088 -4.0625,-14.28125 -2.56255,-2.87037 -7.00817,-5.59375 -18.21875,-5.59375 -11.69655,0 -17.86524,3.15497 -21.59375,6.90625 -3.72851,3.75128 -5.59375,8.92322 -5.59375,15.1875 l 0,11.2623 z"
|
||||
sodipodi:nodetypes="ccccccscsccccssscc"
|
||||
transform="matrix(-1,0,0,1,274.05996,0)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.1 KiB |
|
|
@ -1,175 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.47pre4 r22446"
|
||||
sodipodi:docname="predecessor.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient5258">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5260" />
|
||||
<stop
|
||||
style="stop-color:#ececec;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop5262" />
|
||||
</linearGradient>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutM"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="TriangleOutM"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path3768"
|
||||
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||
transform="scale(0.4)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutS"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="TriangleOutS"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path3771"
|
||||
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||
transform="scale(0.2)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible;">
|
||||
<path
|
||||
id="path3649"
|
||||
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(0.6) rotate(180) translate(0,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Arrow1Mend"
|
||||
style="overflow:visible;">
|
||||
<path
|
||||
id="path3631"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
|
||||
transform="scale(0.4) rotate(180) translate(10,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path3765"
|
||||
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
|
||||
transform="scale(0.8)" />
|
||||
</marker>
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect2826"
|
||||
is_visible="true" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect2822"
|
||||
is_visible="true" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="29.11825 : 616.44767 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
id="perspective10" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5258"
|
||||
id="linearGradient5264"
|
||||
x1="142.54144"
|
||||
y1="285.28931"
|
||||
x2="142.54144"
|
||||
y2="174.1613"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter5282">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.63631671"
|
||||
id="feGaussianBlur5284" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.51319034"
|
||||
inkscape:cx="156.62969"
|
||||
inkscape:cy="949.8091"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g5238"
|
||||
showgrid="false"
|
||||
inkscape:window-width="675"
|
||||
inkscape:window-height="547"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g5238"
|
||||
transform="translate(-23.335007,-4.8042662)"
|
||||
style="fill:none;stroke:#ff0000;stroke-width:0.7;stroke-miterlimit:4;stroke-dasharray:none">
|
||||
<path
|
||||
id="path5244"
|
||||
style="fill:url(#linearGradient5264);stroke:#3a3a3a;stroke-width:4;stroke-miterlimit:4;stroke-dasharray:none;marker-start:none;stroke-opacity:1;fill-opacity:1;filter:url(#filter5282)"
|
||||
d="m 124.05478,198.95569 25.59428,-0.003 -38.08971,39.75365 -36.022918,-39.75365 23.518346,0.003 0,-11.2623 c 0,-11.56985 3.814362,-23.72799 12.843752,-32.8125 9.02939,-9.08451 22.71756,-14.28125 39.34375,-14.28125 15.89659,0 28.81237,4.90636 36.875,13.9375 8.06263,9.03114 10.40625,20.50418 10.40625,30.9375 l 0,97.6875 -25,0 0,-97.6875 c 0,-6.68688 -1.49995,-11.41088 -4.0625,-14.28125 -2.56255,-2.87037 -7.00817,-5.59375 -18.21875,-5.59375 -11.69655,0 -17.86524,3.15497 -21.59375,6.90625 -3.72851,3.75128 -5.59375,8.92322 -5.59375,15.1875 l 0,11.2623 z"
|
||||
sodipodi:nodetypes="ccccccscsccccssscc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.9 KiB |
|
|
@ -1,9 +1,9 @@
|
|||
<%
|
||||
rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
|
||||
rerun_opts = rerun.to_s.strip.empty? ? "--format progress " : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
|
||||
requires = "-r features/support/env.rb -r features/step_definitions"
|
||||
std_opts = "#{rerun_opts} #{requires} --format rerun --out rerun.txt --strict --tags ~@wip"
|
||||
rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
|
||||
std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} --strict --tags ~@wip"
|
||||
%>
|
||||
default: <%= std_opts %> --tags ~@selenium
|
||||
selenium: <%= std_opts %> --tags @selenium -r features/support/selenium.rb
|
||||
default: <%= std_opts %> features --tags ~@selenium
|
||||
selenium: <%= std_opts %> features --tags @selenium
|
||||
wip: --tags @wip:3 --wip features
|
||||
rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ Rails::Initializer.run do |config|
|
|||
config.action_controller.session_store = :active_record_store
|
||||
|
||||
config.action_controller.session = {
|
||||
:session_key => '_tracks_session_id',
|
||||
:secret => SITE_CONFIG['salt'] * (30.0 / SITE_CONFIG['salt'].length).ceil #must be at least 30 characters
|
||||
:key => '_tracks_session_id',
|
||||
:secret => SITE_CONFIG['salt'] * (30.0 / SITE_CONFIG['salt'].length).ceil #must be at least 30 characters
|
||||
}
|
||||
|
||||
config.action_controller.relative_url_root = SITE_CONFIG['subdir'] if SITE_CONFIG['subdir']
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ config.action_controller.allow_forgery_protection = false
|
|||
# ActionMailer::Base.deliveries array.
|
||||
config.action_mailer.delivery_method = :test
|
||||
|
||||
config.gem 'cucumber-rails', :lib => false, :version => '>=0.2.3' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails'))
|
||||
config.gem 'webrat', :lib => false, :version => '>=0.6.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat'))
|
||||
config.gem 'rspec', :lib => false, :version => '>=1.2.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec'))
|
||||
config.gem 'rspec-rails', :lib => false, :version => '>=1.2.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails'))
|
||||
config.gem 'cucumber-rails', :lib => false, :version => '>=0.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails'))
|
||||
config.gem 'database_cleaner', :lib => false, :version => '>=0.5.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner'))
|
||||
config.gem 'webrat', :lib => false, :version => '>=0.7.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat'))
|
||||
config.gem 'rspec', :lib => false, :version => '>=1.3.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec'))
|
||||
config.gem 'rspec-rails', :lib => false, :version => '>=1.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails'))
|
||||
|
||||
|
|
|
|||
|
|
@ -14,4 +14,4 @@ config.action_controller.perform_caching = false
|
|||
config.action_mailer.raise_delivery_errors = false
|
||||
|
||||
# Unique cookies
|
||||
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:session_key] = "TrackDev"
|
||||
config.action_controller.session = { :key => 'TracksDev' }
|
||||
|
|
@ -21,10 +21,11 @@ config.action_controller.allow_forgery_protection = false
|
|||
# ActionMailer::Base.deliveries array.
|
||||
config.action_mailer.delivery_method = :test
|
||||
|
||||
config.gem 'cucumber-rails', :lib => false, :version => '>=0.2.3' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails'))
|
||||
config.gem 'webrat', :lib => false, :version => '>=0.6.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat'))
|
||||
config.gem 'rspec', :lib => false, :version => '>=1.2.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec'))
|
||||
config.gem 'rspec-rails', :lib => false, :version => '>=1.2.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails'))
|
||||
|
||||
config.gem 'database_cleaner', :lib => false, :version => '>=0.2.3' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner'))
|
||||
# Unique cookies
|
||||
config.action_controller.session = { :key => 'TracksSelenium' }
|
||||
|
||||
config.gem 'cucumber-rails', :lib => false, :version => '>=0.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails'))
|
||||
config.gem 'database_cleaner', :lib => false, :version => '>=0.5.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner'))
|
||||
config.gem 'webrat', :lib => false, :version => '>=0.7.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat'))
|
||||
config.gem 'rspec', :lib => false, :version => '>=1.3.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec'))
|
||||
config.gem 'rspec-rails', :lib => false, :version => '>=1.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails'))
|
||||
|
|
@ -19,19 +19,8 @@ config.action_mailer.delivery_method = :test
|
|||
# Disable request forgery protection in test environment
|
||||
config.action_controller.allow_forgery_protection = false
|
||||
|
||||
# We store more than 4K of data in the session during some tests.
|
||||
# Override the hard-coded cookie session store to use a memory store for tests.
|
||||
# See http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/5519ca7fd4dde3c1
|
||||
class ActionController::RackRequest
|
||||
DEFAULT_SESSION_OPTIONS = {
|
||||
#:database_manager => CGI::Session::MemoryStore, # store data in memory
|
||||
:prefix => "ruby_sess.", # prefix session file names
|
||||
:session_path => "/", # available to all paths in app
|
||||
:session_key => "_session_id",
|
||||
:cookie_only => false,
|
||||
:session_http_only=> true
|
||||
}
|
||||
end
|
||||
# Unique cookies
|
||||
config.action_controller.session = { :key => 'TracksTest' }
|
||||
|
||||
# Overwrite the default settings for fixtures in tests. See Fixtures
|
||||
# for more details about these settings.
|
||||
|
|
@ -50,7 +39,7 @@ config.gem "flexmock"
|
|||
config.gem "ZenTest", :lib => "zentest", :version => ">=4.0.0"
|
||||
config.gem "hpricot"
|
||||
config.gem "hoe"
|
||||
config.gem "rspec", :lib => false, :version => ">= 1.2.2"
|
||||
config.gem "rspec-rails", :lib => false, :version => ">=1.2.2"
|
||||
config.gem "webrat", :lib => false, :version => ">=0.4.3"
|
||||
config.gem 'webrat', :lib => false, :version => '>=0.7.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat'))
|
||||
config.gem 'rspec', :lib => false, :version => '>=1.3.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec'))
|
||||
config.gem 'rspec-rails', :lib => false, :version => '>=1.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails'))
|
||||
config.gem "thoughtbot-factory_girl", :lib => "factory_girl", :source => "http://gems.github.com"
|
||||
|
|
|
|||
|
|
@ -1,16 +1,8 @@
|
|||
ActionController::Routing::Routes.draw do |map|
|
||||
map.with_options :controller => 'login' do |login|
|
||||
login.login 'login', :action => 'login'
|
||||
login.login_cas 'login_cas', :action => 'login_cas'
|
||||
login.formatted_login 'login.:format', :action => 'login'
|
||||
login.logout 'logout', :action => 'logout'
|
||||
login.formatted_logout 'logout.:format', :action => 'logout'
|
||||
end
|
||||
|
||||
map.resources :users,
|
||||
:member => {:change_password => :get, :update_password => :post,
|
||||
:change_auth_type => :get, :update_auth_type => :post, :complete => :get,
|
||||
:refresh_token => :post }
|
||||
:member => {:change_password => :get, :update_password => :post,
|
||||
:change_auth_type => :get, :update_auth_type => :post, :complete => :get,
|
||||
:refresh_token => :post }
|
||||
map.with_options :controller => "users" do |users|
|
||||
users.signup 'signup', :action => "new"
|
||||
end
|
||||
|
|
@ -27,9 +19,11 @@ ActionController::Routing::Routes.draw do |map|
|
|||
projects.resources :todos, :name_prefix => "project_"
|
||||
end
|
||||
|
||||
map.resources :notes
|
||||
|
||||
map.resources :todos,
|
||||
:member => {:toggle_check => :put, :toggle_star => :put},
|
||||
:collection => {:check_deferred => :post, :filter_to_context => :post, :filter_to_project => :post}
|
||||
:member => {:toggle_check => :put, :toggle_star => :put},
|
||||
:collection => {:check_deferred => :post, :filter_to_context => :post, :filter_to_project => :post}
|
||||
map.with_options :controller => "todos" do |todos|
|
||||
todos.home '', :action => "index"
|
||||
todos.tickler 'tickler', :action => "list_deferred"
|
||||
|
|
@ -54,27 +48,45 @@ ActionController::Routing::Routes.draw do |map|
|
|||
todos.mobile 'mobile', :action => "index", :format => 'm'
|
||||
todos.mobile_abbrev 'm', :action => "index", :format => 'm'
|
||||
todos.mobile_abbrev_new 'm/new', :action => "new", :format => 'm'
|
||||
|
||||
todos.mobile_todo_show_notes 'todos/notes/:id.m', :action => "show_notes", :format => 'm'
|
||||
todos.todo_show_notes 'todos/notes/:id', :action => "show_notes"
|
||||
end
|
||||
map.root :controller => 'todos' # Make OpenID happy because it needs #root_url defined
|
||||
|
||||
map.resources :notes
|
||||
map.feeds 'feeds', :controller => 'feedlist', :action => 'index'
|
||||
map.feeds 'feeds.m', :controller => 'feedlist', :action => 'index', :format => 'm'
|
||||
|
||||
if Rails.env == 'test'
|
||||
map.connect '/selenium_helper/login', :controller => 'selenium_helper', :action => 'login'
|
||||
end
|
||||
|
||||
map.preferences 'preferences', :controller => 'preferences', :action => 'index'
|
||||
map.integrations 'integrations', :controller => 'integrations', :action => 'index'
|
||||
map.search_plugin '/integrations/search_plugin.xml', :controller => 'integrations', :action => 'search_plugin', :format => 'xml'
|
||||
map.google_gadget '/integrations/google_gadget.xml', :controller => 'integrations', :action => 'google_gadget', :format => 'xml'
|
||||
map.stats 'stats', :controller => 'stats', :action => 'index'
|
||||
|
||||
map.resources :recurring_todos,
|
||||
:member => {:toggle_check => :put, :toggle_star => :put}
|
||||
map.recurring_todos 'recurring_todos', :controller => 'recurring_todos', :action => 'index'
|
||||
|
||||
map.with_options :controller => 'login' do |login|
|
||||
login.login 'login', :action => 'login'
|
||||
login.login_cas 'login_cas', :action => 'login_cas'
|
||||
login.formatted_login 'login.:format', :action => 'login'
|
||||
login.logout 'logout', :action => 'logout'
|
||||
login.formatted_logout 'logout.:format', :action => 'logout'
|
||||
end
|
||||
|
||||
map.with_options :controller => "feedlist" do |fl|
|
||||
fl.mobile_feeds 'feeds.m', :action => 'index', :format => 'm'
|
||||
fl.feeds 'feeds', :action => 'index'
|
||||
end
|
||||
|
||||
map.with_options :controller => "integrations" do |i|
|
||||
i.integrations 'integrations', :action => 'index'
|
||||
i.rest_api_docs 'integrations/rest_api', :action => "rest_api"
|
||||
i.search_plugin 'integrations/search_plugin.xml', :controller => 'integrations', :action => 'search_plugin', :format => 'xml'
|
||||
i.google_gadget 'integrations/google_gadget.xml', :controller => 'integrations', :action => 'google_gadget', :format => 'xml'
|
||||
end
|
||||
|
||||
map.preferences 'preferences', :controller => 'preferences', :action => 'index'
|
||||
map.stats 'stats', :controller => 'stats', :action => 'index'
|
||||
map.search 'search', :controller => 'search', :action => 'index'
|
||||
map.data 'data', :controller => 'data', :action => 'index'
|
||||
|
||||
if Rails.env == 'test'
|
||||
map.connect '/selenium_helper/login', :controller => 'selenium_helper', :action => 'login'
|
||||
end
|
||||
|
||||
# Install the default route as the lowest priority.
|
||||
map.connect ':controller/:action/:id'
|
||||
|
||||
|
|
|
|||
9
db/migrate/20100502162317_add_index_to_todo_state.rb
Normal file
9
db/migrate/20100502162317_add_index_to_todo_state.rb
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class AddIndexToTodoState < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_index :todos, :state
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_index :todos, :state
|
||||
end
|
||||
end
|
||||
|
|
@ -13,7 +13,7 @@ Feature: Manage contexts
|
|||
@selenium
|
||||
Scenario: In place edit of context name
|
||||
Given I have a context called "Errands"
|
||||
When I visits the context page for "Errands"
|
||||
When I visit the context page for "Errands"
|
||||
And I edit the context name in place to be "OutAndAbout"
|
||||
Then I should see the context name is "OutAndAbout"
|
||||
When I go to the contexts page
|
||||
|
|
@ -21,7 +21,7 @@ Feature: Manage contexts
|
|||
And he should see that a context named "OutAndAbout" is present
|
||||
|
||||
@selenium
|
||||
Scenario: Delete context from context page
|
||||
Scenario: Delete context from context page should update badge
|
||||
Given I have a context called "@computer"
|
||||
When I go to the contexts page
|
||||
Then the badge should show 1
|
||||
|
|
@ -48,3 +48,23 @@ Feature: Manage contexts
|
|||
And he should see that a context named "@laptop" is not present
|
||||
And he should see that a context named "@ipad" is present
|
||||
And the badge should show 1
|
||||
|
||||
@selenium
|
||||
Scenario: Add new context
|
||||
Given I have the following contexts
|
||||
| name | hide |
|
||||
| @ipad | true |
|
||||
| @home | false |
|
||||
When I go to the contexts page
|
||||
And I add a new context "@phone"
|
||||
Then I should see the context "@phone" under "active"
|
||||
|
||||
@selenium
|
||||
Scenario: Add new hidden context
|
||||
Given I have the following contexts
|
||||
| name | hide |
|
||||
| @ipad | true |
|
||||
| @home | false |
|
||||
When I go to the contexts page
|
||||
And I add a new hidden context "@hidden"
|
||||
Then I should see the context "@hidden" under "hidden"
|
||||
|
|
|
|||
|
|
@ -22,4 +22,17 @@ Feature: dependencies
|
|||
Then I should see "Todo 2" within the dependencies of "Todo 1"
|
||||
And I should see "Todo 3" within the dependencies of "Todo 1"
|
||||
When I expand the dependencies of "Todo 2"
|
||||
Then I should see "Todo 3" within the dependencies of "Todo 2"
|
||||
Then I should see "Todo 3" within the dependencies of "Todo 2"
|
||||
|
||||
@selenium, @wip
|
||||
Scenario: Adding dependency with comma to todo # for #975
|
||||
Given I have a context called "@pc"
|
||||
And I have a project "dependencies" that has the following todos
|
||||
| description | context |
|
||||
| test,1, 2,3 | @pc |
|
||||
| test me | @pc |
|
||||
When I visit the "dependencies" project
|
||||
And I drag "test me" to "test,1, 2,3"
|
||||
Then the dependencies of "test me" should include "test,1, 2,3"
|
||||
When I edit the dependency of "test me" to '"test,1, 2,3" <"@pc"; "dependencies">,"test,1, 2,3" <"@pc"; "dependencies">'
|
||||
Then there should not be an error
|
||||
|
|
@ -6,9 +6,9 @@ Feature: Existing user logging in
|
|||
|
||||
Background:
|
||||
Given the following user records
|
||||
| login | password | is_admin |
|
||||
| testuser | secret | false |
|
||||
| admin | secret | true |
|
||||
| login | password | is_admin | first_name | last_name |
|
||||
| testuser | secret | false | Test | User |
|
||||
| admin | secret | true | Admin | User |
|
||||
|
||||
Scenario Outline: Succesfull and unsuccesfull login
|
||||
When I go to the login page
|
||||
|
|
@ -21,6 +21,32 @@ Feature: Existing user logging in
|
|||
| admin | secret | redirected to the home page | Login successful |
|
||||
| admin | wrong | on the login page | Login unsuccessful |
|
||||
|
||||
Scenario: Accessing a secured page when not logged in
|
||||
When I go to the home page
|
||||
Then I should be redirected to the login page
|
||||
Scenario Outline: Unauthorized users cannot access Tracks and need to log in first
|
||||
Given there exists a project called "top secret" for user "testuser"
|
||||
And there exists a context called "@secret location" for user "testuser"
|
||||
When I go to the <page>
|
||||
Then I should be redirected to the login page
|
||||
When I submit the login form as user "testuser" with password "secret"
|
||||
Then I should be redirected to the <next page>
|
||||
And I should see "<logout>"
|
||||
|
||||
Examples:
|
||||
| page | next page | logout |
|
||||
| home page | home page | Logout (Test User) |
|
||||
| contexts page | contexts page | Logout (Test User) |
|
||||
| projects page | projects page | Logout (Test User) |
|
||||
| notes page | notes page | Logout (Test User) |
|
||||
| repeating todos page | repeating todos page | Logout (Test User) |
|
||||
| statistics page | statistics page | Logout (Test User) |
|
||||
| manage users page | manage users page | 401 Unauthorized |
|
||||
| integrations page | integrations page | Logout (Test User) |
|
||||
| starred page | starred page | Logout (Test User) |
|
||||
| tickler page | tickler page | Logout (Test User) |
|
||||
| calendar page | calendar page | Logout (Test User) |
|
||||
| feeds page | feeds page | Logout (Test User) |
|
||||
| preference page | preference page | Logout (Test User) |
|
||||
| export page | export page | Logout (Test User) |
|
||||
| rest api docs page | rest api docs page | Logout (Test User) |
|
||||
| search page | search page | Logout (Test User) |
|
||||
| "top secret" project for user "testuser" | "top secret" project for user "testuser" | Logout (Test User) |
|
||||
| context page for "@secret location" for user "testuser" | context page for "@secret location" for user "testuser" | Logout (Test User) |
|
||||
|
|
|
|||
|
|
@ -16,4 +16,40 @@ Feature: Manage a project
|
|||
When I visit the "manage me" project
|
||||
And I edit the project description to "_successfull outcome_: project is *done*"
|
||||
Then I should see the italic text "successfull outcome" in the project description
|
||||
And I should see the bold text "done" in the project description
|
||||
And I should see the bold text "done" in the project description
|
||||
|
||||
# Ticket #1043
|
||||
@selenium
|
||||
Scenario: I can move a todo out of the current project
|
||||
Given I have a project "foo" with 2 todos
|
||||
When I visit the "foo" project
|
||||
And I change the project_name field of "Todo 1" to "bar"
|
||||
Then I should not see the todo "Todo 1"
|
||||
And I should see the todo "Todo 2"
|
||||
|
||||
# Ticket #1041
|
||||
@selenium
|
||||
Scenario: I can change the name of the project using the Edit Project Settings form
|
||||
Given I have a project "bananas" with 1 todos
|
||||
When I visit the "bananas" project
|
||||
And I edit the project name to "cherries"
|
||||
Then the project title should be "cherries"
|
||||
|
||||
# Ticket #1042
|
||||
@selenium
|
||||
Scenario: I cannot change the name of a project in the project view to the name of another existing project
|
||||
Given I have a project "test" with 1 todos
|
||||
When I go to the projects page
|
||||
Then the badge should show 2 # "manage me" and "test"
|
||||
When I visit the "manage me" project
|
||||
And I edit the project name to "test"
|
||||
Then I should see "Name already exists"
|
||||
|
||||
# Ticket #1042
|
||||
@selenium
|
||||
Scenario: I cannot change the name of a project in the project list view to the name of another existing project
|
||||
Given I have a project "test" with 1 todos
|
||||
When I go to the projects page
|
||||
Then the badge should show 2 # "manage me" and "test"
|
||||
When I edit the project name of "manage me" to "test"
|
||||
Then I should see "Name already exists"
|
||||
31
features/manage_users.feature
Normal file
31
features/manage_users.feature
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
Feature: Manage users
|
||||
|
||||
In order to be able to manage the users able to use Tracks
|
||||
As the administrator of this installed Tracks
|
||||
I want to add and delete accounts of users
|
||||
|
||||
Background:
|
||||
Given the following user records
|
||||
| login | password | is_admin |
|
||||
| testuser | secret | false |
|
||||
| admin | secret | true |
|
||||
And I have logged in as "admin" with password "secret"
|
||||
|
||||
Scenario: Show all accounts
|
||||
When I go to the manage users page
|
||||
Then I should see "testuser"
|
||||
And I should see "admin"
|
||||
|
||||
Scenario: Add new account
|
||||
When I go to the manage users page
|
||||
And I follow "Signup new user"
|
||||
Then I should be on the signup page
|
||||
When I submit the signup form with username "new.user", password "secret123" and confirm with "secret123"
|
||||
Then I should be on the manage users page
|
||||
And I should see "new.user"
|
||||
|
||||
@selenium
|
||||
Scenario: Delete account from users page
|
||||
When I go to the manage users page
|
||||
And I delete the user "testuser"
|
||||
Then I should see that a user named "testuser" is not present
|
||||
25
features/recurring_todos.feature
Normal file
25
features/recurring_todos.feature
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Feature: Manage recurring todos
|
||||
|
||||
In order to manage repeating todos
|
||||
As a Tracks user
|
||||
I want to view, edit, add, or remove recurrence patterns of repeating todos
|
||||
|
||||
Background:
|
||||
Given the following user record
|
||||
| login | password | is_admin |
|
||||
| testuser | secret | false |
|
||||
And I have logged in as "testuser" with password "secret"
|
||||
|
||||
@selenium
|
||||
Scenario: Being able to select daily, weekly, monthly and yearly pattern
|
||||
When I go to the repeating todos page
|
||||
And I follow "Add a new recurring action"
|
||||
Then I should see the form for "Daily" recurrence pattern
|
||||
When I select "Weekly" recurrence pattern
|
||||
Then I should see the form for "Weekly" recurrence pattern
|
||||
When I select "Monthly" recurrence pattern
|
||||
Then I should see the form for "Monthly" recurrence pattern
|
||||
When I select "Yearly" recurrence pattern
|
||||
Then I should see the form for "Yearly" recurrence pattern
|
||||
When I select "Daily" recurrence pattern
|
||||
Then I should see the form for "Daily" recurrence pattern
|
||||
137
features/shared_add_new_todo.feature
Normal file
137
features/shared_add_new_todo.feature
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
Feature: Add new next action from every page
|
||||
|
||||
In order to quickly add a new next action
|
||||
As a Tracks user
|
||||
I want to to be able to add one or more new next actions from every page
|
||||
|
||||
Background:
|
||||
Given the following user record
|
||||
| login | password | is_admin |
|
||||
| testuser | secret | false |
|
||||
And I have logged in as "testuser" with password "secret"
|
||||
And I have a context called "test context"
|
||||
And I have a project "test project" with 1 todos
|
||||
|
||||
@selenium
|
||||
Scenario Outline: I can hide the input form for single next action on a page
|
||||
When I <action> the <page>
|
||||
Then the single action form should be visible
|
||||
When I follow "« Hide form"
|
||||
Then the single action form should not be visible
|
||||
|
||||
Scenarios:
|
||||
| action | page |
|
||||
| go to | home page |
|
||||
| go to | tickler page |
|
||||
| visit | project page for "test project"|
|
||||
| visit | context page for "test context"|
|
||||
| visit | tag page for "starred" |
|
||||
|
||||
@selenium
|
||||
Scenario Outline: I can hide the input form for multiple next actions
|
||||
When I <action> the <page>
|
||||
Then the single action form should be visible
|
||||
When I follow "Add multiple next actions"
|
||||
Then the multiple action form should be visible
|
||||
When I follow "« Hide form"
|
||||
Then the single action form should not be visible
|
||||
And the multiple action form should not be visible
|
||||
|
||||
Scenarios:
|
||||
| action | page |
|
||||
| go to | home page |
|
||||
| go to | tickler page |
|
||||
| visit | project page for "test project"|
|
||||
| visit | context page for "test context"|
|
||||
| visit | tag page for "starred" |
|
||||
|
||||
@selenium
|
||||
Scenario Outline: I can hide the input form and then choose both input forms
|
||||
When I <action> the <page>
|
||||
Then the single action form should be visible
|
||||
When I follow "« Hide form"
|
||||
Then the single action form should not be visible
|
||||
When I follow "Add multiple next actions"
|
||||
Then the multiple action form should be visible
|
||||
When I follow "« Hide form"
|
||||
Then the single action form should not be visible
|
||||
And the multiple action form should not be visible
|
||||
|
||||
Scenarios:
|
||||
| action | page |
|
||||
| go to | home page |
|
||||
| go to | tickler page |
|
||||
| visit | project page for "test project"|
|
||||
| visit | context page for "test context"|
|
||||
| visit | tag page for "starred" |
|
||||
|
||||
@selenium
|
||||
Scenario Outline: I can switch forms for single next action to multiple next actions
|
||||
When I <action> the <page>
|
||||
Then the single action form should be visible
|
||||
When I follow "Add multiple next actions"
|
||||
Then the single action form should not be visible
|
||||
And the multiple action form should be visible
|
||||
When I follow "Add single next action"
|
||||
Then the single action form should be visible
|
||||
And the multiple action form should not be visible
|
||||
|
||||
Scenarios:
|
||||
| action | page |
|
||||
| go to | home page |
|
||||
| go to | tickler page |
|
||||
| visit | project page for "test project"|
|
||||
| visit | context page for "test context"|
|
||||
| visit | tag page for "starred" |
|
||||
|
||||
@selenium
|
||||
Scenario Outline: I can add a todo from several pages
|
||||
When I <action> the <page>
|
||||
And I submit a new action with description "a new next action"
|
||||
Then I should <see> "a new next action"
|
||||
|
||||
Scenarios:
|
||||
| action | page | see |
|
||||
| go to | home page | see |
|
||||
| go to | tickler page | not see|
|
||||
| visit | project page for "test project"| see |
|
||||
| visit | context page for "test context"| see |
|
||||
| visit | tag page for "starred" | not see|
|
||||
|
||||
@selenium
|
||||
Scenario Outline: I can add multiple todos from several pages
|
||||
When I <action> the <page>
|
||||
And I follow "Add multiple next actions"
|
||||
And I submit multiple actions with using
|
||||
"""
|
||||
one new next action
|
||||
another new next action
|
||||
"""
|
||||
Then I should <see> "one new next action"
|
||||
And I should <see> "another new next action"
|
||||
And the badge should show <badge>
|
||||
And the number of actions should be <count>
|
||||
|
||||
Scenarios:
|
||||
| action | page | see | badge | count |
|
||||
| go to | home page | see | 3 | 3 |
|
||||
| go to | tickler page | not see| 0 | 3 |
|
||||
| visit | project page for "test project"| see | 3 | 3 |
|
||||
| visit | context page for "test context"| see | 2 | 3 |
|
||||
| visit | tag page for "starred" | not see| 0 | 3 |
|
||||
|
||||
@selenium
|
||||
Scenario: I need to fill in at least one description and a context
|
||||
When I go to the home page
|
||||
And I follow "Add multiple next actions"
|
||||
And I submit the new multiple actions form with "", "", "", ""
|
||||
Then I should see "You need to submit at least one next action"
|
||||
When I submit the new multiple actions form with "one", "", "", ""
|
||||
Then I should see "Context can't be blank"
|
||||
When I fill the multiple actions form with "", "a project", "test context", "tag"
|
||||
And I submit the new multiple actions form with
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
Then I should see "You need to submit at least one next action"
|
||||
38
features/show_integration_options.feature
Normal file
38
features/show_integration_options.feature
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
Feature: Integrate Tracks in various ways
|
||||
|
||||
In order to use tracks with other software
|
||||
As a Tracks user
|
||||
I want to be informed about the various ways to integrate tracks
|
||||
|
||||
Background:
|
||||
Given the following user record
|
||||
| login | password | is_admin |
|
||||
| testuser | secret | false |
|
||||
And I have logged in as "testuser" with password "secret"
|
||||
|
||||
Scenario: I cannot see scripts when I do not have a context
|
||||
Given I have no contexts
|
||||
When I go to the integrations page
|
||||
Then I should see a message that you need a context to see scripts
|
||||
|
||||
Scenario: I can see scripts when I have one or more contexts
|
||||
Given I have a context called "@pc"
|
||||
When I go to the integrations page
|
||||
Then I should see scripts
|
||||
|
||||
@selenium
|
||||
Scenario: When I select a different context the example scripts should change accoordingly
|
||||
Given I have the following contexts:
|
||||
| context |
|
||||
| @pc |
|
||||
| @home |
|
||||
| @shops |
|
||||
| @boss |
|
||||
When I go to the integrations page
|
||||
Then I should see a script "applescript1" for "@pc"
|
||||
When I select "@home" from "applescript1-contexts"
|
||||
Then I should see a script "applescript1" for "@home"
|
||||
When I select "@shops" from "applescript2-contexts"
|
||||
Then I should see a script "applescript2" for "@shops"
|
||||
When I select "@boss" from "quicksilver-contexts"
|
||||
Then I should see a script "quicksilver" for "@boss"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
Feature Show statistics
|
||||
Feature: Show statistics
|
||||
In order to see what I have got done
|
||||
As an user
|
||||
I want see my statistics
|
||||
|
|
|
|||
|
|
@ -1,8 +1,39 @@
|
|||
Given /^I have a context called "([^\"]*)"$/ do |context_name|
|
||||
@context = @current_user.contexts.create!(:name => context_name)
|
||||
Given /^I have no contexts$/ do
|
||||
# should probably not be needed as you use this given at the start of a scenario
|
||||
Context.delete_all
|
||||
end
|
||||
|
||||
When /^I visits the context page for "([^\"]*)"$/ do |context_name|
|
||||
Given /^there exists a context called "([^"]*)" for user "([^"]*)"$/ do |context_name, login|
|
||||
user = User.find_by_login(login)
|
||||
user.should_not be_nil
|
||||
@context = user.contexts.create!(:name => context_name)
|
||||
end
|
||||
|
||||
Given /^I have a context called "([^\"]*)"$/ do |context_name|
|
||||
Given "there exists a context called \"#{context_name}\" for user \"#{@current_user.login}\""
|
||||
end
|
||||
|
||||
Given /^I have the following contexts:$/ do |table|
|
||||
table.hashes.each do |context|
|
||||
Given 'I have a context called "'+context[:context]+'"'
|
||||
end
|
||||
end
|
||||
|
||||
Given /^I have a context "([^\"]*)" with (.*) actions$/ do |context_name, number_of_actions|
|
||||
context = @current_user.contexts.create!(:name => context_name)
|
||||
1.upto number_of_actions.to_i do |i|
|
||||
@current_user.todos.create!(:context_id => context.id, :description => "todo #{i}")
|
||||
end
|
||||
end
|
||||
|
||||
Given /^I have the following contexts$/ do |table|
|
||||
Context.delete_all
|
||||
table.hashes.each do |hash|
|
||||
context = Factory(:context, hash)
|
||||
end
|
||||
end
|
||||
|
||||
When /^I visit the context page for "([^\"]*)"$/ do |context_name|
|
||||
context = @current_user.contexts.find_by_name(context_name)
|
||||
context.should_not be_nil
|
||||
visit "/contexts/#{context.id}"
|
||||
|
|
@ -14,6 +45,46 @@ When /^I edit the context name in place to be "([^\"]*)"$/ do |new_context_name|
|
|||
click_button "OK"
|
||||
end
|
||||
|
||||
When /^I delete the context "([^\"]*)"$/ do |context_name|
|
||||
context = @current_user.contexts.find_by_name(context_name)
|
||||
context.should_not be_nil
|
||||
click_link "delete_context_#{context.id}"
|
||||
selenium.get_confirmation.should == "Are you sure that you want to delete the context '#{context_name}'? Be aware that this will also delete all actions in this context!"
|
||||
wait_for do
|
||||
!selenium.is_element_present("delete_context_#{context.id}")
|
||||
end
|
||||
end
|
||||
|
||||
When /^I edit the context to rename it to "([^\"]*)"$/ do |new_name|
|
||||
click_link "edit_context_#{@context.id}"
|
||||
|
||||
wait_for do
|
||||
selenium.is_element_present("submit_context_#{@context.id}")
|
||||
end
|
||||
|
||||
fill_in "context_name", :with => new_name
|
||||
|
||||
selenium.click "submit_context_#{@context.id}",
|
||||
:wait_for => :text,
|
||||
:element => "flash",
|
||||
:text => "Context saved"
|
||||
|
||||
wait_for do
|
||||
selenium.is_element_present("edit_context_#{@context.id}")
|
||||
end
|
||||
end
|
||||
|
||||
When /^I add a new context "([^"]*)"$/ do |context_name|
|
||||
fill_in "context[name]", :with => context_name
|
||||
submit_new_context_form
|
||||
end
|
||||
|
||||
When /^I add a new hidden context "([^"]*)"$/ do |context_name|
|
||||
fill_in "context[name]", :with => context_name
|
||||
check "context_hide"
|
||||
submit_new_context_form
|
||||
end
|
||||
|
||||
Then /^I should see the context name is "([^\"]*)"$/ do |context_name|
|
||||
Then "I should see \"#{context_name}\""
|
||||
end
|
||||
|
|
@ -26,28 +97,7 @@ Then /^he should see that a context named "([^\"]*)" is not present$/ do |contex
|
|||
Then "I should not see \"#{context_name} (\""
|
||||
end
|
||||
|
||||
Given /^I have a context "([^\"]*)" with (.*) actions$/ do |context_name, number_of_actions|
|
||||
context = @current_user.contexts.create!(:name => context_name)
|
||||
1.upto number_of_actions.to_i do |i|
|
||||
@current_user.todos.create!(:context_id => context.id, :description => "todo #{i}")
|
||||
end
|
||||
end
|
||||
|
||||
When /^I delete the context "([^\"]*)"$/ do |context_name|
|
||||
context = @current_user.contexts.find_by_name(context_name)
|
||||
context.should_not be_nil
|
||||
click_link "delete_context_#{context.id}"
|
||||
selenium.get_confirmation.should == "Are you sure that you want to delete the context '#{context_name}'?"
|
||||
wait_for do
|
||||
!selenium.is_element_present("delete_context_#{context.id}")
|
||||
end
|
||||
end
|
||||
|
||||
When /^I edit the context to rename it to "([^\"]*)"$/ do |new_name|
|
||||
click_link "edit_context_#{@context.id}"
|
||||
fill_in "context_name", :with => new_name
|
||||
click_button "submit_context_#{@context.id}"
|
||||
wait_for do
|
||||
selenium.is_visible("flash")
|
||||
end
|
||||
Then /^I should see the context "([^"]*)" under "([^"]*)"$/ do |context_name, state|
|
||||
context = Context.find_by_name(context_name)
|
||||
response.should have_xpath("//div[@id='list-contexts-#{state}']//div[@id='context_#{context.id}']")
|
||||
end
|
||||
|
|
|
|||
23
features/step_definitions/integration_steps.rb
Normal file
23
features/step_definitions/integration_steps.rb
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
Then /^I should see a message that you need a context to see scripts$/ do
|
||||
Then 'I should see "You do not have any context yet. The script will be available after you add your first context"'
|
||||
end
|
||||
|
||||
Then /^I should see scripts$/ do
|
||||
# check on a small snippet of the first applescript
|
||||
Then 'I should see "set returnValue to call xmlrpc"'
|
||||
end
|
||||
|
||||
Then /^I should see a script "([^\"]*)" for "([^\"]*)"$/ do |script, context_name|
|
||||
selenium.is_visible(script)
|
||||
context = Context.find_by_name(context_name)
|
||||
|
||||
# wait for the script to refresh
|
||||
wait_for :timeout => 15 do
|
||||
selenium.is_text_present("#{context.id} (* #{context_name} *)")
|
||||
end
|
||||
|
||||
# make sure the text is found within the textarea
|
||||
script_source = selenium.get_text("//textarea[@id='#{script}']")
|
||||
script_source.should =~ /#{context.id} \(\* #{context_name} \*\)/
|
||||
end
|
||||
|
||||
|
|
@ -3,6 +3,9 @@ Given /^I have logged in as "(.*)" with password "(.*)"$/ do |username, password
|
|||
fill_in "Login", :with => username
|
||||
fill_in "Password", :with => password
|
||||
click_button
|
||||
if response.respond_to? :selenium
|
||||
selenium.wait_for_page_to_load(5000)
|
||||
end
|
||||
response.should contain(/Login successful/)
|
||||
@current_user = User.find_by_login(username)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ Then /^the first note should disappear$/ do
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
Then /^I should see the note text$/ do
|
||||
Then "I should see \"after 50 characters\""
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,7 +12,16 @@ end
|
|||
Given /^there exists a project "([^\"]*)" for user "([^\"]*)"$/ do |project_name, user_name|
|
||||
user = User.find_by_login(user_name)
|
||||
user.should_not be_nil
|
||||
user.projects.create!(:name => project_name)
|
||||
@project = user.projects.create!(:name => project_name)
|
||||
end
|
||||
|
||||
Given /^there exists a project called "([^"]*)" for user "([^"]*)"$/ do |project_name, login|
|
||||
# TODO: regexp change to integrate this with the previous since only 'called' is different
|
||||
Given "there exists a project \"#{project_name}\" for user \"#{login}\""
|
||||
end
|
||||
|
||||
Given /^I have a project called "([^"]*)"$/ do |project_name|
|
||||
Given "there exists a project \"#{project_name}\" for user \"#{@current_user.login}\""
|
||||
end
|
||||
|
||||
When /^I visit the "([^\"]*)" project$/ do |project_name|
|
||||
|
|
@ -21,12 +30,35 @@ When /^I visit the "([^\"]*)" project$/ do |project_name|
|
|||
visit project_path(@project)
|
||||
end
|
||||
|
||||
When /^I visit the project page for "([^"]*)"$/ do |project_name|
|
||||
When "I visit the \"#{project_name}\" project"
|
||||
end
|
||||
|
||||
When /^I edit the project description to "([^\"]*)"$/ do |new_description|
|
||||
click_link "link_edit_project_#{@project.id}"
|
||||
fill_in "project[description]", :with => new_description
|
||||
click_button "submit_project_#{@project.id}"
|
||||
end
|
||||
|
||||
When /^I edit the project name to "([^\"]*)"$/ do |new_title|
|
||||
click_link "link_edit_project_#{@project.id}"
|
||||
fill_in "project[name]", :with => new_title
|
||||
|
||||
# changed to make sure selenium waits until the saving has a result either
|
||||
# positive or negative. Was: :element=>"flash", :text=>"Project saved"
|
||||
# we may need to change it back if you really need a positive outcome, i.e.
|
||||
# this step needs to fail if the project was not saved succesfully
|
||||
selenium.click "submit_project_#{@project.id}",
|
||||
:wait_for => :text,
|
||||
:text => /(Project saved|1 error prohibited this project from being saved)/
|
||||
end
|
||||
|
||||
When /^I edit the project name of "([^"]*)" to "([^"]*)"$/ do |project_current_name, project_new_name|
|
||||
@project = @current_user.projects.find_by_name(project_current_name)
|
||||
@project.should_not be_nil
|
||||
When "I edit the project name to \"#{project_new_name}\""
|
||||
end
|
||||
|
||||
Then /^I should see the bold text "([^\"]*)" in the project description$/ do |bold|
|
||||
xpath="//div[@class='project_description']/p/strong"
|
||||
|
||||
|
|
@ -44,3 +76,7 @@ Then /^I should see the italic text "([^\"]*)" in the project description$/ do |
|
|||
|
||||
italic_text.should =~ /#{italic}/
|
||||
end
|
||||
|
||||
Then /^the project title should be "(.*)"$/ do |title|
|
||||
selenium.get_text("css=h2#project_name").should == title
|
||||
end
|
||||
7
features/step_definitions/recurring_todo_steps.rb
Normal file
7
features/step_definitions/recurring_todo_steps.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
When /^I select "([^\"]*)" recurrence pattern$/ do |recurrence_period|
|
||||
selenium.click("recurring_todo_recurring_period_#{recurrence_period.downcase}")
|
||||
end
|
||||
|
||||
Then /^I should see the form for "([^\"]*)" recurrence pattern$/ do |recurrence_period|
|
||||
selenium.is_visible("recurring_#{recurrence_period.downcase}")
|
||||
end
|
||||
16
features/step_definitions/shared_new_todo_steps.rb
Normal file
16
features/step_definitions/shared_new_todo_steps.rb
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
Then /^the single action form should be visible$/ do
|
||||
selenium.is_visible("todo_new_action").should == true
|
||||
end
|
||||
|
||||
Then /^the single action form should not be visible$/ do
|
||||
selenium.is_visible("todo_new_action").should == false
|
||||
end
|
||||
|
||||
Then /^the multiple action form should be visible$/ do
|
||||
selenium.is_visible("todo_multi_add").should == true
|
||||
end
|
||||
|
||||
Then /^the multiple action form should not be visible$/ do
|
||||
selenium.is_visible("todo_multi_add").should == false
|
||||
end
|
||||
|
||||
|
|
@ -33,19 +33,25 @@ Given /^"(.*)" depends on "(.*)"$/ do |successor_name, predecessor_name|
|
|||
successor.save!
|
||||
end
|
||||
|
||||
Given /^I have a project "([^"]*)" that has the following todos$/ do |project_name, todos|
|
||||
Given "I have a project called \"#{project_name}\""
|
||||
@project.should_not be_nil
|
||||
todos.hashes.each do |todo|
|
||||
context_id = @current_user.contexts.find_by_name(todo[:context])
|
||||
context_id.should_not be_nil
|
||||
@current_user.todos.create!(
|
||||
:description => todo[:description],
|
||||
:context_id => context_id,
|
||||
:project_id=>@project.id)
|
||||
end
|
||||
end
|
||||
|
||||
When /^I drag "(.*)" to "(.*)"$/ do |dragged, target|
|
||||
drag_id = Todo.find_by_description(dragged).id
|
||||
drop_id = Todo.find_by_description(target).id
|
||||
drag_name = "xpath=//div[@id='line_todo_#{drag_id}']//img[@class='grip']"
|
||||
# xpath does not seem to work here... reverting to css
|
||||
# xpath=//div[@id='line_todo_#{drop_id}']//img[@class='successor_target']
|
||||
drop_name = "css=div#line_todo_#{drop_id} img.successor_target"
|
||||
|
||||
# HACK: the target img is hidden until drag starts. We need to show the img or the
|
||||
# xpath will not find it
|
||||
js="$('div#line_todo_#{drop_id} img.successor_target').show();"
|
||||
selenium.get_eval "(function() {with(this) {#{js}}}).call(selenium.browserbot.getCurrentWindow());"
|
||||
|
||||
drop_name = "xpath=//div[@id='line_todo_#{drop_id}']//div[@class='description']"
|
||||
|
||||
selenium.drag_and_drop_to_object(drag_name, drop_name)
|
||||
|
||||
arrow = "xpath=//div[@id='line_todo_#{drop_id}']/div/a[@class='show_successors']/img"
|
||||
|
|
@ -66,6 +72,57 @@ Then /^I should see ([0-9]+) todos$/ do |count|
|
|||
end
|
||||
end
|
||||
|
||||
When /I change the (.*) field of "([^\"]*)" to "([^\"]*)"$/ do |field, todo_name, new_value|
|
||||
selenium.click("//span[@class=\"todo.descr\"][.=\"#{todo_name}\"]/../../a[@class=\"icon edit_item\"]", :wait_for => :ajax, :javascript_framework => :jquery)
|
||||
selenium.type("css=form.edit_todo_form input[name=#{field}]", new_value)
|
||||
selenium.click("css=button.positive", :wait_for => :ajax, :javascript_framework => :jquery)
|
||||
sleep(5)
|
||||
end
|
||||
|
||||
When /^I submit a new action with description "([^"]*)"$/ do |description|
|
||||
fill_in "todo[description]", :with => description
|
||||
submit_next_action_form
|
||||
end
|
||||
|
||||
When /^I submit multiple actions with using$/ do |multiple_actions|
|
||||
fill_in "todo[multiple_todos]", :with => multiple_actions
|
||||
submit_multiple_next_action_form
|
||||
end
|
||||
|
||||
When /^I fill the multiple actions form with "([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"$/ do |descriptions, project_name, context_name, tags|
|
||||
fill_in "todo[multiple_todos]", :with => descriptions
|
||||
fill_in "multi_todo_project_name", :with => project_name
|
||||
fill_in "multi_todo_context_name", :with => context_name
|
||||
fill_in "multi_tag_list", :with => tags
|
||||
end
|
||||
|
||||
When /^I submit the new multiple actions form with "([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"$/ do |descriptions, project_name, context_name, tags|
|
||||
When "I fill the multiple actions form with \"#{descriptions}\", \"#{project_name}\", \"#{context_name}\", \"#{tags}\""
|
||||
submit_multiple_next_action_form
|
||||
end
|
||||
|
||||
When /^I submit the new multiple actions form with$/ do |multi_line_descriptions|
|
||||
fill_in "todo[multiple_todos]", :with => multi_line_descriptions
|
||||
submit_multiple_next_action_form
|
||||
end
|
||||
|
||||
When /^I edit the dependency of "([^"]*)" to '([^'']*)'$/ do |todo_name, deps|
|
||||
todo = @dep_todo = @current_user.todos.find_by_description(todo_name)
|
||||
todo.should_not be_nil
|
||||
# click edit
|
||||
selenium.click("//div[@id='line_todo_#{todo.id}']//img[@id='edit_icon_todo_#{todo.id}']", :wait_for => :ajax, :javascript_framework => :jquery)
|
||||
fill_in "predecessor_list_todo_#{todo.id}", :with => deps
|
||||
# submit form
|
||||
selenium.click("//div[@id='edit_todo_#{todo.id}']//button[@id='submit_todo_#{todo.id}']", :wait_for => :ajax, :javascript_framework => :jquery)
|
||||
|
||||
end
|
||||
|
||||
Then /^there should not be an error$/ do
|
||||
# form should be gone and thus not errors visible
|
||||
selenium.is_visible("edit_todo_#{@dep_todo.id}").should == false
|
||||
end
|
||||
|
||||
|
||||
Then /^the dependencies of "(.*)" should include "(.*)"$/ do |child_name, parent_name|
|
||||
parent = @current_user.todos.find_by_description(parent_name)
|
||||
parent.should_not be_nil
|
||||
|
|
@ -88,3 +145,15 @@ Then /^I should see "([^\"]*)" within the dependencies of "([^\"]*)"$/ do |succe
|
|||
xpath = "xpath=//div[@id='line_todo_#{todo.id}']//div[@id='successor_line_todo_#{successor.id}']//span"
|
||||
selenium.wait_for_element(xpath, :timeout_in_seconds => 5)
|
||||
end
|
||||
|
||||
Then /^I should see the todo "([^\"]*)"$/ do |todo_description|
|
||||
selenium.is_element_present("//span[.=\"#{todo_description}\"]").should be_true
|
||||
end
|
||||
|
||||
Then /^I should not see the todo "([^\"]*)"$/ do |todo_description|
|
||||
selenium.is_element_present("//span[.=\"#{todo_description}\"]").should be_false
|
||||
end
|
||||
|
||||
Then /^the number of actions should be (\d+)$/ do |count|
|
||||
@current_user.todos.count.should == count.to_i
|
||||
end
|
||||
|
|
|
|||
3
features/step_definitions/todo_tag_steps.rb
Normal file
3
features/step_definitions/todo_tag_steps.rb
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
When /^I visit the tag page for "([^"]*)"$/ do |tag_name|
|
||||
visit "/todos/tag/#{tag_name}"
|
||||
end
|
||||
|
|
@ -10,6 +10,24 @@ Given "no users exists" do
|
|||
User.delete_all
|
||||
end
|
||||
|
||||
When /^I delete the user "([^\"]*)"$/ do |username|
|
||||
# click "//tr[@id='user-3']//img"
|
||||
# assert_confirmation "Warning: this will delete user 'john', all their actions, contexts, project and notes. Are you sure that you want to continue?"
|
||||
user = User.find_by_login(username)
|
||||
user.should_not be_nil
|
||||
|
||||
selenium.click "xpath=//tr[@id='user-#{user.id}']//img"
|
||||
selenium.get_confirmation.should == "Warning: this will delete user '#{user.login}', all their actions, contexts, project and notes. Are you sure that you want to continue?"
|
||||
wait_for do
|
||||
!selenium.is_element_present("//tr[@id='user-#{user.id}']//img")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Then /^I should see that a user named "([^\"]*)" is not present$/ do |username|
|
||||
Then "I should not see \"#{username} (\""
|
||||
end
|
||||
|
||||
Then "I should be an admin" do
|
||||
# just check on the presence of the menu item for managing users
|
||||
Then "I should see \"Manage users\""
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
|
||||
require 'uri'
|
||||
require 'cgi'
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
|
||||
|
||||
# Commonly used webrat steps
|
||||
|
|
@ -19,23 +20,23 @@ When /^(?:|I )go to (.+)$/ do |page_name|
|
|||
visit path_to(page_name)
|
||||
end
|
||||
|
||||
When /^(?:|I )press "([^\"]*)"$/ do |button|
|
||||
When /^(?:|I )press "([^"]*)"$/ do |button|
|
||||
click_button(button)
|
||||
end
|
||||
|
||||
When /^(?:|I )follow "([^\"]*)"$/ do |link|
|
||||
When /^(?:|I )follow "([^"]*)"$/ do |link|
|
||||
click_link(link)
|
||||
end
|
||||
|
||||
When /^(?:|I )follow "([^\"]*)" within "([^\"]*)"$/ do |link, parent|
|
||||
When /^(?:|I )follow "([^"]*)" within "([^"]*)"$/ do |link, parent|
|
||||
click_link_within(parent, link)
|
||||
end
|
||||
|
||||
When /^(?:|I )fill in "([^\"]*)" with "([^\"]*)"$/ do |field, value|
|
||||
When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value|
|
||||
fill_in(field, :with => value)
|
||||
end
|
||||
|
||||
When /^(?:|I )fill in "([^\"]*)" for "([^\"]*)"$/ do |value, field|
|
||||
When /^(?:|I )fill in "([^"]*)" for "([^"]*)"$/ do |value, field|
|
||||
fill_in(field, :with => value)
|
||||
end
|
||||
|
||||
|
|
@ -56,13 +57,13 @@ When /^(?:|I )fill in the following:$/ do |fields|
|
|||
end
|
||||
end
|
||||
|
||||
When /^(?:|I )select "([^\"]*)" from "([^\"]*)"$/ do |value, field|
|
||||
When /^(?:|I )select "([^"]*)" from "([^"]*)"$/ do |value, field|
|
||||
select(value, :from => field)
|
||||
end
|
||||
|
||||
# Use this step in conjunction with Rail's datetime_select helper. For example:
|
||||
# When I select "December 25, 2008 10:00" as the date and time
|
||||
When /^(?:|I )select "([^\"]*)" as the date and time$/ do |time|
|
||||
When /^(?:|I )select "([^"]*)" as the date and time$/ do |time|
|
||||
select_datetime(time)
|
||||
end
|
||||
|
||||
|
|
@ -75,7 +76,7 @@ end
|
|||
# The following steps would fill out the form:
|
||||
# When I select "November 23, 2004 11:20" as the "Preferred" date and time
|
||||
# And I select "November 25, 2004 10:30" as the "Alternative" date and time
|
||||
When /^(?:|I )select "([^\"]*)" as the "([^\"]*)" date and time$/ do |datetime, datetime_label|
|
||||
When /^(?:|I )select "([^"]*)" as the "([^"]*)" date and time$/ do |datetime, datetime_label|
|
||||
select_datetime(datetime, :from => datetime_label)
|
||||
end
|
||||
|
||||
|
|
@ -83,46 +84,46 @@ end
|
|||
# When I select "2:20PM" as the time
|
||||
# Note: Rail's default time helper provides 24-hour time-- not 12 hour time. Webrat
|
||||
# will convert the 2:20PM to 14:20 and then select it.
|
||||
When /^(?:|I )select "([^\"]*)" as the time$/ do |time|
|
||||
When /^(?:|I )select "([^"]*)" as the time$/ do |time|
|
||||
select_time(time)
|
||||
end
|
||||
|
||||
# Use this step when using multiple time_select helpers on a page or you want to
|
||||
# specify the name of the time on the form. For example:
|
||||
# When I select "7:30AM" as the "Gym" time
|
||||
When /^(?:|I )select "([^\"]*)" as the "([^\"]*)" time$/ do |time, time_label|
|
||||
When /^(?:|I )select "([^"]*)" as the "([^"]*)" time$/ do |time, time_label|
|
||||
select_time(time, :from => time_label)
|
||||
end
|
||||
|
||||
# Use this step in conjunction with Rail's date_select helper. For example:
|
||||
# When I select "February 20, 1981" as the date
|
||||
When /^(?:|I )select "([^\"]*)" as the date$/ do |date|
|
||||
When /^(?:|I )select "([^"]*)" as the date$/ do |date|
|
||||
select_date(date)
|
||||
end
|
||||
|
||||
# Use this step when using multiple date_select helpers on one page or
|
||||
# you want to specify the name of the date on the form. For example:
|
||||
# When I select "April 26, 1982" as the "Date of Birth" date
|
||||
When /^(?:|I )select "([^\"]*)" as the "([^\"]*)" date$/ do |date, date_label|
|
||||
When /^(?:|I )select "([^"]*)" as the "([^"]*)" date$/ do |date, date_label|
|
||||
select_date(date, :from => date_label)
|
||||
end
|
||||
|
||||
When /^(?:|I )check "([^\"]*)"$/ do |field|
|
||||
When /^(?:|I )check "([^"]*)"$/ do |field|
|
||||
check(field)
|
||||
end
|
||||
|
||||
When /^(?:|I )uncheck "([^\"]*)"$/ do |field|
|
||||
When /^(?:|I )uncheck "([^"]*)"$/ do |field|
|
||||
uncheck(field)
|
||||
end
|
||||
|
||||
When /^(?:|I )choose "([^\"]*)"$/ do |field|
|
||||
When /^(?:|I )choose "([^"]*)"$/ do |field|
|
||||
choose(field)
|
||||
end
|
||||
|
||||
# Adds support for validates_attachment_content_type. Without the mime-type getting
|
||||
# passed to attach_file() you will get a "Photo file is not one of the allowed file types."
|
||||
# error message
|
||||
When /^(?:|I )attach the file "([^\"]*)" to "([^\"]*)"$/ do |path, field|
|
||||
When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |path, field|
|
||||
type = path.split(".")[1]
|
||||
|
||||
case type
|
||||
|
|
@ -139,123 +140,142 @@ When /^(?:|I )attach the file "([^\"]*)" to "([^\"]*)"$/ do |path, field|
|
|||
attach_file(field, path, type)
|
||||
end
|
||||
|
||||
Then /^(?:|I )should see "([^\"]*)"$/ do |text|
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
Then /^(?:|I )should see "([^"]*)"$/ do |text|
|
||||
if response.respond_to? :should
|
||||
response.should contain(text)
|
||||
else
|
||||
assert_contain text
|
||||
end
|
||||
end
|
||||
|
||||
Then /^(?:|I )should see "([^\"]*)" within "([^\"]*)"$/ do |text, selector|
|
||||
Then /^(?:|I )should see "([^"]*)" within "([^"]*)"$/ do |text, selector|
|
||||
within(selector) do |content|
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
if content.respond_to? :should
|
||||
content.should contain(text)
|
||||
else
|
||||
assert content.include?(text)
|
||||
hc = Webrat::Matchers::HasContent.new(text)
|
||||
assert hc.matches?(content), hc.failure_message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Then /^(?:|I )should see \/([^\/]*)\/$/ do |regexp|
|
||||
regexp = Regexp.new(regexp)
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
if response.respond_to? :should
|
||||
response.should contain(regexp)
|
||||
else
|
||||
assert_contain regexp
|
||||
assert_match(regexp, response_body)
|
||||
end
|
||||
end
|
||||
|
||||
Then /^(?:|I )should see \/([^\/]*)\/ within "([^\"]*)"$/ do |regexp, selector|
|
||||
Then /^(?:|I )should see \/([^\/]*)\/ within "([^"]*)"$/ do |regexp, selector|
|
||||
within(selector) do |content|
|
||||
regexp = Regexp.new(regexp)
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
if content.respond_to? :should
|
||||
content.should contain(regexp)
|
||||
else
|
||||
assert content =~ regexp
|
||||
assert_match(regexp, content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Then /^(?:|I )should not see "([^\"]*)"$/ do |text|
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
Then /^(?:|I )should not see "([^"]*)"$/ do |text|
|
||||
if response.respond_to? :should_not
|
||||
response.should_not contain(text)
|
||||
else
|
||||
assert_not_contain text
|
||||
assert_not_contain(text)
|
||||
end
|
||||
end
|
||||
|
||||
Then /^(?:|I )should not see "([^\"]*)" within "([^\"]*)"$/ do |text, selector|
|
||||
Then /^(?:|I )should not see "([^"]*)" within "([^"]*)"$/ do |text, selector|
|
||||
within(selector) do |content|
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
content.should_not contain(text)
|
||||
if content.respond_to? :should_not
|
||||
content.should_not contain(text)
|
||||
else
|
||||
assert !content.include?(text)
|
||||
hc = Webrat::Matchers::HasContent.new(text)
|
||||
assert !hc.matches?(content), hc.negative_failure_message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Then /^(?:|I )should not see \/([^\/]*)\/$/ do |regexp|
|
||||
regexp = Regexp.new(regexp)
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
if response.respond_to? :should_not
|
||||
response.should_not contain(regexp)
|
||||
else
|
||||
assert_not_contain regexp
|
||||
assert_not_contain(regexp)
|
||||
end
|
||||
end
|
||||
|
||||
Then /^(?:|I )should not see \/([^\/]*)\/ within "([^\"]*)"$/ do |regexp, selector|
|
||||
Then /^(?:|I )should not see \/([^\/]*)\/ within "([^"]*)"$/ do |regexp, selector|
|
||||
within(selector) do |content|
|
||||
regexp = Regexp.new(regexp)
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
if content.respond_to? :should_not
|
||||
content.should_not contain(regexp)
|
||||
else
|
||||
assert content !~ regexp
|
||||
assert_no_match(regexp, content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Then /^the "([^\"]*)" field should contain "([^\"]*)"$/ do |field, value|
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
field_labeled(field).value.should =~ /#{value}/
|
||||
Then /^the "([^"]*)" field should contain "([^"]*)"$/ do |field, value|
|
||||
field_value = field_labeled(field).value
|
||||
if field_value.respond_to? :should
|
||||
field_value.should =~ /#{value}/
|
||||
else
|
||||
assert_match(/#{value}/, field_labeled(field).value)
|
||||
assert_match(/#{value}/, field_value)
|
||||
end
|
||||
end
|
||||
|
||||
Then /^the "([^\"]*)" field should not contain "([^\"]*)"$/ do |field, value|
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
field_labeled(field).value.should_not =~ /#{value}/
|
||||
Then /^the "([^"]*)" field should not contain "([^"]*)"$/ do |field, value|
|
||||
field_value = field_labeled(field).value
|
||||
if field_value.respond_to? :should_not
|
||||
field_value.should_not =~ /#{value}/
|
||||
else
|
||||
assert_no_match(/#{value}/, field_labeled(field).value)
|
||||
assert_no_match(/#{value}/, field_value)
|
||||
end
|
||||
end
|
||||
|
||||
Then /^the "([^\"]*)" checkbox should be checked$/ do |label|
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
field_labeled(label).should be_checked
|
||||
Then /^the "([^"]*)" checkbox should be checked$/ do |label|
|
||||
field = field_labeled(label)
|
||||
if field.respond_to? :should
|
||||
field.should be_checked
|
||||
else
|
||||
assert field_labeled(label).checked?
|
||||
assert field.checked?
|
||||
end
|
||||
end
|
||||
|
||||
Then /^the "([^\"]*)" checkbox should not be checked$/ do |label|
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
field_labeled(label).should_not be_checked
|
||||
Then /^the "([^"]*)" checkbox should not be checked$/ do |label|
|
||||
field = field_labeled(label)
|
||||
if field.respond_to? :should_not
|
||||
field.should_not be_checked
|
||||
else
|
||||
assert !field_labeled(label).checked?
|
||||
assert !field.checked?
|
||||
end
|
||||
end
|
||||
|
||||
Then /^(?:|I )should be on (.+)$/ do |page_name|
|
||||
current_path = URI.parse(current_url).select(:path, :query).compact.join('?')
|
||||
if defined?(Spec::Rails::Matchers)
|
||||
current_path = URI.parse(current_url).path
|
||||
if current_path.respond_to? :should
|
||||
current_path.should == path_to(page_name)
|
||||
else
|
||||
assert_equal path_to(page_name), current_path
|
||||
end
|
||||
end
|
||||
|
||||
Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
|
||||
query = URI.parse(current_url).query
|
||||
actual_params = query ? CGI.parse(query) : {}
|
||||
expected_params = {}
|
||||
expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
|
||||
|
||||
if actual_params.respond_to? :should
|
||||
actual_params.should == expected_params
|
||||
else
|
||||
assert_equal expected_params, actual_params
|
||||
end
|
||||
end
|
||||
|
||||
Then /^show me the page$/ do
|
||||
save_and_open_page
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ require 'cucumber/web/tableish'
|
|||
|
||||
require 'webrat'
|
||||
require 'webrat/core/matchers'
|
||||
# require 'cucumber/webrat/element_locator' # Deprecated in favor of #tableish - remove this line if you don't use #element_at or #table_at
|
||||
|
||||
Webrat.configure do |config|
|
||||
config.mode = :rails
|
||||
|
|
@ -47,11 +46,12 @@ ActionController::Base.allow_rescue = false
|
|||
# subsequent scenarios. If you do this, we recommend you create a Before
|
||||
# block that will explicitly put your database in a known state.
|
||||
Cucumber::Rails::World.use_transactional_fixtures = true
|
||||
|
||||
# How to clean your database when transactions are turned off. See
|
||||
# http://github.com/bmabey/database_cleaner for more info.
|
||||
require 'database_cleaner'
|
||||
DatabaseCleaner.strategy = :truncation
|
||||
|
||||
require 'factory_girl'
|
||||
Dir.glob(File.join(File.dirname(__FILE__), '../../spec/factories/*.rb')).each {|f| require f }
|
||||
if defined?(ActiveRecord::Base)
|
||||
begin
|
||||
require 'database_cleaner'
|
||||
DatabaseCleaner.strategy = :truncation
|
||||
rescue LoadError => ignore_if_database_cleaner_not_present
|
||||
end
|
||||
end
|
||||
|
|
|
|||
2
features/support/init_factory_girl.rb
Normal file
2
features/support/init_factory_girl.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
require 'factory_girl'
|
||||
Dir.glob(File.join(File.dirname(__FILE__), '../../spec/factories/*.rb')).each {|f| require f }
|
||||
|
|
@ -1,27 +1,65 @@
|
|||
module NavigationHelpers
|
||||
# Maps a name to a path. Used by the
|
||||
#
|
||||
# When /^I go to (.+)$/ do |page_name|
|
||||
#
|
||||
# step definition in web_steps.rb
|
||||
#
|
||||
def path_to(page_name)
|
||||
case page_name
|
||||
|
||||
when /the homepage/
|
||||
root_path
|
||||
when /the home page/
|
||||
|
||||
when /the home\s?page/
|
||||
root_path
|
||||
when /the statistics page/
|
||||
stats_path
|
||||
when /the signup page/
|
||||
"/users/new"
|
||||
signup_path
|
||||
when /the login page/
|
||||
login_path
|
||||
when /the notes page/
|
||||
notes_path
|
||||
when /the contexts page/
|
||||
contexts_path
|
||||
|
||||
# Add more page name => path mappings here
|
||||
|
||||
when /the manage users page/
|
||||
users_path
|
||||
when /the repeating todos page/
|
||||
recurring_todos_path
|
||||
when /the integrations page/
|
||||
integrations_path
|
||||
when /the tickler page/
|
||||
tickler_path
|
||||
when /the export page/
|
||||
data_path
|
||||
when /the preference page/
|
||||
preferences_path
|
||||
when /the rest api docs page/
|
||||
rest_api_docs_path
|
||||
when /the search page/
|
||||
search_path
|
||||
when /the starred page/
|
||||
tag_path("starred")
|
||||
when /the feeds page/
|
||||
feeds_path
|
||||
when /the context page for "([^\"]*)" for user "([^\"]*)"/i
|
||||
context_path(User.find_by_login($2).contexts.find_by_name($1))
|
||||
when /the "([^\"]*)" project for user "([^\"]*)"/i
|
||||
project_path(User.find_by_login($2).projects.find_by_name($1))
|
||||
|
||||
# Add more mappings here.
|
||||
# Here is an example that pulls values out of the Regexp:
|
||||
#
|
||||
# when /^(.*)'s profile page$/i
|
||||
# user_profile_path(User.find_by_login($1))
|
||||
|
||||
else
|
||||
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
|
||||
"Now, go and add a mapping in features/support/paths.rb"
|
||||
begin
|
||||
page_name =~ /the (.*) page/
|
||||
path_components = $1.split(/\s+/)
|
||||
self.send(path_components.push('path').join('_').to_sym)
|
||||
rescue Object => e
|
||||
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
|
||||
"Now, go and add a mapping in #{__FILE__}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,22 +1,13 @@
|
|||
Webrat.configure do |config|
|
||||
config.mode = :selenium
|
||||
config.application_environment = :selenium
|
||||
config.selenium_browser_startup_timeout = 30
|
||||
#config.selenium_server_address = "localhost"
|
||||
end
|
||||
if ENV["RAILS_ENV"] == "selenium"
|
||||
puts "Configuring to use Selenium with Webrat for Cucumber stories"
|
||||
Webrat.configure do |config|
|
||||
config.mode = :selenium
|
||||
config.application_environment = :selenium
|
||||
config.selenium_browser_startup_timeout = 30
|
||||
# use only if you run a separate selenium server instance and do not
|
||||
# want webrat to start one for you
|
||||
#config.selenium_server_address = "localhost"
|
||||
end
|
||||
|
||||
Cucumber::Rails::World.use_transactional_fixtures = false
|
||||
|
||||
require 'database_cleaner'
|
||||
|
||||
# clean the database once when starting
|
||||
DatabaseCleaner.clean_with :truncation
|
||||
DatabaseCleaner.strategy = :truncation
|
||||
|
||||
Before do
|
||||
DatabaseCleaner.start
|
||||
end
|
||||
|
||||
After do
|
||||
DatabaseCleaner.clean
|
||||
end
|
||||
Cucumber::Rails::World.use_transactional_fixtures = false
|
||||
end
|
||||
16
features/support/world.rb
Normal file
16
features/support/world.rb
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
module TracksStepHelper
|
||||
def submit_multiple_next_action_form
|
||||
selenium.click("xpath=//form[@id='todo-form-multi-new-action']//button[@id='todo_multi_new_action_submit']", :wait_for => :ajax, :javascript_framework => :jquery)
|
||||
end
|
||||
|
||||
def submit_next_action_form
|
||||
selenium.click("xpath=//form[@id='todo-form-new-action']//button[@id='todo_new_action_submit']", :wait_for => :ajax, :javascript_framework => :jquery)
|
||||
end
|
||||
|
||||
def submit_new_context_form
|
||||
selenium.click("xpath=//form[@id='context-form']//button[@id='context_new_submit']", :wait_for => :ajax, :javascript_framework => :jquery)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
World(TracksStepHelper)
|
||||
32
lib/tasks/cucumber-tracks.rake
Normal file
32
lib/tasks/cucumber-tracks.rake
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
|
||||
# It is recommended to regenerate this file in the future when you upgrade to a
|
||||
# newer version of cucumber-rails. Consider adding your own code to a new file
|
||||
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
||||
# files.
|
||||
|
||||
vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
|
||||
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
|
||||
|
||||
begin
|
||||
require 'cucumber/rake/task'
|
||||
|
||||
namespace :cucumber do
|
||||
Cucumber::Rake::Task.new({:selenium => :env_to_selenium}, 'Run features that require selenium') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'selenium'
|
||||
end
|
||||
|
||||
task :env_to_selenium => 'db:test:prepare' do
|
||||
ENV['RAILS_ENV'] = 'selenium'
|
||||
end
|
||||
|
||||
desc 'Run all features'
|
||||
task :all => [:ok, :wip, :selenium]
|
||||
end
|
||||
rescue LoadError
|
||||
desc 'cucumber rake task not available (cucumber not installed)'
|
||||
task :cucumber do
|
||||
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
||||
end
|
||||
end
|
||||
|
|
@ -7,48 +7,47 @@
|
|||
|
||||
unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks
|
||||
|
||||
vendored_cucumber_bin = Dir["#{RAILS_ROOT}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
|
||||
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
|
||||
vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
|
||||
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
|
||||
|
||||
begin
|
||||
require 'cucumber/rake/task'
|
||||
begin
|
||||
require 'cucumber/rake/task'
|
||||
|
||||
namespace :cucumber do
|
||||
Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
|
||||
t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'default'
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'wip'
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new({:selenium => 'db:test:prepare'}, 'Run features that require selenium') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'selenium'
|
||||
ENV['RAILS_ENV'] = "selenium" # switch to selenium environment
|
||||
end
|
||||
|
||||
desc 'Run all features'
|
||||
task :all => [:ok, :wip]
|
||||
namespace :cucumber do
|
||||
Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
|
||||
t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'default'
|
||||
end
|
||||
desc 'Alias for cucumber:ok'
|
||||
task :cucumber => 'cucumber:ok'
|
||||
|
||||
task :default => :cucumber
|
||||
Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'wip'
|
||||
end
|
||||
|
||||
task :features => :cucumber do
|
||||
STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
|
||||
end
|
||||
rescue LoadError
|
||||
desc 'cucumber rake task not available (cucumber not installed)'
|
||||
task :cucumber do
|
||||
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
||||
Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'rerun'
|
||||
end
|
||||
|
||||
desc 'Run all features'
|
||||
task :all => [:ok, :wip]
|
||||
end
|
||||
desc 'Alias for cucumber:ok'
|
||||
task :cucumber => 'cucumber:ok'
|
||||
|
||||
task :default => :cucumber
|
||||
|
||||
task :features => :cucumber do
|
||||
STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
|
||||
end
|
||||
rescue LoadError
|
||||
desc 'cucumber rake task not available (cucumber not installed)'
|
||||
task :cucumber do
|
||||
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 801 B |
Binary file not shown.
|
Before Width: | Height: | Size: 794 B |
BIN
public/images/mobile_notes.png
Normal file
BIN
public/images/mobile_notes.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
|
|
@ -1,17 +1,17 @@
|
|||
var TracksForm = {
|
||||
toggle: function(toggleDivId, formContainerId, formId, hideLinkText,
|
||||
toggle: function(toggleLinkId, formContainerId, formId, hideLinkText,
|
||||
hideLinkTitle, showLinkText, showLinkTitle) {
|
||||
$('#'+formContainerId).toggle();
|
||||
toggleDiv = $('#'+toggleDivId);
|
||||
toggleLink = toggleDiv.find('a');
|
||||
if (toggleDiv.hasClass('hide_form')) {
|
||||
form=$('#'+formContainerId)
|
||||
form.toggle();
|
||||
toggleLink = $('#'+toggleLinkId);
|
||||
if (!form.is(':visible')) {
|
||||
toggleLink.text(showLinkText).attr('title', showLinkTitle);
|
||||
}
|
||||
else {
|
||||
toggleLink.text(hideLinkText).attr('title', hideLinkTitle);
|
||||
$('#'+formId+' input:text:first').focus();
|
||||
}
|
||||
toggleDiv.toggleClass('hide_form');
|
||||
toggleLink.parent().toggleClass('hide_form');
|
||||
},
|
||||
hide_all_recurring: function () {
|
||||
$.each(['daily', 'weekly', 'monthly', 'yearly'], function(){
|
||||
|
|
@ -49,7 +49,7 @@ $.fn.clearForm = function() {
|
|||
|
||||
/* Set up authenticity token properly */
|
||||
$(document).ajaxSend(function(event, request, settings) {
|
||||
if ( settings.type == 'POST' ) {
|
||||
if ( settings.type == 'POST' || settings.type == 'post' ) {
|
||||
if(typeof(AUTH_TOKEN) != 'undefined'){
|
||||
settings.data = (settings.data ? settings.data + "&" : "")
|
||||
+ "authenticity_token=" + encodeURIComponent( AUTH_TOKEN ) + "&"
|
||||
|
|
@ -137,8 +137,8 @@ function setup_container_toggles(){
|
|||
});
|
||||
}
|
||||
|
||||
function askIfNewContextProvided() {
|
||||
var givenContextName = $('#todo_context_name').val();
|
||||
function askIfNewContextProvided(source) {
|
||||
var givenContextName = $('#'+source+'todo_context_name').val();
|
||||
var contextNames = [];
|
||||
var contextNamesRequest = $.ajax({url: relative_to_root('contexts.autocomplete'),
|
||||
async: false,
|
||||
|
|
@ -200,6 +200,10 @@ function project_defaults(){
|
|||
}
|
||||
|
||||
function enable_rich_interaction(){
|
||||
/* fix for #1036 where closing a edit form before the autocomplete was filled
|
||||
* resulted in a dropdown box that could not be removed. We remove all
|
||||
* autocomplete boxes the hard way */
|
||||
$('.ac_results').remove();
|
||||
$('input.Date').datepicker({'dateFormat': dateFormat, 'firstDay': weekStart});
|
||||
/* Autocomplete */
|
||||
$('input[name=context_name]').autocomplete(
|
||||
|
|
@ -229,8 +233,9 @@ function enable_rich_interaction(){
|
|||
/* Drag & Drop for successor/predecessor */
|
||||
function drop_todo(evt, ui) {
|
||||
dragged_todo = ui.draggable[0].id.split('_')[2];
|
||||
dropped_todo = $(this).parents('.item-show').get(0).id.split('_')[2];
|
||||
dropped_todo = this.id.split('_')[2];
|
||||
ui.draggable.remove();
|
||||
$('.drop_target').hide(); // IE8 doesn't call stop() in this situation
|
||||
$(this).block({message: null});
|
||||
$.post(relative_to_root('todos/add_predecessor'),
|
||||
{successor: dragged_todo, predecessor: dropped_todo},
|
||||
|
|
@ -247,7 +252,7 @@ function enable_rich_interaction(){
|
|||
start: drag_todo,
|
||||
stop: function() {$('.drop_target').hide();}});
|
||||
|
||||
$('.successor_target').droppable({drop: drop_todo,
|
||||
$('.item-show').droppable({drop: drop_todo,
|
||||
tolerance: 'pointer',
|
||||
hoverClass: 'hover'});
|
||||
|
||||
|
|
@ -259,9 +264,8 @@ function enable_rich_interaction(){
|
|||
ui.draggable.remove();
|
||||
target.block({message: null});
|
||||
setTimeout(function() {target.show()}, 0);
|
||||
$.post(relative_to_root('todos/update'),
|
||||
{id: dragged_todo,
|
||||
"todo[id]": dragged_todo,
|
||||
$.post(relative_to_root('todos/change_context'),
|
||||
{"todo[id]": dragged_todo,
|
||||
"todo[context_id]": context_id},
|
||||
function(){target.unblock(); target.hide();}, 'script');
|
||||
}
|
||||
|
|
@ -273,6 +277,8 @@ function enable_rich_interaction(){
|
|||
|
||||
/* Reset auto updater */
|
||||
field_touched = false;
|
||||
|
||||
$('h2#project_name').editable(save_project_name, {style: 'padding:0px', submit: "OK"});
|
||||
}
|
||||
|
||||
/* Auto-refresh */
|
||||
|
|
@ -371,11 +377,30 @@ $(document).ready(function() {
|
|||
setup_container_toggles();
|
||||
|
||||
$('#toggle_action_new').click(function(){
|
||||
if ($("#todo_multi_add").is(':visible')) { /* hide multi next action form first */
|
||||
$('#todo_new_action').show();
|
||||
$('#todo_multi_add').hide();
|
||||
$('a#toggle_multi').text("Add multiple next actions");
|
||||
}
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
$('#toggle_multi').click(function(){
|
||||
if ($("#todo_multi_add").is(':visible')) {
|
||||
$('#todo_new_action').show();
|
||||
$('#todo_multi_add').hide();
|
||||
$('a#toggle_multi').text("Add multiple next actions");
|
||||
} else {
|
||||
$('#todo_new_action').hide();
|
||||
$('#todo_multi_add').show();
|
||||
$('a#toggle_multi').text("Add single next action");
|
||||
$('a#toggle_action_new').text('« Hide form');
|
||||
}
|
||||
});
|
||||
|
||||
$('.edit-form a.negative').live('click', function(){
|
||||
$(this).parents('.container').find('.item-show').show();
|
||||
$(this).parents('.container').find('.project').show();
|
||||
|
|
@ -462,8 +487,6 @@ $(document).ready(function() {
|
|||
return(value);
|
||||
};
|
||||
|
||||
$('h2#project_name').editable(save_project_name, {style: 'padding:0px', submit: "OK"});
|
||||
|
||||
$('.alphabetize_link').click(function(evt){
|
||||
evt.preventDefault();
|
||||
if(confirm('Are you sure that you want to sort these projects alphabetically? This will replace the existing sort order.')){
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
4
public/javascripts/swfobject.js
Normal file
4
public/javascripts/swfobject.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -35,7 +35,7 @@
|
|||
}
|
||||
|
||||
.ac_loading {
|
||||
background: white url('indicator.gif') right center no-repeat;
|
||||
background: white url('../images/waiting.gif') right center no-repeat;
|
||||
}
|
||||
|
||||
.ac_odd {
|
||||
|
|
|
|||
|
|
@ -825,7 +825,7 @@ input#go_to_project, input#context_hide {
|
|||
float: right;
|
||||
}
|
||||
|
||||
#todo-form-new-action .submit_box, #project_form .submit_box, #context_form .submit_box {
|
||||
#todo-form-new-action .submit_box, #project_form .submit_box, #context_form .submit_box, #todo-form-multi-new-action .submit_box {
|
||||
height: 25px;
|
||||
padding: 5px 0;
|
||||
text-align: center;
|
||||
|
|
@ -943,22 +943,15 @@ div.message {
|
|||
display:none;
|
||||
}
|
||||
|
||||
.successor_target {
|
||||
background-image:url("../images/add_successor_off.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center right;
|
||||
}
|
||||
|
||||
.successor_target.hover {
|
||||
background-image:url("../images/add_successor_on.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center right;
|
||||
.hover {
|
||||
background: #EAEAEA;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.context_target {
|
||||
height: 15px;
|
||||
margin: 4px;
|
||||
border: thick dotted #CCC;
|
||||
border: 4px dotted #999;
|
||||
}
|
||||
|
||||
.context_target.hover {
|
||||
|
|
|
|||
BIN
public/swfs/expressInstall.swf
Executable file
BIN
public/swfs/expressInstall.swf
Executable file
Binary file not shown.
0
public/swf/open-flash-chart.swf → public/swfs/open-flash-chart.swf
Executable file → Normal file
0
public/swf/open-flash-chart.swf → public/swfs/open-flash-chart.swf
Executable file → Normal file
|
|
@ -3,4 +3,10 @@ Factory.define :user do |u|
|
|||
u.password "secret"
|
||||
u.password_confirmation { |user| user.password }
|
||||
u.is_admin false
|
||||
end
|
||||
|
||||
Factory.define :context do |c|
|
||||
c.sequence(:name) { |n| "testcontext#{n}" }
|
||||
c.hide false
|
||||
c.created_at Time.now.utc
|
||||
end
|
||||
30
test/fixtures/recurring_todos.yml
vendored
30
test/fixtures/recurring_todos.yml
vendored
|
|
@ -1,23 +1,26 @@
|
|||
<%
|
||||
|
||||
def today
|
||||
Time.zone.now.beginning_of_day.to_s(:db)
|
||||
Time.zone.now.beginning_of_day.to_s(:db)
|
||||
end
|
||||
|
||||
def next_week
|
||||
1.week.from_now.beginning_of_day.to_s(:db)
|
||||
1.week.from_now.beginning_of_day.to_s(:db)
|
||||
end
|
||||
|
||||
def last_week
|
||||
1.week.ago.beginning_of_day.to_s(:db)
|
||||
1.week.ago.beginning_of_day.to_s(:db)
|
||||
end
|
||||
|
||||
def two_weeks_ago
|
||||
2.weeks.ago.beginning_of_day.to_s(:db)
|
||||
2.weeks.ago.beginning_of_day.to_s(:db)
|
||||
end
|
||||
|
||||
def two_weeks_hence
|
||||
2.weeks.from_now.beginning_of_day.to_s(:db)
|
||||
2.weeks.from_now.beginning_of_day.to_s(:db)
|
||||
end
|
||||
|
||||
def way_back
|
||||
Time.zone.local(2008,1,1)
|
||||
end
|
||||
|
||||
%>
|
||||
|
|
@ -30,7 +33,7 @@ call_bill_gates_every_day:
|
|||
description: Call Bill Gates every day
|
||||
notes: ~
|
||||
state: active
|
||||
start_from: ~
|
||||
start_from: <%= way_back %>
|
||||
ends_on: no_end_date
|
||||
end_date: ~
|
||||
number_of_occurences: ~
|
||||
|
|
@ -38,6 +41,7 @@ call_bill_gates_every_day:
|
|||
show_from_delta: ~
|
||||
recurring_period: daily
|
||||
recurrence_selector: ~
|
||||
show_always: 0
|
||||
every_other1: 1
|
||||
every_other2: ~
|
||||
every_other3: ~
|
||||
|
|
@ -62,9 +66,10 @@ call_bill_gates_every_workday:
|
|||
number_of_occurences: ~
|
||||
target: due_date
|
||||
show_from_delta: ~
|
||||
show_always: 0
|
||||
recurring_period: daily
|
||||
recurrence_selector: ~
|
||||
every_other1: ~
|
||||
every_other1: 1
|
||||
every_other2: ~
|
||||
every_other3: ~
|
||||
every_day: ~
|
||||
|
|
@ -82,7 +87,7 @@ call_bill_gates_every_week:
|
|||
description: Call Bill Gates every week
|
||||
notes: ~
|
||||
state: active
|
||||
start_from: ~
|
||||
start_from: <%= way_back %>
|
||||
ends_on: no_end_date
|
||||
end_date: ~
|
||||
number_of_occurences: ~
|
||||
|
|
@ -90,6 +95,7 @@ call_bill_gates_every_week:
|
|||
show_from_delta: ~
|
||||
recurring_period: weekly
|
||||
recurrence_selector: ~
|
||||
show_always: 0
|
||||
every_other1: 2
|
||||
every_other2: ~
|
||||
every_other3: ~
|
||||
|
|
@ -108,7 +114,7 @@ check_with_bill_every_last_friday_of_month:
|
|||
description: Check with Bill every last friday of the month
|
||||
notes: ~
|
||||
state: active
|
||||
start_from: ~
|
||||
start_from: <%= way_back %>
|
||||
ends_on: no_end_date
|
||||
end_date: ~
|
||||
number_of_occurences: ~
|
||||
|
|
@ -116,6 +122,7 @@ check_with_bill_every_last_friday_of_month:
|
|||
show_from_delta: 5
|
||||
recurring_period: monthly
|
||||
recurrence_selector: 1
|
||||
show_always: 0
|
||||
every_other1: 1
|
||||
every_other2: 2
|
||||
every_other3: 5
|
||||
|
|
@ -134,12 +141,13 @@ birthday_reinier:
|
|||
description: Congratulate Reinier on his birthday
|
||||
notes: ~
|
||||
state: active
|
||||
start_from: ~
|
||||
start_from: <%= way_back %>
|
||||
ends_on: no_end_date
|
||||
end_date: ~
|
||||
number_of_occurences: ~
|
||||
target: due_date
|
||||
show_from_delta: 5
|
||||
show_always: 0
|
||||
recurring_period: yearly
|
||||
recurrence_selector: 0
|
||||
every_other1: 8
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class LoginControllerTest < ActionController::TestCase
|
|||
def test_login_with_no_users_redirects_to_signup
|
||||
User.delete_all
|
||||
get :login
|
||||
assert_redirected_to :controller => 'users', :action => 'new'
|
||||
assert_redirected_to signup_url
|
||||
end
|
||||
|
||||
def test_logout
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ class RecurringTodosControllerTest < ActionController::TestCase
|
|||
"recurring_period"=>"yearly",
|
||||
"recurring_show_days_before"=>"10",
|
||||
"recurring_target"=>"due_date",
|
||||
"recurring_show_always" => "1",
|
||||
"start_from"=>"18/08/2008",
|
||||
"weekly_every_x_week"=>"1",
|
||||
"weekly_return_monday"=>"m",
|
||||
|
|
@ -110,6 +111,9 @@ class RecurringTodosControllerTest < ActionController::TestCase
|
|||
@yearly.every_other1 = target_date.day
|
||||
@yearly.every_other2 = target_date.month
|
||||
@yearly.show_from_delta = 10
|
||||
# unless @yearly.valid?
|
||||
# @yearly.errors.each {|obj, error| puts error}
|
||||
# end
|
||||
assert @yearly.save
|
||||
|
||||
# toggle twice to force generation of new todo
|
||||
|
|
@ -155,6 +159,7 @@ class RecurringTodosControllerTest < ActionController::TestCase
|
|||
"recurring_period"=>"yearly",
|
||||
"recurring_show_days_before"=>"0",
|
||||
"recurring_target"=>"due_date",
|
||||
"recurring_show_always" => "1",
|
||||
"start_from"=>"1/10/2012", # adjust after 2012
|
||||
"weekly_every_x_week"=>"1",
|
||||
"weekly_return_monday"=>"w",
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ class TodosControllerTest < ActionController::TestCase
|
|||
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
|
||||
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
|
||||
"project_id"=>"1",
|
||||
"notes"=>"test notes", "description"=>"test_mobile_create_action", "state"=>"0"}}
|
||||
"notes"=>"test notes", "description"=>"test_mobile_create_action"}}
|
||||
t = Todo.find_by_description("test_mobile_create_action")
|
||||
assert_not_nil t
|
||||
assert_equal 2, t.context_id
|
||||
|
|
@ -347,7 +347,7 @@ class TodosControllerTest < ActionController::TestCase
|
|||
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
|
||||
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
|
||||
"project_id"=>"1",
|
||||
"notes"=>"test notes", "state"=>"0"}, "tag_list"=>"test, test2"}
|
||||
"notes"=>"test notes"}, "tag_list"=>"test, test2"}
|
||||
assert_template 'todos/new'
|
||||
end
|
||||
|
||||
|
|
@ -376,12 +376,16 @@ class TodosControllerTest < ActionController::TestCase
|
|||
# change recurrence pattern to monthly and set show_from 2 days before due
|
||||
# date this forces the next todo to be put in the tickler
|
||||
recurring_todo_1.show_from_delta = 2
|
||||
recurring_todo_1.show_always = 0
|
||||
recurring_todo_1.target = 'due_date'
|
||||
recurring_todo_1.recurring_period = 'monthly'
|
||||
recurring_todo_1.recurrence_selector = 0
|
||||
recurring_todo_1.every_other1 = 1
|
||||
recurring_todo_1.every_other2 = 2
|
||||
recurring_todo_1.every_other3 = 5
|
||||
recurring_todo_1.save
|
||||
# use assert to catch validation errors if present. we need to replace
|
||||
# this with a good factory implementation
|
||||
assert recurring_todo_1.save
|
||||
|
||||
# mark next_todo as complete by toggle_check
|
||||
xhr :post, :toggle_check, :id => next_todo.id, :_source_view => 'todo'
|
||||
|
|
@ -416,7 +420,7 @@ class TodosControllerTest < ActionController::TestCase
|
|||
recurring_todo_1.recurrence_selector = 0
|
||||
recurring_todo_1.every_other1 = today.day
|
||||
recurring_todo_1.every_other2 = 1
|
||||
recurring_todo_1.save
|
||||
assert recurring_todo_1.save
|
||||
|
||||
# mark todo_1 as complete by toggle_check, this gets rid of todo_1 that was
|
||||
# not correctly created from the adjusted recurring pattern we defined
|
||||
|
|
|
|||
|
|
@ -154,19 +154,19 @@ class UsersControllerTest < ActionController::TestCase
|
|||
def test_create_with_invalid_password_redirects_to_new_user_page
|
||||
login_as :admin_user
|
||||
post :create, :user => {:login => 'newbie', :password => '', :password_confirmation => ''}
|
||||
assert_redirected_to :controller => 'users', :action => 'new'
|
||||
assert_redirected_to signup_path
|
||||
end
|
||||
|
||||
def test_create_with_invalid_login_does_not_add_a_new_user
|
||||
login_as :admin_user
|
||||
post :create, :user => {:login => 'n', :password => 'newbiepass', :password_confirmation => 'newbiepass'}
|
||||
assert_redirected_to :controller => 'users', :action => 'new'
|
||||
assert_redirected_to signup_path
|
||||
end
|
||||
|
||||
def test_create_with_invalid_login_redirects_to_new_user_page
|
||||
login_as :admin_user
|
||||
post :create, :user => {:login => 'n', :password => 'newbiepass', :password_confirmation => 'newbiepass'}
|
||||
assert_redirected_to :controller => 'users', :action => 'new'
|
||||
assert_redirected_to signup_path
|
||||
end
|
||||
|
||||
def test_create_with_duplicate_login_does_not_add_a_new_user
|
||||
|
|
@ -179,7 +179,7 @@ class UsersControllerTest < ActionController::TestCase
|
|||
def test_create_with_duplicate_login_redirects_to_new_user_page
|
||||
login_as :admin_user
|
||||
post :create, :user => {:login => 'jane', :password => 'newbiepass', :password_confirmation => 'newbiepass'}
|
||||
assert_redirected_to :controller => 'users', :action => 'new'
|
||||
assert_redirected_to signup_path
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
setup :fixtures => :all
|
||||
login :as => 'admin'
|
||||
open "/contexts"
|
||||
click "css=#context_3 .buttons img.edit_item"
|
||||
wait_for_visible "edit_context_3"
|
||||
wait_for_not_visible "context_3"
|
||||
type "//div[@id='edit_context_3'] //input[@name='context[name]']", "telegraph"
|
||||
click "//div[@id='edit_context_3'] //button"
|
||||
wait_for_not_visible "edit_context_3"
|
||||
wait_for_visible "context_3"
|
||||
click "css=#context_3 .buttons img.delete_item"
|
||||
assert_confirmation "Are you sure that you want to delete the context 'telegraph'?"
|
||||
wait_for_visible "flash"
|
||||
wait_for_text "flash", "Deleted context 'telegraph'"
|
||||
wait_for_element_not_present "context_3"
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
setup :fixtures => :all
|
||||
login :as => 'admin'
|
||||
open "/contexts"
|
||||
click "css=#context_3 .buttons img.edit_item"
|
||||
wait_for_visible "edit_context_3"
|
||||
wait_for_not_visible "context_3"
|
||||
type "//div[@id='edit_context_3'] //input[@name='context[name]']", "telegraph"
|
||||
click "//div[@id='edit_context_3'] //button"
|
||||
wait_for_not_visible "edit_context_3"
|
||||
wait_for_visible "context_3"
|
||||
assert_text 'css=#context_3 .data a', 'telegraph'
|
||||
click "css=#context_3 .buttons img.edit_item"
|
||||
wait_for_visible "edit_context_3"
|
||||
wait_for_not_visible "context_3"
|
||||
type "//div[@id='edit_context_3'] //input[@name='context[name]']", "email"
|
||||
click "//div[@id='edit_context_3'] //button"
|
||||
wait_for_not_visible "edit_context_3"
|
||||
wait_for_visible "context_3"
|
||||
assert_text 'css=#context_3 .data a', 'email'
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
setup :fixtures => :users, :clear_tables => [:projects, :contexts, :todos]
|
||||
login :as => 'admin'
|
||||
open "/integrations"
|
||||
wait_for_element_present "no_context_msg"
|
||||
|
||||
open "/contexts"
|
||||
type "context_name", "my first context"
|
||||
click "context_new_submit"
|
||||
# wait for new context to appear before navigating away from project page to
|
||||
# make sure that the changes have been saved
|
||||
wait_for_text_present 'DRAG'
|
||||
|
||||
open "/integrations"
|
||||
wait_for_element_present "applescript1-contexts"
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
setup :fixtures => :users, :clear_tables => [:projects, :contexts, :todos]
|
||||
login :as => 'admin'
|
||||
open "/recurring_todos"
|
||||
click "css=#recurring_new_container a"
|
||||
click "recurring_todo_recurring_period_daily"
|
||||
assert_visible "recurring_daily"
|
||||
click "recurring_todo_recurring_period_weekly"
|
||||
assert_visible "recurring_weekly"
|
||||
click "recurring_todo_recurring_period_monthly"
|
||||
assert_visible "recurring_monthly"
|
||||
click "recurring_todo_recurring_period_yearly"
|
||||
assert_visible "recurring_yearly"
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
setup :fixtures => :all
|
||||
login :as => 'admin'
|
||||
open '/users'
|
||||
assert_text_present "John Deere"
|
||||
click "//tr[@id='user-3']//img"
|
||||
assert_confirmation "Warning: this will delete user 'john', all their actions, contexts, project and notes. Are you sure that you want to continue?"
|
||||
wait_for_text_not_present "John Deere"
|
||||
|
|
@ -124,7 +124,9 @@ class RecurringTodoTest < ActiveSupport::TestCase
|
|||
due_date = @weekly_every_day.get_due_date(@sunday)
|
||||
assert_equal @monday, due_date
|
||||
|
||||
# saturday is last day in week, so the next date should be sunday + n_weeks
|
||||
# saturday is last day in week, so the next date should be sunday + n-1 weeks
|
||||
# n-1 because sunday is already in the next week
|
||||
@weekly_every_day.every_other1 = 3
|
||||
due_date = @weekly_every_day.get_due_date(@saturday)
|
||||
assert_equal @sunday + 2.weeks, due_date
|
||||
|
||||
|
|
@ -141,6 +143,10 @@ class RecurringTodoTest < ActiveSupport::TestCase
|
|||
assert_equal @wednesday, due_date
|
||||
due_date = @weekly_every_day.get_due_date(@wednesday)
|
||||
assert_equal @tuesday+1.week, due_date
|
||||
|
||||
@weekly_every_day.every_day = ' s'
|
||||
due_date = @weekly_every_day.get_due_date(@sunday)
|
||||
assert_equal @saturday+1.week, due_date
|
||||
end
|
||||
|
||||
def test_monthly_pattern
|
||||
|
|
@ -201,11 +207,27 @@ class RecurringTodoTest < ActiveSupport::TestCase
|
|||
# same month, after second wednesday
|
||||
due_date = @yearly.get_due_date(Time.zone.local(2008,6,12)) # june 7th
|
||||
assert_equal Time.zone.local(2009,6,10), due_date # june 10th
|
||||
|
||||
# test handling of nil
|
||||
end
|
||||
|
||||
def test_next_todo_without_previous_todo
|
||||
# test handling of nil as previous
|
||||
#
|
||||
# start_from is way_back
|
||||
due_date1 = @yearly.get_due_date(nil)
|
||||
due_date2 = @yearly.get_due_date(Time.now.utc + 1.day)
|
||||
assert_equal due_date1, due_date2
|
||||
|
||||
# start_from is in the future
|
||||
@yearly.start_from = Time.now.utc + 1.week
|
||||
due_date1 = @yearly.get_due_date(nil)
|
||||
due_date2 = @yearly.get_due_date(Time.now.utc + 1.day)
|
||||
assert_equal due_date1, due_date2
|
||||
|
||||
# start_from is nil
|
||||
@yearly.start_from = nil
|
||||
due_date1 = @yearly.get_due_date(nil)
|
||||
due_date2 = @yearly.get_due_date(Time.now.utc + 1.day)
|
||||
assert_equal due_date1, due_date2
|
||||
end
|
||||
|
||||
def test_last_sunday_of_march
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/../test_helper'
|
|||
require 'date'
|
||||
|
||||
class TodoTest < ActiveSupport::TestCase
|
||||
fixtures :todos, :recurring_todos, :users, :contexts, :preferences, :tags, :taggings
|
||||
fixtures :todos, :recurring_todos, :users, :contexts, :preferences, :tags, :taggings, :projects
|
||||
|
||||
def setup
|
||||
@not_completed1 = Todo.find(1).reload
|
||||
|
|
@ -127,6 +127,20 @@ class TodoTest < ActiveSupport::TestCase
|
|||
assert t.active?
|
||||
end
|
||||
|
||||
def test_activate_also_clears_show_from
|
||||
# setup test case
|
||||
t = @not_completed1
|
||||
t.show_from = 1.week.from_now
|
||||
t.save!
|
||||
assert t.deferred?
|
||||
t.reload
|
||||
|
||||
# activate and check show_from
|
||||
t.activate!
|
||||
assert t.active?
|
||||
assert t.show_from.nil?
|
||||
end
|
||||
|
||||
def test_project_returns_null_object_when_nil
|
||||
t = @not_completed1
|
||||
assert !t.project.is_a?(NullProject)
|
||||
|
|
@ -177,5 +191,74 @@ class TodoTest < ActiveSupport::TestCase
|
|||
@not_completed1.toggle_star!
|
||||
assert !@not_completed1.starred?
|
||||
end
|
||||
|
||||
|
||||
def test_todo_specification_handles_null_project
|
||||
# @not_completed1 has a project
|
||||
todo_desc = @not_completed1.description
|
||||
assert_equal "\"#{todo_desc}\" <\"agenda\"; \"Make more money than Billy Gates\">", @not_completed1.specification
|
||||
|
||||
# now check on null
|
||||
@not_completed1.project = nil
|
||||
@not_completed1.save
|
||||
assert_equal "\"#{todo_desc}\" <\"agenda\"; \"(none)\">", @not_completed1.specification
|
||||
end
|
||||
|
||||
def test_todo_from_specification_should_return_todo
|
||||
todo = Todo.new
|
||||
|
||||
assert_equal @not_completed1, todo.todo_from_specification(@not_completed1.specification)
|
||||
|
||||
# should handle comma's in description (#975)
|
||||
@not_completed1.description = "test,1,2,3"
|
||||
@not_completed1.save
|
||||
assert_equal @not_completed1, todo.todo_from_specification(@not_completed1.specification)
|
||||
end
|
||||
|
||||
def test_todo_from_specification_should_return_nil_on_invalid_input
|
||||
todo = Todo.new
|
||||
todo_desc = @not_completed1.description
|
||||
project_name = @not_completed1.project.name
|
||||
context_name = @not_completed1.context.name
|
||||
|
||||
assert todo.todo_from_specification("").nil?
|
||||
assert todo.todo_from_specification("bla, bla , bla").nil?
|
||||
assert todo.todo_from_specification("#{todo_desc} <#{context_name}; #{project_name}>").nil? # missing \"
|
||||
end
|
||||
|
||||
def test_add_predecessor_list
|
||||
todo = Todo.new
|
||||
|
||||
single = @not_completed1.specification
|
||||
multi = single + "," + @not_completed2.specification
|
||||
|
||||
@predecessor_array = todo.add_predecessor_list(single)
|
||||
assert_not_nil @predecessor_array
|
||||
assert_equal 1, @predecessor_array.size
|
||||
|
||||
@predecessor_array = todo.add_predecessor_list(multi)
|
||||
assert_not_nil @predecessor_array
|
||||
assert_equal 2, @predecessor_array.size
|
||||
end
|
||||
|
||||
def test_add_predecessor_list_with_comma
|
||||
# test for #975
|
||||
todo = Todo.new
|
||||
|
||||
@not_completed1.description = "test,1,2,3"
|
||||
@not_completed1.save
|
||||
@not_completed2.description = "test,4,5,6"
|
||||
@not_completed2.save
|
||||
|
||||
single = @not_completed1.specification
|
||||
multi = single + "," + @not_completed2.specification
|
||||
|
||||
@predecessor_array = todo.add_predecessor_list(single)
|
||||
assert_not_nil @predecessor_array
|
||||
assert_equal 1, @predecessor_array.size
|
||||
|
||||
@predecessor_array = todo.add_predecessor_list(multi)
|
||||
assert_not_nil @predecessor_array
|
||||
assert_equal 2, @predecessor_array.size
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
35
vendor/plugins/flashobject_helper/README
vendored
35
vendor/plugins/flashobject_helper/README
vendored
|
|
@ -1,35 +0,0 @@
|
|||
FlashObject v.1.0
|
||||
====================
|
||||
This FlashObject Helper is based on the flash view helper of the plugin http://laszlo-plugin.rubyforge.org/
|
||||
|
||||
It's only copy the FlashObject.js (version 1.5) on your public/javascript directory and register it in the AssetTagHelper of rails,
|
||||
so for include the javascript in your page you only can add this (if you havn't yet):
|
||||
|
||||
<%= javascript_include_tag :defaults %>
|
||||
|
||||
or if you only need the flashobject add this:
|
||||
|
||||
<%= javascript_include_tag "flashobject" %>
|
||||
|
||||
The is very simple for add a Flash in your page add this:
|
||||
|
||||
<%= flashobject_tag "/source/of/your/flash.swf" %>
|
||||
|
||||
It's simple.
|
||||
|
||||
You can add some options if you want:
|
||||
|
||||
- div_id: the HTML +id+ of the +div+ element that is used to contain the Flash object; default "flashcontent"
|
||||
- flash_id: the +id+ of the Flash object itself.
|
||||
- background_color: the background color of the Flash object; default white
|
||||
- flash_version: the version of the Flash player that is required; default "7"
|
||||
- size: the size of the Flash object, in the form "100x100". Defaults to "100%x100%"
|
||||
- variables: a Hash of initialization variables that are passed to the object; default <tt>{:lzproxied => false}</tt>
|
||||
- parameters: a Hash of parameters that configure the display of the object; default <tt>{:scale => 'noscale'}</tt>
|
||||
- fallback_html: HTML text that is displayed when the Flash player is not available.
|
||||
|
||||
Example:
|
||||
<%= flashobject_tag "/source/of/your/flash.swf", :size => "350x320" %>
|
||||
|
||||
|
||||
Vist my website: http://blog.lipsiasoft.com
|
||||
4
vendor/plugins/flashobject_helper/init.rb
vendored
4
vendor/plugins/flashobject_helper/init.rb
vendored
|
|
@ -1,4 +0,0 @@
|
|||
# Author:: Davide D'Agostino Aka DAddYE
|
||||
# WebSite:: http://www.lipsiasoft.com
|
||||
|
||||
require 'flashobject_view_helper'
|
||||
5
vendor/plugins/flashobject_helper/install.rb
vendored
5
vendor/plugins/flashobject_helper/install.rb
vendored
|
|
@ -1,5 +0,0 @@
|
|||
require 'fileutils'
|
||||
|
||||
flashobject = File.dirname(__FILE__) + '/../../../public/javascripts/flashobject.js'
|
||||
FileUtils.cp File.dirname(__FILE__) + '/javascripts/flashobject.js', flashobject unless File.exist?(flashobject)
|
||||
puts IO.read(File.join(File.dirname(__FILE__), 'README'))
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,96 +0,0 @@
|
|||
# Author:: Davide D'Agostino aka DAddYE
|
||||
# WebSite:: http://www.lipsiasoft.com
|
||||
require 'action_view'
|
||||
|
||||
module ActionView #:nodoc:
|
||||
module Helpers # :nodoc:
|
||||
module FlashObjectHelper # :nodoc:
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
include InstanceMethods
|
||||
end
|
||||
end
|
||||
module InstanceMethods
|
||||
# Returns a set of tags that display a Flash object within an
|
||||
# HTML page.
|
||||
#
|
||||
# Options:
|
||||
# * <tt>:div_id</tt> - the HTML +id+ of the +div+ element that is used to contain the Flash object; default "flashcontent"
|
||||
# * <tt>:flash_id</tt> - the +id+ of the Flash object itself.
|
||||
# * <tt>:background_color</tt> - the background color of the Flash object; default white
|
||||
# * <tt>:flash_version</tt> - the version of the Flash player that is required; default "7"
|
||||
# * <tt>:size</tt> - the size of the Flash object, in the form "100x100". Defaults to "100%x100%"
|
||||
# * <tt>:variables</tt> - a Hash of initialization variables that are passed to the object; default <tt>{:lzproxied => false}</tt>
|
||||
# * <tt>:parameters</tt> - a Hash of parameters that configure the display of the object; default <tt>{:scale => 'noscale'}</tt>
|
||||
# * <tt>:fallback_html</tt> - HTML text that is displayed when the Flash player is not available.
|
||||
#
|
||||
# The following options are for developers. They default to true in
|
||||
# development mode, and false otherwise.
|
||||
# * <tt>:check_for_javascript_include</tt> - if true, the return value will cause the browser to display a diagnostic message if the FlashObject JavaScript was not included.
|
||||
# * <tt>:verify_file_exists</tt> - if true, the return value will cause the browser to display a diagnostic message if the Flash object does not exist.
|
||||
#
|
||||
# (This method is called flashobject_tag instead of flashobject_tag
|
||||
# because it returns a *sequence* of HTML tags: a +div+, followed by
|
||||
# a +script+.)
|
||||
def flashobject_tag source, options={}
|
||||
source = flash_path(source)
|
||||
query_params = '?' + options[:query_params].map{ |key, value| "#{key}=#{value}" }.join('&') if options[:query_params]
|
||||
div_id = options[:div_id] || "flashcontent_#{rand(1_100)}"
|
||||
flash_id = options[:flash_id] || File.basename(source, '.swf') + "_#{rand(1_100)}"
|
||||
width, height = (options[:size]||'100%x100%').scan(/^(\d*%?)x(\d*%?)$/).first
|
||||
background_color = options[:background_color] || '#ffffff'
|
||||
flash_version = options[:flash_version] || 7
|
||||
class_name = options[:class_name] || 'flash'
|
||||
variables = options.fetch(:variables, {})
|
||||
parameters = options.fetch(:parameters, {:scale => 'noscale'})
|
||||
fallback_html = options[:fallback_html] || %q{<p>Requires the Flash plugin. If the plugin is already installed, click <a href="?detectflash=false">here</a>.</p>}
|
||||
if options.fetch(:check_for_javascript_include, ENV['RAILS_ENV'] == 'development')
|
||||
check_for_javascript ="if (typeof FlashObject == 'undefined') document.getElementById('#{div_id}').innerHTML = '<strong>Warning:</strong> FlashObject is undefined. Did you forget to execute <tt>rake update_javascripts</tt>, or to include <tt><%= javascript_include_tag :defaults %></tt> in your view file?';"
|
||||
end
|
||||
return <<-"EOF"
|
||||
<div id="#{div_id}" class="#{class_name}" style="height: #{height}">
|
||||
#{fallback_html}
|
||||
</div>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
#{check_for_javascript}
|
||||
var fo = new FlashObject("#{source}#{query_params}", "#{flash_id}", "#{width}", "#{height}", "#{flash_version}", "#{background_color}");
|
||||
#{parameters.map{|k,v|%Q[fo.addParam("#{k}", "#{v}");]}.join("\n")}
|
||||
#{variables.map{|k,v|%Q[fo.addVariable("#{k}", "#{v}");]}.join("\n")}
|
||||
fo.write("#{div_id}");
|
||||
//]]>
|
||||
</script>
|
||||
EOF
|
||||
end
|
||||
|
||||
# Computes the path to a flash asset in the public swf directory.
|
||||
# If the +source+ filename has no extension, .swf will be appended.
|
||||
# Full paths from the document root will be passed through.
|
||||
#
|
||||
# flash_path "movie" # => /swf/movie.swf
|
||||
# flash_path "dir/movie.swf" # => /swf/dir/movie.swf
|
||||
# flash_path "/dir/movie" # => /dir/movie.swf
|
||||
def flash_path(source)
|
||||
#BROKEN IN RAILS 2.2 -- code below hacked in pending a refresh of this plugin or change to another --luke@lukemelia.com
|
||||
#compute_public_path(source, 'swf', 'swf', false)
|
||||
dir = "/swf/"
|
||||
if source !~ %r{^/}
|
||||
source = "#{dir}#{source}"
|
||||
end
|
||||
|
||||
relative_url_root = ActionController::Base.relative_url_root
|
||||
if source !~ %r{^#{relative_url_root}/}
|
||||
source = "#{relative_url_root}#{source}"
|
||||
end
|
||||
source
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActionView::Base.class_eval do
|
||||
include ActionView::Helpers::FlashObjectHelper
|
||||
end
|
||||
|
||||
ActionView::Helpers::AssetTagHelper.register_javascript_include_default 'flashobject'
|
||||
46
vendor/plugins/swf_fu/CHANGELOG.rdoc
vendored
Normal file
46
vendor/plugins/swf_fu/CHANGELOG.rdoc
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
= swf_fu --- History
|
||||
|
||||
== Version 1.4.0 - May 8, 2010
|
||||
|
||||
* Any option can be a block, in which case it is called (with the source swf passed as argument)
|
||||
|
||||
== Version 1.3.1 - February 5, 2010
|
||||
|
||||
* Improved compatibility with Rails 3.0: swf_tag now outputs html_safe content.
|
||||
|
||||
* Got rid of deprecation warning in Rails 2.2+ when using swf_tag in block form.
|
||||
|
||||
== Version 1.3.0 - June 20, 2009
|
||||
|
||||
* Updated to swf_object v2.2. Change should not be noticeable to users, except compatibility improvements and better auto install. Added the option +switch_off_auto_hide_show+.
|
||||
|
||||
== Version 1.2.0 - January 14, 2009
|
||||
|
||||
* flashvars[:id] will now default to the DOM id of the object. I didn't want to have any extra defaults than the very basic ones, but there is no easy way to get this from Flash (see http://www.actionscript.org/forums/showthread.php3?t=136044 ) and no easy way to specify that using +swf_default_options+.
|
||||
* If flashvars is a string (e.g. "myVar=myValue") it will be parsed into a hash so that the behaviour for default values apply to strings or hashes. swf_default_options[:flashvars] can also be a string and will also be parsed before being merged.
|
||||
* Small bug fix: the options passed as hashes (:flashvars, :parameters and :html_options) were changed if swf_default_options[:flashvars, ...] existed. They are now left unchanged.
|
||||
|
||||
== Version 1.1.0 - January 3, 2009
|
||||
|
||||
* Improved the way to specify alternate content
|
||||
|
||||
== Version 1.0.3 - January 3, 2009
|
||||
|
||||
* Improved javascript initialization
|
||||
|
||||
:initialize => [1, 2, 3] # produces in javascript: obj.initialize(1,2,3) instead of ([1,2,3])
|
||||
# no :initialize produces in javascript: obj.initialize() instead of (null)
|
||||
:initialize => nil # stil produces obj.initialize(null)
|
||||
|
||||
== Version 1.0.2 - January 3, 2009
|
||||
|
||||
* Bug fix for flashvars in dynamic method
|
||||
|
||||
== Version 1.0.1 - January 2, 2009
|
||||
|
||||
* File reorganization
|
||||
* Bug fix for default options
|
||||
|
||||
== Version 1.0 - X-mas, 2008
|
||||
|
||||
=== Initial release.
|
||||
31
vendor/plugins/swf_fu/FLASH_OBJECT.rdoc
vendored
Normal file
31
vendor/plugins/swf_fu/FLASH_OBJECT.rdoc
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
== Compatibility with FlashObject
|
||||
|
||||
This document is intended for users of FlashObject, a (much older) swf embedding plugin that inspired swf_fu.
|
||||
|
||||
You can choose to:
|
||||
|
||||
1) keep both. They won't interfere and +flashobject_tag+ will continue to use the older SWFObject 1.5 library.
|
||||
|
||||
2) remove FlashObject:
|
||||
|
||||
script/plugin remove flashobject_helper
|
||||
|
||||
You can also manually remove <tt>javascripts/flashobject.js</tt>
|
||||
|
||||
+swf_fu+ will take over the +flashobject_tag+ and will use the new SWFObject 2.2 library.
|
||||
This should not have impacts as long as:
|
||||
* your swf path is absolute (e.g. "/path/to/my_flash.swf"). If it is relative, move your swf file from 'public/' to the new 'public/swfs/' asset folder
|
||||
* you include the default javascripts (otherwise you need to include 'swfobject' explicitely and stop including 'flashobject')
|
||||
* you don't use the javascript object before the page is loaded. SWFObject 2.2 makes the changes to the web page later
|
||||
* you don't rely on the +verify_file_exists+ option (it doesn't do anything anymore)
|
||||
|
||||
In either case 1 or 2, you change existing calls to +flashobject_tag+ for +swf_tag+ at your leisure.
|
||||
The interface is similar and the main differences are some options name changes:
|
||||
:flash_id => :id
|
||||
:variables => :flashvars
|
||||
:background_color => options[:parameters][:bgcolor]
|
||||
|
||||
Moreover, the following defaults are gone:
|
||||
:flashvars[:lzproxied]
|
||||
:parameters[:scale]
|
||||
:parameters[:bgcolor]
|
||||
29
vendor/plugins/swf_fu/LICENSE
vendored
Normal file
29
vendor/plugins/swf_fu/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# swf_fu plugin for rails
|
||||
# Copyright (c) 2010, Marc-André Lafortune.
|
||||
# All rights reserved.
|
||||
# Inspired by FlashObject by Davide D'Agostino aka DAddYE (http://www.lipsiasoft.com)
|
||||
# Uses SWFObject.js 2.1 (http://code.google.com/p/swfobject)
|
||||
#
|
||||
# Licensed under the terms of the (modified) BSD License below:
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the <organization> nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
91
vendor/plugins/swf_fu/README.rdoc
vendored
Normal file
91
vendor/plugins/swf_fu/README.rdoc
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
= +swf_fu+
|
||||
|
||||
With the +swf_fu+ plugin, rails treats your swf files like any other asset (images, javascripts, etc...).
|
||||
|
||||
+swf_fu+ (pronounced "swif-fu", bonus joke for french speakers) uses SWFObject 2.2 to embed swf objects in HTML and supports all its options.
|
||||
SWFObject 2 is such a nice library that Adobe now uses it as the official way to embed swf!
|
||||
SWFObject's project can be found at http://code.google.com/p/swfobject
|
||||
|
||||
+swf_fu+ has been tested with rails v2.0 up to v3.0b and has decent test coverage so <tt>rake test:plugins</tt> should reveal any incompatibility. Comments and pull requests welcome: http://github.com/marcandre/swf_fu
|
||||
|
||||
== Install
|
||||
|
||||
Assuming you have git[http://git-scm.com/] installed (check with <tt>git version</tt>), it is easy to install from your applications directory:
|
||||
|
||||
rails plugin install git://github.com/marcandre/swf_fu.git # rails 3
|
||||
|
||||
script/plugin install git://github.com/marcandre/swf_fu.git # rails 2 (starting at 2.0.2)
|
||||
|
||||
For older versions of +rails+ or without +git+, you can always download
|
||||
+swf_fu+ from github[http://github.com/marcandre/swf_fu/archives/master] and then install it manually:
|
||||
|
||||
rails plugin install ~/Download/swf_fu # rails 3
|
||||
|
||||
script/plugin install ~/Downloads/swf_fu # rails 2.x
|
||||
|
||||
== Usage
|
||||
|
||||
=== Embedding in HTML
|
||||
|
||||
To embed a swf file, use +swf_tag+:
|
||||
<%= swf_tag "i_like_flashing" %>
|
||||
|
||||
Exactly like images and javascripts, +swf_tag+ will use +swf_path+
|
||||
to determine the path of the swf file; it will assume it is in <tt>/public/swfs/</tt>
|
||||
unless specified otherwise and it will add the ".swf" extension automatically.
|
||||
|
||||
You can specify alternate content either with the options <tt>:alt => "Get Flash!"</tt> or you can use +swf_tag+ as a block:
|
||||
|
||||
<% swf_tag "i_like_flashing" do %>
|
||||
Get Flash
|
||||
<% end %>
|
||||
|
||||
=== Options
|
||||
|
||||
* <tt>:id</tt> - the DOM +id+ of the flash +object+ element that is used to contain the Flash object; defaults to the name of the swf in +source+
|
||||
* <tt>:width, :height</tt> - the width & height of the Flash object. Defaults to "100%". These could also specified using :size
|
||||
* <tt>:size</tt> - the size of the Flash object, in the form "400x300".
|
||||
* <tt>:mode</tt> - Either :dynamic (default) or :static. Refer to SWFObject's doc[http://code.google.com/p/swfobject/wiki/documentation#Should_I_use_the_static_or_dynamic_publishing_method?]
|
||||
* <tt>:flashvars</tt> - a Hash of variables that are passed to the swf. Can also be a string like <tt>"foo=bar&hello=world"</tt>. Defaults to <tt>{:id => the DOM id}</tt>
|
||||
* <tt>:parameters</tt> - a Hash of configuration parameters for the swf. See Adobe's doc[http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12701#optional]
|
||||
* <tt>:alt</tt> - HTML text that is displayed when the Flash player is not available. Defaults to a "Get Flash" image pointing to Adobe Flash's installation page. This can also be specified as a block (see embedding section). In Rails 3, this text is _assumed_ to be HTML, so there is no need to call +html_safe+ on it.
|
||||
* <tt>:flash_version</tt> - the version of the Flash player that is required (e.g. "7" (default) or "8.1.0")
|
||||
* <tt>:auto_install</tt> - a swf file that will upgrade flash player if needed (defaults to "expressInstall" which was installed by +swf_fu+)
|
||||
* <tt>:javascript_class</tt> - specify a javascript class (e.g. "MyFlash") for your flash object. If it exists, the initialize method will be called.
|
||||
* <tt>:initialize</tt> - arguments to pass to the initialization method of your javascript class.
|
||||
* <tt>:div_id</tt> - the DOM +id+ of the containing div itself. Defaults to <tt>"#{option[:id]}_div"</tt>
|
||||
* <tt>:switch_off_auto_hide_show</tt> - switch off SWFObject's default hide/show behavior. SWFObject temporarily hides your SWF or alternative content until the library has decided which content to display. Defaults to nil.
|
||||
|
||||
You can override these default options with a global setting:
|
||||
|
||||
ActionView::Base.swf_default_options = {:mode => :static} # All swf_tag will use the static mode by default
|
||||
|
||||
Any of these options can be a +Proc+, in which case it will be called each time swf_tag is called.
|
||||
|
||||
For example, the following will generate unique IDs:
|
||||
|
||||
my_swf_counter = 0
|
||||
ActionView::Base.swf_default_options[:id] = Proc.new{"swf_unique_id_#{my_swf_counter+=1}"}
|
||||
|
||||
=== Javascript
|
||||
|
||||
+swf_fu+ will add 'swfobject' to the list of default javascript files. If you don't include
|
||||
the default javascripts, a simple <tt>javascript_include "swfobject"</tt> is needed.
|
||||
|
||||
=== swf_path
|
||||
|
||||
+swf_tag+ implements and relies on +swf_path+ which behaves in a similar fashion to +image_path+, +javascript_path+, etc...:
|
||||
|
||||
swf_path("example") => "/swfs/example.swf"
|
||||
swf_path("example.swf") => "/swfs/example.swf"
|
||||
swf_path("fonts/optima") => "/swfs/fonts/optima.swf"
|
||||
swf_path("/fonts/optima") => "/fonts/optima.swf"
|
||||
swf_path("http://www.example.com/game.swf") => "http://www.example.com/game.swf"
|
||||
|
||||
It takes into account the global setting +asset_host+, like any other asset:
|
||||
|
||||
ActionController::Base.asset_host = "http://assets.example.com"
|
||||
image_path("logo.jpg") => "http://assets.example.com/images/logo.jpg"
|
||||
swf_path("fonts/optima") => "http://assets.example.com/swfs/fonts/optima.swf""
|
||||
|
||||
Copyright (c) 2010 Marc-André Lafortune, released under the BSD license
|
||||
22
vendor/plugins/swf_fu/Rakefile
vendored
Normal file
22
vendor/plugins/swf_fu/Rakefile
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the swf_fu plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the swf_fu plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'Swf Fu'
|
||||
rdoc.options << '--line-numbers' << '--inline-source' << '-m README.rdoc'
|
||||
rdoc.rdoc_files.include('*.rdoc')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue