3235: Merge branch 'master' into 3235-top-nav-site-search
[arvados.git] / apps / workbench / app / helpers / application_helper.rb
index dc97251132ea106493ead45ff7e290e5c6746a0e..1b186c5420fc85739559fe0fabbf8bb20204bf93 100644 (file)
@@ -95,8 +95,12 @@ module ApplicationHelper
             link_name = attrvalue.friendly_link_name
           else
             begin
-              link_name = resource_class.find(link_uuid).friendly_link_name
-            rescue RuntimeError
+              if resource_class.name == 'Collection'
+                link_name = collections_for_object(link_uuid).andand.first.andand.friendly_link_name
+              else
+                link_name = object_for_dataclass(resource_class, link_uuid).andand.friendly_link_name
+              end
+            rescue ArvadosApiClient::NotFoundException
               # If that lookup failed, the link will too. So don't make one.
               return attrvalue
             end
@@ -106,13 +110,15 @@ module ApplicationHelper
           link_name = "#{resource_class.to_s}: #{link_name}"
         end
         if !opts[:no_tags] and resource_class == Collection
-          Link.where(head_uuid: link_uuid, link_class: ["tag", "identifier"]).each do |tag|
-            link_name += ' <span class="label label-info">' + html_escape(tag.name) + '</span>'
+          links_for_object(link_uuid).each do |tag|
+            if tag.link_class.in? ["tag", "identifier"]
+              link_name += ' <span class="label label-info">' + html_escape(tag.name) + '</span>'
+            end
           end
         end
         if opts[:thumbnail] and resource_class == Collection
           # add an image thumbnail if the collection consists of a single image file.
-          Collection.where(uuid: link_uuid).each do |c|
+          collections_for_object(link_uuid).each do |c|
             if c.files.length == 1 and CollectionsHelper::is_image c.files.first[1]
               link_name += " "
               link_name += image_tag "#{url_for c}/#{CollectionsHelper::file_path c.files.first}", style: "height: 4em; width: auto"
@@ -124,7 +130,7 @@ module ApplicationHelper
       if opts[:no_link]
         raw(link_name)
       else
-        link_to raw(link_name), { controller: resource_class.to_s.tableize, action: 'show', id: link_uuid }, style_opts
+        link_to raw(link_name), { controller: resource_class.to_s.tableize, action: 'show', id: ((opts[:name_link].andand.uuid) || link_uuid) }, style_opts
       end
     else
       # just return attrvalue if it is not recognizable as an Arvados object or uuid.
@@ -136,8 +142,10 @@ module ApplicationHelper
     attrvalue = object.send(attr) if attrvalue.nil?
     if !object.attribute_editable?(attr, :ever) or
         (!object.editable? and
-         !object.owner_uuid.in?(my_folders.collect(&:uuid)))
-      return attrvalue 
+         !object.owner_uuid.in?(my_projects.collect(&:uuid)))
+      return ((attrvalue && attrvalue.length > 0 && attrvalue) ||
+              (attr == 'name' and object.andand.default_name) ||
+              '(none)')
     end
 
     input_type = 'text'
@@ -179,7 +187,12 @@ module ApplicationHelper
       "id" => span_id,
       :class => "editable"
     }.merge(htmloptions).merge(ajax_options)
-    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>')
+    edit_button = 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="' + (htmloptions[:tiptitle] || 'edit') + '"><i class="fa fa-fw fa-pencil"></i></a>')
+    if htmloptions[:btnplacement] == :left
+      edit_button + ' ' + span_tag
+    else
+      span_tag + ' ' + edit_button
+    end
   end
 
   def render_pipeline_component_attribute(object, attr, subattr, value_info, htmloptions={})
@@ -215,7 +228,7 @@ module ApplicationHelper
     if !object or
         !object.attribute_editable?(attr, :ever) or
         (!object.editable? and
-         !object.owner_uuid.in?(my_folders.collect(&:uuid)))
+         !object.owner_uuid.in?(my_projects.collect(&:uuid)))
       return link_to_if_arvados_object attrvalue
     end
 
@@ -249,7 +262,7 @@ module ApplicationHelper
       end
       modal_path = choose_collections_path \
       ({ title: 'Choose a dataset:',
-         filters: [['owner_uuid', '=', object.owner_uuid]].to_json,
+         filters: [['tail_uuid', '=', object.owner_uuid]].to_json,
          action_name: 'OK',
          action_href: pipeline_instance_path(id: object.uuid),
          action_method: 'patch',
@@ -260,7 +273,9 @@ module ApplicationHelper
          }.to_json,
         })
       return content_tag('div', :class => 'input-group') do
-        html = text_field_tag(dn, display_value, :class => 'form-control')
+        html = text_field_tag(dn, display_value,
+                              :class =>
+                              "form-control #{'required' if required}")
         html + content_tag('span', :class => 'input-group-btn') do
           link_to('Choose',
                   modal_path,
@@ -285,27 +300,45 @@ module ApplicationHelper
       datatype = 'text'
     end
 
+    # preload data
+    preload_uuids = []
+    items = []
     selectables = []
+
     attrtext = attrvalue
-    if dataclass and dataclass.is_a? Class
+    if dataclass.is_a? Class and dataclass < ArvadosBase
+      objects = get_n_objects_of_class dataclass, 10
+      objects.each do |item|
+        items << item
+        preload_uuids << item.uuid
+      end
       if attrvalue and !attrvalue.empty?
-        Link.where(head_uuid: attrvalue, link_class: ["tag", "identifier"]).each do |tag|
-          attrtext += " [#{tag.name}]"
+        preload_uuids << attrvalue
+      end
+      preload_links_for_objects preload_uuids
+
+      if attrvalue and !attrvalue.empty?
+        links_for_object(attrvalue).each do |link|
+          if link.link_class.in? ["tag", "identifier"]
+            attrtext += " [#{link.name}]"
+          end
         end
         selectables.append({name: attrtext, uuid: attrvalue, type: dataclass.to_s})
       end
-      #dataclass.where(uuid: attrvalue).each do |item|
-      #  selectables.append({name: item.uuid, uuid: item.uuid, type: dataclass.to_s})
-      #end
       itemuuids = []
-      dataclass.limit(10).each do |item|
+      items.each do |item|
         itemuuids << item.uuid
         selectables.append({name: item.uuid, uuid: item.uuid, type: dataclass.to_s})
       end
-      Link.where(head_uuid: itemuuids, link_class: ["tag", "identifier"]).each do |tag|
-        selectables.each do |selectable|
-          if selectable['uuid'] == tag.head_uuid
-            selectable['name'] += ' [' + tag.name + ']'
+
+      itemuuids.each do |itemuuid|
+        links_for_object(itemuuid).each do |link|
+          if link.link_class.in? ["tag", "identifier"]
+            selectables.each do |selectable|
+              if selectable['uuid'] == link.head_uuid
+                selectable['name'] += ' [' + link.name + ']'
+              end
+            end
           end
         end
       end
@@ -319,8 +352,9 @@ module ApplicationHelper
       "data-title" => "Set value for #{subattr[-1].to_s}",
       "data-name" => dn,
       "data-pk" => "{id: \"#{object.uuid}\", key: \"#{object.class.to_s.underscore}\"}",
-      "data-showbuttons" => "false",
       "data-value" => attrvalue,
+      # "clear" button interferes with form-control's up/down arrows
+      "data-clear" => false,
       :class => "editable #{'required' if required} form-control",
       :id => id
     }.merge(htmloptions)
@@ -358,39 +392,49 @@ module ApplicationHelper
       render opts.merge(partial: "application/#{partial}")
     end
   end
-    
-  def fa_icon_class_for_object object
-    case object.class.to_s.to_sym
-    when :User
-      'fa-user'
-    when :Group
+
+  RESOURCE_CLASS_ICONS = {
+    "Collection" => "fa-archive",
+    "Group" => "fa-users",
+    "Human" => "fa-male",  # FIXME: Use a more inclusive icon.
+    "Job" => "fa-gears",
+    "KeepDisk" => "fa-hdd-o",
+    "KeepService" => "fa-exchange",
+    "Link" => "fa-arrows-h",
+    "Node" => "fa-cloud",
+    "PipelineInstance" => "fa-gears",
+    "PipelineTemplate" => "fa-gears",
+    "Repository" => "fa-code-fork",
+    "Specimen" => "fa-flask",
+    "Trait" => "fa-clipboard",
+    "User" => "fa-user",
+    "VirtualMachine" => "fa-terminal",
+  }
+  DEFAULT_ICON_CLASS = "fa-cube"
+
+  def fa_icon_class_for_class(resource_class, default=DEFAULT_ICON_CLASS)
+    RESOURCE_CLASS_ICONS.fetch(resource_class.to_s, default)
+  end
+
+  def fa_icon_class_for_uuid(uuid, default=DEFAULT_ICON_CLASS)
+    fa_icon_class_for_class(resource_class_for_uuid(uuid), default)
+  end
+
+  def fa_icon_class_for_object(object, default=DEFAULT_ICON_CLASS)
+    case class_name = object.class.to_s
+    when "Group"
       object.group_class ? 'fa-folder' : 'fa-users'
-    when :Job, :PipelineInstance, :PipelineTemplate
-      'fa-gears'
-    when :Collection
-      'fa-archive'
-    when :Specimen
-      'fa-flask'
-    when :Trait
-      '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'
+      RESOURCE_CLASS_ICONS.fetch(class_name, default)
+    end
+  end
+
+  def chooser_preview_url_for object
+    case object.class.to_s
+    when 'Collection'
+      polymorphic_path(object, tab_pane: 'chooser_preview')
+    else
+      nil
     end
   end
 end