Author: assaf
Date: Mon Jun  2 21:53:03 2008
New Revision: 662646

URL: http://svn.apache.org/viewvc?rev=662646&view=rev
Log:
More thoughout task rendering:
  Tasks can render using only their description (e.g. offline tasks).
  Perform URL only used when performing task by owner.
  Otherwise either using details URL or task description.
  The perform query parameter is no longer in use.
Post completion redirects back to task list.

Removed:
    ode/sandbox/singleshot/lib/patches/to_xml_primitive.rb
Modified:
    ode/sandbox/singleshot/app/controllers/sandwiches_controller.rb
    ode/sandbox/singleshot/app/controllers/tasks_controller.rb
    ode/sandbox/singleshot/app/helpers/application_helper.rb
    ode/sandbox/singleshot/app/helpers/task_helper.rb
    ode/sandbox/singleshot/app/models/activity.rb
    ode/sandbox/singleshot/app/models/stakeholder.rb
    ode/sandbox/singleshot/app/models/task.rb
    ode/sandbox/singleshot/app/views/sandwiches/show.html.erb
    ode/sandbox/singleshot/app/views/tasks/following.html.erb
    ode/sandbox/singleshot/app/views/tasks/show.html.erb
    ode/sandbox/singleshot/config/routes.rb
    ode/sandbox/singleshot/db/migrate/20080506015046_create_tasks.rb
    ode/sandbox/singleshot/db/schema.rb
    ode/sandbox/singleshot/lib/patches.rb
    ode/sandbox/singleshot/lib/tasks/populate.rake
    ode/sandbox/singleshot/public/stylesheets/default.css
    ode/sandbox/singleshot/spec/models/stakeholder_spec.rb

Modified: ode/sandbox/singleshot/app/controllers/sandwiches_controller.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/controllers/sandwiches_controller.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/controllers/sandwiches_controller.rb (original)
+++ ode/sandbox/singleshot/app/controllers/sandwiches_controller.rb Mon Jun  2 
21:53:03 2008
@@ -7,14 +7,14 @@
   before_filter :instance
 
   def show
-    @read_only = true unless params['perform'] == 'true'
+    [EMAIL PROTECTED] = true unless params['perform'] == 'true'
   end
 
   def update
     @sandwich.update_attributes params['sandwich']
     if @sandwich.save
       flash[:success] = 'Changes have been saved.'
-      redirect_to :action=>'show', :task_url=>@task_url, :perform=>true
+      redirect_to :back
     else
       render :action=>'show'
     end
@@ -24,8 +24,7 @@
     @sandwich.update_attributes params['sandwich']
     if @sandwich.save
       flash[:success] = 'Changes have been saved.'
-      # TODO: FIX!
-      render 
:text=>"<script>frames.top.location.href='http://localhost:3000/tasks'</script>"
+      redirect_to params['complete_url']
     else
       render :action=>'show'
     end

Modified: ode/sandbox/singleshot/app/controllers/tasks_controller.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/controllers/tasks_controller.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/controllers/tasks_controller.rb (original)
+++ ode/sandbox/singleshot/app/controllers/tasks_controller.rb Mon Jun  2 
21:53:03 2008
@@ -50,7 +50,8 @@
 
   def show
     @title = @task.title
-    @alternate = { Mime::ICS=>formatted_tasks_url(:format=>:ics, 
:access_key=>authenticated.access_key) }
+    @alternate = { Mime::ICS=>formatted_task_url(@task, :ics, 
:access_key=>authenticated.access_key),
+                   Mime::ATOM=>formatted_activity_url(@task, :atom, 
:access_key=>authenticated.access_key) }
     respond_to do |wants|
       wants.html { render :layout=>'head' }
       # TODO: wants.xml
@@ -88,6 +89,10 @@
     end
   end
 
+  def complete_redirect
+    render :text=>"<script>frames.top.location.href='#{tasks_url}'</script>"
+  end
+
 
 
   def new

Modified: ode/sandbox/singleshot/app/helpers/application_helper.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/helpers/application_helper.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/helpers/application_helper.rb (original)
+++ ode/sandbox/singleshot/app/helpers/application_helper.rb Mon Jun  2 
21:53:03 2008
@@ -3,8 +3,7 @@
 
   # Returns a link to a person using their full name as the link text and site 
URL
   # (or profile, if unspecified) as the reference.
-  def link_to_person(person, *args)
-    options = args.extract_options!
+  def link_to_person(person, options = {})
     options[:class] = "#{options[:class]} fn url"
     person.url ? link_to(h(person.fullname), person.url, 
options.merge(:title=>"See #{person.fullname}'s profile")) :
       content_tag('span', fullname, options)

Modified: ode/sandbox/singleshot/app/helpers/task_helper.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/helpers/task_helper.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/helpers/task_helper.rb (original)
+++ ode/sandbox/singleshot/app/helpers/task_helper.rb Mon Jun  2 21:53:03 2008
@@ -11,8 +11,8 @@
     case task.status
     when 'ready', 'active'
       vitals = [ 'Created ' + abbr_time(task.created_at, 
relative_time(task.created_at), :class=>'published') ]
-      vitals.first << ' by ' + link_to_person(task.creator, :creator) if 
task.creator
-      vitals << 'assigned to ' + link_to_person(task.owner, :owner) if 
task.owner
+      vitals.first << ' by ' + link_to_person(task.creator, :rel=>:creator) if 
task.creator
+      vitals << 'assigned to ' + link_to_person(task.owner, :rel=>:owner) if 
task.owner
       vitals << 'high priority' if task.high_priority?
       vitals << 'due ' + abbr_date(task.due_on, relative_date(task.due_on)) if 
task.due_on
       vitals.to_sentence
@@ -20,25 +20,24 @@
     when 'suspended'
       return "Suspended"
     when 'completed'
-      "Completed on #{task.updated_at.to_date.to_s(:long)} by #{link_to_person 
task.owner, :owner}"
+      "Completed on #{task.updated_at.to_date.to_s(:long)} by #{link_to_person 
task.owner, :rel=>:owner}"
     when 'cancelled'
       "Cancelled on #{task.updated_at.to_date.to_s(:long)}"
     end
   end
 
-  def task_frame(task)
-    if task.form_perform_url
-      task_uri = URI(task_perform_url(task))
-      task_uri.user, task_uri.password = '_token', 
task.token_for(authenticated)
-      if task.can_complete?(authenticated)
-        uri = URI(task.form_perform_url)
-        uri.query = CGI.parse(uri.query || '').update('perform'=>'true', 
'task_url'=>task_uri).to_query
-      else
-        uri = URI(task.form_view_url || task.form_perform_url)
-        uri.query = CGI.parse(uri.query || 
'').update('task_url'=>task_uri).to_query
-      end
-      content_tag 'iframe', '', :id=>'task_frame', :src=>uri.to_s
+  def task_frame(task, performing)
+    state_uri = URI(task_perform_url(task))
+    state_uri.user, state_uri.password = '_token', 
task.token_for(authenticated)
+    params = { 'task_url'=>state_uri.to_s }
+    if performing
+      uri = URI(task.rendering.perform_url)
+      params.update 'complete_url'=>complete_redirect_tasks_url if 
task.rendering.completing
+    else
+      uri = URI(task.rendering.details_url)
     end
+    uri.query = CGI.parse(uri.query || '').update(params).to_query
+    content_tag 'iframe', '', :id=>'task_frame', :src=>uri.to_s
   end
 
 end

Modified: ode/sandbox/singleshot/app/models/activity.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/models/activity.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/models/activity.rb (original)
+++ ode/sandbox/singleshot/app/models/activity.rb Mon Jun  2 21:53:03 2008
@@ -5,7 +5,7 @@
 #
 #  id         :integer         not null, primary key
 #  person_id  :integer
-#  task_id    :integer
+#  task_id    :integer         not null
 #  action     :string(255)     not null
 #  created_at :datetime        not null
 #

Modified: ode/sandbox/singleshot/app/models/stakeholder.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/models/stakeholder.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/models/stakeholder.rb (original)
+++ ode/sandbox/singleshot/app/models/stakeholder.rb Mon Jun  2 21:53:03 2008
@@ -27,11 +27,7 @@
   # * observer  -- Watches and receives notifications about the task.
   PLURAL_ROLES = ['potential', 'excluded', 'observer', 'admin']
 
-  # All supported roles.
-  ROLES = SINGULAR_ROLES + PLURAL_ROLES
-
-  ACCESSOR_FROM_ROLE = SINGULAR_ROLES.inject({}) { |hash, role| 
hash.update(role=>role) }.
-    update('potential'=>'potential_owners', 'excluded'=>'excluded_owners', 
'observer'=>'observers', 'admin'=>'admins')
+  ALL_ROLES = SINGULAR_ROLES + PLURAL_ROLES
 
   # Stakeholder associated with a task.
   belongs_to :task
@@ -41,93 +37,7 @@
   validates_presence_of :person
 
   # Role for this stakeholder.
-  validates_inclusion_of :role, :in=>ROLES
-
+  validates_inclusion_of :role, :in=>ALL_ROLES
   validates_uniqueness_of :role, :scope=>[:task_id, :person_id]
 
-
-  # Task creator and owner.  Adds three methods for each role:
-  # * {role}          -- Returns person associated with this role, or nil.
-  # * {role}?(person) -- Returns true if person associated with this role.
-  # * {role}= person  -- Assocaites person with this role (can be nil).
-  module Accessors
-
-    SINGULAR_ROLES.each do |role|
-      define_method(role) { in_role(role).first }
-      define_method("#{role}?") { |identity| in_role?(role, identity) }
-      define_method "#{role}=" do |identity|
-        old_value = in_role(role)
-        new_value = set_role(role, identity)
-        changed_attributes[role] = old_value unless 
changed_attributes.has_key?(role) || old_value == new_value
-      end
-    end
-
-    def creator=(identity)
-      return creator unless new_record?
-      set_role 'creator', identity
-    end
-
-    PLURAL_ROLES.each do |role|
-      accessor = ACCESSOR_FROM_ROLE[role]
-      define_method(accessor) { in_role(role) }
-      define_method("#{accessor.singularize}?") { |identity| in_role?(role, 
identity) }
-      define_method("#{accessor}=") { |identities| set_role role, identities }
-    end
-
-    # Returns true if person is a stakeholder in this task: any role except 
excluded owners list.
-    def stakeholder?(person)
-      stakeholders.any? { |sh| sh.person_id == person.id && sh.role != 
'excluded' }
-    end
-
-  private
-
-    # Return all people in this role.
-    def in_role(role)
-      stakeholders.select { |sh| sh.role == role }.map(&:person)
-    end
-
-    # Return true if person in this role.
-    def in_role?(role, identity)
-      person = Person.identify(identity)
-      stakeholders.any? { |sh| sh.role == role && sh.person == person }
-    end
-
-    # Set people associated with this role.
-    def set_role(role, identities)
-      new_set = [identities].flatten.compact.map { |id| Person.identify(id) }
-      keeping = stakeholders.select { |sh| sh.role == role }
-      stakeholders.delete keeping.reject { |sh| new_set.include?(sh.person) }
-      (new_set - keeping.map(&:person)).each { |person| stakeholders.build 
:person=>person, :role=>role }
-      return new_set
-    end
-
-  end
-
-
-  module Validation
-    def self.included(base)
-      base.class_eval do
-        before_validation_on_create do |record|
-          record.owner = record.potential_owners.first unless record.owner || 
record.potential_owners.size > 1
-        end
-
-        # Can only have one member of a singular role.
-        SINGULAR_ROLES.each do |role|
-          validate do |record|
-            record.errors.add role, "Can only have one #{role}." if 
record.stakeholders.select { |sh| sh.role == role }.size > 1
-          end
-        end
-        validate do |record|
-          creator = record.stakeholders.detect { |sh| sh.role == 'creator' }
-          record.errors.add :creator, 'Cannot change creator.' if 
record.changed.include?(:creator) && !record.new_record?
-          record.errors.add :owner, "#{record.owner.fullname} is on the 
excluded owners list and cannot be owner of this task." if
-            record.excluded_owner?(record.owner)
-          conflicting = record.potential_owners & record.excluded_owners
-          record.errors.add :potential_owners, 
"#{conflicting.map(&:fullname).join(', ')} listed on both excluded and 
potential owners list" unless
-            conflicting.empty?
-        end
-      end
-    end
-  end
-
 end

Modified: ode/sandbox/singleshot/app/models/task.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/models/task.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/models/task.rb (original)
+++ ode/sandbox/singleshot/app/models/task.rb Mon Jun  2 21:53:03 2008
@@ -3,22 +3,22 @@
 #
 # Table name: tasks
 #
-#  id               :integer         not null, primary key
-#  title            :string(255)     not null
-#  description      :string(255)     not null
-#  priority         :integer(1)      not null
-#  due_on           :date
-#  status           :string(255)     not null
-#  form_view_url    :string(255)
-#  form_perform_url :string(255)
-#  form_completing  :boolean
-#  outcome_url      :string(255)
-#  outcome_type     :string(255)
-#  access_key       :string(32)
-#  data             :text            not null
-#  version          :integer         default(0), not null
-#  created_at       :datetime
-#  updated_at       :datetime
+#  id              :integer         not null, primary key
+#  title           :string(255)     not null
+#  description     :string(255)     not null
+#  priority        :integer(1)      not null
+#  due_on          :date
+#  status          :string(255)     not null
+#  perform_url     :string(255)
+#  details_url     :string(255)
+#  form_completing :boolean
+#  outcome_url     :string(255)
+#  outcome_type    :string(255)
+#  access_key      :string(32)
+#  data            :text            not null
+#  version         :integer         default(0), not null
+#  created_at      :datetime
+#  updated_at      :datetime
 #
 
 require 'openssl'
@@ -69,6 +69,7 @@
   # states.
   STATUSES = ['reserved', 'ready', 'active', 'suspended', 'completed', 
'cancelled']
 
+
   # Cannot change in mass update.
   validates_inclusion_of :status, :in=>STATUSES
 
@@ -84,16 +85,21 @@
     task.status ||= 'ready' if task.new_record?
     case task.status
     when 'ready'
+      # When task first created, if we only have one potential owner, pick 
them as owner.
       task.owner = task.potential_owners.first unless task.owner || 
task.potential_owners.size > 1
+      # Assigned task => active.
       task.status = 'active' if task.owner
     when 'active'
+      # Unassigned task => ready.
       task.status = 'ready' unless task.owner
     when 'completed', 'cancelled'
+      # Cannot modify completed/cancelled tasks.
       task.readonly! unless task.status_changed?
     end
   end
 
   validate do |task|
+    # Check state transitions.
     from, to = task.status_change
     if from == 'completed' 
       task.errors.add :status, 'Cannot change status of completed task.'
@@ -115,16 +121,53 @@
 
   # -- View and perform ---
 
-  validates_url :form_perform_url, :if=>:form_perform_url
-  validates_url :form_view_url, :if=>:form_view_url
+  # Some tasks are performed offline, for example, calling a customer.  Other
+  # tasks are performed online, in which case we would like to include the UI
+  # for performing the task as part of the task page.
+  #
+  # There are two views for each task.  One view presented to the task owner
+  # for performing the task, the other view, presented to everyone else only
+  # provides details about the task.
+  #
+  # Some forms are integrated with the task manager, these know how to update
+  # the task status and mark the task as completed.  For all other forms, we
+  # need to include a button to mark the task as completed.
+  #
+  # We handle these cases through several combinations of rendering
+  # information.  Some tasks are rendered using only the task description (e.g.
+  # offline tasks).  Other tasks provide a URL for performing the task, using
+  # the description for everyone else.  Last, some tasks provide both a URL for
+  # performing the task and a URL for viewing task details.
+  class Rendering
 
-  before_validation do |record|
-    unless record.form_perform_url
-      record.form_view_url = nil 
-      record.form_completing = nil
+    MAPPING = [%w{perform_url perform_url}, %w{details_url details_url}, 
%w{form_completing completing}]
+    attr_reader :perform_url, :details_url, :completing
+
+    def initialize(perform_url, details_url, completing)
+      @perform_url = perform_url
+      @details_url = details_url if perform_url
+      @completing = perform_url && completing || false
+    end
+
+    # True if rendering the task using only the task description.
+    def use_description?(performing)
+      performing ? perform_url.nil? : details_url.nil?
+    end
+
+    # True if we need to include button to mark task as completed.
+    def use_completion_button?
+      !perform_url || !completing
     end
+
+  end
+
+  composed_of :rendering, :class_name=>Rendering.to_s, 
:mapping=>Rendering::MAPPING do |hash|
+    Rendering.new(hash[:perform_url], hash[:details_url], hash[:completing])
   end
 
+  validates_url :perform_url, :allow_nil=>true
+  validates_url :details_url, :allow_nil=>true
+
 
   # --- Task data ---
 
@@ -143,9 +186,6 @@
   # Stakeholders and people (as stakeholders) associated with this task.
   has_many :stakeholders, :include=>:person, :dependent=>:delete_all
   attr_protected :stakeholders
- 
-  include Stakeholder::Accessors
-  include Stakeholder::Validation
 
   # Eager loading of stakeholders associated with each task.
   named_scope :with_stakeholders, :include=>{ :stakeholders=>:person }
@@ -154,6 +194,81 @@
     { :joins=>'JOIN stakeholders AS involved ON involved.task_id=tasks.id', 
:readonly=>false,
       :conditions=>["involved.person_id=? AND involved.role != 'excluded' AND 
tasks.status != 'reserved'", person.id] } }
 
+  # Task creator and owner.  Adds three methods for each role:
+  # * {role}          -- Returns person associated with this role, or nil.
+  # * {role}?(person) -- Returns true if person associated with this role.
+  # * {role}= person  -- Assocaites person with this role (can be nil).
+  Stakeholder::SINGULAR_ROLES.each do |role|
+    define_method(role) { in_role(role).first }
+    define_method("#{role}?") { |identity| in_role?(role, identity) }
+    define_method "#{role}=" do |identity|
+      old_value = in_role(role)
+      new_value = set_role(role, identity)
+      changed_attributes[role] = old_value unless 
changed_attributes.has_key?(role) || old_value == new_value
+    end
+  end
+
+  def creator=(identity)
+    return creator unless new_record?
+    set_role 'creator', identity
+  end
+
+  ACCESSOR_FROM_ROLE = { 'potential'=>'potential_owners', 
'excluded'=>'excluded_owners', 'observer'=>'observers', 'admin'=>'admins' }
+  ACCESSOR_FROM_ROLE.default = lambda { |role| role }
+
+  # Task observer, admins and potential/excluded owner.  Adds three methods 
for each role:
+  # * {plural}            -- Returns people associated with this role.
+  # * {singular}?(person) -- Returns true if person associated with this role.
+  # * {plural}= people    -- Assocaites people with this role.
+  Stakeholder::PLURAL_ROLES.each do |role|
+    accessor = ACCESSOR_FROM_ROLE[role]
+    define_method(accessor) { in_role(role) }
+    define_method("#{accessor.singularize}?") { |identity| in_role?(role, 
identity) }
+    define_method("#{accessor}=") { |identities| set_role role, identities }
+  end
+
+  # Returns true if person is a stakeholder in this task: any role except 
excluded owners list.
+  def stakeholder?(person)
+    stakeholders.any? { |sh| sh.person_id == person.id && sh.role != 
'excluded' }
+  end
+
+  # Return all people in this role.
+  def in_role(role)
+    stakeholders.select { |sh| sh.role == role }.map(&:person)
+  end
+
+  # Return true if person in this role.
+  def in_role?(role, identity)
+    person = Person.identify(identity)
+    stakeholders.any? { |sh| sh.role == role && sh.person == person }
+  end
+
+  # Set people associated with this role.
+  def set_role(role, identities)
+    new_set = [identities].flatten.compact.map { |id| Person.identify(id) }
+    keeping = stakeholders.select { |sh| sh.role == role }
+    stakeholders.delete keeping.reject { |sh| new_set.include?(sh.person) }
+    (new_set - keeping.map(&:person)).each { |person| stakeholders.build 
:person=>person, :role=>role }
+    return new_set
+  end
+
+  # Can only have one member of a singular role.
+  validate do |record|
+    Stakeholder::SINGULAR_ROLES.each do |role|
+      record.errors.add role, "Can only have one #{role}." if 
record.stakeholders.select { |sh| sh.role == role }.size > 1
+    end
+  end
+
+  validate do |record|
+    creator = record.stakeholders.detect { |sh| sh.role == 'creator' }
+    record.errors.add :creator, 'Cannot change creator.' if 
record.changed.include?(:creator) && !record.new_record?
+    record.errors.add :owner, "#{record.owner.fullname} is on the excluded 
owners list and cannot be owner of this task." if
+      record.excluded_owner?(record.owner)
+    conflicting = record.potential_owners & record.excluded_owners
+    record.errors.add :potential_owners, 
"#{conflicting.map(&:fullname).join(', ')} listed on both excluded and 
potential owners list" unless
+      conflicting.empty?
+  end
+
 
   # --- Priority and ordering ---
   
@@ -217,7 +332,7 @@
         case to
         when 'ready'
           log.add nil, 'resumed' if from == 'suspended'
-          log.add task.changes['owner'].first, 'released' if from == 'active'
+          log.add nil, 'released' if from == 'active'
         when 'active'
           log.add nil, 'resumed' if from == 'suspended'
           log.add task.owner, 'is owner of'

Modified: ode/sandbox/singleshot/app/views/sandwiches/show.html.erb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/views/sandwiches/show.html.erb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/views/sandwiches/show.html.erb (original)
+++ ode/sandbox/singleshot/app/views/sandwiches/show.html.erb Mon Jun  2 
21:53:03 2008
@@ -38,7 +38,8 @@
             <%= content_tag :button, 'Save and continue', :name=>:_method, 
:value=>:put %>
           </p>
         <% end %>
-        <%= hidden_field_tag 'task_url', @task_url %>
+        <%= hidden_field_tag 'task_url', params['task_url'] %>
+        <%= hidden_field_tag 'complete_url', params['complete_url'] %>
       <% end %>
       <%= javascript_tag "$('sandwich').focusFirstElement()" %>
     </div>

Modified: ode/sandbox/singleshot/app/views/tasks/following.html.erb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/views/tasks/following.html.erb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/views/tasks/following.html.erb (original)
+++ ode/sandbox/singleshot/app/views/tasks/following.html.erb Mon Jun  2 
21:53:03 2008
@@ -1,6 +1,6 @@
 <table class='tasks hfeed'>
   <thead>
-    <th style='width:6em'>Status</th>
+    <th style='width:7em'>Status</th>
     <th>Task</th>
     <th style='width:7em'>Due on</th>
     <th style='width:5em'>Priority</th>
@@ -18,7 +18,7 @@
         <td><%= image_tag('exclamation.png') if task.over_due? %> <%= 
abbr_date task.due_on, relative_date(task.due_on).titleize, 
:class=>(task.over_due? ? 'overdue' : nil) if task.due_on %></td>
         <td><%= content_tag 'span', ['High', 'Normal', 'Low'][task.priority - 
1], :class=>"priority_#{task.priority}" %></td>
         <td><%= abbr_time task.created_at, age(task.created_at, false), 
:class=>'published' %></td>
-        <td><%= link_to_person task.owner, :owner if task.owner %></td>
+        <td><%= link_to_person task.owner, :rel=>:owner if task.owner %></td>
       <% end %>
     <% end %>
   </tbody>

Modified: ode/sandbox/singleshot/app/views/tasks/show.html.erb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/views/tasks/show.html.erb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/views/tasks/show.html.erb (original)
+++ ode/sandbox/singleshot/app/views/tasks/show.html.erb Mon Jun  2 21:53:03 
2008
@@ -1,39 +1,43 @@
+<%
+  performing = @task.can_complete?(authenticated)
+  use_description = @task.rendering.use_description?(performing)
+%>
 <% div_for @task do %>
-  <div class='bar'>
-    <div class='bar-actions'>
+  <div class='header'>
+    <div class='actions'>
       <%= quick_actions(@task) %>
-      <%= content_tag 'button', 'More Options', 
:onclick=>"Singleshot.expand(event, '.expanded', 'Less options')", 
:class=>'button-to' %>
+      <%= content_tag 'button', 'More Options', 
:onclick=>"Singleshot.expand(event, '.header .details', 'Less options')", 
:class=>'button-to' %>
     </div>
     <div class='vitals'>
       <%= task_vitals(@task) %>: <%= content_tag 'span', h(@task.title), 
:class=>'title', :title=>@task.title %>
     </div>
-    
-    <div class='expanded' style='display:one'>
-      <div><%= sanitize(simple_format(@task.description)) %></div>
-      <%= link_to image_tag('calendar.png') + ' Calendar', 
formatted_task_url(@task, 'ics', :access_key=>authenticated.access_key),
-          :rel=>'alternate', :title=>'Add this task to your calendar' %>
-      <%= link_to image_tag('feed.png') + ' Activity', 
formatted_activity_url(@task, 'atom', :access_key=>authenticated.access_key),
-          :rel=>'alternate', :title=>'Subscribe to see changes to this task' %>
+    <div class='details' style='display:none'>
+      <%= link_to image_tag('calendar.png') + ' Calendar', 
@alternate[Mime::ICS], :rel=>'alternate', :title=>'Add this task to your 
calendar' %>
+      <%= link_to image_tag('feed.png') + ' Activity', @alternate[Mime::ATOM], 
:rel=>'alternate', :title=>'Subscribe to see changes to this task' %>
       <dl>
+        <%= content_tag('dt', 'Description') + content_tag('dd', 
sanitize(simple_format(@task.description))) unless use_description %>
         <dt>Priority</dt><dd><%= ['High', 'Medium', 'Low'[EMAIL PROTECTED] - 
1] %></dd>
         <%= content_tag('dt', 'Due on') + content_tag('dd', 
relative_date(@task.due_on)) if @task.due_on %>
         <dt>Recent activity</dt>
         <dd><%= render :file=>'activities/_activities', :locals=>{ 
:today=>Date.today, :activities=>@task.activities } %></dd>
-        <%= admins = @task.admins.map { |person| link_to_person(person, 
:admin) }
+        <%= admins = @task.admins.map { |person| link_to_person(person, 
:rel=>:admin) }
             content_tag('dt', 'Admins') + content_tag('dd', 
admins.to_sentence) unless admins.empty? %>
-        <%= observers = @task.observers.map { |person| link_to_person(person, 
:observer) }
+            <%= observers = @task.observers.map { |person| 
link_to_person(person, :rel=>:observer) }
             content_tag('dt', 'Observers') + content_tag('dd', 
observers.to_sentence) unless observers.empty? %>
       </dl>
-      <div class='actions'>
-        <%= quick_actions(@task) %>
-      </div>
     </div>
   </div>
 
-  <%= task_frame @task %>
+  <% if use_description %>
+    <div class='description'><%= sanitize(simple_format(@task.description)) 
%></div>
+  <% else %>
+    <%= task_frame @task, performing %>
+    <%= javascript_tag "Singleshot.taskView('task_frame')" %>
+  <% end %>
 
-  <% unless @task.form_completing %>
-    <div class='completion'><div class='actions'><%= button_to 'Completed', 
task_url(@task, :status=>'completed'), :title=>'Click when task completed' 
%></div></div>
+  <% if performing && @task.rendering.use_completion_button? %>
+    <div class='footer'>
+      <div class='actions'><%= button_to 'Completed', task_url(@task, 
:status=>'completed'), :title=>'Click when task completed' %></div>
+    </div>
   <% end %>
-  <%= javascript_tag "Singleshot.taskView('task_frame')" %>
 <% end %>

Modified: ode/sandbox/singleshot/config/routes.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/config/routes.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/config/routes.rb (original)
+++ ode/sandbox/singleshot/config/routes.rb Mon Jun  2 21:53:03 2008
@@ -1,8 +1,9 @@
 ActionController::Routing::Routes.draw do |map|
 
   map.resource 'session'
-  map.resources 'tasks', :collection=>{ 'following'=>:get, 'completed'=>:get 
}, :member=>{ 'activity'=>:get },
-    :has_one=>[ 'perform' ]
+  map.resources 'tasks', :collection=>{ 'completed'=>:get, 'following'=>:get, 
'complete_redirect'=>:get }, :member=>{ 'activity'=>:get } do |tasks|
+    tasks.resource :perform
+  end
   map.resources 'activities'
   map.day_activity 'activity/:year/:month/:day', :controller=>'activities', 
:action=>'show', :year =>/\d{4}/, :month=>/\d{1,2}/, :day=>/\d{1,2}/
   map.root :tasks

Modified: ode/sandbox/singleshot/db/migrate/20080506015046_create_tasks.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/db/migrate/20080506015046_create_tasks.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/db/migrate/20080506015046_create_tasks.rb (original)
+++ ode/sandbox/singleshot/db/migrate/20080506015046_create_tasks.rb Mon Jun  2 
21:53:03 2008
@@ -6,8 +6,8 @@
       t.integer   'priority',     :null=>false, :limit=>1
       t.date      'due_on',       :null=>true
       t.string    'status',       :null=>false
-      t.string    'form_view_url'
-      t.string    'form_perform_url'
+      t.string    'perform_url'
+      t.string    'details_url'
       t.boolean   'form_completing'
       t.string    'outcome_url',  :null=>true
       t.string    'outcome_type', :null=>true

Modified: ode/sandbox/singleshot/db/schema.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/db/schema.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/db/schema.rb (original)
+++ ode/sandbox/singleshot/db/schema.rb Mon Jun  2 21:53:03 2008
@@ -43,19 +43,19 @@
   end
 
   create_table "tasks", :force => true do |t|
-    t.string   "title",                                         :null => false
-    t.string   "description",                                   :null => false
-    t.integer  "priority",         :limit => 1,                 :null => false
+    t.string   "title",                                        :null => false
+    t.string   "description",                                  :null => false
+    t.integer  "priority",        :limit => 1,                 :null => false
     t.date     "due_on"
-    t.string   "status",                                        :null => false
-    t.string   "form_view_url"
-    t.string   "form_perform_url"
+    t.string   "status",                                       :null => false
+    t.string   "perform_url"
+    t.string   "details_url"
     t.boolean  "form_completing"
     t.string   "outcome_url"
     t.string   "outcome_type"
-    t.string   "access_key",       :limit => 32
-    t.text     "data",                                          :null => false
-    t.integer  "version",                        :default => 0, :null => false
+    t.string   "access_key",      :limit => 32
+    t.text     "data",                                         :null => false
+    t.integer  "version",                       :default => 0, :null => false
     t.datetime "created_at"
     t.datetime "updated_at"
   end

Modified: ode/sandbox/singleshot/lib/patches.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/lib/patches.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/lib/patches.rb (original)
+++ ode/sandbox/singleshot/lib/patches.rb Mon Jun  2 21:53:03 2008
@@ -1,4 +1,3 @@
 # Patches to Rails.
-require File.expand_path('patches/to_xml_primitive', File.dirname(__FILE__))
 require File.expand_path('patches/http_basic', File.dirname(__FILE__))
 require File.expand_path('patches/client_error', File.dirname(__FILE__))

Modified: ode/sandbox/singleshot/lib/tasks/populate.rake
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/lib/tasks/populate.rake?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/lib/tasks/populate.rake (original)
+++ ode/sandbox/singleshot/lib/tasks/populate.rake Mon Jun  2 21:53:03 2008
@@ -35,7 +35,7 @@
       Task.delay 
       you = Person.find_by_identity(ENV['USER']) 
       defaults = { :title=>Faker::Lorem.sentence, 
:description=>Faker::Lorem.paragraphs(3).join("\n\n"),
-                   :form_perform_url=>'http://localhost:3001/sandwich', 
:form_completing=>true, :potential_owners=>[you, other] }
+                   :rendering=>{ 
:perform_url=>'http://localhost:3001/sandwich', :completing=>true }, 
:potential_owners=>[you, other] }
       returning Task.new(defaults.merge(attributes || {})) do |task|
         task.modified_by(you).save!
         def task.delay(duration = 2.hours)

Modified: ode/sandbox/singleshot/public/stylesheets/default.css
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/public/stylesheets/default.css?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/public/stylesheets/default.css (original)
+++ ode/sandbox/singleshot/public/stylesheets/default.css Mon Jun  2 21:53:03 
2008
@@ -275,40 +275,11 @@
 
 /** Single task **/
 
-
-table.summary {
-  width: 100%;
-  border-spacing: 0;
-  border-padding: 0;
-  border: none;
-  margin: 0;
-  padding:0 2.5em 0 2.5em;
-  table-layout: fixed;
-  font-size: 1.1em;
-}
-table.summary td {
-  margin: 0;
-  padding: 0;
-  white-space: nowrap; 
-}
-table.summary td.title {
-  text-overflow: ellipsis;
-  overflow: hidden;
-  white-space: nowrap; 
-}
-table.summary td.actions {
-  text-align: right;
-}
-
-
-
-
-
 div.task {
   margin: 0;
   padding: 0;
 }
-div.task div.bar {
+div.task div.header {
   position: absolute;
   width: 100%;
   margin: 0;
@@ -317,66 +288,52 @@
   background-color: #e0e0e0;
 }
 
-div.task div.bar div.vitals {
+div.task div.header div.vitals {
   margin: 0.6em 0em 0 3em;
   font-size: 1.1em;
   text-overflow: ellipsis;
   overflow: hidden;
   white-space: nowrap;
 }
-div.task div.bar div.vitals span.title {
+div.task div.header div.vitals span.title {
   font-weight: bold;
 }
-div.task div.bar div.bar-actions {
+div.task div.header div.actions {
   display: inline;
   background: #e0e0e0;
   float: right;
   margin: 0.5em 2.5em 0.5em 0;
 }
-div.task div.bar div.bar-actions .button-to {
+div.task div.header div.actions .button-to {
   margin-left: 0.6em;
 }
 
-div.task div.bar div.expanded {
+div.task div.header div.details {
   clear: both;
-  padding: 2em 3em 2em 3em;
+  padding: 0em 3em 2em 3em;
   border-top: 1px solid #ccc;
   margin: 0;
 }
-div.task div.bar div.expanded a[rel=alternate] {
+div.task div.header div.details a[rel=alternate] {
   float: right;
   padding-left: 1em;
   text-decoration: none;
 }
-div.task div.bar div.expanded a[rel=alternate] img {
+div.task div.header div.details a[rel=alternate] img {
   border: none;
   vertical-align: bottom;
   margin: 0.1em;
 }
-div.task div.bar div.expanded h2 {
-  font-size: 1.2em;
-  margin: 0 0 1em 0;
-}
-div.task div.bar div.expanded dl {
-}
-div.task div.bar div.expanded dt {
+div.task div.header div.details dt {
   font-weight: bold;
-  display: inline;
   float: left;
 }
-div.task div.bar div.expanded dd {
+div.task div.header div.details dd {
   margin: 0 0 1em 10em;
 }
-div.task div.bar div.expanded ol.activities {
-  margin: 0;
-  padding: 0;
-}
-div.task div.bar div.expanded div.actions {
-  text-align: right;
-}
 
 div.task div.description {
-  padding: 3em 3em 1em 3em;
+  padding: 3em;
 }
 
 div.task iframe {
@@ -388,7 +345,7 @@
   background: transparent;
 }
 
-div.task div.completion {
+div.task div.footer {
   position: fixed;
   bottom: 0;
   width: 100%;
@@ -397,12 +354,12 @@
   background-color: #e0e0e0;
   border-top: solid 2px #046380;
 }
-div.task div.completion .actions {
+div.task div.footer .actions {
   margin-right: 3em;
   padding-right: 3em;
   text-align: right;
 }
-div.task div.completion .actions .button-to {
+div.task div.footer .actions .button-to {
   margin-left: 1em;
 }
 

Modified: ode/sandbox/singleshot/spec/models/stakeholder_spec.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/spec/models/stakeholder_spec.rb?rev=662646&r1=662645&r2=662646&view=diff
==============================================================================
--- ode/sandbox/singleshot/spec/models/stakeholder_spec.rb (original)
+++ ode/sandbox/singleshot/spec/models/stakeholder_spec.rb Mon Jun  2 21:53:03 
2008
@@ -22,7 +22,7 @@
   end
 
   it 'should have supported role' do
-    Stakeholder::ROLES.each do |role|
+    Stakeholder::ALL_ROLES.each do |role|
       Stakeholder.create(:person=>@person, :task=>@task, :role=>role).should 
have(:no).errors
     end
   end
@@ -49,8 +49,8 @@
   end
 
   it 'should not exist unless specified' do
-    Task.create default_task.except(@role)
-    Task.first.send(@role).should be_nil
+    Task.create! default_task.except(@role)
+    Task.last.send(@role).should be_nil
   end
 
   it 'should accept person at task creation' do
@@ -58,14 +58,14 @@
   end
 
   it 'should return person when loading task' do
-    Task.create default_task.merge(@role=>person('foo'))
-    Task.first.send(@role).should eql(person('foo'))
+    Task.create! default_task.merge(@role=>person('foo'))
+    Task.last.send(@role).should eql(person('foo'))
   end
 
   it 'should identify person in role' do
-    Task.create default_task.merge(@role=>person('foo'))
-    Task.first.send("[EMAIL PROTECTED]", person('foo')).should be_true
-    Task.first.send("[EMAIL PROTECTED]", person('bar')).should be_false
+    Task.create! default_task.merge(@role=>person('foo'))
+    Task.last.send("[EMAIL PROTECTED]", person('foo')).should be_true
+    Task.last.send("[EMAIL PROTECTED]", person('bar')).should be_false
   end
 end
 
@@ -75,15 +75,15 @@
   it_should_behave_like 'singular role'
 
   it 'should not allow changing creator' do
-    Task.create default_task.merge(:creator=>person('creator'))
-    Task.first.update_attributes :creator=>person('other')
-    Task.first.creator.should == person('creator')
+    Task.create! default_task.merge(:creator=>person('creator'))
+    Task.last.update_attributes :creator=>person('other')
+    Task.last.creator.should == person('creator')
   end
 
   it 'should not allow setting creator on existing task' do
-    Task.create default_task
-    Task.first.update_attributes :creator=>person('creator')
-    Task.first.creator.should be_nil
+    Task.create! default_task
+    Task.last.update_attributes :creator=>person('creator')
+    Task.last.creator.should be_nil
   end
 
 end
@@ -94,44 +94,44 @@
   it_should_behave_like 'singular role'
 
   it 'should allow changing owner on existing task' do
-    Task.create default_task.merge(:owner=>person('owner'))
-    Task.first.update_attributes! :owner=>person('other')
-    Task.first.owner.should == person('other')
+    Task.create! default_task.merge(:owner=>person('owner'))
+    Task.last.update_attributes! :owner=>person('other')
+    Task.last.owner.should == person('other')
   end
 
   it 'should only store one owner association for task' do
-    Task.create default_task.merge(:owner=>person('owner'))
-    Task.first.update_attributes! :owner=>person('other')
-    Stakeholder.find(:all, :conditions=>{:task_id=>Task.first.id}).size.should 
== 1
+    Task.create! default_task.merge(:owner=>person('owner'))
+    Task.last.update_attributes! :owner=>person('other')
+    Stakeholder.find(:all, :conditions=>{:task_id=>Task.last.id}).size.should 
== 1
   end
 
   it 'should allow setting owner to nil' do
-    Task.create default_task.merge(:owner=>person('owner'))
-    Task.first.update_attributes! :owner=>nil
-    Task.first.owner.should be_nil
+    Task.create! default_task.merge(:owner=>person('owner'))
+    Task.last.update_attributes! :owner=>nil
+    Task.last.owner.should be_nil
   end
 
   it 'should not allow owner if listed in excluded owners' do
-    Task.create default_task.merge(:excluded_owners=>person('excluded'))
-    lambda { Task.first.update_attributes! :owner=>person('excluded') }.should 
raise_error
-    Task.first.owner.should be_nil
+    Task.create! default_task.merge(:excluded_owners=>person('excluded'))
+    lambda { Task.last.update_attributes! :owner=>person('excluded') }.should 
raise_error
+    Task.last.owner.should be_nil
   end
 
   it 'should be potential owner if task created with one potential owner' do
-    Task.create default_task.merge(:potential_owners=>person('foo'))
-    Task.first.owner.should == person('foo')
+    Task.create! default_task.merge(:potential_owners=>person('foo'))
+    Task.last.owner.should == person('foo')
   end
 
   it 'should not be potential owner if task created with more than one' do
-    Task.create default_task.merge(:potential_owners=>people('foo', 'bar'))
-    Task.first.owner.should be_nil
+    Task.create! default_task.merge(:potential_owners=>people('foo', 'bar'))
+    Task.last.owner.should be_nil
   end
 
   it 'should not be potential owner if task updated to have no owner' do
-    Task.create default_task.merge(:potential_owners=>person('foo'))
-    Task.first.update_attributes! :owner=>person('bar')
-    Task.first.update_attributes! :owner=>nil
-    Task.first.owner.should be(nil)
+    Task.create! default_task.merge(:potential_owners=>person('foo'))
+    Task.last.update_attributes! :owner=>person('bar')
+    Task.last.update_attributes! :owner=>nil
+    Task.last.owner.should be(nil)
   end
 end
 
@@ -148,8 +148,8 @@
   end
 
   it 'should not exist unless specified' do
-    Task.create default_task.except(@role)
-    Task.first.send(@role).should be_empty
+    Task.create! default_task.except(@role)
+    Task.last.send(@role).should be_empty
   end
 
   it 'should accept list of people at task creation' do
@@ -157,53 +157,53 @@
   end
 
   it 'should list of people when loading task' do
-    Task.create default_task.merge(@role=>@people)
-    Task.first.send(@role).should == @people
+    Task.create! default_task.merge(@role=>@people)
+    Task.last.send(@role).should == @people
   end
 
   it 'should accept single person' do
-    Task.create default_task.merge(@role=>person('foo'))
-    Task.first.send(@role).should == [person('foo')]
+    Task.create! default_task.merge(@role=>person('foo'))
+    Task.last.send(@role).should == [person('foo')]
   end
 
   it 'should accept empty list' do
     Task.create(default_task.merge(@role=>[]))
-    Task.first.send(@role).should be_empty
+    Task.last.send(@role).should be_empty
   end
 
   it 'should accept empty list and discard all stakeholders' do
-    Task.create default_task.merge(@role=>@people)
-    Task.first.update_attributes! @role=>[]
-    Task.first.send(@role).should be_empty
+    Task.create! default_task.merge(@role=>@people)
+    Task.last.update_attributes! @role=>[]
+    Task.last.send(@role).should be_empty
   end
 
   it 'should accept nil and treat it as empty list' do
-    Task.create default_task.merge(@role=>@people)
-    Task.first.update_attributes! @role=>nil
-    Task.first.send(@role).should be_empty
+    Task.create! default_task.merge(@role=>@people)
+    Task.last.update_attributes! @role=>nil
+    Task.last.send(@role).should be_empty
   end
 
   it 'should allow adding stakeholders' do
-    Task.create default_task.merge(@role=>person('foo'))
-    Task.first.update_attributes! @role=>[person('foo'), person('bar')]
-    Task.first.send(@role).should == [person('foo'), person('bar')]
+    Task.create! default_task.merge(@role=>person('foo'))
+    Task.last.update_attributes! @role=>[person('foo'), person('bar')]
+    Task.last.send(@role).should == [person('foo'), person('bar')]
   end
 
   it 'should add each person only once' do
-    Task.create default_task.merge(@role=>([person('foo')] *3))
-    Task.first.send(@role).size.should == 1
+    Task.create! default_task.merge(@role=>([person('foo')] *3))
+    Task.last.send(@role).size.should == 1
   end
 
   it 'should allow removing stakeholders' do
-    Task.create default_task.merge(@role=>[person('foo'), person('bar')])
-    Task.first.update_attributes! @role=>person('bar')
-    Task.first.send(@role).should == [person('bar')]
+    Task.create! default_task.merge(@role=>[person('foo'), person('bar')])
+    Task.last.update_attributes! @role=>person('bar')
+    Task.last.send(@role).should == [person('bar')]
   end
 
   it 'should identify person in role' do
-    Task.create default_task.merge(@role=>person('foo'))
-    Task.first.send("[EMAIL PROTECTED]", person('foo')).should be_true
-    Task.first.send("[EMAIL PROTECTED]", person('bar')).should be_false
+    Task.create! default_task.merge(@role=>person('foo'))
+    Task.last.send("[EMAIL PROTECTED]", person('foo')).should be_true
+    Task.last.send("[EMAIL PROTECTED]", person('bar')).should be_false
   end
 end
 


Reply via email to