diff --git a/tracks/app/controllers/contexts_controller.rb b/tracks/app/controllers/contexts_controller.rb
index 9ee95aaf..efb3e297 100644
--- a/tracks/app/controllers/contexts_controller.rb
+++ b/tracks/app/controllers/contexts_controller.rb
@@ -4,12 +4,29 @@ class ContextsController < ApplicationController
before_filter :init, :except => [:create, :destroy, :order]
before_filter :init_todos, :only => :show
+ 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]) }
def index
- @page_title = "TRACKS::List Contexts"
- respond_to do |wants|
- wants.html
- wants.xml { render :xml => @contexts.to_xml( :except => :user_id ) }
+ respond_to do |format|
+ format.html do
+ @page_title = "TRACKS::List Contexts"
+ render
+ end
+ format.xml { render :xml => @contexts.to_xml( :except => :user_id ) }
+ format.rss do
+ render_rss_feed_for @contexts, :feed => Context.feed_options(@user),
+ :item => { :description => lambda { |c| c.summary(count_undone_todos(c)) } }
+ end
+ format.atom do
+ render_atom_feed_for @contexts, :feed => Context.feed_options(@user),
+ :item => { :description => lambda { |c| c.summary(count_undone_todos(c)) },
+ :author => lambda { |c| nil } }
+ end
+ format.text do
+ render :action => 'index_text', :layout => false, :content_type => Mime::TEXT
+ end
end
end
diff --git a/tracks/app/controllers/feed_controller.rb b/tracks/app/controllers/feed_controller.rb
index 33f6f9f4..7c977d2f 100644
--- a/tracks/app/controllers/feed_controller.rb
+++ b/tracks/app/controllers/feed_controller.rb
@@ -45,14 +45,7 @@ class FeedController < ApplicationController
end
headers["Content-Type"] = "text/calendar"
end
-
- def list_contexts_only
- init_not_done_counts('context')
- @contexts = @user.contexts
- @description = "Lists all the contexts for #{@user.login}."
- render :action => 'contexts_' + params['feedtype']
- end
-
+
protected
# Check whether the token in the URL matches the word in the User's table
diff --git a/tracks/app/helpers/feed_helper.rb b/tracks/app/helpers/feed_helper.rb
index c38bc212..18cb2791 100644
--- a/tracks/app/helpers/feed_helper.rb
+++ b/tracks/app/helpers/feed_helper.rb
@@ -30,19 +30,7 @@ module FeedHelper
end
return result_string
end
-
- def build_contexts_text_page(contexts)
- result_string = ""
- contexts.each do |c|
- result_string << "\n" + c.name.upcase + "\n"
- result_string << "#{count_undone_todos(c)}. Context is #{c.hidden? ? 'Hidden' : 'Active'}.\n"
- result_string << "\n"
- end
-
- return result_string
- end
-
def format_ical_notes(notes)
split_notes = notes.split(/\n/)
joined_notes = split_notes.join("\\n")
diff --git a/tracks/app/models/context.rb b/tracks/app/models/context.rb
index 041abb11..2a0f5538 100644
--- a/tracks/app/models/context.rb
+++ b/tracks/app/models/context.rb
@@ -16,6 +16,13 @@ class Context < ActiveRecord::Base
validates_does_not_contain :name, :string => '/', :message => "cannot contain the slash ('/') character"
validates_does_not_contain :name, :string => ',', :message => "cannot contain the comma (',') character"
+ def self.feed_options(user)
+ {
+ :title => 'Tracks Contexts',
+ :description => "Lists all the contexts for #{user.display_name}."
+ }
+ end
+
def hidden?
self.hide == true
end
@@ -23,5 +30,16 @@ class Context < ActiveRecord::Base
def to_param
url_friendly_name
end
-
+
+ def title
+ name
+ end
+
+ def summary(undone_todo_count)
+ s = "
#{undone_todo_count}. "
+ s += "Context is #{hidden? ? 'Hidden' : 'Active'}. "
+ s += "
"
+ s
+ end
+
end
diff --git a/tracks/app/views/contexts/index_text.rhtml b/tracks/app/views/contexts/index_text.rhtml
new file mode 100644
index 00000000..329e8a15
--- /dev/null
+++ b/tracks/app/views/contexts/index_text.rhtml
@@ -0,0 +1,5 @@
+<% @contexts.each do |c| -%>
+
+<%= c.name.upcase %>
+<%= count_undone_todos(c)%>. Context is <%= c.hidden? ? "Hidden" : "Active" %>.
+<% end -%>
\ No newline at end of file
diff --git a/tracks/app/views/feed/contexts_text.rhtml b/tracks/app/views/feed/contexts_text.rhtml
deleted file mode 100644
index d3496d05..00000000
--- a/tracks/app/views/feed/contexts_text.rhtml
+++ /dev/null
@@ -1,2 +0,0 @@
-<% headers["Content-Type"] = "text/plain; charset=utf-8" -%>
-<%= build_contexts_text_page( @contexts ) -%>
diff --git a/tracks/app/views/feedlist/index.rhtml b/tracks/app/views/feedlist/index.rhtml
index 37563476..7954bbf1 100644
--- a/tracks/app/views/feedlist/index.rhtml
+++ b/tracks/app/views/feedlist/index.rhtml
@@ -40,8 +40,8 @@
Actions completed in the last 7 days
- <%= rss_feed_link({ :action => 'list_contexts_only', :feedtype => 'rss' }) %>
- <%= text_feed_link({ :action => 'list_contexts_only', :feedtype => 'text' }) %>
+ <%= rss_formatted_link({:controller => 'contexts', :action => 'index'}) %>
+ <%= text_formatted_link({:controller => 'contexts', :action => 'index'}) %>
All Contexts
diff --git a/tracks/config/routes.rb b/tracks/config/routes.rb
index 12908d19..83a2e19d 100644
--- a/tracks/config/routes.rb
+++ b/tracks/config/routes.rb
@@ -31,7 +31,6 @@ ActionController::Routing::Routes.draw do |map|
# Context Routes
map.resources :contexts, :collection => {:order => :post}
map.connect 'context/:context/feed/:action/:login/:token', :controller => 'feed'
- map.connect 'contexts/feed/:feedtype/:login/:token', :controller => 'feed', :action => 'list_contexts_only'
# Projects Routes
map.resources :projects, :collection => {:order => :post}
diff --git a/tracks/db/migrate/027_add_context_timestamps.rb b/tracks/db/migrate/027_add_context_timestamps.rb
new file mode 100644
index 00000000..8c2d8201
--- /dev/null
+++ b/tracks/db/migrate/027_add_context_timestamps.rb
@@ -0,0 +1,13 @@
+class AddContextTimestamps < ActiveRecord::Migration
+
+ def self.up
+ add_column :contexts, :created_at, :timestamp
+ add_column :contexts, :updated_at, :timestamp
+ end
+
+ def self.down
+ remove_column :contexts, :created_at
+ remove_column :contexts, :updated_at
+ end
+
+end
diff --git a/tracks/db/schema.rb b/tracks/db/schema.rb
index 388a2b41..5afc62ac 100644
--- a/tracks/db/schema.rb
+++ b/tracks/db/schema.rb
@@ -2,13 +2,15 @@
# migrations feature of ActiveRecord to incrementally modify your database, and
# then regenerate this schema definition.
-ActiveRecord::Schema.define(:version => 26) do
+ActiveRecord::Schema.define(:version => 27) do
create_table "contexts", :force => true do |t|
- t.column "name", :string, :default => "", :null => false
- t.column "hide", :integer, :limit => 4, :default => 0, :null => false
- t.column "position", :integer, :default => 0, :null => false
- t.column "user_id", :integer, :default => 0, :null => false
+ t.column "name", :string, :default => "", :null => false
+ t.column "hide", :integer, :limit => 4, :default => 0, :null => false
+ 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"
diff --git a/tracks/test/fixtures/contexts.yml b/tracks/test/fixtures/contexts.yml
index 09cdea93..15d3af66 100644
--- a/tracks/test/fixtures/contexts.yml
+++ b/tracks/test/fixtures/contexts.yml
@@ -1,11 +1,19 @@
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+<%
+def today
+ Time.now.utc.to_s(:db)
+end
+
+%>
agenda:
id: 1
name: agenda
position: 1
hide: false
user_id: 1
+ created_at: <%= today %>
+ updated_at: <%= today %>
call:
id: 2
@@ -13,6 +21,8 @@ call:
position: 2
hide: false
user_id: 1
+ created_at: <%= today %>
+ updated_at: <%= today %>
email:
id: 3
@@ -20,6 +30,8 @@ email:
position: 3
hide: false
user_id: 1
+ created_at: <%= today %>
+ updated_at: <%= today %>
errand:
id: 4
@@ -27,6 +39,8 @@ errand:
position: 4
hide: false
user_id: 1
+ created_at: <%= today %>
+ updated_at: <%= today %>
lab:
id: 5
@@ -34,6 +48,8 @@ lab:
position: 5
hide: false
user_id: 1
+ created_at: <%= today %>
+ updated_at: <%= today %>
library:
id: 6
@@ -41,6 +57,8 @@ library:
position: 6
hide: false
user_id: 1
+ created_at: <%= today %>
+ updated_at: <%= today %>
freetime:
id: 7
@@ -48,6 +66,8 @@ freetime:
position: 7
hide: false
user_id: 1
+ created_at: <%= today %>
+ updated_at: <%= today %>
office:
id: 8
@@ -55,6 +75,8 @@ office:
position: 8
hide: false
user_id: 1
+ created_at: <%= today %>
+ updated_at: <%= today %>
waitingfor:
id: 9
@@ -62,3 +84,5 @@ waitingfor:
position: 9
hide: false
user_id: 1
+ created_at: <%= today %>
+ updated_at: <%= today %>
diff --git a/tracks/test/functional/contexts_controller_test.rb b/tracks/test/functional/contexts_controller_test.rb
index 40724f0c..8804be75 100644
--- a/tracks/test/functional/contexts_controller_test.rb
+++ b/tracks/test/functional/contexts_controller_test.rb
@@ -39,5 +39,107 @@ class ContextsControllerTest < TodoContainerControllerTestBase
assert_rjs :show, 'status'
assert_rjs :update, 'status', "1 error prohibited this record from being saved
There were problems with the following fields:
Name cannot contain the slash ('/') character
"
end
+
+ def test_rss_feed_content
+ @request.session['user_id'] = users(:admin_user).id
+ get :index, { :format => "rss" }
+ assert_equal 'application/rss+xml; charset=utf-8', @response.headers["Content-Type"]
+ #puts @response.body
+
+ assert_xml_select 'rss[version="2.0"]' do
+ assert_xml_select 'channel' do
+ assert_xml_select '>title', 'Tracks Contexts'
+ assert_xml_select '>description', "Lists all the contexts for #{users(:admin_user).display_name}."
+ assert_xml_select 'language', 'en-us'
+ assert_xml_select 'ttl', '40'
+ end
+ assert_xml_select 'item', 9 do
+ assert_xml_select 'title', /.+/
+ assert_xml_select 'description', /<p>\d+ actions. Context is (active|hidden). <\/p>/
+ %w(guid link).each do |node|
+ assert_xml_select node, /http:\/\/test.host\/contexts\/.+/
+ end
+ assert_xml_select 'pubDate', contexts(:agenda).created_at.to_s(:rfc822)
+ end
+ end
+ end
+
+ def test_rss_feed_not_accessible_to_anonymous_user_without_token
+ @request.session['user_id'] = nil
+ get :index, { :format => "rss" }
+ assert_response 401
+ end
+
+ def test_rss_feed_not_accessible_to_anonymous_user_with_invalid_token
+ @request.session['user_id'] = nil
+ get :index, { :format => "rss", :token => 'foo' }
+ assert_response 401
+ end
+
+ def test_rss_feed_accessible_to_anonymous_user_with_valid_token
+ @request.session['user_id'] = nil
+ get :index, { :format => "rss", :token => users(:admin_user).word }
+ assert_response :ok
+ end
+
+ def test_atom_feed_content
+ @request.session['user_id'] = users(:admin_user).id
+ get :index, { :format => "atom" }
+ assert_equal 'application/atom+xml; charset=utf-8', @response.headers["Content-Type"]
+ #puts @response.body
+
+ assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
+ assert_xml_select '>title', 'Tracks Contexts'
+ assert_xml_select '>subtitle', "Lists all the contexts for #{users(:admin_user).display_name}."
+ assert_xml_select 'entry', 3 do
+ assert_xml_select 'title', /.+/
+ assert_xml_select 'content[type="html"]', /<p>\d+ actions. Context is (active|hidden). <\/p>/
+ assert_xml_select 'published', contexts(:agenda).created_at.to_s(:rfc822)
+ end
+ end
+ end
+
+ def test_atom_feed_not_accessible_to_anonymous_user_without_token
+ @request.session['user_id'] = nil
+ get :index, { :format => "atom" }
+ assert_response 401
+ end
+
+ def test_atom_feed_not_accessible_to_anonymous_user_with_invalid_token
+ @request.session['user_id'] = nil
+ get :index, { :format => "atom", :token => 'foo' }
+ assert_response 401
+ end
+
+ def test_atom_feed_accessible_to_anonymous_user_with_valid_token
+ @request.session['user_id'] = nil
+ get :index, { :format => "atom", :token => users(:admin_user).word }
+ assert_response :ok
+ end
+
+ def test_text_feed_content
+ @request.session['user_id'] = users(:admin_user).id
+ get :index, { :format => "txt" }
+ assert_equal 'text/plain; charset=utf-8', @response.headers["Content-Type"]
+ #puts @response.body
+ end
+
+ def test_text_feed_not_accessible_to_anonymous_user_without_token
+ @request.session['user_id'] = nil
+ get :index, { :format => "txt" }
+ assert_response 401
+ end
+
+ def test_text_feed_not_accessible_to_anonymous_user_with_invalid_token
+ @request.session['user_id'] = nil
+ get :index, { :format => "txt", :token => 'foo' }
+ assert_response 401
+ end
+
+ def test_text_feed_accessible_to_anonymous_user_with_valid_token
+ @request.session['user_id'] = nil
+ get :index, { :format => "txt", :token => users(:admin_user).word }
+ assert_response :ok
+ end
end
diff --git a/tracks/test/integration/context_xml_api_test.rb b/tracks/test/integration/context_xml_api_test.rb
index 685350e8..12cd23ab 100644
--- a/tracks/test/integration/context_xml_api_test.rb
+++ b/tracks/test/integration/context_xml_api_test.rb
@@ -4,7 +4,7 @@ require 'contexts_controller'
# Re-raise errors caught by the controller.
class ContextsController; def rescue_action(e) raise e end; end
-class ContextsControllerXmlApiTest < ActionController::IntegrationTest
+class ContextXmlApiTest < ActionController::IntegrationTest
fixtures :users, :contexts
@@context_name = "@newcontext"
@@ -51,7 +51,7 @@ class ContextsControllerXmlApiTest < ActionController::IntegrationTest
def test_creates_new_context
initial_count = Context.count
authenticated_post_xml_to_context_create
- assert_response_and_body_matches 200, %r|^<\?xml version="1.0" encoding="UTF-8"\?>\n\n 0\n \d+\n #{@@context_name}\n 1\n\n$|
+ assert_response_and_body_matches 200, %r|^<\?xml version="1.0" encoding="UTF-8"\?>\n\n \d{4}+-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\n 0\n \d+\n #{@@context_name}\n 1\n \d{4}+-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\n\n$|
assert_equal initial_count + 1, Context.count
context1 = Context.find_by_name(@@context_name)
assert_not_nil context1, "expected context '#{@@context_name}' to be created"
diff --git a/tracks/test/integration/feed_smoke_test.rb b/tracks/test/integration/feed_smoke_test.rb
index 438cd7f7..864bc841 100644
--- a/tracks/test/integration/feed_smoke_test.rb
+++ b/tracks/test/integration/feed_smoke_test.rb
@@ -72,11 +72,11 @@ class FeedSmokeTest < ActionController::IntegrationTest
end
def test_all_contexts_rss
- assert_success "/contexts/feed/rss/admin/#{ users(:admin_user).word }"
+ assert_success "/contexts.rss?token=#{ users(:admin_user).word }"
end
def test_all_contexts_txt
- assert_success "/contexts/feed/text/admin/#{ users(:admin_user).word }"
+ assert_success "/contexts.txt?token=#{ users(:admin_user).word }"
end
def test_all_projects_rss
diff --git a/tracks/test/unit/context_test.rb b/tracks/test/unit/context_test.rb
index a489a90b..3afdbe89 100644
--- a/tracks/test/unit/context_test.rb
+++ b/tracks/test/unit/context_test.rb
@@ -107,6 +107,9 @@ class ContextTest < Test::Unit::TestCase
def test_to_param_returns_url_friendly_name
assert_equal 'agenda', @agenda.to_param
end
-
+
+ def test_title_reader_returns_name
+ assert_equal @agenda.name, @agenda.title
+ end
end