From a871511c24a87c1cddd2447d7e2714214eebbbe7 Mon Sep 17 00:00:00 2001 From: sf Date: Mon, 10 Oct 2011 14:14:27 -0400 Subject: [PATCH 01/13] added develop notification bar on RAILS_ENV != production --- app/views/layouts/standard.html.erb | 5 +++++ public/images/construction.gif | Bin 0 -> 1087 bytes public/stylesheets/standard.css | 6 ++++++ 3 files changed, 11 insertions(+) create mode 100644 public/images/construction.gif diff --git a/app/views/layouts/standard.html.erb b/app/views/layouts/standard.html.erb index f47cfa96..2eb70882 100644 --- a/app/views/layouts/standard.html.erb +++ b/app/views/layouts/standard.html.erb @@ -38,7 +38,12 @@ +
+ <% if RAILS_ENV != "production" -%> +
 
+ <% end -%> +

<% if @count -%> diff --git a/public/images/construction.gif b/public/images/construction.gif new file mode 100644 index 0000000000000000000000000000000000000000..95ef4e6274b446a2416df847338dcfb2b3dd3a1e GIT binary patch literal 1087 zcmX9-ZD^Hs6#rF|Mj}DcinjC=SSg0RK#N9pg5`EKs5YF~W}S#Oq3uKTX144UmXt*F zAxhJAdT|3Ibpwl{+db(P5|kezBH8kUkyIEKq92sq&foshA<=oaM(~p!jZ5jzyS$p zzycnq*MS0)xlCp){n$$uCrTn_Vj)&yBX;5-&fE;=5oTpJW@nCUAWTxCBCNtD?7|_O zBuPuED66t5yK*QebFxw|#%gTFZXCub2YG2K&gyK=?i|jk5e4TY3D#f>_TUIc5jfka z6nWGN>Ex%W8c~@oq)KX}P8y^rYziPLaB^}E$$pypk`$C8s-h<9q9NHbt1~fUYO1ap zs%Zn6CK<_)shPTIn5H-6nhYiLT+P*8!!-?~(4;L{9BQE+8lfmlk=)YiOiBaOtF)z9 zNK}LnDjkj7$%E3FG@+;?T*g?@CS@pQlN8BB%4`#N@sLy^NhiOgQUIB{Yfdg}K~}^S zD=5vpe&&SPN2*#jqW=G4D+5~q4Sc(eFyixe_-=P7q8gf z`43HQx#H@c>4W3)%^TGA+H~W@Z&Pnxzi?aM&o}N|`O=a{t{tv)_usQ^>CnXF*6y*& z$fDp15H;w@b9`Gb}qWNr+4egfkS)N9+-1S^R2yyKO6dR zs@mJS=9lWD$C~E1JT-W9->dag^P1K_@%y2-Iy#nZ-|+a4W8-7})x(32{q@u53kNQ* s%{=$a_>p}JR=?MB_Q_Mnf8E@A=g7uqPadm|URN7=VYq(5oCdW02Qi7u0ssI2 literal 0 HcmV?d00001 diff --git a/public/stylesheets/standard.css b/public/stylesheets/standard.css index 36b21d4d..fa8f893a 100644 --- a/public/stylesheets/standard.css +++ b/public/stylesheets/standard.css @@ -277,6 +277,12 @@ a.show_successors:hover, a.link_to_successors:hover {background-image: url(../im #navlist a:hover { color: #CCC; } +#develop-notify-bar { + line-height:0.5; + background-image: url(/images/construction.gif); + background-repeat: repeat-x; +} + #topbar { position: fixed; top: 0px; From 7dfe36a20450d4ebe23d71a4800b048cd9c86977 Mon Sep 17 00:00:00 2001 From: Demian Gemperli Date: Sun, 23 Oct 2011 15:37:30 +0200 Subject: [PATCH 02/13] Removed manual that doesn't exist anymore --- .gitmodules | 3 --- doc/manual | 1 - 2 files changed, 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 doc/manual diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index bca6838e..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "doc/manual"] - path = doc/manual - url = git://github.com/bsag/tracks_manual.git diff --git a/doc/manual b/doc/manual deleted file mode 160000 index 8cb38474..00000000 --- a/doc/manual +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8cb3847448223c5f0397a98e92a93a6af656e69d From 6256caeb723d3e44b5d107125ba1526e4544e27b Mon Sep 17 00:00:00 2001 From: Demian Gemperli Date: Mon, 24 Oct 2011 21:40:18 +0200 Subject: [PATCH 03/13] Yardoc - show private and protected methods in api doc --- .yardopts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.yardopts b/.yardopts index 88651e3e..924f2990 100644 --- a/.yardopts +++ b/.yardopts @@ -1,3 +1,5 @@ --title "Tracks Documentation" --charset utf-8 --markup="textile" +--private +--protected From fafbdae079f81d9bb00c3ed607a751375e9b7413 Mon Sep 17 00:00:00 2001 From: Demian Gemperli Date: Mon, 24 Oct 2011 21:47:15 +0200 Subject: [PATCH 04/13] Logut user after password change, Closes #1047 --- app/controllers/login_controller.rb | 18 +-------------- app/controllers/preferences_controller.rb | 15 ++++++++++-- config/locales/en.yml | 2 ++ lib/login_system.rb | 28 ++++++++++++++++++++++- 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/app/controllers/login_controller.rb b/app/controllers/login_controller.rb index 52716b6f..fed1dc3a 100644 --- a/app/controllers/login_controller.rb +++ b/app/controllers/login_controller.rb @@ -69,16 +69,7 @@ class LoginController < ApplicationController end def logout - @user.forget_me if logged_in? - cookies.delete :auth_token - session['user_id'] = nil - if ( SITE_CONFIG['authentication_schemes'].include? 'cas') && session[:cas_user] - CASClient::Frameworks::Rails::Filter.logout(self) - else - reset_session - notify :notice, t('login.logged_out') - redirect_to_login - end + logout_user end def expire_session @@ -149,13 +140,6 @@ class LoginController < ApplicationController private - def redirect_to_login - respond_to do |format| - format.html { redirect_to login_path } - format.m { redirect_to login_path(:format => 'm') } - end - end - def should_expire_sessions? session['noexpiry'] != "on" end diff --git a/app/controllers/preferences_controller.rb b/app/controllers/preferences_controller.rb index e1c8638c..0f0d8df9 100644 --- a/app/controllers/preferences_controller.rb +++ b/app/controllers/preferences_controller.rb @@ -12,8 +12,11 @@ class PreferencesController < ApplicationController user_updated = current_user.update_attributes(params['user']) prefs_updated = current_user.preference.update_attributes(params['prefs']) if (user_updated && prefs_updated) - notify :notice, "Preferences updated" - redirect_to :action => 'index' + if !params['user']['password'].blank? # password updated? + logout_user t('preferences.password_changed') + else + preference_updated + end else msg = "Preferences could not be updated: " msg += "User model errors; " unless user_updated @@ -28,4 +31,12 @@ class PreferencesController < ApplicationController render :text => l(Date.today, :format => format) end +private + + # Display notification if preferences are successful updated + def preference_updated + notify :notice, t('preferences.updated') + redirect_to :action => 'index' + end + end diff --git a/config/locales/en.yml b/config/locales/en.yml index 3c908e1d..338d9531 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -659,6 +659,8 @@ en: staleness_starts_after: Staleness starts after %{days} days change_identity_url: Change Your Identity URL change_password: Change your password + password_changed: You password has been changed, please log on again. + updated: Preferences updated page_title: TRACKS::Preferences title: Your preferences token_description: Token (for feeds and API use) diff --git a/lib/login_system.rb b/lib/login_system.rb index 8bec423a..8a8a70a1 100644 --- a/lib/login_system.rb +++ b/lib/login_system.rb @@ -9,6 +9,22 @@ module LoginSystem def prefs current_user.prefs unless current_user.nil? end + + # Logout the {#current_user} and redirect to login page + # + # @param [String] message notification to display + def logout_user message=t('login.logged_out') + @user.forget_me if logged_in? + cookies.delete :auth_token + session['user_id'] = nil + if ( SITE_CONFIG['authentication_schemes'].include? 'cas') && session[:cas_user] + CASClient::Frameworks::Rails::Filter.logout(self) + else + reset_session + notify :notice, message + redirect_to_login + end + end protected @@ -132,7 +148,7 @@ module LoginSystem def set_current_user(user) @user = user end - + # overwrite if you want to have special behavior in case the user is not authorized # to access the current operation. # the default action is to redirect to the login screen @@ -192,4 +208,14 @@ module LoginSystem render :text => t('login.unsuccessful'), :status => 401 end +private + + # Redirect the user to the login page. + def redirect_to_login + respond_to do |format| + format.html { redirect_to login_path } + format.m { redirect_to login_path(:format => 'm') } + end + end + end \ No newline at end of file From 8fdbcedcbafba5c509c383dd4d4248ad14ca68a3 Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Thu, 27 Oct 2011 23:08:06 +0200 Subject: [PATCH 05/13] Fix running Tracks on passenger. The exception that Passenger threw was NoMethodError on the find_by_name method --- config/initializers/mongrel_workaround.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/mongrel_workaround.rb b/config/initializers/mongrel_workaround.rb index dcad6931..60b207b9 100644 --- a/config/initializers/mongrel_workaround.rb +++ b/config/initializers/mongrel_workaround.rb @@ -4,7 +4,7 @@ def check_mongrel_around_115 begin # Gem.available? is deprecated from rubygems 1.8.2 Gem::Specification::find_by_name "mongrel", "~>1.1.5" - rescue Gem::LoadError + rescue if RUBY_VERSION[2] == "9" false else From 34a005ec0e24713cf7ebbc2ac433ab8fcf7b638a Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Fri, 28 Oct 2011 11:17:31 +0200 Subject: [PATCH 06/13] change notify banner. you can now set it in development.rb --- app/views/layouts/standard.html.erb | 4 +--- config/environment.rb | 3 +++ config/environments/development.rb | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/views/layouts/standard.html.erb b/app/views/layouts/standard.html.erb index 2eb70882..611ccdc7 100644 --- a/app/views/layouts/standard.html.erb +++ b/app/views/layouts/standard.html.erb @@ -40,9 +40,7 @@
- <% if RAILS_ENV != "production" -%> -
 
- <% end -%> + <%= NOTIFY_BAR %>

diff --git a/config/environment.rb b/config/environment.rb index 1fc8f03b..61db438c 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -107,6 +107,9 @@ if ( SITE_CONFIG['authentication_schemes'].include? 'cas') end end +# changed in development.rb to show under_construction bar +NOTIFY_BAR = "" unless defined?(NOTIFY_BAR) + tracks_version='2.1devel' # comment out next two lines if you do not want (or can not) the date of the # last git commit in the footer diff --git a/config/environments/development.rb b/config/environments/development.rb index c30f67cf..05c880d7 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -14,4 +14,6 @@ config.action_controller.perform_caching = false config.action_mailer.raise_delivery_errors = false # Unique cookies -config.action_controller.session = { :key => 'TracksDev' } \ No newline at end of file +config.action_controller.session = { :key => 'TracksDev' } + +NOTIFY_BAR="
 
" From 2accbd0a3257c2e4a99fab2f3fb23f3fc64c603f Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Sat, 1 Oct 2011 03:45:41 +0200 Subject: [PATCH 07/13] start changing param parsing to allow and and or of tags --- app/controllers/todos_controller.rb | 63 +++++++++++++++++++++++---- app/models/todo.rb | 1 + test/functional/todos_tagging_test.rb | 62 ++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 test/functional/todos_tagging_test.rb diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index b626653f..6294a82b 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -584,27 +584,72 @@ class TodosController < ApplicationController redirect_to project_todos_path(project, :format => 'm') end + def get_ids_from_tag_expr(tag_expr) + ids = [] + tag_expr.each do |tag_list| + id_list = [] + tag_list.each do |tag| + tag = Tag.find_by_name(tag) + id_list << tag.id if tag + end + ids << id_list + end + return ids + end + + def get_params_for_tag_view + # use sanitize to prevent XSS attacks + + @tag_expr = [] + @tag_expr << sanitize(params[:name]).split(',') + @tag_expr << sanitize(params[:and]).split(',') if params[:and] + + i = 1 + while params['and'+i.to_s] + @tag_expr << sanitize(params['and'+i.to_s]).split(',') + i=i+1 + end + + @single_tag = @tag_expr.size == 1 && @tag_expr[0].size == 1 + @tag_name = @tag_expr[0][0] # if @single_tag + end + + def find_todos_with_tag_ids(tag_ids) + todos = current_user.todos + tag_ids.each do |ids| + todos = todos.with_tags(ids) unless ids.nil? || ids.empty? + end + return todos + end + # /todos/tag/[tag_name] shows all the actions tagged with tag_name def tag - init_data_for_sidebar unless mobile? - @source_view = params['_source_view'] || 'tag' - @tag_name = sanitize(params[:name]) # sanitize to prevent XSS vunerability! @page_title = t('todos.tagged_page_title', :tag_name => @tag_name) + @source_view = params['_source_view'] || 'tag' - # mobile tags are routed with :name ending on .m. So we need to chomp it - @tag_name = @tag_name.chomp('.m') if mobile? + get_params_for_tag_view + + if 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 @tag = Tag.find_by_name(@tag_name) @tag = Tag.new(:name => @tag_name) if @tag.nil? - @not_done_todos = current_user.todos.with_tag(@tag).active.not_hidden.find(:all, + @tag_ids = get_ids_from_tag_expr(@tag_expr) + todos_with_tag_ids = find_todos_with_tag_ids(@tag_ids) + + @not_done_todos = todos_with_tag_ids.active.not_hidden.find(:all, :order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC', :include => Todo::DEFAULT_INCLUDES) - @hidden_todos = current_user.todos.with_tag(@tag).hidden.find(:all, + @hidden_todos = todos_with_tag_ids.hidden.find(:all, :include => Todo::DEFAULT_INCLUDES, :order => 'todos.completed_at DESC, todos.created_at DESC') - @deferred = current_user.todos.with_tag(@tag).deferred.find(:all, + @deferred = todos_with_tag_ids.deferred.find(:all, :order => 'show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES) - @pending = current_user.todos.with_tag(@tag).blocked.find(:all, + @pending = todos_with_tag_ids.blocked.find(:all, :order => 'show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES) # If you've set no_completed to zero, the completed items box isn't shown on diff --git a/app/models/todo.rb b/app/models/todo.rb index ea6e17c9..80481546 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -39,6 +39,7 @@ class Todo < ActiveRecord::Base # other scopes named_scope :are_due, :conditions => ['NOT (todos.due IS NULL)'] named_scope :with_tag, lambda { |tag| {:joins => :taggings, :conditions => ["taggings.tag_id = ? ", tag.id] } } + named_scope :with_tags, lambda { |tag_ids| {:joins => :taggings, :conditions => ["taggings.tag_id IN (?) ", tag_ids] } } named_scope :of_user, lambda { |user_id| {:conditions => ["todos.user_id = ? ", user_id] } } named_scope :completed_after, lambda { |date| {:conditions => ["todos.completed_at > ? ", date] } } named_scope :completed_before, lambda { |date| {:conditions => ["todos.completed_at < ? ", date] } } diff --git a/test/functional/todos_tagging_test.rb b/test/functional/todos_tagging_test.rb new file mode 100644 index 00000000..46dd1627 --- /dev/null +++ b/test/functional/todos_tagging_test.rb @@ -0,0 +1,62 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'todos_controller' + +# Re-raise errors caught by the controller. +class TodosController; def rescue_action(e) raise e end; end + +class TodosControllerTest < ActionController::TestCase + fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings, :recurring_todos + + def test_get_boolean_expression_from_parameters_of_tag_view_single_tag + login_as(:admin_user) + get :tag, :name => "single" + assert_equal true, assigns['single_tag'], "should recognize it is a single tag name" + assert_equal "single", assigns['tag_expr'][0][0], "should store the single tag" + end + + def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags + login_as(:admin_user) + get :tag, :name => "multiple", :and => "tags", :and1 => "present", :and2 => "here" + assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" + assert_equal 4, assigns['tag_expr'].size, "should have 4 AND expressions" + end + + def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags_without_digitless_and + login_as(:admin_user) + get :tag, :name => "multiple", :and1 => "tags", :and2 => "present", :and3 => "here" + assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" + assert_equal 4, assigns['tag_expr'].size, "should have 4 AND expressions" + end + + def test_get_boolean_expression_from_parameters_of_tag_view_multiple_ORs + login_as(:admin_user) + get :tag, :name => "multiple,tags,present" + assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" + assert_equal 1, assigns['tag_expr'].size, "should have 1 expressions" + assert_equal 3, assigns['tag_expr'][0].size, "should have 3 ORs in 1st expression" + end + + def test_get_boolean_expression_from_parameters_of_tag_view_multiple_ORs_and_ANDS + login_as(:admin_user) + get :tag, :name => "multiple,tags,present", :and => "here,is,two", :and1=>"and,three" + assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" + assert_equal 3, assigns['tag_expr'].size, "should have 3 expressions" + assert_equal 3, assigns['tag_expr'][0].size, "should have 3 ORs in 1st expression" + assert_equal 3, assigns['tag_expr'][1].size, "should have 3 ORs in 2nd expression" + assert_equal 2, assigns['tag_expr'][2].size, "should have 2 ORs in 3rd expression" + end + + def test_get_ids_from_tag_expr + login_as(:admin_user) + + # make sure the tags exits + # "multiple,tags,present,here,is,two,and,three".split(',').each { |tag| Tag.find_or_create_by_name(:name=>tag)} + + get :tag, :name => "foo,bar", :and => "baz" + + assert_equal 1, assigns['tag_ids'][0][0], "first id should be 1 for foo" + assert_equal 2, assigns['tag_ids'][0][1], "second id should be 2 for bar" + assert_equal 3, assigns['tag_ids'][1][0], "third id should be 3 for baz" + end + +end \ No newline at end of file From 58d8bc56d1e394db9e83dc80bf450e2288241663 Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Fri, 28 Oct 2011 19:33:51 +0200 Subject: [PATCH 08/13] fix #827. You can now select todos with tags using OR and AND /todos/tag/tagA,tagB?and=tagC will select all todos with (tagA or tagB) AND tagC --- app/controllers/todos_controller.rb | 113 ++++++++++---------- app/models/todo.rb | 10 +- app/views/todos/mobile_tag.rhtml | 12 +-- app/views/todos/tag.html.erb | 14 +-- config/locales/en.yml | 4 +- config/routes.rb | 1 + test/functional/todos_controller_test.rb | 64 ++++++++++++ test/functional/todos_tagging_test.rb | 62 ----------- test/unit/todo_test.rb | 127 ++++++++++++++++++++--- 9 files changed, 255 insertions(+), 152 deletions(-) delete mode 100644 test/functional/todos_tagging_test.rb diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index 6294a82b..fdf12454 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -2,8 +2,8 @@ class TodosController < ApplicationController helper :todos - skip_before_filter :login_required, :only => [:index, :calendar] - prepend_before_filter :login_or_feed_token_required, :only => [:index, :calendar] + skip_before_filter :login_required, :only => [:index, :calendar, :tag] + prepend_before_filter :login_or_feed_token_required, :only => [:index, :calendar, :tag] append_before_filter :find_and_activate_ready, :only => [:index, :list_deferred] # TODO: replace :except with :only @@ -584,50 +584,11 @@ class TodosController < ApplicationController redirect_to project_todos_path(project, :format => 'm') end - def get_ids_from_tag_expr(tag_expr) - ids = [] - tag_expr.each do |tag_list| - id_list = [] - tag_list.each do |tag| - tag = Tag.find_by_name(tag) - id_list << tag.id if tag - end - ids << id_list - end - return ids - end - - def get_params_for_tag_view - # use sanitize to prevent XSS attacks - - @tag_expr = [] - @tag_expr << sanitize(params[:name]).split(',') - @tag_expr << sanitize(params[:and]).split(',') if params[:and] - - i = 1 - while params['and'+i.to_s] - @tag_expr << sanitize(params['and'+i.to_s]).split(',') - i=i+1 - end - - @single_tag = @tag_expr.size == 1 && @tag_expr[0].size == 1 - @tag_name = @tag_expr[0][0] # if @single_tag - end - - def find_todos_with_tag_ids(tag_ids) - todos = current_user.todos - tag_ids.each do |ids| - todos = todos.with_tags(ids) unless ids.nil? || ids.empty? - end - return todos - end - # /todos/tag/[tag_name] shows all the actions tagged with tag_name def tag - @page_title = t('todos.tagged_page_title', :tag_name => @tag_name) - @source_view = params['_source_view'] || 'tag' - get_params_for_tag_view + @page_title = t('todos.tagged_page_title', :tag_name => @tag_title) + @source_view = params['_source_view'] || 'tag' if mobile? # mobile tags are routed with :name ending on .m. So we need to chomp it @@ -636,11 +597,7 @@ class TodosController < ApplicationController init_data_for_sidebar end - @tag = Tag.find_by_name(@tag_name) - @tag = Tag.new(:name => @tag_name) if @tag.nil? - - @tag_ids = get_ids_from_tag_expr(@tag_expr) - todos_with_tag_ids = find_todos_with_tag_ids(@tag_ids) + todos_with_tag_ids = find_todos_with_tag_expr(@tag_expr) @not_done_todos = todos_with_tag_ids.active.not_hidden.find(:all, :order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC', :include => Todo::DEFAULT_INCLUDES) @@ -648,17 +605,15 @@ class TodosController < ApplicationController :include => Todo::DEFAULT_INCLUDES, :order => 'todos.completed_at DESC, todos.created_at DESC') @deferred = todos_with_tag_ids.deferred.find(:all, - :order => 'show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES) + :order => 'todos.show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES) @pending = todos_with_tag_ids.blocked.find(:all, - :order => 'show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES) + :order => 'todos.show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES) # 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 = current_user.todos.with_tag(@tag).completed.find(:all, - :limit => max_completed, - :order => 'todos.completed_at DESC', - :include => Todo::DEFAULT_INCLUDES) + @done = todos_with_tag_ids.completed.find(:all, + :limit => current_user.prefs.show_number_completed, + :order => 'todos.completed_at DESC', :include => Todo::DEFAULT_INCLUDES) @projects = current_user.projects @contexts = current_user.contexts @@ -680,6 +635,9 @@ class TodosController < ApplicationController cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']} render :action => "mobile_tag" } + format.text { + render :action => 'index', :layout => false, :content_type => Mime::TEXT + } end end @@ -1033,6 +991,51 @@ class TodosController < ApplicationController :include => [ :project, :context, :tags ]) end + def tag_title(tag_expr) + and_list = tag_expr.inject([]) { |s,tag_list| s << tag_list.join(',') } + return and_list.join(' AND ') + end + + def get_params_for_tag_view + # use sanitize to prevent XSS attacks + + @tag_expr = [] + @tag_expr << sanitize(params[:name]).split(',') + @tag_expr << sanitize(params[:and]).split(',') if params[:and] + + i = 1 + while params['and'+i.to_s] + @tag_expr << sanitize(params['and'+i.to_s]).split(',') + i=i+1 + end + + @single_tag = @tag_expr.size == 1 && @tag_expr[0].size == 1 + @tag_name = @tag_expr[0][0] + @tag_title = @single_tag ? @tag_name : tag_title(@tag_expr) + end + + def get_ids_from_tag_expr(tag_expr) + ids = [] + tag_expr.each do |tag_list| + id_list = [] + tag_list.each do |tag| + tag = Tag.find_by_name(tag) + id_list << tag.id if tag + end + ids << id_list + end + return ids + end + + def find_todos_with_tag_expr(tag_expr) + tag_ids = get_ids_from_tag_expr(tag_expr) + todos = current_user.todos + tag_ids.each do |ids| + todos = todos.with_tags(ids) unless ids.nil? || ids.empty? + end + return todos + end + def determine_down_count source_view do |from| from.todo do diff --git a/app/models/todo.rb b/app/models/todo.rb index 80481546..45253ab3 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -28,18 +28,18 @@ class Todo < ActiveRecord::Base named_scope :deferred_or_blocked, :conditions => ["(todos.completed_at IS NULL AND NOT(todos.show_from IS NULL)) OR (todos.state = ?)", "pending"] named_scope :not_deferred_or_blocked, :conditions => ["todos.completed_at IS NULL AND todos.show_from IS NULL AND NOT(todos.state = ?)", "pending"] named_scope :hidden, - :joins => :context, - :conditions => ["todos.state = ? OR (contexts.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?))", + :joins => "INNER JOIN contexts c_hidden ON c_hidden.id = todos.context_id", + :conditions => ["todos.state = ? OR (c_hidden.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?))", 'project_hidden', true, 'active', 'deferred', 'pending'] named_scope :not_hidden, - :joins => [:context], - :conditions => ['NOT(todos.state = ? OR (contexts.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?)))', + :joins => "INNER JOIN contexts c_hidden ON c_hidden.id = todos.context_id", + :conditions => ['NOT(todos.state = ? OR (c_hidden.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?)))', 'project_hidden', true, 'active', 'deferred', 'pending'] # other scopes named_scope :are_due, :conditions => ['NOT (todos.due IS NULL)'] named_scope :with_tag, lambda { |tag| {:joins => :taggings, :conditions => ["taggings.tag_id = ? ", tag.id] } } - named_scope :with_tags, lambda { |tag_ids| {:joins => :taggings, :conditions => ["taggings.tag_id IN (?) ", tag_ids] } } + named_scope :with_tags, lambda { |tag_ids| {:conditions => ["EXISTS(SELECT * from taggings t WHERE t.tag_id IN (?) AND t.taggable_id=todos.id AND t.taggable_type='Todo')", tag_ids] } } named_scope :of_user, lambda { |user_id| {:conditions => ["todos.user_id = ? ", user_id] } } named_scope :completed_after, lambda { |date| {:conditions => ["todos.completed_at > ? ", date] } } named_scope :completed_before, lambda { |date| {:conditions => ["todos.completed_at < ? ", date] } } diff --git a/app/views/todos/mobile_tag.rhtml b/app/views/todos/mobile_tag.rhtml index 5dd8efd3..4dde5250 100644 --- a/app/views/todos/mobile_tag.rhtml +++ b/app/views/todos/mobile_tag.rhtml @@ -2,24 +2,24 @@ <% if @not_done_todos.empty? -%>

<%= t('todos.no_actions_found_title') %>

-
<%= t('todos.no_actions_with', :tag_name => @tag_name) %>
+
<%= t('todos.no_actions_with', :tag_name => @tag_title) %>
<% end -%> <%= render :partial => "contexts/mobile_context", :collection => @contexts_to_show -%> -

<%= t('todos.deferred_actions_with', :tag_name=> @tag_name) %>

+

<%= t('todos.deferred_actions_with', :tag_name=> @tag_title) %>

<% unless (@deferred.nil? or @deferred.size == 0) -%> <%= render :partial => "todos/mobile_todo", :collection => @deferred, :locals => { :parent_container_type => "tag" } -%> -
+ <% else -%> -<%= t('todos.no_deferred_actions_with', :tag_name => @tag_name) %> +<%= t('todos.no_deferred_actions_with', :tag_name => @tag_title) %> <% end -%> -

<%= t('todos.completed_actions_with', :tag_name => @tag_name) %>

+

<%= t('todos.completed_actions_with', :tag_name => @tag_title) %>

<% unless (@done.nil? or @done.size == 0) -%> <%= render :partial => "todos/mobile_todo", :collection => @done, :locals => { :parent_container_type => "tag" } %>
<% else -%> -<%= t('todos.no_completed_actions_with', :tag_name => @tag_name) %> +<%= t('todos.no_completed_actions_with', :tag_name => @tag_title) %> <% end -%>

\ No newline at end of file diff --git a/app/views/todos/tag.html.erb b/app/views/todos/tag.html.erb index 646f153c..64506aa1 100644 --- a/app/views/todos/tag.html.erb +++ b/app/views/todos/tag.html.erb @@ -8,22 +8,22 @@ :locals => { :collapsible => true } %> <% unless @deferred.nil? -%> - <%= render :partial => "todos/deferred", :locals => { - :deferred => @deferred, + <%= render :partial => "todos/deferred", :locals => { + :deferred => @deferred, :pending => @pending, - :collapsible => true, - :append_descriptor => t('todos.tagged_with', :tag_name => @tag_name), - :parent_container_type => 'tag' + :collapsible => true, + :append_descriptor => t('todos.tagged_with', :tag_name => @tag_title), + :parent_container_type => 'tag' } %> <% end -%> <% unless @hidden_todos.nil? -%> - <%= render :partial => "todos/hidden", :object => @hidden_todos, :locals => { :collapsible => true, :append_descriptor => t('todos.tagged_with', :tag_name => @tag_name) } %> + <%= render :partial => "todos/hidden", :object => @hidden_todos, :locals => { :collapsible => true, :append_descriptor => t('todos.tagged_with', :tag_name => @tag_title) } %> <% end -%> <% unless @done.nil? -%> <%= render :partial => "todos/completed", :object => @done, - :locals => { :collapsible => true, :append_descriptor => t('todos.tagged_with', :tag_name => @tag_name) } %> + :locals => { :collapsible => true, :append_descriptor => t('todos.tagged_with', :tag_name => @tag_title) } %> <% end -%>
diff --git a/config/locales/en.yml b/config/locales/en.yml index 338d9531..9feff005 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -391,10 +391,10 @@ en: all_completed: All completed actions feed_title_in_context: in context '%{context}' older_than_days: "" - completed_tagged_page_title: TRACKS::Completed tasks with tag %{tag_name} + completed_tagged_page_title: TRACKS::Completed tasks with tag '%{tag_name}' edit: Edit pending: Pending - completed_actions_with: Completed actions with the tag %{tag_name} + completed_actions_with: Completed actions with the tag '%{tag_name}' deleted_success: The action was deleted succesfully. completed_tasks_title: TRACKS::Completed tasks feed_title_in_project: in project '%{project}' diff --git a/config/routes.rb b/config/routes.rb index 60f777cd..533ca270 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -40,6 +40,7 @@ ActionController::Routing::Routes.draw do |map| # UPDATE: added support for mobile view. All tags ending on .m will be # routed to mobile view of tags. todos.mobile_tag 'todos/tag/:name.m', :action => "tag", :format => 'm' + todos.mobile_tag 'todos/tag/:name.txt', :action => "tag", :format => 'txt' todos.tag 'todos/tag/:name', :action => "tag", :name => /.*/ todos.done_tag 'todos/done/tag/:name', :action => "done_tag" todos.all_done_tag 'todos/all_done/tag/:name', :action => "all_done_tag" diff --git a/test/functional/todos_controller_test.rb b/test/functional/todos_controller_test.rb index 1abdfadc..20b3c7a5 100644 --- a/test/functional/todos_controller_test.rb +++ b/test/functional/todos_controller_test.rb @@ -644,4 +644,68 @@ class TodosControllerTest < ActionController::TestCase assert_select("div#notes_todo_#{todo.id} a", 'link me to onenote') assert_select("div#notes_todo_#{todo.id} a[href=onenote:///E:\\OneNote\\dir\\notes.one#PAGE&section-id={FD597D3A-3793-495F-8345-23D34A00DD3B}&page-id={1C95A1C7-6408-4804-B3B5-96C28426022B}&end]", 'link me to onenote') end + + def test_get_boolean_expression_from_parameters_of_tag_view_single_tag + login_as(:admin_user) + get :tag, :name => "single" + assert_equal true, assigns['single_tag'], "should recognize it is a single tag name" + assert_equal "single", assigns['tag_expr'][0][0], "should store the single tag" + end + + def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags + login_as(:admin_user) + get :tag, :name => "multiple", :and => "tags", :and1 => "present", :and2 => "here" + assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" + assert_equal 4, assigns['tag_expr'].size, "should have 4 AND expressions" + end + + def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags_without_digitless_and + login_as(:admin_user) + get :tag, :name => "multiple", :and1 => "tags", :and2 => "present", :and3 => "here" + assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" + assert_equal 4, assigns['tag_expr'].size, "should have 4 AND expressions" + end + + def test_get_boolean_expression_from_parameters_of_tag_view_multiple_ORs + login_as(:admin_user) + get :tag, :name => "multiple,tags,present" + assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" + assert_equal 1, assigns['tag_expr'].size, "should have 1 expressions" + assert_equal 3, assigns['tag_expr'][0].size, "should have 3 ORs in 1st expression" + end + + def test_get_boolean_expression_from_parameters_of_tag_view_multiple_ORs_and_ANDS + login_as(:admin_user) + get :tag, :name => "multiple,tags,present", :and => "here,is,two", :and1=>"and,three" + assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" + assert_equal 3, assigns['tag_expr'].size, "should have 3 expressions" + assert_equal 3, assigns['tag_expr'][0].size, "should have 3 ORs in 1st expression" + assert_equal 3, assigns['tag_expr'][1].size, "should have 3 ORs in 2nd expression" + assert_equal 2, assigns['tag_expr'][2].size, "should have 2 ORs in 3rd expression" + end + + def test_set_right_title + login_as(:admin_user) + + get :tag, :name => "foo" + assert_equal "foo", assigns['tag_title'] + get :tag, :name => "foo,bar", :and => "baz" + assert_equal "foo,bar AND baz", assigns['tag_title'] + end + + def test_set_default_tag + login_as(:admin_user) + + get :tag, :name => "foo" + assert_equal "foo", assigns['initial_tags'] + get :tag, :name => "foo,bar", :and => "baz" + assert_equal "foo", assigns['initial_tags'] + end + + def test_tag_text_feed_not_accessible_to_anonymous_user_without_token + login_as nil + get :tag, {:name => "foo", :format => "txt" } + assert_response 401 + end + end diff --git a/test/functional/todos_tagging_test.rb b/test/functional/todos_tagging_test.rb deleted file mode 100644 index 46dd1627..00000000 --- a/test/functional/todos_tagging_test.rb +++ /dev/null @@ -1,62 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' -require 'todos_controller' - -# Re-raise errors caught by the controller. -class TodosController; def rescue_action(e) raise e end; end - -class TodosControllerTest < ActionController::TestCase - fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings, :recurring_todos - - def test_get_boolean_expression_from_parameters_of_tag_view_single_tag - login_as(:admin_user) - get :tag, :name => "single" - assert_equal true, assigns['single_tag'], "should recognize it is a single tag name" - assert_equal "single", assigns['tag_expr'][0][0], "should store the single tag" - end - - def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags - login_as(:admin_user) - get :tag, :name => "multiple", :and => "tags", :and1 => "present", :and2 => "here" - assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" - assert_equal 4, assigns['tag_expr'].size, "should have 4 AND expressions" - end - - def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags_without_digitless_and - login_as(:admin_user) - get :tag, :name => "multiple", :and1 => "tags", :and2 => "present", :and3 => "here" - assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" - assert_equal 4, assigns['tag_expr'].size, "should have 4 AND expressions" - end - - def test_get_boolean_expression_from_parameters_of_tag_view_multiple_ORs - login_as(:admin_user) - get :tag, :name => "multiple,tags,present" - assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" - assert_equal 1, assigns['tag_expr'].size, "should have 1 expressions" - assert_equal 3, assigns['tag_expr'][0].size, "should have 3 ORs in 1st expression" - end - - def test_get_boolean_expression_from_parameters_of_tag_view_multiple_ORs_and_ANDS - login_as(:admin_user) - get :tag, :name => "multiple,tags,present", :and => "here,is,two", :and1=>"and,three" - assert_equal false, assigns['single_tag'], "should recognize it has multiple tags" - assert_equal 3, assigns['tag_expr'].size, "should have 3 expressions" - assert_equal 3, assigns['tag_expr'][0].size, "should have 3 ORs in 1st expression" - assert_equal 3, assigns['tag_expr'][1].size, "should have 3 ORs in 2nd expression" - assert_equal 2, assigns['tag_expr'][2].size, "should have 2 ORs in 3rd expression" - end - - def test_get_ids_from_tag_expr - login_as(:admin_user) - - # make sure the tags exits - # "multiple,tags,present,here,is,two,and,three".split(',').each { |tag| Tag.find_or_create_by_name(:name=>tag)} - - get :tag, :name => "foo,bar", :and => "baz" - - assert_equal 1, assigns['tag_ids'][0][0], "first id should be 1 for foo" - assert_equal 2, assigns['tag_ids'][0][1], "second id should be 2 for bar" - assert_equal 3, assigns['tag_ids'][1][0], "third id should be 3 for baz" - end - -end \ No newline at end of file diff --git a/test/unit/todo_test.rb b/test/unit/todo_test.rb index 00460139..eb677699 100644 --- a/test/unit/todo_test.rb +++ b/test/unit/todo_test.rb @@ -9,7 +9,7 @@ class TodoTest < ActiveSupport::TestCase @not_completed2 = Todo.find(2).reload @completed = Todo.find(8).reload end - + # Test loading a todo item def test_load assert_kind_of Todo, @not_completed1 @@ -24,13 +24,13 @@ class TodoTest < ActiveSupport::TestCase assert_nil @not_completed1.completed_at assert_equal 1, @not_completed1.user_id end - + def test_completed assert_kind_of Todo, @completed assert @completed.completed? assert_not_nil @completed.completed_at end - + def test_completed_at_cleared_after_toggle_to_active assert_kind_of Todo, @completed assert @completed.completed? @@ -38,8 +38,8 @@ class TodoTest < ActiveSupport::TestCase assert @completed.active? assert_nil @completed.completed_at end - - + + # Validation tests # def test_validate_presence_of_description @@ -49,7 +49,7 @@ class TodoTest < ActiveSupport::TestCase assert_equal 1, @not_completed2.errors.count assert_equal "can't be blank", @not_completed2.errors.on(:description) end - + def test_validate_length_of_description assert_equal "Call dinosaur exterminator", @not_completed2.description @not_completed2.description = generate_random_string(101) @@ -57,7 +57,7 @@ class TodoTest < ActiveSupport::TestCase assert_equal 1, @not_completed2.errors.count assert_equal "is too long (maximum is 100 characters)", @not_completed2.errors.on(:description) end - + def test_validate_length_of_notes assert_equal "Ask him if I need to hire a skip for the corpses.", @not_completed2.notes @not_completed2.notes = generate_random_string(60001) @@ -74,7 +74,7 @@ class TodoTest < ActiveSupport::TestCase assert_equal 1, t.errors.count assert_equal "must be a date in the future", t.errors.on(:show_from) end - + def test_defer_an_existing_todo @not_completed2 assert_equal :active, @not_completed2.aasm_current_state @@ -82,20 +82,20 @@ class TodoTest < ActiveSupport::TestCase assert @not_completed2.save, "should have saved successfully" + @not_completed2.errors.to_xml assert_equal :deferred, @not_completed2.aasm_current_state end - + def test_create_a_new_deferred_todo user = users(:other_user) todo = user.todos.build todo.show_from = next_week todo.context_id = 1 - todo.description = 'foo' + todo.description = 'foo' assert todo.save, "should have saved successfully" + todo.errors.to_xml assert_equal :deferred, todo.aasm_current_state end def test_create_a_new_deferred_todo_by_passing_attributes user = users(:other_user) - todo = user.todos.build(:show_from => next_week, :context_id => 1, :description => 'foo') + todo = user.todos.build(:show_from => next_week, :context_id => 1, :description => 'foo') assert todo.save, "should have saved successfully" + todo.errors.to_xml assert_equal :deferred, todo.aasm_current_state end @@ -167,15 +167,15 @@ class TodoTest < ActiveSupport::TestCase t.reload assert_equal :deferred, t.aasm_current_state end - + def test_todo_is_not_starred assert !@not_completed1.starred? end - + def test_todo_2_is_not_starred assert !Todo.find(2).starred? end - + def test_todo_is_starred_after_starred_tag_is_added @not_completed1._add_tags('starred') assert @not_completed1.starred? @@ -185,7 +185,7 @@ class TodoTest < ActiveSupport::TestCase @not_completed1.toggle_star! assert @not_completed1.starred? end - + def test_todo_is_not_starred_after_toggle_starred_twice @not_completed1.toggle_star! @not_completed1.toggle_star! @@ -239,4 +239,101 @@ class TodoTest < ActiveSupport::TestCase assert_equal 2, @predecessor_array.size end + def test_finding_todos_with_a_tag + todo = @not_completed1 + todo.tag_list = "a, b, c" + todo.save! + + tag_a = Tag.find_by_name("a") + tag_b = Tag.find_by_name("b") + tag_c = Tag.find_by_name("c") + + todos_with_a = Todo.with_tag(tag_a) + assert 1, todos_with_a.count + assert_equal todo.description, todos_with_a.first.description + + todos_with_b = Todo.with_tag(tag_b) + assert 1, todos_with_b.count + assert_equal todo.id, todos_with_b.first.id + + todo2 = @not_completed2 + todo2.tag_list = "a, c, d" + todo2.save! + + tag_d = Tag.find_by_name("d") + + todos_with_a = Todo.with_tag(tag_a) + assert 2, todos_with_a.count + + todos_with_d = Todo.with_tag(tag_d) + assert 1, todos_with_a.count + end + + def test_finding_todos_with_more_tags_using_OR + todo1 = @not_completed1 + todo1.tag_list = "a, b, c" + todo1.save! + + todo2 = @not_completed2 + todo2.tag_list = "a, c, d" + todo2.save! + + tag_a = Tag.find_by_name("a") + tag_b = Tag.find_by_name("b") + tag_c = Tag.find_by_name("c") + tag_d = Tag.find_by_name("d") + + # overlapping tags + tag_ids = [tag_a.id, tag_c.id] + todos_with_a_or_c = Todo.with_tags(tag_ids) + assert 2, todos_with_a_or_c.count + + # non-overlapping tags + tag_ids = [tag_b.id, tag_d.id] + todos_with_b_or_d = Todo.with_tags(tag_ids) + assert 2, todos_with_b_or_d.count + end + + def test_finding_todos_with_more_tags_using_AND + todo1 = @not_completed1 + todo1.tag_list = "a, b, c" + todo1.save! + + todo2 = @not_completed2 + todo2.tag_list = "a, c, d" + todo2.save! + + tag_a_id = Tag.find_by_name("a").id + tag_b_id = Tag.find_by_name("b").id + + todos_with_a_and_b = Todo.with_tags([tag_a_id]).with_tags([tag_b_id]) + assert 1, todos_with_a_and_b.count + assert todo1.id, todos_with_a_and_b.first.id + end + + def test_finding_todos_with_more_tags_using_AND_and_OR + todo1 = @not_completed1 + todo1.tag_list = "a, b, c" + todo1.save! + + todo2 = @not_completed2 + todo2.tag_list = "a, c, d" + todo2.save! + + tag_a_id = Tag.find_by_name("a").id + tag_b_id = Tag.find_by_name("b").id + tag_c_id = Tag.find_by_name("c").id + + todos_with_aORc_and_b = Todo.with_tags([tag_a_id, tag_c_id]).with_tags([tag_b_id]) + assert 1, todos_with_aORc_and_b.count + assert todo1.id, todos_with_aORc_and_b.first.id + + # let todo2 fit the expression + todo2.tag_list = "a, b, r" + todo2.save! + todos_with_aORc_and_b = Todo.with_tags([tag_a_id, tag_c_id]).with_tags([tag_b_id]) + assert 2, todos_with_aORc_and_b.count + end + + end From bfb6c4ee9abcf320178a282ce6862c5249df995f Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Fri, 28 Oct 2011 20:50:38 +0200 Subject: [PATCH 09/13] optimize for the common case: selecting only one tag --- app/controllers/todos_controller.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index fdf12454..7f021d80 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -1028,6 +1028,9 @@ class TodosController < ApplicationController end def find_todos_with_tag_expr(tag_expr) + # optimize for the common case: selecting only one tag + return current_user.todos.with_tag(@tag_name) if @single_tag + tag_ids = get_ids_from_tag_expr(tag_expr) todos = current_user.todos tag_ids.each do |ids| From 29e1de8ef268fc595acc4db9b8f15db9dffedd46 Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Fri, 28 Oct 2011 20:51:02 +0200 Subject: [PATCH 10/13] fix failing selenium tests --- app/views/projects/update.js.erb | 2 +- features/step_definitions/project_steps.rb | 2 +- public/javascripts/application.js | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/projects/update.js.erb b/app/views/projects/update.js.erb index 45b7df79..270a38b0 100644 --- a/app/views/projects/update.js.erb +++ b/app/views/projects/update.js.erb @@ -30,7 +30,7 @@ function update_project_page() { remove_project_edit_form(); update_and_show_project_settings(); TracksForm.set_project_name("<%= escape_javascript(@project.name)%>"); - $("h2#project_name").html("<%= escape_javascript(@project.name)%>"); + $("h2 div#project_name").html("<%= escape_javascript(@project.name)%>"); <% if @project.default_context %> TracksForm.set_context_name_and_default_context_name("<%= escape_javascript(@project.default_context.name)%>"); <% end %> diff --git a/features/step_definitions/project_steps.rb b/features/step_definitions/project_steps.rb index cfb3589d..145cbd06 100644 --- a/features/step_definitions/project_steps.rb +++ b/features/step_definitions/project_steps.rb @@ -274,7 +274,7 @@ end Then /^the project title should be "(.*)"$/ do |title| wait_for :timeout => 2 do - selenium.get_text("css=h2#project_name") == title + selenium.get_text("css=h2#project_name_container div#project_name") == title end end diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 280bf6bf..7e5ec824 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -305,19 +305,19 @@ var TracksPages = { $(".todo_notes").toggle(); }); - + /* Poor man's perspectives, allows to hide any context that is collapsed */ $("#toggle-contexts-nav").click(function () { /* Need to keep a single toggle across all contexts */ - $(this).toggleClass("context_visibility"); + $(this).toggleClass("context_visibility"); if ($(this).hasClass("context_visibility")) { $(".context_collapsed").hide(); /* Hide all collapsed contexts together*/ } else { - $(".context_collapsed").show(); + $(".context_collapsed").show(); } }); - + /* fade flashes and alerts in automatically */ $(".alert").fadeOut(8000); } From 10f6a4306b99c04de65e4639d2fa3d5f9f2a9a62 Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Fri, 28 Oct 2011 20:51:28 +0200 Subject: [PATCH 11/13] Merge branches 'multi-tag' and 'master' From 67d574bf7302c860b3ed54b23f11d21c7cf1956c Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Mon, 31 Oct 2011 13:13:44 +0100 Subject: [PATCH 12/13] fix #887. Reference log file in error 500 message --- public/500.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/500.html b/public/500.html index ec3bbf02..4bc0a825 100644 --- a/public/500.html +++ b/public/500.html @@ -24,7 +24,7 @@

We're sorry, but something went wrong.

-

We've been notified about this issue and we'll take a look at it shortly.

+

The Tracks application failed with error 500. More details can be found in the log file of Tracks

From cda75a0cc103c85eacbae330e8198ad178e5f072 Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Mon, 31 Oct 2011 14:39:52 +0100 Subject: [PATCH 13/13] fix regression where viewing a page for a single tag (like starred) did not work and fix some test warnings/errors --- app/controllers/todos_controller.rb | 24 ++++++++++++++---------- app/models/todo.rb | 2 +- config/routes.rb | 2 +- features/shared_add_new_todo.feature | 2 +- test/functional/todos_controller_test.rb | 3 ++- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index 7f021d80..61aee981 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -648,7 +648,7 @@ class TodosController < ApplicationController @tag = Tag.find_by_name(@tag_name) @tag = Tag.new(:name => @tag_name) if @tag.nil? - completed_todos = current_user.todos.completed.with_tag(@tag) + completed_todos = current_user.todos.completed.with_tag(@tag.id) @done_today = get_done_today(completed_todos) @done_this_week = get_done_this_week(completed_todos) @@ -665,7 +665,7 @@ class TodosController < ApplicationController @tag = Tag.find_by_name(@tag_name) @tag = Tag.new(:name => @tag_name) if @tag.nil? - @done = current_user.todos.completed.with_tag(@tag).paginate :page => params[:page], :per_page => 20, :order => 'completed_at DESC', :include => Todo::DEFAULT_INCLUDES + @done = current_user.todos.completed.with_tag(@tag.id).paginate :page => params[:page], :per_page => 20, :order => 'completed_at DESC', :include => Todo::DEFAULT_INCLUDES @count = @done.size render :template => 'todos/all_done' end @@ -1029,7 +1029,11 @@ class TodosController < ApplicationController def find_todos_with_tag_expr(tag_expr) # optimize for the common case: selecting only one tag - return current_user.todos.with_tag(@tag_name) if @single_tag + if @single_tag + tag = Tag.find_by_name(@tag_name) + tag_id = tag.nil? ? -1 : tag.id + return current_user.todos.with_tag(tag_id) + end tag_ids = get_ids_from_tag_expr(tag_expr) todos = current_user.todos @@ -1070,7 +1074,7 @@ class TodosController < ApplicationController if @tag.nil? @tag = Tag.new(:name => @tag_name) end - @down_count = current_user.todos.with_tag(@tag).active.not_hidden.count + @down_count = current_user.todos.with_tag(@tag.id).active.not_hidden.count end end end @@ -1087,10 +1091,10 @@ class TodosController < ApplicationController if tag.nil? tag = Tag.new(:name => params['tag']) end - @remaining_deferred_or_pending_count = current_user.todos.with_tag(tag).deferred_or_blocked.count - @remaining_in_context = current_user.contexts.find(context_id).todos.active.not_hidden.with_tag(tag).count - @target_context_count = current_user.contexts.find(@todo.context_id).todos.active.not_hidden.with_tag(tag).count - @remaining_hidden_count = current_user.todos.hidden.with_tag(tag).count + @remaining_deferred_or_pending_count = current_user.todos.with_tag(tag.id).deferred_or_blocked.count + @remaining_in_context = current_user.contexts.find(context_id).todos.active.not_hidden.with_tag(tag.id).count + @target_context_count = current_user.contexts.find(@todo.context_id).todos.active.not_hidden.with_tag(tag.id).count + @remaining_hidden_count = current_user.todos.hidden.with_tag(tag.id).count } from.project { project_id = @project_changed ? @original_item_project_id : @todo.project_id @@ -1141,7 +1145,7 @@ class TodosController < ApplicationController end end from.tag do - @completed_count = current_user.todos.with_tag(@tag).completed.count + @completed_count = current_user.todos.with_tag(@tag.id).completed.count end end end @@ -1149,7 +1153,7 @@ class TodosController < ApplicationController def determine_deferred_tag_count(tag_name) tag = Tag.find_by_name(tag_name) # tag.nil? should normally not happen, but is a workaround for #929 - @remaining_deferred_or_pending_count = tag.nil? ? 0 : current_user.todos.deferred.with_tag(tag).count + @remaining_deferred_or_pending_count = tag.nil? ? 0 : current_user.todos.deferred.with_tag(tag.id).count end def render_todos_html diff --git a/app/models/todo.rb b/app/models/todo.rb index 45253ab3..06b23699 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -38,7 +38,7 @@ class Todo < ActiveRecord::Base # other scopes named_scope :are_due, :conditions => ['NOT (todos.due IS NULL)'] - named_scope :with_tag, lambda { |tag| {:joins => :taggings, :conditions => ["taggings.tag_id = ? ", tag.id] } } + named_scope :with_tag, lambda { |tag_id| {:joins => :taggings, :conditions => ["taggings.tag_id = ? ", tag_id] } } named_scope :with_tags, lambda { |tag_ids| {:conditions => ["EXISTS(SELECT * from taggings t WHERE t.tag_id IN (?) AND t.taggable_id=todos.id AND t.taggable_type='Todo')", tag_ids] } } named_scope :of_user, lambda { |user_id| {:conditions => ["todos.user_id = ? ", user_id] } } named_scope :completed_after, lambda { |date| {:conditions => ["todos.completed_at > ? ", date] } } diff --git a/config/routes.rb b/config/routes.rb index 533ca270..ec0ed738 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -40,7 +40,7 @@ ActionController::Routing::Routes.draw do |map| # UPDATE: added support for mobile view. All tags ending on .m will be # routed to mobile view of tags. todos.mobile_tag 'todos/tag/:name.m', :action => "tag", :format => 'm' - todos.mobile_tag 'todos/tag/:name.txt', :action => "tag", :format => 'txt' + todos.text_tag 'todos/tag/:name.txt', :action => "tag", :format => 'txt' todos.tag 'todos/tag/:name', :action => "tag", :name => /.*/ todos.done_tag 'todos/done/tag/:name', :action => "done_tag" todos.all_done_tag 'todos/all_done/tag/:name', :action => "all_done_tag" diff --git a/features/shared_add_new_todo.feature b/features/shared_add_new_todo.feature index 11f1cc65..8e48d567 100644 --- a/features/shared_add_new_todo.feature +++ b/features/shared_add_new_todo.feature @@ -95,7 +95,7 @@ Feature: Add new next action from every page | tickler page | not see | | "test project" project | see | | context page for "test context" | see | - | tag page for "starred" | not see | + | tag page for "starred" | see | @selenium Scenario Outline: I can add multiple todos from several pages diff --git a/test/functional/todos_controller_test.rb b/test/functional/todos_controller_test.rb index 20b3c7a5..e8003eac 100644 --- a/test/functional/todos_controller_test.rb +++ b/test/functional/todos_controller_test.rb @@ -492,7 +492,7 @@ class TodosControllerTest < ActionController::TestCase recurring_todo_1 = RecurringTodo.find(1) set_user_to_current_time_zone(recurring_todo_1.user) todo_1 = Todo.find_by_recurring_todo_id(1) - today = Time.now.at_midnight + today = Time.zone.now.at_midnight # change recurrence pattern to monthly and set show_from to today recurring_todo_1.target = 'show_from_date' @@ -650,6 +650,7 @@ class TodosControllerTest < ActionController::TestCase get :tag, :name => "single" assert_equal true, assigns['single_tag'], "should recognize it is a single tag name" assert_equal "single", assigns['tag_expr'][0][0], "should store the single tag" + assert_equal "single", assigns['tag_name'], "should store the single tag name" end def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags