9587: expose untrash api
authorradhika <radhika@curoverse.com>
Wed, 31 May 2017 19:03:53 +0000 (15:03 -0400)
committerradhika <radhika@curoverse.com>
Mon, 5 Jun 2017 22:57:35 +0000 (18:57 -0400)
12 files changed:
apps/workbench/app/assets/images/trash-icon.png [new file with mode: 0644]
apps/workbench/app/controllers/trash_items_controller.rb
apps/workbench/app/models/collection.rb
apps/workbench/app/views/application/_breadcrumbs.html.erb
apps/workbench/app/views/trash_items/_show_trash_items.html.erb
apps/workbench/app/views/trash_items/_show_trash_rows.html.erb
apps/workbench/app/views/trash_items/_untrash_item.html.erb
apps/workbench/app/views/work_units/_show_all_processes.html.erb
apps/workbench/test/integration/trash_test.rb [new file with mode: 0644]
services/api/app/controllers/arvados/v1/collections_controller.rb
services/api/config/routes.rb
services/api/test/fixtures/collections.yml

diff --git a/apps/workbench/app/assets/images/trash-icon.png b/apps/workbench/app/assets/images/trash-icon.png
new file mode 100644 (file)
index 0000000..5c26c24
Binary files /dev/null and b/apps/workbench/app/assets/images/trash-icon.png differ
index 5190474e903b7f0be113aec42d12ae449865bdd9..bbc0b26c8354742a1bc445c2a7d8557e5b5a47fa 100644 (file)
@@ -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|
index ea81ad8c0a7588edc00062585d99ba9fa116035f..305ea015306fe898c97bd16b443a13d9ff230781 100644 (file)
@@ -98,4 +98,7 @@ class Collection < ArvadosBase
     [ 'description' ]
   end
 
+  def untrash
+    arvados_api_client.api(self.class, "/#{self.uuid}/untrash", {})
+  end
 end
index 489dbf32b5d979a8eeec6649dce7425cf24f1b48..2db43eda6251ceee56356ff71ba3fc00e07ca616 100644 (file)
@@ -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>
index dc615fc9ab55a4eccdcea5fe03691cdc8c27dbad..153d7d033397d93d58dddb7fba1c022c5ea5b20b 100644 (file)
@@ -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"
     <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>
index 73b89dd5493c8a45dcc1c8030d7d0c4fb1f5b127..d617d8d2ea0c4a139daa00f45db6eb2e74387421 100644 (file)
@@ -1,37 +1,30 @@
 <% @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 %>
           &vellip;
         <% 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>&nbsp;
-          <% end %>
-        <% end %>
-        </span>
-      </td>
       <td>
         <%= render partial: 'untrash_item', locals: {object:obj} %>
       </td>
index a40a4be7e1e74a6b6df96123b14eff54cae8ab69..2ba9dc93f189bf5840a6f4ab11daa3b037412f43 100644 (file)
@@ -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 %>
index ea178438cfef1fe912bed6f22ec5a5f54bb9b6b2..0fc1ef625f4d96dbd7470abbbe13a7c439dd150a 100644 (file)
@@ -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 (file)
index 0000000..6cac1be
--- /dev/null
@@ -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
index 8ba7925b288e7f4b3b03d7bd1277b9a1e21f1e2f..73a7e09c5e69cdd6447d91f1e9d0907c97d143ce 100644 (file)
@@ -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
index 77e5372a15423686d3597095c455d90513779bc9..87c4d91757a9daf9d086b56bf6ffcb0f6066cc71 100644 (file)
@@ -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
index f48fbf1b8542d0f35fd37888c778fd264e820e7a..8aedbdc7057bc1d74d0749477a6a9c3c62b9624b 100644 (file)
@@ -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