2809: Merge branch 'master' refs #2809
authorTom Clegg <tom@curoverse.com>
Fri, 9 May 2014 18:38:39 +0000 (14:38 -0400)
committerTom Clegg <tom@curoverse.com>
Thu, 15 May 2014 18:18:08 +0000 (14:18 -0400)
Conflicts:
apps/workbench/Gemfile.lock
apps/workbench/app/controllers/application_controller.rb
apps/workbench/app/models/arvados_api_client.rb
apps/workbench/app/models/group.rb
apps/workbench/app/models/user.rb
apps/workbench/app/views/layouts/application.html.erb

13 files changed:
1  2 
apps/workbench/Gemfile
apps/workbench/Gemfile.lock
apps/workbench/app/controllers/actions_controller.rb
apps/workbench/app/controllers/application_controller.rb
apps/workbench/app/controllers/collections_controller.rb
apps/workbench/app/models/arvados_api_client.rb
apps/workbench/app/models/arvados_base.rb
apps/workbench/app/models/arvados_resource_list.rb
apps/workbench/app/models/collection.rb
apps/workbench/app/models/group.rb
apps/workbench/app/models/user.rb
apps/workbench/app/views/layouts/application.html.erb
apps/workbench/config/routes.rb

Simple merge
index 8e748326a1582871efbef261ed72d2288fe70a87,ca9afb14a5db00e6dcc94572fe717b12c8fb8b8d..4cdd3fc122aa62c0b71d523f3af0a6c99fac6ee4
@@@ -2,36 -10,33 +10,33 @@@ GE
    remote: https://rubygems.org/
    specs:
      RedCloth (4.2.9)
-     actionmailer (3.2.15)
-       actionpack (= 3.2.15)
 -    actionmailer (4.0.4)
 -      actionpack (= 4.0.4)
++    actionmailer (4.0.5)
++      actionpack (= 4.0.5)
        mail (~> 2.5.4)
-     actionpack (3.2.15)
-       activemodel (= 3.2.15)
-       activesupport (= 3.2.15)
-       builder (~> 3.0.0)
 -    actionpack (4.0.4)
 -      activesupport (= 4.0.4)
++    actionpack (4.0.5)
++      activesupport (= 4.0.5)
+       builder (~> 3.1.0)
        erubis (~> 2.7.0)
-       journey (~> 1.0.4)
-       rack (~> 1.4.5)
-       rack-cache (~> 1.2)
-       rack-test (~> 0.6.1)
-       sprockets (~> 2.2.1)
-     activemodel (3.2.15)
-       activesupport (= 3.2.15)
-       builder (~> 3.0.0)
-     activerecord (3.2.15)
-       activemodel (= 3.2.15)
-       activesupport (= 3.2.15)
-       arel (~> 3.0.2)
-       tzinfo (~> 0.3.29)
-     activeresource (3.2.15)
-       activemodel (= 3.2.15)
-       activesupport (= 3.2.15)
-     activesupport (3.2.15)
-       i18n (~> 0.6, >= 0.6.4)
-       multi_json (~> 1.0)
+       rack (~> 1.5.2)
+       rack-test (~> 0.6.2)
 -    activemodel (4.0.4)
 -      activesupport (= 4.0.4)
++    activemodel (4.0.5)
++      activesupport (= 4.0.5)
+       builder (~> 3.1.0)
 -    activerecord (4.0.4)
 -      activemodel (= 4.0.4)
++    activerecord (4.0.5)
++      activemodel (= 4.0.5)
+       activerecord-deprecated_finders (~> 1.0.2)
 -      activesupport (= 4.0.4)
++      activesupport (= 4.0.5)
+       arel (~> 4.0.0)
+     activerecord-deprecated_finders (1.0.3)
 -    activesupport (4.0.4)
++    activesupport (4.0.5)
+       i18n (~> 0.6, >= 0.6.9)
+       minitest (~> 4.2)
+       multi_json (~> 1.3)
+       thread_safe (~> 0.1)
+       tzinfo (~> 0.3.37)
      andand (1.3.3)
-     arel (3.0.2)
+     arel (4.0.2)
 -    bootstrap-sass (3.1.1.1)
 +    bootstrap-sass (3.1.0.1)
        sass (~> 3.2)
      bootstrap-x-editable-rails (1.5.1.1)
        railties (>= 3.0)
        rack (>= 1.0.0)
        rack-test (>= 0.5.4)
        xpath (~> 2.0)
 -    childprocess (0.5.3)
 +    childprocess (0.5.1)
        ffi (~> 1.0, >= 1.0.11)
      cliver (0.3.2)
-     coffee-rails (3.2.2)
+     coffee-rails (4.0.1)
        coffee-script (>= 2.2.0)
-       railties (~> 3.2.0)
+       railties (>= 4.0.0, < 5.0)
      coffee-script (2.2.0)
        coffee-script-source
        execjs
-     coffee-script-source (1.6.3)
+     coffee-script-source (1.7.0)
      commonjs (0.2.7)
 -    daemon_controller (1.2.0)
 +    daemon_controller (1.1.7)
      deep_merge (1.0.1)
      erubis (2.7.0)
      execjs (2.0.2)
      ffi (1.9.3)
      headless (1.0.1)
 -    highline (1.6.21)
 +    highline (1.6.20)
      hike (1.2.3)
      httpclient (2.3.4.1)
-     i18n (0.6.5)
-     journey (1.0.4)
+     i18n (0.6.9)
 -    jquery-rails (3.1.0)
 +    jquery-rails (3.0.4)
        railties (>= 3.0, < 5.0)
        thor (>= 0.14, < 2.0)
      json (1.8.1)
      mail (2.5.4)
        mime-types (~> 1.16)
        treetop (~> 1.4.8)
-     mime-types (1.25)
+     mime-types (1.25.1)
 -    mini_portile (0.5.3)
 +    mini_portile (0.5.2)
-     multi_json (1.8.2)
+     minitest (4.7.5)
 -    multi_json (1.9.3)
 -    net-scp (1.2.1)
++    multi_json (1.10.0)
 +    net-scp (1.1.2)
        net-ssh (>= 2.6.5)
      net-sftp (2.1.2)
        net-ssh (>= 2.6.5)
        cliver (~> 0.3.1)
        multi_json (~> 1.0)
        websocket-driver (>= 0.2.0)
-     polyglot (0.3.3)
-     rack (1.4.5)
-     rack-cache (1.2)
-       rack (>= 0.4)
-     rack-ssl (1.3.3)
-       rack
+     polyglot (0.3.4)
+     rack (1.5.2)
      rack-test (0.6.2)
        rack (>= 1.0)
-     rails (3.2.15)
-       actionmailer (= 3.2.15)
-       actionpack (= 3.2.15)
-       activerecord (= 3.2.15)
-       activeresource (= 3.2.15)
-       activesupport (= 3.2.15)
-       bundler (~> 1.0)
-       railties (= 3.2.15)
-     railties (3.2.15)
-       actionpack (= 3.2.15)
-       activesupport (= 3.2.15)
-       rack-ssl (~> 1.3.2)
 -    rails (4.0.4)
 -      actionmailer (= 4.0.4)
 -      actionpack (= 4.0.4)
 -      activerecord (= 4.0.4)
 -      activesupport (= 4.0.4)
++    rails (4.0.5)
++      actionmailer (= 4.0.5)
++      actionpack (= 4.0.5)
++      activerecord (= 4.0.5)
++      activesupport (= 4.0.5)
+       bundler (>= 1.3.0, < 2.0)
 -      railties (= 4.0.4)
++      railties (= 4.0.5)
+       sprockets-rails (~> 2.0.0)
 -    railties (4.0.4)
 -      actionpack (= 4.0.4)
 -      activesupport (= 4.0.4)
++    railties (4.0.5)
++      actionpack (= 4.0.5)
++      activesupport (= 4.0.5)
        rake (>= 0.8.7)
-       rdoc (~> 3.4)
-       thor (>= 0.14.6, < 2.0)
-     rake (10.1.0)
-     rdoc (3.12.2)
-       json (~> 1.4)
+       thor (>= 0.18.1, < 2.0)
+     rake (10.3.1)
      ref (1.0.5)
 -    rubyzip (1.1.3)
 +    rubyzip (1.1.0)
      rvm-capistrano (1.5.1)
        capistrano (~> 2.15.4)
 -    sass (3.2.19)
 +    sass (3.2.12)
-     sass-rails (3.2.6)
-       railties (~> 3.2.0)
-       sass (>= 3.1.10)
-       tilt (~> 1.3)
+     sass-rails (4.0.3)
+       railties (>= 4.0.0, < 5.0)
+       sass (~> 3.2.0)
+       sprockets (~> 2.8, <= 2.11.0)
+       sprockets-rails (~> 2.0)
 -    selenium-webdriver (2.41.0)
 +    selenium-webdriver (2.40.0)
        childprocess (>= 0.5.0)
        multi_json (~> 1.0)
        rubyzip (~> 1.0)
        websocket (~> 1.0.4)
-     sprockets (2.2.2)
 +    simplecov (0.7.1)
 +      multi_json (~> 1.0)
 +      simplecov-html (~> 0.7.1)
 +    simplecov-html (0.7.1)
 +    simplecov-rcov (0.2.3)
 +      simplecov (>= 0.4.1)
+     sprockets (2.11.0)
        hike (~> 1.2)
        multi_json (~> 1.0)
        rack (~> 1.0)
        tilt (~> 1.1, != 1.3.0)
 -    sqlite3 (1.3.9)
 -    therubyracer (0.12.1)
+     sprockets-rails (2.0.1)
+       actionpack (>= 3.0)
+       activesupport (>= 3.0)
+       sprockets (~> 2.8)
-     themes_for_rails (0.5.1)
-       rails (>= 3.0.0)
 +    sqlite3 (1.3.8)
 +    therubyracer (0.12.0)
        libv8 (~> 3.16.14.0)
        ref
-     thor (0.18.1)
+     thor (0.19.1)
+     thread_safe (0.3.3)
      tilt (1.4.1)
      treetop (1.4.15)
        polyglot
        polyglot (>= 0.3.1)
-     tzinfo (0.3.38)
+     tzinfo (0.3.39)
 -    uglifier (2.5.0)
 +    uglifier (2.3.1)
        execjs (>= 0.3.0)
        json (>= 1.8.0)
      websocket (1.0.7)
@@@ -201,14 -196,12 +202,14 @@@ DEPENDENCIE
    passenger
    piwik_analytics
    poltergeist
-   rails (~> 3.2.0)
+   rails (~> 4.0.0)
    rvm-capistrano
    sass
-   sass-rails (~> 3.2.0)
+   sass-rails (~> 4.0.0)
    selenium-webdriver
 +  simplecov (~> 0.7.1)
 +  simplecov-rcov
    sqlite3
-   themes_for_rails
+   themes_for_rails!
    therubyracer
    uglifier (>= 1.0.3)
index a3576bc83e153ad8f3daf62449b9cf9f240f787c,4ed6187f58270f2c03712536145330c878d6c67c..dfb9e892e6ab2a59292edccf07369b8792462fd5
@@@ -1,18 -1,14 +1,20 @@@
  class ApplicationController < ActionController::Base
+   include ArvadosApiClientHelper
    respond_to :html, :json, :js
    protect_from_forgery
 +
 +  ERROR_ACTIONS = [:render_error, :render_not_found]
 +
    around_filter :thread_clear
 -  around_filter :thread_with_mandatory_api_token, :except => [:render_exception, :render_not_found]
 +  around_filter(:thread_with_mandatory_api_token,
 +                except: [:index, :show] + ERROR_ACTIONS)
    around_filter :thread_with_optional_api_token
 -  before_filter :find_object_by_uuid, :except => [:index, :render_exception, :render_not_found]
 -  before_filter :check_user_agreements, :except => [:render_exception, :render_not_found]
 -  before_filter :check_user_notifications, :except => [:render_exception, :render_not_found]
 +  before_filter :check_user_agreements, except: ERROR_ACTIONS
 +  before_filter :check_user_notifications, except: ERROR_ACTIONS
 +  around_filter :using_reader_tokens, only: [:index, :show]
 +  before_filter :find_object_by_uuid, except: [:index] + ERROR_ACTIONS
 +  before_filter :check_my_folders, :except => ERROR_ACTIONS
    theme :select_theme
  
    begin
    end
  
    protected
 -    
 +
 +  def redirect_to_login
 +    respond_to do |f|
 +      f.html {
 +        if request.method == 'GET'
-           redirect_to $arvados_api_client.arvados_login_url(return_to: request.url)
++          redirect_to arvados_api_client.arvados_login_url(return_to: request.url)
 +        else
 +          flash[:error] = "Either you are not logged in, or your session has timed out. I can't automatically log you in and re-attempt this request."
 +          redirect_to :back
 +        end
 +      }
 +      f.json {
 +        @errors = ['You do not seem to be logged in. You did not supply an API token with this request, and your session (if any) has timed out.']
 +        self.render_error status: 422
 +      }
 +    end
 +    false  # For convenience to return from callbacks
 +  end
 +
 +  def using_reader_tokens(login_optional=false)
 +    if params[:reader_tokens].is_a?(Array) and params[:reader_tokens].any?
 +      Thread.current[:reader_tokens] = params[:reader_tokens]
 +    end
 +    begin
 +      yield
 +    rescue ArvadosApiClient::NotLoggedInException
 +      if login_optional
 +        raise
 +      else
 +        return redirect_to_login
 +      end
 +    ensure
 +      Thread.current[:reader_tokens] = nil
 +    end
 +  end
 +
 +  def using_specific_api_token(api_token)
 +    start_values = {}
 +    [:arvados_api_token, :user].each do |key|
 +      start_values[key] = Thread.current[key]
 +    end
 +    Thread.current[:arvados_api_token] = api_token
 +    Thread.current[:user] = nil
 +    begin
 +      yield
 +    ensure
 +      start_values.each_key { |key| Thread.current[key] = start_values[key] }
 +    end
 +  end
 +
    def find_object_by_uuid
      if params[:id] and params[:id].match /\D/
        params[:uuid] = params.delete :id
index 1a0da6424a828b0638aaf95305ffbcb5d34b8273,47a9d6d02eed085f134f7ec31fd7b4fc575d55d1..aea6b778371c0f7059877f1b50e3078531d08ce5
@@@ -21,27 -29,28 +29,34 @@@ class ArvadosBase < ActiveRecord::Bas
        end
    end
  
-   def initialize(*args)
-     super(*args)
+   def initialize raw_params={}
+     begin
+       super self.class.permit_attribute_params(raw_params)
+     rescue Exception => e
+       logger.debug raw_params
+       logger.debug self.class.permit_attribute_params(raw_params).inspect
+       logger.debug self.class.attribute_info.inspect
+       raise e
+     end
      @attribute_sortkey ||= {
        'id' => nil,
 -      'uuid' => '000',
 -      'owner_uuid' => '001',
 -      'created_at' => '002',
 -      'modified_at' => '003',
 -      'modified_by_user_uuid' => '004',
 -      'modified_by_client_uuid' => '005',
 -      'name' => '050',
 -      'tail_uuid' => '100',
 -      'head_uuid' => '101',
 -      'info' => 'zzz-000',
 -      'updated_at' => 'zzz-999'
 +      'name' => '000',
 +      'owner_uuid' => '002',
 +      'event_type' => '100',
 +      'link_class' => '100',
 +      'group_class' => '100',
 +      'tail_uuid' => '101',
 +      'head_uuid' => '102',
 +      'object_uuid' => '102',
 +      'summary' => '104',
 +      'description' => '104',
 +      'properties' => '150',
 +      'info' => '150',
 +      'created_at' => '200',
 +      'modified_at' => '201',
 +      'modified_by_user_uuid' => '202',
 +      'modified_by_client_uuid' => '203',
 +      'uuid' => '999',
      }
    end
  
index 3f74407c01429229bd3ecacf26da7238232dd706,1e2e720d4e8a4f897ddca8c0b3a35f18766f7740..dedd18c81d7eb21193523a52c2dde99cee3176cf
@@@ -1,7 -1,8 +1,8 @@@
  class ArvadosResourceList
+   include ArvadosApiClientHelper
    include Enumerable
  
 -  def initialize(resource_class)
 +  def initialize resource_class=nil
      @resource_class = resource_class
    end
  
index dde6019e9ca4ed9a5d51978fe933fabee0208727,da8fe609d1005b475db7a4878fb784e1c952eb04..8d8d3900c75a95ea718e36d1f1fb0d7d0e2a7ead
@@@ -1,20 -1,6 +1,20 @@@
  class Group < ArvadosBase
 -  def self.owned_items
 -    res = arvados_api_client.api self, "/#{self.uuid}/owned_items", {}
 -    arvados_api_client.unpack_api_response(res)
 +  def contents params={}
-     res = $arvados_api_client.api self.class, "/#{self.uuid}/contents", {
++    res = arvados_api_client.api self.class, "/#{self.uuid}/contents", {
 +      _method: 'GET'
 +    }.merge(params)
 +    ret = ArvadosResourceList.new
-     ret.results = $arvados_api_client.unpack_api_response(res)
++    ret.results = arvados_api_client.unpack_api_response(res)
 +    ret
 +  end
 +
 +  def class_for_display
 +    group_class == 'folder' ? 'Folder' : super
 +  end
 +
 +  def editable?
 +    respond_to?(:writable_by) and
 +      writable_by and
 +      writable_by.index(current_user.uuid)
    end
  end
index 44d615b89fecf117dcc618e01627e1beb74e38f2,59ab1ba5addabd5962a3bd81aa5fbd44c0b21f06..c1656bde692ea1b0d454585663b1aca7ec4d3a8a
@@@ -11,12 -11,17 +11,12 @@@ class User < ArvadosBas
    end
  
    def self.system
-     $arvados_system_user ||= begin
-                                res = $arvados_api_client.api self, '/system'
-                                $arvados_api_client.unpack_api_response(res)
-                              end
+     @@arvados_system_user ||= begin
+                                 res = arvados_api_client.api self, '/system'
+                                 arvados_api_client.unpack_api_response(res)
+                               end
    end
  
 -  def owned_items
 -    res = arvados_api_client.api self.class, "/#{self.uuid}/owned_items"
 -    arvados_api_client.unpack_api_response(res)
 -  end
 -
    def full_name
      (self.first_name || "") + " " + (self.last_name || "")
    end
index 2b5ec88fc62bb71ce9919e777603e5e8b47cd596,265256596bc01a68304c3d156665a67137f31100..f5aca0cd4e1da6f161d2a0a3cd63d31eeab327d2
          <a class="navbar-brand" href="/"><%= Rails.configuration.site_name rescue Rails.application.class.parent_name %></a>
        </div>
  
 -      <div class="collapse navbar-collapse" id="workbench-navbar">
 -      <ul class="nav navbar-nav navbar-left breadcrumbs">
 -        <% if current_user %>
 -        <% if content_for?(:breadcrumbs) %>
 -          <%= yield(:breadcrumbs) %>
 -        <% else %>
 -          <li class="nav-separator"><span class="glyphicon glyphicon-arrow-right"></span></li>
 -          <li>
 -            <%= link_to(
 -                        controller.model_class.to_s.pluralize.underscore.gsub('_', ' '),
 -                        url_for({controller: params[:controller]})) %>
 -          </li>
 -          <% if params[:action] != 'index' %>
 -            <li class="nav-separator">
 -              <span class="glyphicon glyphicon-arrow-right"></span>
 +      <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>
 -              <%= link_to_if_arvados_object @object %>
 +
 +            <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 style="padding: 14px 0 14px">
 -              <%= form_tag do |f| %>
 -                <%= render :partial => "selection_checkbox", :locals => {:object => @object} %>
 -              <% end %>
 +
 +            <li class="dropdown">
 +              <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-lg fa-folder-o fa-fw"></i> Folders <b class="caret"></b></a>
 +              <ul class="dropdown-menu">
 +                <li><%= link_to raw('<i class="fa fa-plus fa-fw"></i> Create new folder'), folders_path, method: :post %></li>
 +                <% @my_top_level_folders.call[0..7].each do |folder| %>
 +                <li><%= link_to raw('<i class="fa fa-folder-open fa-fw"></i> ') + folder.name, folder_path(folder) %></li>
 +                <% end %>
 +                <li><a href="/folders">
 +                    <i class="fa fa-ellipsis-h fa-fw"></i> Show all folders
 +                </a></li>
 +              </ul>
              </li>
 -          <% end %>
 -        <% end %>
 -        <% end %>
 -      </ul>
 -
 -      <ul class="nav navbar-nav navbar-right">
 -
 -        <li>
 -          <a><i class="rotating loading glyphicon glyphicon-refresh"></i></a>
 -        </li>
 -
 -        <% if current_user %>
 -        <!-- XXX placeholder for this when search is implemented
 -        <li>
 -          <form class="navbar-form" role="search">
 -            <div class="input-group" style="width: 220px">
 -              <input type="text" class="form-control" placeholder="search">
 -              <span class="input-group-addon"><span class="glyphicon glyphicon-search"></span></span>
 -            </div>
 -          </form>
 -        </li>
 -        -->
 -
 -        <li class="dropdown notification-menu">
 -          <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="collections-menu">
 -            <span class="glyphicon glyphicon-paperclip"></span>
 -            <span class="badge" id="persistent-selection-count"></span>
 -            <span class="caret"></span>
 -          </a>
 -            <ul class="dropdown-menu" role="menu" id="persistent-selection-list">
 -              <%= form_tag '/actions' do %>
 -              <div id="selection-form-content"></div>
 -              <% end %>
 -          </ul>
 -        </li>
 -
 -        <% if current_user.is_active %>
 -        <li class="dropdown notification-menu">
 -          <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="notifications-menu">
 -            <span class="glyphicon glyphicon-envelope"></span>
 -            <span class="badge badge-alert notification-count"><%= @notification_count %></span>
 -            <span class="caret"></span>
 -          </a>
 -          <ul class="dropdown-menu" role="menu">
 -            <% if (@notifications || []).length > 0 %>
 -              <% @notifications.each_with_index do |n, i| %>
 -                <% if i > 0 %><li class="divider"></li><% end %>
 -                <li class="notification"><%= n.call(self) %></li>
 -              <% end %>
 -            <% else %>
 -              <li class="notification empty">No notifications.</li>
 +            <li><a href="/collections">
 +                <i class="fa fa-lg fa-briefcase fa-fw"></i> Collections (data files)
 +            </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>&nbsp;</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-cogs fa-fw"></i> Compute nodes
 +            </a></li>
 +            <li><a href="/keep_disks">
 +                <i class="fa fa-lg fa-hdd-o fa-fw"></i> Keep disks
 +            </a></li>
            </ul>
 -        </li>
          <% end %>
  
 -        <li class="dropdown">
 -          <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="user-menu">
 -            <span class="glyphicon glyphicon-user"></span><span class="caret"></span>
 -          </a>
 -          <ul class="dropdown-menu" role="menu">
 -            <li role="presentation" class="dropdown-header"><%= current_user.email %></li>
 -            <% if current_user.is_active %>
 -            <li role="presentation" class="divider"></li>
 -            <li role="presentation"><a href="/authorized_keys" role="menuitem">Manage ssh keys</a></li>
 -            <li role="presentation"><a href="/api_client_authorizations" role="menuitem">Manage API tokens</a></li>
 -            <li role="presentation" class="divider"></li>
 +        <ul class="nav navbar-nav navbar-left breadcrumbs">
 +          <% if current_user %>
 +            <% if content_for?(:breadcrumbs) %>
 +              <%= yield(:breadcrumbs) %>
 +            <% 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]})) %>
 +              </li>
 +              <% if params[:action] != 'index' %>
 +                <li class="nav-separator">
 +                  <span class="glyphicon glyphicon-arrow-right"></span>
 +                </li>
 +                <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 %>
 -            <li role="presentation"><a href="<%= logout_path %>" role="menuitem">Log out</a></li>
 -          </ul>
 -        </li>
 -      <% else -%>
 -          <li><a href="<%= arvados_api_client.arvados_login_url(return_to: root_url) %>">Log in</a></li>
 -      <% end -%>
 -      </ul>
 -      </div><!-- /.navbar-collapse -->
 -    </div><!-- /.container-fluid -->
 -  </div>
 +          <% end %>
 +        </ul>
  
 -  <div class="container-fluid">
 -      <div class="col-sm-9 col-sm-offset-3">
 -        <div id="content" class="body-content">
 -          <%= yield %>
 -        </div>
 -      </div>
 -      <div class="col-sm-3 left-nav">
 -        <div class="arvados-nav-container">
 -        <% if current_user.andand.is_active %>
 -        <div class="well">
 -        <ul class="arvados-nav">
 -          <li class="<%= 'arvados-nav-active' if params[:action] == 'home' %>">
 -            <a href="/">Dashboard</a>
 +        <ul class="nav navbar-nav navbar-right">
 +
 +          <li>
 +            <a><i class="rotating loading glyphicon glyphicon-refresh"></i></a>
            </li>
  
 -          <% [['Data', [['collections', 'Collections (data files)'],
 -                        ['humans'],
 -                        ['traits'],
 -                        ['specimens'],
 -                        ['links']]],
 -              ['Activity', [['pipeline_instances', 'Recent pipeline instances'],
 -                            ['jobs', 'Recent jobs']]],
 -              ['Compute', [['pipeline_templates'],
 -                           ['repositories', 'Code repositories'],
 -                           ['virtual_machines']]],
 -              ['System', [['users'],
 -                         ['groups'],
 -                         ['nodes', 'Compute nodes'],
 -                         ['keep_disks']]]].each do |j| %>
 -            <li><%= j[0] %>
 -              <ul>
 -              <% j[1].each do |k| %>
 -                <% unless k[0] == 'users' and !current_user.andand.is_admin %>
 -                  <li class="<%= 'arvados-nav-active' if (params[:controller] == k[0] && params[:action] != 'home') %>">
 -                    <a href="/<%= k[0] %>">
 -                      <%= if k[1] then k[1] else k[0].capitalize.gsub('_', ' ') end %>
 -                    </a>
 -                  </li>
 +          <% if current_user %>
 +          <!-- XXX placeholder for this when search is implemented
 +          <li>
 +            <form class="navbar-form" role="search">
 +              <div class="input-group" style="width: 220px">
 +                <input type="text" class="form-control" placeholder="search">
 +                <span class="input-group-addon"><span class="glyphicon glyphicon-search"></span></span>
 +              </div>
 +            </form>
 +          </li>
 +          -->
 +
 +          <li class="dropdown notification-menu">
 +            <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="collections-menu">
 +              <span class="glyphicon glyphicon-paperclip"></span>
 +              <span class="badge" id="persistent-selection-count"></span>
 +              <span class="caret"></span>
 +            </a>
 +              <ul class="dropdown-menu" role="menu" id="persistent-selection-list">
 +                <%= form_tag '/actions' do %>
 +                <%= hidden_field_tag 'uuid', @object.andand.uuid %>
 +                <div id="selection-form-content"></div>
                  <% end %>
 +            </ul>
 +          </li>
 +
 +          <% if current_user.is_active %>
 +          <li class="dropdown notification-menu">
 +            <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="notifications-menu">
 +              <span class="glyphicon glyphicon-envelope"></span>
 +              <span class="badge badge-alert notification-count"><%= @notification_count %></span>
 +              <span class="caret"></span>
 +            </a>
 +            <ul class="dropdown-menu" role="menu">
 +              <% if (@notifications || []).length > 0 %>
 +                <% @notifications.each_with_index do |n, i| %>
 +                  <% if i > 0 %><li class="divider"></li><% end %>
 +                  <li class="notification"><%= n.call(self) %></li>
 +                <% end %>
 +              <% else %>
 +                <li class="notification empty">No notifications.</li>
                <% end %>
 -              </ul>
 -            </li>
 +            </ul>
 +          </li>
            <% end %>
  
 -          <li>Help
 -            <ul>
 -              <li><%= link_to 'Tutorials and User guide', "#{Rails.configuration.arvados_docsite}/user", target: "_blank" %></li>
 -              <li><%= link_to 'API Reference', "#{Rails.configuration.arvados_docsite}/api", target: "_blank" %></li>
 -              <li><%= link_to 'SDK Reference', "#{Rails.configuration.arvados_docsite}/sdk", target: "_blank" %></li>
 +          <li class="dropdown">
 +            <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="user-menu">
 +              <span class="glyphicon glyphicon-user"></span><span class="caret"></span>
 +            </a>
 +            <ul class="dropdown-menu" role="menu">
 +              <li role="presentation" class="dropdown-header"><%= current_user.email %></li>
 +              <% if current_user.is_active %>
 +              <li role="presentation" class="divider"></li>
 +              <li role="presentation"><a href="/authorized_keys" role="menuitem"><i class="fa fa-key fa-fw"></i> Manage ssh keys</a></li>
 +              <li role="presentation"><a href="/api_client_authorizations" role="menuitem"><i class="fa fa-ticket fa-fw"></i> Manage API tokens</a></li>
 +              <li role="presentation" class="divider"></li>
 +              <% end %>
 +              <li role="presentation"><a href="<%= logout_path %>" role="menuitem"><i class="fa fa-sign-out fa-fw"></i> Log out</a></li>
              </ul>
            </li>
-             <li><a href="<%= $arvados_api_client.arvados_login_url(return_to: root_url) %>">Log in</a></li>
 +          <% else %>
++            <li><a href="<%= arvados_api_client.arvados_login_url(return_to: root_url) %>">Log in</a></li>
 +          <% end %>
          </ul>
 -        </div>
 -        <% end %>
 -      </div>
 -        </div>
 +      </div><!-- /.navbar-collapse -->
 +    </nav>
 +
 +    <div id="page-wrapper">
 +      <%= yield %>
 +    </div>
    </div>
  
 +</div>
 +
    <%= yield :footer_html %>
    <%= piwik_tracking_tag %>
    <%= javascript_tag do %>
Simple merge