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/.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 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/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index b626653f..61aee981 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 @@ -586,34 +586,34 @@ class TodosController < ApplicationController # /todos/tag/[tag_name] shows all the actions tagged with tag_name def tag - init_data_for_sidebar unless mobile? + get_params_for_tag_view + @page_title = t('todos.tagged_page_title', :tag_name => @tag_title) @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) - # mobile tags are routed with :name ending on .m. So we need to chomp it - @tag_name = @tag_name.chomp('.m') if mobile? + 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? + todos_with_tag_ids = find_todos_with_tag_expr(@tag_expr) - @not_done_todos = current_user.todos.with_tag(@tag).active.not_hidden.find(:all, + @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, - :order => 'show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES) - @pending = current_user.todos.with_tag(@tag).blocked.find(:all, - :order => 'show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES) + @deferred = todos_with_tag_ids.deferred.find(:all, + :order => 'todos.show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES) + @pending = todos_with_tag_ids.blocked.find(:all, + :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 @@ -635,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 @@ -645,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) @@ -662,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 @@ -988,6 +991,58 @@ 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) + # optimize for the common case: selecting only one 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 + 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 @@ -1019,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 @@ -1036,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 @@ -1090,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 @@ -1098,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 ea6e17c9..06b23699 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -28,17 +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_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] } } named_scope :completed_before, lambda { |date| {:conditions => ["todos.completed_at < ? ", date] } } diff --git a/app/views/layouts/standard.html.erb b/app/views/layouts/standard.html.erb index f47cfa96..611ccdc7 100644 --- a/app/views/layouts/standard.html.erb +++ b/app/views/layouts/standard.html.erb @@ -38,7 +38,10 @@
+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