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