[ARVADOS] updated: 949c388c5ec3bc4f8cc6b3d503c11dcd20e5bbd4

git at public.curoverse.com git at public.curoverse.com
Thu May 15 14:20:02 EDT 2014


Summary of changes:
 apps/workbench/Gemfile                             |  18 ++-
 apps/workbench/Gemfile.lock                        | 147 +++++++++++----------
 .../api_client_authorizations.js.coffee            |   3 -
 .../assets/javascripts/authorized_keys.js.coffee   |   3 -
 .../app/assets/javascripts/folders.js.coffee       |   3 -
 .../app/assets/javascripts/groups.js.coffee        |   3 -
 .../app/assets/javascripts/humans.js.coffee        |   3 -
 .../app/assets/javascripts/job_tasks.js.coffee     |   3 -
 .../app/assets/javascripts/jobs.js.coffee          |   3 -
 .../app/assets/javascripts/keep_disks.js.coffee    |   4 -
 .../app/assets/javascripts/links.js.coffee         |   3 -
 .../app/assets/javascripts/logs.js.coffee          |   3 -
 .../app/assets/javascripts/nodes.js.coffee         |   3 -
 .../app/assets/javascripts/pipeline_instances.js   |   1 -
 .../javascripts/pipeline_templates.js.coffee       |   3 -
 .../app/assets/javascripts/repositories.js.coffee  |   3 -
 .../app/assets/javascripts/sessions.js.coffee      |   3 -
 .../app/assets/javascripts/specimens.js.coffee     |   3 -
 .../app/assets/javascripts/traits.js.coffee        |   3 -
 .../assets/javascripts/user_agreements.js.coffee   |   3 -
 .../app/assets/javascripts/users.js.coffee         |   3 -
 .../assets/javascripts/virtual_machines.js.coffee  |   3 -
 .../app/controllers/actions_controller.rb          |   2 +-
 .../app/controllers/application_controller.rb      |   4 +-
 .../app/controllers/collections_controller.rb      |   2 +-
 .../app/controllers/sessions_controller.rb         |   2 +-
 apps/workbench/app/controllers/users_controller.rb |  10 +-
 .../app/helpers/arvados_api_client_helper.rb       |  13 ++
 apps/workbench/app/models/arvados_api_client.rb    |  34 +++--
 apps/workbench/app/models/arvados_base.rb          |  60 ++++++---
 apps/workbench/app/models/arvados_resource_list.rb |   7 +-
 apps/workbench/app/models/collection.rb            |   4 +-
 apps/workbench/app/models/group.rb                 |   4 +-
 apps/workbench/app/models/user.rb                  |  26 ++--
 apps/workbench/app/models/user_agreement.rb        |   8 +-
 .../app/views/layouts/application.html.erb         |   2 +-
 apps/workbench/app/views/links/_recent.html.erb    |   2 +-
 .../workbench/app/views/users/_show_admin.html.erb |   2 +-
 apps/workbench/app/views/users/welcome.html.erb    |   2 +-
 apps/workbench/app/views/websocket/index.html.erb  |   2 +-
 apps/workbench/config/application.default.yml      |  14 +-
 apps/workbench/config/application.rb               |  15 +--
 .../config/environments/development.rb.example     |  12 +-
 .../config/environments/production.rb.example      |   6 +-
 apps/workbench/config/environments/test.rb.example |   6 -
 .../config/initializers/zzz_arvados_api_client.rb  |   8 --
 .../zza_load_config.rb => load_config.rb}          |   0
 apps/workbench/config/routes.rb                    |   8 +-
 apps/workbench/test/integration/users_test.rb      |   7 +-
 apps/workbench/test/test_helper.rb                 |  78 ++++++++---
 .../install-workbench-app.html.textile.liquid      |  11 ++
 51 files changed, 298 insertions(+), 277 deletions(-)
 delete mode 100644 apps/workbench/app/assets/javascripts/api_client_authorizations.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/authorized_keys.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/folders.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/groups.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/humans.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/job_tasks.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/jobs.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/links.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/logs.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/nodes.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/pipeline_templates.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/repositories.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/sessions.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/specimens.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/traits.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/user_agreements.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/users.js.coffee
 delete mode 100644 apps/workbench/app/assets/javascripts/virtual_machines.js.coffee
 create mode 100644 apps/workbench/app/helpers/arvados_api_client_helper.rb
 delete mode 100644 apps/workbench/config/initializers/zzz_arvados_api_client.rb
 rename apps/workbench/config/{initializers/zza_load_config.rb => load_config.rb} (100%)

       via  949c388c5ec3bc4f8cc6b3d503c11dcd20e5bbd4 (commit)
       via  03395937ba05b9e3192e346a355c691f45cc7c85 (commit)
       via  cff5301bbc1e1c8f8421fb56dca5a76dfebb058c (commit)
       via  2d9f17f33a9d8737d97705b8b77e3e1c05f7b411 (commit)
       via  67176a2637ca895015908c7c3bdb1ab7688c1b2c (commit)
       via  a797b27604a0f5a277d73e2d3522dec033fd4ecf (commit)
       via  6ebfb077cbe7357aadccc7ae4f803c61d66d0279 (commit)
       via  a792aabc3159439c66098935fea2da685eee7ed2 (commit)
       via  7ec5dedd6301ae84db941de986f2f0eacc4532e7 (commit)
       via  130bd6ec07cf6844861e49602d33b07dd2402595 (commit)
       via  a46e35694b573fd5c9b355c51323992670fc87b4 (commit)
       via  93842871ab5266e45fb6a29da89462af102a6384 (commit)
       via  85ab544a82c685a74d814a0e10c48624810be0af (commit)
       via  bfa9ee952c1ae3f03fe2f9fa781a132411963030 (commit)
       via  647c8b8c47ef521046bea7675af84b621c235e0f (commit)
       via  6f6235983d91e0b9fb5660d0a2825f510bbdac73 (commit)
       via  54897c8f978f127a44c0a9d20c1e87cea840b21f (commit)
      from  ce5b843c32f6ee74f02a40044073dd4288d16dee (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 949c388c5ec3bc4f8cc6b3d503c11dcd20e5bbd4
Merge: ce5b843 0339593
Author: Tom Clegg <tom at curoverse.com>
Date:   Thu May 15 14:19:32 2014 -0400

    2809: Merge branch '2809-workbench-rails4' refs #2809


commit 03395937ba05b9e3192e346a355c691f45cc7c85
Merge: cff5301 ce5b843
Author: Tom Clegg <tom at curoverse.com>
Date:   Thu May 15 14:19:06 2014 -0400

    2809: Merge branch 'master' into 2809-workbench-rails4 refs #2809
    
    Conflicts:
    	apps/workbench/app/models/arvados_api_client.rb

diff --cc apps/workbench/app/models/arvados_api_client.rb
index dc1b6ed,c6d8720..25e776a
--- a/apps/workbench/app/models/arvados_api_client.rb
+++ b/apps/workbench/app/models/arvados_api_client.rb
@@@ -67,12 -57,10 +67,12 @@@ class ArvadosApiClien
  
      header = {"Accept" => "application/json"}
  
-     profile_checkpoint { "Prepare request #{url} #{query[:uuid]} #{query[:where]}" }
+     profile_checkpoint { "Prepare request #{url} #{query[:uuid]} #{query[:where]} #{query[:filters]}" }
 -    msg = @@api_client.post(url,
 -                            query,
 -                            header: header)
 +    msg = @client_mtx.synchronize do
 +      @api_client.post(url,
 +                       query,
 +                       header: header)
 +    end
      profile_checkpoint 'API transaction'
  
      if msg.status_code == 401

commit cff5301bbc1e1c8f8421fb56dca5a76dfebb058c
Author: Tom Clegg <tom at curoverse.com>
Date:   Thu May 15 12:30:25 2014 -0400

    2809: Move coffee-rails out of :assets section to make tests thread-safe.
    
    Otherwise, when Rails decides to build assets while running the test
    suite, we get:
    
    WARN: tilt autoloading 'coffee_script' in a non thread-safe way; explicit require 'coffee_script' suggested.

diff --git a/apps/workbench/Gemfile b/apps/workbench/Gemfile
index 5458025..754d5c6 100644
--- a/apps/workbench/Gemfile
+++ b/apps/workbench/Gemfile
@@ -12,11 +12,17 @@ gem 'multi_json'
 gem 'oj'
 gem 'sass'
 
+# Note: keeping this out of the "group :assets" section "may" allow us
+# to use Coffescript for UJS responses. It also prevents a
+# warning/problem when running tests: "WARN: tilt autoloading
+# 'coffee_script' in a non thread-safe way; explicit require
+# 'coffee_script' suggested."
+gem 'coffee-rails'
+
 # Gems used only for assets and not required
 # in production environments by default.
 group :assets do
   gem 'sass-rails'
-  gem 'coffee-rails'
 
   # See https://github.com/sstephenson/execjs#readme for more supported runtimes
   gem 'therubyracer', :platforms => :ruby

commit 2d9f17f33a9d8737d97705b8b77e3e1c05f7b411
Author: Tom Clegg <tom at curoverse.com>
Date:   Thu May 15 12:26:10 2014 -0400

    2809: Remove javascript and coffeescript boilerplate.

diff --git a/apps/workbench/app/assets/javascripts/api_client_authorizations.js.coffee b/apps/workbench/app/assets/javascripts/api_client_authorizations.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/api_client_authorizations.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/authorized_keys.js.coffee b/apps/workbench/app/assets/javascripts/authorized_keys.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/authorized_keys.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/folders.js.coffee b/apps/workbench/app/assets/javascripts/folders.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/folders.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/groups.js.coffee b/apps/workbench/app/assets/javascripts/groups.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/groups.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/humans.js.coffee b/apps/workbench/app/assets/javascripts/humans.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/humans.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/job_tasks.js.coffee b/apps/workbench/app/assets/javascripts/job_tasks.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/job_tasks.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/jobs.js.coffee b/apps/workbench/app/assets/javascripts/jobs.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/jobs.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/keep_disks.js.coffee b/apps/workbench/app/assets/javascripts/keep_disks.js.coffee
index cc3aac7..e4aa4b4 100644
--- a/apps/workbench/app/assets/javascripts/keep_disks.js.coffee
+++ b/apps/workbench/app/assets/javascripts/keep_disks.js.coffee
@@ -1,7 +1,3 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
-
 cache_age_in_days = (milliseconds_age) ->
   ONE_DAY = 1000 * 60 * 60 * 24
   milliseconds_age / ONE_DAY
diff --git a/apps/workbench/app/assets/javascripts/links.js.coffee b/apps/workbench/app/assets/javascripts/links.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/links.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/logs.js.coffee b/apps/workbench/app/assets/javascripts/logs.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/logs.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/nodes.js.coffee b/apps/workbench/app/assets/javascripts/nodes.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/nodes.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/pipeline_instances.js b/apps/workbench/app/assets/javascripts/pipeline_instances.js
index ee14e3b..a9ca4df 100644
--- a/apps/workbench/app/assets/javascripts/pipeline_instances.js
+++ b/apps/workbench/app/assets/javascripts/pipeline_instances.js
@@ -1,4 +1,3 @@
-
 (function() {
     var run_pipeline_button_state = function() {
         var a = $('a.editable.required.editable-empty');
diff --git a/apps/workbench/app/assets/javascripts/pipeline_templates.js.coffee b/apps/workbench/app/assets/javascripts/pipeline_templates.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/pipeline_templates.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/repositories.js.coffee b/apps/workbench/app/assets/javascripts/repositories.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/repositories.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/sessions.js.coffee b/apps/workbench/app/assets/javascripts/sessions.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/sessions.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/specimens.js.coffee b/apps/workbench/app/assets/javascripts/specimens.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/specimens.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/traits.js.coffee b/apps/workbench/app/assets/javascripts/traits.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/traits.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/user_agreements.js.coffee b/apps/workbench/app/assets/javascripts/user_agreements.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/user_agreements.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/users.js.coffee b/apps/workbench/app/assets/javascripts/users.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/users.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/apps/workbench/app/assets/javascripts/virtual_machines.js.coffee b/apps/workbench/app/assets/javascripts/virtual_machines.js.coffee
deleted file mode 100644
index 7615679..0000000
--- a/apps/workbench/app/assets/javascripts/virtual_machines.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/

commit 67176a2637ca895015908c7c3bdb1ab7688c1b2c
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed May 14 18:00:09 2014 -0400

    2809: Remove some unnecessary sleep(0.1).

diff --git a/apps/workbench/test/integration/users_test.rb b/apps/workbench/test/integration/users_test.rb
index a019ec7..7651563 100644
--- a/apps/workbench/test/integration/users_test.rb
+++ b/apps/workbench/test/integration/users_test.rb
@@ -123,7 +123,6 @@ class UsersTest < ActionDispatch::IntegrationTest
       wait_for_ajax
     end
 
-    sleep(1)
     assert page.has_text? 'modified_by_client_uuid'
 
     click_link 'Metadata'
@@ -143,7 +142,6 @@ class UsersTest < ActionDispatch::IntegrationTest
       wait_for_ajax
     end
 
-    sleep(0.1)
     assert page.has_text? 'modified_by_client_uuid'
 
     click_link 'Metadata'
@@ -209,7 +207,6 @@ class UsersTest < ActionDispatch::IntegrationTest
       wait_for_ajax
     end
 
-    sleep(0.1)
     assert page.has_text? 'modified_by_client_uuid'
 
     click_link 'Metadata'

commit a797b27604a0f5a277d73e2d3522dec033fd4ecf
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed May 14 17:56:59 2014 -0400

    2809: Rearrange "run API server during tests" to suit Minitest 5.
    Also, use passenger instead of webrick.

diff --git a/apps/workbench/Gemfile b/apps/workbench/Gemfile
index e7e60c5..5458025 100644
--- a/apps/workbench/Gemfile
+++ b/apps/workbench/Gemfile
@@ -15,8 +15,8 @@ gem 'sass'
 # Gems used only for assets and not required
 # in production environments by default.
 group :assets do
-  gem 'sass-rails',   '~> 4.0.0'
-  gem 'coffee-rails', '~> 4.0.0'
+  gem 'sass-rails'
+  gem 'coffee-rails'
 
   # See https://github.com/sstephenson/execjs#readme for more supported runtimes
   gem 'therubyracer', :platforms => :ruby
diff --git a/apps/workbench/Gemfile.lock b/apps/workbench/Gemfile.lock
index 9adb4a3..173be13 100644
--- a/apps/workbench/Gemfile.lock
+++ b/apps/workbench/Gemfile.lock
@@ -195,7 +195,7 @@ DEPENDENCIES
   bootstrap-sass (~> 3.1.0)
   bootstrap-x-editable-rails
   capybara
-  coffee-rails (~> 4.0.0)
+  coffee-rails
   deep_merge
   headless
   httpclient
@@ -211,7 +211,7 @@ DEPENDENCIES
   rails (~> 4.1.0)
   rvm-capistrano
   sass
-  sass-rails (~> 4.0.0)
+  sass-rails
   selenium-webdriver
   simplecov (~> 0.7.1)
   simplecov-rcov
diff --git a/apps/workbench/test/test_helper.rb b/apps/workbench/test/test_helper.rb
index 05be43c..c1eed5c 100644
--- a/apps/workbench/test/test_helper.rb
+++ b/apps/workbench/test/test_helper.rb
@@ -22,9 +22,6 @@ end
 require File.expand_path('../../config/environment', __FILE__)
 require 'rails/test_help'
 
-$ARV_API_SERVER_DIR = File.expand_path('../../../../services/api', __FILE__)
-SERVER_PID_PATH = 'tmp/pids/server.pid'
-
 class ActiveSupport::TestCase
   # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in
   # alphabetical order.
@@ -54,7 +51,8 @@ module ApiFixtureLoader
       # Returns the data structure from the named API server test fixture.
       @@api_fixtures[name] ||= \
       begin
-        path = File.join($ARV_API_SERVER_DIR, 'test', 'fixtures', "#{name}.yml")
+        path = File.join(ApiServerForTests::ARV_API_SERVER_DIR,
+                         'test', 'fixtures', "#{name}.yml")
         YAML.load(IO.read(path))
       end
     end
@@ -73,8 +71,13 @@ class ActiveSupport::TestCase
   end
 end
 
-class ApiServerBackedTestRunner < MiniTest::Unit
-  def _system(*cmd)
+class ApiServerForTests
+  ARV_API_SERVER_DIR = File.expand_path('../../../../services/api', __FILE__)
+  SERVER_PID_PATH = File.expand_path('tmp/pids/wbtest-server.pid', ARV_API_SERVER_DIR)
+  @main_process_pid = $$
+
+  def self._system(*cmd)
+    $stderr.puts "_system #{cmd.inspect}"
     Bundler.with_clean_env do
       if not system({'RAILS_ENV' => 'test'}, *cmd)
         raise RuntimeError, "#{cmd[0]} returned exit code #{$?.exitstatus}"
@@ -82,35 +85,66 @@ class ApiServerBackedTestRunner < MiniTest::Unit
     end
   end
 
-  def _run(args=[])
+  def self.make_ssl_cert
+    unless File.exists? './self-signed.key'
+      _system('openssl', 'req', '-new', '-x509', '-nodes',
+              '-out', './self-signed.pem',
+              '-keyout', './self-signed.key',
+              '-days', '3650',
+              '-subj', '/CN=localhost')
+    end
+  end
+
+  def self.kill_server
+    if (pid = find_server_pid)
+      $stderr.puts "Sending TERM to API server, pid #{pid}"
+      Process.kill 'TERM', pid
+    end
+  end
+
+  def self.find_server_pid
+    pid = nil
+    begin
+      pid = IO.read(SERVER_PID_PATH).to_i
+      $stderr.puts "API server is running, pid #{pid.inspect}"
+    rescue Errno::ENOENT
+    end
+    return pid
+  end
+
+  def self.run(args=[])
+    ::MiniTest.after_run do
+      self.kill_server
+    end
+
+    # Kill server left over from previous test run
+    self.kill_server
+
     Capybara.javascript_driver = :poltergeist
-    server_pid = Dir.chdir($ARV_API_SERVER_DIR) do |apidir|
+    Dir.chdir(ARV_API_SERVER_DIR) do |apidir|
       ENV["NO_COVERAGE_TEST"] = "1"
+      make_ssl_cert
       _system('bundle', 'exec', 'rake', 'db:test:load')
       _system('bundle', 'exec', 'rake', 'db:fixtures:load')
-      _system('bundle', 'exec', 'rails', 'server', '-d')
+      _system('bundle', 'exec', 'passenger', 'start', '-d', '-p3001',
+              '--pid-file', SERVER_PID_PATH,
+              '--ssl',
+              '--ssl-certificate', 'self-signed.pem',
+              '--ssl-certificate-key', 'self-signed.key')
       timeout = Time.now.tv_sec + 10
       good_pid = false
       while (not good_pid) and (Time.now.tv_sec < timeout)
         sleep 0.2
-        begin
-          server_pid = IO.read(SERVER_PID_PATH).to_i
-          good_pid = (server_pid > 0) and (Process.kill(0, server_pid) rescue false)
-        rescue Errno::ENOENT
-          good_pid = false
-        end
+        server_pid = find_server_pid
+        good_pid = (server_pid and
+                    (server_pid > 0) and
+                    (Process.kill(0, server_pid) rescue false))
       end
       if not good_pid
         raise RuntimeError, "could not find API server Rails pid"
       end
-      server_pid
-    end
-    begin
-      super(args)
-    ensure
-      Process.kill('TERM', server_pid)
     end
   end
 end
 
-MiniTest::Unit.runner = ApiServerBackedTestRunner.new
+ApiServerForTests.run

commit 6ebfb077cbe7357aadccc7ae4f803c61d66d0279
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed May 14 16:35:39 2014 -0400

    2809: Upgrade to Rails 4.1

diff --git a/apps/workbench/Gemfile b/apps/workbench/Gemfile
index 98d5747..e7e60c5 100644
--- a/apps/workbench/Gemfile
+++ b/apps/workbench/Gemfile
@@ -1,6 +1,7 @@
 source 'https://rubygems.org'
 
-gem 'rails', '~> 4.0.0'
+gem 'rails', '~> 4.1.0'
+gem 'minitest', '>= 5.0.0'
 
 # Bundle edge Rails instead:
 # gem 'rails', :git => 'git://github.com/rails/rails.git'
diff --git a/apps/workbench/Gemfile.lock b/apps/workbench/Gemfile.lock
index 4cdd3fc..9adb4a3 100644
--- a/apps/workbench/Gemfile.lock
+++ b/apps/workbench/Gemfile.lock
@@ -10,37 +10,39 @@ GEM
   remote: https://rubygems.org/
   specs:
     RedCloth (4.2.9)
-    actionmailer (4.0.5)
-      actionpack (= 4.0.5)
+    actionmailer (4.1.1)
+      actionpack (= 4.1.1)
+      actionview (= 4.1.1)
       mail (~> 2.5.4)
-    actionpack (4.0.5)
-      activesupport (= 4.0.5)
-      builder (~> 3.1.0)
-      erubis (~> 2.7.0)
+    actionpack (4.1.1)
+      actionview (= 4.1.1)
+      activesupport (= 4.1.1)
       rack (~> 1.5.2)
       rack-test (~> 0.6.2)
-    activemodel (4.0.5)
-      activesupport (= 4.0.5)
-      builder (~> 3.1.0)
-    activerecord (4.0.5)
-      activemodel (= 4.0.5)
-      activerecord-deprecated_finders (~> 1.0.2)
-      activesupport (= 4.0.5)
-      arel (~> 4.0.0)
-    activerecord-deprecated_finders (1.0.3)
-    activesupport (4.0.5)
+    actionview (4.1.1)
+      activesupport (= 4.1.1)
+      builder (~> 3.1)
+      erubis (~> 2.7.0)
+    activemodel (4.1.1)
+      activesupport (= 4.1.1)
+      builder (~> 3.1)
+    activerecord (4.1.1)
+      activemodel (= 4.1.1)
+      activesupport (= 4.1.1)
+      arel (~> 5.0.0)
+    activesupport (4.1.1)
       i18n (~> 0.6, >= 0.6.9)
-      minitest (~> 4.2)
-      multi_json (~> 1.3)
+      json (~> 1.7, >= 1.7.7)
+      minitest (~> 5.1)
       thread_safe (~> 0.1)
-      tzinfo (~> 0.3.37)
+      tzinfo (~> 1.1)
     andand (1.3.3)
-    arel (4.0.2)
+    arel (5.0.1.20140414130214)
     bootstrap-sass (3.1.0.1)
       sass (~> 3.2)
     bootstrap-x-editable-rails (1.5.1.1)
       railties (>= 3.0)
-    builder (3.1.4)
+    builder (3.2.2)
     capistrano (2.15.5)
       highline
       net-scp (>= 1.0.0)
@@ -89,7 +91,7 @@ GEM
       treetop (~> 1.4.8)
     mime-types (1.25.1)
     mini_portile (0.5.2)
-    minitest (4.7.5)
+    minitest (5.3.3)
     multi_json (1.10.0)
     net-scp (1.1.2)
       net-ssh (>= 2.6.5)
@@ -118,17 +120,19 @@ GEM
     rack (1.5.2)
     rack-test (0.6.2)
       rack (>= 1.0)
-    rails (4.0.5)
-      actionmailer (= 4.0.5)
-      actionpack (= 4.0.5)
-      activerecord (= 4.0.5)
-      activesupport (= 4.0.5)
+    rails (4.1.1)
+      actionmailer (= 4.1.1)
+      actionpack (= 4.1.1)
+      actionview (= 4.1.1)
+      activemodel (= 4.1.1)
+      activerecord (= 4.1.1)
+      activesupport (= 4.1.1)
       bundler (>= 1.3.0, < 2.0)
-      railties (= 4.0.5)
-      sprockets-rails (~> 2.0.0)
-    railties (4.0.5)
-      actionpack (= 4.0.5)
-      activesupport (= 4.0.5)
+      railties (= 4.1.1)
+      sprockets-rails (~> 2.0)
+    railties (4.1.1)
+      actionpack (= 4.1.1)
+      activesupport (= 4.1.1)
       rake (>= 0.8.7)
       thor (>= 0.18.1, < 2.0)
     rake (10.3.1)
@@ -158,7 +162,7 @@ GEM
       multi_json (~> 1.0)
       rack (~> 1.0)
       tilt (~> 1.1, != 1.3.0)
-    sprockets-rails (2.0.1)
+    sprockets-rails (2.1.3)
       actionpack (>= 3.0)
       activesupport (>= 3.0)
       sprockets (~> 2.8)
@@ -172,7 +176,8 @@ GEM
     treetop (1.4.15)
       polyglot
       polyglot (>= 0.3.1)
-    tzinfo (0.3.39)
+    tzinfo (1.1.0)
+      thread_safe (~> 0.1)
     uglifier (2.3.1)
       execjs (>= 0.3.0)
       json (>= 1.8.0)
@@ -197,12 +202,13 @@ DEPENDENCIES
   jquery-rails
   less
   less-rails
+  minitest (>= 5.0.0)
   multi_json
   oj
   passenger
   piwik_analytics
   poltergeist
-  rails (~> 4.0.0)
+  rails (~> 4.1.0)
   rvm-capistrano
   sass
   sass-rails (~> 4.0.0)

commit a792aabc3159439c66098935fea2da685eee7ed2
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed May 14 14:21:05 2014 -0400

    2809: Add wait_for_ajax after submitting forms, to avoid race condition.

diff --git a/apps/workbench/test/integration/users_test.rb b/apps/workbench/test/integration/users_test.rb
index 6df7ee3..a019ec7 100644
--- a/apps/workbench/test/integration/users_test.rb
+++ b/apps/workbench/test/integration/users_test.rb
@@ -63,6 +63,7 @@ class UsersTest < ActionDispatch::IntegrationTest
       fill_in "email", :with => "foo at example.com"
       fill_in "repo_name", :with => "test_repo"
       click_button "Submit"
+      wait_for_ajax
     end
 
     visit '/users'
@@ -119,6 +120,7 @@ class UsersTest < ActionDispatch::IntegrationTest
       assert has_text? 'Virtual Machine'
       fill_in "repo_name", :with => "test_repo"
       click_button "Submit"
+      wait_for_ajax
     end
 
     sleep(1)
@@ -138,6 +140,7 @@ class UsersTest < ActionDispatch::IntegrationTest
       fill_in "repo_name", :with => "second_test_repo"
       select("testvm.shell", :from => 'vm_uuid')
       click_button "Submit"
+      wait_for_ajax
     end
 
     sleep(0.1)
@@ -203,6 +206,7 @@ class UsersTest < ActionDispatch::IntegrationTest
       fill_in "repo_name", :with => "second_test_repo"
       select("testvm.shell", :from => 'vm_uuid')
       click_button "Submit"
+      wait_for_ajax
     end
 
     sleep(0.1)

commit 7ec5dedd6301ae84db941de986f2f0eacc4532e7
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed May 14 13:26:42 2014 -0400

    2809: Remove debug printfs, add comment explaining why so much "permit!"

diff --git a/apps/workbench/app/models/arvados_base.rb b/apps/workbench/app/models/arvados_base.rb
index aea6b77..1ad0230 100644
--- a/apps/workbench/app/models/arvados_base.rb
+++ b/apps/workbench/app/models/arvados_base.rb
@@ -30,14 +30,7 @@ class ArvadosBase < ActiveRecord::Base
   end
 
   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
+    super self.class.permit_attribute_params(raw_params)
     @attribute_sortkey ||= {
       'id' => nil,
       'name' => '000',
@@ -144,11 +137,14 @@ class ArvadosBase < ActiveRecord::Base
     # strong_parameters does not provide security in Workbench: anyone
     # who can get this far can just as well do a call directly to our
     # database (Arvados) with the same credentials we use.
+    #
+    # The following permit! is necessary even with
+    # "ActionController::Parameters.permit_all_parameters = true",
+    # because permit_all does not permit nested attributes.
     ActionController::Parameters.new(raw_params).permit!
   end
 
   def self.create raw_params={}
-    logger.error permit_attribute_params(raw_params).inspect
     super(permit_attribute_params(raw_params))
   end
 

commit 130bd6ec07cf6844861e49602d33b07dd2402595
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed May 14 13:25:24 2014 -0400

    2809: Add note about harmless warning when installing the themes_for_rails gem.

diff --git a/doc/install/install-workbench-app.html.textile.liquid b/doc/install/install-workbench-app.html.textile.liquid
index eaf4ede..055ef47 100644
--- a/doc/install/install-workbench-app.html.textile.liquid
+++ b/doc/install/install-workbench-app.html.textile.liquid
@@ -49,6 +49,17 @@ Alternatively, if you don't have sudo/root privileges on the host, install the g
 ~/arvados/apps/workbench$ <span class="userinput">bundle install --path=vendor/bundle</span>
 </code></pre></notextile>
 
+The @bundle install@ command might produce a warning about the themes_for_rails gem. This is OK:
+
+<notextile>
+<pre><code>themes_for_rails at /home/<b>you</b>/.rvm/gems/ruby-2.1.1/bundler/gems/themes_for_rails-1fd2d7897d75 did not have a valid gemspec.
+This prevents bundler from installing bins or native extensions, but that may not affect its functionality.
+The validation message from Rubygems was:
+  duplicate dependency on rails (= 3.0.11, development), (>= 3.0.0) use:
+    add_runtime_dependency 'rails', '= 3.0.11', '>= 3.0.0'
+Using themes_for_rails (0.5.1) from https://github.com/holtkampw/themes_for_rails (at 1fd2d78)
+</code></pre></notextile>
+
 h2. Configure the Workbench application
 
 This application needs a secret token. Generate a new secret:

commit a46e35694b573fd5c9b355c51323992670fc87b4
Author: Tom Clegg <tom at curoverse.com>
Date:   Wed May 14 13:08:04 2014 -0400

    2809: Fix whitespace.

diff --git a/apps/workbench/app/models/arvados_api_client.rb b/apps/workbench/app/models/arvados_api_client.rb
index 9a2b0ce..dc1b6ed 100644
--- a/apps/workbench/app/models/arvados_api_client.rb
+++ b/apps/workbench/app/models/arvados_api_client.rb
@@ -69,7 +69,7 @@ class ArvadosApiClient
 
     profile_checkpoint { "Prepare request #{url} #{query[:uuid]} #{query[:where]}" }
     msg = @client_mtx.synchronize do
-      @api_client.post(url, 
+      @api_client.post(url,
                        query,
                        header: header)
     end

commit 93842871ab5266e45fb6a29da89462af102a6384
Author: Tom Clegg <tom at curoverse.com>
Date:   Fri May 9 20:29:09 2014 -0400

    2809: Turn off secret_key_base by default, instead of requiring it in config.

diff --git a/apps/workbench/config/application.default.yml b/apps/workbench/config/application.default.yml
index 533382e..2fe701a 100644
--- a/apps/workbench/config/application.default.yml
+++ b/apps/workbench/config/application.default.yml
@@ -71,6 +71,6 @@ common:
   arvados_theme: default
   show_user_agreement_inline: false
   secret_token: ~
-  secret_key_base: ~
+  secret_key_base: false
   default_openid_prefix: https://www.google.com/accounts/o8/id
   send_user_setup_notification_email: true

commit 85ab544a82c685a74d814a0e10c48624810be0af
Author: Tom Clegg <tom at curoverse.com>
Date:   Fri May 9 20:28:40 2014 -0400

    2809: Fix load_config so it loads in time to set passenger configs.

diff --git a/apps/workbench/config/application.rb b/apps/workbench/config/application.rb
index c456bb1..4ac6819 100644
--- a/apps/workbench/config/application.rb
+++ b/apps/workbench/config/application.rb
@@ -49,3 +49,5 @@ module ArvadosWorkbench
     config.assets.version = '1.0'
   end
 end
+
+require File.expand_path('../load_config', __FILE__)
diff --git a/apps/workbench/config/initializers/zza_load_config.rb b/apps/workbench/config/load_config.rb
similarity index 100%
rename from apps/workbench/config/initializers/zza_load_config.rb
rename to apps/workbench/config/load_config.rb

commit bfa9ee952c1ae3f03fe2f9fa781a132411963030
Merge: 568d941 647c8b8
Author: Tom Clegg <tom at curoverse.com>
Date:   Fri May 9 14:38:39 2014 -0400

    2809: Merge branch 'master' refs #2809
    
    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

diff --cc apps/workbench/Gemfile.lock
index 8e74832,ca9afb1..4cdd3fc
--- a/apps/workbench/Gemfile.lock
+++ b/apps/workbench/Gemfile.lock
@@@ -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)
@@@ -48,29 -53,28 +53,28 @@@
        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)
@@@ -83,10 -87,11 +87,11 @@@
      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)
@@@ -109,70 -114,60 +114,66 @@@
        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)
 +    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.2.2)
+     sprockets (2.11.0)
        hike (~> 1.2)
        multi_json (~> 1.0)
        rack (~> 1.0)
        tilt (~> 1.1, != 1.3.0)
+     sprockets-rails (2.0.1)
+       actionpack (>= 3.0)
+       activesupport (>= 3.0)
+       sprockets (~> 2.8)
 -    sqlite3 (1.3.9)
 -    therubyracer (0.12.1)
 +    sqlite3 (1.3.8)
-     themes_for_rails (0.5.1)
-       rails (>= 3.0.0)
 +    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)
diff --cc apps/workbench/app/controllers/application_controller.rb
index a3576bc,4ed6187..dfb9e89
--- a/apps/workbench/app/controllers/application_controller.rb
+++ b/apps/workbench/app/controllers/application_controller.rb
@@@ -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
@@@ -194,56 -185,7 +196,56 @@@
    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
diff --cc apps/workbench/app/models/arvados_base.rb
index 1a0da64,47a9d6d..aea6b77
--- a/apps/workbench/app/models/arvados_base.rb
+++ b/apps/workbench/app/models/arvados_base.rb
@@@ -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
  
diff --cc apps/workbench/app/models/arvados_resource_list.rb
index 3f74407,1e2e720..dedd18c
--- a/apps/workbench/app/models/arvados_resource_list.rb
+++ b/apps/workbench/app/models/arvados_resource_list.rb
@@@ -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
  
diff --cc apps/workbench/app/models/group.rb
index dde6019,da8fe60..8d8d390
--- a/apps/workbench/app/models/group.rb
+++ b/apps/workbench/app/models/group.rb
@@@ -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
diff --cc apps/workbench/app/models/user.rb
index 44d615b,59ab1ba..c1656bd
--- a/apps/workbench/app/models/user.rb
+++ b/apps/workbench/app/models/user.rb
@@@ -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
diff --cc apps/workbench/app/views/layouts/application.html.erb
index 2b5ec88,2652565..f5aca0c
--- a/apps/workbench/app/views/layouts/application.html.erb
+++ b/apps/workbench/app/views/layouts/application.html.erb
@@@ -59,189 -68,167 +59,189 @@@
          <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> </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>
 +          <% else %>
-             <li><a href="<%= $arvados_api_client.arvados_login_url(return_to: root_url) %>">Log in</a></li>
++            <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 %>

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list