Author: assaf
Date: Tue May 20 17:32:12 2008
New Revision: 658515
URL: http://svn.apache.org/viewvc?rev=658515&view=rev
Log:
Revised iCal buildr and added line wrapping.
Tasks now show as todos in OS X iCal.
Added title and subtitle for activity feeds.
Modified:
ode/sandbox/singleshot/app/controllers/activities_controller.rb
ode/sandbox/singleshot/app/controllers/tasks_controller.rb
ode/sandbox/singleshot/app/models/stakeholder.rb
ode/sandbox/singleshot/app/views/activities/index.atom.builder
ode/sandbox/singleshot/app/views/activities/index.ics.ical
ode/sandbox/singleshot/app/views/tasks/index.ics.ical
ode/sandbox/singleshot/db/migrate/20080506015046_tasks.rb
ode/sandbox/singleshot/db/migrate/20080506015119_stakeholders.rb
ode/sandbox/singleshot/db/migrate/20080506015153_activities.rb
ode/sandbox/singleshot/db/schema.rb
ode/sandbox/singleshot/lib/extensions/ical_template.rb
ode/sandbox/singleshot/lib/tasks/populate.rake
Modified: ode/sandbox/singleshot/app/controllers/activities_controller.rb
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/controllers/activities_controller.rb?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/controllers/activities_controller.rb (original)
+++ ode/sandbox/singleshot/app/controllers/activities_controller.rb Tue May 20
17:32:12 2008
@@ -5,6 +5,7 @@
def index
@title = 'Activities'
+ @subtitle = 'Track activity in tasks you participate in or observe.'
@alternate = { Mime::ATOM=>formatted_activities_url(:format=>:atom,
:access_key=>authenticated.access_key),
Mime::ICS=>formatted_activities_url(:format=>:ics,
:access_key=>authenticated.access_key) }
@activities = Activity.for_stakeholder(authenticated)
@@ -19,7 +20,8 @@
end
def show
- @title = "Activities — [EMAIL PROTECTED]"
+ @title = "Activities - [EMAIL PROTECTED]"
+ @subtitle = "Track all activities in the task [EMAIL PROTECTED]"
@alternate = { Mime::ATOM=>formatted_activity_url(@task, :atom,
:access_key=>authenticated.access_key),
Mime::ICS=>formatted_activity_url(@task, :ics,
:access_key=>authenticated.access_key) }
@activities = @task.activities
Modified: ode/sandbox/singleshot/app/controllers/tasks_controller.rb
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/controllers/tasks_controller.rb?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/controllers/tasks_controller.rb (original)
+++ ode/sandbox/singleshot/app/controllers/tasks_controller.rb Tue May 20
17:32:12 2008
@@ -8,6 +8,8 @@
before_filter :forbid_reserved, :except=>[:update, :destroy]
def index
+ @title = 'Tasks'
+ @subtitle = 'Tasks you are performing or can claim for your own.'
@alternate = { Mime::ATOM=>formatted_tasks_url(:format=>:atom,
:access_key=>authenticated.access_key),
Mime::ICS=>formatted_tasks_url(:format=>:ics,
:access_key=>authenticated.access_key) }
@tasks =
Task.with_stakeholders.for_stakeholder(authenticated).pending.prioritized
@@ -20,6 +22,7 @@
format.xml { render :xml=>@task }
format.json { render :json=>@task }
format.ics do
+ @title = @task.title
@tasks = [EMAIL PROTECTED]
render :action=>'index'
end
Modified: ode/sandbox/singleshot/app/models/stakeholder.rb
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/models/stakeholder.rb?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/models/stakeholder.rb (original)
+++ ode/sandbox/singleshot/app/models/stakeholder.rb Tue May 20 17:32:12 2008
@@ -7,8 +7,7 @@
# task_id :integer not null
# person_id :integer not null
# role :string(255) not null
-# created_at :datetime
-# updated_at :datetime
+# created_at :datetime not null
#
# Represents a stakeholder in the task. Identifies the person and their role.
Modified: ode/sandbox/singleshot/app/views/activities/index.atom.builder
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/views/activities/index.atom.builder?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/views/activities/index.atom.builder (original)
+++ ode/sandbox/singleshot/app/views/activities/index.atom.builder Tue May 20
17:32:12 2008
@@ -1,5 +1,6 @@
atom_feed :root_url=>activities_url do |feed|
- feed.title 'Singleshot: Activities'
+ feed.title @title
+ feed.subtitle @subtitle
feed.updated @activities.first.created_at
for activity in @activities
Modified: ode/sandbox/singleshot/app/views/activities/index.ics.ical
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/views/activities/index.ics.ical?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/views/activities/index.ics.ical (original)
+++ ode/sandbox/singleshot/app/views/activities/index.ics.ical Tue May 20
17:32:12 2008
@@ -1,6 +1,9 @@
-calendar.prodid = 'Apache Singleshot'
+calendar.prodid '-//Apache.org//Singleshot//EN'
+calendar.x_wr_calname 'Singleshot - ' + @title
+calendar.x_wr_caldesc @subtitle
for activity in @activities
- calendar.event do |event|
+ calendar.event activity do |event|
+ event.dtstart activity.created_at
event.summary "#{activity.person.fullname} #{activity.action}
#{activity.task.title}"
event.description activity.task.description
event.url task_url(activity.task)
Modified: ode/sandbox/singleshot/app/views/tasks/index.ics.ical
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/views/tasks/index.ics.ical?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/views/tasks/index.ics.ical (original)
+++ ode/sandbox/singleshot/app/views/tasks/index.ics.ical Tue May 20 17:32:12
2008
@@ -1,4 +1,6 @@
-calendar.prodid 'Apache Singleshot'
+calendar.prodid '-//Apache.org//Singleshot//EN'
+calendar.x_wr_calname 'Singleshot - ' + @title
+calendar.x_wr_caldesc @subtitle
for task in @tasks
calendar.todo task do |todo|
todo.summary task.title
Modified: ode/sandbox/singleshot/db/migrate/20080506015046_tasks.rb
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/db/migrate/20080506015046_tasks.rb?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/db/migrate/20080506015046_tasks.rb (original)
+++ ode/sandbox/singleshot/db/migrate/20080506015046_tasks.rb Tue May 20
17:32:12 2008
@@ -1,23 +1,22 @@
class Tasks < ActiveRecord::Migration
def self.up
- create_table :tasks do |t|
- t.string :title, :null=>false
- t.string :description, :null=>false
- t.integer :priority, :null=>false, :limit=>1
- t.date :due_on, :null=>true
- t.string :status, :null=>false
- t.string :frame_url, :null=>true
- t.string :outcome_url, :null=>true
- t.string :outcome_type, :null=>true
- t.string :access_key, :null=>true, :limit=>32
- t.text :data, :null=>false
- t.integer :version, :null=>false, :default=>0
+ create_table 'tasks' do |t|
+ t.string 'title', :null=>false
+ t.string 'description', :null=>false
+ t.integer 'priority', :null=>false, :limit=>1
+ t.date 'due_on', :null=>true
+ t.string 'status', :null=>false
+ t.string 'frame_url', :null=>true
+ t.string 'outcome_url', :null=>true
+ t.string 'outcome_type', :null=>true
+ t.string 'access_key', :null=>true, :limit=>32
+ t.text 'data', :null=>false
+ t.integer 'version', :null=>false, :default=>0
t.timestamps
end
- add_index :tasks, [:status, :updated_at]
end
def self.down
- drop_table :tasks
+ drop_table 'tasks'
end
end
Modified: ode/sandbox/singleshot/db/migrate/20080506015119_stakeholders.rb
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/db/migrate/20080506015119_stakeholders.rb?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/db/migrate/20080506015119_stakeholders.rb (original)
+++ ode/sandbox/singleshot/db/migrate/20080506015119_stakeholders.rb Tue May 20
17:32:12 2008
@@ -1,17 +1,14 @@
class Stakeholders < ActiveRecord::Migration
def self.up
- create_table :stakeholders do |t|
- t.integer :task_id, :null=>false
- t.integer :person_id, :null=>false
- t.string :role, :null=>false
- t.timestamps
+ create_table 'stakeholders' do |t|
+ t.integer 'task_id', :null=>false
+ t.integer 'person_id', :null=>false
+ t.string 'role', :null=>false
+ t.datetime 'created_at', :null=>false
end
- add_index :stakeholders, [:task_id, :person_id, :role], :unique=>true
- add_index :stakeholders, [:task_id, :role]
- add_index :stakeholders, [:person_id, :role]
end
def self.down
- drop_table :stakeholders
+ drop_table 'stakeholders'
end
end
Modified: ode/sandbox/singleshot/db/migrate/20080506015153_activities.rb
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/db/migrate/20080506015153_activities.rb?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/db/migrate/20080506015153_activities.rb (original)
+++ ode/sandbox/singleshot/db/migrate/20080506015153_activities.rb Tue May 20
17:32:12 2008
@@ -1,10 +1,10 @@
class Activities < ActiveRecord::Migration
def self.up
- create_table 'activities' do |t|
- t.integer 'person_id', :null=>false
- t.integer 'task_id', :null=>false
- t.string 'action', :null=>false
- t.datetime 'created_at', :null=>false
+ create_table 'activities' do |t|
+ t.integer 'person_id', :null=>false
+ t.integer 'task_id', :null=>false
+ t.string 'action', :null=>false
+ t.datetime 'created_at', :null=>false
end
end
Modified: ode/sandbox/singleshot/db/schema.rb
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/db/schema.rb?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/db/schema.rb (original)
+++ ode/sandbox/singleshot/db/schema.rb Tue May 20 17:32:12 2008
@@ -39,14 +39,9 @@
t.integer "task_id", :null => false
t.integer "person_id", :null => false
t.string "role", :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
end
- add_index "stakeholders", ["person_id", "role"], :name =>
"index_stakeholders_on_person_id_and_role"
- add_index "stakeholders", ["task_id", "role"], :name =>
"index_stakeholders_on_task_id_and_role"
- add_index "stakeholders", ["task_id", "person_id", "role"], :name =>
"index_stakeholders_on_task_id_and_person_id_and_role", :unique => true
-
create_table "tasks", :force => true do |t|
t.string "title", :null => false
t.string "description", :null => false
@@ -63,6 +58,4 @@
t.datetime "updated_at"
end
- add_index "tasks", ["status", "updated_at"], :name =>
"index_tasks_on_status_and_updated_at"
-
end
Modified: ode/sandbox/singleshot/lib/extensions/ical_template.rb
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/lib/extensions/ical_template.rb?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/lib/extensions/ical_template.rb (original)
+++ ode/sandbox/singleshot/lib/extensions/ical_template.rb Tue May 20 17:32:12
2008
@@ -1,104 +1,85 @@
module ActionView
- class ICalBuilder
+ class ICalBuilder < BlankSlate
- module Properties
- attr_accessor :properties
+ module Encoding
private
- def property(name, value)
- value = { :value=>value } unless Hash === value
- name.to_s.underscore.upcase +
- value.except(:value).map { |name, value|
";#{name.to_s.underscore}=#{stringify(value)}" }.join +
- ":#{stringify(value[:value])}"
+ def method_missing(name, *args)
+ params = args.extract_options!
+ write name, args, params
+ end
+
+ # Write a content line the specified name, value and parameters.
+ # Names are automatically converted to upper case with dash separators.
+ def write(name, value, params = {})
+ write_line name.to_s.underscore.upcase +
+ params.map { |name, value|
";#{name.to_s.underscore.upcase}=#{stringify(value)}" }.join +
+ ":#{stringify(value)}"
+ end
+
+ # Write a content line, observing the 75 character limit and unfolding
rules.
+ def write_line(line)
+ if line.size <= 75
+ @output << "#{line}\r\n"
+ else
+ @output << "#{line[0...75]}\r\n"
+ write_line " #{line[75..-1]}"
+ end
end
+ # Convert Ruby value into most appropriate iCal representation.
def stringify(value)
case value
- when String then value
+ when Array then value.map { |item| stringify(item) }.join(',')
when Date then value.strftime('%Y%m%d')
when Time then value.strftime(value.utc? ? '%Y%m%dT%H%M%SZ' :
'%Y%m%dT%H%M%S')
else value.to_s
end
end
- def method_missing(name, *args)
- options = args.extract_options!
- options[:value] = args.first
- @properties[name] = options
- end
end
- class Component
- include Properties
-
- def initialize(request, record = nil)
- @properties = {}
- if record
- uid "#{request.host}:#{record.class}/#{record.id}"
- dtstamp record.created_at.utc.strftime('%Y%m%dT%H%M%SZ') if
record.respond_to?(:created_at)
- last_modified record.updated_at.utc.strftime('%Y%m%dT%H%M%SZ') if
record.respond_to?(:updated_at)
- sequence record.send(record.class.locking_column) if
record.locking_enabled?
- end
- end
-
- def to_ical
- # TODO: escaping for values
- # TODO: break up long lines
- # TODO: all other conformance requirements
- properties = @properties.map { |name, value| property(name, value) }
- ["BEGIN:#{self.class.const_get :NAME}", properties,
"END:#{self.class.const_get :NAME}"].flatten.join("\n")
- end
+ include Encoding
+ def initialize(request, output = nil)
+ @request = request
+ @output = output || StringIO.new
+ write 'BEGIN', 'VCALENDAR'
+ write 'VERSION', '2.0'
+ yield self
+ write 'END', 'VCALENDAR'
end
- class Event < Component
-
- NAME = 'VEVENT'
-
+ def event(record, &block)
+ component 'vevent', record, &block
end
-
- class Todo < Component
-
- NAME = 'VTODO'
-
- end
-
- include Properties
-
- def initialize(request)
- @request =request
- @properties = { :method=>'PUBLISH' }
- @components = []
+ def todo(record, &block)
+ component 'vtodo', record, &block
end
- attr_reader :components
-
- def event(record = nil)
- returning Event.new(@request, record) do |event|
- yield event if block_given?
- @components << event
- end
+ def journal(record, &block)
+ component 'vjournal', record, &block
end
- def todo(record = nil)
- returning Todo.new(@request, record) do |todo|
- yield todo if block_given?
- @components << todo
- end
+ def to_s
+ @output.respond_to?(:string) ? @output.string : @output.to_s
end
- def to_ical
- # TODO: user's timezone
- properties = @properties.map { |name, value| property(name, value) }
- components = @components.map(&:to_ical)
- ['BEGIN:VCALENDAR', 'VERSION:2.0', properties, components,
'END:VCALENDAR'].flatten.join("\n")
- end
+ private
- def content_type
- "#{Mime::ICS};method=#{properties[:method]}"
+ def component(type, record)
+ write 'BEGIN', type.upcase
+ if record
+ uid MD5.hexdigest([EMAIL PROTECTED], record.class,
record.id, record.created_at].join(':'))
+ dtstamp record.created_at.utc.strftime('%Y%m%dT%H%M%SZ') if
record.respond_to?(:created_at)
+ last_modified record.updated_at.utc.strftime('%Y%m%dT%H%M%SZ') if
record.respond_to?(:updated_at)
+ sequence record.send(record.class.locking_column) if
record.locking_enabled?
+ end
+ yield self
+ write 'END', type.upcase
end
end
@@ -114,10 +95,13 @@
def compile(template)
content_type_handler =
(@view.send!(:controller).respond_to?(:response) ? "controller.response" :
"controller")
- "calendar = ::ActionView::ICalBuilder.new(request)\n" +
- template.source +
- "#{content_type_handler}.content_type ||= calendar.content_type\n" +
- "\ncalendar.to_ical\n"
+ <<-RUBY
+ #{content_type_handler}.content_type ||= "#{Mime::ICS};method=PUBLISH"
+ ical = ActionView::ICalBuilder.new controller.request do |calendar|
+ #{template.source}
+ end
+ ical.to_s
+ RUBY
end
def cache_fragment(block, name = {}, options = nil)
Modified: ode/sandbox/singleshot/lib/tasks/populate.rake
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/lib/tasks/populate.rake?rev=658515&r1=658514&r2=658515&view=diff
==============================================================================
--- ode/sandbox/singleshot/lib/tasks/populate.rake (original)
+++ ode/sandbox/singleshot/lib/tasks/populate.rake Tue May 20 17:32:12 2008
@@ -11,39 +11,55 @@
end
puts "Populating database for #{you.identity}"
- url = 'http://localhost:3001/sandwich'
other = Person.identify('anon') || Person.create(:email=>'[EMAIL
PROTECTED]')
Activity.delete_all
Stakeholder.delete_all
Task.delete_all
- create = lambda do |attributes|
+
+ def retract(*models)
+ models.each do |model|
+ model.all.each do |record|
+ change = ['created_at = ?', record.created_at - 4.hours]
+ if record.respond_to?(:updated_at)
+ change.first << ', updated_at = ?'
+ change << record.updated_at - 1.hours
+ end
+ model.update_all change, :id=>record.id
+ end
+ end
+ end
+
+ def create(attributes)
+ retract Task, Stakeholder, Activity
attributes = { :title=>Faker::Lorem.sentence,
:description=>Faker::Lorem.paragraph,
- :frame_url=>url, :modified_by=>you }.merge(attributes ||
{})
+ :frame_url=>'http://localhost:3001/sandwich',
:modified_by=>Person.find_by_identity(ENV['USER']) }.
+ merge(attributes || {})
Task.create!(attributes)
end
+
# Tasks you should not see.
- create[:title=>'You will not see this task since this task is reserved.',
:status=>'reserved', :creator=>you]
- create[:title=>'You will not see this task since you are not a
stakeholder.']
+ create :title=>'You will not see this task since this task is reserved.',
:status=>'reserved', :creator=>you
+ create :title=>'You will not see this task since you are not a
stakeholder.'
# Tasks in which we are:
# - creator
# - owner
# - observer
# - admin
- create[:creator=>you]
- create[:creator=>you, :owner=>you]
- create[:observers=>you]
- create[:admins=>you]
+ create :creator=>you
+ create :creator=>you, :owner=>you
+ create :observers=>you
+ create :admins=>you
# Tasks in which we are only or one of many potential owners.
- create[:potential_owners=>you]
- create[:potential_owners=>[you, other]]
- create[:owner=>other, :potential_owners=>you]
+ create :potential_owners=>you
+ create :potential_owners=>[you, other]
+ create :owner=>other, :potential_owners=>you
# High priority should show first.
- create[:owner=>you, :priority=>Task::PRIORITIES.first]
+ create :owner=>you, :priority=>Task::PRIORITIES.first
# Over-due before due today before anything else.
- create[:owner=>you, :due_on=>Time.today - 1.day]
- create[:owner=>you, :due_on=>Time.today]
- create[:owner=>you, :due_on=>Time.today + 1.day]
+ create :owner=>you, :due_on=>Time.today - 1.day
+ create :owner=>you, :due_on=>Time.today
+ create :owner=>you, :due_on=>Time.today + 1.day
end
end