@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
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
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|
[ 'description' ]
end
+ def untrash
+ arvados_api_client.api(self.class, "/#{self.uuid}/untrash", {})
+ end
end
<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>
-<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">
</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>
<th>Name</th>
<th>Created at</th>
<th>Trashed at</th>
+ <th>Owner</th>
<th>Contents</th>
- <th>Tags</th>
<th></th>
</tr>
</thead>
<% @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>
<% 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 %>
-<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"
--- /dev/null
+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
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
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
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
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