Merge branch 'master' into rails4

Conflicts:
	Gemfile.lock
	config/routes.rb
This commit is contained in:
Reinier Balt 2013-06-17 09:25:02 +02:00
commit 4a485558e2
51 changed files with 5475 additions and 5015 deletions

2
.gitignore vendored
View file

@ -3,6 +3,8 @@
.dotest .dotest
.idea .idea
.rvmrc .rvmrc
.ruby-gemset
.ruby-version
.sass-cache/ .sass-cache/
.yardoc .yardoc
/.bundle /.bundle

View file

@ -9,9 +9,15 @@ GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
RedCloth (4.2.9) RedCloth (4.2.9)
<<<<<<< HEAD
aasm (3.0.18) aasm (3.0.18)
actionmailer (4.0.0.rc1) actionmailer (4.0.0.rc1)
actionpack (= 4.0.0.rc1) actionpack (= 4.0.0.rc1)
=======
aasm (3.0.19)
actionmailer (3.2.13)
actionpack (= 3.2.13)
>>>>>>> master
mail (~> 2.5.3) mail (~> 2.5.3)
actionpack (4.0.0.rc1) actionpack (4.0.0.rc1)
activesupport (= 4.0.0.rc1) activesupport (= 4.0.0.rc1)
@ -36,7 +42,11 @@ GEM
tzinfo (~> 0.3.37) tzinfo (~> 0.3.37)
acts_as_list (0.2.0) acts_as_list (0.2.0)
activerecord (>= 3.0) activerecord (>= 3.0)
<<<<<<< HEAD
arel (4.0.0) arel (4.0.0)
=======
arel (3.0.2)
>>>>>>> master
aruba (0.5.3) aruba (0.5.3)
childprocess (>= 0.3.6) childprocess (>= 0.3.6)
cucumber (>= 1.1.1) cucumber (>= 1.1.1)
@ -73,6 +83,10 @@ GEM
capybara (>= 1.1.2) capybara (>= 1.1.2)
cucumber (>= 1.1.8) cucumber (>= 1.1.8)
nokogiri (>= 1.5.0) nokogiri (>= 1.5.0)
<<<<<<< HEAD
=======
rails (~> 3.0)
>>>>>>> master
database_cleaner (1.0.1) database_cleaner (1.0.1)
diff-lcs (1.2.4) diff-lcs (1.2.4)
erubis (2.7.0) erubis (2.7.0)
@ -83,33 +97,49 @@ GEM
factory_girl_rails (4.2.1) factory_girl_rails (4.2.1)
factory_girl (~> 4.2.0) factory_girl (~> 4.2.0)
railties (>= 3.0.0) railties (>= 3.0.0)
ffi (1.8.1) ffi (1.9.0)
gherkin (2.12.0) gherkin (2.12.0)
multi_json (~> 1.3) multi_json (~> 1.3)
hike (1.2.2) hike (1.2.3)
htmlentities (4.3.1) htmlentities (4.3.1)
<<<<<<< HEAD
i18n (0.6.4) i18n (0.6.4)
jquery-rails (2.2.1) jquery-rails (2.2.1)
railties (>= 3.0, < 5.0) railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
=======
i18n (0.6.1)
journey (1.0.4)
jquery-rails (3.0.1)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
json (1.8.0)
>>>>>>> master
libv8 (3.11.8.17) libv8 (3.11.8.17)
mail (2.5.4) mail (2.5.4)
mime-types (~> 1.16) mime-types (~> 1.16)
treetop (~> 1.4.8) treetop (~> 1.4.8)
metaclass (0.0.1) metaclass (0.0.1)
mime-types (1.23) mime-types (1.23)
<<<<<<< HEAD
minitest (4.7.4) minitest (4.7.4)
mocha (0.14.0) mocha (0.14.0)
metaclass (~> 0.0.1) metaclass (~> 0.0.1)
multi_json (1.7.3) multi_json (1.7.3)
=======
mocha (0.14.0)
metaclass (~> 0.0.1)
multi_json (1.7.6)
>>>>>>> master
mysql2 (0.3.11) mysql2 (0.3.11)
nokogiri (1.5.9) nokogiri (1.5.10)
polyglot (0.3.3) polyglot (0.3.3)
rack (1.5.2) rack (1.5.2)
rack-mini-profiler (0.1.26) rack-mini-profiler (0.1.26)
rack (>= 1.1.3) rack (>= 1.1.3)
rack-test (0.6.2) rack-test (0.6.2)
rack (>= 1.0) rack (>= 1.0)
<<<<<<< HEAD
rails (4.0.0.rc1) rails (4.0.0.rc1)
actionmailer (= 4.0.0.rc1) actionmailer (= 4.0.0.rc1)
actionpack (= 4.0.0.rc1) actionpack (= 4.0.0.rc1)
@ -123,23 +153,49 @@ GEM
railties (4.0.0.rc1) railties (4.0.0.rc1)
actionpack (= 4.0.0.rc1) actionpack (= 4.0.0.rc1)
activesupport (= 4.0.0.rc1) activesupport (= 4.0.0.rc1)
=======
rails (3.2.13)
actionmailer (= 3.2.13)
actionpack (= 3.2.13)
activerecord (= 3.2.13)
activeresource (= 3.2.13)
activesupport (= 3.2.13)
bundler (~> 1.0)
railties (= 3.2.13)
rails_autolink (1.1.0)
rails (> 3.1)
railties (3.2.13)
actionpack (= 3.2.13)
activesupport (= 3.2.13)
rack-ssl (~> 1.3.2)
>>>>>>> master
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rake (10.0.4) rake (10.0.4)
<<<<<<< HEAD
=======
rdoc (3.12.2)
json (~> 1.4)
>>>>>>> master
ref (1.0.5) ref (1.0.5)
rspec-expectations (2.13.0) rspec-expectations (2.13.0)
diff-lcs (>= 1.1.3, < 2.0) diff-lcs (>= 1.1.3, < 2.0)
rubyzip (0.9.9) rubyzip (0.9.9)
safe_yaml (0.9.1) safe_yaml (0.9.3)
sanitize (2.0.3) sanitize (2.0.3)
nokogiri (>= 1.4.4, < 1.6) nokogiri (>= 1.4.4, < 1.6)
sass (3.2.9) sass (3.2.9)
<<<<<<< HEAD
sass-rails (4.0.0.rc1) sass-rails (4.0.0.rc1)
railties (>= 4.0.0.beta, < 5.0) railties (>= 4.0.0.beta, < 5.0)
=======
sass-rails (3.2.6)
railties (~> 3.2.0)
>>>>>>> master
sass (>= 3.1.10) sass (>= 3.1.10)
sprockets-rails (~> 2.0.0.rc0) sprockets-rails (~> 2.0.0.rc0)
tilt (~> 1.3) tilt (~> 1.3)
selenium-webdriver (2.32.1) selenium-webdriver (2.33.0)
childprocess (>= 0.2.5) childprocess (>= 0.2.5)
multi_json (~> 1.0) multi_json (~> 1.0)
rubyzip rubyzip
@ -172,7 +228,7 @@ GEM
tolk (1.3.9) tolk (1.3.9)
safe_yaml (~> 0.8) safe_yaml (~> 0.8)
will_paginate will_paginate
treetop (1.4.12) treetop (1.4.14)
polyglot polyglot
polyglot (>= 0.3.1) polyglot (>= 0.3.1)
tzinfo (0.3.37) tzinfo (0.3.37)

View file

@ -34,8 +34,9 @@ var TracksForm = {
$('#default_project_name_id').val(name); $('#default_project_name_id').val(name);
$('#project_name').html(name); $('#project_name').html(name);
}, },
set_tag_list: function (name) { set_tag_list_and_default_tag_list: function (name) {
$('input#tag_list').val(name); $('input#tag_list').val(name);
$('input#initial_tag_list').val(name);
}, },
set_tag_list_for_multi_add: function (name) { set_tag_list_for_multi_add: function (name) {
$('#multi_tag_list').val(name); $('#multi_tag_list').val(name);

View file

@ -21,6 +21,7 @@ class ApplicationController < ActionController::Base
before_filter :set_time_zone before_filter :set_time_zone
before_filter :set_zindex_counter before_filter :set_zindex_counter
before_filter :set_locale before_filter :set_locale
append_before_filter :set_group_view_by
prepend_before_filter :login_required prepend_before_filter :login_required
prepend_before_filter :enable_mobile_content_negotiation prepend_before_filter :enable_mobile_content_negotiation
after_filter :set_charset after_filter :set_charset
@ -291,4 +292,8 @@ class ApplicationController < ActionController::Base
render :template => 'todos/done' render :template => 'todos/done'
end end
def set_group_view_by
@group_view_by = params['_group_view_by'] || cookies['group_view_by'] || 'context'
end
end end

View file

@ -42,6 +42,7 @@ class ContextsController < ApplicationController
@max_completed = current_user.prefs.show_number_completed @max_completed = current_user.prefs.show_number_completed
@done = @context.todos.completed.limit(@max_completed).reorder("todos.completed_at DESC, todos.created_at DESC").includes(Todo::DEFAULT_INCLUDES) @done = @context.todos.completed.limit(@max_completed).reorder("todos.completed_at DESC, todos.created_at DESC").includes(Todo::DEFAULT_INCLUDES)
@not_done_todos = @context.todos.active.reorder("todos.due IS NULL, todos.due ASC, todos.created_at ASC").includes(Todo::DEFAULT_INCLUDES) @not_done_todos = @context.todos.active.reorder("todos.due IS NULL, todos.due ASC, todos.created_at ASC").includes(Todo::DEFAULT_INCLUDES)
@todos_without_project = @not_done_todos.select{|t| t.project.nil?}
@deferred_todos = @context.todos.deferred.includes(Todo::DEFAULT_INCLUDES) @deferred_todos = @context.todos.deferred.includes(Todo::DEFAULT_INCLUDES)
@pending_todos = @context.todos.pending.includes(Todo::DEFAULT_INCLUDES) @pending_todos = @context.todos.pending.includes(Todo::DEFAULT_INCLUDES)
@ -49,6 +50,9 @@ class ContextsController < ApplicationController
@projects = current_user.projects @projects = current_user.projects
@contexts = current_user.contexts @contexts = current_user.contexts
@projects_to_show = @projects.active
@contexts_to_show = [@context]
@count = @not_done_todos.count + @deferred_todos.count + @pending_todos.count @count = @not_done_todos.count + @deferred_todos.count + @pending_todos.count
@page_title = "TRACKS::Context: #{@context.name}" @page_title = "TRACKS::Context: #{@context.name}"
respond_to do |format| respond_to do |format|

View file

@ -47,29 +47,6 @@ class LoginController < ApplicationController
logout_user logout_user
end end
def expire_session
# this is a hack to enable cucumber to expire a session by calling this
# method. The method will be unavailable for production environment
@user.forget_me if logged_in?
cookies.delete :auth_token
session['user_id'] = nil
reset_session
unless Rails.env.production?
session['expiry_time'] = Time.now
respond_to do |format|
format.html { render :text => "Session expired for test purposes"}
format.js { render :text => "" }
end
else
respond_to do |format|
format.html { render :text => "Not available for production use"}
format.js { render :text => "" }
end
end
end
def check_expiry def check_expiry
# Gets called by periodically_call_remote to check whether # Gets called by periodically_call_remote to check whether
# the session has timed out yet # the session has timed out yet

View file

@ -131,12 +131,14 @@ class ProjectsController < ApplicationController
@not_done_todos = @project.todos.active_or_hidden.includes(Todo::DEFAULT_INCLUDES) @not_done_todos = @project.todos.active_or_hidden.includes(Todo::DEFAULT_INCLUDES)
@deferred_todos = @project.todos.deferred.includes(Todo::DEFAULT_INCLUDES) @deferred_todos = @project.todos.deferred.includes(Todo::DEFAULT_INCLUDES)
@pending_todos = @project.todos.pending.includes(Todo::DEFAULT_INCLUDES) @pending_todos = @project.todos.pending.includes(Todo::DEFAULT_INCLUDES)
@contexts_to_show = current_user.contexts.active
@projects_to_show = [@project]
@done = {} @done = {}
@done = @project.todos.completed. @done = @project.todos.completed.
reorder("todos.completed_at DESC"). reorder("todos.completed_at DESC").
limit(current_user.prefs.show_number_completed). limit(current_user.prefs.show_number_completed).
includes(Todo::DEFAULT_INCLUDES) unless current_user.prefs.show_number_completed == 0 includes(Todo::DEFAULT_INCLUDES) unless @max_completed == 0
@count = @not_done_todos.size @count = @not_done_todos.size
@down_count = @count + @deferred_todos.size + @pending_todos.size @down_count = @count + @deferred_todos.size + @pending_todos.size

View file

@ -3,7 +3,6 @@ class TodosController < ApplicationController
skip_before_filter :login_required, :only => [:index, :tag] skip_before_filter :login_required, :only => [:index, :tag]
prepend_before_filter :login_or_feed_token_required, :only => [:index, :tag] prepend_before_filter :login_or_feed_token_required, :only => [:index, :tag]
append_before_filter :find_and_activate_ready, :only => [:index, :list_deferred] append_before_filter :find_and_activate_ready, :only => [:index, :list_deferred]
append_before_filter :set_group_view_by, :only => [:index, :tag, :create, :list_deferred, :destroy, :defer, :update, :toggle_check]
protect_from_forgery :except => :check_deferred protect_from_forgery :except => :check_deferred
@ -594,13 +593,8 @@ class TodosController < ApplicationController
get_params_for_tag_view get_params_for_tag_view
@page_title = t('todos.tagged_page_title', :tag_name => @tag_title) @page_title = t('todos.tagged_page_title', :tag_name => @tag_title)
@source_view = params['_source_view'] || 'tag' @source_view = params['_source_view'] || 'tag'
if mobile? init_data_for_sidebar unless mobile?
# mobile tags are routed with :name ending on .m. So we need to chomp it
@tag_name = @tag_name.chomp('.m')
else
init_data_for_sidebar
end
todos_with_tag_ids = find_todos_with_tag_expr(@tag_expr) todos_with_tag_ids = find_todos_with_tag_expr(@tag_expr)
@ -833,8 +827,9 @@ class TodosController < ApplicationController
end end
def get_params_for_tag_view def get_params_for_tag_view
# use sanitize to prevent XSS attacks filter_format_for_tag_view
# use sanitize to prevent XSS attacks
@tag_expr = [] @tag_expr = []
@tag_expr << sanitize(params[:name]).split(',') @tag_expr << sanitize(params[:name]).split(',')
@tag_expr << sanitize(params[:and]).split(',') if params[:and] @tag_expr << sanitize(params[:and]).split(',') if params[:and]
@ -850,6 +845,27 @@ class TodosController < ApplicationController
@tag_title = @single_tag ? @tag_name : tag_title(@tag_expr) @tag_title = @single_tag ? @tag_name : tag_title(@tag_expr)
end end
def filter_format_for_tag_view
# routes for tag view do not set :format
if params[:name] =~ /.*\.m$/
set_format_for_tag_view(:m)
elsif params[:name] =~ /.*\.txt$/
set_format_for_tag_view(:txt)
# set content-type to text/plain or it remains text/html
response.headers["Content-Type"] = 'text/plain'
elsif params[:format].nil?
# if no format is given, default to html
# note that if url has ?format=m, we should not overwrite it here
request.format, params[:format] = :html, :html
end
end
def set_format_for_tag_view(format)
# tag name ends with .m, set format to :m en remove .m from name
request.format, params[:format] = format, format
params[:name] = params[:name].chomp(".#{format.to_s}")
end
def get_ids_from_tag_expr(tag_expr) def get_ids_from_tag_expr(tag_expr)
ids = [] ids = []
tag_expr.each do |tag_list| tag_expr.each do |tag_list|

View file

@ -21,13 +21,13 @@ module TodosHelper
end end
end end
def show_grouped_todos def show_grouped_todos(settings = {})
collection = (@group_view_by == 'context') ? @contexts_to_show : @projects_to_show collection = (@group_view_by == 'context') ? @contexts_to_show : @projects_to_show
render(:partial => collection, :locals => { :settings => { render(:partial => collection, :locals => { :settings => settings.reverse_merge!({
:collapsible => true, :collapsible => true,
:show_empty_containers => @show_empty_containers, :show_empty_containers => @show_empty_containers,
:parent_container_type => @group_view_by :parent_container_type => @group_view_by
}}) })})
end end
def default_collection_settings def default_collection_settings
@ -80,14 +80,14 @@ module TodosHelper
:locals => {:settings => settings.reverse_merge!(default_collection_settings)} :locals => {:settings => settings.reverse_merge!(default_collection_settings)}
end end
def show_todos_without_project(todos_without_project) def show_todos_without_project(todos_without_project, settings = {})
render :partial => 'todos/collection', render :partial => 'todos/collection',
:object => todos_without_project, :object => todos_without_project,
:locals => {:settings => { :locals => {:settings => settings.reverse_merge!({
:collapsible => true, :collapsible => true,
:container_name => "without_project", :container_name => "without_project",
:parent_container_type => "home" :parent_container_type => "home"
} })
} }
end end
@ -480,7 +480,7 @@ module TodosHelper
def should_show_new_item(todo = @todo) def should_show_new_item(todo = @todo)
return false if todo.nil? return false if todo.nil?
source_view do |page| source_view do |page|
page.todo { return !todo.hidden? } page.todo { return !todo.hidden? && !todo.deferred? }
page.deferred { return todo.deferred? || todo.pending? } page.deferred { return todo.deferred? || todo.pending? }
page.context { return show_todo_on_current_context_page && todo_should_not_be_hidden_on_context_page } page.context { return show_todo_on_current_context_page && todo_should_not_be_hidden_on_context_page }
page.tag { return todo.has_tag?(@tag_name) } page.tag { return todo.has_tag?(@tag_name) }
@ -512,18 +512,18 @@ module TodosHelper
end end
def update_needs_to_hide_container def update_needs_to_hide_container
if source_view_is(:tag) if source_view_is_one_of(:tag, :context, :project)
return @remaining_in_context == 0 && ( return @remaining_in_context == 0 && (
todo_moved_out_of_container || todo_moved_out_of_container ||
(@todo_hidden_state_changed && @todo.hidden?) || (@todo_hidden_state_changed && @todo.hidden?) ||
@todo_was_deferred_from_active_state || @todo_was_deferred_from_active_state ||
@tag_was_removed || @tag_was_removed ||
@todo_was_destroyed || @todo_was_destroyed ||
(@todo.completed? && !(@original_item_was_deferred || @original_item_was_hidden)) (@todo.completed? && !(@original_item_was_deferred || @original_item_was_hidden || @original_item_was_pending))
) )
end end
return false if source_view_is_one_of(:project, :calendar, :done, :context) return false if source_view_is_one_of(:calendar, :done)
return @remaining_in_context == 0 return @remaining_in_context == 0
end end
@ -599,7 +599,7 @@ module TodosHelper
end end
def todo_container_empty_id(todo) def todo_container_empty_id(todo)
raise Exception.new, "no todo set in TodosHelper::todo_container_empty_id. You probably not assign @original_item" if !todo raise Exception.new, "no todo set in TodosHelper::todo_container_empty_id. You probably did not assign @original_item" if !todo
@group_view_by == "project" ? project_container_empty_id(todo) : context_container_empty_id(todo) @group_view_by == "project" ? project_container_empty_id(todo) : context_container_empty_id(todo)
end end
@ -609,8 +609,7 @@ module TodosHelper
return "#{@new_due_id}_container" if source_view_is :calendar return "#{@new_due_id}_container" if source_view_is :calendar
return "deferred_pending_container" if !source_view_is(:todo) && (todo.deferred? || todo.pending?) return "deferred_pending_container" if !source_view_is(:todo) && (todo.deferred? || todo.pending?)
return "completed_container" if todo.completed? return "completed_container" if todo.completed?
return "p#{todo.project_id}" if source_view_is :project return project_container_id(todo) if source_view_is_one_of(:todo, :tag, :project, :context) && @group_view_by == 'project'
return project_container_id(todo) if source_view_is_one_of(:todo, :tag) && @group_view_by == 'project'
return context_container_id(todo) return context_container_id(todo)
end end
@ -620,7 +619,7 @@ module TodosHelper
source_view do |page| source_view do |page|
page.project { page.project {
return "deferred_pending_container-empty-d" if empty_criteria_met return "deferred_pending_container-empty-d" if empty_criteria_met
return project_container_empty_id(todo) return todo_container_empty_id(todo)
} }
page.tag { page.tag {
return "deferred_pending_container-empty-d" if empty_criteria_met return "deferred_pending_container-empty-d" if empty_criteria_met
@ -633,7 +632,7 @@ module TodosHelper
} }
page.context { page.context {
return "deferred_pending_container-empty-d" if empty_criteria_met return "deferred_pending_container-empty-d" if empty_criteria_met
return context_container_empty_id(todo) return todo_container_empty_id(todo)
} }
page.todo { page.todo {
return todo_container_empty_id(todo) return todo_container_empty_id(todo)

View file

@ -539,14 +539,13 @@ class RecurringTodo < ActiveRecord::Base
end end
def get_monthly_date(previous) def get_monthly_date(previous)
start = determine_start(previous) start = determine_start(previous)
day = self.every_other1 day = self.every_other1
n = self.every_other2 n = self.every_other2
case self.recurrence_selector case self.recurrence_selector
when 0 # specific day of the month when 0 # specific day of the month
if start.mday >= day if start.mday > day
# there is no next day n in this month, search in next month # there is no next day n in this month, search in next month
# #
# start += n.months # start += n.months

View file

@ -99,7 +99,7 @@ class Todo < ActiveRecord::Base
end end
event :block do event :block do
transitions :to => :pending, :from => [:active, :deferred] transitions :to => :pending, :from => [:active, :deferred, :project_hidden]
end end
end end
@ -152,6 +152,14 @@ class Todo < ActiveRecord::Base
return !( uncompleted_predecessors.empty? || state == 'project_hidden' ) return !( uncompleted_predecessors.empty? || state == 'project_hidden' )
end end
def guard_for_transition_from_deferred_to_pending
no_uncompleted_predecessors? && not_part_of_hidden_container?
end
def not_part_of_hidden_container?
!( (self.project && self.project.hidden?) || self.context.hidden? )
end
# Returns a string with description <context, project> # Returns a string with description <context, project>
def specification def specification
project_name = self.project.is_a?(NullProject) ? "(none)" : self.project.name project_name = self.project.is_a?(NullProject) ? "(none)" : self.project.name
@ -195,7 +203,7 @@ class Todo < ActiveRecord::Base
def remove_predecessor(predecessor) def remove_predecessor(predecessor)
self.predecessors.delete(predecessor) self.predecessors.delete(predecessor)
if self.predecessors.empty? if self.predecessors.empty?
self.activate! self.not_part_of_hidden_container? ? self.activate! : self.hide!
else else
save! save!
end end

View file

@ -3,7 +3,7 @@
# invalidate the cache every day because of staleness or # invalidate the cache every day because of staleness or
# rendering of "due in x days" that change without touching updated at of the todo # rendering of "due in x days" that change without touching updated at of the todo
cache [context, @source_view, current_user.date.strftime("%Y%m%d"), @tag_name] do cache [context, @source_view, current_user.date.strftime("%Y%m%d"), @tag_name] do
%> -%>
<%= <%=
render :partial => 'todos/collection', render :partial => 'todos/collection',
:object => @not_done, :object => @not_done,

View file

@ -1,7 +1,7 @@
<div class="list-stategroup-contexts-container" id="list-<%= state %>-contexts-container"> <div class="list-stategroup-contexts-container" id="list-<%= state %>-contexts-container">
<h2> <h2>
<span id="<%= state %>-contexts-count" class="badge"><%= context_state_group.length %></span> <span id="<%= state %>-contexts-count" class="badge"><%= context_state_group.length %></span>
<%= t("states."+ state +"_plural")%> <%= t('common.contexts') %> <%= t("states.contexts."+ state) %>
</h2> </h2>
<div id="<%= state%>-contexts-empty-nd" style="<%= no_contexts ? 'display:block' : 'display:none'%>"> <div id="<%= state%>-contexts-empty-nd" style="<%= no_contexts ? 'display:block' : 'display:none'%>">

View file

@ -2,9 +2,17 @@
suffix_completed = t('contexts.last_completed_in_context', :number=>prefs.show_number_completed) suffix_completed = t('contexts.last_completed_in_context', :number=>prefs.show_number_completed)
deferred_pending_options = {:append_descriptor => nil, :parent_container_type => 'context'} deferred_pending_options = {:append_descriptor => nil, :parent_container_type => 'context'}
done_todo_options = {:append_descriptor => suffix_completed, :suppress_context => true, :parent_container_type => 'context'} done_todo_options = {:append_descriptor => suffix_completed, :suppress_context => true, :parent_container_type => 'context'}
show_empty_containers = (@group_view_by == 'context')
-%> -%>
<div id="display_box"> <div id="display_box">
<%= render :partial => @context, :locals => { :settings => {:collapsible => false, :show_empty_containers => true, :parent_container_type => 'context' }} %>
<%= empty_message_holder("not_done_project", @not_done_todos.empty?) %>
<%= show_grouped_todos({:collapsible => false, :show_empty_containers => show_empty_containers, :parent_container_type => 'context'}) %>
<% if @group_view_by == 'project' -%>
<%= show_todos_without_project(@todos_without_project, {:collapsible => false, :parent_container_type => 'context', :title_param => @context.name}) -%>
<% end -%>
<%= show_deferred_pending_todos(@deferred_todos, @pending_todos, deferred_pending_options) %> <%= show_deferred_pending_todos(@deferred_todos, @pending_todos, deferred_pending_options) %>

View file

@ -46,10 +46,6 @@
</h1> </h1>
</div> </div>
<div id="minilinks"> <div id="minilinks">
<%= link_to(t('layouts.toggle_contexts'), "#", {:title => t('layouts.toggle_contexts_title'), :id => "toggle-contexts-nav"}) %>
&nbsp;|&nbsp;
<%= link_to(t('layouts.toggle_notes'), "#", {:accesskey => "S", :title => t('layouts.toggle_notes_title'), :id => "toggle-notes-nav"}) %>
&nbsp;|&nbsp;
<%= link_to("#{t('common.logout')} (#{current_user.display_name}) &raquo;".html_safe, logout_path) %> <%= link_to("#{t('common.logout')} (#{current_user.display_name}) &raquo;".html_safe, logout_path) %>
</div> </div>
<div id="navcontainer"> <div id="navcontainer">
@ -66,13 +62,15 @@
<li><%= navigation_link( t('layouts.navigation.recurring_todos'), {:controller => "recurring_todos", :action => "index"}, :title => t('layouts.navigation.recurring_todos_title')) %></li> <li><%= navigation_link( t('layouts.navigation.recurring_todos'), {:controller => "recurring_todos", :action => "index"}, :title => t('layouts.navigation.recurring_todos_title')) %></li>
</ul> </ul>
</li> </li>
<li><a href="#"><%= t('layouts.navigation.view') %></a> <li id="menu_view"><a href="#" id="menu_view_link"><%= t('layouts.navigation.view') %></a>
<ul> <ul>
<li><%= navigation_link( t('layouts.navigation.calendar'), calendar_path, :title => t('layouts.navigation.calendar_title')) %></li> <li><%= navigation_link( t('layouts.navigation.calendar'), calendar_path, :title => t('layouts.navigation.calendar_title')) %></li>
<li><%= navigation_link( t('layouts.navigation.completed_tasks'), done_overview_path, {:accesskey=>"d", :title=>t('layouts.navigation.completed_tasks_title')} ) %></li> <li><%= navigation_link( t('layouts.navigation.completed_tasks'), done_overview_path, {:accesskey=>"d", :title=>t('layouts.navigation.completed_tasks_title')} ) %></li>
<li><%= navigation_link( t('layouts.navigation.feeds'), feeds_path, :title => t('layouts.navigation.feeds_title')) %></li> <li><%= navigation_link( t('layouts.navigation.feeds'), feeds_path, :title => t('layouts.navigation.feeds_title')) %></li>
<li><%= navigation_link( t('layouts.navigation.stats'), stats_path, :title => t('layouts.navigation.stats_title')) %></li> <li><%= navigation_link( t('layouts.navigation.stats'), stats_path, :title => t('layouts.navigation.stats_title')) %></li>
<li><hr/></li> <li><hr/></li>
<li id="menu_view_toggle_contexts"><%= link_to(t('layouts.toggle_contexts'), "#", {:title => t('layouts.toggle_contexts_title'), :id => "toggle-contexts-nav"}) %></li>
<li><%= link_to(t('layouts.toggle_notes'), "#", {:accesskey => "S", :title => t('layouts.toggle_notes_title'), :id => "toggle-notes-nav"}) %></li>
<%= group_view_by_menu_entry %> <%= group_view_by_menu_entry %>
</ul> </ul>
</li> </li>

View file

@ -2,11 +2,8 @@
@not_done = @not_done_todos.select {|t| t.project_id == project.id } @not_done = @not_done_todos.select {|t| t.project_id == project.id }
# invalidate the cache every day because of staleness or # invalidate the cache every day because of staleness or
# rendering of "due in x days" that change without touching updated at of the todo # rendering of "due in x days" that change without touching updated at of the todo
cache [project, @source_view, current_user.date.strftime("%Y%m%d")] do cache [project, @source_view, current_user.date.strftime("%Y%m%d"), @tag_name] do
-%> -%>
<% if source_view_is :project -%>
<%= render :partial => "project_settings_container", :locals => {:project => project} %>
<% end -%>
<%= <%=
title = source_view_is(:project) ? t('projects.actions_in_project_title') : show_project_name(project) title = source_view_is(:project) ? t('projects.actions_in_project_title') : show_project_name(project)

View file

@ -8,14 +8,9 @@
<h2> <h2>
<span id="<%= state %>-projects-count" class="badge"><%= project_state_group.length%><%= total_count_string%></span> <span id="<%= state %>-projects-count" class="badge"><%= project_state_group.length%><%= total_count_string%></span>
<%= t('common.last' ) unless ( ['review','stalled','blocked','current'].include?(state) )%> <%= t('common.last' ) if state == 'completed' %>
<% if (I18n.locale == :fr) %> <%= t('states.projects.'+state) %>
<%= t('common.projects').downcase %> <%= total_count==-1 ? "" : " (#{link_to(t('common.show_all'), done_projects_path)})".html_safe%>
<%= t('states.'+state+'_plural' ).downcase %><%= total_count==-1 ? "" : " (#{link_to(t('common.show_all'), done_projects_path)})".html_safe%>
<% else %>
<%= t('states.'+state+'_plural' )%>
<%= t('common.projects') %><%= total_count==-1 ? "" : " (#{link_to(t('common.show_all'), done_projects_path)})".html_safe%>
<% end %>
</h2> </h2>
<% unless suppress_sort_menu %> <% unless suppress_sort_menu %>

View file

@ -5,11 +5,19 @@
:suppress_project => true, :suppress_project => true,
:parent_container_type => 'project' :parent_container_type => 'project'
} }
if @not_done_todos.count == 0
# force project view so one empty container will be shown with an empty message
@group_view_by = 'project'
end
-%> -%>
<div id="display_box"> <div id="display_box">
<%= project_next_prev %> <%= project_next_prev %>
<%= render :partial => @project, :locals => {:settings => {:collapsible => false, :show_empty_containers => true, :parent_container_type => 'project' }} %> <%= render :partial => "project_settings_container", :locals => {:project => @project} %>
<%= empty_message_holder("not_done_context", @not_done_todos.empty?) %>
<%= show_grouped_todos({:collapsible => false, :show_empty_containers => false, :parent_container_type => 'project' }) %>
<%= show_deferred_pending_todos(@deferred_todos, @pending_todos, deferred_pending_options) %> <%= show_deferred_pending_todos(@deferred_todos, @pending_todos, deferred_pending_options) %>

View file

@ -36,7 +36,7 @@ function update_project_page() {
TracksForm.set_context_name_and_default_context_name("<%= escape_javascript(@project.default_context.name)%>"); TracksForm.set_context_name_and_default_context_name("<%= escape_javascript(@project.default_context.name)%>");
<% end %> <% end %>
<% if @project.default_tags %> <% if @project.default_tags %>
TracksForm.set_tag_list("<%= escape_javascript(@project.default_tags)%>"); TracksForm.set_tag_list_and_default_tag_list("<%= escape_javascript(@project.default_tags)%>");
<% end %> <% end %>
TracksPages.update_sidebar(html_for_sidebar()); TracksPages.update_sidebar(html_for_sidebar());
} }

View file

@ -84,12 +84,12 @@
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year2))) %><br/> :month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year2))) %><br/>
</div> </div>
<div id="recurring_target"> <div id="recurring_target">
<label><%= t('todos.recurrence.recurrence_on_options') %></label><br/> <label><%= t('todos.recurrence.recurrence_on.options') %></label><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', @recurring_todo.target == 'due_date')%> <%= t('todos.recurrence.recurrence_on_due_date') %>. <%= t('todos.recurrence.show_options') %>: <%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', @recurring_todo.target == 'due_date')%> <%= t('todos.recurrence.recurrence_on.due_date') %>. <%= t('todos.recurrence.recurrence_on.show_options') %>:
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', @recurring_todo.show_always?)%> <%= t('todos.recurrence.show_option_always') %> <%= radio_button_tag('recurring_todo[recurring_show_always]', '1', @recurring_todo.show_always?)%> <%= t('todos.recurrence.recurrence_on.show_always') %>
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', !@recurring_todo.show_always?)%> <%= radio_button_tag('recurring_todo[recurring_show_always]', '0', !@recurring_todo.show_always?)%>
<%= raw t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', @recurring_todo.show_from_delta, {"size" => 3})) %><br/> <%= raw t('todos.recurrence.recurrence_on.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', @recurring_todo.show_from_delta, {"size" => 3})) %><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', @recurring_todo.target == 'show_from_date')%> <%= t('todos.recurrence.from_tickler') %><br/> <%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', @recurring_todo.target == 'show_from_date')%> <%= t('todos.recurrence.recurrence_on.from_tickler') %><br/>
<br/> <br/>
</div> </div>
<% end %> <% end %>

View file

@ -76,13 +76,13 @@
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, Time.zone.now.month))) %><br/> :month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, Time.zone.now.month))) %><br/>
</div> </div>
<div id="recurring_target"> <div id="recurring_target">
<label><%= t('todos.recurrence.recurrence_on_options') %></label><br/> <label><%= t('todos.recurrence.recurrence_on.options') %></label><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', true)%> <%= t('todos.recurrence.recurrence_on_due_date') %>. <%= t('todos.recurrence.show_options') %>: <%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', true)%> <%= t('todos.recurrence.recurrence_on.due_date') %>. <%= t('todos.recurrence.recurrence_on.show_options') %>:
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', true)%> <%= t('todos.recurrence.show_option_always') %> <%= radio_button_tag('recurring_todo[recurring_show_always]', '1', true)%> <%= t('todos.recurrence.recurrence_on.show_always') %>
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', false)%> <%= radio_button_tag('recurring_todo[recurring_show_always]', '0', false)%>
<%= raw t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', "0", {"size" => 3})) %> <%= raw t('todos.recurrence.recurrence_on.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', "0", {"size" => 3})) %>
<br/> <br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', false)%> <%= t('todos.recurrence.from_tickler') %><br/> <%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', false)%> <%= t('todos.recurrence.recurrence_on.from_tickler') %><br/>
<br/> <br/>
</div> </div>

View file

@ -31,7 +31,7 @@
$('#todo-form-new-action').clearDeps(); $('#todo-form-new-action').clearDeps();
TracksForm.set_context_name('<%=escape_javascript @initial_context_name%>'); TracksForm.set_context_name('<%=escape_javascript @initial_context_name%>');
TracksForm.set_project_name('<%=escape_javascript @initial_project_name%>'); TracksForm.set_project_name('<%=escape_javascript @initial_project_name%>');
TracksForm.set_tag_list('<%=escape_javascript @initial_tags%>'); TracksForm.set_tag_list_and_default_tag_list('<%=escape_javascript @initial_tags%>');
$('#todo-form-new-action input:text:first').focus(); $('#todo-form-new-action input:text:first').focus();
$('#new_todo_starred_link .todo_star').removeClass('starred'); $('#new_todo_starred_link .todo_star').removeClass('starred');
$('#new_todo_starred').val('false'); $('#new_todo_starred').val('false');
@ -86,4 +86,4 @@
return "<%= @saved ? escape_javascript(render(:partial => @todo, :locals => { :parent_container_type => parent_container_type, :source_view => @source_view })) : "" %>"; return "<%= @saved ? escape_javascript(render(:partial => @todo, :locals => { :parent_container_type => parent_container_type, :source_view => @source_view })) : "" %>";
} }
<% end -%> <% end -%>

View file

@ -15,7 +15,7 @@
animation << "add_todo_to_container" unless source_view_is(:done) animation << "add_todo_to_container" unless source_view_is(:done)
animation << "block_predecessors" animation << "block_predecessors"
end end
animation << "update_empty_container" if source_view_is_one_of(:tag, :todo, :deferred) animation << "update_empty_container" if source_view_is_one_of(:tag, :todo, :deferred, :project, :context)
animation << "regenerate_predecessor_family" animation << "regenerate_predecessor_family"
else else
animation << "replace_todo" animation << "replace_todo"
@ -63,15 +63,14 @@ function replace_todo(next_steps) {
} }
function add_todo_to_container(next_steps) { function add_todo_to_container(next_steps) {
// <%= @group_view_by %>
$('#<%= item_container_id(@todo) %>_items').append(html_for_todo()); $('#<%= item_container_id(@todo) %>_items').append(html_for_todo());
<% if should_make_context_visible -%> <% if should_make_context_visible -%>
$('#<%= item_container_id(@todo) %>').slideDown(500, function() { $('#<%= item_container_id(@todo) %>').slideDown(500, function() {
$("#<%= empty_container_msg_div_id %>").slideUp(100); $("#<%= empty_container_msg_div_id %>").slideUp(100);
highlight_updated_todo(next_steps); highlight_updated_todo(next_steps);
}); });
<% else -%> <% else -%>
$("#<%= empty_container_msg_div_id(@todo) %>").slideUp(100); $("#<%= empty_container_msg_div_id(@todo) %>").slideUp(100);
highlight_updated_todo(next_steps); highlight_updated_todo(next_steps);
<% end -%> <% end -%>
<% if @completed_count == 0 -%> <% if @completed_count == 0 -%>
@ -120,7 +119,7 @@ function highlight_updated_todo(next_steps) {
function activate_pending_todos(next_steps) { function activate_pending_todos(next_steps) {
<% # Activate pending todos that are successors of the completed <% # Activate pending todos that are successors of the completed
if @saved && @pending_to_activate if @pending_to_activate
# do not render the js in case of an error or if no todos to activate # do not render the js in case of an error or if no todos to activate
@pending_to_activate.each do |t| @pending_to_activate.each do |t|
html = escape_javascript(render(:partial => t, :locals => { :parent_container_type => parent_container_type })) html = escape_javascript(render(:partial => t, :locals => { :parent_container_type => parent_container_type }))
@ -173,7 +172,7 @@ end
function html_for_recurring_todo() { function html_for_recurring_todo() {
<%- <%-
js = @saved && @new_recurring_todo ? escape_javascript(render(:partial => @new_recurring_todo, :locals => { :parent_container_type => parent_container_type })) : "" js = @new_recurring_todo ? escape_javascript(render(:partial => @new_recurring_todo, :locals => { :parent_container_type => parent_container_type })) : ""
-%> -%>
return "<%= js %>"; return "<%= js %>";
} }
@ -181,7 +180,7 @@ function html_for_recurring_todo() {
function html_for_todo() { function html_for_todo() {
<%- <%-
js = "" js = ""
if @saved && !source_view_is(:done) if !source_view_is(:done)
js = escape_javascript(render( js = escape_javascript(render(
:partial => @todo, :partial => @todo,
:locals => { :locals => {

View file

@ -20,7 +20,7 @@
end end
animation << "hide_container" if update_needs_to_hide_container animation << "hide_container" if update_needs_to_hide_container
animation << "highlight_updated_todo" animation << "highlight_updated_todo"
animation << "update_empty_container" if source_view_is_one_of(:tag, :todo, :deferred) animation << "update_empty_container" if source_view_is_one_of(:tag, :todo, :deferred, :project, :context)
animation << "update_predecessors" animation << "update_predecessors"
%> %>
TracksPages.page_notify('notice', '<%=escape_javascript @status_message%>', 5); TracksPages.page_notify('notice', '<%=escape_javascript @status_message%>', 5);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -333,6 +333,14 @@ en:
hide_action_form_title: Hide new action form hide_action_form_title: Hide new action form
make_actions_dependent: Make actions dependent on each other make_actions_dependent: Make actions dependent on each other
states: states:
contexts:
hidden: Hidden contexts
active: Active contexts
closed: Closed contexts
projects:
hidden: Hidden projects
active: Active projects
closed: Closed projects
hidden_plural: Hidden hidden_plural: Hidden
completed: Completed completed: Completed
completed_plural: Completed completed_plural: Completed
@ -427,6 +435,8 @@ en:
completed: Currently there are no completed actions completed: Currently there are no completed actions
title: No actions found title: No actions found
not_done_with_tag: "Currently there are no incomplete actions with the tag '%{param}'" not_done_with_tag: "Currently there are no incomplete actions with the tag '%{param}'"
not_done_project: Currently there are no incomplete actions in this project
not_done_context: Currently there are no incomplete actions in this context
completed_recurring: Currently there are no completed recurring todos completed_recurring: Currently there are no completed recurring todos
not_done: Currently there are no incomplete actions not_done: Currently there are no incomplete actions
project: Currently there are no incomplete actions in this project project: Currently there are no incomplete actions in this project
@ -444,6 +454,7 @@ en:
home_completed: Completed actions home_completed: Completed actions
tag_completed: "Completed actions tagged with '%{param}'" tag_completed: "Completed actions tagged with '%{param}'"
home_without_project: "Actions without project" home_without_project: "Actions without project"
context_without_project: "Actions without project in %{param}"
project_project: "Actions in this project" project_project: "Actions in this project"
project_deferred_pending: Deferred/pending actions in this project project_deferred_pending: Deferred/pending actions in this project
context_deferred_pending: Deferred/pending actions in this context context_deferred_pending: Deferred/pending actions in this context
@ -584,7 +595,6 @@ en:
ends_on_number_times: Ends after %{number} times ends_on_number_times: Ends after %{number} times
ends_on_date: Ends on %{date} ends_on_date: Ends on %{date}
every_work_day: Every work day every_work_day: Every work day
recurrence_on_due_date: the date that the todo is due
weekly_options: Settings for weekly recurring actions weekly_options: Settings for weekly recurring actions
weekly: Weekly weekly: Weekly
monthly_options: Settings for monthly recurring actions monthly_options: Settings for monthly recurring actions
@ -633,16 +643,18 @@ en:
due: due due: due
until: until until: until
every_month: every month every_month: every month
show_option_always: always
daily: Daily daily: Daily
yearly_every_x_day: Every %{month} %{day} yearly_every_x_day: Every %{month} %{day}
recurrence_on_options: Set recurrence on recurrence_on:
options: Use the calculated date to
due_date: set the actions due date
show_options: Show the action
show_always: always
show_days_before: "not until %{days} days before the due date"
from_tickler: set the date the action should be shown (do not set a due date)
daily_every_number_day: Every %{number} day(s) daily_every_number_day: Every %{number} day(s)
show_options: Show the todo
weekly_every_number_week: Returns every %{number} week on weekly_every_number_week: Returns every %{number} week on
ends_on: Ends on ends_on: Ends on
show_days_before: "%{days} days before the todo is due"
from_tickler: the date todo comes from tickler (no due date set)
no_end_date: No end date no_end_date: No end date
day_x_on_every_x_month: Day %{day} on every %{month} month day_x_on_every_x_month: Day %{day} on every %{month} month
yearly_options: Settings for yearly recurring actions yearly_options: Settings for yearly recurring actions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,6 @@ Tracksapp::Application.routes.draw do
post 'login' => 'login#login' post 'login' => 'login#login'
get 'login' => 'login#login' get 'login' => 'login#login'
get 'login/expire_session' => 'login#expire_session'
get 'login/check_expiry' => 'login#check_expiry' get 'login/check_expiry' => 'login#check_expiry'
get 'logout' => 'login#logout' get 'logout' => 'login#logout'
@ -102,9 +101,13 @@ Tracksapp::Application.routes.draw do
post 'add_predecessor' post 'add_predecessor'
end end
end end
get 'todos/tag/:name' => 'todos#tag', :as => :tag
get 'tags.autocomplete' => "todos#tags", :format => 'autocomplete'
# match /todos/tag and put everything in :name, including extensions like .m and .txt.
# This means the controller action needs to parse the extension and set format/content type
# Needed for /todos/tag/first.last.m to work
get 'todos/tag/:name' => 'todos#tag', :as => :tag, :format => false, :name => /.*/
get 'tags.autocomplete' => "todos#tags", :format => 'autocomplete'
get 'todos/done/tag/:name' => "todos#done_tag", :as => :done_tag get 'todos/done/tag/:name' => "todos#done_tag", :as => :done_tag
get 'todos/all_done/tag/:name' => "todos#all_done_tag", :as => :all_done_tag get 'todos/all_done/tag/:name' => "todos#all_done_tag", :as => :all_done_tag
get 'auto_complete_for_predecessor' => 'todos#auto_complete_for_predecessor' get 'auto_complete_for_predecessor' => 'todos#auto_complete_for_predecessor'

View file

@ -16,10 +16,23 @@
== Version 2.3devel == Version 2.3devel
New and changed features New and changed features
* you can select to group todos on the home page by context or by * You can select to group todos on the home page by context or by
project (using the view menu). This also works for tag page project (using the view menu). This also works for tag page, the project page,
the tickler and the context page
* You can now change the state of a context to closed * You can now change the state of a context to closed
* Czech locale has been renamed from cz to cs to follow ISO standards * Czech locale has been renamed from cz to cs to follow ISO standards
* The toggle-notes and toggle-collapsed-containers have been moved into the view
menu
* Bugfixes
* Tracks is tested on Ruby 1.9.3 and on Ruby 2.0
Removed features
* This version of Tracks dropped support for Ruby 1.8.x. Please use Ruby 1.9 or
Ruby 2.0
Under the hood
* Upgrade to Rails 3.213
* Several refactorings al over the place for easier maintenance
== Version 2.2.2 == Version 2.2.2

View file

@ -68,10 +68,10 @@ Feature: dependencies
And "test 2" depends on "test 1" And "test 2" depends on "test 1"
When I go to the "dependencies" project When I go to the "dependencies" project
Then I should see "test 2" in the deferred container Then I should see "test 2" in the deferred container
And I should see "test 1" in the action container And I should see "test 1" in the context container for "@pc"
When I mark "test 1" as complete When I mark "test 1" as complete
Then I should see "test 1" in the completed container Then I should see "test 1" in the completed container
And I should see "test 2" in the action container And I should see "test 2" in the context container for "@pc"
And I should not see "test 2" in the deferred container And I should not see "test 2" in the deferred container
And I should see empty message for deferred todos of project And I should see empty message for deferred todos of project
@ -86,9 +86,9 @@ Feature: dependencies
And "test 2" depends on "test 1" And "test 2" depends on "test 1"
When I go to the "dependencies" project When I go to the "dependencies" project
Then I should see "test 2" in the deferred container Then I should see "test 2" in the deferred container
And I should see "test 1" in the action container And I should see "test 1" in the context container for "@pc"
When I delete the action "test 1" When I delete the action "test 1"
Then I should see "test 2" in the action container Then I should see "test 2" in the context container for "@pc"
And I should not see "test 2" in the deferred container And I should not see "test 2" in the deferred container
And I should see empty message for deferred todos of project And I should see empty message for deferred todos of project
@ -111,7 +111,7 @@ Feature: dependencies
Then I should see "test 3" within the dependencies of "test 1" Then I should see "test 3" within the dependencies of "test 1"
And I should not see "test 2" And I should not see "test 2"
@javascript @javascript
Scenario: Dragging an action to a completed action will not add it as a dependency Scenario: Dragging an action to a completed action will not add it as a dependency
Given I have a context called "@pc" Given I have a context called "@pc"
And I have a project "dependencies" that has the following todos And I have a project "dependencies" that has the following todos
@ -122,7 +122,7 @@ Feature: dependencies
When I go to the "dependencies" project When I go to the "dependencies" project
And I drag "test 1" to "test 3" And I drag "test 1" to "test 3"
Then I should see an error flash message saying "Cannot add this action as a dependency to a completed action!" Then I should see an error flash message saying "Cannot add this action as a dependency to a completed action!"
And I should see "test 1" in the project container of "dependencies" And I should see "test 1" in the context container for "@pc"
@javascript @javascript
Scenario Outline: Marking a successor as complete will update predecessor Scenario Outline: Marking a successor as complete will update predecessor
@ -148,7 +148,7 @@ Feature: dependencies
| tag page for "bla" | context | | tag page for "bla" | context |
| tag page for "bla" | project | | tag page for "bla" | project |
@javascript @javascript
Scenario Outline: Marking a successor as active will update predecessor Scenario Outline: Marking a successor as active will update predecessor
Given I have a context called "@pc" Given I have a context called "@pc"
And I have selected the view for group by <grouping> And I have selected the view for group by <grouping>
@ -171,5 +171,6 @@ Feature: dependencies
Scenarios: Scenarios:
| page | grouping | | page | grouping |
| "dependencies" project | project | | "dependencies" project | project |
| "dependencies" project | context |
| tag page for "bla" | context | | tag page for "bla" | context |
| tag page for "bla" | project | | tag page for "bla" | project |

View file

@ -55,7 +55,7 @@ Feature: Edit a next action from every page
@javascript @wip @javascript @wip
Scenario Outline: Changing container of the todo in that container will hide it Scenario Outline: Changing container of the todo in that container will hide it
# this script fails on https://code.google.com/p/selenium/issues/detail?id=3075 for selenium-webdriver > 2.14. # this script fails on https://code.google.com/p/selenium/issues/detail?id=3075 for selenium-webdriver > 2.14.
# and selenium-webdriver < 2.20 fails on firefox 11 :-( So @wip for now. This may work on webkit though # and selenium-webdriver < 2.20 fails on firefox 11 :-( So @wip for now. This may work with webkit though
Given I have a todo "delete me" in the context "@home" in the project "do it" Given I have a todo "delete me" in the context "@home" in the project "do it"
And I have a project "go for it" And I have a project "go for it"
And I have selected the view for group by <grouping> And I have selected the view for group by <grouping>
@ -77,6 +77,7 @@ Feature: Edit a next action from every page
@javascript @javascript
Scenario Outline: Deleting the last todo in container will show empty message # only project, context, tag, not todo Scenario Outline: Deleting the last todo in container will show empty message # only project, context, tag, not todo
Given I have a context called "@home" Given I have a context called "@home"
And I have selected the view for group by <grouping>
And I have a project "my project" that has the following todos And I have a project "my project" that has the following todos
| context | description | tags | | context | description | tags |
| @home | first action | test, bla | | @home | first action | test, bla |
@ -90,12 +91,15 @@ Feature: Edit a next action from every page
Then I should see empty message for todos of <page type> Then I should see empty message for todos of <page type>
Scenarios: Scenarios:
| page | page type | | page | page type | grouping |
| "my project" project | project | | "my project" project | project | project |
| context page for "@home" | context | | "my project" project | project | context |
| tag page for "bla" | tag | | context page for "@home" | context | context |
| context page for "@home" | context | project |
| tag page for "bla" | tag | context |
| tag page for "bla" | tag | project |
@javascript @javascript
Scenario Outline: I can mark an active todo complete and it will update empty messages Scenario Outline: I can mark an active todo complete and it will update empty messages
Given I have a context called "visible context" Given I have a context called "visible context"
And I have a project called "visible project" And I have a project called "visible project"
@ -134,7 +138,7 @@ Feature: Edit a next action from every page
| "visible project" project | project | | "visible project" project | project |
| context page for "visible context" | context | | context page for "visible context" | context |
@javascript @javascript
Scenario Outline: I can mark a completed todo active and it will update empty messages and context containers Scenario Outline: I can mark a completed todo active and it will update empty messages and context containers
Given I have a completed todo with description "visible todo" in project "visible project" with tags "starred" in the context "visible context" Given I have a completed todo with description "visible todo" in project "visible project" with tags "starred" in the context "visible context"
And I have selected the view for group by <grouping> And I have selected the view for group by <grouping>
@ -155,8 +159,8 @@ Feature: Edit a next action from every page
| home page | home | context | container for context "visible context" | | home page | home | context | container for context "visible context" |
| home page | home | project | container for project "visible project" | | home page | home | project | container for project "visible project" |
@javascript @javascript
Scenario Outline: I can mark a completed todo active and it will update empty messages for pages without context containers Scenario Outline: I can mark a completed todo active and it will update empty messages for pages without hideable containers
Given I have a completed todo with description "visible todo" in project "visible project" with tags "starred" in the context "visible context" Given I have a completed todo with description "visible todo" in project "visible project" with tags "starred" in the context "visible context"
When I go to the <page> When I go to the <page>
Then I should see empty message for todos of <page type> Then I should see empty message for todos of <page type>

View file

@ -42,7 +42,7 @@ Feature: View, add, remove notes
And I edit the first note to "edited note" And I edit the first note to "edited note"
Then I should see "edited note" Then I should see "edited note"
@javascript @javascript
Scenario: Toggle all notes Scenario: Toggle all notes
Given I have a context called "@pc" Given I have a context called "@pc"
And I have a project "take notes" that has the following todos And I have a project "take notes" that has the following todos

View file

@ -123,7 +123,7 @@ Feature: Edit a project
Scenario: Moving the todo to the tickler will move todo to tickler container and update empty messages Scenario: Moving the todo to the tickler will move todo to tickler container and update empty messages
Given I have a project "test" with 1 todos Given I have a project "test" with 1 todos
When I go to the "test" project When I go to the "test" project
Then I should see "todo 1" in the action container Then I should see "todo 1" in the context container for "Context A"
And I should see empty message for deferred todos of project And I should see empty message for deferred todos of project
And I should see empty message for completed todos of project And I should see empty message for completed todos of project
When I defer "todo 1" for 1 day When I defer "todo 1" for 1 day

View file

@ -97,7 +97,9 @@ Feature: Add new next action from every page
| tickler page | context | not see | | tickler page | context | not see |
| tickler page | project | not see | | tickler page | project | not see |
| "test project" project | context | see | | "test project" project | context | see |
| "test project" project | project | see |
| context page for "test context" | context | see | | context page for "test context" | context | see |
| context page for "test context" | project | see |
| tag page for "starred" | context | see | | tag page for "starred" | context | see |
| tag page for "starred" | project | see | | tag page for "starred" | project | see |
@ -124,7 +126,9 @@ Feature: Add new next action from every page
| tickler page | not see | 0 | 3 | context | | tickler page | not see | 0 | 3 | context |
| tickler page | not see | 0 | 3 | project | | tickler page | not see | 0 | 3 | project |
| "testing" project | see | 3 | 3 | context | | "testing" project | see | 3 | 3 | context |
| "testing" project | see | 3 | 3 | project |
| context page for "test context" | see | 2 | 3 | context | | context page for "test context" | see | 2 | 3 | context |
| context page for "test context" | see | 2 | 3 | project |
| tag page for "starred" | see | 2 | 3 | context | | tag page for "starred" | see | 2 | 3 | context |
| tag page for "starred" | see | 2 | 3 | project | | tag page for "starred" | see | 2 | 3 | project |
@ -199,9 +203,13 @@ Feature: Add new next action from every page
| tickler page | context | not see | not see | | tickler page | context | not see | not see |
| tickler page | project | not see | not see | | tickler page | project | not see | not see |
| "visible project" project | project | not see | see | | "visible project" project | project | not see | see |
| "visible project" project | context | not see | see |
| "hidden project" project | project | see | not see | | "hidden project" project | project | see | not see |
| "hidden project" project | context | see | not see |
| context page for "visible context" | context | not see | see | | context page for "visible context" | context | not see | see |
| context page for "visible context" | project | not see | see |
| context page for "other context" | context | not see | not see | | context page for "other context" | context | not see | not see |
| context page for "other context" | project | not see | not see |
| tag page for "starred" | context | not see | not see | | tag page for "starred" | context | not see | not see |
| tag page for "starred" | project | not see | not see | | tag page for "starred" | project | not see | not see |
| tag page for "test" | context | see | see | | tag page for "test" | context | see | see |

View file

@ -11,7 +11,9 @@ When(/^I collapse the project container of "(.*?)"$/) do |project_name|
end end
When /^I toggle all collapsed context containers$/ do When /^I toggle all collapsed context containers$/ do
click_link 'Toggle collapsed contexts' open_view_menu do
click_link 'Toggle collapsed contexts'
end
end end
####### Context ####### ####### Context #######
@ -91,7 +93,6 @@ Then(/^I should (not see|see) "([^"]*)" in the (completed|done today|done this w
id = 'completed_rest_of_month_container' if container == 'done this month' id = 'completed_rest_of_month_container' if container == 'done this month'
css = "div##{id} div#line_todo_#{find_todo(todo_description).id}" css = "div##{id} div#line_todo_#{find_todo(todo_description).id}"
page.should have_css(css)
check_css_visibility(visible, css) check_css_visibility(visible, css)
end end
@ -138,9 +139,8 @@ end
Then /^I should (see|not see) empty message for (done today|done this week|done this month|completed todos|deferred todos|todos) (of done actions|of context|of project|of home|of tag)/ do |visible, state, type| Then /^I should (see|not see) empty message for (done today|done this week|done this month|completed todos|deferred todos|todos) (of done actions|of context|of project|of home|of tag)/ do |visible, state, type|
css = "error: wrong state" css = "error: wrong state"
css = "div#c#{@context.id}-empty-d" if state == "todos" && type == "of context" css = "div#c#{@context.id}-empty-d" if state == "todos"
css = "div#p#{@project.id}-empty-d" if state == "todos" && type == "of project" css = "div#no_todos_in_view" if state == "todos" && ["of home", "of tag", "of context", "of project"].include?(type)
css = "div#no_todos_in_view" if state == "todos" && (type == "of home" || type == "of tag")
css = "div#completed_today_container" if state == "done today" css = "div#completed_today_container" if state == "done today"
css = "div#completed_rest_of_week_container" if state == "done this week" css = "div#completed_rest_of_week_container" if state == "done this week"
css = "div#completed_rest_of_month_container" if state == "done this month" css = "div#completed_rest_of_month_container" if state == "done this month"

View file

@ -1,13 +1,7 @@
Given /^I have logged in as "(.*)" with password "(.*)"$/ do |username, password| Given /^I have logged in as "(.*)" with password "(.*)"$/ do |username, password|
step "I go to the login page" user = User.where(:login => username).first
fill_in "user_login", :with => username request_signin_as(user)
fill_in "user_password", :with => password @current_user = user
uncheck "user_noexpiry"
click_button "Sign in"
logout_regexp = @mobile_interface ? "Logout" : "Logout \(#{username}\)"
page.should have_content(logout_regexp)
@current_user = User.where(:login => username).first
end end
When /^I submit the login form as user "([^\"]*)" with password "([^\"]*)"$/ do |username, password| When /^I submit the login form as user "([^\"]*)" with password "([^\"]*)"$/ do |username, password|

View file

@ -30,7 +30,7 @@ When /^I edit the first note to "([^"]*)"$/ do |note_body|
end end
end end
When /^I toggle the note of "([^"]*)"$/ do |todo_description| When(/^I toggle the note of "([^"]*)"$/) do |todo_description|
todo = @current_user.todos.where(:description => todo_description).first todo = @current_user.todos.where(:description => todo_description).first
todo.should_not be_nil todo.should_not be_nil
@ -39,7 +39,9 @@ When /^I toggle the note of "([^"]*)"$/ do |todo_description|
end end
When /^I click Toggle Notes$/ do When /^I click Toggle Notes$/ do
click_link 'Toggle notes' open_view_menu do
click_link 'Toggle notes'
end
end end
When /^I toggle all notes$/ do When /^I toggle all notes$/ do

View file

@ -70,9 +70,9 @@ Given /^I have ([0-9]+) todos$/ do |count|
end end
Given /^I have a todo with description "([^"]*)" in project "([^"]*)" with tags "([^"]*)" in the context "([^"]*)"$/ do |action_description, project_name, tags, context_name| Given /^I have a todo with description "([^"]*)" in project "([^"]*)" with tags "([^"]*)" in the context "([^"]*)"$/ do |action_description, project_name, tags, context_name|
context = @current_user.contexts.where(:name => context_name).first_or_create @context = @current_user.contexts.where(:name => context_name).first_or_create
project = @current_user.projects.where(:name => project_name).first_or_create @project = @current_user.projects.where(:name => project_name).first_or_create
@todo = @current_user.todos.create!(:context_id => context.id, :project_id => project.id, :description => action_description) @todo = @current_user.todos.create!(:context_id => @context.id, :project_id => @project.id, :description => action_description)
@todo.tag_with(tags) @todo.tag_with(tags)
@todo.save @todo.save
end end

View file

@ -0,0 +1,52 @@
class SessionBackdoorController < ::ApplicationController
skip_before_filter :login_required
def create
session['user_id'] = params[:user_id]
user = User.find(params[:user_id])
set_current_user(user)
user.remember_me
cookies[:auth_token] = { :value => user.remember_token, :expires => user.remember_token_expires_at }
redirect_to root_path
end
def expire_session
current_user.forget_me if logged_in?
cookies.delete :auth_token
session['user_id'] = nil
reset_session
session['expiry_time'] = Time.now
respond_to do |format|
format.html { render :text => "Session expired for test purposes"}
format.js { render :text => "" }
end
end
end
module TracksLoginHelper
begin
_routes = Rails.application.routes
_routes.disable_clear_and_finalize = true
_routes.clear!
Rails.application.routes_reloader.paths.each{ |path| load(path) }
_routes.draw do
# here you can add any route you want
match "/test_login_backdoor", to: "session_backdoor#create"
match "login/expire_session", to: "session_backdoor#expire_session"
end
ActiveSupport.on_load(:action_controller) { _routes.finalize! }
ensure
_routes.disable_clear_and_finalize = false
end
def request_signin_as(user)
visit "/test_login_backdoor?user_id=#{user.id}"
end
def signin_as(user)
session[:user_id] = user.id
@current_user = user
end
end

View file

@ -97,6 +97,23 @@ module TracksStepHelper
execute_javascript("$('#{sortable_css}').simulateDragSortable({move: #{delta}, handle: '.grip'});") execute_javascript("$('#{sortable_css}').simulateDragSortable({move: #{delta}, handle: '.grip'});")
end end
def open_view_menu
view_menu = "ul.sf-menu li#menu_view"
# click menu
view_menu_link = "#{view_menu} a#menu_view_link"
page.should have_css(view_menu_link, :visible => true)
page.find(view_menu_link).click
# wait for menu to be visible
view_menu_item = "#{view_menu} li#menu_view_toggle_contexts"
page.should have_css(view_menu_item)
within view_menu do
yield
end
end
def open_submenu_for(todo) def open_submenu_for(todo)
submenu_arrow = "div#line_todo_#{todo.id} img.todo-submenu" submenu_arrow = "div#line_todo_#{todo.id} img.todo-submenu"
page.should have_css(submenu_arrow, :visible=>true) page.should have_css(submenu_arrow, :visible=>true)

View file

@ -1,3 +1,4 @@
World(TracksLoginHelper)
World(TracksStepHelper) World(TracksStepHelper)
World(TracksFormHelper) World(TracksFormHelper)
World(TracksIdHelper) World(TracksIdHelper)

View file

@ -60,7 +60,7 @@ Feature: Toggle the containers
And I should not see the todo "test 2" And I should not see the todo "test 2"
And I should not see the todo "test 3" And I should not see the todo "test 3"
@javascript @javascript
Scenario: I can hide all collapsed context containers Scenario: I can hide all collapsed context containers
Given I have the following contexts Given I have the following contexts
| context | hide | | context | hide |
@ -87,7 +87,7 @@ Feature: Toggle the containers
And I should not see the context container for "@boss" And I should not see the context container for "@boss"
And I should not see the context container for "@ipad" And I should not see the context container for "@ipad"
@javascript @javascript
Scenario: I can hide all collapsed project containers Scenario: I can hide all collapsed project containers
Given I have the following contexts Given I have the following contexts
| context | hide | | context | hide |

View file

@ -235,6 +235,52 @@ class RecurringTodosControllerTest < ActionController::TestCase
assert_equal true, recurring_todo.show_always? assert_equal true, recurring_todo.show_always?
end end
def test_start_on_monthly_rec_todo
Timecop.travel(Time.local(2012,1,1)) do
login_as(:admin_user)
put :create,
"context_name"=>"library",
"project_name"=>"Build a working time machine",
"recurring_todo" =>
{
"daily_every_x_days"=>"1",
"daily_selector"=>"daily_every_x_day",
"description"=>"new recurring pattern",
"end_date" => nil,
"ends_on" => "no_end_date",
"monthly_day_of_week" => "2",
"monthly_every_x_day" => "2",
"monthly_every_x_month2" => "1",
"monthly_every_x_month" => "3",
"monthly_every_xth_day"=>"1",
"monthly_selector"=>"monthly_every_x_day",
"notes"=>"with some notes",
"number_of_occurences" => nil,
"recurring_period"=>"monthly",
"recurring_show_days_before"=>"0",
"recurring_target"=>"show_from_date",
"recurring_show_always" => "1",
"start_from"=>"2/1/2013",
"weekly_every_x_week"=>"1",
"weekly_return_monday"=>"m",
"yearly_day_of_week"=>"1",
"yearly_every_x_day"=>"8",
"yearly_every_xth_day"=>"1",
"yearly_month_of_year2"=>"8",
"yearly_month_of_year"=>"6",
"yearly_selector"=>"yearly_every_x_day"
},
"tag_list"=>"one, two, three, four"
assert_equal "new recurring pattern", assigns['recurring_todo'].description
assert_equal "2013-01-02 00:00:00 +0000", assigns['recurring_todo'].start_from.to_s
todo = assigns['recurring_todo'].todos.first
assert_equal "2013-01-02 00:00:00 +0000", todo.show_from.to_s
end
end
def test_find_and_inactivate def test_find_and_inactivate
login_as(:admin_user) login_as(:admin_user)

View file

@ -120,6 +120,28 @@ class TodosControllerTest < ActionController::TestCase
assert_response :success assert_response :success
assert_equal 3, @tagged assert_equal 3, @tagged
end end
def test_find_tagged_with_terms_separated_with_dot
login_as :admin_user
create_todo(description: "test dotted tag", tag_list: "first.last, second")
t = assigns['todo']
assert_equal "first.last, second", t.tag_list
get :tag, name: 'first.last.m'
assert_equal "text/html", request.format, "controller should set right content type"
assert_equal "text/html", @response.content_type
assert_equal "first.last", assigns['tag_name'], ".m should be chomped"
get :tag, name: 'first.last.txt'
assert_equal "text/plain", request.format, "controller should set right content type"
assert_equal "text/plain", @response.content_type
assert_equal "first.last", assigns['tag_name'], ".txt should be chomped"
get :tag, name: 'first.last'
assert_equal "text/html", request.format, "controller should set right content type"
assert_equal "text/html", @response.content_type
assert_equal "first.last", assigns['tag_name'], ":name should be correct"
end
def test_get_boolean_expression_from_parameters_of_tag_view_single_tag def test_get_boolean_expression_from_parameters_of_tag_view_single_tag
login_as(:admin_user) login_as(:admin_user)
@ -669,7 +691,7 @@ class TodosControllerTest < ActionController::TestCase
recurring_todo_1 = RecurringTodo.find(1) recurring_todo_1 = RecurringTodo.find(1)
#set_user_to_current_time_zone(recurring_todo_1.user) #set_user_to_current_time_zone(recurring_todo_1.user)
todo_1 = Todo.where(:recurring_todo_id => 1).first todo_1 = Todo.where(:recurring_todo_id => 1).first
today = Time.zone.now.at_midnight today = Time.zone.now.at_midnight - 1.day
# change recurrence pattern to monthly and set show_from to today # change recurrence pattern to monthly and set show_from to today
recurring_todo_1.target = 'show_from_date' recurring_todo_1.target = 'show_from_date'
@ -708,7 +730,7 @@ class TodosControllerTest < ActionController::TestCase
assert !new_todo.show_from.nil? assert !new_todo.show_from.nil?
# do not use today here. It somehow gets messed up with the timezone calculation. # do not use today here. It somehow gets messed up with the timezone calculation.
next_month = (Time.zone.now + 1.month).at_midnight next_month = (Time.zone.now - 1.day + 1.month).at_midnight
assert_equal next_month.utc.to_date.to_s(:db), new_todo.show_from.utc.to_date.to_s(:db) assert_equal next_month.utc.to_date.to_s(:db), new_todo.show_from.utc.to_date.to_s(:db)
end end
@ -920,4 +942,19 @@ class TodosControllerTest < ActionController::TestCase
assert t4.pending?, "t4 should remain pending" assert t4.pending?, "t4 should remain pending"
assert t4.predecessors.map(&:id).include?(t3.id) assert t4.predecessors.map(&:id).include?(t3.id)
end end
private
def create_todo(params={})
defaults = { source_view: 'todo',
context_name: "library", project_name: "Build a working time machine",
notes: "note", description: "a new todo", due: nil, tag_list: "a,b,c"}
params=params.reverse_merge(defaults)
put :create, _source_view: params[:_source_view],
context_name: params[:context_name], project_name: params[:project_name], tag_list: params[:tag_list],
todo: {notes: params[:notes], description: params[:description], due: params[:due]}
end
end end

View file

@ -175,6 +175,9 @@ class RecurringTodoTest < ActiveSupport::TestCase
assert_equal @sunday, due_date # june 8th assert_equal @sunday, due_date # june 8th
due_date = @monthly.get_due_date(@sunday) # june 8th due_date = @monthly.get_due_date(@sunday) # june 8th
assert_equal Time.zone.local(2008,6,8), due_date # june 8th
due_date = @monthly.get_due_date(@monday) # june 9th
assert_equal Time.zone.local(2008,8,8), due_date # aug 8th assert_equal Time.zone.local(2008,8,8), due_date # aug 8th
end end

View file

@ -11,6 +11,6 @@ class TaggingTest < ActiveSupport::TestCase
tagging.destroy tagging.destroy
assert_nil Tag.where(:name => "hello").first assert_nil Tag.where(:name => "hello").first, "Tag should be destroyed when last use in tagging was removed"
end end
end end

View file

@ -239,6 +239,26 @@ class TodoTest < ActiveSupport::TestCase
assert !@not_completed1.starred? assert !@not_completed1.starred?
end end
def test_hidden_todo_remains_hidden_after_getting_unblokked
todo = todos(:call_bill)
project=todo.project
project.hide!
assert todo.reload.hidden?, "todo in hidden project should be hidden"
todo2 = todos(:call_dino_ext)
todo.add_predecessor(todo2)
todo.block!
assert todo.pending?, "todo with predecessor should be blocked"
# cannot activate if part of hidden project
assert_raise(AASM::InvalidTransition) { todo.activate! }
todo.remove_predecessor(todo2)
assert todo.reload.hidden?, "todo should be put back in hidden state"
end
def test_todo_specification_handles_null_project def test_todo_specification_handles_null_project
# @not_completed1 has a project # @not_completed1 has a project
todo_desc = @not_completed1.description todo_desc = @not_completed1.description