[ARVADOS] created: 06f5f794cd1fc8cb4dbf72e3a2fbc440b44101b9
git at public.curoverse.com
git at public.curoverse.com
Mon Jun 2 01:25:58 EDT 2014
at 06f5f794cd1fc8cb4dbf72e3a2fbc440b44101b9 (commit)
commit 06f5f794cd1fc8cb4dbf72e3a2fbc440b44101b9
Author: Tom Clegg <tom at curoverse.com>
Date: Mon Jun 2 01:24:55 2014 -0400
2872: Show collection details in chooser.
diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb
index 0941921..c1e82eb 100644
--- a/apps/workbench/app/controllers/application_controller.rb
+++ b/apps/workbench/app/controllers/application_controller.rb
@@ -495,4 +495,15 @@ class ApplicationController < ActionController::Base
root_of[g.uuid] == current_user.uuid
end
end
+
+ helper_method :get_object
+ def get_object uuid
+ if @get_object.nil? and @objects
+ @get_object = @objects.each_with_object({}) do |object, h|
+ h[object.uuid] = object
+ end
+ end
+ @get_object ||= {}
+ @get_object[uuid]
+ end
end
diff --git a/apps/workbench/app/views/collections/_choose_rows.html.erb b/apps/workbench/app/views/collections/_choose_rows.html.erb
index 8622830..55e6451 100644
--- a/apps/workbench/app/views/collections/_choose_rows.html.erb
+++ b/apps/workbench/app/views/collections/_choose_rows.html.erb
@@ -1,11 +1,15 @@
<% @name_links.each do |name_link| %>
<div class="row selectable <%= 'multiple' if multiple %>" data-object-uuid="<%= name_link.uuid %>">
- <div class="col-sm-8" style="overflow-x:hidden">
+ <div class="col-lg-12" style="overflow-x:hidden">
<i class="fa fa-fw fa-archive"></i>
<%= name_link.name %>
</div>
- <div class="col-sm-4" style="overflow-x:hidden">
- <%= link_to_if_arvados_object name_link.tail_uuid, friendly_name: true %>
+ <div class="col-lg-12 arv-description-in-table">
+ <%= render_controller_partial(
+ 'show_object_description_cell.html',
+ controller_name: 'collections',
+ locals: {object: get_object(name_link.head_uuid)})
+ %>
</div>
</div>
<% end %>
diff --git a/apps/workbench/app/views/collections/_show_object_description_cell.html.erb b/apps/workbench/app/views/collections/_show_object_description_cell.html.erb
index 0b3352b..1f1325d 100644
--- a/apps/workbench/app/views/collections/_show_object_description_cell.html.erb
+++ b/apps/workbench/app/views/collections/_show_object_description_cell.html.erb
@@ -1,9 +1,4 @@
<div class="nowrap">
- <div>
- <% Link.filter([['link_class','=','tag'],['head_uuid','=',object.uuid]]).collect(&:name).each do |tagname| %>
- <span class="label label-info"><%= tagname %></span>
- <% end %>
- </div>
<span class="deemphasize">
Files (<%= human_readable_bytes_html(object.total_bytes) %>):
</span><span class="arvados-filename">
@@ -11,4 +6,9 @@
<%= file %>
<% end %>
</span>
+ <div>
+ <% Link.filter([['link_class','=','tag'],['head_uuid','=',object.uuid]]).collect(&:name).each do |tagname| %>
+ <span class="label label-info"><%= tagname %></span>
+ <% end %>
+ </div>
</div>
commit 830678c2715bac06d581dcdce8ea518c1bcc2939
Author: Tom Clegg <tom at curoverse.com>
Date: Mon Jun 2 01:10:17 2014 -0400
2872: Fix css on collection tab line buttons.
diff --git a/apps/workbench/app/views/collections/_show_files.html.erb b/apps/workbench/app/views/collections/_show_files.html.erb
index 74e02f7..c461163 100644
--- a/apps/workbench/app/views/collections/_show_files.html.erb
+++ b/apps/workbench/app/views/collections/_show_files.html.erb
@@ -1,14 +1,6 @@
<% content_for :tab_line_buttons do %>
-<div class="row">
- <div class="col-md-6"></div>
- <div class="col-md-6">
- <div class="pull-right">
- <span style="padding-left: 1em">Collection storage status:</span>
- <%= render partial: 'toggle_persist', locals: { uuid: @object.uuid, current_state: (@is_persistent ? 'persistent' : 'cache') } %>
-
- </div>
- </div>
-</div>
+<span style="padding-left: 1em">Collection storage status:</span>
+<%= render partial: 'toggle_persist', locals: { uuid: @object.uuid, current_state: (@is_persistent ? 'persistent' : 'cache') } %>
<% end %>
<% file_tree = @object.andand.files_tree %>
commit bff608d3f7430cb0f97d2ac185d1d7674c6b9ff8
Author: Tom Clegg <tom at curoverse.com>
Date: Mon Jun 2 01:09:06 2014 -0400
2872: Add useful pipeline instance content summary.
diff --git a/apps/workbench/app/helpers/pipeline_instances_helper.rb b/apps/workbench/app/helpers/pipeline_instances_helper.rb
index 7b6fb72..35d28d5 100644
--- a/apps/workbench/app/helpers/pipeline_instances_helper.rb
+++ b/apps/workbench/app/helpers/pipeline_instances_helper.rb
@@ -82,17 +82,22 @@ module PipelineInstancesHelper
end
if pj[:job][:success]
pj[:result] = 'complete'
+ pj[:labeltype] = 'success'
pj[:complete] = true
pj[:progress] = 1.0
elsif pj[:job][:finished_at]
pj[:result] = 'failed'
+ pj[:labeltype] = 'danger'
pj[:failed] = true
elsif pj[:job][:started_at]
pj[:result] = 'running'
+ pj[:labeltype] = 'primary'
elsif pj[:job][:uuid]
pj[:result] = 'queued'
+ pj[:labeltype] = 'default'
else
pj[:result] = 'none'
+ pj[:labeltype] = 'default'
end
pj[:job_id] = pj[:job][:uuid]
pj[:script] = pj[:job][:script] || c[:script]
diff --git a/apps/workbench/app/views/pipeline_instances/_component_labels.html.erb b/apps/workbench/app/views/pipeline_instances/_component_labels.html.erb
new file mode 100644
index 0000000..d2d824b
--- /dev/null
+++ b/apps/workbench/app/views/pipeline_instances/_component_labels.html.erb
@@ -0,0 +1,5 @@
+<% pipeline_jobs(object).each do |pj| %>
+ <span class="label label-<%= pj[:labeltype] %>">
+ <%= pj[:name] %>
+ </span>
+<% end %>
diff --git a/apps/workbench/app/views/pipeline_instances/_show_object_description_cell.html.erb b/apps/workbench/app/views/pipeline_instances/_show_object_description_cell.html.erb
new file mode 100644
index 0000000..38f51a3
--- /dev/null
+++ b/apps/workbench/app/views/pipeline_instances/_show_object_description_cell.html.erb
@@ -0,0 +1,4 @@
+<div class="nowrap">
+ <%= object.content_summary %><br />
+ <%= render partial: 'pipeline_instances/component_labels', locals: {object: object} %>
+</div>
commit a9f77ff084be7ad9caeb9e9601c022cb05e3c548
Author: Tom Clegg <tom at curoverse.com>
Date: Mon Jun 2 00:52:50 2014 -0400
2872: Disable chooser OK button until selection is made, update run-pipeline wording.
diff --git a/apps/workbench/app/assets/javascripts/select_modal.js b/apps/workbench/app/assets/javascripts/select_modal.js
index 278dbb5..6e22d80 100644
--- a/apps/workbench/app/assets/javascripts/select_modal.js
+++ b/apps/workbench/app/assets/javascripts/select_modal.js
@@ -1,4 +1,5 @@
$(document).on('click', '.selectable', function() {
+ var any;
var $this = $(this);
if (!$this.hasClass('multiple')) {
$this.closest('.selectable-container').
@@ -6,6 +7,13 @@ $(document).on('click', '.selectable', function() {
removeClass('active');
}
$this.toggleClass('active');
+ any = ($this.
+ closest('.selectable-container').
+ find('.selectable.active').length > 0)
+ $this.
+ closest('.modal').
+ find('[data-enable-if-selection]').
+ prop('disabled', !any);
}).on('click', '.modal button[data-action-href]', function() {
var selection = [];
var data = [];
diff --git a/apps/workbench/app/views/application/_choose.html.erb b/apps/workbench/app/views/application/_choose.html.erb
index 2b4f61e..94ed8e6 100644
--- a/apps/workbench/app/views/application/_choose.html.erb
+++ b/apps/workbench/app/views/application/_choose.html.erb
@@ -15,7 +15,7 @@
<div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Cancel</button>
- <button class="btn btn-primary" aria-hidden="true"><%= params[:action_name] || 'Select' %></button>
+ <button class="btn btn-primary" aria-hidden="true" data-enable-if-selection disabled><%= raw(params[:action_name]) || 'Select' %></button>
<div class="modal-error hide" style="text-align: left; margin-top: 1em;">
</div>
</div>
diff --git a/apps/workbench/app/views/folders/_show_contents.html.erb b/apps/workbench/app/views/folders/_show_contents.html.erb
index 3a11930..450ba29 100644
--- a/apps/workbench/app/views/folders/_show_contents.html.erb
+++ b/apps/workbench/app/views/folders/_show_contents.html.erb
@@ -36,7 +36,7 @@
<%= link_to(
choose_pipeline_templates_path(
title: 'Choose a pipeline to run:',
- action_name: 'Configure...',
+ action_name: 'Next: choose inputs <i class="fa fa-fw fa-arrow-circle-right"></i>',
action_href: pipeline_instances_path,
action_method: 'post',
action_data: {'selection_param' => 'pipeline_instance[pipeline_template_uuid]', 'pipeline_instance[owner_uuid]' => @object.uuid, 'success' => 'redirect-to-created-object'}.to_json),
diff --git a/apps/workbench/app/views/pipeline_instances/_show_components.html.erb b/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
index b8f94d5..6d6a8b1 100644
--- a/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
+++ b/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
@@ -10,7 +10,8 @@
</h2>
<% if template %>
<blockquote><span class="deemphasize">From template:</span><br />
- <%= link_to_if_arvados_object template, friendly_name: true %>
+ <%= link_to_if_arvados_object template, friendly_name: true %><br />
+ <%= template.description %>
</blockquote>
<% end %>
<% end %>
@@ -120,7 +121,7 @@ setInterval(function(){$('a.refresh').click()}, 15000);
<% else %> <%# State new or ready or paused %>
<% if @object.state == 'New' %>
- <p>Please set the desired input parameters for the components of this pipeline. Parameters highlighted in red are required.</p>
+ <p><i>Here are all of the pipeline's components (jobs that will need to run in order to complete the pipeline). If you know what you're doing (or you're experimenting) you can modify these parameters before starting the pipeline. Usually, you only need to edit the settings presented on the "Inputs" tab above.</i></p>
<% end %>
<% content_for :tab_line_buttons do %>
commit 0a6ca2aada7bc0706fd1e41c1002cd6aa375c3a1
Author: Tom Clegg <tom at curoverse.com>
Date: Mon Jun 2 00:18:42 2014 -0400
2872: Add infinite scroll to chooser modal.
diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb
index 3ed440f..0941921 100644
--- a/apps/workbench/app/controllers/application_controller.rb
+++ b/apps/workbench/app/controllers/application_controller.rb
@@ -129,8 +129,25 @@ class ApplicationController < ActionController::Base
end
def choose
+ params[:limit] ||= 20
find_objects_for_index if !@objects
- render partial: 'choose', locals: {multiple: params[:multiple]}
+ respond_to do |f|
+ if params[:partial]
+ f.json {
+ render json: {
+ content: render_to_string(partial: "choose_rows.html",
+ formats: [:html],
+ locals: {
+ multiple: params[:multiple]
+ }),
+ next_page_href: @next_page_href
+ }
+ }
+ end
+ f.js {
+ render partial: 'choose', locals: {multiple: params[:multiple]}
+ }
+ end
end
def render_content
diff --git a/apps/workbench/app/controllers/collections_controller.rb b/apps/workbench/app/controllers/collections_controller.rb
index 4ba23c5..24b9b1f 100644
--- a/apps/workbench/app/controllers/collections_controller.rb
+++ b/apps/workbench/app/controllers/collections_controller.rb
@@ -41,10 +41,13 @@ class CollectionsController < ApplicationController
end
def choose
+ params[:limit] ||= 20
@objects = Link.
filter([['link_class','=','name'],
['head_uuid','is_a','arvados#collection']])
find_objects_for_index
+ @next_page_href = (next_page_offset and
+ url_for(offset: next_page_offset, partial: true))
@name_links = @objects
@objects = Collection.
filter([['uuid','in', at name_links.collect(&:head_uuid)]])
diff --git a/apps/workbench/app/controllers/folders_controller.rb b/apps/workbench/app/controllers/folders_controller.rb
index b7b4854..637ae36 100644
--- a/apps/workbench/app/controllers/folders_controller.rb
+++ b/apps/workbench/app/controllers/folders_controller.rb
@@ -82,7 +82,8 @@ class FoldersController < ApplicationController
end
def show
- @objects = @object.contents(include_linked: true,
+ @objects = @object.contents(limit: 50,
+ include_linked: true,
offset: params[:offset] || 0)
@share_links = Link.filter([['head_uuid', '=', @object.uuid],
['link_class', '=', 'permission']])
diff --git a/apps/workbench/app/views/application/_choose.html.erb b/apps/workbench/app/views/application/_choose.html.erb
index cf18cdc..2b4f61e 100644
--- a/apps/workbench/app/views/application/_choose.html.erb
+++ b/apps/workbench/app/views/application/_choose.html.erb
@@ -8,7 +8,7 @@
</div>
<div class="modal-body">
- <div class="container-fluid arv-folder-list selectable-container" style="height: 15em; overflow-y: scroll">
+ <div class="container-fluid arv-folder-list selectable-container" style="height: 15em; overflow-y: scroll" data-infinite-scroller="#choose-scroll" id="choose-scroll" data-infinite-content-href="<%= @next_page_href %>">
<%= render partial: 'choose_rows', locals: {multiple: multiple} %>
</div>
</div>
commit 70ac6969e76770bcc6becfe94aa5811a0f320fcb
Author: Tom Clegg <tom at curoverse.com>
Date: Sun Jun 1 17:27:15 2014 -0400
2872: Rearrange folder index. Show collection tags and files in folder view.
diff --git a/apps/workbench/app/assets/javascripts/editable.js b/apps/workbench/app/assets/javascripts/editable.js
index a32eb7e..093a671 100644
--- a/apps/workbench/app/assets/javascripts/editable.js
+++ b/apps/workbench/app/assets/javascripts/editable.js
@@ -41,10 +41,9 @@ $.fn.editable.defaults.validate = function (value) {
$(document).
on('ready ajax:complete', function() {
- $('#editable-submit').click(function() {
- console.log($(this));
- });
$('.editable').
+ not('.editable-done-setup').
+ addClass('editable-done-setup').
editable({
success: function(response, newValue) {
// If we just created a new object, stash its UUID
@@ -77,10 +76,13 @@ $(document).
});
}).
on('ready ajax:complete', function() {
- $("[data-toggle~='x-editable']").click(function(e) {
- e.stopPropagation();
- $($(this).attr('data-toggle-selector')).editable('toggle');
- });
+ $("[data-toggle~='x-editable']").
+ not('.editable-done-setup').
+ addClass('editable-done-setup').
+ click(function(e) {
+ e.stopPropagation();
+ $($(this).attr('data-toggle-selector')).editable('toggle');
+ });
});
$.fn.editabletypes.text.defaults.tpl = '<input type="text" name="editable-text">'
diff --git a/apps/workbench/app/assets/stylesheets/application.css.scss b/apps/workbench/app/assets/stylesheets/application.css.scss
index 7d76f5a..75080a0 100644
--- a/apps/workbench/app/assets/stylesheets/application.css.scss
+++ b/apps/workbench/app/assets/stylesheets/application.css.scss
@@ -47,6 +47,7 @@ table.table-justforlayout {
font-size: .8em;
color: #888;
}
+.arvados-filename,
.arvados-uuid {
font-size: .8em;
font-family: monospace;
@@ -184,6 +185,7 @@ div#wrapper {
}
.arv-description-in-table {
max-height: 3.5em;
+ overflow-x: hidden;
overflow-y: hidden;
}
.arv-description-in-table:hover {
diff --git a/apps/workbench/app/assets/stylesheets/folders.css.scss b/apps/workbench/app/assets/stylesheets/folders.css.scss
index 163c188..aa5f14c 100644
--- a/apps/workbench/app/assets/stylesheets/folders.css.scss
+++ b/apps/workbench/app/assets/stylesheets/folders.css.scss
@@ -6,3 +6,7 @@
.arv-folder-list > .row.folder:hover {
background: #d9edf7;
}
+div.scroll-20em {
+ height: 20em;
+ overflow-y: scroll;
+}
diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb
index 97ef55a..3ed440f 100644
--- a/apps/workbench/app/controllers/application_controller.rb
+++ b/apps/workbench/app/controllers/application_controller.rb
@@ -144,7 +144,7 @@ class ApplicationController < ActionController::Base
end
def update
- @updates ||= params[@object.class.to_s.underscore.singularize.to_sym]
+ @updates ||= params[@object.resource_param_name.to_sym]
@updates.keys.each do |attr|
if @object.send(attr).is_a? Hash
if @updates[attr].is_a? String
diff --git a/apps/workbench/app/helpers/application_helper.rb b/apps/workbench/app/helpers/application_helper.rb
index 117f808..dc97251 100644
--- a/apps/workbench/app/helpers/application_helper.rb
+++ b/apps/workbench/app/helpers/application_helper.rb
@@ -212,7 +212,8 @@ module ApplicationHelper
end
end
- if !object.attribute_editable?(attr, :ever) or
+ if !object or
+ !object.attribute_editable?(attr, :ever) or
(!object.editable? and
!object.owner_uuid.in?(my_folders.collect(&:uuid)))
return link_to_if_arvados_object attrvalue
@@ -349,6 +350,15 @@ module ApplicationHelper
end
end
+ def render_controller_partial partial, opts
+ cname = opts.delete :controller_name
+ begin
+ render opts.merge(partial: "#{cname}/#{partial}")
+ rescue ActionView::MissingTemplate
+ render opts.merge(partial: "application/#{partial}")
+ end
+ end
+
def fa_icon_class_for_object object
case object.class.to_s.to_sym
when :User
@@ -365,6 +375,22 @@ module ApplicationHelper
'fa-clipboard'
when :Human
'fa-male'
+ when :VirtualMachine
+ 'fa-terminal'
+ when :Repository
+ 'fa-code-fork'
+ when :Link
+ 'fa-arrows-h'
+ when :User
+ 'fa-user'
+ when :Node
+ 'fa-cloud'
+ when :KeepService
+ 'fa-exchange'
+ when :KeepDisk
+ 'fa-hdd-o'
+ else
+ 'fa-cube'
end
end
end
diff --git a/apps/workbench/app/models/arvados_base.rb b/apps/workbench/app/models/arvados_base.rb
index cb45701..e8b4caf 100644
--- a/apps/workbench/app/models/arvados_base.rb
+++ b/apps/workbench/app/models/arvados_base.rb
@@ -348,6 +348,10 @@ class ArvadosBase < ActiveRecord::Base
resource_class
end
+ def resource_param_name
+ self.class.to_s.underscore
+ end
+
def friendly_link_name
(name if self.respond_to? :name) || default_name
end
@@ -364,6 +368,14 @@ class ArvadosBase < ActiveRecord::Base
self.to_s.underscore.humanize
end
+ def controller
+ (self.class.to_s.pluralize + 'Controller').constantize
+ end
+
+ def controller_name
+ self.class.to_s.tableize
+ end
+
# Placeholder for name when name is missing or empty
def default_name
if self.respond_to? :name
diff --git a/apps/workbench/app/views/application/_content.html.erb b/apps/workbench/app/views/application/_content.html.erb
index 32c053f..b41da15 100644
--- a/apps/workbench/app/views/application/_content.html.erb
+++ b/apps/workbench/app/views/application/_content.html.erb
@@ -1,3 +1,24 @@
+<% content_for :content_top do %>
+ <% if @object and not @object.is_a?(Group) and @object.class.goes_in_folders? and @object.owner_uuid == current_user.uuid %>
+ <div class="pull-right" style="width: 40%">
+ <div class="alert alert-warning alert-dismissable">
+ <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
+ <strong>Hey.</strong> This <%= @object.class_for_display.downcase %> belongs to your account, but it's not in any of your folders. If you want it to be easy to find in the future, you should move it to a folder.<br />
+ <%= button_to(choose_folders_path(
+ title: 'Move to...',
+ editable: true,
+ action_name: 'Move',
+ action_href: url_for(action: :update),
+ action_method: 'patch',
+ action_data: {selection_param: @object.resource_param_name+'[owner_uuid]', success: 'page-refresh'}.to_json),
+ { class: "btn btn-primary btn-sm", remote: true, method: 'get' }) do %>
+ <i class="fa fa-fw fa-folder"></i> Choose a folder...
+ <% end %>
+ </div>
+ </div>
+ <% end %>
+<% end %>
+
<% content_for :tab_panes do %>
<% comparable = controller.respond_to? :compare %>
diff --git a/apps/workbench/app/views/application/_content_layout.html.erb b/apps/workbench/app/views/application/_content_layout.html.erb
index 1bc0b85..426a497 100644
--- a/apps/workbench/app/views/application/_content_layout.html.erb
+++ b/apps/workbench/app/views/application/_content_layout.html.erb
@@ -1,4 +1,5 @@
<%= content_for :content_top %>
+<br clear="all" />
<div class="pull-right">
<%= content_for :tab_line_buttons %>
</div>
diff --git a/apps/workbench/app/views/application/_show_object_button.html.erb b/apps/workbench/app/views/application/_show_object_button.html.erb
index 157ae49..17e9737 100644
--- a/apps/workbench/app/views/application/_show_object_button.html.erb
+++ b/apps/workbench/app/views/application/_show_object_button.html.erb
@@ -1,3 +1,12 @@
<% htmloptions = {class: ''}.merge(htmloptions || {})
htmloptions[:class] += " btn-#{size}" rescue nil %>
-<%= link_to_if_arvados_object object, { link_text: raw('<i class="glyphicon glyphicon-zoom-in"></i>') }, { data: {toggle: 'tooltip', placement: 'top'}, title: 'show', class: 'btn btn-default btn-nodecorate ' + htmloptions[:class] } %>
+<%= link_to_if_arvados_object object, {
+ link_text: raw('<i class="fa fa-fw ' + fa_icon_class_for_object(object) + '"></i>')
+ }, {
+ data: {
+ toggle: 'tooltip',
+ placement: 'top'
+ },
+ title: 'show',
+ class: 'btn btn-default btn-nodecorate ' + htmloptions[:class]
+ } %>
diff --git a/apps/workbench/app/views/application/_show_object_description_cell.html.erb b/apps/workbench/app/views/application/_show_object_description_cell.html.erb
new file mode 100644
index 0000000..7ad3f0e
--- /dev/null
+++ b/apps/workbench/app/views/application/_show_object_description_cell.html.erb
@@ -0,0 +1,2 @@
+<%= object.content_summary %>
+
diff --git a/apps/workbench/app/views/collections/_show_object_description_cell.html.erb b/apps/workbench/app/views/collections/_show_object_description_cell.html.erb
new file mode 100644
index 0000000..0b3352b
--- /dev/null
+++ b/apps/workbench/app/views/collections/_show_object_description_cell.html.erb
@@ -0,0 +1,14 @@
+<div class="nowrap">
+ <div>
+ <% Link.filter([['link_class','=','tag'],['head_uuid','=',object.uuid]]).collect(&:name).each do |tagname| %>
+ <span class="label label-info"><%= tagname %></span>
+ <% end %>
+ </div>
+ <span class="deemphasize">
+ Files (<%= human_readable_bytes_html(object.total_bytes) %>):
+ </span><span class="arvados-filename">
+ <% object.files.each do |path, file, size| %>
+ <%= file %>
+ <% end %>
+ </span>
+</div>
diff --git a/apps/workbench/app/views/folders/_index_folders.html.erb b/apps/workbench/app/views/folders/_index_folders.html.erb
new file mode 100644
index 0000000..734e0f4
--- /dev/null
+++ b/apps/workbench/app/views/folders/_index_folders.html.erb
@@ -0,0 +1,31 @@
+<div class="container-fluid arv-folder-list">
+ <% tree.each do |foldernode| %>
+ <% rowtype = foldernode[:object].class %>
+ <% next if rowtype != Group and !show_root_node %>
+ <div class="<%= 'folder' if rowtype == Group %> row">
+ <div class="col-md-12" style="padding-left: <%= foldernode[:depth] - (show_root_node ? 0 : 1) %>em;">
+ <% if show_root_node and rowtype == String %>
+ <i class="fa fa-fw fa-folder-open-o"></i>
+ <%= foldernode[:object] %>
+ <% elsif show_root_node and rowtype == User %>
+ <% if foldernode[:object].uuid == current_user.andand.uuid %>
+ <i class="fa fa-fw fa-folder-open-o"></i>
+ My Folders
+ <% else %>
+ <i class="fa fa-fw fa-folder-o"></i>
+ <%= foldernode[:object].friendly_link_name %>
+ <% end %>
+ <% elsif rowtype == Group %>
+ <i class="fa fa-fw fa-folder-o"></i>
+ <% opts = {} %>
+ <% opts[:title] = foldernode[:object].description %>
+ <% opts[:'data-toggle'] = 'tooltip' %>
+ <% opts[:'data-placement'] = 'bottom' %>
+ <%= link_to foldernode[:object], opts do %>
+ <%= foldernode[:object].friendly_link_name %>
+ <% end %>
+ <% end %>
+ </div>
+ </div>
+ <% end %>
+</div>
diff --git a/apps/workbench/app/views/folders/_show_contents.html.erb b/apps/workbench/app/views/folders/_show_contents.html.erb
index ce9f1b1..3a11930 100644
--- a/apps/workbench/app/views/folders/_show_contents.html.erb
+++ b/apps/workbench/app/views/folders/_show_contents.html.erb
@@ -67,7 +67,7 @@
</div>
</div>
-<table class="table table-condensed arv-index arv-folder-contents">
+<table class="table table-condensed table-fixedlayout arv-index arv-folder-contents" style="overflow-x: hidden">
<colgroup>
<col width="40%" />
<col width="60%" />
diff --git a/apps/workbench/app/views/folders/_show_contents_rows.html.erb b/apps/workbench/app/views/folders/_show_contents_rows.html.erb
index c20bf27..54e7ff6 100644
--- a/apps/workbench/app/views/folders/_show_contents_rows.html.erb
+++ b/apps/workbench/app/views/folders/_show_contents_rows.html.erb
@@ -6,21 +6,23 @@
>
<td>
<% if folder.editable? %>
- <%= link_to({action: 'remove_item', id: folder.uuid, item_uuid: ((name_link && name_link.uuid) || object.uuid)}, method: :delete, remote: true, data: {confirm: "Remove #{object.class_for_display.downcase} #{name_object.name rescue object.uuid} from this folder?", toggle: 'tooltip', placement: 'top'}, class: 'btn btn-xs btn-default btn-nodecorate', title: 'remove') do %>
+ <%= link_to({action: 'remove_item', id: folder.uuid, item_uuid: ((name_link && name_link.uuid) || object.uuid)}, method: :delete, remote: true, data: {confirm: "Remove #{object.class_for_display.downcase} #{name_object.name rescue object.uuid} from this folder?", toggle: 'tooltip', placement: 'top'}, class: 'btn btn-sm btn-default btn-nodecorate', title: 'remove') do %>
<i class="fa fa-fw fa-ban"></i>
<% end %>
<% else %>
<i class="fa fa-fw"></i><%# placeholder %>
<% end %>
- <%= render :partial => "show_object_button", :locals => {object: object, size: 'xs'} %>
-
- <i class="fa fa-fw <%= fa_icon_class_for_object(object) %>"></i>
+ <%= render :partial => "show_object_button", :locals => {object: object, size: 'sm'} %>
<%= render_editable_attribute name_object, 'name', nil %>
</td>
- <td>
- <%= object.content_summary %>
+ <td class="arv-description-in-table">
+ <%= render_controller_partial(
+ 'show_object_description_cell.html',
+ controller_name: object.controller_name,
+ locals: {object: object})
+ %>
</td>
</tr>
<% end %>
diff --git a/apps/workbench/app/views/folders/_show_folders.html.erb b/apps/workbench/app/views/folders/_show_folders.html.erb
deleted file mode 100644
index 8c4f0d8..0000000
--- a/apps/workbench/app/views/folders/_show_folders.html.erb
+++ /dev/null
@@ -1,33 +0,0 @@
-<div class="container-fluid arv-folder-list">
- <% [@my_folder_tree, @shared_folder_tree].each do |tree| %>
- <% tree.each do |foldernode| %>
- <% rowtype = foldernode[:object].class %>
- <div class="<%= 'folder' if rowtype == Group %> row">
- <div class="col-lg-4" style="padding-left: <%= 1 + foldernode[:depth] %>em;">
- <% if rowtype == String %>
- <i class="fa fa-fw fa-folder-open-o"></i>
- <%= foldernode[:object] %>
- <% elsif rowtype == User %>
- <% if foldernode[:object].uuid == current_user.andand.uuid %>
- <i class="fa fa-fw fa-folder-open-o"></i>
- My Folders
- <% else %>
- <i class="fa fa-fw fa-folder-o"></i>
- <%= foldernode[:object].friendly_link_name %>
- <% end %>
- <% else %>
- <i class="fa fa-fw fa-folder-o"></i>
- <%= link_to foldernode[:object] do %>
- <%= foldernode[:object].friendly_link_name %>
- <% end %>
- <% end %>
- </div>
- <div class="col-lg-8 arv-description-in-table">
- <% if rowtype == Group %>
- <%= foldernode[:object].description %>
- <% end %>
- </div>
- </div>
- <% end %>
- <% end %>
-</div>
diff --git a/apps/workbench/app/views/folders/index.html.erb b/apps/workbench/app/views/folders/index.html.erb
new file mode 100644
index 0000000..7666e54
--- /dev/null
+++ b/apps/workbench/app/views/folders/index.html.erb
@@ -0,0 +1,57 @@
+<% content_for :breadcrumbs do %>
+<li class="nav-separator"><span class="glyphicon glyphicon-arrow-right"></span></li>
+<li><a href="#">Home</a></li>
+<% end %>
+
+<div class="container-fluid">
+ <div class="row">
+ <div class="col-sm-6">
+ <% if my_folders.empty? %>
+ <div class="panel panel-info">
+ <div class="panel-heading">
+ <h3 class="panel-title">
+ Welcome to <b><%= Rails.configuration.site_name %></b>.
+ </h3>
+ </div>
+ <div class="panel-body">
+ <img src="/favicon.ico" class="pull-right" alt="" style="opacity: 0.3"/>
+ <p>
+ This site runs Arvados, the open source biomedical analysis platform. <a href="https://arvados.org" target="_blank">Learn more…</a>
+ </p>
+ <p>
+ <b>To get started,</b> create a folder using the "Add new folder" button below.
+ </p>
+ </div>
+ </div>
+ <% end %>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <div class="pull-right">
+ <%= button_to folders_path(method: 'post'), class: 'btn btn-xs btn-primary' do %>
+ <i class="fa fa-fw fa-plus"></i>
+ Add new folder
+ <% end %>
+ </div>
+ <h3 class="panel-title">
+ My folders
+ </h3>
+ </div>
+ <div class="panel-body scroll-20em">
+ <%= render partial: 'index_folders', locals: {tree: @my_folder_tree, show_root_node: false} %>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h3 class="panel-title">
+ Shared folders
+ </h3>
+ </div>
+ <div class="panel-body scroll-20em">
+ <%= render partial: 'index_folders', locals: {tree: @shared_folder_tree, show_root_node: false} %>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/apps/workbench/app/views/layouts/application.html.erb b/apps/workbench/app/views/layouts/application.html.erb
index 0ec08cc..631595f 100644
--- a/apps/workbench/app/views/layouts/application.html.erb
+++ b/apps/workbench/app/views/layouts/application.html.erb
@@ -167,7 +167,10 @@
<span class="glyphicon glyphicon-cog"></span><span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
- <li role="presentation" class="dropdown-header">System objects</li>
+ <li role="presentation" class="dropdown-header">
+ <i class="fa fa-lg fa-fw"></i>
+ System tools
+ </li>
<li role="presentation" class="divider"></li>
<li role="presentation"><a href="/repositories">
<i class="fa fa-lg fa-code-fork fa-fw"></i> Repositories
diff --git a/apps/workbench/app/views/pipeline_instances/_show_components.html.erb b/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
index b08e0b8..b8f94d5 100644
--- a/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
+++ b/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
@@ -9,10 +9,9 @@
<%= render_editable_attribute @object, 'name', nil %>
</h2>
<% if template %>
- <h4>
- From template:
+ <blockquote><span class="deemphasize">From template:</span><br />
<%= link_to_if_arvados_object template, friendly_name: true %>
- </h4>
+ </blockquote>
<% end %>
<% end %>
commit 1b981c1ca2e3d4d5aa6841fe2b334a19cc11f4f5
Author: Tom Clegg <tom at curoverse.com>
Date: Sun Jun 1 14:31:48 2014 -0400
2872: Add "delete folder" button.
diff --git a/apps/workbench/app/views/application/_content_layout.html.erb b/apps/workbench/app/views/application/_content_layout.html.erb
index c7ff33b..1bc0b85 100644
--- a/apps/workbench/app/views/application/_content_layout.html.erb
+++ b/apps/workbench/app/views/application/_content_layout.html.erb
@@ -1,3 +1,5 @@
<%= content_for :content_top %>
-<%= content_for :tab_line_buttons %>
+<div class="pull-right">
+ <%= content_for :tab_line_buttons %>
+</div>
<%= content_for :tab_panes %>
diff --git a/apps/workbench/app/views/application/_delete_object_button.html.erb b/apps/workbench/app/views/application/_delete_object_button.html.erb
index 69ed9dc..6d6383e 100644
--- a/apps/workbench/app/views/application/_delete_object_button.html.erb
+++ b/apps/workbench/app/views/application/_delete_object_button.html.erb
@@ -1,5 +1,5 @@
<% if object.editable? %>
- <%= link_to({action: 'destroy', id: object.uuid}, method: :delete, remote: true, data: {confirm: "You are about to delete #{object.class_for_display.downcase} '#{object.friendly_link_name}' (#{object.uuid}).\n\nAre you sure?"}) do %>
+ <%= link_to({action: 'destroy', id: object.uuid}, method: :delete, remote: true, data: {confirm: "Really delete #{object.class_for_display.downcase} '#{object.friendly_link_name}'?"}) do %>
<i class="glyphicon glyphicon-trash"></i>
<% end %>
<% end %>
diff --git a/apps/workbench/app/views/application/index.html.erb b/apps/workbench/app/views/application/index.html.erb
index 20af648..0e72f7a 100644
--- a/apps/workbench/app/views/application/index.html.erb
+++ b/apps/workbench/app/views/application/index.html.erb
@@ -7,14 +7,18 @@
<% if controller.model_class.creatable? %>
<% if controller.model_class.name == 'User' %>
- <%= link_to "Add a new #{controller.model_class.to_s.underscore.gsub '_', ' '}", setup_user_popup_path,
- {class: 'btn btn-primary pull-right', :remote => true, 'data-toggle' => "modal",
- 'data-target' => '#user-setup-modal-window', return_to: request.url} %>
+ <%= link_to setup_user_popup_path,
+ {class: 'btn btn-sm btn-primary', :remote => true, 'data-toggle' => "modal",
+ 'data-target' => '#user-setup-modal-window', return_to: request.url} do %>
+ <i class="fa fa-fw fa-plus"></i> Add a new user
+ <% end %>
<div id="user-setup-modal-window" class="modal fade" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"></div>
<% else %>
- <%= button_to "Add a new #{controller.controller_name.singularize.humanize.downcase}",
- { action: 'create' },
- { class: 'btn btn-primary pull-right' } %>
+ <%= button_to({action: 'create'}, {class: 'btn btn-sm btn-primary'}) do %>
+ <i class="fa fa-fw fa-plus"></i>
+ Add a new
+ <%= controller.controller_name.singularize.humanize.downcase %>
+ <% end %>
<% end %>
<% end %>
diff --git a/apps/workbench/app/views/folders/_show_contents.html.erb b/apps/workbench/app/views/folders/_show_contents.html.erb
index 63b6abc..ce9f1b1 100644
--- a/apps/workbench/app/views/folders/_show_contents.html.erb
+++ b/apps/workbench/app/views/folders/_show_contents.html.erb
@@ -10,6 +10,14 @@
<% end %>
+<% content_for :tab_line_buttons do %>
+ <% if @objects_and_names.empty? %>
+ <%= button_to(folder_path(id: @object.uuid, return_to: folders_path), method: 'delete', class: 'btn btn-sm btn-primary', data: {confirm: "Really delete folder '#{@object.name}'?"}) do %>
+ <i class="fa fa-fw fa-trash-o"></i> Delete folder
+ <% end %>
+ <% end %>
+<% end %>
+
<div class="container-fluid">
<div class="row">
<div class="col-md-4">
commit 5758bd5c9ee5a253c695431f98d9e95b843e798e
Author: Tom Clegg <tom at curoverse.com>
Date: Sun Jun 1 14:11:41 2014 -0400
2872: Fix "remove from folder"
diff --git a/apps/workbench/app/controllers/folders_controller.rb b/apps/workbench/app/controllers/folders_controller.rb
index eae92ba..b7b4854 100644
--- a/apps/workbench/app/controllers/folders_controller.rb
+++ b/apps/workbench/app/controllers/folders_controller.rb
@@ -36,7 +36,7 @@ class FoldersController < ApplicationController
if item.owner_uuid == @object.uuid
# Object is owned by this folder. Remove it from the folder by
# changing owner to the current user.
- item.update_attributes owner_uuid: current_user
+ item.update_attributes owner_uuid: current_user.uuid
@removed_uuids << item.uuid
end
end
commit 605522fc29649de26b7038e73339ffb35d12768e
Author: Tom Clegg <tom at curoverse.com>
Date: Sun Jun 1 14:04:18 2014 -0400
2872: Make editable work for names in folder, choose data from current folder, tweak css.
diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb
index e839dd1..97ef55a 100644
--- a/apps/workbench/app/controllers/application_controller.rb
+++ b/apps/workbench/app/controllers/application_controller.rb
@@ -452,4 +452,30 @@ class ApplicationController < ActionController::Base
@notification_count = ''
end
end
+
+ helper_method :my_folders
+ def my_folders
+ return @my_folders if @my_folders
+ @my_folders = []
+ root_of = {}
+ Group.filter([['group_class','=','folder']]).each do |g|
+ root_of[g.uuid] = g.owner_uuid
+ @my_folders << g
+ end
+ done = false
+ while not done
+ done = true
+ root_of = root_of.each_with_object({}) do |(child, parent), h|
+ if root_of[parent]
+ h[child] = root_of[parent]
+ done = false
+ else
+ h[child] = parent
+ end
+ end
+ end
+ @my_folders = @my_folders.select do |g|
+ root_of[g.uuid] == current_user.uuid
+ end
+ end
end
diff --git a/apps/workbench/app/controllers/collections_controller.rb b/apps/workbench/app/controllers/collections_controller.rb
index ece8ccc..4ba23c5 100644
--- a/apps/workbench/app/controllers/collections_controller.rb
+++ b/apps/workbench/app/controllers/collections_controller.rb
@@ -41,11 +41,11 @@ class CollectionsController < ApplicationController
end
def choose
- @name_links = Link.
+ @objects = Link.
filter([['link_class','=','name'],
['head_uuid','is_a','arvados#collection']])
- @name_links.collect { |x| x }
- $stderr.puts @name_links.inspect
+ find_objects_for_index
+ @name_links = @objects
@objects = Collection.
filter([['uuid','in', at name_links.collect(&:head_uuid)]])
super
diff --git a/apps/workbench/app/helpers/application_helper.rb b/apps/workbench/app/helpers/application_helper.rb
index 4749e0e..117f808 100644
--- a/apps/workbench/app/helpers/application_helper.rb
+++ b/apps/workbench/app/helpers/application_helper.rb
@@ -134,7 +134,11 @@ module ApplicationHelper
def render_editable_attribute(object, attr, attrvalue=nil, htmloptions={})
attrvalue = object.send(attr) if attrvalue.nil?
- return attrvalue if !object.attribute_editable? attr
+ if !object.attribute_editable?(attr, :ever) or
+ (!object.editable? and
+ !object.owner_uuid.in?(my_folders.collect(&:uuid)))
+ return attrvalue
+ end
input_type = 'text'
case object.class.attribute_info[attr.to_sym].andand[:type]
@@ -208,7 +212,9 @@ module ApplicationHelper
end
end
- unless object.andand.attribute_editable? attr
+ if !object.attribute_editable?(attr, :ever) or
+ (!object.editable? and
+ !object.owner_uuid.in?(my_folders.collect(&:uuid)))
return link_to_if_arvados_object attrvalue
end
@@ -242,6 +248,7 @@ module ApplicationHelper
end
modal_path = choose_collections_path \
({ title: 'Choose a dataset:',
+ filters: [['owner_uuid', '=', object.owner_uuid]].to_json,
action_name: 'OK',
action_href: pipeline_instance_path(id: object.uuid),
action_method: 'patch',
diff --git a/apps/workbench/app/models/arvados_base.rb b/apps/workbench/app/models/arvados_base.rb
index a4f12d3..cb45701 100644
--- a/apps/workbench/app/models/arvados_base.rb
+++ b/apps/workbench/app/models/arvados_base.rb
@@ -305,13 +305,15 @@ class ArvadosBase < ActiveRecord::Base
(writable_by.include? current_user.uuid rescue false)))
end
- def attribute_editable?(attr)
+ def attribute_editable?(attr, ever=nil)
if "created_at modified_at modified_by_user_uuid modified_by_client_uuid updated_at".index(attr.to_s)
false
elsif not (current_user.andand.is_active)
false
elsif attr == 'uuid'
current_user.is_admin
+ elsif ever
+ true
else
editable?
end
diff --git a/apps/workbench/app/models/pipeline_instance.rb b/apps/workbench/app/models/pipeline_instance.rb
index beeba62..3e9eda2 100644
--- a/apps/workbench/app/models/pipeline_instance.rb
+++ b/apps/workbench/app/models/pipeline_instance.rb
@@ -29,9 +29,10 @@ class PipelineInstance < ArvadosBase
end
end
- def attribute_editable?(attr)
- attr && (attr.to_sym == :name ||
- (attr.to_sym == :components and (self.state == 'New' || self.state == 'Ready')))
+ def attribute_editable? attr, *args
+ super(attr, *args) && (attr.to_sym == :name ||
+ (attr.to_sym == :components and
+ (self.state == 'New' || self.state == 'Ready')))
end
def attributes_for_display
diff --git a/apps/workbench/app/views/folders/_show_contents.html.erb b/apps/workbench/app/views/folders/_show_contents.html.erb
index ecfd405..63b6abc 100644
--- a/apps/workbench/app/views/folders/_show_contents.html.erb
+++ b/apps/workbench/app/views/folders/_show_contents.html.erb
@@ -12,7 +12,7 @@
<div class="container-fluid">
<div class="row">
- <div class="col-lg-4">
+ <div class="col-md-4">
<% if @object.editable? %>
<%= link_to(
choose_collections_path(
@@ -37,14 +37,14 @@
<% end %>
<% end %>
</div>
- <div class="col-lg-4">
+ <div class="col-md-4">
<form class="form-inline" role="form">
Show:
<select class="form-control" data-filter-rows-by="data-kind" data-filter-target="table.arv-index.arv-folder-contents tbody">
<option value="">Everything</option>
<option value="arvados#collection">Data</option>
- <option value="arvados#pipeline_instance arvados#job">Compute jobs</option>
- <option value="arvados#pipeline_template">Pipelines</option>
+ <option value="arvados#pipelineInstance arvados#job">Compute jobs</option>
+ <option value="arvados#pipelineTemplate">Pipelines</option>
<!--
<option value="arvados#specimen">Specimens</option>
<option value="arvados#human">Humans</option>
@@ -53,7 +53,7 @@
</select>
</form>
</div>
- <div class="col-lg-4">
+ <div class="col-md-4">
<input type="text" class="form-control search-folder-contents" placeholder="Search folder contents" data-search-target=".arv-index.arv-folder-contents"/>
</div>
</div>
diff --git a/apps/workbench/app/views/folders/_show_permissions.html.erb b/apps/workbench/app/views/folders/_show_permissions.html.erb
index 41a7cd7..262cb3b 100644
--- a/apps/workbench/app/views/folders/_show_permissions.html.erb
+++ b/apps/workbench/app/views/folders/_show_permissions.html.erb
@@ -1,6 +1,6 @@
<div class="container-fluid">
<div class="row">
- <div class="col-lg-6">
+ <div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
Additional permissions
@@ -19,7 +19,7 @@
</div>
</div>
</div>
- <div class="col-lg-6">
+ <div class="col-md-6">
<% if @object.owner %>
<div class="panel panel-default">
<div class="panel-heading">
commit 1b79dc4a25091e035a1c02693ecfcb8453649c27
Author: Tom Clegg <tom at curoverse.com>
Date: Sun Jun 1 05:45:40 2014 -0400
2872: Rearrange standard views to deemphasize "advanced" usage, add infinite scroll
diff --git a/apps/workbench/app/assets/javascripts/folders.js b/apps/workbench/app/assets/javascripts/folders.js
index ff1c7b8..73dcfbb 100644
--- a/apps/workbench/app/assets/javascripts/folders.js
+++ b/apps/workbench/app/assets/javascripts/folders.js
@@ -1,20 +1,42 @@
$(document).
on('paste keyup change', 'input.search-folder-contents', function() {
var q = new RegExp($(this).val(), 'i');
- $(this).closest('div.panel').find('tbody tr').each(function() {
- $(this).toggle(!!$(this).text().match(q));
- });
+ $($(this).attr('data-search-target')).find('tbody').
+ data('q', q).
+ trigger('refresh');
+ }).on('refresh', 'tbody', function() {
+ var q = $(this).data('q');
+ var filters = $(this).data('filters');
+ $('tr', this).hide();
+ $('tr', this).filter(function() {
+ var $row = $(this);
+ var pass = true;
+ if (q && !$row.text().match(q))
+ return false;
+ if (filters) {
+ $.each(filters, function(filterby, val) {
+ if (!val) return;
+ if (!pass) return;
+ pass = false;
+ $.each(val.split(" "), function(i, e) {
+ if ($row.attr(filterby) == e)
+ pass = true;
+ });
+ });
+ }
+ return pass;
+ }).show();
+ $('.infinite-scroller').trigger('scroll');
}).on('change', 'select[data-filter-rows-by]', function() {
var val = $(this).val();
var filterby = $(this).attr('data-filter-rows-by');
- var $target = $($(this).attr('data-filter-in'));
- if (val == '') {
- $target.find('.filterable').show();
- } else {
- $target.find('.filterable').hide();
- console.log('.filterable[' + filterby + '="' + val + '"]');
- $.each(val.split(" "), function(i, e) {
- $target.find('.filterable['+filterby+'="'+e+'"]').show();
- });
- }
+ var $target = $($(this).attr('data-filter-target'));
+ var filters = $target.data('filters') || {};
+ filters[filterby] = val;
+ $target.
+ data('filters', filters).
+ trigger('refresh');
+ }).on('ajax:complete', function() {
+ $('input.search-folder-contents').trigger('change');
+ $('select[data-filter-rows-by]').trigger('change');
});
diff --git a/apps/workbench/app/assets/javascripts/infinite_scroll.js b/apps/workbench/app/assets/javascripts/infinite_scroll.js
new file mode 100644
index 0000000..48b8136
--- /dev/null
+++ b/apps/workbench/app/assets/javascripts/infinite_scroll.js
@@ -0,0 +1,47 @@
+function maybe_load_more_content() {
+ var scroller = this; // element with scroll bars
+ var container; // element that receives new content
+ var src; // url for retrieving content
+ if ($(scroller).scrollTop() + $(scroller).height()
+ >
+ scroller.scrollHeight - 50) {
+ container = $(this).data('infinite-container');
+ src = $(container).attr('data-infinite-content-href');
+ if (!src)
+ // Finished
+ return;
+ // Don't start another request until this one finishes
+ $(container).attr('data-infinite-content-href', null);
+ $.ajax(src,
+ {dataType: 'json',
+ type: 'GET',
+ data: {},
+ context: {container: container, src: src}}).
+ fail(function(jqxhr, status, error) {
+ if (jqxhr.readyState == 0 || jqxhr.status == 0) {
+ message = "Cancelled."
+ } else if (jqxhr.responseJSON && jqxhr.responseJSON.errors) {
+ message = jqxhr.responseJSON.errors.join("; ");
+ } else {
+ message = "Request failed.";
+ }
+ // TODO: report this to the user.
+ console.log(message);
+ $(this.container).attr('data-infinite-content-href', this.src);
+ }).
+ done(function(data, status, jqxhr) {
+ $(this.container).append(data.content);
+ $(this.container).attr('data-infinite-content-href', data.next_page_href);
+ $(document).trigger('ajax:complete');
+ });
+ }
+}
+$(document).
+ on('ready ajax:complete', function() {
+ $('[data-infinite-scroller]').each(function() {
+ $($(this).attr('data-infinite-scroller')).
+ addClass('infinite-scroller').
+ data('infinite-container', this).
+ on('scroll', maybe_load_more_content);
+ });
+ });
diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb
index 4aa79fb..e839dd1 100644
--- a/apps/workbench/app/controllers/application_controller.rb
+++ b/apps/workbench/app/controllers/application_controller.rb
@@ -88,6 +88,20 @@ class ApplicationController < ActionController::Base
@objects = @objects.filter(@filters).limit(@limit).offset(@offset)
end
+ helper_method :next_page_offset
+ def next_page_offset
+ if @objects.respond_to?(:result_offset) and
+ @objects.respond_to?(:result_limit) and
+ @objects.respond_to?(:items_available)
+ next_offset = @objects.result_offset + @objects.result_limit
+ if next_offset < @objects.items_available
+ next_offset
+ else
+ nil
+ end
+ end
+ end
+
def index
find_objects_for_index if !@objects
respond_to do |f|
@@ -115,7 +129,7 @@ class ApplicationController < ActionController::Base
end
def choose
- find_objects_for_index
+ find_objects_for_index if !@objects
render partial: 'choose', locals: {multiple: params[:multiple]}
end
@@ -198,7 +212,7 @@ class ApplicationController < ActionController::Base
end
def show_pane_list
- %w(Attributes Metadata Advanced)
+ %w(Attributes Advanced)
end
protected
diff --git a/apps/workbench/app/controllers/collections_controller.rb b/apps/workbench/app/controllers/collections_controller.rb
index 9371bd1..ece8ccc 100644
--- a/apps/workbench/app/controllers/collections_controller.rb
+++ b/apps/workbench/app/controllers/collections_controller.rb
@@ -7,7 +7,7 @@ class CollectionsController < ApplicationController
RELATION_LIMIT = 5
def show_pane_list
- %w(Files Attributes Metadata Provenance_graph Used_by JSON API)
+ %w(Files Provenance_graph Used_by Advanced)
end
def set_persistent
@@ -44,6 +44,8 @@ class CollectionsController < ApplicationController
@name_links = Link.
filter([['link_class','=','name'],
['head_uuid','is_a','arvados#collection']])
+ @name_links.collect { |x| x }
+ $stderr.puts @name_links.inspect
@objects = Collection.
filter([['uuid','in', at name_links.collect(&:head_uuid)]])
super
diff --git a/apps/workbench/app/controllers/folders_controller.rb b/apps/workbench/app/controllers/folders_controller.rb
index 73e24bc..eae92ba 100644
--- a/apps/workbench/app/controllers/folders_controller.rb
+++ b/apps/workbench/app/controllers/folders_controller.rb
@@ -8,7 +8,7 @@ class FoldersController < ApplicationController
end
def show_pane_list
- %w(Contents Permissions)
+ %w(Contents Permissions Advanced)
end
def remove_item
@@ -82,7 +82,8 @@ class FoldersController < ApplicationController
end
def show
- @objects = @object.contents include_linked: true
+ @objects = @object.contents(include_linked: true,
+ offset: params[:offset] || 0)
@share_links = Link.filter([['head_uuid', '=', @object.uuid],
['link_class', '=', 'permission']])
@logs = Log.limit(10).filter([['object_uuid', '=', @object.uuid]])
@@ -95,13 +96,31 @@ class FoldersController < ApplicationController
end
else
@objects_and_names << [object,
- Link.new(tail_uuid: @object.uuid,
+ Link.new(owner_uuid: @object.uuid,
+ tail_uuid: @object.uuid,
head_uuid: object.uuid,
link_class: "name",
name: "")]
end
end
- super
+ if params[:partial]
+ respond_to do |f|
+ f.json {
+ render json: {
+ content: render_to_string(partial: 'show_contents_rows.html',
+ formats: [:html],
+ locals: {
+ objects_and_names: @objects_and_names,
+ folder: @object
+ }),
+ next_page_href: (next_page_offset and
+ url_for(offset: next_page_offset, partial: true))
+ }
+ }
+ end
+ else
+ super
+ end
end
def create
diff --git a/apps/workbench/app/controllers/pipeline_templates_controller.rb b/apps/workbench/app/controllers/pipeline_templates_controller.rb
index afe41c4..2be51c6 100644
--- a/apps/workbench/app/controllers/pipeline_templates_controller.rb
+++ b/apps/workbench/app/controllers/pipeline_templates_controller.rb
@@ -6,6 +6,6 @@ class PipelineTemplatesController < ApplicationController
end
def show_pane_list
- %w(Components Pipelines Attributes Metadata JSON API)
+ %w(Components Pipelines Advanced)
end
end
diff --git a/apps/workbench/app/helpers/application_helper.rb b/apps/workbench/app/helpers/application_helper.rb
index a463a50..4749e0e 100644
--- a/apps/workbench/app/helpers/application_helper.rb
+++ b/apps/workbench/app/helpers/application_helper.rb
@@ -161,7 +161,7 @@ module ApplicationHelper
ajax_options['data-pk'][:defaults] = object.attributes
end
ajax_options['data-pk'] = ajax_options['data-pk'].to_json
- @unique_id ||= 0
+ @unique_id ||= (Time.now.to_f*1000000).to_i
span_id = object.uuid.to_s + '-' + attr.to_s + '-' + (@unique_id += 1).to_s
span_tag = content_tag 'span', attrvalue.to_s, {
diff --git a/apps/workbench/app/models/arvados_base.rb b/apps/workbench/app/models/arvados_base.rb
index a6aded9..a4f12d3 100644
--- a/apps/workbench/app/models/arvados_base.rb
+++ b/apps/workbench/app/models/arvados_base.rb
@@ -364,7 +364,11 @@ class ArvadosBase < ActiveRecord::Base
# Placeholder for name when name is missing or empty
def default_name
- "New #{class_for_display.downcase}"
+ if self.respond_to? :name
+ "New #{class_for_display.downcase}"
+ else
+ uuid
+ end
end
def owner
diff --git a/apps/workbench/app/models/collection.rb b/apps/workbench/app/models/collection.rb
index 90e53e8..ae86108 100644
--- a/apps/workbench/app/models/collection.rb
+++ b/apps/workbench/app/models/collection.rb
@@ -25,6 +25,7 @@ class Collection < ArvadosBase
end
def files_tree
+ return [] if files.empty?
tree = files.group_by { |file_spec| File.split(file_spec.first) }
# Fill in entries for empty directories.
tree.keys.map { |basedir, _| File.split(basedir) }.each do |splitdir|
diff --git a/apps/workbench/app/models/job.rb b/apps/workbench/app/models/job.rb
index 56428ab..6fe3743 100644
--- a/apps/workbench/app/models/job.rb
+++ b/apps/workbench/app/models/job.rb
@@ -10,4 +10,19 @@ class Job < ArvadosBase
def self.creatable?
false
end
+
+ def default_name
+ if script
+ x = "\"#{script}\" job"
+ else
+ x = super
+ end
+ if finished_at
+ x += " finished #{finished_at.strftime('%b %-d')}"
+ elsif started_at
+ x += " started #{started_at.strftime('%b %-d')}"
+ elsif created_at
+ x += " submitted #{created_at.strftime('%b %-d')}"
+ end
+ end
end
diff --git a/apps/workbench/app/models/link.rb b/apps/workbench/app/models/link.rb
index 5e7b42a..868082b 100644
--- a/apps/workbench/app/models/link.rb
+++ b/apps/workbench/app/models/link.rb
@@ -4,4 +4,8 @@ class Link < ArvadosBase
def self.by_tail(t, opts={})
where(opts.merge :tail_uuid => t.uuid)
end
+
+ def default_name
+ self.class.resource_class_for_uuid(head_uuid).default_name rescue super
+ end
end
diff --git a/apps/workbench/app/views/application/_arvados_attr_value.html.erb b/apps/workbench/app/views/application/_arvados_attr_value.html.erb
index 3efca72..8f82b04 100644
--- a/apps/workbench/app/views/application/_arvados_attr_value.html.erb
+++ b/apps/workbench/app/views/application/_arvados_attr_value.html.erb
@@ -3,7 +3,7 @@
<%= message %><br />
<% end %>
<% else %>
- <% if obj.attribute_editable?(attr) %>
+ <% if obj.attribute_editable?(attr) and (!defined?(editable) || editable) %>
<%= render_editable_attribute obj, attr %>
<% if resource_class_for_uuid(attrvalue, {referring_object: obj, referring_attr: attr}) %>
<br />
diff --git a/apps/workbench/app/views/application/_content.html.erb b/apps/workbench/app/views/application/_content.html.erb
index 8a0624b..32c053f 100644
--- a/apps/workbench/app/views/application/_content.html.erb
+++ b/apps/workbench/app/views/application/_content.html.erb
@@ -15,7 +15,7 @@
<div class="tab-content">
<% panes.each_with_index do |(pane, content), i| %>
<div id="<%= pane %>" class="tab-pane fade <%= 'in active' if i==0 %>">
- <div class="smart-scroll" style="margin-top:0.5em;">
+ <div id="<%= pane %>-scroll" class="smart-scroll" style="margin-top:0.5em;">
<%= content %>
</div>
</div>
diff --git a/apps/workbench/app/views/application/_show_advanced.html.erb b/apps/workbench/app/views/application/_show_advanced.html.erb
index d31b5f5..e8474f6 100644
--- a/apps/workbench/app/views/application/_show_advanced.html.erb
+++ b/apps/workbench/app/views/application/_show_advanced.html.erb
@@ -1,5 +1,6 @@
<div class="panel-group" id="arv-adv-accordion">
- <% ['API response',
+ <% ['Metadata',
+ 'API response',
'Python example',
'CLI example',
'curl example'].each do |section| %>
diff --git a/apps/workbench/app/views/application/_show_advanced_metadata.html.erb b/apps/workbench/app/views/application/_show_advanced_metadata.html.erb
new file mode 100644
index 0000000..68e4298
--- /dev/null
+++ b/apps/workbench/app/views/application/_show_advanced_metadata.html.erb
@@ -0,0 +1,44 @@
+<% outgoing = Link.where(tail_uuid: @object.uuid) %>
+<% incoming = Link.where(head_uuid: @object.uuid) %>
+
+<% if (outgoing | incoming).any? %>
+<table class="table topalign">
+ <colgroup>
+ <col width="20%" />
+ <col width="10%" />
+ <col width="10%" />
+ <col width="20%" />
+ <col width="20%" />
+ <col width="20%" />
+ </colgroup>
+ <thead>
+ <tr>
+ <th></th>
+ <th>link_class</th>
+ <th>name</th>
+ <th>tail</th>
+ <th>head</th>
+ <th>properties</th>
+ </tr>
+ </thead>
+ <tbody>
+ <% (outgoing | incoming).each do |link| %>
+ <tr>
+ <td>
+ <%= render partial: 'show_object_button', locals: { object: link, size: 'xs' } %>
+ <span class="arvados-uuid"><%= link.uuid %></span>
+ </td>
+ <td><%= link.link_class %></td>
+ <td><%= link.name %></td>
+ <td><%= link.tail_uuid == object.uuid ? 'this' : (render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "tail_uuid", attrvalue: link.tail_uuid, editable: false }) %></td>
+ <td><%= link.head_uuid == object.uuid ? 'this' : (render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "head_uuid", attrvalue: link.head_uuid, editable: false }) %></td>
+ <td><%= render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "properties", attrvalue: link.properties, editable: false } %></td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
+<% else %>
+<span class="deemphasize">
+ (No metadata links found)
+</span>
+<% end %>
diff --git a/apps/workbench/app/views/application/_show_metadata.html.erb b/apps/workbench/app/views/application/_show_metadata.html.erb
deleted file mode 100644
index 551806f..0000000
--- a/apps/workbench/app/views/application/_show_metadata.html.erb
+++ /dev/null
@@ -1,58 +0,0 @@
-<% outgoing = Link.where(tail_uuid: @object.uuid) %>
-<% incoming = Link.where(head_uuid: @object.uuid) %>
-
-<h3>Metadata about this object</h3>
-<% if outgoing.items_available > 0 %>
-<table class="table topalign">
- <thead>
- <tr>
- <th>metadata uuid</th>
- <th>class</th>
- <th>name</th>
- <th>properties</th>
- <th>object</th>
- </tr>
- </thead>
- <tbody>
- <% outgoing.each do |link| %>
- <tr>
- <td><%= render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "uuid", attrvalue: link.uuid } %></td>
- <td><%= render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "link_class", attrvalue: link.link_class } %></td>
- <td><%= render_editable_attribute link, 'name' %></td>
- <td><%= render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "properties", attrvalue: link.properties } %></td>
- <td><%= render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "head_uuid", attrvalue: link.head_uuid } %></td>
- </tr>
- <% end %>
- </tbody>
-</table>
-<% else %>
-No metadata.
-<% end %>
-
-<h3>Metadata that refers to this object</h3>
-<% if incoming.items_available > 0 %>
-<table class="table topalign">
- <thead>
- <tr>
- <th>metadata uuid</th>
- <th>subject</th>
- <th>class</th>
- <th>name</th>
- <th>properties</th>
- </tr>
- </thead>
- <tbody>
- <% incoming.each do |link| %>
- <tr>
- <td><%= render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "uuid", attrvalue: link.uuid } %></td>
- <td><%= render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "tail_uuid", attrvalue: link.tail_uuid } %></td>
- <td><%= render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "link_class", attrvalue: link.link_class } %></td>
- <td><%= render_editable_attribute link, 'name' %></td>
- <td><%= render partial: 'application/arvados_attr_value', locals: { obj: link, attr: "properties", attrvalue: link.properties } %></td>
- </tr>
- <% end %>
- </tbody>
-</table>
-<% else %>
-No metadata.
-<% end %>
diff --git a/apps/workbench/app/views/collections/show.html.erb b/apps/workbench/app/views/collections/show.html.erb
index 440d2ff..16497c7 100644
--- a/apps/workbench/app/views/collections/show.html.erb
+++ b/apps/workbench/app/views/collections/show.html.erb
@@ -13,19 +13,19 @@
<% end %>
<% if @output_of.andand.any? %>
- <p>Output of jobs:<br />
+ <p>This collection was the output of:<br />
<%= render_arvados_object_list_start(@output_of, 'Show all jobs',
jobs_path(filter: [['output', '=', @object.uuid]].to_json)) do |job| %>
- <%= link_to_if_arvados_object(job, friendly_name: true) %><br />
+ <%= link_to_if_arvados_object(job, friendly_name: true) %><br />
<% end %>
</p>
<% end %>
<% if @log_of.andand.any? %>
- <p>Log of jobs:<br />
+ <p>This collection contains log messages from:<br />
<%= render_arvados_object_list_start(@log_of, 'Show all jobs',
jobs_path(filter: [['log', '=', @object.uuid]].to_json)) do |job| %>
- <%= link_to_if_arvados_object(job, friendly_name: true) %><br />
+ <%= link_to_if_arvados_object(job, friendly_name: true) %><br />
<% end %>
</p>
<% end %>
diff --git a/apps/workbench/app/views/folders/_show_contents.html.erb b/apps/workbench/app/views/folders/_show_contents.html.erb
index d880e01..ecfd405 100644
--- a/apps/workbench/app/views/folders/_show_contents.html.erb
+++ b/apps/workbench/app/views/folders/_show_contents.html.erb
@@ -13,32 +13,34 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-4">
- <%= link_to(
- choose_collections_path(
- title: 'Add data to folder:',
- multiple: true,
- action_name: 'Add',
- action_href: actions_path(id: @object.uuid),
- action_method: 'post',
- action_data: {selection_param: 'selection[]', copy_selections_into_folder: @object.uuid, success: 'page-refresh'}.to_json),
- { class: "btn btn-primary btn-sm", remote: true, method: 'get', data: {'event-after-select' => 'page-refresh'} }) do %>
- <i class="fa fa-fw fa-plus"></i> Add data
- <% end %>
- <%= link_to(
- choose_pipeline_templates_path(
- title: 'Choose a pipeline to run:',
- action_name: 'Configure...',
- action_href: pipeline_instances_path,
- action_method: 'post',
- action_data: {'selection_param' => 'pipeline_instance[pipeline_template_uuid]', 'pipeline_instance[owner_uuid]' => @object.uuid, 'success' => 'redirect-to-created-object'}.to_json),
- { class: "btn btn-primary btn-sm", remote: true, method: 'get' }) do %>
- <i class="fa fa-fw fa-gear"></i> Run a pipeline
+ <% if @object.editable? %>
+ <%= link_to(
+ choose_collections_path(
+ title: 'Add data to folder:',
+ multiple: true,
+ action_name: 'Add',
+ action_href: actions_path(id: @object.uuid),
+ action_method: 'post',
+ action_data: {selection_param: 'selection[]', copy_selections_into_folder: @object.uuid, success: 'page-refresh'}.to_json),
+ { class: "btn btn-primary btn-sm", remote: true, method: 'get', data: {'event-after-select' => 'page-refresh'} }) do %>
+ <i class="fa fa-fw fa-plus"></i> Add data
+ <% end %>
+ <%= link_to(
+ choose_pipeline_templates_path(
+ title: 'Choose a pipeline to run:',
+ action_name: 'Configure...',
+ action_href: pipeline_instances_path,
+ action_method: 'post',
+ action_data: {'selection_param' => 'pipeline_instance[pipeline_template_uuid]', 'pipeline_instance[owner_uuid]' => @object.uuid, 'success' => 'redirect-to-created-object'}.to_json),
+ { class: "btn btn-primary btn-sm", remote: true, method: 'get' }) do %>
+ <i class="fa fa-fw fa-gear"></i> Run a pipeline
+ <% end %>
<% end %>
</div>
<div class="col-lg-4">
<form class="form-inline" role="form">
Show:
- <select class="form-control" data-filter-rows-by="data-kind" data-filter-in="table.arv-index.arv-folder-contents">
+ <select class="form-control" data-filter-rows-by="data-kind" data-filter-target="table.arv-index.arv-folder-contents tbody">
<option value="">Everything</option>
<option value="arvados#collection">Data</option>
<option value="arvados#pipeline_instance arvados#job">Compute jobs</option>
@@ -52,43 +54,18 @@
</form>
</div>
<div class="col-lg-4">
- <input type="text" class="form-control search-folder-contents" placeholder="Search folder contents"/>
+ <input type="text" class="form-control search-folder-contents" placeholder="Search folder contents" data-search-target=".arv-index.arv-folder-contents"/>
</div>
</div>
</div>
<table class="table table-condensed arv-index arv-folder-contents">
- <tbody>
- <colgroup>
- <col width="40%" />
- <col width="60%" />
- </colgroup>
- <% @objects_and_names.each do |object, name_link| %>
- <% name_object = (object.respond_to?(:name) || !name_link) ? object : name_link %>
- <tr class="filterable"
- data-object-uuid="<%= name_object.uuid %>"
- data-kind="<%= object.kind %>"
- >
- <td>
- <% if @object.editable? %>
- <%= link_to({action: 'remove_item', id: @object.uuid, item_uuid: ((name_link && name_link.uuid) || object.uuid)}, method: :delete, remote: true, data: {confirm: "Remove #{object.class_for_display.downcase} #{name_object.name rescue object.uuid} from this folder?", toggle: 'tooltip', placement: 'top'}, class: 'btn btn-xs btn-default btn-nodecorate', title: 'remove') do %>
- <i class="fa fa-fw fa-ban"></i>
- <% end %>
- <% else %>
- <i class="fa fa-fw"></i><%# placeholder %>
- <% end %>
-
- <%= render :partial => "show_object_button", :locals => {object: object, size: 'xs'} %>
-
- <i class="fa fa-fw <%= fa_icon_class_for_object(object) %>"></i>
-
- <%= render_editable_attribute name_object, 'name', nil, {data: {emptytext: "Unnamed #{object.class_for_display}"}} %>
- </td>
- <td>
- <%= object.content_summary %>
- </td>
- </tr>
- <% end %>
+ <colgroup>
+ <col width="40%" />
+ <col width="60%" />
+ </colgroup>
+ <tbody data-infinite-scroller="#Contents-scroll" data-infinite-content-href="<%= url_for(format: :json, partial: :contents_rows, offset: next_page_offset) if next_page_offset %>">
+ <%= render partial: 'show_contents_rows', locals: {folder: @object, objects_and_names: @objects_and_names} %>
</tbody>
<thead>
<tr>
diff --git a/apps/workbench/app/views/folders/_show_contents_rows.html.erb b/apps/workbench/app/views/folders/_show_contents_rows.html.erb
new file mode 100644
index 0000000..c20bf27
--- /dev/null
+++ b/apps/workbench/app/views/folders/_show_contents_rows.html.erb
@@ -0,0 +1,26 @@
+<% objects_and_names.each do |object, name_link| %>
+ <% name_object = (object.respond_to?(:name) || !name_link) ? object : name_link %>
+ <tr class="filterable"
+ data-object-uuid="<%= name_object.uuid %>"
+ data-kind="<%= object.kind %>"
+ >
+ <td>
+ <% if folder.editable? %>
+ <%= link_to({action: 'remove_item', id: folder.uuid, item_uuid: ((name_link && name_link.uuid) || object.uuid)}, method: :delete, remote: true, data: {confirm: "Remove #{object.class_for_display.downcase} #{name_object.name rescue object.uuid} from this folder?", toggle: 'tooltip', placement: 'top'}, class: 'btn btn-xs btn-default btn-nodecorate', title: 'remove') do %>
+ <i class="fa fa-fw fa-ban"></i>
+ <% end %>
+ <% else %>
+ <i class="fa fa-fw"></i><%# placeholder %>
+ <% end %>
+
+ <%= render :partial => "show_object_button", :locals => {object: object, size: 'xs'} %>
+
+ <i class="fa fa-fw <%= fa_icon_class_for_object(object) %>"></i>
+
+ <%= render_editable_attribute name_object, 'name', nil %>
+ </td>
+ <td>
+ <%= object.content_summary %>
+ </td>
+ </tr>
+<% end %>
diff --git a/services/api/app/controllers/arvados/v1/groups_controller.rb b/services/api/app/controllers/arvados/v1/groups_controller.rb
index 4bccbcc..da82e81 100644
--- a/services/api/app/controllers/arvados/v1/groups_controller.rb
+++ b/services/api/app/controllers/arvados/v1/groups_controller.rb
@@ -20,9 +20,10 @@ class Arvados::V1::GroupsController < ApplicationController
offset_all = @offset
@orders = []
- [Group, Job, PipelineInstance, PipelineTemplate,
- Human, Specimen, Trait,
- Collection].each do |klass|
+ [Group,
+ Job, PipelineInstance, PipelineTemplate,
+ Collection,
+ Human, Specimen, Trait].each do |klass|
@objects = klass.readable_by(*@read_users)
cond_sql = "#{klass.table_name}.owner_uuid = ?"
cond_params = [@object.uuid]
diff --git a/services/api/app/models/link.rb b/services/api/app/models/link.rb
index af39185..1b3fc34 100644
--- a/services/api/app/models/link.rb
+++ b/services/api/app/models/link.rb
@@ -10,6 +10,7 @@ class Link < ArvadosModel
after_destroy :maybe_invalidate_permissions_cache
attr_accessor :head_kind, :tail_kind
validate :name_link_has_valid_name
+ validate :name_link_owner_is_tail
api_accessible :user, extend: :common do |t|
t.add :tail_uuid
@@ -92,4 +93,11 @@ class Link < ArvadosModel
true
end
end
+
+ def name_link_owner_is_tail
+ if link_class == 'name'
+ self.owner_uuid = tail_uuid
+ ensure_owner_uuid_is_permitted
+ end
+ end
end
commit b3cc51580f7d4bfa0c390e29950b2d605bfcda79
Author: Tom Clegg <tom at curoverse.com>
Date: Sun Jun 1 00:05:51 2014 -0400
2872: Use data chooser for running pipelines. Many presentation fixes.
diff --git a/apps/workbench/app/assets/javascripts/application.js b/apps/workbench/app/assets/javascripts/application.js
index d66cb92..7274b3b 100644
--- a/apps/workbench/app/assets/javascripts/application.js
+++ b/apps/workbench/app/assets/javascripts/application.js
@@ -111,6 +111,11 @@ jQuery(function($){
$('.btn').button();
});
+ $(document).
+ on('ready ajax:complete', function() {
+ $('[data-toggle~=tooltip]').tooltip({container:'body'});
+ });
+
HeaderRowFixer = function(selector) {
this.duplicateTheadTr = function() {
$(selector).each(function() {
diff --git a/apps/workbench/app/assets/javascripts/editable.js b/apps/workbench/app/assets/javascripts/editable.js
index 0514c1e..a32eb7e 100644
--- a/apps/workbench/app/assets/javascripts/editable.js
+++ b/apps/workbench/app/assets/javascripts/editable.js
@@ -75,6 +75,12 @@ $(document).
});
}
});
+ }).
+ on('ready ajax:complete', function() {
+ $("[data-toggle~='x-editable']").click(function(e) {
+ e.stopPropagation();
+ $($(this).attr('data-toggle-selector')).editable('toggle');
+ });
});
$.fn.editabletypes.text.defaults.tpl = '<input type="text" name="editable-text">'
diff --git a/apps/workbench/app/assets/javascripts/folders.js b/apps/workbench/app/assets/javascripts/folders.js
index 10695cf..ff1c7b8 100644
--- a/apps/workbench/app/assets/javascripts/folders.js
+++ b/apps/workbench/app/assets/javascripts/folders.js
@@ -1,12 +1,20 @@
$(document).
- on('ready ajax:complete', function() {
- $("[data-toggle='x-editable']").click(function(e) {
- e.stopPropagation();
- $($(this).attr('data-toggle-selector')).editable('toggle');
- });
- }).on('paste keyup change', 'input.search-folder-contents', function() {
+ on('paste keyup change', 'input.search-folder-contents', function() {
var q = new RegExp($(this).val(), 'i');
$(this).closest('div.panel').find('tbody tr').each(function() {
$(this).toggle(!!$(this).text().match(q));
});
+ }).on('change', 'select[data-filter-rows-by]', function() {
+ var val = $(this).val();
+ var filterby = $(this).attr('data-filter-rows-by');
+ var $target = $($(this).attr('data-filter-in'));
+ if (val == '') {
+ $target.find('.filterable').show();
+ } else {
+ $target.find('.filterable').hide();
+ console.log('.filterable[' + filterby + '="' + val + '"]');
+ $.each(val.split(" "), function(i, e) {
+ $target.find('.filterable['+filterby+'="'+e+'"]').show();
+ });
+ }
});
diff --git a/apps/workbench/app/assets/stylesheets/application.css.scss b/apps/workbench/app/assets/stylesheets/application.css.scss
index d67d8dc..7d76f5a 100644
--- a/apps/workbench/app/assets/stylesheets/application.css.scss
+++ b/apps/workbench/app/assets/stylesheets/application.css.scss
@@ -189,3 +189,7 @@ div#wrapper {
.arv-description-in-table:hover {
overflow-y: scroll;
}
+
+.btn.btn-nodecorate {
+ border: none;
+}
diff --git a/apps/workbench/app/assets/stylesheets/select_modal.css.scss b/apps/workbench/app/assets/stylesheets/select_modal.css.scss
index 47ae374..f97110a 100644
--- a/apps/workbench/app/assets/stylesheets/select_modal.css.scss
+++ b/apps/workbench/app/assets/stylesheets/select_modal.css.scss
@@ -1,3 +1,8 @@
+.selectable-container > .row {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-right: 1em;
+}
.selectable.active, .selectable:hover {
background: #d9edf7;
}
diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb
index 1c92191..4aa79fb 100644
--- a/apps/workbench/app/controllers/application_controller.rb
+++ b/apps/workbench/app/controllers/application_controller.rb
@@ -1,5 +1,6 @@
class ApplicationController < ActionController::Base
include ArvadosApiClientHelper
+ include ApplicationHelper
respond_to :html, :json, :js
protect_from_forgery
@@ -84,11 +85,11 @@ class ApplicationController < ActionController::Base
end
@objects ||= model_class
- @objects = @objects.filter(@filters).limit(@limit).offset(@offset).all
+ @objects = @objects.filter(@filters).limit(@limit).offset(@offset)
end
def index
- find_objects_for_index
+ find_objects_for_index if !@objects
respond_to do |f|
f.json { render json: @objects }
f.html { render }
@@ -197,7 +198,7 @@ class ApplicationController < ActionController::Base
end
def show_pane_list
- %w(Attributes Metadata JSON API)
+ %w(Attributes Metadata Advanced)
end
protected
@@ -244,7 +245,13 @@ class ApplicationController < ActionController::Base
if params[:uuid].empty?
@object = nil
else
- @object = model_class.find(params[:uuid])
+ if (model_class != Link and
+ resource_class_for_uuid(params[:uuid]) == Link)
+ @name_link = Link.find(params[:uuid])
+ @object = model_class.find(@name_link.head_uuid)
+ else
+ @object = model_class.find(params[:uuid])
+ end
end
else
@object = model_class.where(uuid: params[:uuid]).first
diff --git a/apps/workbench/app/controllers/folders_controller.rb b/apps/workbench/app/controllers/folders_controller.rb
index bfae5fd..73e24bc 100644
--- a/apps/workbench/app/controllers/folders_controller.rb
+++ b/apps/workbench/app/controllers/folders_controller.rb
@@ -41,8 +41,9 @@ class FoldersController < ApplicationController
end
end
- def index
+ def find_objects_for_index
@objects = Group.where(group_class: 'folder').order('name')
+ super
parent_of = {current_user.uuid => 'me'}
@objects.each do |ob|
parent_of[ob.uuid] = ob.owner_uuid
@@ -100,7 +101,6 @@ class FoldersController < ApplicationController
name: "")]
end
end
-
super
end
diff --git a/apps/workbench/app/controllers/jobs_controller.rb b/apps/workbench/app/controllers/jobs_controller.rb
index 4746635..8743a6f 100644
--- a/apps/workbench/app/controllers/jobs_controller.rb
+++ b/apps/workbench/app/controllers/jobs_controller.rb
@@ -44,6 +44,6 @@ class JobsController < ApplicationController
end
def show_pane_list
- %w(Attributes Provenance Metadata JSON API)
+ %w(Details Provenance Advanced)
end
end
diff --git a/apps/workbench/app/controllers/pipeline_instances_controller.rb b/apps/workbench/app/controllers/pipeline_instances_controller.rb
index 761dc91..3bb18aa 100644
--- a/apps/workbench/app/controllers/pipeline_instances_controller.rb
+++ b/apps/workbench/app/controllers/pipeline_instances_controller.rb
@@ -3,6 +3,33 @@ class PipelineInstancesController < ApplicationController
before_filter :find_objects_by_uuid, only: :compare
include PipelineInstancesHelper
+ def update
+ @updates ||= params[@object.class.to_s.underscore.singularize.to_sym]
+ if (components = @updates[:components])
+ components.each do |cname, component|
+ if component[:script_parameters]
+ component[:script_parameters].each do |param, value_info|
+ if value_info.is_a? Hash
+ if resource_class_for_uuid(value_info[:value]) == Link
+ # Use the link target, not the link itself, as script
+ # parameter; but keep the link info around as well.
+ link = Link.find value_info[:value]
+ value_info[:value] = link.head_uuid
+ value_info[:link_uuid] = link.uuid
+ value_info[:link_name] = link.name
+ else
+ # Delete stale link_uuid and link_name data.
+ value_info[:link_uuid] = nil
+ value_info[:link_name] = nil
+ end
+ end
+ end
+ end
+ end
+ end
+ super
+ end
+
def graph(pipelines)
count = {}
provenance = {}
@@ -142,10 +169,13 @@ class PipelineInstancesController < ApplicationController
end
def show_pane_list
- panes = %w(Components Graph Attributes Metadata JSON API)
+ panes = %w(Components Graph Advanced)
if @object and @object.state.in? ['New', 'Ready']
panes = %w(Inputs) + panes
end
+ if not @object.components.values.collect { |x| x['job'] }.compact.any?
+ panes -= ['Graph']
+ end
panes
end
diff --git a/apps/workbench/app/helpers/application_helper.rb b/apps/workbench/app/helpers/application_helper.rb
index 81fc689..a463a50 100644
--- a/apps/workbench/app/helpers/application_helper.rb
+++ b/apps/workbench/app/helpers/application_helper.rb
@@ -79,10 +79,16 @@ module ApplicationHelper
#
def link_to_if_arvados_object(attrvalue, opts={}, style_opts={})
if (resource_class = resource_class_for_uuid(attrvalue, opts))
- link_uuid = attrvalue.is_a?(ArvadosBase) ? attrvalue.uuid : attrvalue
+ if attrvalue.is_a? ArvadosBase
+ object = attrvalue
+ link_uuid = attrvalue.uuid
+ else
+ object = nil
+ link_uuid = attrvalue
+ end
link_name = opts[:link_text]
if !link_name
- link_name = link_uuid
+ link_name = object.andand.default_name || resource_class.default_name
if opts[:friendly_name]
if attrvalue.respond_to? :friendly_link_name
@@ -159,17 +165,17 @@ module ApplicationHelper
span_id = object.uuid.to_s + '-' + attr.to_s + '-' + (@unique_id += 1).to_s
span_tag = content_tag 'span', attrvalue.to_s, {
- "data-emptytext" => "none",
+ "data-emptytext" => (object.andand.default_name || 'none'),
"data-placement" => "bottom",
"data-type" => input_type,
- "data-title" => "Update #{attr.gsub '_', ' '}",
+ "data-title" => "Edit #{attr.gsub '_', ' '}",
"data-name" => attr,
"data-object-uuid" => object.uuid,
"data-toggle" => "manual",
"id" => span_id,
:class => "editable"
}.merge(htmloptions).merge(ajax_options)
- span_tag + raw(' <a href="#" class="btn btn-xs btn-default" data-toggle="x-editable" data-toggle-selector="#' + span_id + '"><i class="fa fa-fw fa-pencil"></i> Edit</a>')
+ span_tag + raw(' <a href="#" class="btn btn-xs btn-default btn-nodecorate" data-toggle="x-editable tooltip" data-toggle-selector="#' + span_id + '" data-placement="top" title="edit"><i class="fa fa-fw fa-pencil"></i></a>')
end
def render_pipeline_component_attribute(object, attr, subattr, value_info, htmloptions={})
@@ -215,6 +221,49 @@ module ApplicationHelper
dataclass = ArvadosBase.resource_class_for_uuid(attrvalue)
end
+ id = "#{object.uuid}-#{subattr.join('-')}"
+ dn = "[#{attr}]"
+ subattr.each do |a|
+ dn += "[#{a}]"
+ end
+ if value_info.is_a? Hash
+ dn += '[value]'
+ end
+
+ if dataclass == Collection
+ selection_param = object.class.to_s.underscore + dn
+ display_value = attrvalue
+ if value_info.is_a?(Hash)
+ if (link = Link.find? value_info[:link_uuid])
+ display_value = link.name
+ elsif value_info[:link_name]
+ display_value = value_info[:link_name]
+ end
+ end
+ modal_path = choose_collections_path \
+ ({ title: 'Choose a dataset:',
+ action_name: 'OK',
+ action_href: pipeline_instance_path(id: object.uuid),
+ action_method: 'patch',
+ action_data: {
+ merge: true,
+ selection_param: selection_param,
+ success: 'page-refresh'
+ }.to_json,
+ })
+ return content_tag('div', :class => 'input-group') do
+ html = text_field_tag(dn, display_value, :class => 'form-control')
+ html + content_tag('span', :class => 'input-group-btn') do
+ link_to('Choose',
+ modal_path,
+ { :class => "btn btn-primary",
+ :remote => true,
+ :method => 'get',
+ })
+ end
+ end
+ end
+
if dataclass.andand.is_a?(Class)
datatype = 'select'
elsif dataclass == 'number'
@@ -228,15 +277,6 @@ module ApplicationHelper
datatype = 'text'
end
- id = "#{object.uuid}-#{subattr.join('-')}"
- dn = "[#{attr}]"
- subattr.each do |a|
- dn += "[#{a}]"
- end
- if value_info.is_a? Hash
- dn += '[value]'
- end
-
selectables = []
attrtext = attrvalue
if dataclass and dataclass.is_a? Class
@@ -303,15 +343,21 @@ module ApplicationHelper
end
def fa_icon_class_for_object object
- case object.class.to_s
- when 'User'
+ case object.class.to_s.to_sym
+ when :User
'fa-user'
- when 'Group'
+ when :Group
object.group_class ? 'fa-folder' : 'fa-users'
- when 'Job', 'PipelineInstance', 'PipelineTemplate'
+ when :Job, :PipelineInstance, :PipelineTemplate
'fa-gears'
- when 'Collection'
+ when :Collection
'fa-archive'
+ when :Specimen
+ 'fa-flask'
+ when :Trait
+ 'fa-clipboard'
+ when :Human
+ 'fa-male'
end
end
end
diff --git a/apps/workbench/app/models/arvados_base.rb b/apps/workbench/app/models/arvados_base.rb
index 2c2963c..a6aded9 100644
--- a/apps/workbench/app/models/arvados_base.rb
+++ b/apps/workbench/app/models/arvados_base.rb
@@ -109,6 +109,10 @@ class ArvadosBase < ActiveRecord::Base
new.private_reload(hash)
end
+ def self.find?(*args)
+ find(*args) rescue nil
+ end
+
def self.order(*args)
ArvadosResourceList.new(self).order(*args)
end
@@ -282,7 +286,7 @@ class ArvadosBase < ActiveRecord::Base
end
def class_for_display
- self.class.to_s
+ self.class.to_s.underscore.humanize
end
def self.creatable?
@@ -343,7 +347,7 @@ class ArvadosBase < ActiveRecord::Base
end
def friendly_link_name
- (name if self.respond_to? :name) || uuid
+ (name if self.respond_to? :name) || default_name
end
def content_summary
@@ -354,6 +358,15 @@ class ArvadosBase < ActiveRecord::Base
friendly_link_name
end
+ def self.default_name
+ self.to_s.underscore.humanize
+ end
+
+ # Placeholder for name when name is missing or empty
+ def default_name
+ "New #{class_for_display.downcase}"
+ end
+
def owner
ArvadosBase.find(owner_uuid) rescue nil
end
diff --git a/apps/workbench/app/models/collection.rb b/apps/workbench/app/models/collection.rb
index 2fe4e2b..90e53e8 100644
--- a/apps/workbench/app/models/collection.rb
+++ b/apps/workbench/app/models/collection.rb
@@ -1,6 +1,4 @@
class Collection < ArvadosBase
- include ApplicationHelper
-
MD5_EMPTY = 'd41d8cd98f00b204e9800998ecf8427e'
# Return true if the given string is the locator of a zero-length blob
@@ -13,7 +11,7 @@ class Collection < ArvadosBase
end
def content_summary
- human_readable_bytes_html(total_bytes) + " " + super
+ ApplicationController.helpers.human_readable_bytes_html(total_bytes) + " " + super
end
def total_bytes
diff --git a/apps/workbench/app/views/application/_show_advanced.html.erb b/apps/workbench/app/views/application/_show_advanced.html.erb
new file mode 100644
index 0000000..d31b5f5
--- /dev/null
+++ b/apps/workbench/app/views/application/_show_advanced.html.erb
@@ -0,0 +1,22 @@
+<div class="panel-group" id="arv-adv-accordion">
+ <% ['API response',
+ 'Python example',
+ 'CLI example',
+ 'curl example'].each do |section| %>
+ <% section_id = section.gsub(" ","_").downcase %>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h4 class="panel-title">
+ <a data-toggle="collapse" data-parent="#arv-adv-accordion" href="#advanced_<%=section_id%>">
+ <%= section %>
+ </a>
+ </h4>
+ </div>
+ <div id="advanced_<%=section_id%>" class="panel-collapse collapse">
+ <div class="panel-body">
+ <%= render partial: "show_advanced_#{section_id}", locals: {object: @object} %>
+ </div>
+ </div>
+ </div>
+ <% end %>
+</div>
diff --git a/apps/workbench/app/views/application/_show_advanced_api_response.html.erb b/apps/workbench/app/views/application/_show_advanced_api_response.html.erb
new file mode 100644
index 0000000..9ef124a
--- /dev/null
+++ b/apps/workbench/app/views/application/_show_advanced_api_response.html.erb
@@ -0,0 +1,3 @@
+<pre>
+<%= JSON.pretty_generate(object.attributes.reject { |k,v| k == 'id' }) rescue nil %>
+</pre>
diff --git a/apps/workbench/app/views/application/_show_advanced_cli_example.html.erb b/apps/workbench/app/views/application/_show_advanced_cli_example.html.erb
new file mode 100644
index 0000000..4516876
--- /dev/null
+++ b/apps/workbench/app/views/application/_show_advanced_cli_example.html.erb
@@ -0,0 +1,8 @@
+<pre>
+arv --pretty <%= object.class.to_s.underscore %> get \
+ --uuid <%= object.uuid %>
+
+arv <%= object.class.to_s.underscore %> update \
+ --uuid <%= object.uuid %> \
+ --<%= object.class.to_s.underscore.gsub '_', '-' %> '<%= JSON.generate({object.attributes.keys[-3] => object.attributes.values[-3]}).gsub("'","'\''") %>'
+</pre>
diff --git a/apps/workbench/app/views/application/_show_advanced_curl_example.html.erb b/apps/workbench/app/views/application/_show_advanced_curl_example.html.erb
new file mode 100644
index 0000000..0454c13
--- /dev/null
+++ b/apps/workbench/app/views/application/_show_advanced_curl_example.html.erb
@@ -0,0 +1,9 @@
+<pre>
+curl -X PUT \
+ -H "Authorization: OAuth2 $ARVADOS_API_TOKEN" \
+ --data-urlencode <%= object.class.to_s.underscore %>@/dev/stdin \
+ https://$ARVADOS_API_HOST/arvados/v1/<%= object.class.to_s.pluralize.underscore %>/<%= object.uuid %> \
+ <<EOF
+<%= JSON.pretty_generate({object.attributes.keys[-3] => object.attributes.values[-3]}) %>
+EOF
+</pre>
diff --git a/apps/workbench/app/views/application/_show_advanced_python_example.html.erb b/apps/workbench/app/views/application/_show_advanced_python_example.html.erb
new file mode 100644
index 0000000..3ceae4f
--- /dev/null
+++ b/apps/workbench/app/views/application/_show_advanced_python_example.html.erb
@@ -0,0 +1,5 @@
+<pre>
+import arvados
+
+x = arvados.api().<%= object.class.to_s.pluralize.underscore %>().get(uuid='<%= object.uuid %>').execute()
+</pre>
diff --git a/apps/workbench/app/views/application/_show_json.html.erb b/apps/workbench/app/views/application/_show_json.html.erb
deleted file mode 100644
index 2f0cd21..0000000
--- a/apps/workbench/app/views/application/_show_json.html.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<pre>
-<%= JSON.pretty_generate(@object.attributes.reject { |k,v| k == 'id' }) rescue nil %>
-</pre>
diff --git a/apps/workbench/app/views/application/_show_object_button.html.erb b/apps/workbench/app/views/application/_show_object_button.html.erb
index 81ca745..157ae49 100644
--- a/apps/workbench/app/views/application/_show_object_button.html.erb
+++ b/apps/workbench/app/views/application/_show_object_button.html.erb
@@ -1,3 +1,3 @@
<% htmloptions = {class: ''}.merge(htmloptions || {})
htmloptions[:class] += " btn-#{size}" rescue nil %>
-<%= link_to_if_arvados_object object, { link_text: raw('Show <i class="fa fa-fw fa-arrow-circle-right"></i>') }, { class: 'btn btn-default ' + htmloptions[:class] } %>
+<%= link_to_if_arvados_object object, { link_text: raw('<i class="glyphicon glyphicon-zoom-in"></i>') }, { data: {toggle: 'tooltip', placement: 'top'}, title: 'show', class: 'btn btn-default btn-nodecorate ' + htmloptions[:class] } %>
diff --git a/apps/workbench/app/views/folders/_choose.html.erb b/apps/workbench/app/views/folders/_choose.html.erb
index c27d669..7d57ca5 100644
--- a/apps/workbench/app/views/folders/_choose.html.erb
+++ b/apps/workbench/app/views/folders/_choose.html.erb
@@ -8,7 +8,7 @@
</div>
<div class="modal-body">
- <div class="container-fluid arv-folder-list selectable-container" style="height: 15em; overflow-y: scroll">
+ <div class="container-fluid selectable-container" style="height: 15em; overflow-y: scroll">
<% [@my_folder_tree, @shared_folder_tree].each do |tree| %>
<% tree.each do |foldernode| %>
<% if foldernode[:object].is_a? String %>
diff --git a/apps/workbench/app/views/folders/_show_contents.html.erb b/apps/workbench/app/views/folders/_show_contents.html.erb
index 6a609cf..d880e01 100644
--- a/apps/workbench/app/views/folders/_show_contents.html.erb
+++ b/apps/workbench/app/views/folders/_show_contents.html.erb
@@ -26,7 +26,7 @@
<% end %>
<%= link_to(
choose_pipeline_templates_path(
- title: 'Run pipeline:',
+ title: 'Choose a pipeline to run:',
action_name: 'Configure...',
action_href: pipeline_instances_path,
action_method: 'post',
@@ -36,12 +36,20 @@
<% end %>
</div>
<div class="col-lg-4">
- <select class="form-control" data-filter-rows-by="kind">
- <option value="">Show everything</option>
- <option value="arvados#collection">Data</option>
- <option value="arvados#pipeline_instance arvados#job">Compute jobs</option>
- <option value="arvados#pipeline_template">Pipelines</option>
- </select>
+ <form class="form-inline" role="form">
+ Show:
+ <select class="form-control" data-filter-rows-by="data-kind" data-filter-in="table.arv-index.arv-folder-contents">
+ <option value="">Everything</option>
+ <option value="arvados#collection">Data</option>
+ <option value="arvados#pipeline_instance arvados#job">Compute jobs</option>
+ <option value="arvados#pipeline_template">Pipelines</option>
+ <!--
+ <option value="arvados#specimen">Specimens</option>
+ <option value="arvados#human">Humans</option>
+ <option value="arvados#trait">Traits</option>
+ -->
+ </select>
+ </form>
</div>
<div class="col-lg-4">
<input type="text" class="form-control search-folder-contents" placeholder="Search folder contents"/>
@@ -49,48 +57,46 @@
</div>
</div>
-<table class="table table-condensed arv-index">
+<table class="table table-condensed arv-index arv-folder-contents">
<tbody>
<colgroup>
<col width="40%" />
- <col width="40%" />
- <col width="10%" />
- <col width="10%" />
+ <col width="60%" />
</colgroup>
<% @objects_and_names.each do |object, name_link| %>
- <tr data-object-uuid="<%= (name_link && name_link.uuid) || object.uuid %>">
+ <% name_object = (object.respond_to?(:name) || !name_link) ? object : name_link %>
+ <tr class="filterable"
+ data-object-uuid="<%= name_object.uuid %>"
+ data-kind="<%= object.kind %>"
+ >
<td>
+ <% if @object.editable? %>
+ <%= link_to({action: 'remove_item', id: @object.uuid, item_uuid: ((name_link && name_link.uuid) || object.uuid)}, method: :delete, remote: true, data: {confirm: "Remove #{object.class_for_display.downcase} #{name_object.name rescue object.uuid} from this folder?", toggle: 'tooltip', placement: 'top'}, class: 'btn btn-xs btn-default btn-nodecorate', title: 'remove') do %>
+ <i class="fa fa-fw fa-ban"></i>
+ <% end %>
+ <% else %>
+ <i class="fa fa-fw"></i><%# placeholder %>
+ <% end %>
+
+ <%= render :partial => "show_object_button", :locals => {object: object, size: 'xs'} %>
+
<i class="fa fa-fw <%= fa_icon_class_for_object(object) %>"></i>
- <%= render_editable_attribute name_link, 'name', nil, {data: {emptytext: "Unnamed #{object.class_for_display}"}} %>
+
+ <%= render_editable_attribute name_object, 'name', nil, {data: {emptytext: "Unnamed #{object.class_for_display}"}} %>
</td>
<td>
<%= object.content_summary %>
</td>
- <td>
- <%= render :partial => "show_object_button", :locals => {object: object, size: 'xs'} %>
- </td>
- <td>
- <% if @object.editable? %>
- <%= link_to({action: 'remove_item', id: @object.uuid, item_uuid: ((name_link && name_link.uuid) || object.uuid)}, method: :delete, remote: true, data: {confirm: "Remove #{object.class_for_display} #{name_link.andand.uuid ? name_link.name : object.uuid} from this folder?"}, class: 'btn btn-xs btn-default') do %>
- Remove <i class="fa fa-fw fa-ban"></i>
- <% end %>
- <% end %>
- </td>
</tr>
<% end %>
</tbody>
<thead>
<tr>
<th>
- name
</th>
<th>
description
</th>
- <th>
- </th>
- <th>
- </th>
</tr>
</thead>
</table>
diff --git a/apps/workbench/app/views/jobs/_show_details.html.erb b/apps/workbench/app/views/jobs/_show_details.html.erb
new file mode 100644
index 0000000..7b6b176
--- /dev/null
+++ b/apps/workbench/app/views/jobs/_show_details.html.erb
@@ -0,0 +1 @@
+<%= render partial: 'application/show_attributes' %>
diff --git a/apps/workbench/app/views/layouts/application.html.erb b/apps/workbench/app/views/layouts/application.html.erb
index 6e3618e..0ec08cc 100644
--- a/apps/workbench/app/views/layouts/application.html.erb
+++ b/apps/workbench/app/views/layouts/application.html.erb
@@ -71,9 +71,15 @@
<% else %>
<li class="nav-separator"><span class="glyphicon glyphicon-arrow-right"></span></li>
<li>
- <%= link_to(
- controller.controller_name.humanize.downcase,
- url_for({controller: params[:controller]})) %>
+ <% if @object and \
+ ((@name_link and (o = Group.find?(@name_link.tail_uuid)))\
+ or (o = Group.find?(@object.owner_uuid))) %>
+ <%= link_to(o.name, folder_path(o.uuid)) %>
+ <% else %>
+ <%= link_to(
+ controller.controller_name.humanize.downcase,
+ url_for({controller: params[:controller]})) %>
+ <% end %>
</li>
<% if params[:action] != 'index' %>
<li class="nav-separator">
diff --git a/apps/workbench/app/views/pipeline_instances/_show_components.html.erb b/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
index c075610..b08e0b8 100644
--- a/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
+++ b/apps/workbench/app/views/pipeline_instances/_show_components.html.erb
@@ -6,7 +6,7 @@
<%= content_for :content_top do %>
<h2>
- <%= render_editable_attribute @object, 'name', nil, { 'data-emptytext' => 'Unnamed pipeline' } %>
+ <%= render_editable_attribute @object, 'name', nil %>
</h2>
<% if template %>
<h4>
commit 8cc28c45ca3da20e9d66aabbaf43de777c582a84
Author: Tom Clegg <tom at curoverse.com>
Date: Sat May 31 18:04:09 2014 -0400
2872: Start doing everything from folder perspective.
diff --git a/apps/workbench/app/assets/javascripts/select_modal.js b/apps/workbench/app/assets/javascripts/select_modal.js
index 85d97c9..278dbb5 100644
--- a/apps/workbench/app/assets/javascripts/select_modal.js
+++ b/apps/workbench/app/assets/javascripts/select_modal.js
@@ -8,18 +8,24 @@ $(document).on('click', '.selectable', function() {
$this.toggleClass('active');
}).on('click', '.modal button[data-action-href]', function() {
var selection = [];
- var data = {};
+ var data = [];
var $modal = $(this).closest('.modal');
+ var action_data = $(this).data('action-data');
+ var selection_param = action_data.selection_param;
$modal.find('.modal-error').removeClass('hide').hide();
$modal.find('.selectable.active[data-object-uuid]').each(function() {
- selection.push($(this).attr('data-object-uuid'));
+ var val = $(this).attr('data-object-uuid');
+ data.push({name: selection_param, value: val});
+ });
+ $.each(action_data, function(key, value) {
+ data.push({name: key, value: value});
});
- data[$(this).data('action-data').selection_param] = selection[0];
$.ajax($(this).attr('data-action-href'),
{dataType: 'json',
type: $(this).attr('data-method'),
data: data,
- context: {modal: $modal}}).
+ traditional: true,
+ context: {modal: $modal, action_data: action_data}}).
fail(function(jqxhr, status, error) {
if (jqxhr.readyState == 0 || jqxhr.status == 0) {
message = "Cancelled."
@@ -32,8 +38,15 @@ $(document).on('click', '.selectable', function() {
html('<div class="alert alert-danger">' + message + '</div>').
show();
}).
- success(function() {
+ done(function(data, status, jqxhr) {
+ var event_name = this.action_data.success;
this.modal.find('.modal-error').hide();
- window.location.reload();
+ $(document).trigger(event_name!=null ? event_name : 'page-refresh',
+ [data, status, jqxhr, this.action_data]);
});
});
+$(document).on('page-refresh', function(event, data, status, jqxhr, action_data) {
+ window.location.reload();
+}).on('redirect-to-created-object', function(event, data, status, jqxhr, action_data) {
+ window.location.href = data.href.replace(/^[^\/]*\/\/[^\/]*/, '');
+});
diff --git a/apps/workbench/app/assets/stylesheets/application.css.scss b/apps/workbench/app/assets/stylesheets/application.css.scss
index 51c96d7..d67d8dc 100644
--- a/apps/workbench/app/assets/stylesheets/application.css.scss
+++ b/apps/workbench/app/assets/stylesheets/application.css.scss
@@ -64,6 +64,9 @@ table.arv-index tbody td.arv-object-AuthorizedKey.arv-attr-public_key {
overflow-x: hidden;
max-width: 120px;
}
+table.arv-index > thead > tr > th {
+ border-top: none;
+}
table.table-fixedlayout {
white-space: nowrap;
table-layout: fixed;
@@ -171,23 +174,18 @@ table.table-fixed-header-row tbody {
z-index:1055;
}
-.navbar-nav.side-nav {
- box-shadow: inset -1px 0 #e7e7e7;
-}
-.navbar-nav.side-nav > li:first-child {
- margin-top: 5px; /* keep "hover" bg below top nav bottom border */
+/* Do not leave space for left-nav */
+div#wrapper {
+ padding-left: 0;
}
-.navbar-nav.side-nav > li > a {
- padding-top: 10px;
- padding-bottom: 10px;
+
+.arv-description-as-subtitle {
+ padding-bottom: 1em;
}
-.navbar-nav.side-nav > li.dropdown > ul.dropdown-menu > li > a {
- padding-top: 5px;
- padding-bottom: 5px;
+.arv-description-in-table {
+ max-height: 3.5em;
+ overflow-y: hidden;
}
-.navbar-nav.side-nav a.active,
-.navbar-nav.side-nav a:hover,
-.navbar-nav.side-nav a:focus {
- border-right: 1px solid #ffffff;
- background: #ffffff;
+.arv-description-in-table:hover {
+ overflow-y: scroll;
}
diff --git a/apps/workbench/app/assets/stylesheets/folders.css.scss b/apps/workbench/app/assets/stylesheets/folders.css.scss
index a033e87..163c188 100644
--- a/apps/workbench/app/assets/stylesheets/folders.css.scss
+++ b/apps/workbench/app/assets/stylesheets/folders.css.scss
@@ -6,8 +6,3 @@
.arv-folder-list > .row.folder:hover {
background: #d9edf7;
}
-.arv-folder-list > .row.folder.active,
-.arv-folder-list > .row.folder.active:hover {
- background: #428bca;
- color: #fff;
-}
diff --git a/apps/workbench/app/assets/stylesheets/select_modal.css.scss b/apps/workbench/app/assets/stylesheets/select_modal.css.scss
index 6fe5651..47ae374 100644
--- a/apps/workbench/app/assets/stylesheets/select_modal.css.scss
+++ b/apps/workbench/app/assets/stylesheets/select_modal.css.scss
@@ -1,3 +1,10 @@
.selectable.active, .selectable:hover {
background: #d9edf7;
}
+.selectable.active,
+.selectable.active a,
+.selectable.active:hover,
+.selectable.active:hover a {
+ background: #428bca;
+ color: #fff;
+}
diff --git a/apps/workbench/app/controllers/actions_controller.rb b/apps/workbench/app/controllers/actions_controller.rb
index 368d9a8..cbe3405 100644
--- a/apps/workbench/app/controllers/actions_controller.rb
+++ b/apps/workbench/app/controllers/actions_controller.rb
@@ -20,15 +20,44 @@ class ActionsController < ApplicationController
end
expose_action :copy_selections_into_folder do
+ link_selections = Link.filter([['uuid','in',params["selection"]]])
+ link_uuids = link_selections.collect(&:uuid)
+
+ # Given a link uuid, we'll add the link's head_uuid. Given another
+ # type, we'll add the object itself.
+ uuids_to_add = params["selection"] - link_uuids
+ uuids_to_add += link_selections.collect(&:head_uuid)
+
+ # Skip anything that's already here.
already_named = Link.
filter([['tail_uuid','=', at object.uuid],
- ['head_uuid','in',params["selection"]]]).
+ ['head_uuid','in',uuids_to_add],
+ ['link_class','=','name']]).
collect(&:head_uuid)
- (params["selection"] - already_named).each do |s|
- Link.create(tail_uuid: @object.uuid,
- head_uuid: s,
- link_class: 'name',
- name: "#{s} added #{Time.now}")
+ uuids_to_add -= already_named
+
+ # Given a name link, we'll try to add the linked object using the
+ # same name.
+ name_for = {}
+ link_selections.
+ select { |x| x.link_class == 'name' }.
+ each do |link|
+ name_for[link.head_uuid] = link.name
+ end
+
+ uuids_to_add.each do |s|
+ name = name_for[s] || s
+ begin
+ Link.create(tail_uuid: @object.uuid,
+ head_uuid: s,
+ link_class: 'name',
+ name: name)
+ rescue
+ Link.create(tail_uuid: @object.uuid,
+ head_uuid: s,
+ link_class: 'name',
+ name: name + " (#{Time.now.localtime})")
+ end
end
redirect_to @object
end
diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb
index 59ca350..1c92191 100644
--- a/apps/workbench/app/controllers/application_controller.rb
+++ b/apps/workbench/app/controllers/application_controller.rb
@@ -63,7 +63,7 @@ class ApplicationController < ActionController::Base
self.render_error status: 404
end
- def index
+ def find_objects_for_index
@limit ||= 200
if params[:limit]
@limit = params[:limit].to_i
@@ -85,6 +85,10 @@ class ApplicationController < ActionController::Base
@objects ||= model_class
@objects = @objects.filter(@filters).limit(@limit).offset(@offset).all
+ end
+
+ def index
+ find_objects_for_index
respond_to do |f|
f.json { render json: @objects }
f.html { render }
@@ -109,6 +113,11 @@ class ApplicationController < ActionController::Base
end
end
+ def choose
+ find_objects_for_index
+ render partial: 'choose', locals: {multiple: params[:multiple]}
+ end
+
def render_content
if !@object
return render_not_found("object not found")
diff --git a/apps/workbench/app/controllers/collections_controller.rb b/apps/workbench/app/controllers/collections_controller.rb
index 218ba89..9371bd1 100644
--- a/apps/workbench/app/controllers/collections_controller.rb
+++ b/apps/workbench/app/controllers/collections_controller.rb
@@ -40,6 +40,15 @@ class CollectionsController < ApplicationController
end
end
+ def choose
+ @name_links = Link.
+ filter([['link_class','=','name'],
+ ['head_uuid','is_a','arvados#collection']])
+ @objects = Collection.
+ filter([['uuid','in', at name_links.collect(&:head_uuid)]])
+ super
+ end
+
def index
if params[:search].andand.length.andand > 0
tags = Link.where(any: ['contains', params[:search]])
diff --git a/apps/workbench/app/controllers/folders_controller.rb b/apps/workbench/app/controllers/folders_controller.rb
index 8ebb1a3..bfae5fd 100644
--- a/apps/workbench/app/controllers/folders_controller.rb
+++ b/apps/workbench/app/controllers/folders_controller.rb
@@ -7,6 +7,10 @@ class FoldersController < ApplicationController
%w(Folders)
end
+ def show_pane_list
+ %w(Contents Permissions)
+ end
+
def remove_item
@removed_uuids = []
links = []
@@ -76,11 +80,6 @@ class FoldersController < ApplicationController
buildtree.call(children_of, false)})
end
- def choose
- index
- render partial: 'choose'
- end
-
def show
@objects = @object.contents include_linked: true
@share_links = Link.filter([['head_uuid', '=', @object.uuid],
diff --git a/apps/workbench/app/controllers/pipeline_templates_controller.rb b/apps/workbench/app/controllers/pipeline_templates_controller.rb
index 5173d4e..afe41c4 100644
--- a/apps/workbench/app/controllers/pipeline_templates_controller.rb
+++ b/apps/workbench/app/controllers/pipeline_templates_controller.rb
@@ -8,5 +8,4 @@ class PipelineTemplatesController < ApplicationController
def show_pane_list
%w(Components Pipelines Attributes Metadata JSON API)
end
-
end
diff --git a/apps/workbench/app/helpers/application_helper.rb b/apps/workbench/app/helpers/application_helper.rb
index d844350..81fc689 100644
--- a/apps/workbench/app/helpers/application_helper.rb
+++ b/apps/workbench/app/helpers/application_helper.rb
@@ -155,16 +155,21 @@ module ApplicationHelper
ajax_options['data-pk'][:defaults] = object.attributes
end
ajax_options['data-pk'] = ajax_options['data-pk'].to_json
+ @unique_id ||= 0
+ span_id = object.uuid.to_s + '-' + attr.to_s + '-' + (@unique_id += 1).to_s
- content_tag 'span', attrvalue.to_s, {
+ span_tag = content_tag 'span', attrvalue.to_s, {
"data-emptytext" => "none",
"data-placement" => "bottom",
"data-type" => input_type,
"data-title" => "Update #{attr.gsub '_', ' '}",
"data-name" => attr,
"data-object-uuid" => object.uuid,
+ "data-toggle" => "manual",
+ "id" => span_id,
:class => "editable"
}.merge(htmloptions).merge(ajax_options)
+ span_tag + raw(' <a href="#" class="btn btn-xs btn-default" data-toggle="x-editable" data-toggle-selector="#' + span_id + '"><i class="fa fa-fw fa-pencil"></i> Edit</a>')
end
def render_pipeline_component_attribute(object, attr, subattr, value_info, htmloptions={})
@@ -296,4 +301,17 @@ module ApplicationHelper
button_href, params, *rest)
end
end
+
+ def fa_icon_class_for_object object
+ case object.class.to_s
+ when 'User'
+ 'fa-user'
+ when 'Group'
+ object.group_class ? 'fa-folder' : 'fa-users'
+ when 'Job', 'PipelineInstance', 'PipelineTemplate'
+ 'fa-gears'
+ when 'Collection'
+ 'fa-archive'
+ end
+ end
end
diff --git a/apps/workbench/app/models/pipeline_instance.rb b/apps/workbench/app/models/pipeline_instance.rb
index aad7cfc..beeba62 100644
--- a/apps/workbench/app/models/pipeline_instance.rb
+++ b/apps/workbench/app/models/pipeline_instance.rb
@@ -5,6 +5,14 @@ class PipelineInstance < ArvadosBase
true
end
+ def content_summary
+ begin
+ PipelineTemplate.find(pipeline_template_uuid).name
+ rescue
+ super
+ end
+ end
+
def update_job_parameters(new_params)
self.components[:steps].each_with_index do |step, i|
step[:params].each do |param|
diff --git a/apps/workbench/app/views/application/_choose.html.erb b/apps/workbench/app/views/application/_choose.html.erb
new file mode 100644
index 0000000..cf18cdc
--- /dev/null
+++ b/apps/workbench/app/views/application/_choose.html.erb
@@ -0,0 +1,24 @@
+<div class="modal">
+ <div class="modal-dialog">
+ <div class="modal-content">
+
+ <div class="modal-header">
+ <button type="button" class="close" onClick="reset_form()" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title"><%= params[:title] || "Choose #{@objects.first.class_for_display}" %></h4>
+ </div>
+
+ <div class="modal-body">
+ <div class="container-fluid arv-folder-list selectable-container" style="height: 15em; overflow-y: scroll">
+ <%= render partial: 'choose_rows', locals: {multiple: multiple} %>
+ </div>
+ </div>
+
+ <div class="modal-footer">
+ <button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Cancel</button>
+ <button class="btn btn-primary" aria-hidden="true"><%= params[:action_name] || 'Select' %></button>
+ <div class="modal-error hide" style="text-align: left; margin-top: 1em;">
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/apps/workbench/app/views/folders/_choose.js.erb b/apps/workbench/app/views/application/_choose.js.erb
similarity index 88%
copy from apps/workbench/app/views/folders/_choose.js.erb
copy to apps/workbench/app/views/application/_choose.js.erb
index 4ecd072..3bb3694 100644
--- a/apps/workbench/app/views/folders/_choose.js.erb
+++ b/apps/workbench/app/views/application/_choose.js.erb
@@ -1,4 +1,4 @@
-$('body > .modal-container').html("<%= escape_javascript(render partial: 'choose.html') %>");
+$('body > .modal-container').html("<%= escape_javascript(render partial: 'choose.html', locals: {multiple: multiple}) %>");
$('body > .modal-container .modal').modal('show');
$('body > .modal-container .modal .modal-footer .btn-primary').
addClass('<%= j params[:action_class] %>').
diff --git a/apps/workbench/app/views/collections/_choose.js.erb b/apps/workbench/app/views/collections/_choose.js.erb
new file mode 120000
index 0000000..8420a7f
--- /dev/null
+++ b/apps/workbench/app/views/collections/_choose.js.erb
@@ -0,0 +1 @@
+../application/_choose.js.erb
\ No newline at end of file
diff --git a/apps/workbench/app/views/collections/_choose_rows.html.erb b/apps/workbench/app/views/collections/_choose_rows.html.erb
new file mode 100644
index 0000000..8622830
--- /dev/null
+++ b/apps/workbench/app/views/collections/_choose_rows.html.erb
@@ -0,0 +1,11 @@
+<% @name_links.each do |name_link| %>
+ <div class="row selectable <%= 'multiple' if multiple %>" data-object-uuid="<%= name_link.uuid %>">
+ <div class="col-sm-8" style="overflow-x:hidden">
+ <i class="fa fa-fw fa-archive"></i>
+ <%= name_link.name %>
+ </div>
+ <div class="col-sm-4" style="overflow-x:hidden">
+ <%= link_to_if_arvados_object name_link.tail_uuid, friendly_name: true %>
+ </div>
+ </div>
+<% end %>
diff --git a/apps/workbench/app/views/folders/_choose.js.erb b/apps/workbench/app/views/folders/_choose.js.erb
deleted file mode 100644
index 4ecd072..0000000
--- a/apps/workbench/app/views/folders/_choose.js.erb
+++ /dev/null
@@ -1,8 +0,0 @@
-$('body > .modal-container').html("<%= escape_javascript(render partial: 'choose.html') %>");
-$('body > .modal-container .modal').modal('show');
-$('body > .modal-container .modal .modal-footer .btn-primary').
- addClass('<%= j params[:action_class] %>').
- attr('data-action-href', '<%= j params[:action_href] %>').
- attr('data-method', '<%= j params[:action_method] %>').
- data('action-data', <%= raw params[:action_data] %>);
-$(document).trigger('ajax:complete');
diff --git a/apps/workbench/app/views/folders/_choose.js.erb b/apps/workbench/app/views/folders/_choose.js.erb
new file mode 120000
index 0000000..8420a7f
--- /dev/null
+++ b/apps/workbench/app/views/folders/_choose.js.erb
@@ -0,0 +1 @@
+../application/_choose.js.erb
\ No newline at end of file
diff --git a/apps/workbench/app/views/folders/_show_contents.html.erb b/apps/workbench/app/views/folders/_show_contents.html.erb
new file mode 100644
index 0000000..6a609cf
--- /dev/null
+++ b/apps/workbench/app/views/folders/_show_contents.html.erb
@@ -0,0 +1,96 @@
+<% content_for :content_top do %>
+
+<h2>
+ <%= render_editable_attribute @object, 'name', nil, { 'data-emptytext' => "New folder" } %>
+</h2>
+
+<div class="arv-description-as-subtitle">
+ <%= render_editable_attribute @object, 'description', nil, { 'data-emptytext' => "(No description provided)", 'data-toggle' => 'manual' } %>
+</div>
+
+<% end %>
+
+<div class="container-fluid">
+ <div class="row">
+ <div class="col-lg-4">
+ <%= link_to(
+ choose_collections_path(
+ title: 'Add data to folder:',
+ multiple: true,
+ action_name: 'Add',
+ action_href: actions_path(id: @object.uuid),
+ action_method: 'post',
+ action_data: {selection_param: 'selection[]', copy_selections_into_folder: @object.uuid, success: 'page-refresh'}.to_json),
+ { class: "btn btn-primary btn-sm", remote: true, method: 'get', data: {'event-after-select' => 'page-refresh'} }) do %>
+ <i class="fa fa-fw fa-plus"></i> Add data
+ <% end %>
+ <%= link_to(
+ choose_pipeline_templates_path(
+ title: 'Run pipeline:',
+ action_name: 'Configure...',
+ action_href: pipeline_instances_path,
+ action_method: 'post',
+ action_data: {'selection_param' => 'pipeline_instance[pipeline_template_uuid]', 'pipeline_instance[owner_uuid]' => @object.uuid, 'success' => 'redirect-to-created-object'}.to_json),
+ { class: "btn btn-primary btn-sm", remote: true, method: 'get' }) do %>
+ <i class="fa fa-fw fa-gear"></i> Run a pipeline
+ <% end %>
+ </div>
+ <div class="col-lg-4">
+ <select class="form-control" data-filter-rows-by="kind">
+ <option value="">Show everything</option>
+ <option value="arvados#collection">Data</option>
+ <option value="arvados#pipeline_instance arvados#job">Compute jobs</option>
+ <option value="arvados#pipeline_template">Pipelines</option>
+ </select>
+ </div>
+ <div class="col-lg-4">
+ <input type="text" class="form-control search-folder-contents" placeholder="Search folder contents"/>
+ </div>
+ </div>
+</div>
+
+<table class="table table-condensed arv-index">
+ <tbody>
+ <colgroup>
+ <col width="40%" />
+ <col width="40%" />
+ <col width="10%" />
+ <col width="10%" />
+ </colgroup>
+ <% @objects_and_names.each do |object, name_link| %>
+ <tr data-object-uuid="<%= (name_link && name_link.uuid) || object.uuid %>">
+ <td>
+ <i class="fa fa-fw <%= fa_icon_class_for_object(object) %>"></i>
+ <%= render_editable_attribute name_link, 'name', nil, {data: {emptytext: "Unnamed #{object.class_for_display}"}} %>
+ </td>
+ <td>
+ <%= object.content_summary %>
+ </td>
+ <td>
+ <%= render :partial => "show_object_button", :locals => {object: object, size: 'xs'} %>
+ </td>
+ <td>
+ <% if @object.editable? %>
+ <%= link_to({action: 'remove_item', id: @object.uuid, item_uuid: ((name_link && name_link.uuid) || object.uuid)}, method: :delete, remote: true, data: {confirm: "Remove #{object.class_for_display} #{name_link.andand.uuid ? name_link.name : object.uuid} from this folder?"}, class: 'btn btn-xs btn-default') do %>
+ Remove <i class="fa fa-fw fa-ban"></i>
+ <% end %>
+ <% end %>
+ </td>
+ </tr>
+ <% end %>
+ </tbody>
+ <thead>
+ <tr>
+ <th>
+ name
+ </th>
+ <th>
+ description
+ </th>
+ <th>
+ </th>
+ <th>
+ </th>
+ </tr>
+ </thead>
+</table>
diff --git a/apps/workbench/app/views/folders/_show_featured.html.erb b/apps/workbench/app/views/folders/_show_featured.html.erb
new file mode 100644
index 0000000..6547b5e
--- /dev/null
+++ b/apps/workbench/app/views/folders/_show_featured.html.erb
@@ -0,0 +1,18 @@
+<div class="row">
+ <% @objects[0..3].each do |object| %>
+ <div class="card arvados-object">
+ <div class="card-top blue">
+ <a href="#">
+ <img src="/favicon.ico" alt=""/>
+ </a>
+ </div>
+ <div class="card-info">
+ <span class="title"><%= @objects.name_for(object) || object.class_for_display %></span>
+ <div class="desc"><%= object.respond_to?(:description) ? object.description : object.uuid %></div>
+ </div>
+ <div class="card-bottom">
+ <%= render :partial => "show_object_button", :locals => {object: object, htmloptions: {class: 'btn-default btn-block'}} %>
+ </div>
+ </div>
+ <% end %>
+</div>
diff --git a/apps/workbench/app/views/folders/_show_folders.html.erb b/apps/workbench/app/views/folders/_show_folders.html.erb
index 2ecfd6e..8c4f0d8 100644
--- a/apps/workbench/app/views/folders/_show_folders.html.erb
+++ b/apps/workbench/app/views/folders/_show_folders.html.erb
@@ -2,27 +2,31 @@
<% [@my_folder_tree, @shared_folder_tree].each do |tree| %>
<% tree.each do |foldernode| %>
<% rowtype = foldernode[:object].class %>
- <div class="<%= 'folder' if rowtype == Group %> row" style="padding-left: <%= 1 + foldernode[:depth] %>em;">
- <% if rowtype == String %>
- <i class="fa fa-fw fa-folder-open-o"></i>
- <%= foldernode[:object] %>
- <% elsif rowtype == User %>
- <% if foldernode[:object].uuid == current_user.andand.uuid %>
+ <div class="<%= 'folder' if rowtype == Group %> row">
+ <div class="col-lg-4" style="padding-left: <%= 1 + foldernode[:depth] %>em;">
+ <% if rowtype == String %>
<i class="fa fa-fw fa-folder-open-o"></i>
- My Folders
+ <%= foldernode[:object] %>
+ <% elsif rowtype == User %>
+ <% if foldernode[:object].uuid == current_user.andand.uuid %>
+ <i class="fa fa-fw fa-folder-open-o"></i>
+ My Folders
+ <% else %>
+ <i class="fa fa-fw fa-folder-o"></i>
+ <%= foldernode[:object].friendly_link_name %>
+ <% end %>
<% else %>
<i class="fa fa-fw fa-folder-o"></i>
- <%= foldernode[:object].friendly_link_name %>
+ <%= link_to foldernode[:object] do %>
+ <%= foldernode[:object].friendly_link_name %>
+ <% end %>
<% end %>
- <% else %>
- <i class="fa fa-fw fa-folder-o"></i>
- <%= link_to foldernode[:object] do %>
- <%= foldernode[:object].friendly_link_name %>
+ </div>
+ <div class="col-lg-8 arv-description-in-table">
+ <% if rowtype == Group %>
+ <%= foldernode[:object].description %>
<% end %>
- <div class="pull-right">
- <%= render partial: 'delete_object_button', locals: {object: foldernode[:object]} %>
- </div>
- <% end %>
+ </div>
</div>
<% end %>
<% end %>
diff --git a/apps/workbench/app/views/folders/_show_permissions.html.erb b/apps/workbench/app/views/folders/_show_permissions.html.erb
new file mode 100644
index 0000000..41a7cd7
--- /dev/null
+++ b/apps/workbench/app/views/folders/_show_permissions.html.erb
@@ -0,0 +1,53 @@
+<div class="container-fluid">
+ <div class="row">
+ <div class="col-lg-6">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ Additional permissions
+ </div>
+ <div class="panel-body">
+ <p>
+ <% if not @share_links.any? %>
+ <span class="deemphasize">(No additional permissions)</span>
+ <% else %>
+ Also shared with:
+ <% @share_links.andand.each do |link| %>
+ <br /><%= link_to_if_arvados_object link.tail_uuid, friendly_name: true %>
+ <% end %>
+ <% end %>
+ </p>
+ </div>
+ </div>
+ </div>
+ <div class="col-lg-6">
+ <% if @object.owner %>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ Inherited permissions
+ </div>
+ <div class="panel-body">
+ <p>Permissions for this folder are inherited by the owner or parent folder:
+ </p>
+ <p style="margin-left: 4em">
+ <% if User == resource_class_for_uuid(@object.owner_uuid) %>
+ <i class="fa fa-fw fa-user"></i>
+ <% else %>
+ <i class="fa fa-fw fa-folder"></i>
+ <% end %>
+ <%= link_to_if_arvados_object @object.owner_uuid, friendly_name: true %>
+ <%= button_to('Move to...',
+ choose_folders_path(
+ title: 'Move to...',
+ editable: true,
+ action_name: 'Move',
+ action_href: folder_path(@object.uuid),
+ action_method: 'put',
+ action_data: {selection_param: 'folder[owner_uuid]', success: 'page-refresh'}.to_json),
+ { class: "btn btn-default btn-xs arv-move-to-folder", remote: true, method: 'get' }) %>
+ </p>
+ </div>
+ </div>
+ <% end %>
+ </div>
+ </div>
+</div>
diff --git a/apps/workbench/app/views/folders/show.html.erb b/apps/workbench/app/views/folders/show.html.erb
deleted file mode 100644
index ebf504e..0000000
--- a/apps/workbench/app/views/folders/show.html.erb
+++ /dev/null
@@ -1,214 +0,0 @@
-<div class="row row-fill-height">
- <div class="col-md-6">
- <div class="panel panel-info">
- <div class="panel-heading">
- <h3 class="panel-title">
- <%= render_editable_attribute @object, 'name', nil, {data: {emptytext: "New folder"}} %>
- </h3>
- </div>
- <div class="panel-body">
- <img src="/favicon.ico" class="pull-right" alt="" style="opacity: 0.3"/>
- <%= render_editable_attribute @object, 'description', nil, { 'data-emptytext' => "Created: #{@object.created_at.to_s(:long)}", 'data-toggle' => 'manual', 'id' => "#{@object.uuid}-description" } %>
- <% if @object.attribute_editable? 'description' %>
- <div style="margin-top: 1em;">
- <a href="#" class="btn btn-xs btn-default" data-toggle="x-editable" data-toggle-selector="#<%= @object.uuid %>-description"><i class="fa fa-fw fa-pencil"></i> Edit description</a>
- </div>
- <% end %>
- </div>
- </div>
- </div>
- <div class="col-md-3">
- <div class="panel panel-default">
- <div class="panel-heading">
- <h3 class="panel-title">
- Activity
- </h3>
- </div>
- <div class="panel-body smaller-text">
- <!--
- <input type="text" class="form-control" placeholder="Search"/>
- -->
- <div style="height:0.5em;"></div>
- <% if @logs.any? %>
- <%= render_arvados_object_list_start(@logs, 'Show all activity',
- logs_path(filters: [['object_uuid','=', at object.uuid]].to_json)) do |log| %>
- <p>
- <%= time_ago_in_words(log.event_at) %> ago: <%= log.summary %>
- <% if log.object_uuid %>
- <%= link_to_if_arvados_object log.object_uuid, link_text: raw('<i class="fa fa-hand-o-right"></i>') %>
- <% end %>
- </p>
- <% end %>
- <% else %>
- <p>
- Created: <%= @object.created_at.to_s(:long) %>
- </p>
- <p>
- Last modified: <%= @object.modified_at.to_s(:long) %> by <%= link_to_if_arvados_object @object.modified_by_user_uuid, friendly_name: true %>
- </p>
- <% end %>
- </div>
- </div>
- </div>
- <div class="col-md-3">
- <div class="panel panel-default">
- <div class="panel-heading">
- <h3 class="panel-title">
- Sharing and permissions
- </h3>
- </div>
- <div class="panel-body">
- <!--
- <input type="text" class="form-control" placeholder="Search"/>
- -->
- <div style="height:0.5em;"></div>
- <% if @object.owner %>
- <p>Permissions inherited from:
- <br />
- <% if User == resource_class_for_uuid(@object.owner_uuid) %>
- <i class="fa fa-fw fa-user"></i>
- <% else %>
- <i class="fa fa-fw fa-folder"></i>
- <% end %>
- <%= link_to_if_arvados_object @object.owner_uuid, friendly_name: true %>
- <%= button_to('Move to...',
- choose_folders_path(
- title: 'Move to...',
- editable: true,
- action_name: 'Move',
- action_href: folder_path(@object.uuid),
- action_method: 'put',
- action_data: {selection_param: 'folder[owner_uuid]'}.to_json),
- { class: "btn btn-default btn-xs arv-move-to-folder", remote: true, method: 'get' }) %>
- </p>
- <hr />
- <% end %>
- <p>
- <% if not @share_links.any? %>
- <span class="deemphasize">(No additional permissions)</span>
- <% else %>
- Also shared with:
- <% @share_links.andand.each do |link| %>
- <br /><%= link_to_if_arvados_object link.tail_uuid, friendly_name: true %>
- <% end %>
- <% end %>
- </p>
- </div>
- </div>
- </div>
-</div>
-
-<% if @show_cards %>
-<!-- disable cards section until we have bookmarks -->
-<div class="row">
- <% @objects[0..3].each do |object| %>
- <div class="card arvados-object">
- <div class="card-top blue">
- <a href="#">
- <img src="/favicon.ico" alt=""/>
- </a>
- </div>
- <div class="card-info">
- <span class="title"><%= @objects.name_for(object) || object.class_for_display %></span>
- <div class="desc"><%= object.respond_to?(:description) ? object.description : object.uuid %></div>
- </div>
- <div class="card-bottom">
- <%= render :partial => "show_object_button", :locals => {object: object, htmloptions: {class: 'btn-default btn-block'}} %>
- </div>
- </div>
- <% end %>
-</div>
-<!-- end disabled cards section -->
-<% end %>
-
-<div class="row">
- <div class="col-md-12">
- <div class="panel panel-info">
- <div class="panel-heading">
- <div class="row">
- <div class="col-md-6">
- <h3 class="panel-title" style="vertical-align:middle;">
- Contents
- </h3>
- </div>
- <div class="col-md-6">
- <div class="input-group input-group-sm pull-right">
- <input type="text" class="form-control search-folder-contents" placeholder="Search folder contents"/>
- </div>
- </div>
- </div>
- </div>
- <div class="panel-body">
- <p>
- </p>
- <table class="table table-condensed arv-index">
- <tbody>
- <colgroup>
- <col width="3%" />
- <col width="8%" />
- <col width="30%" />
- <col width="15%" />
- <col width="15%" />
- <col width="20%" />
- <col width="8%" />
- </colgroup>
- <% @objects_and_names.each do |object, name_link| %>
- <tr data-object-uuid="<%= (name_link && name_link.uuid) || object.uuid %>">
- <td>
- <%= render :partial => "selection_checkbox", :locals => {object: object} %>
- </td>
- <td>
- <%= render :partial => "show_object_button", :locals => {object: object, size: 'xs'} %>
- </td>
- <td>
- <%= render_editable_attribute name_link, 'name', nil, {data: {emptytext: "Unnamed #{object.class_for_display}"}} %>
- </td>
- <td>
- <%= object.content_summary %>
- </td>
- <td title="<%= object.modified_at %>">
- <span>
- <%= raw distance_of_time_in_words(object.modified_at, Time.now).sub('about ','~').sub(' ',' ') + ' ago' rescue object.modified_at %>
- </span>
- </td>
- <td class="arvados-uuid">
- <%= object.uuid %>
- </td>
- <td>
- <% if @object.editable? %>
- <%= link_to({action: 'remove_item', id: @object.uuid, item_uuid: ((name_link && name_link.uuid) || object.uuid)}, method: :delete, remote: true, data: {confirm: "You are about to remove #{object.class_for_display} #{object.uuid} from this folder.\n\nAre you sure?"}, class: 'btn btn-xs btn-default') do %>
- Remove <i class="fa fa-fw fa-ban"></i>
- <% end %>
- <% end %>
- </td>
- </tr>
- <% end %>
- </tbody>
- <thead>
- <tr>
- <th>
- </th>
- <th>
- </th>
- <th>
- name
- </th>
- <th>
- type
- </th>
- <th>
- modified
- </th>
- <th>
- uuid
- </th>
- <th>
- </th>
- </tr>
- </thead>
- </table>
- <p></p>
- </div>
- </div>
- </div>
-</div>
diff --git a/apps/workbench/app/views/layouts/application.html.erb b/apps/workbench/app/views/layouts/application.html.erb
index d3ea5b0..6e3618e 100644
--- a/apps/workbench/app/views/layouts/application.html.erb
+++ b/apps/workbench/app/views/layouts/application.html.erb
@@ -48,7 +48,7 @@
}
}
</style>
- <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
+ <link href="//netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.css" rel="stylesheet">
</head>
<body>
<div id="wrapper">
@@ -64,77 +64,6 @@
</div>
<div class="collapse navbar-collapse">
- <% if current_user.andand.is_active %>
- <ul class="nav navbar-nav side-nav">
-
- <li class="<%= 'arvados-nav-active' if params[:action] == 'home' %>">
- <a href="/"><i class="fa fa-lg fa-dashboard fa-fw"></i> Dashboard</a>
- </li>
-
- <li class="dropdown">
- <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-lg fa-hand-o-up fa-fw"></i> Help <b class="caret"></b></a>
- <ul class="dropdown-menu">
- <li><%= link_to raw('<i class="fa fa-book fa-fw"></i> Tutorials and User guide'), "#{Rails.configuration.arvados_docsite}/user", target: "_blank" %></li>
- <li><%= link_to raw('<i class="fa fa-book fa-fw"></i> API Reference'), "#{Rails.configuration.arvados_docsite}/api", target: "_blank" %></li>
- <li><%= link_to raw('<i class="fa fa-book fa-fw"></i> SDK Reference'), "#{Rails.configuration.arvados_docsite}/sdk", target: "_blank" %></li>
- </ul>
- </li>
-
- <li class="dropdown">
- <a href="/folders">
- <i class="fa fa-lg fa-folder-o fa-fw"></i> Folders
- </a></li>
- <li><a href="/collections">
- <i class="fa fa-lg fa-briefcase fa-fw"></i> Collections (data files)
- </a></li>
- <li><a href="/jobs">
- <i class="fa fa-lg fa-tasks fa-fw"></i> Jobs
- </a></li>
- <li><a href="/pipeline_instances">
- <i class="fa fa-lg fa-tasks fa-fw"></i> Pipeline instances
- </a></li>
- <li><a href="/pipeline_templates">
- <i class="fa fa-lg fa-gears fa-fw"></i> Pipeline templates
- </a></li>
- <li> </li>
- <li><a href="/repositories">
- <i class="fa fa-lg fa-code-fork fa-fw"></i> Repositories
- </a></li>
- <li><a href="/virtual_machines">
- <i class="fa fa-lg fa-terminal fa-fw"></i> Virtual machines
- </a></li>
- <li><a href="/humans">
- <i class="fa fa-lg fa-male fa-fw"></i> Humans
- </a></li>
- <li><a href="/specimens">
- <i class="fa fa-lg fa-flask fa-fw"></i> Specimens
- </a></li>
- <li><a href="/traits">
- <i class="fa fa-lg fa-clipboard fa-fw"></i> Traits
- </a></li>
- <li><a href="/links">
- <i class="fa fa-lg fa-arrows-h fa-fw"></i> Links
- </a></li>
- <% if current_user.andand.is_admin %>
- <li><a href="/users">
- <i class="fa fa-lg fa-user fa-fw"></i> Users
- </a></li>
- <% end %>
- <li><a href="/groups">
- <i class="fa fa-lg fa-users fa-fw"></i> Groups
- </a></li>
- <li><a href="/nodes">
- <i class="fa fa-lg fa-cloud fa-fw"></i> Compute nodes
- </a></li>
- <li><a href="/keep_services">
- <i class="fa fa-lg fa-exchange fa-fw"></i> Keep services
- </a></li>
- <li><a href="/keep_disks">
- <i class="fa fa-lg fa-hdd-o fa-fw"></i> Keep disks
- </a></li>
- </ul>
- <% end %>
-
<ul class="nav navbar-nav navbar-left breadcrumbs">
<% if current_user %>
<% if content_for?(:breadcrumbs) %>
@@ -153,11 +82,6 @@
<li>
<%= link_to_if_arvados_object @object, {friendly_name: true}, {data: {object_uuid: @object.andand.uuid, name: 'name'}} %>
</li>
- <li style="padding: 14px 0 14px">
- <%= form_tag do |f| %>
- <%= render :partial => "selection_checkbox", :locals => {:object => @object} %>
- <% end %>
- </li>
<% end %>
<% end %>
<% end %>
@@ -230,6 +154,44 @@
<li role="presentation"><a href="<%= logout_path %>" role="menuitem"><i class="fa fa-sign-out fa-fw"></i> Log out</a></li>
</ul>
</li>
+
+ <% if current_user.is_active %>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="system-menu">
+ <span class="glyphicon glyphicon-cog"></span><span class="caret"></span>
+ </a>
+ <ul class="dropdown-menu" role="menu">
+ <li role="presentation" class="dropdown-header">System objects</li>
+ <li role="presentation" class="divider"></li>
+ <li role="presentation"><a href="/repositories">
+ <i class="fa fa-lg fa-code-fork fa-fw"></i> Repositories
+ </a></li>
+ <li role="presentation"><a href="/virtual_machines">
+ <i class="fa fa-lg fa-terminal fa-fw"></i> Virtual machines
+ </a></li>
+ <li role="presentation"><a href="/links">
+ <i class="fa fa-lg fa-arrows-h fa-fw"></i> Links
+ </a></li>
+ <% if current_user.andand.is_admin %>
+ <li role="presentation"><a href="/users">
+ <i class="fa fa-lg fa-user fa-fw"></i> Users
+ </a></li>
+ <% end %>
+ <li role="presentation"><a href="/groups">
+ <i class="fa fa-lg fa-users fa-fw"></i> Groups
+ </a></li>
+ <li role="presentation"><a href="/nodes">
+ <i class="fa fa-lg fa-cloud fa-fw"></i> Compute nodes
+ </a></li>
+ <li role="presentation"><a href="/keep_services">
+ <i class="fa fa-lg fa-exchange fa-fw"></i> Keep services
+ </a></li>
+ <li role="presentation"><a href="/keep_disks">
+ <i class="fa fa-lg fa-hdd-o fa-fw"></i> Keep disks
+ </a></li>
+ </ul>
+ </li>
+ <% end %>
<% else %>
<li><a href="<%= arvados_api_client.arvados_login_url(return_to: root_url) %>">Log in</a></li>
<% end %>
diff --git a/apps/workbench/app/views/pipeline_templates/_choose.js.erb b/apps/workbench/app/views/pipeline_templates/_choose.js.erb
new file mode 120000
index 0000000..8420a7f
--- /dev/null
+++ b/apps/workbench/app/views/pipeline_templates/_choose.js.erb
@@ -0,0 +1 @@
+../application/_choose.js.erb
\ No newline at end of file
diff --git a/apps/workbench/app/views/pipeline_templates/_choose_rows.html.erb b/apps/workbench/app/views/pipeline_templates/_choose_rows.html.erb
new file mode 100644
index 0000000..0e8c32f
--- /dev/null
+++ b/apps/workbench/app/views/pipeline_templates/_choose_rows.html.erb
@@ -0,0 +1,11 @@
+<% @objects.each do |object| %>
+ <div class="row selectable <%= 'multiple' if multiple %>" data-object-uuid="<%= object.uuid %>">
+ <div class="col-md-6" style="overflow-x:hidden">
+ <i class="fa fa-fw fa-gear"></i>
+ <%= object.name %>
+ </div>
+ <div class="col-md-6 arv-description-in-table">
+ <%= object.description %>
+ </div>
+ </div>
+<% end %>
diff --git a/apps/workbench/config/routes.rb b/apps/workbench/config/routes.rb
index b4da656..a28cc69 100644
--- a/apps/workbench/config/routes.rb
+++ b/apps/workbench/config/routes.rb
@@ -35,7 +35,9 @@ ArvadosWorkbench::Application.routes.draw do
resources :uploaded_datasets
resources :groups
resources :specimens
- resources :pipeline_templates
+ resources :pipeline_templates do
+ get 'choose', on: :collection
+ end
resources :pipeline_instances do
get 'compare', on: :collection
end
@@ -46,6 +48,7 @@ ArvadosWorkbench::Application.routes.draw do
get 'sharing_popup', :on => :member
post 'share', :on => :member
post 'unshare', :on => :member
+ get 'choose', on: :collection
end
get('/collections/download/:uuid/:reader_token/*file' => 'collections#show_file',
format: false)
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list