myexperiment-hackers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[myexperiment-hackers] [2145] branches/apace: svn merge -r2128:2144 svn+


From: noreply
Subject: [myexperiment-hackers] [2145] branches/apace: svn merge -r2128:2144 svn+ssh://address@hidden/ var/svn/myexperiment/trunk
Date: Sun, 29 Mar 2009 09:56:06 -0400 (EDT)

Revision
2145
Author
dgc
Date
2009-03-29 09:56:05 -0400 (Sun, 29 Mar 2009)

Log Message

svn merge -r2128:2144 svn+ssh://address@hidden/var/svn/myexperiment/trunk

Modified Paths

Added Paths

Removed Paths

Diff

Modified: branches/apace/app/controllers/api_controller.rb (2144 => 2145)


--- branches/apace/app/controllers/api_controller.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/controllers/api_controller.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -13,6 +13,9 @@
 
   def process_request
 
+    # all responses from the API are in XML
+    response.content_type = "application/xml"
+
     user = current_user
 
     auth = request.env["HTTP_AUTHORIZATION"]
@@ -21,25 +24,20 @@
       credentials = Base64.decode64(auth.sub(/^Basic /, '')).split(':')
       user = User.authenticate(credentials[0], credentials[1])
 
-      if user.nil?
-        render :xml => rest_error_response(401, 'Not authorized').to_s
-        return
-      end
+      return rest_response(401) if user.nil?
 
     end
 
-    query  = CGIMethods.parse_query_parameters(request.query_string)
     method = request.method.to_s.upcase
     uri    = params[:uri]
 
-   # logger.info "current token: #{current_token.inspect}"
-   # logger.info "current user: #{user.id}"
-   # logger.info "query: #{query}"
-   # logger.info "method: #{method}"
-   # logger.info "uri: #{uri}"
+    # logger.info "current token: #{current_token.inspect}"
+    # logger.info "current user: #{user.id}"
+    # logger.info "method: #{method}"
+    # logger.info "uri: #{uri}"
 
-    return bad_rest_request if TABLES['REST'][:data][uri].nil? 
-    return bad_rest_request if TABLES['REST'][:data][uri][method].nil?
+    return rest_response(400) if TABLES['REST'][:data][uri].nil? 
+    return rest_response(400) if TABLES['REST'][:data][uri][method].nil?
 
     rules = TABLES['REST'][:data][uri][method]
 
@@ -54,21 +52,15 @@
         permission_found = true if permission.for == requested_permission
       end
 
-      if permission_found == false
-        render :xml => rest_error_response(403, 'Not authorized').to_s
-        return
-      end
+      return rest_response(403) if permission_found == false
     end  
 
     case rules['Type']
-      when 'index'; doc = rest_index_request(rules, user, query)
-      when 'crud';  doc = rest_crud_request(rules, user)
-      when 'call';  doc = rest_call_request(rules, user, query)
-      else;         bad_rest_request
+      when 'index'; doc = rest_index_request(params[:uri], rules, user, request.query_parameters)
+      when 'crud';  doc = rest_crud_request(params[:uri], rules, user, request.query_parameters)
+      when 'call';  doc = rest_call_request(params[:uri], rules, user, request.query_parameters)
+      else;         return rest_response(400)
     end
-
-    current_token = nil
-    render :xml => doc.to_s
   end
 end
 

Modified: branches/apace/app/controllers/runners_controller.rb (2144 => 2145)


--- branches/apace/app/controllers/runners_controller.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/controllers/runners_controller.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -123,7 +123,7 @@
 
   def error(notice, message, attr=:id)
     flash[:error] = notice
-    (err = Runner.new.errors).add(attr, message)
+    (err = TavernaEnactor.new.errors).add(attr, message)
     
     respond_to do |format|
       format.html { redirect_to runners_url }

Modified: branches/apace/app/controllers/workflows_controller.rb (2144 => 2145)


--- branches/apace/app/controllers/workflows_controller.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/controllers/workflows_controller.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -829,7 +829,8 @@
           # Set the internal unique name for this particular workflow (or workflow_version).
           workflow_to_set.set_unique_name
           
-          workflow_to_set.image, workflow_to_set.svg = processor_instance.get_preview_images if processor_class.can_generate_preview?
+          workflow_to_set.image = processor_instance.get_preview_image if processor_class.can_generate_preview_image?
+          workflow_to_set.svg   = processor_instance.get_preview_svg   if processor_class.can_generate_preview_svg?
         rescue Exception => ex
           worked = false
           err_msg = "ERROR: some processing failed in workflow processor '#{processor_class.to_s}'.\nEXCEPTION: #{ex}"

Modified: branches/apace/app/helpers/application_helper.rb (2144 => 2145)


--- branches/apace/app/helpers/application_helper.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/helpers/application_helper.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -51,21 +51,12 @@
   
   def owner_text(thing)
     return '' if thing.nil?
-    
-    case thing.class.to_s
-    when "Workflow"
-      return "Original Uploader"
-    when "Blob"
-      return "Uploader"
-    when "Pack"
-      return "Creator"
-    when "Network"
-      return "Admin"
-    when "Profile"
-      return "User"
-    else
-      return ''
-    end
+
+    text = thing.class.owner_text if thing.class.respond_to?('owner_text')
+
+    return '' if text.nil?
+
+    text
   end
   
   def datetime(old_dt, long=true)

Modified: branches/apace/app/helpers/mashup_helper.rb (2144 => 2145)


--- branches/apace/app/helpers/mashup_helper.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/helpers/mashup_helper.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -1,86 +1,2 @@
-
-require 'lib/rest'
-
 module MashupHelper
-
-  def get_rest_routes(type)
-
-    routes = []
-
-    TABLES['REST'][:data].sort.each do |uri,methods|
-      methods.each do |method,rules|
-        routes << [uri,method,rules] if rules['Type'] == type
-      end
-    end
-
-    routes
-  end
-
-  def get_model_attributes(rest_name)
-    TABLES['Model'][:data][rest_name]
-  end
-
-  def get_example_id(rules)
-    rules['Example'][rules['REST Attribute'].index('id')]
-  end
-
-  def rest_example_id(type)
-    case type
-      when "workflow"; return "20"
-    end
-  end
-
-  def trim_and_wrap(doc)
-
-    # Clean up the base64 sections
-
-    doc.root.children.each do |node|
-      if node["encoding"] == "base64"
-
-        text = node.child.to_s
-
-        lines = text.strip.split("\n")
-        lines = lines[0..9] + ['...'] if lines.length > 10
-        lines = lines.map do |line|
-          "    #{line.strip}"
-        end
-
-        text = lines.join("\n").strip
-        text = "\n    #{text}\n  "
-
-        node.children[0].remove!
-        node << text
-      end
-    end
-
-    doc.to_s
-  end
-  
-  def rest_example(method, rest_name, model_name, id, show_version)
-
-    query = { 'id' => id, 'all_elements' => 'yes' }
-
-    query['version'] = 1 if show_version
-
-    ob = eval(model_name.camelize).find_by_id(id.to_i)
-
-    return "" if ob.nil?
-
-    doc = rest_get_request(ob, rest_name, nil, rest_resource_uri(ob), rest_name, query)
-
-    trim_and_wrap(doc)
-  end
-
-  def rest_index_example(thing)
-    doc = rest_index_request(TABLES['REST'][:data][thing]['GET'], nil, {} )
-
-    trim_and_wrap(doc)
-  end
-
-  def try_it_now_link(method, uri)
-    target = "#{request.protocol}#{request.host_with_port}#{uri}"
-    "#{target} <input type=\"button\" value=\"Try it now\"  '#{target}')\" />"
-  end
-
 end
-

Modified: branches/apace/app/models/blob.rb (2144 => 2145)


--- branches/apace/app/models/blob.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/models/blob.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -4,11 +4,15 @@
 # See license.txt for details.
 
 require 'acts_as_contributable'
+require 'acts_as_site_entity'
 require 'acts_as_creditable'
 require 'acts_as_attributor'
 require 'acts_as_attributable'
 
 class Blob < ActiveRecord::Base
+
+  acts_as_site_entity :owner_text => 'Uploader'
+
   acts_as_contributable
 
   acts_as_bookmarkable

Modified: branches/apace/app/models/blog.rb (2144 => 2145)


--- branches/apace/app/models/blog.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/models/blog.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -3,9 +3,13 @@
 # Copyright (c) 2007 University of Manchester and the University of Southampton.
 # See license.txt for details.
 
+require 'acts_as_site_entity'
 require 'acts_as_contributable'
 
 class Blog < ActiveRecord::Base
+
+  acts_as_site_entity
+
   acts_as_contributable
   
   acts_as_bookmarkable

Modified: branches/apace/app/models/comment.rb (2144 => 2145)


--- branches/apace/app/models/comment.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/models/comment.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -23,6 +23,10 @@
     }
   )
   
+  validates_presence_of :comment
+  validates_presence_of :commentable_type
+  validates_presence_of :commentable_id
+
   def simile_title
     "Comment by: #{self.user.name}"
   end

Modified: branches/apace/app/models/network.rb (2144 => 2145)


--- branches/apace/app/models/network.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/models/network.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -5,11 +5,14 @@
 
 require 'acts_as_contributor'
 require 'acts_as_creditor'
+require 'acts_as_site_entity'
 
 class Network < ActiveRecord::Base
   acts_as_contributor
   acts_as_creditor
   
+  acts_as_site_entity :owner_text => 'Admin'
+
   acts_as_commentable
   acts_as_taggable
   
@@ -49,6 +52,8 @@
              :class_name => "User",
              :foreign_key => :user_id
              
+  alias_method :contributor, :owner
+
   def owner?(userid)
     user_id.to_i == userid.to_i
   end

Modified: branches/apace/app/models/pack.rb (2144 => 2145)


--- branches/apace/app/models/pack.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/models/pack.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -4,6 +4,7 @@
 # See license.txt for details.
 
 require 'acts_as_contributable'
+require 'acts_as_site_entity'
 require 'uri'
 require 'zip/zip'
 require 'tempfile'
@@ -11,6 +12,9 @@
 
 
 class Pack < ActiveRecord::Base
+
+  acts_as_site_entity :owner_text => 'Creator'
+
   acts_as_contributable
   
   acts_as_bookmarkable

Modified: branches/apace/app/models/profile.rb (2144 => 2145)


--- branches/apace/app/models/profile.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/models/profile.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -3,6 +3,8 @@
 # Copyright (c) 2007 University of Manchester and the University of Southampton.
 # See license.txt for details.
 
+require 'acts_as_site_entity'
+
 class Profile < ActiveRecord::Base
   
   belongs_to :owner,
@@ -34,6 +36,8 @@
   belongs_to :picture
   
   validates_email_veracity_of :email
+
+  acts_as_site_entity :owner_text => 'User'
   
   acts_as_solr :fields => [ :email,
                             :website,

Modified: branches/apace/app/models/user.rb (2144 => 2145)


--- branches/apace/app/models/user.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/models/user.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -5,6 +5,7 @@
 
 require 'digest/sha1'
 
+require 'acts_as_site_entity'
 require 'acts_as_contributor'
 require 'acts_as_creditor'
 
@@ -272,6 +273,8 @@
     return Conf.admins.include?(self.username.downcase)
   end
   
+  acts_as_site_entity
+
   acts_as_contributor
   
   has_many :blobs, :as => :contributor

Modified: branches/apace/app/models/workflow.rb (2144 => 2145)


--- branches/apace/app/models/workflow.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/models/workflow.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -3,8 +3,8 @@
 # Copyright (c) 2007 University of Manchester and the University of Southampton.
 # See license.txt for details.
 
+require 'acts_as_site_entity'
 require 'acts_as_contributable'
-require 'acts_as_contributable'
 require 'acts_as_creditable'
 require 'acts_as_attributor'
 require 'acts_as_attributable'
@@ -29,6 +29,11 @@
                         wv.destroy
                       end }
 
+  before_validation :check_unique_name
+  before_validation :extract_metadata
+
+  acts_as_site_entity :owner_text => 'Original Uploader'
+
   acts_as_contributable
   
   acts_as_bookmarkable
@@ -114,6 +119,10 @@
     return list
   end
   
+  def check_unique_name
+    set_unique_name if unique_name.nil?
+  end
+
   # Sets an internal unique name for this workflow.
   def set_unique_name
     salt = rand 1000000
@@ -125,6 +134,29 @@
     end
   end
   
+  # This method is called before save and attempts to pull out metadata if it
+  # hasn't been set
+  def extract_metadata
+
+    if !content_blob.nil? && processor_class
+
+      do_image = true if image.nil? && processor_class.can_generate_preview_image?
+      do_svg   = true if svg.nil?   && processor_class.can_generate_preview_svg?
+      do_title = true if title.nil?
+      do_desc  = true if body.nil?
+      
+      if do_image || do_svg || do_title || do_desc
+
+        processor = processor_class.new(content_blob.data)
+
+        self.image = processor.get_preview_image if do_image
+        self.svg   = processor.get_preview_svg   if do_svg
+        self.title = processor.get_title         if do_title
+        self.body  = processor.get_description   if do_desc
+      end
+    end
+  end
+
   def processor_class
     @processor_class ||= WorkflowTypesHandler.processor_class_for_content_type(self.content_type)
   end

Modified: branches/apace/app/views/comments/_comment.rhtml (2144 => 2145)


--- branches/apace/app/views/comments/_comment.rhtml	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/views/comments/_comment.rhtml	2009-03-29 13:56:05 UTC (rev 2145)
@@ -6,7 +6,6 @@
         <%= contributor(comment.user_id, 'User', true, 60) %>
       </td>
       <td class="header" height="1.2em">
-		  <!--<% unless comment.title.nil? %><b><%=h comment.title %></b><br/><% end %>-->
 	      <%=datetime comment.created_at %>
 	  </td>
     </tr>
@@ -32,4 +31,4 @@
 		</td>
 	</tr>
   </table>
-</li>
\ No newline at end of file
+</li>

Modified: branches/apace/app/views/mashup/api.rhtml (2144 => 2145)


--- branches/apace/app/views/mashup/api.rhtml	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/views/mashup/api.rhtml	2009-03-29 13:56:05 UTC (rev 2145)
@@ -5,34 +5,10 @@
 
 <h1>API version <%= "#{API_VERSION}" %></h1>
 
-<h2>Table of Contents</h2>
+<p>Please refer to the <%= Conf.sitename %> wiki for
+<a href="" documentation</a>.
+</p>
 
-<ul>
-  <li><a href="" it out</a></li>
-  <li><a href="" requests</a></li>
-  <ul>
-    <% get_rest_routes('index').each do |route| %>
-      <li><a href="" "#{route[0]}" %>"><%= "#{route[0]} index" %></a></li>
-    <% end %>
-  </ul>
-  <li><a href="" requests</a></li>
-  <ul>
-    <% get_rest_routes('crud').each do |route| %>
-      <li><a href="" "rest-#{route[1]}-#{route[0]}" %>"><%= "#{route[1]} #{route[0]}" %></a></li>
-    <% end %>
-  </ul>
-  <li><a href="" search</a></li>
-  <li><a href="" selection</a></li>
-  <li><a href=""
-  <li><a href=""
-  <li><a href=""
-  <li><a href="" filtered by tag</a></li>
-<!--
-  <li><a href="" URIs</a></li>
--->
-  <li><a href="" version</a></li>
-  <li><a href="" responses</a></li>
-</ul>
 
 <h2 id="try-it-out">Try it out</h2>
 
@@ -57,239 +33,3 @@
   <textarea class="try_it_out_box" id="output" cols="100" rows="20" style="font-size: 0.9em"></textarea><br />
 </div>
 
-<!--
-<h2>REST commands</h2>
-
-<table summary="">
-
- <tr>
-  <td>Auth ID</td>
-  <td><input value="1" id="auth-id" size="4" /></td>
-  <td>
-
-   <input type="button" value="Login"  />
-   <input type="button" value="Logout"  />
-
-  </td>
- </tr>
-
-
- <tr>
-  <td>User ID</td>
-  <td><input value="1" id="user-id" size="4" /></td>
-  <td>
-
-   <input type="button" value="New user"  />
-   <input type="button" value="List users"  />
-   <input type="button" value="Get user"  />
-   <input type="button" value="Update user"  />
-   <input type="button" value="Delete user"  />
-
-  </td>
- </tr>
-
- <tr>
-  <td>Profile ID</td>
-  <td><input value="1" id="profile-id" size="4" /></td>
-  <td>
-
-   <input type="button" value="New profile"  />
-   <input type="button" value="List profiles"  />
-   <input type="button" value="Get profile"  />
-   <input type="button" value="Update profile"  />
-   <input type="button" value="Delete profile"  />
-   
-  </td>
- </tr>
-
- <tr>
-  <td>Group ID</td>
-  <td><input value="1" id="group-id" size="4" /></td>
-  <td>
-
-   <input type="button" value="New group"  />
-   <input type="button" value="List groups"  />
-   <input type="button" value="Get group"  />
-   <input type="button" value="Update group"  />
-   <input type="button" value="Delete group"  />
-   
-  </td>
- </tr>
-
- <tr>
-  <td>Message ID</td>
-  <td><input value="1" id="message-id" size="4" /></td>
-  <td>
-
-   <input type="button" value="New message"  />
-   <input type="button" value="List messages"  />
-   <input type="button" value="Get message"  />
-   <input type="button" value="Update message"  />
-   <input type="button" value="Delete message"  />
-   
-  </td>
- </tr>
-</table>
--->
-<h2 id="index">Index requests</h2>
-
-<% get_rest_routes('index').each do |route| %>
-
-  <h3 id="index-<%= "#{route[0]}" %>">GET <%= "/#{route[0]}.xml" %></h3>
-
-  <h4>Example curl request:</h4>
-  <p><tt>curl <%= "#{url_for(:controller => '/', : false)}#{route[0]}.xml" %></tt></p>
-
-  <pre style="overflow: hidden"><%=h rest_index_example(route[0]) %></pre>
-
-<% end %>
-
-<h2 id="rest">REST (Create/Read/Update/Destroy) requests</h2>
-
-<% get_rest_routes('crud').each do |route| %>
-
-  <h3 id="<%= "rest-#{route[1]}-#{route[0]}" %>"><%= "#{route[1]} /#{route[0]}.xml" %></h3>
-
-  <% next unless route[1] == 'GET' %>
-
-  <div class="rest_table">
-    <table>
-      <thead>
-        <tr>
-          <td>Element</td>
-          <td>Example</td>
-          <td>Read by default</td>
-        </tr>
-      </thead>
-      <tbody>
-        <% attrs = get_model_attributes(route[2]['REST Entity']) %>
-        <% (0..attrs['REST Attribute'].length).each do |i| %>
-
-            <tr>
-              <td><%= attrs['REST Attribute'][i] %></td>
-              <td><%= attrs['Example'][i] %></td>
-              <td><%= attrs['Read by default'][i] %></td>
-            </tr>
-        <% end %>
-      </tbody>
-    </table>
-    <p><small>Note: You can specify which elements to get by using the 
-  <a href="" query option.</small></p>
-  </div>
-
-  <h4>Example curl request:</h4>
-  <p><tt>curl <%= "#{url_for(:controller => '/', : false)}#{route[0]}.xml?id=#{get_example_id(attrs)}" %></tt></p>
-
-  <pre style="overflow: hidden"><%=h rest_example(route[1], route[2]['REST Entity'], route[2]['Model Entity'], get_example_id(attrs).to_i, false) %></pre>
-
-<% end %>
-
-<h2 id="search">General search</h2>
-
-<p>The search facility is available from the following URL:</p>
-
-<pre>  <%= try_it_now_link('GET', '/search.xml?query=bioaid') %></pre>
-
-<p>Particular types can be specified by the <tt>type</tt> query option:</p>
-
-<pre>  <%= try_it_now_link('GET', '/search.xml?query=bioaid&type=workflow') %></pre>
-
-<h2 id="elements">Element selection</h2>
-
-<p>The elements within each REST response can be tailored to include only the
-parts of the response of interest.  This saves bandwidth and response time!</p>
-
-<pre>  <%= try_it_now_link('GET', "/workflow.xml?id=#{rest_example_id('workflow')}&elements=title,description") %></pre>
-
-<p>All elements can be returned too.</p>
-
-<pre>  <%= try_it_now_link('GET', "/workflow.xml?id=#{rest_example_id('workflow')}&all_elements=yes") %></pre>
-
-<h2 id="versions">Versions</h2>
-
-<p>For objects that have versions (e.g. Workflows), you may specify the
-specific version to be worked on by including <tt>version</tt> in the URI
-query.  For example:</p>
-
-<pre>  <%= try_it_now_link('GET', '/workflow.xml?id=20&version=1') %></pre>
-
-<h2 id="sorting">Sorting</h2>
-
-<p>Index and search requests can be sorted.  By default, these results are
-sorted by ascending creation order but sorting by creation time
-(<tt>created</tt>), update time (<tt>updated</tt>), title (<tt>title</tt>) and
-name (<tt>name</tt>) is also possible.  In addition, the results can be
-returned in reverse order.</p>
-
-<pre>  <%= try_it_now_link('GET', '/workflows.xml?sort=title') %></pre>
-
-<pre>  <%= try_it_now_link('GET', '/files.xml?sort=title&order=reverse') %></pre>
-
-<h2 id="pagination">Pagination</h2>
-
-<p>For the index requests, a default maximum of 25 results is shown.  This can
-be raised to 100 by using adding <tt>num</tt> to the query.  A particular
-page of results can be selected by adding <tt>page</tt> to the query.</p>
-
-<pre>  <%= try_it_now_link('GET', '/workflows.xml?num=50&page=2') %></pre>
-
-<h2 id="tags">Index filtered by tag</h2>
-
-<p>For indices of taggable items, the index can be filtered on a particular tag.</p>
-
-<pre>  <%= try_it_now_link('GET', '/workflows.xml?tag=bioaid') %></pre>
-
-<!--
-<h2 id="rest-uris">REST URIs</h2>
-
-<p>URIs used within the API refer to the <%= Conf.sitename %> records.  In addition to
-these, "REST URIs" can be requested within each API response by including 
-<tt>rest_uris=yes</tt> in the query.</p>
-
-<p>The REST URIs are useful endpoints for further API calls.</p>
-
-<pre>  <%= try_it_now_link('GET', '/workflow.xml?id=36&rest_uris=yes') %></pre>
--->
-
-<h2 id="api-version">API version</h2>
-
-<p>The version of the REST API that the server uses can be requested by
-including <tt>api_version</tt> in the query.</p>
-
-<pre>  <%= try_it_now_link('GET', '/workflows.xml?api_version=yes') %></pre>
-
-<h2 id="errors">Error responses</h2>
-
-<p>The general format of an error response from the REST API is an XML document
-with an <tt>error</tt> element.  The <tt>code</tt> attribute contains a number
-that determines the class of error.  The <tt>message</tt> attribute of the
-error element contains a human readable message describing the error.</p>
-
-<h3>Example error response</h3>
-
-<pre>
-&lt;?xml version="1.0" encoding="UTF-8"?&gt; 
-&lt;error message="Resource not found" code="404"/&gt; 
-</pre>
-
-<h3>Error messages</h3>
-
-<div class="rest_table" summary="">
-  <table>
-    <thead>
-      <tr>
-        <td>Message</td>
-        <td>Code</td>
-      </tr>
-    </thead>
-    <tbody>
-      <tr><td>Resource not found</td><td>404</td></tr>
-      <tr><td>Not authorized</td><td>403</td></tr>
-      <tr><td>Resource not versioned</td><td>400</td></tr>
-      <tr><td>Resource version not found</td><td>404</td></tr>
-      <tr><td>Service unavailable</td><td>503</td></tr>
-    </tbody>
-  </table>
-</div>
-
-

Modified: branches/apace/app/views/messages/show.rhtml (2144 => 2145)


--- branches/apace/app/views/messages/show.rhtml	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/app/views/messages/show.rhtml	2009-03-29 13:56:05 UTC (rev 2145)
@@ -18,7 +18,8 @@
 	</li>
 </ul>
 
-<% if @message.reply? %>
+<% # this makes sure that the message is a reply to something, and that the original message still exists -%>
+<% if @message.reply? && @message.reply_to && address@hidden %>
   <br/>
   <p class="box_infotext">
     <b>This message is a reply to:</b> <%= link_to h(@message.reply_to.subject), message_path(@message.reply_to) %>

Modified: branches/apace/config/settings.yml.pre (2144 => 2145)


--- branches/apace/config/settings.yml.pre	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/config/settings.yml.pre	2009-03-29 13:56:05 UTC (rev 2145)
@@ -193,6 +193,7 @@
 # model_aliases - These are visible names for models where the desired visible
 #                 name might conflict with existing classes (e.g. "File" or
 #                 "Application")
+
 model_aliases:
 
   File:  Blob

Modified: branches/apace/config/tables.xml


(Binary files differ)

Deleted: branches/apace/db/migrate/074_create_algorithms.rb (2144 => 2145)


--- branches/apace/db/migrate/074_create_algorithms.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/db/migrate/074_create_algorithms.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -1,24 +0,0 @@
-# myExperiment: db/migrate/074_create_algorithms.rb
-#
-# Copyright (c) 2009 University of Manchester and the University of Southampton.
-# See license.txt for details.
-
-class CreateAlgorithms < ActiveRecord::Migration
-  def self.up
-    create_table :algorithms do |t|
-      t.column :contributor_id, :integer
-      t.column :contributor_type, :string
-      t.column :title, :string
-      t.column :description, :text
-      t.column :description_html, :text
-      t.column :license, :string
-      t.column :url, :text
-      t.column :created_at, :datetime
-      t.column :updated_at, :datetime
-    end
-  end
-
-  def self.down
-    drop_table :algorithms
-  end
-end

Copied: branches/apace/db/migrate/074_modify_comments.rb (from rev 2143, trunk/db/migrate/074_modify_comments.rb) (0 => 2145)


--- branches/apace/db/migrate/074_modify_comments.rb	                        (rev 0)
+++ branches/apace/db/migrate/074_modify_comments.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -0,0 +1,9 @@
+class ModifyComments < ActiveRecord::Migration
+  def self.up
+    remove_column :comments, :title
+  end
+
+  def self.down
+    add_column :comments, :title, :string, :limit => 50, :default => ""
+  end
+end

Copied: branches/apace/db/migrate/075_create_algorithms.rb (from rev 2143, branches/apace/db/migrate/074_create_algorithms.rb) (0 => 2145)


--- branches/apace/db/migrate/075_create_algorithms.rb	                        (rev 0)
+++ branches/apace/db/migrate/075_create_algorithms.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -0,0 +1,24 @@
+# myExperiment: db/migrate/074_create_algorithms.rb
+#
+# Copyright (c) 2009 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class CreateAlgorithms < ActiveRecord::Migration
+  def self.up
+    create_table :algorithms do |t|
+      t.column :contributor_id, :integer
+      t.column :contributor_type, :string
+      t.column :title, :string
+      t.column :description, :text
+      t.column :description_html, :text
+      t.column :license, :string
+      t.column :url, :text
+      t.column :created_at, :datetime
+      t.column :updated_at, :datetime
+    end
+  end
+
+  def self.down
+    drop_table :algorithms
+  end
+end

Deleted: branches/apace/db/migrate/075_create_apps.rb (2144 => 2145)


--- branches/apace/db/migrate/075_create_apps.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/db/migrate/075_create_apps.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -1,23 +0,0 @@
-# myExperiment: db/migrate/075_create_apps.rb
-#
-# Copyright (c) 2009 University of Manchester and the University of Southampton.
-# See license.txt for details.
-
-class CreateApps < ActiveRecord::Migration
-  def self.up
-    create_table :apps do |t|
-      t.column :contributor_id, :integer
-      t.column :contributor_type, :string
-      t.column :title, :string
-      t.column :description, :text
-      t.column :description_html, :text
-      t.column :license, :string
-      t.column :created_at, :datetime
-      t.column :updated_at, :datetime
-    end
-  end
-
-  def self.down
-    drop_table :apps
-  end
-end

Deleted: branches/apace/db/migrate/076_create_algorithm_instances.rb (2144 => 2145)


--- branches/apace/db/migrate/076_create_algorithm_instances.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/db/migrate/076_create_algorithm_instances.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -1,18 +0,0 @@
-# myExperiment: db/migrate/076_create_algorithm_instances.rb
-#
-# Copyright (c) 2009 University of Manchester and the University of Southampton.
-# See license.txt for details.
-
-class CreateAlgorithmInstances < ActiveRecord::Migration
-  def self.up
-    create_table :algorithm_instances do |t|
-      t.column :algorithm_id, :integer
-      t.column :app_id, :integer
-    end
-  end
-
-  def self.down
-    drop_table :algorithm_instances
-  end
-end
-

Copied: branches/apace/db/migrate/076_create_apps.rb (from rev 2143, branches/apace/db/migrate/075_create_apps.rb) (0 => 2145)


--- branches/apace/db/migrate/076_create_apps.rb	                        (rev 0)
+++ branches/apace/db/migrate/076_create_apps.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -0,0 +1,23 @@
+# myExperiment: db/migrate/075_create_apps.rb
+#
+# Copyright (c) 2009 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class CreateApps < ActiveRecord::Migration
+  def self.up
+    create_table :apps do |t|
+      t.column :contributor_id, :integer
+      t.column :contributor_type, :string
+      t.column :title, :string
+      t.column :description, :text
+      t.column :description_html, :text
+      t.column :license, :string
+      t.column :created_at, :datetime
+      t.column :updated_at, :datetime
+    end
+  end
+
+  def self.down
+    drop_table :apps
+  end
+end

Copied: branches/apace/db/migrate/077_create_algorithm_instances.rb (from rev 2143, branches/apace/db/migrate/076_create_algorithm_instances.rb) (0 => 2145)


--- branches/apace/db/migrate/077_create_algorithm_instances.rb	                        (rev 0)
+++ branches/apace/db/migrate/077_create_algorithm_instances.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -0,0 +1,18 @@
+# myExperiment: db/migrate/076_create_algorithm_instances.rb
+#
+# Copyright (c) 2009 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class CreateAlgorithmInstances < ActiveRecord::Migration
+  def self.up
+    create_table :algorithm_instances do |t|
+      t.column :algorithm_id, :integer
+      t.column :app_id, :integer
+    end
+  end
+
+  def self.down
+    drop_table :algorithm_instances
+  end
+end
+

Modified: branches/apace/lib/acts_as_contributable.rb (2144 => 2145)


--- branches/apace/lib/acts_as_contributable.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/lib/acts_as_contributable.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -69,11 +69,6 @@
           return contribution.contributor.title if contribution.contributor.respond_to?('title')
         end
         
-        def label
-          return name  if respond_to?('name')
-          return title if respond_to?('title')
-        end
-
         # This is so that the updated_at time on the record tallies up with the
         # contributable
         def save_contributable_record

Copied: branches/apace/lib/acts_as_site_entity.rb (from rev 2144, trunk/lib/acts_as_site_entity.rb) (0 => 2145)


--- branches/apace/lib/acts_as_site_entity.rb	                        (rev 0)
+++ branches/apace/lib/acts_as_site_entity.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -0,0 +1,50 @@
+# myExperiment: lib/acts_as_site_entity.rb
+#
+# Copyright (c) 2009 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+module MyExperiment
+  module Acts #:nodoc:
+    module SiteEntity #:nodoc:
+      def self.included(mod)
+        mod.extend(ClassMethods)
+      end
+      
+      module ClassMethods
+        def acts_as_site_entity(args = {})
+
+          class_eval do
+            extend MyExperiment::Acts::SiteEntity::SingletonMethods
+          end
+          include MyExperiment::Acts::SiteEntity::InstanceMethods
+          
+          self.owner_text = args[:owner_text] ? args[:owner_text] : ''
+        end
+      end
+      
+      module SingletonMethods
+
+        def owner_text=(new_value)
+          @owner_text = new_value
+        end
+
+        def owner_text
+          @owner_text
+        end
+      end
+      
+      module InstanceMethods
+
+        def label
+          return name  if respond_to?('name')
+          return title if respond_to?('title')
+        end
+      end
+    end
+  end
+end
+
+ActiveRecord::Base.class_eval do
+  include MyExperiment::Acts::SiteEntity
+end
+

Modified: branches/apace/lib/authorization.rb (2144 => 2145)


--- branches/apace/lib/authorization.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/lib/authorization.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -46,6 +46,42 @@
     end
   end
 
+  def Authorization.is_authorized_for_type?(action, object_type, user, context)
+
+    # This method deals with cases where there is no instantiated object to
+    # authorize.  This is usually when thing area created.  The other normal
+    # CRUD actions (read, update and destroy) are handled by is_authorized?
+    # since there's an instantiatable object to authorize on.
+ 
+    # normalise user to nil if this is for an unauthenticated user
+    user = nil if user == 0
+
+    raise "object_type missing in is_authorized_for_type?" if object_type.nil?
+
+    # Workflow permissions
+    
+    if (object_type == 'Workflow') && (action == 'create')
+
+      # Workflows can only be created by authenticated users
+      return !user.nil?
+    end
+    
+    # Comment permissions
+    
+    if (object_type == 'Comment') && (action == 'create')
+
+      # Comments can only be created by authenticated users
+      return false if user.nil?
+
+      # Comments can only be added to things that a user can view
+      return Authorization.is_authorized?('view', nil, context, user) if context
+
+      return true
+    end
+    
+    return false
+  end
+
   # 1) action_name - name of the action that is about to happen with the "thing"
   # 2) thing_type - class name of the thing that needs to be authorized;
   #                 use NIL as a value of this parameter if an instance of the object to be authorized is supplied as "thing";
@@ -92,6 +128,7 @@
       # OR
       # -- Network instance
       # -- Experiment / Job / Runner / TavernaEnactor instance
+      # -- Comment
       # -- or any other object instance, for which we'll use the object itself to run .authorized?() on it
       thing_instance = thing
       thing_type = thing.class.name
@@ -122,7 +159,7 @@
     # this is required to get "policy_id" for policy-based aurhorized objects (like workflows / blobs / packs / contributions)
     # and to get objects themself for other object types (networks, experiments, jobs, tavernaenactors, runners)
     if (thing_contribution.nil? && ["Workflow", "Blob", "Pack", "Contribution"].include?(thing_type)) || 
-       (thing_instance.nil? && ["Network", "Experiment", "Job", "TavernaEnactor", "Runner"].include?(thing_type))
+       (thing_instance.nil? && ["Network", "Comment", "Experiment", "Job", "TavernaEnactor", "Runner"].include?(thing_type))
       
       found_thing = find_thing(thing_type, thing_id)
       
@@ -240,7 +277,20 @@
           else
             is_authorized = true
         end
-        
+      
+      when "Comment"
+        case action
+          when "destroy"
+            # only the user who posted the comment can delete it
+            is_authorized = Authorization.is_owner?(user_id, thing_instance)
+          when "view"
+            # user can view comment if they can view the item that this comment references 
+            is_authorized = Authorization.is_authorized?('view', thing_instance.commentable_type, thing_instance.commentable_id, user)
+          else
+            # 'edit' or any other actions are not allowed on comments
+            is_authorized = false
+        end
+      
       when "Experiment"
 
         user_instance = get_user(user_id) unless user_instance
@@ -278,13 +328,13 @@
 
   def Authorization.categorize_action(action_name)
     case action_name
-      when 'show', 'index', 'view', 'search', 'favourite', 'favourite_delete', 'comment', 'comment_delete', 'comments', 'comments_timeline', 'rate', 'tag',  'items', 'statistics', 'tag_suggestions'
+      when 'show', 'index', 'view', 'search', 'favourite', 'favourite_delete', 'comment', 'comment_delete', 'comments', 'comments_timeline', 'rate', 'tag',  'items', 'statistics', 'tag_suggestions', 'read', 'verify'
         action = ''
       when 'edit', 'new', 'create', 'update', 'new_version', 'create_version', 'destroy_version', 'edit_version', 'update_version', 'new_item', 'create_item', 'edit_item', 'update_item', 'quick_add', 'resolve_link', 'process_tag_suggestions'
         action = ''
-      when 'download', 'named_download', 'launch', 'submit_job'
+      when 'download', 'named_download', 'launch', 'submit_job', 'save_inputs', 'refresh_status', 'rerun', 'refresh_outputs', 'render_output', 'outputs_xml', 'outputs_package'
         action = ''
-      when 'destroy', 'destroy_item'
+      when 'destroy', 'delete', 'destroy_item'
         action = ''
       when 'execute'
         # action is available only(?) for runners at the moment;
@@ -314,6 +364,8 @@
           found_instance = Contribution.find(thing_id)
         when "Network"
           found_instance = Network.find(thing_id)
+        when "Comment"
+          found_instance = Comment.find(thing_id)
         when "Experiment"
           found_instance = Experiment.find(thing_id)
         when "Job"
@@ -334,14 +386,21 @@
 
 
   # checks if "user" is owner of the "thing"
-  def Authorization.is_owner?(user_id, thing_contribution)
+  def Authorization.is_owner?(user_id, thing)
     is_authorized = false
 
-    # if owner of the "thing" is the "user" then the "user" is authorized
-    if thing_contribution.contributor_type == 'User' && thing_contribution.contributor_id == user_id
-      is_authorized = true
-    elsif thing_contribution.contributor_type == 'Network'
-      is_authorized = is_network_admin?(user_id, thing_contribution.contributor_id)
+    case thing.class.name
+      when "Contribution"
+        # if owner of the "thing" is the "user" then the "user" is authorized
+        if thing.contributor_type == 'User' && thing.contributor_id == user_id
+          is_authorized = true
+        elsif thing.contributor_type == 'Network'
+          is_authorized = is_network_admin?(user_id, thing.contributor_id)
+        end
+      when "Comment"
+        is_authorized = (thing.user_id == user_id)
+      #else
+        # do nothing -- unknown "thing" types are not authorized by default 
     end
 
     return is_authorized

Modified: branches/apace/lib/rest.rb (2144 => 2145)


--- branches/apace/lib/rest.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/lib/rest.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -3,6 +3,7 @@
 # Copyright (c) 2007 University of Manchester and the University of Southampton.
 # See license.txt for details.
 
+require 'lib/conf'
 require 'lib/excel_xml'
 require 'xml/libxml'
 require 'uri'
@@ -17,17 +18,15 @@
                                'Foreign Accessor',
                                'List Element Name', 'List Element Accessor',
                                'Example', 'Versioned', 'Key type',
-                               'Limited to user' ] },
+                               'Limited to user', 'Permission' ] },
                 
     'REST'  => { :indices => [ 'URI', 'Method' ] }
   } )
 
 # Temporary removals
 
-TABLES["REST"][:data]["comment"].delete("PUT")
-TABLES["REST"][:data]["comment"].delete("DELETE")
-TABLES["REST"][:data]["workflow"].delete("PUT")
 TABLES["REST"][:data]["job"].delete("POST")
+TABLES["REST"][:data]["messages"].delete("GET")
 
 def rest_routes(map)
   TABLES['REST'][:data].keys.each do |uri|
@@ -37,8 +36,51 @@
   end
 end
 
-def bad_rest_request
-  render(:text => '400 Bad Request', :status => '400 Bad Request')
+def rest_response(code, args = {})
+
+  if code == 401
+    response.headers['WWW-Authenticate'] = "Basic realm=\"#{Conf.sitename} REST API\""
+  end
+
+  if code == 307
+    response.headers['Location'] = args[:location]
+  end
+
+  message = "Unknown Error"
+
+  case code
+    when 200: message = "OK"
+    when 307: message = "Temporary Redirect"
+    when 400: message = "Bad Request"
+    when 401: message = "Unauthorized"
+    when 403: message = "Forbidden"
+    when 404: message = "Not Found"
+    when 500: message = "Internal Server Error"
+  end
+
+  if (code >= 300) && (code < 400)
+
+    doc = ""
+
+  else 
+
+    error = XML::Node.new('error')
+    error["code"   ] = code.to_s
+    error["message"] = message
+
+    doc = XML::Document.new
+    doc.root = error
+
+    if args[:object]
+      args[:object].errors.full_messages.each do |message|
+        reason = XML::Node.new('reason')
+        reason << message
+        doc.root << reason
+      end
+    end
+  end
+
+  render(:xml => doc.to_s, :status => "#{code} #{message}")
 end
 
 def file_column_url(ob, field)
@@ -52,6 +94,8 @@
 
 def rest_get_element(ob, user, rest_entity, rest_attribute, query, elements)
 
+  # puts "rest_get_element: #{rest_entity} / #{rest_attribute}"
+
   model_data = TABLES['Model'][:data][rest_entity]
 
   i = model_data['REST Attribute'].index(rest_attribute)
@@ -70,6 +114,12 @@
     return nil if limited_ob != user
   end
 
+  permission = model_data['Permission'][i]
+
+  if permission
+    return nil if !Authorization.is_authorized?(permission, nil, ob, user)
+  end
+
   unless query['all_elements'] == 'yes'
     return nil if elements and not elements.index(model_data['REST Attribute'][i])
     return nil if not elements and model_data['Read by default'][i] == 'no'
@@ -159,9 +209,12 @@
           end
 
           if model_data['Foreign Accessor'][i]
-            resource_uri = eval("rest_resource_uri(ob.#{model_data['Foreign Accessor'][i]})")
-            attrs['resource'] = resource_uri if resource_uri
-            attrs['uri'] = eval("rest_access_uri(ob.#{model_data['Foreign Accessor'][i]})")
+            foreign_ob = eval("ob.#{model_data['Foreign Accessor'][i]}")
+            if foreign_ob != nil
+              resource_uri = rest_resource_uri(foreign_ob)
+              attrs['resource'] = resource_uri if resource_uri
+              attrs['uri'] = rest_access_uri(foreign_ob)
+            end
           end
         end
 
@@ -182,9 +235,9 @@
 def rest_get_request(ob, req_uri, user, uri, entity_name, query)
 
   if query['version']
-    return rest_error_response(400, 'Resource not versioned') unless ob.respond_to?('versions')
-    return rest_error_response(404, 'Resource version not found') if query['version'].to_i < 1
-    return rest_error_response(404, 'Resource version not found') if ob.versions[query['version'].to_i - 1].nil?
+    return rest_response(400) unless ob.respond_to?('versions')
+    return rest_response(404) if query['version'].to_i < 1
+    return rest_response(404) if ob.versions[query['version'].to_i - 1].nil?
   end
 
   elements = query['elements'] ? query['elements'].split(',') : nil
@@ -214,39 +267,17 @@
     root << data unless data.nil?
   end
 
-  doc
+  render(:xml => doc.to_s)
 end
 
-def rest_error_response(code, message, error_ob = nil)
+def rest_crud_request(req_uri, rules, user, query)
 
-  error = XML::Node.new('error')
-  error["code"   ] = code.to_s
-  error["message"] = message
-
-  doc = XML::Document.new
-  doc.root = error
-
-  if error_ob
-    error_ob.errors.full_messages.each do |message|
-      reason = XML::Node.new('reason')
-      reason << message
-      doc.root << reason
-    end
-  end
-
-  doc
-end
-
-def rest_crud_request(rules, user)
-
-  query = CGIMethods.parse_query_parameters(request.query_string)
-
   rest_name  = rules['REST Entity']
   model_name = rules['Model Entity']
 
   ob = eval(model_name.camelize).find_by_id(params[:id].to_i)
 
-  return rest_error_response(404, 'Resource not found') if ob.nil?
+  return rest_response(404) if ob.nil?
 
   perm_ob = ob
 
@@ -254,16 +285,63 @@
 
   case rules['Permission']
     when 'public'; # do nothing
-    when 'view'; return rest_error_response(403, 'Not authorized') if not Authorization.is_authorized?("show", nil, perm_ob, user)
-    when 'owner'; return rest_error_response(403, 'Not authorized') if logged_in?.nil? or object_owner(perm_ob) != user
+    when 'view';  return rest_response(401) if not Authorization.is_authorized?("show", nil, perm_ob, user)
+    when 'owner'; return rest_response(401) if logged_in?.nil? or object_owner(perm_ob) != user
   end
 
-  response.content_type = "application/xml"
   rest_get_request(ob, params[:uri], user, eval("rest_resource_uri(ob)"), rest_name, query)
 end
 
-def rest_index_request(rules, user, query)
+def find_all_paginated_auth(model, find_args, num, page, user)
 
+  def aux(model, find_args, num, page, user)
+
+    find_args = find_args.clone
+    find_args[:page] = { :size => num, :current => page }
+
+    results = eval(model).find(:all, find_args)
+
+    return nil if results.page > results.page_count
+
+    results.select do |result|
+      Authorization.is_authorized?('view', nil, result, user)
+    end
+  end
+
+  # 'upto' is the number of results needed to fulfil the request
+
+  upto = num * page
+
+  results = []
+  current_page = 1
+
+  # if this isn't the first page, do a single request to fetch all the pages
+  # up to possibly fulfil the request
+
+  if (page > 1)
+    results = aux(model, find_args, upto, 1, user)
+    current_page = page + 1
+  end
+
+  while (results.length < upto)
+
+    results_page = aux(model, find_args, num, current_page, user)
+
+    if results_page.nil?
+      break
+    else
+      results += results_page
+      current_page += 1
+    end
+  end
+
+  range = results[num * (page - 1)..(num * page) - 1]
+  range = [] if range.nil?
+  range
+end
+
+def rest_index_request(req_uri, rules, user, query)
+
   rest_name  = rules['REST Entity']
   model_name = rules['Model Entity']
 
@@ -280,8 +358,6 @@
   limit = max_limit if limit > max_limit
 
   page = 1 if page < 1
-  
-  part = { :size => limit, :current => page }
 
   if query['tag']
     tag = Tag.find_by_name(query['tag'])
@@ -306,33 +382,44 @@
 
     order = 'DESC' if query['order'] == 'reverse'
 
-    find_args = { :page => part, :order => "#{sort} #{order}" }
+    find_args = { :order => "#{sort} #{order}" }
 
     find_args[:conditions] = conditions if conditions
 
-    obs = eval(model_name.camelize).find(:all, find_args)
+    obs = find_all_paginated_auth(model_name.camelize, find_args, limit, page, user)
   end
 
-  # filter out ones they are not allowed to get
-  obs = (obs.select do |c| c.respond_to?('contribution') == false or Authorization.is_authorized?("index", nil, c, user) end)
-
-  produce_rest_list(rules, query, obs, rest_name.pluralize)
+  produce_rest_list(req_uri, rules, query, obs, rest_name.pluralize, user)
 end
 
-def produce_rest_list(rules, query, obs, tag)
+def produce_rest_list(req_uri, rules, query, obs, tag, user)
 
   root = XML::Node.new(tag)
 
   root['api-version'] = API_VERSION if query['api_version'] == 'yes'
 
-  obs.map do |ob|
-    root << rest_reference(ob, query)
+  elements = query['elements'] ? query['elements'].split(',') : nil
+
+  rest_entity = TABLES['REST'][:data][req_uri]['GET']['REST Entity']
+
+  obs.each do |ob|
+
+    el = rest_reference(ob, query, !elements.nil?)
+
+    if elements
+      TABLES['Model'][:data][rest_entity]['REST Attribute'].each do |rest_attribute|
+        data = "" user, rest_entity, rest_attribute, query, elements)
+        el << data unless data.nil?
+      end
+    end
+
+    root << el
   end
 
   doc = XML::Document.new
   doc.root = root
 
-  doc
+  render(:xml => doc.to_s)
 end
 
 def object_owner(ob)
@@ -373,6 +460,7 @@
 
     when 'Creditation';     return nil
     when 'Attribution';     return nil
+    when 'Tagging';         return nil
 
     when 'Workflow::Version'; return "#{rest_resource_uri(ob.workflow)}?version=#{ob.version}"
   end
@@ -406,6 +494,7 @@
     when 'Download';               return "#{base}/download.xml?id=#{ob.id}"
     when 'PackContributableEntry'; return "#{base}/internal-pack-item.xml?id=#{ob.id}"
     when 'PackRemoteEntry';        return "#{base}/external-pack-item.xml?id=#{ob.id}"
+    when 'Tagging';                return "#{base}/tagging.xml?id=#{ob.id}"
 
     when 'Creditation';     return nil
     when 'Attribution';     return nil
@@ -428,6 +517,7 @@
     when 'Citation';               return 'citation'
     when 'Announcement';           return 'announcement'
     when 'Tag';                    return 'tag'
+    when 'Tagging';                return 'tagging'
     when 'Pack';                   return 'pack'
     when 'Experiment';             return 'experiment'
     when 'Download';               return 'download'
@@ -451,6 +541,7 @@
     when 'Citation';               return ob.title
     when 'Announcement';           return ob.title
     when 'Tag';                    return ob.name
+    when 'Tagging';                return ob.tag.name
     when 'Pack';                   return ob.title
     when 'Experiment';             return ob.title
     when 'Download';               return ''
@@ -462,26 +553,24 @@
   return ''
 end
 
-def rest_reference(ob, query)
+def rest_reference(ob, query, skip_text = false)
 
-  tag  = rest_object_tag_text(ob)
-  text = rest_object_label_text(ob)
+  el = XML::Node.new(rest_object_tag_text(ob))
 
-  el = XML::Node.new(tag)
-
   resource_uri = rest_resource_uri(ob)
 
   el['resource'] = resource_uri if resource_uri
   el['uri'     ] = rest_access_uri(ob)
   el['version' ] = ob.version.to_s if ob.respond_to?('version')
-  el << text
 
+  el << rest_object_label_text(ob) if !skip_text
+
   el
 end
 
 def parse_resource_uri(str)
 
-  base_uri = URI.parse("#{request.protocol}#{request.host_with_port}/")
+  base_uri = URI.parse("#{Conf.base_uri}/")
   uri      = base_uri.merge(str)
   is_local = base_uri.host == uri.host and base_uri.port == uri.port
 
@@ -508,76 +597,155 @@
 
 end
 
-def get_rest_uri(rules, user, query)
+def resolve_resource_node(resource_node, user = nil, permission = nil)
 
-  return bad_rest_request if query['resource'].nil?
+  return nil if resource_node.nil?
 
-  obs = (obs.select do |c| c.respond_to?('contribution') == false or Authorization.is_authorized?("index", nil, c, user) end)
-  doc = REXML::Document.new("<?xml version=\"1.0\" encoding=\"UTF-8\"?><rest-uri/>")
-  "bing"
+  attr = resource_node.find_first('@resource')
+
+  return nil if attr.nil?
+
+  resource_uri = attr.value
+
+  resource_bits = parse_resource_uri(resource_uri)
+
+  return nil if resource_bits.nil?
+  
+  resource = eval(resource_bits[0]).find_by_id(resource_bits[1].to_i)
+
+  return nil if resource.nil?
+
+  if permission
+    return nil if !Authorization.is_authorized?(permission, nil, resource, user)
+  end
+
+  resource
 end
 
+def obtain_rest_resource(type, id, user, permission = nil)
+
+  resource = eval(type).find_by_id(id)
+
+  if resource.nil?
+    rest_response(404)
+    return nil
+  end
+
+  if permission
+    if !Authorization.is_authorized?(permission, nil, resource, user)
+      rest_response(401)
+      return nil
+    end
+  end
+
+  resource
+end
+
+def rest_access_redirect(req_uri, rules, user, query)
+
+  return rest_response(400) if query['resource'].nil?
+
+  bits = parse_resource_uri(query['resource'])
+
+  return rest_response(404) if bits.nil?
+
+  ob = eval(bits[0]).find_by_id(bits[1])
+
+  return rest_response(404) if ob.nil?
+
+  return rest_response(401) if !Authorization.is_authorized?('view', nil, ob, user)
+
+  rest_response(307, :location => rest_access_uri(ob))
+end
+
 def create_default_policy(user)
   Policy.new(:contributor => user, :name => 'auto', :update_mode => 6, :share_mode => 0)
 end
 
-def post_workflow(rules, user, query)
+def workflow_aux(action, req_uri, rules, user, query)
 
-  return rest_error_response(400, 'Bad Request') if user.nil?
-  return rest_error_response(400, 'Bad Request') if params["workflow"].nil?
+  # Obtain object
 
-  elements = params["workflow"]
+  case action
+    when 'create':
+      return rest_response(401) unless Authorization.is_authorized_for_type?('create', 'Workflow', user, nil)
+      ob = Workflow.new(:contributor => user)
+    when 'read', 'update', 'destroy':
+      ob = obtain_rest_resource('Workflow', query['id'], user, action)
+    else
+      raise "Invalid action '#{action}'"
+  end
 
-  # build the contributable
+  return if ob.nil? # appropriate rest response already given
 
-  workflow = Workflow.new(:contributor => user)
+  if action == "destroy"
 
-  content = Base64.decode64(elements["content"]) if elements["content"]
+    ob.destroy
 
-  workflow.title        = elements["title"]        if elements["title"]
-  workflow.body         = elements["description"]  if elements["description"]
-  workflow.license      = elements["license_type"] if elements["license_type"]
-  workflow.content_type = elements["content_type"] if elements["content_type"]
+  else
 
-  workflow.content_blob = ContentBlob.new(:data ="" content) if content
+    data = ""
 
-  # Handle the preview and svg images.  If there's a preview supplied, use it.
-  # Otherwise auto-generate one if we can.
+    title        = parse_element(data, :text,   '/workflow/title')
+    description  = parse_element(data, :text,   '/workflow/description')
+    license_type = parse_element(data, :text,   '/workflow/license-type')
+    content_type = parse_element(data, :text,   '/workflow/content-type')
+    content      = parse_element(data, :binary, '/workflow/content')
+    preview      = parse_element(data, :binary, '/workflow/preview')
 
-  if params["workflow"]["preview"]
+    # build the contributable
 
-    image = Tempfile.new('image')
-    image.write(Base64.decode64(params["workflow"]["preview"]))
-    image.rewind
+    ob.title        = title        if title
+    ob.body         = description  if description
+    ob.license      = license_type if license_type
+    ob.content_type = content_type if content_type
 
-    image.extend FileUpload
-    image.original_filename = 'preview'
-    
-    workflow.image = image
+    ob.content_blob = ContentBlob.new(:data ="" content) if content
 
-    image.close
+    # Handle the preview and svg images.  If there's a preview supplied, use
+    # it.  Otherwise auto-generate one if we can.
 
-  elsif content and workflow.processor_class and workflow.processor_class.can_generate_preview?
+    if preview
 
-    processor = workflow.processor_class.new(content)
-    workflow.image, workflow.svg = processor.get_preview_images
+      image = Tempfile.new('image')
+      image.write(preview)
+      image.rewind
 
-  end
+      image.extend FileUpload
+      image.original_filename = 'preview'
+      
+      ob.image = image
 
-  workflow.set_unique_name
+      image.close
+    end
 
-  if not workflow.save
-    return rest_error_response(400, 'Bad Request', workflow)
+    if not ob.save
+      return rest_response(400, :object => ob)
+    end
+
+    if ob.contribution.policy.nil?
+      ob.contribution.policy = create_default_policy(user)
+      ob.contribution.save
+    end
   end
 
-  workflow.contribution.policy = create_default_policy(user)
-  workflow.contribution.save
+  rest_get_request(ob, "workflow", user,
+      rest_resource_uri(ob), "workflow", { "id" => ob.id.to_s })
+end
 
-  rest_get_request(workflow, "workflow", user,
-      rest_resource_uri(workflow), "workflow", { "id" => workflow.id.to_s })
+def post_workflow(req_uri, rules, user, query)
+  workflow_aux('create', req_uri, rules, user, query)
 end
 
-# def post_job(rules, user, query)
+def put_workflow(req_uri, rules, user, query)
+  workflow_aux('update', req_uri, rules, user, query)
+end
+
+def delete_workflow(req_uri, rules, user, query)
+  workflow_aux('destroy', req_uri, rules, user, query)
+end
+
+# def post_job(req_uri, rules, user, query)
 #
 #   title       = params["job"]["title"]
 #   description = params["job"]["description"]
@@ -586,20 +754,20 @@
 #   runner_bits     = parse_resource_uri(params["job"]["runner"])
 #   runnable_bits   = parse_resource_uri(params["job"]["runnable"])
 #
-#   return rest_error_response(400, 'Bad Request') if title.nil?
-#   return rest_error_response(400, 'Bad Request') if description.nil?
+#   return rest_response(400) if title.nil?
+#   return rest_response(400) if description.nil?
 #
-#   return rest_error_response(400, 'Bad Request') if experiment_bits.nil? or experiment_bits[0] != 'Experiment'
-#   return rest_error_response(400, 'Bad Request') if runner_bits.nil?     or runner_bits[0]     != 'Runner'
-#   return rest_error_response(400, 'Bad Request') if runnable_bits.nil?   or runnable_bits[0]   != 'Workflow'
+#   return rest_response(400) if experiment_bits.nil? or experiment_bits[0] != 'Experiment'
+#   return rest_response(400) if runner_bits.nil?     or runner_bits[0]     != 'Runner'
+#   return rest_response(400) if runnable_bits.nil?   or runnable_bits[0]   != 'Workflow'
 #
 #   experiment = Experiment.find_by_id(experiment_bits[1].to_i)
 #   runner     = TavernaEnactor.find_by_id(runner_bits[1].to_i)
 #   runnable   = Workflow.find_by_id(runnable_bits[1].to_i)
 #
-#   return rest_error_response(400, 'Bad Request') if experiment.nil? or not Authorization.is_authorized?('edit', nil, experiment, user)
-#   return rest_error_response(400, 'Bad Request') if runner.nil?     or not Authorization.is_authorized?('download', nil, runner, user)
-#   return rest_error_response(400, 'Bad Request') if runnable.nil?   or not Authorization.is_authorized?('view', nil, runnable, user)
+#   return rest_response(400) if experiment.nil? or not Authorization.is_authorized?('edit', nil, experiment, user)
+#   return rest_response(400) if runner.nil?     or not Authorization.is_authorized?('download', nil, runner, user)
+#   return rest_response(400) if runnable.nil?   or not Authorization.is_authorized?('view', nil, runnable, user)
 #
 #   puts "#{params[:job]}"
 #
@@ -613,13 +781,13 @@
 #
 #   success = job.submit_and_run!
 #
-#   return rest_error_response(200, 'Failed to submit job') if not success
+#   return rest_response(500) if not success
 #
 #   return "<yes/>"
 #
 # end
 
-def search(rules, user, query)
+def search(req_uri, rules, user, query)
 
   search_query = query['query']
 
@@ -654,10 +822,11 @@
 
   doc = XML::Document.new
   doc.root = root
-  doc
+
+  render(:xml => doc.to_s)
 end
 
-def user_count(rules, user, query)
+def user_count(req_uri, rules, user, query)
   
   users = User.find(:all).select do |user| user.activated? end
 
@@ -667,21 +836,37 @@
   doc = XML::Document.new
   doc.root = root
 
-  doc
+  render(:xml => doc.to_s)
 end
 
-def group_count(rules, user, query)
+def group_count(req_uri, rules, user, query)
   
   root = XML::Node.new('group-count')
   root << Network.count.to_s
 
   doc = XML::Document.new
   doc.root = root
-  doc
+
+  render(:xml => doc.to_s)
 end
 
-def pack_count(rules, user, query)
+def workflow_count(req_uri, rules, user, query)
   
+  workflows = Workflow.find(:all).select do |w|
+    Authorization.is_authorized?('view', nil, w, user)
+  end
+
+  root = XML::Node.new('workflow-count')
+  root << workflows.length.to_s
+
+  doc = XML::Document.new
+  doc.root = root
+
+  render(:xml => doc.to_s)
+end
+
+def pack_count(req_uri, rules, user, query)
+  
   packs = Pack.find(:all).select do |p|
     Authorization.is_authorized?('view', nil, p, user)
   end
@@ -691,12 +876,13 @@
 
   doc = XML::Document.new
   doc.root = root
-  doc
+
+  render(:xml => doc.to_s)
 end
 
-def get_tagged(rules, user, query)
+def get_tagged(req_uri, rules, user, query)
 
-  return rest_error_response(400, 'Bad Request') if query['tag'].nil?
+  return rest_response(400) if query['tag'].nil?
 
   tag = Tag.find_by_name(query['tag'])
 
@@ -705,10 +891,10 @@
   # filter out ones they are not allowed to get
   obs = (obs.select do |c| c.respond_to?('contribution') == false or Authorization.is_authorized?("index", nil, c, user) end)
 
-  produce_rest_list(rules, query, obs, 'tagged')
+  produce_rest_list("tagged", rules, query, obs, 'tagged', user)
 end
 
-def tag_cloud(rules, user, query)
+def tag_cloud(req_uri, rules, user, query)
 
   num  = 25
   type = nil
@@ -743,53 +929,89 @@
     root << tag_node
   end
 
-  doc
+  render(:xml => doc.to_s)
 end
 
-def post_comment(rules, user, query)
+def whoami_redirect(req_uri, rules, user, query)
+  if user.class == User
+    rest_response(307, :location => rest_access_uri(user))
+  else
+    rest_response(401)
+  end
+end
 
-  title    = params[:comment][:title]
-  text     = params[:comment][:comment]
-  resource = params[:comment][:resource]
+def parse_element(doc, kind, query)
+  case kind
+    when :text
+      el = doc.find_first("#{query}/text()")
+      return el.to_s if el
+    when :binary
+      el = doc.find_first("#{query}/text()")
+      return Base64::decode64(el.to_s) if el
+    when :resource
+      return resolve_resource_node(doc.find_first(query))
+  end
+end
 
-  title = '' if title.nil?
+# Comments
 
-  resource_bits = parse_resource_uri(params["comment"]["resource"])
+def comment_aux(action, req_uri, rules, user, query)
 
-  return rest_error_response(400, 'Bad Request') if user.nil?
-  return rest_error_response(400, 'Bad Request') if text.nil? or text.length.zero?
-  return rest_error_response(400, 'Bad Request') if resource_bits.nil?
+  # Obtain object
 
-  return rest_error_response(400, 'Bad Request') unless ['Blob', 'Network', 'Pack', 'Workflow'].include?(resource_bits[0])
+  case action
+    when 'create':
+      return rest_response(401) unless Authorization.is_authorized_for_type?('create', 'Comment', user, nil)
 
-  resource = eval(resource_bits[0]).find_by_id(resource_bits[1].to_i)
+      ob = Comment.new(:user => user)
+    when 'read', 'update', 'destroy':
+      ob = obtain_rest_resource('Comment', query['id'], user, action)
+    else
+      raise "Invalid action '#{action}'"
+  end
 
-  comment = Comment.create(:user => user, :comment => text)
-  resource.comments << comment
+  return if ob.nil? # appropriate rest response already given
 
-  rest_get_request(comment, "comment", user, rest_resource_uri(comment), "comment", { "id" => comment.id.to_s })
+  if action == "destroy"
+
+    ob.destroy
+
+  else
+
+    data = ""
+
+    comment = parse_element(data, :text,     '/comment/comment')
+    subject = parse_element(data, :resource, '/comment/subject')
+
+    ob.comment = comment if comment
+
+    if subject
+      return rest_response(400) unless [Blob, Network, Pack, Workflow].include?(subject.class)
+      return rest_response(401) unless Authorization.is_authorized_for_type?(action, 'Comment', user, subject)
+      ob.commentable = subject
+    end
+
+    return rest_response(400, :object => ob) unless ob.save
+  end
+
+  rest_get_request(ob, "comment", user, rest_resource_uri(ob), "comment", { "id" => ob.id.to_s })
 end
 
-# def put_comment(rules, user, query)
-# end
-#
-# def delete_comment(rules, user, query)
-#
-#   return rest_error_response(400, 'Bad Request') if query['id'].nil?
-#
-#   resource = Comment.find_by_id(query['id'])
-#
-#   return rest_error_response(404, 'Resource Not Found') if resource.nil?
-#
-#   FIXME: The following respond_to? would not work anymore
-#
-#   if resource.respond_to?('authorized?')
-#     return rest_error_response(403, 'Not Authorized') if not Authorization.is_authorized?('edit', nil, resource, user)
-#   end
-#
-# end
+def post_comment(req_uri, rules, user, query)
+  comment_aux('create', req_uri, rules, user, query)
+end
 
-def rest_call_request(rules, user, query)
-  eval("#{rules['Function']}(rules, user, query)")
+def put_comment(req_uri, rules, user, query)
+  comment_aux('update', req_uri, rules, user, query)
 end
 
+def delete_comment(req_uri, rules, user, query)
+  comment_aux('destroy', req_uri, rules, user, query)
+end
+
+# Call dispatcher
+
+def rest_call_request(req_uri, rules, user, query)
+  eval("#{rules['Function']}(req_uri, rules, user, query)")
+end
+

Modified: branches/apace/lib/workflow_processors/interface.rb (2144 => 2145)


--- branches/apace/lib/workflow_processors/interface.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/lib/workflow_processors/interface.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -46,10 +46,14 @@
       false
     end
     
-    def self.can_generate_preview?
+    def self.can_generate_preview_image?
       false
     end
     
+    def self.can_generate_preview_svg?
+      false
+    end
+    
     # End Class Methods
 
 
@@ -74,10 +78,14 @@
       nil
     end
     
-    def get_preview_images
+    def get_preview_image
       nil
     end
     
+    def get_preview_svg
+      nil
+    end
+    
     def get_workflow_model_object
       nil
     end

Modified: branches/apace/lib/workflow_processors/taverna2_beta.rb (2144 => 2145)


--- branches/apace/lib/workflow_processors/taverna2_beta.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/lib/workflow_processors/taverna2_beta.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -45,10 +45,14 @@
       false
     end
     
-    def self.can_generate_preview?
+    def self.can_generate_preview_image?
       false
     end
     
+    def self.can_generate_preview_svg?
+      false
+    end
+    
     # End Class Methods
     
     
@@ -69,4 +73,4 @@
     
     # End Instance Methods
   end
-end
\ No newline at end of file
+end

Modified: branches/apace/lib/workflow_processors/taverna_scufl.rb (2144 => 2145)


--- branches/apace/lib/workflow_processors/taverna_scufl.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/lib/workflow_processors/taverna_scufl.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -63,10 +63,14 @@
       true
     end
     
-    def self.can_generate_preview?
+    def self.can_generate_preview_image?
       true
     end
     
+    def self.can_generate_preview_svg?
+      true
+    end
+    
     # End Class Methods
     
     
@@ -94,30 +98,42 @@
       return @scufl_model.description.description
     end
     
-    def get_preview_images
-      return nil if @scufl_model.nil?
+    def get_preview_image
+      return nil if @scufl_model.nil? || RUBY_PLATFORM =~ /mswin32/
+
+      title = @scufl_model.description.title.blank? ? "untitled" : @scufl_model.description.title
+      filename = title.gsub(/[^\w\.\-]/,'_').downcase
       
-      if RUBY_PLATFORM =~ /mswin32/
-        return nil
-      else
-        title = @scufl_model.description.title.blank? ? "untitled" : @scufl_model.description.title
-        filename = title.gsub(/[^\w\.\-]/,'_').downcase
-        
-        i = Tempfile.new("image")
-        Scufl::Dot.new.write_dot(i, @scufl_model)
-        i.close(false)
-        img = StringIO.new(`dot -Tpng #{i.path}`)
-        svg = StringIO.new(`dot -Tsvg #{i.path}`)
-        img.extend FileUpload
-        img.original_filename = "#{filename}.png"
-        img.content_type = "image/png"
-        svg.extend FileUpload
-        svg.original_filename = "#{filename}.svg"
-        svg.content_type = "image/svg+xml"
-        return [img, svg]
-      end
+      i = Tempfile.new("image")
+      Scufl::Dot.new.write_dot(i, @scufl_model)
+      i.close(false)
+
+      img = StringIO.new(`dot -Tpng #{i.path}`)
+      img.extend FileUpload
+      img.original_filename = "#{filename}.png"
+      img.content_type = "image/png"
+
+      img
     end
-    
+
+    def get_preview_svg
+      return nil if @scufl_model.nil? || RUBY_PLATFORM =~ /mswin32/
+
+      title = @scufl_model.description.title.blank? ? "untitled" : @scufl_model.description.title
+      filename = title.gsub(/[^\w\.\-]/,'_').downcase
+      
+      i = Tempfile.new("image")
+      Scufl::Dot.new.write_dot(i, @scufl_model)
+      i.close(false)
+
+      svg = StringIO.new(`dot -Tsvg #{i.path}`)
+      svg.extend FileUpload
+      svg.original_filename = "#{filename}.svg"
+      svg.content_type = "image/svg+xml"
+
+      svg
+    end
+
     def get_workflow_model_object
       @scufl_model
     end

Modified: branches/apace/lib/workflow_processors/trident_opc.rb (2144 => 2145)


--- branches/apace/lib/workflow_processors/trident_opc.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/lib/workflow_processors/trident_opc.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -45,10 +45,14 @@
       false
     end
     
-    def self.can_generate_preview?
+    def self.can_generate_preview_image?
       false
     end
     
+    def self.can_generate_preview_svg?
+      false
+    end
+    
     # End Class Methods
     
     
@@ -69,4 +73,4 @@
     
     # End Instance Methods
   end
-end
\ No newline at end of file
+end

Modified: branches/apace/lib/workflow_processors/trident_xoml.rb (2144 => 2145)


--- branches/apace/lib/workflow_processors/trident_xoml.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/lib/workflow_processors/trident_xoml.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -45,10 +45,14 @@
       false
     end
     
-    def self.can_generate_preview?
+    def self.can_generate_preview_image?
       false
     end
     
+    def self.can_generate_preview_svg?
+      false
+    end
+    
     # End Class Methods
     
     
@@ -69,4 +73,4 @@
     
     # End Instance Methods
   end
-end
\ No newline at end of file
+end

Modified: branches/apace/vendor/plugins/acts_as_taggable_redux/lib/tagging.rb (2144 => 2145)


--- branches/apace/vendor/plugins/acts_as_taggable_redux/lib/tagging.rb	2009-03-29 13:50:59 UTC (rev 2144)
+++ branches/apace/vendor/plugins/acts_as_taggable_redux/lib/tagging.rb	2009-03-29 13:56:05 UTC (rev 2145)
@@ -10,4 +10,8 @@
               :order => "created_at DESC",
               :limit => limit)
   end
-end
\ No newline at end of file
+
+  def label
+    return tag.name if tag
+  end
+end

reply via email to

[Prev in Thread] Current Thread [Next in Thread]