diff --git a/tracks/app/controllers/application.rb b/tracks/app/controllers/application.rb index 6b42a528..52d4b533 100644 --- a/tracks/app/controllers/application.rb +++ b/tracks/app/controllers/application.rb @@ -111,6 +111,10 @@ class ApplicationController < ActionController::Base def markdown(text) RedCloth.new(text).to_html end + + def build_default_project_context_name_map(projects) + Hash[*projects.reject{ |p| p.default_context.nil? }.map{ |p| [p.name, p.default_context.name] }.flatten].to_json + end protected diff --git a/tracks/app/controllers/contexts_controller.rb b/tracks/app/controllers/contexts_controller.rb index bbdfc5da..cc5464fc 100644 --- a/tracks/app/controllers/contexts_controller.rb +++ b/tracks/app/controllers/contexts_controller.rb @@ -162,6 +162,7 @@ class ContextsController < ApplicationController # Hides actions in hidden projects from context. @not_done_todos = @context.todos.find(:all, :conditions => ['todos.state = ?', 'active'], :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC", :include => :project) @count = @not_done_todos.size + @default_project_context_name_map = build_default_project_context_name_map(@projects).to_json end end diff --git a/tracks/app/controllers/projects_controller.rb b/tracks/app/controllers/projects_controller.rb index 5ed1149c..4fd22e59 100644 --- a/tracks/app/controllers/projects_controller.rb +++ b/tracks/app/controllers/projects_controller.rb @@ -3,6 +3,7 @@ class ProjectsController < ApplicationController helper :application, :todos, :notes before_filter :init, :except => [:create, :destroy, :order] before_filter :check_user_set_project, :only => [:update, :destroy, :show] + before_filter :default_context_filter, :only => [:create,:update] skip_before_filter :login_required, :only => [:index] prepend_before_filter :login_or_feed_token_required, :only => [:index] session :off, :only => :index, :if => Proc.new { |req| ['rss','atom','txt'].include?(req.parameters[:format]) } @@ -28,6 +29,7 @@ class ProjectsController < ApplicationController @count = @not_done.size @next_project = @user.projects.next_from(@project) @previous_project = @user.projects.previous_from(@project) + @default_project_context_name_map = build_default_project_context_name_map(@projects).to_json end # Example XML usage: curl -H 'Accept: application/xml' -H 'Content-Type: application/xml' @@ -50,6 +52,7 @@ class ProjectsController < ApplicationController @saved = @project.save @project_not_done_counts = { @project.id => 0 } @active_projects_count = @user.projects.count(:conditions => "state = 'active'") + @contexts = @user.contexts respond_to do |wants| wants.js wants.xml do @@ -93,6 +96,8 @@ class ProjectsController < ApplicationController render elsif boolean_param('update_status') render :action => 'update_status' + elsif boolean_param('update_default_context') + render :action => 'update_default_context' else render :text => success_text || 'Success' end @@ -181,6 +186,19 @@ class ProjectsController < ApplicationController @done = @user.todos.find_in_state(:all, :completed, :order => "completed_at DESC") init_data_for_sidebar end + + def default_context_filter + p = params['project'] + p = params['request']['project'] if p.nil? && params['request'] + p = {} if p.nil? + default_context_name = p['default_context_name'] + p.delete('default_context_name') + + unless default_context_name.blank? + default_context = Context.find_or_create_by_name(default_context_name) + p['default_context_id'] = default_context.id + end + end def summary(project) project_description = '' diff --git a/tracks/app/controllers/todos_controller.rb b/tracks/app/controllers/todos_controller.rb index e837418d..391b7dec 100644 --- a/tracks/app/controllers/todos_controller.rb +++ b/tracks/app/controllers/todos_controller.rb @@ -224,6 +224,7 @@ class TodosController < ApplicationController @page_title = "TRACKS::Tickler" @tickles = @user.deferred_todos @count = @tickles.size + @default_project_context_name_map = build_default_project_context_name_map(@projects).to_json end # Check for any due tickler items, activate them @@ -262,6 +263,7 @@ class TodosController < ApplicationController @done = @user.completed_todos.find(:all, :limit => max_completed, :include => [ :context, :project, :tags ]) unless max_completed == 0 # Set count badge to number of items with this tag @not_done_todos.empty? ? @count = 0 : @count = @not_done_todos.size + @default_project_context_name_map = build_default_project_context_name_map(@projects).to_json end @@ -421,7 +423,9 @@ class TodosController < ApplicationController # Set count badge to number of not-done, not hidden context items @count = @todos.reject { |x| !x.active? || x.context.hide? }.size - + + @default_project_context_name_map = build_default_project_context_name_map(@projects).to_json + render end end diff --git a/tracks/app/helpers/projects_helper.rb b/tracks/app/helpers/projects_helper.rb index 55360243..09be149f 100644 --- a/tracks/app/helpers/projects_helper.rb +++ b/tracks/app/helpers/projects_helper.rb @@ -23,7 +23,7 @@ module ProjectsHelper project_name = truncate(@previous_project.name, 40, "...") html << link_to_project(@previous_project, "« #{project_name}") end - html << '|' if @previous_project && @next_project + html << ' | ' if @previous_project && @next_project unless @next_project.nil? project_name = truncate(@next_project.name, 40, "...") html << link_to_project(@next_project, "#{project_name} »") diff --git a/tracks/app/models/context.rb b/tracks/app/models/context.rb index 25012d35..22912120 100644 --- a/tracks/app/models/context.rb +++ b/tracks/app/models/context.rb @@ -22,6 +22,10 @@ class Context < ActiveRecord::Base :description => "Lists all the contexts for #{user.display_name}" } end + + def self.null_object + NullContext.new + end def hidden? self.hide == true || self.hide == 1 @@ -43,3 +47,19 @@ class Context < ActiveRecord::Base end end + +class NullContext + + def nil? + true + end + + def id + nil + end + + def name + '' + end + +end \ No newline at end of file diff --git a/tracks/app/models/project.rb b/tracks/app/models/project.rb index 46519b37..2d91a940 100644 --- a/tracks/app/models/project.rb +++ b/tracks/app/models/project.rb @@ -1,6 +1,7 @@ class Project < ActiveRecord::Base has_many :todos, :dependent => :delete_all, :include => :context has_many :notes, :dependent => :delete_all, :order => "created_at DESC" + belongs_to :default_context, :dependent => :nullify, :class_name => "Context", :foreign_key => "default_context_id" belongs_to :user validates_presence_of :name, :message => "project must have a name" @@ -65,7 +66,13 @@ class Project < ActiveRecord::Base end end end + + alias_method :original_default_context, :default_context + def default_context + original_default_context.nil? ? Context.null_object : original_default_context + end + # would prefer to call this method state=(), but that causes an endless loop # as a result of acts_as_state_machine calling state=() to update the attribute def transition_to(candidate_state) @@ -96,5 +103,5 @@ class NullProject def id nil end - + end \ No newline at end of file diff --git a/tracks/app/views/projects/_default_context_autocomplete.rhtml b/tracks/app/views/projects/_default_context_autocomplete.rhtml new file mode 100644 index 00000000..65977467 --- /dev/null +++ b/tracks/app/views/projects/_default_context_autocomplete.rhtml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/tracks/app/views/projects/_project_form.rhtml b/tracks/app/views/projects/_project_form.rhtml index f3917ee3..b783aa28 100644 --- a/tracks/app/views/projects/_project_form.rhtml +++ b/tracks/app/views/projects/_project_form.rhtml @@ -17,3 +17,10 @@ <% end %> + + + + <%= text_field_tag("project[default_context_name]", @project.default_context.name, {:tabindex=>1,:size=> 25}) %> + <%= render :partial => 'default_context_autocomplete' %> + + \ No newline at end of file diff --git a/tracks/app/views/projects/_project_listing.rhtml b/tracks/app/views/projects/_project_listing.rhtml index 7a68955e..a2d99242 100644 --- a/tracks/app/views/projects/_project_listing.rhtml +++ b/tracks/app/views/projects/_project_listing.rhtml @@ -1,6 +1,8 @@ -<% project = project_listing %> -
" class="list"> -
+<% project = project_listing + @project_listing_zindex = @project_listing_zindex.nil? ? 200 : @project_listing_zindex - 1 +-%> +
" class="list" style="z-index:<%= @project_listing_zindex %>"> +
DRAG
diff --git a/tracks/app/views/projects/index.rhtml b/tracks/app/views/projects/index.rhtml index 5cf820d4..3ccebf1c 100644 --- a/tracks/app/views/projects/index.rhtml +++ b/tracks/app/views/projects/index.rhtml @@ -21,14 +21,19 @@
<%= error_messages_for('project') %>

- <%= text_field 'project', 'name' %>
+ <%= text_field 'project', 'name', "tabindex" => 1 %>

- <%= text_area 'project', 'description', "cols" => 30, "rows" => 4 %>
- - <%= check_box_tag 'go_to_project' %>
+ <%= text_area 'project', 'description', "cols" => 30, "rows" => 4, "tabindex" => 2 %>
- +
+ <%= text_field_tag("project[default_context_name]", @project.default_context.name, :tabindex => 3) %> + <%= render :partial => 'default_context_autocomplete' %> +
+ +
+ +
<% end -%>
diff --git a/tracks/app/views/projects/show.rhtml b/tracks/app/views/projects/show.rhtml index a66602c6..085a5716 100644 --- a/tracks/app/views/projects/show.rhtml +++ b/tracks/app/views/projects/show.rhtml @@ -18,21 +18,21 @@ <%= render :partial => "notes/notes_summary", :collection => @project.notes %>
-
-
@@ -49,6 +49,24 @@
+ +
+
+

Default Context

+
+ <% form_remote_tag( :url => project_path(@project), :method => :put, + :html=> { :id => 'set-default-context-action', + :name => 'default_context', + :class => 'inline-form' }) do -%> + <%= hidden_field_tag("update_default_context", true) %> + <%= text_field_tag("project[default_context_name]", + @project.default_context.name, + { :tabindex => 9,:size => 25 }) %> + <%= submit_tag "Set Default Context for this Project", { :tabindex => 10 } %> + <%= render :partial => 'default_context_autocomplete' %> + <% end -%> +
+
diff --git a/tracks/app/views/projects/update_default_context.rjs b/tracks/app/views/projects/update_default_context.rjs new file mode 100644 index 00000000..19dc57fc --- /dev/null +++ b/tracks/app/views/projects/update_default_context.rjs @@ -0,0 +1,10 @@ +if @project.default_context.nil? + page.notify :notice, "Removed default context", 5.0 +else + if source_view_is :project + page['todo_context_name'].value = @project.default_context.name + end + page.notify :notice, "Set project's default context to #{@project.default_context.name}", 5.0 +end +page.hide "busy" + diff --git a/tracks/app/views/projects/update_status.rjs b/tracks/app/views/projects/update_status.rjs index 28e04a1f..39100e58 100644 --- a/tracks/app/views/projects/update_status.rjs +++ b/tracks/app/views/projects/update_status.rjs @@ -8,3 +8,4 @@ page.select('#project_status .completed span').each do |element| element.className = @project.current_state == :completed ? 'active_state' : 'inactive_state' end page.notify :notice, "Set project status to #{@project.current_state}", 5.0 +page.hide 'busy' diff --git a/tracks/app/views/shared/_add_new_item_form.rhtml b/tracks/app/views/shared/_add_new_item_form.rhtml index 5a561a50..c3f151b1 100644 --- a/tracks/app/views/shared/_add_new_item_form.rhtml +++ b/tracks/app/views/shared/_add_new_item_form.rhtml @@ -1,6 +1,7 @@ <% @todo = nil @initial_context_name = @context.name unless @context.nil? + @initial_context_name ||= @project.default_context.name unless @project.nil? || @project.default_context.nil? @initial_context_name ||= @contexts[0].name unless @contexts[0].nil? @initial_project_name = @project.name unless @project.nil? %> @@ -24,24 +25,34 @@ <%= text_area( "todo", "notes", "cols" => 25, "rows" => 6, "tabindex" => 2) %> - - - - - - - + + + + + + + diff --git a/tracks/app/views/todos/tickler.rhtml b/tracks/app/views/todos/tickler.rhtml deleted file mode 100644 index 32086a0e..00000000 --- a/tracks/app/views/todos/tickler.rhtml +++ /dev/null @@ -1,21 +0,0 @@ -
- -
-

Deferred actions

- -
-
-

Currently there are no deferred actions

-
- - <%= render :partial => "todos/todo", :collection => @tickles, :locals => { :parent_container_type => 'tickler' } %> - -
-
- -
- -
- <%= render :partial => "shared/add_new_item_form" %> - <%= render "sidebar/sidebar" %> -
\ No newline at end of file diff --git a/tracks/db/migrate/031_add_default_context_to_project.rb b/tracks/db/migrate/031_add_default_context_to_project.rb new file mode 100644 index 00000000..e51b26b1 --- /dev/null +++ b/tracks/db/migrate/031_add_default_context_to_project.rb @@ -0,0 +1,9 @@ +class AddDefaultContextToProject < ActiveRecord::Migration + def self.up + add_column :projects, :default_context_id, :integer + end + + def self.down + remove_column :projects, :default_context_id, :integer + end +end diff --git a/tracks/db/schema.rb b/tracks/db/schema.rb index 30fe9b8b..e755499f 100644 --- a/tracks/db/schema.rb +++ b/tracks/db/schema.rb @@ -2,17 +2,18 @@ # migrations feature of ActiveRecord to incrementally modify your database, and # then regenerate this schema definition. -ActiveRecord::Schema.define(:version => 30) do +ActiveRecord::Schema.define(:version => 31) do create_table "contexts", :force => true do |t| - t.column "name", :string, :default => "", :null => false - t.column "position", :integer, :default => 0, :null => false + t.column "name", :string, :default => "", :null => false t.column "hide", :boolean, :default => false - t.column "user_id", :integer, :default => 1 + t.column "position", :integer, :default => 0, :null => false + t.column "user_id", :integer, :default => 0, :null => false t.column "created_at", :datetime t.column "updated_at", :datetime end + add_index "contexts", ["user_id"], :name => "index_contexts_on_user_id" add_index "contexts", ["user_id", "name"], :name => "index_contexts_on_user_id_and_name" create_table "notes", :force => true do |t| @@ -63,15 +64,17 @@ ActiveRecord::Schema.define(:version => 30) do add_index "preferences", ["user_id"], :name => "index_preferences_on_user_id" create_table "projects", :force => true do |t| - t.column "name", :string, :default => "", :null => false - t.column "position", :integer, :default => 0, :null => false - t.column "user_id", :integer, :default => 1 - t.column "description", :text - t.column "state", :string, :limit => 20, :default => "active", :null => false - t.column "created_at", :datetime - t.column "updated_at", :datetime + t.column "name", :string, :default => "", :null => false + t.column "position", :integer, :default => 0, :null => false + t.column "user_id", :integer, :default => 0, :null => false + t.column "description", :text + t.column "state", :string, :limit => 20, :default => "active", :null => false + t.column "created_at", :datetime + t.column "updated_at", :datetime + t.column "default_context_id", :integer end + add_index "projects", ["user_id"], :name => "index_projects_on_user_id" add_index "projects", ["user_id", "name"], :name => "index_projects_on_user_id_and_name" create_table "sessions", :force => true do |t| @@ -100,16 +103,16 @@ ActiveRecord::Schema.define(:version => 30) do add_index "tags", ["name"], :name => "index_tags_on_name" create_table "todos", :force => true do |t| - t.column "context_id", :integer, :default => 0, :null => false - t.column "project_id", :integer - t.column "description", :string, :default => "", :null => false + t.column "context_id", :integer, :default => 0, :null => false + t.column "description", :string, :limit => 100, :default => "", :null => false t.column "notes", :text t.column "created_at", :datetime t.column "due", :date t.column "completed_at", :datetime - t.column "user_id", :integer, :default => 1 + t.column "project_id", :integer + t.column "user_id", :integer, :default => 0, :null => false t.column "show_from", :date - t.column "state", :string, :limit => 20, :default => "immediate", :null => false + t.column "state", :string, :limit => 20, :default => "immediate", :null => false end add_index "todos", ["user_id", "state"], :name => "index_todos_on_user_id_and_state" @@ -119,10 +122,10 @@ ActiveRecord::Schema.define(:version => 30) do add_index "todos", ["user_id", "context_id"], :name => "index_todos_on_user_id_and_context_id" create_table "users", :force => true do |t| - t.column "login", :string, :limit => 80, :default => "", :null => false - t.column "password", :string, :limit => 40, :default => "", :null => false + t.column "login", :string, :limit => 80 + t.column "password", :string, :limit => 40 t.column "word", :string - t.column "is_admin", :boolean, :default => false, :null => false + t.column "is_admin", :integer, :limit => 4, :default => 0, :null => false t.column "first_name", :string t.column "last_name", :string t.column "auth_type", :string, :default => "database", :null => false diff --git a/tracks/public/stylesheets/standard.css b/tracks/public/stylesheets/standard.css index 86c3e3ab..3728526a 100644 --- a/tracks/public/stylesheets/standard.css +++ b/tracks/public/stylesheets/standard.css @@ -313,6 +313,10 @@ div#project_status > div { #project_status .active_state { font-weight:bold; } + +div#default_context > div{ + padding:10px; +} a.footer_link {color: #cc3334; font-style: normal;} a.footer_link:hover {color: #fff; background-color: #cc3334 !important;} @@ -557,7 +561,6 @@ form { border: 1px solid #CCC; padding: 10px; margin: 0px; - width: 313px; } .inline-form { border: none; diff --git a/tracks/test/selenium/tags/find_tagged.rsel b/tracks/test/selenium/tags/find_tagged.rsel index 51603c50..1b01a05a 100644 --- a/tracks/test/selenium/tags/find_tagged.rsel +++ b/tracks/test/selenium/tags/find_tagged.rsel @@ -1,6 +1,5 @@ setup :fixtures => :all include_partial 'login/login', :username => 'admin', :password => 'abracadabra' open "/todos/tag/foo" -wait_for_element_present "xpath=//div[@id='t'] //h2" -assert_not_visible "t_empty-nd" +wait_for_element_present "xpath=//div[@id='c1'] //h2" wait_for_text 'badge_count', '2' \ No newline at end of file diff --git a/tracks/test/unit/project_test.rb b/tracks/test/unit/project_test.rb index a529c0e4..cd5bd788 100644 --- a/tracks/test/unit/project_test.rb +++ b/tracks/test/unit/project_test.rb @@ -192,5 +192,12 @@ class ProjectTest < Test::Unit::TestCase @moremoney.todos[0].complete! assert_equal 2, @moremoney.not_done_todo_count end + + def test_default_context_name + p = Project.new + assert_equal '', p.default_context.name + p.default_context = contexts(:agenda) + assert_equal 'agenda', p.default_context.name + end end