[ARVADOS] updated: 55315b668b8fa04572a44fd7db6598478f54130a

Git user git at public.curoverse.com
Mon Jun 5 18:58:17 EDT 2017


Summary of changes:
 apps/workbench/app/assets/images/trash-icon.png    | Bin 0 -> 18381 bytes
 .../app/assets/javascripts/selection.js.erb        |   4 +
 .../app/controllers/trash_items_controller.rb      |  67 ++++++++++++++++
 apps/workbench/app/models/arvados_base.rb          |   4 +
 apps/workbench/app/models/arvados_resource_list.rb |   6 ++
 apps/workbench/app/models/collection.rb            |   3 +
 .../app/views/application/_breadcrumbs.html.erb    |   7 ++
 .../trash_items/_create_new_object_button.html.erb |   1 +
 .../app/views/trash_items/_show_recent.html.erb    |  54 +++++++++++++
 .../views/trash_items/_show_trash_rows.html.erb    |  32 ++++++++
 .../app/views/trash_items/_untrash_item.html.erb   |   7 ++
 .../workbench/app/views/trash_items/index.html.erb |   1 +
 .../untrash_items.js.erb}                          |   3 +-
 .../views/work_units/_show_all_processes.html.erb  |   2 +-
 apps/workbench/config/routes.rb                    |   5 ++
 apps/workbench/test/integration/trash_test.rb      |  87 +++++++++++++++++++++
 .../arvados/v1/collections_controller.rb           |  13 ++-
 services/api/config/routes.rb                      |   1 +
 services/api/test/fixtures/collections.yml         |   2 +-
 .../arvados/v1/collections_controller_test.rb      |  18 +++++
 20 files changed, 311 insertions(+), 6 deletions(-)
 create mode 100644 apps/workbench/app/assets/images/trash-icon.png
 create mode 100644 apps/workbench/app/controllers/trash_items_controller.rb
 create mode 100644 apps/workbench/app/views/trash_items/_create_new_object_button.html.erb
 create mode 100644 apps/workbench/app/views/trash_items/_show_recent.html.erb
 create mode 100644 apps/workbench/app/views/trash_items/_show_trash_rows.html.erb
 create mode 100644 apps/workbench/app/views/trash_items/_untrash_item.html.erb
 create mode 100644 apps/workbench/app/views/trash_items/index.html.erb
 copy apps/workbench/app/views/{projects/remove_items.js.erb => trash_items/untrash_items.js.erb} (58%)
 create mode 100644 apps/workbench/test/integration/trash_test.rb

       via  55315b668b8fa04572a44fd7db6598478f54130a (commit)
       via  0a6adabd77bf483186a29a7cffef38866a5ee8e0 (commit)
       via  0560a5812377315a92c8a4e9b41a68677832ea65 (commit)
       via  695a100d4bd3bf4f5534c7e489c118c2917bf35a (commit)
      from  dcd20d41fde4216345a20ddfa950b40de74b85b5 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 55315b668b8fa04572a44fd7db6598478f54130a
Merge: dcd20d4 0a6adab
Author: radhika <radhika at curoverse.com>
Date:   Mon Jun 5 18:58:03 2017 -0400

    closes #9587
    Merge branch '9587-trash-page'


commit 0a6adabd77bf483186a29a7cffef38866a5ee8e0
Author: radhika <radhika at curoverse.com>
Date:   Fri Jun 2 17:31:04 2017 -0400

    9587: sort order issue

diff --git a/apps/workbench/app/controllers/trash_items_controller.rb b/apps/workbench/app/controllers/trash_items_controller.rb
index bbc0b26..31581bb 100644
--- a/apps/workbench/app/controllers/trash_items_controller.rb
+++ b/apps/workbench/app/controllers/trash_items_controller.rb
@@ -3,6 +3,10 @@ class TrashItemsController < ApplicationController
     Collection
   end
 
+  def show_pane_list
+    %w(Recent)
+  end
+
   def find_objects_for_index
     # If it's not the index rows partial display, just return
     # The /index request will again be invoked to display the
@@ -12,6 +16,7 @@ class TrashItemsController < ApplicationController
     trashed_items
 
     if @objects.any?
+      @objects = @objects.sort_by { |obj| obj.created_at }.reverse
       @next_page_filters = next_page_filters('<=')
       @next_page_href = url_for(partial: :trash_rows,
                                 filters: @next_page_filters.to_json)
diff --git a/apps/workbench/app/views/trash_items/_create_new_object_button.html.erb b/apps/workbench/app/views/trash_items/_create_new_object_button.html.erb
new file mode 100644
index 0000000..2ba9e1a
--- /dev/null
+++ b/apps/workbench/app/views/trash_items/_create_new_object_button.html.erb
@@ -0,0 +1 @@
+<%# There is no such thing %>
diff --git a/apps/workbench/app/views/trash_items/_show_trash_items.html.erb b/apps/workbench/app/views/trash_items/_show_recent.html.erb
similarity index 95%
rename from apps/workbench/app/views/trash_items/_show_trash_items.html.erb
rename to apps/workbench/app/views/trash_items/_show_recent.html.erb
index 153d7d0..6776e7a 100644
--- a/apps/workbench/app/views/trash_items/_show_trash_items.html.erb
+++ b/apps/workbench/app/views/trash_items/_show_recent.html.erb
@@ -27,10 +27,10 @@
       <colgroup>
         <col width="5%" />
         <col width="20%" />
+        <col width="15%" />
+        <col width="15%" />
         <col width="10%" />
-        <col width="10%" />
-        <col width="10%" />
-        <col width="40%" />
+        <col width="30%" />
         <col width="5%" />
       </colgroup>
 
diff --git a/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb b/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb
index d617d8d..8db21ed 100644
--- a/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb
+++ b/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb
@@ -1,4 +1,4 @@
-<% @objects.sort_by { |obj| obj.created_at }.reverse.each do |obj| %>
+<% @objects.each do |obj| %>
     <tr data-object-uuid="<%= obj.uuid %>" data-kind="<%= obj.kind %>" >
       <td>
         <% if obj.editable? %>
@@ -8,9 +8,9 @@
       <td>
         <%= if !obj.name.blank? then obj.name else obj.uuid end %>
       <td>
-        <%= obj.created_at.to_s if obj.created_at %>
+        <%= render_localized_date(obj.created_at) if obj.created_at %>
       <td>
-        <%= obj.trash_at.to_s if obj.trash_at %>
+        <%= render_localized_date(obj.trash_at) if obj.trash_at %>
       </td>
       <td>
         <%= link_to_if_arvados_object obj.owner_uuid, friendly_name: true %>
@@ -22,7 +22,7 @@
           <%= file_path %><br />
         <% end %>
         <% if obj.files.length > 3 %>
-          ⋮
+          <%= "(#{obj.files.length-3} more files)" %>
         <% end %>
       </td>
       <td>
diff --git a/apps/workbench/app/views/trash_items/_untrash_item.html.erb b/apps/workbench/app/views/trash_items/_untrash_item.html.erb
index 2ba9dc9..8732531 100644
--- a/apps/workbench/app/views/trash_items/_untrash_item.html.erb
+++ b/apps/workbench/app/views/trash_items/_untrash_item.html.erb
@@ -1,7 +1,7 @@
 <% if object.editable? %>
-  <% msg = "Un-trash '" + if !object.name.blank? then object.name else object.uuid end + "'?" %>
+  <% msg = "Untrash '" + if !object.name.blank? then object.name else object.uuid end + "'?" %>
   <%= link_to({action: 'untrash_items', selection: [object.uuid]}, remote: true, method: :post,
-      data: {confirm: msg}) do %>
+      title: "Untrash", data: {confirm: msg}) do %>
     <i class="fa fa-fw fa-recycle"></i>
   <% end %>
 <% end %>
diff --git a/apps/workbench/app/views/trash_items/index.html.erb b/apps/workbench/app/views/trash_items/index.html.erb
index bd55a3f..5f5bc83 100644
--- a/apps/workbench/app/views/trash_items/index.html.erb
+++ b/apps/workbench/app/views/trash_items/index.html.erb
@@ -1 +1 @@
-<%= render partial: 'show_trash_items' %>
+<%= render file: 'application/index.html.erb', locals: local_assigns %>
diff --git a/services/api/test/functional/arvados/v1/collections_controller_test.rb b/services/api/test/functional/arvados/v1/collections_controller_test.rb
index a31ad8a..7618985 100644
--- a/services/api/test/functional/arvados/v1/collections_controller_test.rb
+++ b/services/api/test/functional/arvados/v1/collections_controller_test.rb
@@ -1024,4 +1024,22 @@ EOS
       assert_operator c.delete_at, :>=, time_before_trashing + Rails.configuration.default_trash_lifetime
     end
   end
+
+  test 'untrash a trashed collection' do
+    authorize_with :active
+    post :untrash, {
+      id: collections(:expired_collection).uuid,
+    }
+    assert_response 200
+    assert_equal false, json_response['is_trashed']
+    assert_nil json_response['trash_at']
+  end
+
+  test 'untrash error on not trashed collection' do
+    authorize_with :active
+    post :untrash, {
+      id: collections(:collection_owned_by_active).uuid,
+    }
+    assert_response 422
+  end
 end

commit 0560a5812377315a92c8a4e9b41a68677832ea65
Author: radhika <radhika at curoverse.com>
Date:   Wed May 31 15:03:53 2017 -0400

    9587: expose untrash api

diff --git a/apps/workbench/app/assets/images/trash-icon.png b/apps/workbench/app/assets/images/trash-icon.png
new file mode 100644
index 0000000..5c26c24
Binary files /dev/null and b/apps/workbench/app/assets/images/trash-icon.png differ
diff --git a/apps/workbench/app/controllers/trash_items_controller.rb b/apps/workbench/app/controllers/trash_items_controller.rb
index 5190474..bbc0b26 100644
--- a/apps/workbench/app/controllers/trash_items_controller.rb
+++ b/apps/workbench/app/controllers/trash_items_controller.rb
@@ -15,7 +15,6 @@ class TrashItemsController < ApplicationController
       @next_page_filters = next_page_filters('<=')
       @next_page_href = url_for(partial: :trash_rows,
                                 filters: @next_page_filters.to_json)
-      preload_links_for_objects(@objects.to_a)
     else
       @next_page_href = nil
     end
@@ -37,39 +36,13 @@ class TrashItemsController < ApplicationController
 
     if params[:search].andand.length.andand > 0
       tags = Link.where(any: ['contains', params[:search]])
-      @objects = (base_search.limit(limit).offset(offset).where(uuid: tags.collect(&:head_uuid)) |
-                      base_search.where(any: ['contains', params[:search]])).
-        uniq { |c| c.uuid }
+      base_search = base_search.limit(limit).offset(offset)
+      @objects = (base_search.where(uuid: tags.collect(&:head_uuid)) |
+                  base_search.where(any: ['contains', params[:search]])).
+                  uniq { |c| c.uuid }
     else
       @objects = base_search.limit(limit).offset(offset)
     end
-
-    @links = Link.where(head_uuid: @objects.collect(&:uuid))
-    @collection_info = {}
-    @objects.each do |c|
-      @collection_info[c.uuid] = {
-        tag_links: [],
-        wanted: false,
-        wanted_by_me: false,
-        provenance: [],
-        links: []
-      }
-    end
-    @links.each do |link|
-      @collection_info[link.head_uuid] ||= {}
-      info = @collection_info[link.head_uuid]
-      case link.link_class
-      when 'tag'
-        info[:tag_links] << link
-      when 'resources'
-        info[:wanted] = true
-        info[:wanted_by_me] ||= link.tail_uuid == current_user.uuid
-      when 'provenance'
-        info[:provenance] << link.name
-      end
-      info[:links] << link
-    end
-    @request_url = request.url
   end
 
   def untrash_items
@@ -77,9 +50,9 @@ class TrashItemsController < ApplicationController
 
     updates = {trash_at: nil}
 
-    params[:selection].collect { |uuid| ArvadosBase.find uuid }.each do |item|
-      item.update_attributes updates
-      @untrashed_uuids << item.uuid
+    Collection.include_trash(1).where(uuid: params[:selection]).each do |c|
+      c.untrash
+      @untrashed_uuids << c.uuid
     end
 
     respond_to do |format|
diff --git a/apps/workbench/app/models/collection.rb b/apps/workbench/app/models/collection.rb
index ea81ad8..305ea01 100644
--- a/apps/workbench/app/models/collection.rb
+++ b/apps/workbench/app/models/collection.rb
@@ -98,4 +98,7 @@ class Collection < ArvadosBase
     [ 'description' ]
   end
 
+  def untrash
+    arvados_api_client.api(self.class, "/#{self.uuid}/untrash", {})
+  end
 end
diff --git a/apps/workbench/app/views/application/_breadcrumbs.html.erb b/apps/workbench/app/views/application/_breadcrumbs.html.erb
index 489dbf3..2db43ed 100644
--- a/apps/workbench/app/views/application/_breadcrumbs.html.erb
+++ b/apps/workbench/app/views/application/_breadcrumbs.html.erb
@@ -69,7 +69,7 @@
         <ul class="nav navbar-nav navbar-right">
           <li>
             <a href="/trash">
-              <i class="fa fa-lg fa-fw fa-trash-o"></i>
+              <%= image_tag("trash-icon.png", size: "19x19" ) %> Trash
             </a>
           </li>
         </ul>
diff --git a/apps/workbench/app/views/trash_items/_show_trash_items.html.erb b/apps/workbench/app/views/trash_items/_show_trash_items.html.erb
index dc615fc..153d7d0 100644
--- a/apps/workbench/app/views/trash_items/_show_trash_items.html.erb
+++ b/apps/workbench/app/views/trash_items/_show_trash_items.html.erb
@@ -1,5 +1,5 @@
-<div class="container selection-action-container">
-  <div class="col-md-6 pull-left">
+<div class="container selection-action-container" style="width: 100%">
+  <div class="col-md-2 pull-left">
     <div class="btn-group btn-group-sm">
       <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">Selection... <span class="caret"></span></button>
       <ul class="dropdown-menu" role="menu">
@@ -15,7 +15,7 @@
       </ul>
     </div>
   </div>
-  <div class="col-md-6 pull-right">
+  <div class="col-md-4 pull-right">
     <input type="text" class="form-control filterable-control recent-trash-items"
            placeholder="Search trash"
            data-filterable-target="#recent-trash-items"
@@ -26,11 +26,11 @@
     <table id="trash-index" class="topalign table table-condensed table-fixedlayout">
       <colgroup>
         <col width="5%" />
-        <col width="15%" />
+        <col width="20%" />
         <col width="10%" />
         <col width="10%" />
-        <col width="30%" />
-        <col width="25%" />
+        <col width="10%" />
+        <col width="40%" />
         <col width="5%" />
       </colgroup>
 
@@ -40,8 +40,8 @@
           <th>Name</th>
           <th>Created at</th>
           <th>Trashed at</th>
+          <th>Owner</th>
           <th>Contents</th>
-          <th>Tags</th>
           <th></th>
         </tr>
       </thead>
diff --git a/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb b/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb
index 73b89dd..d617d8d 100644
--- a/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb
+++ b/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb
@@ -1,38 +1,31 @@
 <% @objects.sort_by { |obj| obj.created_at }.reverse.each do |obj| %>
     <tr data-object-uuid="<%= obj.uuid %>" data-kind="<%= obj.kind %>" >
       <td>
-        <%= check_box_tag 'uuids[]', obj.uuid, false, :class => 'persistent-selection' %>
+        <% if obj.editable? %>
+          <%= check_box_tag 'uuids[]', obj.uuid, false, :class => 'persistent-selection' %>
+        <% end %>
       </td>
       <td>
-        <%= link_to_if_arvados_object obj, friendly_name: true %>
+        <%= if !obj.name.blank? then obj.name else obj.uuid end %>
       <td>
         <%= obj.created_at.to_s if obj.created_at %>
       <td>
         <%= obj.trash_at.to_s if obj.trash_at %>
       </td>
       <td>
-        <% i = 0 %>
-        <% while i < 3 and i < obj.files.length %>
+        <%= link_to_if_arvados_object obj.owner_uuid, friendly_name: true %>
+      </td>
+      <td>
+        <% for i in (0..[2, obj.files.length-1].min) %>
           <% file = obj.files[i] %>
           <% file_path = "#{file[0]}/#{file[1]}" %>
-          <%= link_to file[1], {controller: 'collections', action: 'show_file', uuid: obj.uuid, file: file_path, size: file[2], disposition: 'inline'}, {title: 'View in browser'} %><br />
-          <% i += 1 %>
+          <%= file_path %><br />
         <% end %>
-        <% if i < obj.files.length %>
+        <% if obj.files.length > 3 %>
           ⋮
         <% end %>
       </td>
       <td>
-        <span class="tag-container">
-        <% if @collection_info[obj.uuid] %>
-          <% @collection_info[obj.uuid][:tag_links].each do |tag_link| %>
-            <span class="label label-info" data-tag-link-uuid="<%= tag_link.uuid %>"><%= tag_link.name %>
-            </span> 
-          <% end %>
-        <% end %>
-        </span>
-      </td>
-      <td>
         <%= render partial: 'untrash_item', locals: {object:obj} %>
       </td>
     </tr>
diff --git a/apps/workbench/app/views/trash_items/_untrash_item.html.erb b/apps/workbench/app/views/trash_items/_untrash_item.html.erb
index a40a4be..2ba9dc9 100644
--- a/apps/workbench/app/views/trash_items/_untrash_item.html.erb
+++ b/apps/workbench/app/views/trash_items/_untrash_item.html.erb
@@ -1,5 +1,7 @@
 <% if object.editable? %>
-  <%= link_to({action: 'untrash_items', selection: [object.uuid]}, remote: true, method: :post, data: {confirm: "Un-trash #{object.class_for_display.downcase} '#{object.friendly_link_name}'?"}) do %>
+  <% msg = "Un-trash '" + if !object.name.blank? then object.name else object.uuid end + "'?" %>
+  <%= link_to({action: 'untrash_items', selection: [object.uuid]}, remote: true, method: :post,
+      data: {confirm: msg}) do %>
     <i class="fa fa-fw fa-recycle"></i>
   <% end %>
 <% end %>
diff --git a/apps/workbench/app/views/work_units/_show_all_processes.html.erb b/apps/workbench/app/views/work_units/_show_all_processes.html.erb
index ea17843..0fc1ef6 100644
--- a/apps/workbench/app/views/work_units/_show_all_processes.html.erb
+++ b/apps/workbench/app/views/work_units/_show_all_processes.html.erb
@@ -1,4 +1,4 @@
-<div class="container">
+<div class="container" style="width: 100%">
   <div class="row">
     <div class="pull-right">
       <input type="text" class="form-control filterable-control recent-all-processes-filterable-control"
diff --git a/apps/workbench/test/integration/trash_test.rb b/apps/workbench/test/integration/trash_test.rb
new file mode 100644
index 0000000..6cac1be
--- /dev/null
+++ b/apps/workbench/test/integration/trash_test.rb
@@ -0,0 +1,87 @@
+require 'integration_helper'
+
+class TrashTest < ActionDispatch::IntegrationTest
+  setup do
+    need_javascript
+  end
+
+  test "trash page" do
+    deleted = api_fixture('collections')['deleted_on_next_sweep']
+    expired1 = api_fixture('collections')['unique_expired_collection']
+    expired2 = api_fixture('collections')['unique_expired_collection2']
+
+    # visit trash page
+    visit page_with_token('active', "/trash")
+
+    assert_text deleted['name']
+    assert_text expired1['name']
+    assert_text expired2['name']
+    assert_no_text 'foo_file'
+
+    # Un-trash one item using selection dropdown
+    within('tr', text: deleted['name']) do
+      first('input').click
+    end
+
+    click_button 'Selection...'
+    within('.selection-action-container') do
+      click_link 'Un-trash selected items'
+    end
+
+    wait_for_ajax
+
+    assert_text expired1['name']      # this should still be there
+    assert_no_text deleted['name']    # this should no longer be here
+
+    # expired2 is not editable by me; checkbox and recycle button shouldn't be offered
+    within('tr', text: expired2['name']) do
+      assert_nil first('input')
+      assert_nil first('.fa-recycle')
+    end
+
+    # Un-trash another item using the recycle button
+    within('tr', text: expired1['name']) do
+      first('.fa-recycle').click
+      accept_alert
+    end
+
+    wait_for_ajax
+
+    assert_text expired2['name']
+    assert_no_text expired1['name']
+
+    # verify that the two un-trashed items are now shown in /collections page
+    visit page_with_token('active', "/collections")
+    assert_text deleted['uuid']
+    assert_text expired1['uuid']
+    assert_no_text expired2['uuid']
+  end
+
+  test "trash page with search" do
+    deleted = api_fixture('collections')['deleted_on_next_sweep']
+    expired = api_fixture('collections')['unique_expired_collection2']
+
+    visit page_with_token('active', "/trash")
+
+    assert_text deleted['name']
+    assert_text expired['name']
+
+    page.find_field('Search trash').set 'expired'
+
+    assert_text expired['name']
+    assert_no_text deleted['name']
+
+    click_button 'Selection...'
+    within('.selection-action-container') do
+      assert_selector 'li.disabled', text: 'Un-trash selected items'
+    end
+
+    first('input').click
+
+    click_button 'Selection...'
+    within('.selection-action-container') do
+      assert_selector 'li', text: 'Un-trash selected items'
+      assert_selector 'li.disabled', text: 'Un-trash selected items'
+    end
+  end
+end
diff --git a/services/api/app/controllers/arvados/v1/collections_controller.rb b/services/api/app/controllers/arvados/v1/collections_controller.rb
index 8ba7925..73a7e09 100644
--- a/services/api/app/controllers/arvados/v1/collections_controller.rb
+++ b/services/api/app/controllers/arvados/v1/collections_controller.rb
@@ -12,7 +12,7 @@ class Arvados::V1::CollectionsController < ApplicationController
   end
 
   def find_objects_for_index
-    if params[:include_trash] || ['destroy', 'trash'].include?(action_name)
+    if params[:include_trash] || ['destroy', 'trash', 'untrash'].include?(action_name)
       @objects = Collection.readable_by(*@read_users).unscoped
     end
     super
@@ -63,6 +63,15 @@ class Arvados::V1::CollectionsController < ApplicationController
     show
   end
 
+  def untrash
+    if @object.is_trashed
+      @object.update_attributes!(trash_at: nil)
+    else
+      raise InvalidStateTransitionError
+    end
+    show
+  end
+
   def find_collections(visited, sp, &b)
     case sp
     when ArvadosModel
diff --git a/services/api/config/routes.rb b/services/api/config/routes.rb
index 77e5372..87c4d91 100644
--- a/services/api/config/routes.rb
+++ b/services/api/config/routes.rb
@@ -21,6 +21,7 @@ Server::Application.routes.draw do
         get 'provenance', on: :member
         get 'used_by', on: :member
         post 'trash', on: :member
+        post 'untrash', on: :member
       end
       resources :groups do
         get 'contents', on: :collection
diff --git a/services/api/test/fixtures/collections.yml b/services/api/test/fixtures/collections.yml
index f48fbf1..8aedbdc 100644
--- a/services/api/test/fixtures/collections.yml
+++ b/services/api/test/fixtures/collections.yml
@@ -297,7 +297,7 @@ unique_expired_collection:
   trash_at: 2001-01-01T00:00:00Z
   delete_at: 2038-01-01T00:00:00Z
   manifest_text: ". 29d7797f1888013986899bc9083783fa+3 0:3:expired\n"
-  name: unique_expired_collection
+  name: unique_expired_collection1
 
 unique_expired_collection2:
   uuid: zzzzz-4zz18-mto52zx1s7sn3jr

commit 695a100d4bd3bf4f5534c7e489c118c2917bf35a
Author: radhika <radhika at curoverse.com>
Date:   Tue May 30 18:35:06 2017 -0400

    9587: trash page

diff --git a/apps/workbench/app/assets/javascripts/selection.js.erb b/apps/workbench/app/assets/javascripts/selection.js.erb
index f60bef7..a8e2738 100644
--- a/apps/workbench/app/assets/javascripts/selection.js.erb
+++ b/apps/workbench/app/assets/javascripts/selection.js.erb
@@ -81,6 +81,10 @@ function enable_disable_selection_actions() {
         toggleClass('disabled',
                     ($checked.length < 0) ||
                     !($checked.length > 0 && collection_lock_classes && collection_lock_classes.indexOf("fa-unlock") !=-1));
+    $('[data-selection-action=untrash-selected-items]', $container).
+        closest('li').
+        toggleClass('disabled',
+                    ($checked.length < 1));
 }
 
 $(document).
diff --git a/apps/workbench/app/controllers/trash_items_controller.rb b/apps/workbench/app/controllers/trash_items_controller.rb
new file mode 100644
index 0000000..5190474
--- /dev/null
+++ b/apps/workbench/app/controllers/trash_items_controller.rb
@@ -0,0 +1,89 @@
+class TrashItemsController < ApplicationController
+  def model_class
+    Collection
+  end
+
+  def find_objects_for_index
+    # If it's not the index rows partial display, just return
+    # The /index request will again be invoked to display the
+    # partial at which time, we will be using the objects found.
+    return if !params[:partial]
+
+    trashed_items
+
+    if @objects.any?
+      @next_page_filters = next_page_filters('<=')
+      @next_page_href = url_for(partial: :trash_rows,
+                                filters: @next_page_filters.to_json)
+      preload_links_for_objects(@objects.to_a)
+    else
+      @next_page_href = nil
+    end
+  end
+
+  def next_page_href with_params={}
+    @next_page_href
+  end
+
+  def trashed_items
+    # API server index doesn't return manifest_text by default, but our
+    # callers want it unless otherwise specified.
+    @select ||= Collection.columns.map(&:name)
+    limit = if params[:limit] then params[:limit].to_i else 100 end
+    offset = if params[:offset] then params[:offset].to_i else 0 end
+
+    base_search = Collection.select(@select).include_trash(true).where(is_trashed: true)
+    base_search = base_search.filter(params[:filters]) if params[:filters]
+
+    if params[:search].andand.length.andand > 0
+      tags = Link.where(any: ['contains', params[:search]])
+      @objects = (base_search.limit(limit).offset(offset).where(uuid: tags.collect(&:head_uuid)) |
+                      base_search.where(any: ['contains', params[:search]])).
+        uniq { |c| c.uuid }
+    else
+      @objects = base_search.limit(limit).offset(offset)
+    end
+
+    @links = Link.where(head_uuid: @objects.collect(&:uuid))
+    @collection_info = {}
+    @objects.each do |c|
+      @collection_info[c.uuid] = {
+        tag_links: [],
+        wanted: false,
+        wanted_by_me: false,
+        provenance: [],
+        links: []
+      }
+    end
+    @links.each do |link|
+      @collection_info[link.head_uuid] ||= {}
+      info = @collection_info[link.head_uuid]
+      case link.link_class
+      when 'tag'
+        info[:tag_links] << link
+      when 'resources'
+        info[:wanted] = true
+        info[:wanted_by_me] ||= link.tail_uuid == current_user.uuid
+      when 'provenance'
+        info[:provenance] << link.name
+      end
+      info[:links] << link
+    end
+    @request_url = request.url
+  end
+
+  def untrash_items
+    @untrashed_uuids = []
+
+    updates = {trash_at: nil}
+
+    params[:selection].collect { |uuid| ArvadosBase.find uuid }.each do |item|
+      item.update_attributes updates
+      @untrashed_uuids << item.uuid
+    end
+
+    respond_to do |format|
+      format.js
+    end
+  end
+end
diff --git a/apps/workbench/app/models/arvados_base.rb b/apps/workbench/app/models/arvados_base.rb
index 5d6a4c9..f06193c 100644
--- a/apps/workbench/app/models/arvados_base.rb
+++ b/apps/workbench/app/models/arvados_base.rb
@@ -152,6 +152,10 @@ class ArvadosBase < ActiveRecord::Base
     ArvadosResourceList.new(self).distinct(*args)
   end
 
+  def self.include_trash(*args)
+    ArvadosResourceList.new(self).include_trash(*args)
+  end
+
   def self.eager(*args)
     ArvadosResourceList.new(self).eager(*args)
   end
diff --git a/apps/workbench/app/models/arvados_resource_list.rb b/apps/workbench/app/models/arvados_resource_list.rb
index 35dcde3..8ae48d8 100644
--- a/apps/workbench/app/models/arvados_resource_list.rb
+++ b/apps/workbench/app/models/arvados_resource_list.rb
@@ -21,6 +21,11 @@ class ArvadosResourceList
     self
   end
 
+  def include_trash(option=nil)
+    @include_trash = option
+    self
+  end
+
   def limit(max_results)
     if not max_results.nil? and not max_results.is_a? Integer
       raise ArgumentError("argument to limit() must be an Integer or nil")
@@ -192,6 +197,7 @@ class ArvadosResourceList
     api_params[:order] = @orderby_spec if @orderby_spec
     api_params[:filters] = @filters if @filters
     api_params[:distinct] = @distinct if @distinct
+    api_params[:include_trash] = @include_trash if @include_trash
     if @fetch_multiple_pages
       # Default limit to (effectively) api server's MAX_LIMIT
       api_params[:limit] = 2**(0.size*8 - 1) - 1
diff --git a/apps/workbench/app/views/application/_breadcrumbs.html.erb b/apps/workbench/app/views/application/_breadcrumbs.html.erb
index 3ef2aec..489dbf3 100644
--- a/apps/workbench/app/views/application/_breadcrumbs.html.erb
+++ b/apps/workbench/app/views/application/_breadcrumbs.html.erb
@@ -66,4 +66,11 @@
             <% end %>
           <% end %>
         </ul>
+        <ul class="nav navbar-nav navbar-right">
+          <li>
+            <a href="/trash">
+              <i class="fa fa-lg fa-fw fa-trash-o"></i>
+            </a>
+          </li>
+        </ul>
       </nav>
diff --git a/apps/workbench/app/views/trash_items/_show_trash_items.html.erb b/apps/workbench/app/views/trash_items/_show_trash_items.html.erb
new file mode 100644
index 0000000..dc615fc
--- /dev/null
+++ b/apps/workbench/app/views/trash_items/_show_trash_items.html.erb
@@ -0,0 +1,54 @@
+<div class="container selection-action-container">
+  <div class="col-md-6 pull-left">
+    <div class="btn-group btn-group-sm">
+      <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">Selection... <span class="caret"></span></button>
+      <ul class="dropdown-menu" role="menu">
+        <li><%= link_to "Un-trash selected items", '#',
+                method: :post,
+                remote: true,
+                'id' => 'untrash_selected_items',
+                'data-href' => untrash_items_trash_items_path,
+                'data-selection-param-name' => 'selection[]',
+                'data-selection-action' => 'untrash-selected-items',
+                'data-toggle' => 'dropdown'
+          %></li>
+      </ul>
+    </div>
+  </div>
+  <div class="col-md-6 pull-right">
+    <input type="text" class="form-control filterable-control recent-trash-items"
+           placeholder="Search trash"
+           data-filterable-target="#recent-trash-items"
+           value="<%= params[:search] %>" />
+  </div>
+
+  <div>
+    <table id="trash-index" class="topalign table table-condensed table-fixedlayout">
+      <colgroup>
+        <col width="5%" />
+        <col width="15%" />
+        <col width="10%" />
+        <col width="10%" />
+        <col width="30%" />
+        <col width="25%" />
+        <col width="5%" />
+      </colgroup>
+
+      <thead>
+        <tr class="contain-align-left">
+          <th></th>
+          <th>Name</th>
+          <th>Created at</th>
+          <th>Trashed at</th>
+          <th>Contents</th>
+          <th>Tags</th>
+          <th></th>
+        </tr>
+      </thead>
+
+      <tbody data-infinite-scroller="#recent-trash-items" id="recent-trash-items"
+        data-infinite-content-href="<%= url_for partial: :trash_rows %>" >
+      </tbody>
+    </table>
+  </div>
+</div>
diff --git a/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb b/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb
new file mode 100644
index 0000000..73b89dd
--- /dev/null
+++ b/apps/workbench/app/views/trash_items/_show_trash_rows.html.erb
@@ -0,0 +1,39 @@
+<% @objects.sort_by { |obj| obj.created_at }.reverse.each do |obj| %>
+    <tr data-object-uuid="<%= obj.uuid %>" data-kind="<%= obj.kind %>" >
+      <td>
+        <%= check_box_tag 'uuids[]', obj.uuid, false, :class => 'persistent-selection' %>
+      </td>
+      <td>
+        <%= link_to_if_arvados_object obj, friendly_name: true %>
+      <td>
+        <%= obj.created_at.to_s if obj.created_at %>
+      <td>
+        <%= obj.trash_at.to_s if obj.trash_at %>
+      </td>
+      <td>
+        <% i = 0 %>
+        <% while i < 3 and i < obj.files.length %>
+          <% file = obj.files[i] %>
+          <% file_path = "#{file[0]}/#{file[1]}" %>
+          <%= link_to file[1], {controller: 'collections', action: 'show_file', uuid: obj.uuid, file: file_path, size: file[2], disposition: 'inline'}, {title: 'View in browser'} %><br />
+          <% i += 1 %>
+        <% end %>
+        <% if i < obj.files.length %>
+          ⋮
+        <% end %>
+      </td>
+      <td>
+        <span class="tag-container">
+        <% if @collection_info[obj.uuid] %>
+          <% @collection_info[obj.uuid][:tag_links].each do |tag_link| %>
+            <span class="label label-info" data-tag-link-uuid="<%= tag_link.uuid %>"><%= tag_link.name %>
+            </span> 
+          <% end %>
+        <% end %>
+        </span>
+      </td>
+      <td>
+        <%= render partial: 'untrash_item', locals: {object:obj} %>
+      </td>
+    </tr>
+<% end %>
diff --git a/apps/workbench/app/views/trash_items/_untrash_item.html.erb b/apps/workbench/app/views/trash_items/_untrash_item.html.erb
new file mode 100644
index 0000000..a40a4be
--- /dev/null
+++ b/apps/workbench/app/views/trash_items/_untrash_item.html.erb
@@ -0,0 +1,5 @@
+<% if object.editable? %>
+  <%= link_to({action: 'untrash_items', selection: [object.uuid]}, remote: true, method: :post, data: {confirm: "Un-trash #{object.class_for_display.downcase} '#{object.friendly_link_name}'?"}) do %>
+    <i class="fa fa-fw fa-recycle"></i>
+  <% end %>
+<% end %>
diff --git a/apps/workbench/app/views/trash_items/index.html.erb b/apps/workbench/app/views/trash_items/index.html.erb
new file mode 100644
index 0000000..bd55a3f
--- /dev/null
+++ b/apps/workbench/app/views/trash_items/index.html.erb
@@ -0,0 +1 @@
+<%= render partial: 'show_trash_items' %>
diff --git a/apps/workbench/app/views/trash_items/untrash_items.js.erb b/apps/workbench/app/views/trash_items/untrash_items.js.erb
new file mode 100644
index 0000000..3d26658
--- /dev/null
+++ b/apps/workbench/app/views/trash_items/untrash_items.js.erb
@@ -0,0 +1,5 @@
+<% @untrashed_uuids.each do |uuid| %>
+	$('[data-object-uuid=<%= uuid %>]').hide('slow', function() {
+	    $(this).remove();
+	});
+<% end %>
diff --git a/apps/workbench/config/routes.rb b/apps/workbench/config/routes.rb
index 0eef73f..badb471 100644
--- a/apps/workbench/config/routes.rb
+++ b/apps/workbench/config/routes.rb
@@ -108,6 +108,11 @@ ArvadosWorkbench::Application.routes.draw do
 
   resources :workflows
 
+  get "trash" => 'trash_items#index', :as => :trash
+  resources :trash_items do
+    post 'untrash_items', on: :collection
+  end
+
   post 'actions' => 'actions#post'
   get 'actions' => 'actions#show'
   get 'websockets' => 'websocket#index'
diff --git a/services/api/app/controllers/arvados/v1/collections_controller.rb b/services/api/app/controllers/arvados/v1/collections_controller.rb
index 939ca21..8ba7925 100644
--- a/services/api/app/controllers/arvados/v1/collections_controller.rb
+++ b/services/api/app/controllers/arvados/v1/collections_controller.rb
@@ -13,7 +13,7 @@ class Arvados::V1::CollectionsController < ApplicationController
 
   def find_objects_for_index
     if params[:include_trash] || ['destroy', 'trash'].include?(action_name)
-      @objects = Collection.unscoped.readable_by(*@read_users)
+      @objects = Collection.readable_by(*@read_users).unscoped
     end
     super
   end

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list