Merge branch 'master' of git://github.com/bsag/tracks into bsag

This commit is contained in:
Hans de Graaff 2010-08-13 15:14:34 +02:00
commit ab9c00090b
110 changed files with 2366 additions and 1165 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -90,7 +90,7 @@ class UsersController < ApplicationController
unless user.valid?
session['new_user'] = user
redirect_to :action => 'new'
redirect_to signup_path
return
end

View file

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

View file

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

View file

@ -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, /</, '&lt;').try(:gsub, />/, '&gt;'))
end
def raw_notes=(value)
self[:notes] = value
end

View file

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

View file

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

View file

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

View file

@ -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"})+" | ") -%>

View file

@ -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"}) %>
&nbsp;|&nbsp;
<%= 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>

View file

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

View file

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

View file

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

View file

@ -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&nbsp;</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">&nbsp;|&nbsp;</span><div class="tasks_sort">
<%= link_to("By number of tasks", actionize_projects_path(:state => state),
:class => "actionize_link", :title => "Sort these projects by number of tasks") %>
<%= 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>

View file

@ -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">&laquo; 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>

View file

@ -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();"

View file

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

View file

@ -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();"

View file

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

View file

@ -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="#">&laquo; Hide form</a>
<div id="toggle_forms" class="toggle_forms">
<a title="Hide new action form" accesskey="n" href="#" id="toggle_action_new">&laquo; 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>

View file

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

View file

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

View file

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

View file

@ -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')) +

View 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

View 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 %>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View file

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

View 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

View 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

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

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

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View 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

View 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

View file

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

View file

@ -0,0 +1,3 @@
When /^I visit the tag page for "([^"]*)"$/ do |tag_name|
visit "/todos/tag/#{tag_name}"
end

View file

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

View file

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

View file

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

View file

@ -0,0 +1,2 @@
require 'factory_girl'
Dir.glob(File.join(File.dirname(__FILE__), '../../spec/factories/*.rb')).each {|f| require f }

View file

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

View file

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

View 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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

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

File diff suppressed because one or more lines are too long

View file

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

View file

@ -825,7 +825,7 @@ input#go_to_project, input#context_hide {
float: right;
}
#todo-form-new-action .submit_box, #project_form .submit_box, #context_form .submit_box {
#todo-form-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

Binary file not shown.

View file

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +0,0 @@
# Author:: Davide D'Agostino Aka DAddYE
# WebSite:: http://www.lipsiasoft.com
require 'flashobject_view_helper'

View file

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

View file

@ -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>&lt;%= 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
View 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
View 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
View 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
View 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
View 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