[ARVADOS] updated: 95cb55add68af9a0742ca8054c0c328658e5e651

git at public.curoverse.com git at public.curoverse.com
Sat Jun 7 13:41:12 EDT 2014


Summary of changes:
 apps/workbench/app/assets/javascripts/selection.js |   2 +-
 .../app/assets/stylesheets/application.css.scss    |  40 ++++--
 .../{folders.css.scss => projects.css.scss}        |   4 +-
 .../app/controllers/actions_controller.rb          |   2 +-
 .../app/controllers/application_controller.rb      |  61 +++++++--
 .../app/controllers/collections_controller.rb      |   6 +-
 .../workbench/app/controllers/groups_controller.rb |   9 +-
 ...olders_controller.rb => projects_controller.rb} |  26 ++--
 apps/workbench/app/helpers/application_helper.rb   |   4 +-
 apps/workbench/app/helpers/folders_helper.rb       |   2 -
 apps/workbench/app/helpers/projects_helper.rb      |   2 +
 apps/workbench/app/models/arvados_base.rb          |   2 +-
 apps/workbench/app/models/collection.rb            |   2 +-
 apps/workbench/app/models/group.rb                 |   4 +-
 apps/workbench/app/models/human.rb                 |   2 +-
 apps/workbench/app/models/job.rb                   |   2 +-
 apps/workbench/app/models/pipeline_instance.rb     |   2 +-
 apps/workbench/app/models/pipeline_template.rb     |   2 +-
 apps/workbench/app/models/specimen.rb              |   2 +-
 apps/workbench/app/models/trait.rb                 |   2 +-
 .../app/views/application/_content.html.erb        |   8 +-
 .../views/application/_selection_checkbox.html.erb |   2 +-
 .../app/views/application/_show_recent.html.erb    |   4 +-
 apps/workbench/app/views/collections/show.html.erb |  10 +-
 .../app/views/folders/_index_folders.html.erb      |  31 -----
 .../app/views/layouts/application.html.erb         | 137 +++++++++++----------
 .../app/views/pipeline_instances/compare.html.erb  |   2 +-
 .../views/{folders => projects}/_choose.html.erb   |  20 +--
 .../app/views/{folders => projects}/_choose.js.erb |   0
 .../_index_jobs_and_pipelines.html.erb             |   0
 .../app/views/projects/_index_projects.html.erb    |  31 +++++
 .../{folders => projects}/_show_contents.html.erb  |  18 +--
 .../_show_contents_rows.html.erb                   |   4 +-
 .../{folders => projects}/_show_featured.html.erb  |   0
 .../_show_permissions.html.erb                     |  10 +-
 .../app/views/{folders => projects}/index.html.erb |  16 +--
 .../{folders => projects}/remove_items.js.erb      |   0
 apps/workbench/config/routes.rb                    |   4 +-
 .../test/functional/collections_controller_test.rb |   8 +-
 ...troller_test.rb => projects_controller_test.rb} |   2 +-
 .../workbench/test/integration/collections_test.rb |   2 +-
 .../test/integration/pipeline_instances_test.rb    |  62 +++++++++-
 .../{folders_test.rb => projects_test.rb}          |  42 +++----
 .../test/unit/arvados_resource_list_test.rb        |  12 +-
 apps/workbench/test/unit/group_test.rb             |  12 +-
 .../test/unit/helpers/folders_helper_test.rb       |   4 -
 ...oups_helper_test.rb => projects_helper_test.rb} |   0
 ...0607150616_rename_folder_to_project.rb_deferred |   9 ++
 services/api/test/fixtures/groups.yml              |  14 +--
 services/api/test/fixtures/links.yml               |  18 +--
 services/api/test/fixtures/specimens.yml           |   8 +-
 .../api/test/functional/arvados/v1/filters_test.rb |   4 +-
 .../arvados/v1/groups_controller_test.rb           |  42 +++----
 .../functional/arvados/v1/links_controller_test.rb |   6 +-
 services/api/test/unit/group_test.rb               |   2 +-
 services/api/test/unit/link_test.rb                |  10 +-
 services/api/test/unit/owner_test.rb               |   2 +-
 57 files changed, 431 insertions(+), 303 deletions(-)
 rename apps/workbench/app/assets/stylesheets/{folders.css.scss => projects.css.scss} (70%)
 rename apps/workbench/app/controllers/{folders_controller.rb => projects_controller.rb} (86%)
 delete mode 100644 apps/workbench/app/helpers/folders_helper.rb
 create mode 100644 apps/workbench/app/helpers/projects_helper.rb
 delete mode 100644 apps/workbench/app/views/folders/_index_folders.html.erb
 rename apps/workbench/app/views/{folders => projects}/_choose.html.erb (57%)
 rename apps/workbench/app/views/{folders => projects}/_choose.js.erb (100%)
 rename apps/workbench/app/views/{folders => projects}/_index_jobs_and_pipelines.html.erb (100%)
 create mode 100644 apps/workbench/app/views/projects/_index_projects.html.erb
 rename apps/workbench/app/views/{folders => projects}/_show_contents.html.erb (84%)
 rename apps/workbench/app/views/{folders => projects}/_show_contents_rows.html.erb (71%)
 rename apps/workbench/app/views/{folders => projects}/_show_featured.html.erb (100%)
 rename apps/workbench/app/views/{folders => projects}/_show_permissions.html.erb (79%)
 rename apps/workbench/app/views/{folders => projects}/index.html.erb (80%)
 rename apps/workbench/app/views/{folders => projects}/remove_items.js.erb (100%)
 rename apps/workbench/test/functional/{folders_controller_test.rb => projects_controller_test.rb} (87%)
 rename apps/workbench/test/integration/{folders_test.rb => projects_test.rb} (60%)
 delete mode 100644 apps/workbench/test/unit/helpers/folders_helper_test.rb
 copy apps/workbench/test/unit/helpers/{groups_helper_test.rb => projects_helper_test.rb} (100%)
 create mode 100644 services/api/db/migrate/20140607150616_rename_folder_to_project.rb_deferred

       via  95cb55add68af9a0742ca8054c0c328658e5e651 (commit)
       via  e08c67024acccb83a30d2010c34862973b883585 (commit)
       via  a1f5f2c608f87b7bb59a4044cd02184a47494188 (commit)
       via  b2607e91145c2286eb6379abb5d3108eae8f4b30 (commit)
       via  563f568124876cc8577baec11caf33e73466b975 (commit)
      from  b6199657cd6867b05ab191da01e04e15ad10cc7d (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 95cb55add68af9a0742ca8054c0c328658e5e651
Author: Tom Clegg <tom at curoverse.com>
Date:   Sat Jun 7 13:40:40 2014 -0400

    2872: Rearrange top nav with breadcrumbs/projects bar.

diff --git a/apps/workbench/app/assets/stylesheets/application.css.scss b/apps/workbench/app/assets/stylesheets/application.css.scss
index 1a821bc..d261de8 100644
--- a/apps/workbench/app/assets/stylesheets/application.css.scss
+++ b/apps/workbench/app/assets/stylesheets/application.css.scss
@@ -90,13 +90,37 @@ form.small-form-margin {
     white-space: nowrap;
 }
 
-.navbar .nav li.nav-separator span {
-    display: block;
-    float: none;
-    color: #bbbbbb;
-    padding: 10px 0 10px;
-    text-decoration: none;
-    text-shadow: 0 1px 0 #ffffff;
+/* top nav */
+$top-nav-bg: #3c163d;
+nav.navbar-fixed-top .navbar-brand {
+    color: #79537a;
+    letter-spacing: 0.4em;
+}
+nav.navbar-fixed-top {
+    background: $top-nav-bg;
+}
+.navbar.breadcrumbs {
+    line-height: 50px;
+    border-radius: 0;
+    width: 10000px;
+    margin-bottom: 0;
+}
+.navbar.breadcrumbs .nav > li > a,
+.navbar.breadcrumbs .nav > li {
+    color: #000;
+}
+.navbar.breadcrumbs .nav > li.nav-separator > i {
+    color: #bbb;
+}
+nav.navbar-fixed-top .navbar-nav.navbar-right > li.open > a,
+nav.navbar-fixed-top .navbar-nav.navbar-right > li.open > a:focus,
+nav.navbar-fixed-top .navbar-nav.navbar-right > li.open > a:hover {
+    background: lighten($top-nav-bg, 5%);
+}
+nav.navbar-fixed-top .navbar-nav.navbar-right > li,
+nav.navbar-fixed-top .navbar-nav.navbar-right > li > a,
+nav.navbar-fixed-top .navbar-nav.navbar-right > li > a:hover {
+    color: #fff;
 }
 
 .dax {
diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb
index 9627ab6..48b508a 100644
--- a/apps/workbench/app/controllers/application_controller.rb
+++ b/apps/workbench/app/controllers/application_controller.rb
@@ -501,12 +501,17 @@ class ApplicationController < ActionController::Base
     end
   end
 
+  helper_method :all_projects
+  def all_projects
+    @all_projects ||= Group.filter([['group_class','in',['project','folder']]])
+  end
+
   helper_method :my_projects
   def my_projects
     return @my_projects if @my_projects
     @my_projects = []
     root_of = {}
-    Group.filter([['group_class','in',['project','folder']]]).each do |g|
+    all_projects.each do |g|
       root_of[g.uuid] = g.owner_uuid
       @my_projects << g
     end
@@ -527,6 +532,12 @@ class ApplicationController < ActionController::Base
     end
   end
 
+  helper_method :projects_shared_with_me
+  def projects_shared_with_me
+    my_project_uuids = my_projects.collect &:uuid
+    all_projects.reject { |x| x.uuid.in? my_project_uuids }
+  end
+
   helper_method :recent_jobs_and_pipelines
   def recent_jobs_and_pipelines
     in_my_projects = ['owner_uuid','in',my_projects.collect(&:uuid)]
@@ -547,4 +558,34 @@ class ApplicationController < ActionController::Base
     @get_object ||= {}
     @get_object[uuid]
   end
+
+  helper_method :project_breadcrumbs
+  def project_breadcrumbs
+    crumbs = []
+    current = @name_link || @object
+    while current
+      if current.is_a?(Group) and current.group_class.in?(['project','folder'])
+        crumbs.prepend current
+      end
+      if current.is_a? Link
+        current = Group.find?(current.tail_uuid)
+      else
+        current = Group.find?(current.owner_uuid)
+      end
+    end
+    crumbs
+  end
+
+  helper_method :current_project_uuid
+  def current_project_uuid
+    if @object.is_a? Group and @object.group_class.in?(['project','folder'])
+      @object.uuid
+    elsif @name_link.andand.tail_uuid
+      @name_link.tail_uuid
+    elsif @object and resource_class_for_uuid(@object.owner_uuid) == Group
+      @object.owner_uuid
+    else
+      nil
+    end
+  end
 end
diff --git a/apps/workbench/app/views/layouts/application.html.erb b/apps/workbench/app/views/layouts/application.html.erb
index 7a7231f..4287115 100644
--- a/apps/workbench/app/views/layouts/application.html.erb
+++ b/apps/workbench/app/views/layouts/application.html.erb
@@ -34,10 +34,6 @@
 
     @media (max-width: 979px) { body { padding-top: 0; } }
 
-    .navbar .nav li.nav-separator > span.glyphicon.glyphicon-arrow-right {
-    padding-top: 1.25em;
-    }
-
     @media (max-width: 767px) {
     .breadcrumbs {
     display: none;
@@ -56,40 +52,10 @@
           <span class="icon-bar"></span>
           <span class="icon-bar"></span>
         </button>
-        <a class="navbar-brand" href="/"><%= Rails.configuration.site_name rescue Rails.application.class.parent_name %></a>
+        <a class="navbar-brand" href="/"><%= Rails.configuration.site_name.downcase rescue Rails.application.class.parent_name %></a>
       </div>
 
       <div class="collapse navbar-collapse">
-        <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>
-                <% if @object and \
-                      ((@name_link and (o = Group.find?(@name_link.tail_uuid)))\
-                       or (o = Group.find?(@object.owner_uuid))) %>
-                  <%= link_to(o.name, project_path(o.uuid)) %>
-                <% else %>
-                  <%= link_to(
-                      controller.controller_name.humanize.downcase,
-                      url_for({controller: params[:controller]})) %>
-                <% end %>
-              </li>
-              <% if params[:action] != 'index' %>
-                <li class="nav-separator">
-                  <span class="glyphicon glyphicon-arrow-right"></span>
-                </li>
-                <li>
-                  
-                  <%= link_to_if_arvados_object (@name_link || @object), {friendly_name: true}, {data: {object_uuid: (@name_link.andand.uuid || @object.andand.uuid), name: 'name'}} %>
-                </li>
-              <% end %>
-            <% end %>
-          <% end %>
-        </ul>
-
         <ul class="nav navbar-nav navbar-right">
 
           <li>
@@ -108,67 +74,53 @@
           </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 %>
+          <% if current_user %>
           <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>
+              <%= current_user.email %>
             </a>
             <ul class="dropdown-menu" role="menu">
-              <% if (@notifications || []).length > 0 %>
+              <% if current_user.is_active %>
+              <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>
+              <% if current_user.is_active and
+                    (@notifications || []).length > 0 %>
+                <li role="presentation" class="divider"></li>
                 <% @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>
           <% 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>
+          <li class="dropdown notification-menu">
+            <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="collections-menu">
+              <span class="fa fa-lg fa-paperclip"></span>
+              <span class="badge" id="persistent-selection-count"></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>
+            <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 %>
-              <li role="presentation"><a href="<%= logout_path %>" role="menuitem"><i class="fa fa-sign-out fa-fw"></i> Log out</a></li>
             </ul>
           </li>
 
           <% if current_user.is_active %>
             <li class="dropdown">
               <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="system-menu">
-                <span class="glyphicon glyphicon-cog"></span><span class="caret"></span>
+                <span class="fa fa-lg fa-gear"></span>
               </a>
               <ul class="dropdown-menu" role="menu">
                 <li role="presentation" class="dropdown-header">
-                  <i class="fa fa-lg fa-fw"></i>
                   System tools
                 </li>
-                <li role="presentation" class="divider"></li>
                 <li role="presentation"><a href="/repositories">
                     <i class="fa fa-lg fa-code-fork fa-fw"></i> Repositories
                 </a></li>
@@ -204,6 +156,54 @@
         </ul>
       </div><!-- /.navbar-collapse -->
     </nav>
+    <nav class="navbar navbar-default breadcrumbs" role="navigation">
+        <ul class="nav navbar-nav navbar-left">
+          <li class="dropdown">
+            <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="projects-menu">
+              <i class="fa fa-lg fa-fw fa-home"></i>
+              Projects
+              <span class="caret"></span>
+            </a>
+            <ul class="dropdown-menu" role="menu">
+              <li role="presentation" class="dropdown-header">
+                <%= link_to projects_path('project[owner_uuid]' => current_project_uuid), method: 'post', class: 'btn btn-xs btn-default pull-right' do %>
+                  <i class="fa fa-plus"></i> New project
+                <% end %>
+                My projects
+              </li>
+              <% my_projects.each do |p| %>
+                <li>
+                  <%= link_to(p.name, project_path(p.uuid)) %>
+                </li>
+              <% end %>
+              <li class="divider">
+              <li role="presentation" class="dropdown-header">
+                Projects shared with me
+              </li>
+              <% projects_shared_with_me.each do |p| %>
+                <li>
+                  <%= link_to project_path(p.uuid) do %>
+                    <i class="fa fa-fw fa-share-alt"></i> <%= p.name %>
+                  <% end %>
+                </li>
+              <% end %>
+            </ul>
+          </li>
+          <% project_breadcrumbs.each do |p| %>
+            <li class="nav-separator">
+              <i class="fa fa-lg fa-angle-double-right"></i>
+            </li>
+            <li>
+              <%= link_to(p.name, project_path(p.uuid), data: {object_uuid: p.uuid, name: 'name'}) %>
+            </li>
+          <% end %>
+          <% if current_project_uuid.andand != @object.andand.uuid %>
+            <li class="nav-separator">
+              <i class="fa fa-lg fa-angle-double-right"></i>
+            </li>
+          <% end %>
+        </ul>
+    </nav>
 
     <div id="page-wrapper">
       <%= yield %>

commit e08c67024acccb83a30d2010c34862973b883585
Author: Tom Clegg <tom at curoverse.com>
Date:   Sat Jun 7 11:36:58 2014 -0400

    2872: Rename folders to projects

diff --git a/apps/workbench/app/assets/javascripts/selection.js b/apps/workbench/app/assets/javascripts/selection.js
index 59f0fd1..84d65cd 100644
--- a/apps/workbench/app/assets/javascripts/selection.js
+++ b/apps/workbench/app/assets/javascripts/selection.js
@@ -58,7 +58,7 @@ jQuery(function($){
         if (lst.length > 0) {
             html = '<li><a href="#" class="btn btn-xs btn-info" id="clear_selections_button"><i class="fa fa-fw fa-ban"></i> Clear selections</a></li>';
             if (this_object_uuid.match('-j7d0g-'))
-                html += '<li><button class="btn btn-xs btn-info" type="submit" name="copy_selections_into_folder" id="copy_selections_into_folder"><i class="fa fa-fw fa-folder-open"></i> Copy selections into this folder</button></li>';
+                html += '<li><button class="btn btn-xs btn-info" type="submit" name="copy_selections_into_project" id="copy_selections_into_project"><i class="fa fa-fw fa-folder-open"></i> Copy selections into this project</button></li>';
             html += '<li><button class="btn btn-xs btn-info" type="submit" name="combine_selected_files_into_collection" '
                 + ' id="combine_selected_files_into_collection">'
                 + '<i class="fa fa-fw fa-archive"></i> Combine selected collections and files into a new collection</button></li>'
diff --git a/apps/workbench/app/assets/stylesheets/folders.css.scss b/apps/workbench/app/assets/stylesheets/projects.css.scss
similarity index 70%
rename from apps/workbench/app/assets/stylesheets/folders.css.scss
rename to apps/workbench/app/assets/stylesheets/projects.css.scss
index aa5f14c..53b352d 100644
--- a/apps/workbench/app/assets/stylesheets/folders.css.scss
+++ b/apps/workbench/app/assets/stylesheets/projects.css.scss
@@ -1,9 +1,9 @@
-.arv-folder-list > .row {
+.arv-project-list > .row {
     padding-top: 5px;
     padding-bottom: 5px;
     padding-right: 1em;
 }
-.arv-folder-list > .row.folder:hover {
+.arv-project-list > .row.project:hover {
     background: #d9edf7;
 }
 div.scroll-20em {
diff --git a/apps/workbench/app/controllers/actions_controller.rb b/apps/workbench/app/controllers/actions_controller.rb
index cbe3405..06775fc 100644
--- a/apps/workbench/app/controllers/actions_controller.rb
+++ b/apps/workbench/app/controllers/actions_controller.rb
@@ -19,7 +19,7 @@ class ActionsController < ApplicationController
     redirect_to :back
   end
 
-  expose_action :copy_selections_into_folder do
+  expose_action :copy_selections_into_project do
     link_selections = Link.filter([['uuid','in',params["selection"]]])
     link_uuids = link_selections.collect(&:uuid)
 
diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb
index 12f9c70..9627ab6 100644
--- a/apps/workbench/app/controllers/application_controller.rb
+++ b/apps/workbench/app/controllers/application_controller.rb
@@ -501,14 +501,14 @@ class ApplicationController < ActionController::Base
     end
   end
 
-  helper_method :my_folders
-  def my_folders
-    return @my_folders if @my_folders
-    @my_folders = []
+  helper_method :my_projects
+  def my_projects
+    return @my_projects if @my_projects
+    @my_projects = []
     root_of = {}
-    Group.filter([['group_class','=','folder']]).each do |g|
+    Group.filter([['group_class','in',['project','folder']]]).each do |g|
       root_of[g.uuid] = g.owner_uuid
-      @my_folders << g
+      @my_projects << g
     end
     done = false
     while not done
@@ -522,16 +522,16 @@ class ApplicationController < ActionController::Base
         end
       end
     end
-    @my_folders = @my_folders.select do |g|
+    @my_projects = @my_projects.select do |g|
       root_of[g.uuid] == current_user.uuid
     end
   end
 
   helper_method :recent_jobs_and_pipelines
   def recent_jobs_and_pipelines
-    in_my_folders = ['owner_uuid','in',my_folders.collect(&:uuid)]
-    (Job.limit(10).filter([in_my_folders]) |
-     PipelineInstance.limit(10).filter([in_my_folders])).
+    in_my_projects = ['owner_uuid','in',my_projects.collect(&:uuid)]
+    (Job.limit(10).filter([in_my_projects]) |
+     PipelineInstance.limit(10).filter([in_my_projects])).
       sort_by do |x|
       x.finished_at || x.started_at || x.created_at rescue x.created_at
     end
diff --git a/apps/workbench/app/controllers/collections_controller.rb b/apps/workbench/app/controllers/collections_controller.rb
index bf57625..0148b72 100644
--- a/apps/workbench/app/controllers/collections_controller.rb
+++ b/apps/workbench/app/controllers/collections_controller.rb
@@ -157,10 +157,10 @@ class CollectionsController < ApplicationController
       end
       @output_of = jobs_with.call(output: @object.uuid)
       @log_of = jobs_with.call(log: @object.uuid)
-      folder_links = Link.limit(RELATION_LIMIT).order("modified_at DESC")
+      project_links = Link.limit(RELATION_LIMIT).order("modified_at DESC")
         .where(head_uuid: @object.uuid, link_class: 'name').results
-      folder_hash = Group.where(uuid: folder_links.map(&:tail_uuid)).to_hash
-      @folders = folder_links.map { |link| folder_hash[link.tail_uuid] }
+      project_hash = Group.where(uuid: project_links.map(&:tail_uuid)).to_hash
+      @projects = project_links.map { |link| project_hash[link.tail_uuid] }
       @permissions = Link.limit(RELATION_LIMIT).order("modified_at DESC")
         .where(head_uuid: @object.uuid, link_class: 'permission',
                name: 'can_read').results
diff --git a/apps/workbench/app/controllers/groups_controller.rb b/apps/workbench/app/controllers/groups_controller.rb
index 854496a..f97bb20 100644
--- a/apps/workbench/app/controllers/groups_controller.rb
+++ b/apps/workbench/app/controllers/groups_controller.rb
@@ -1,13 +1,16 @@
 class GroupsController < ApplicationController
   def index
-    @groups = Group.filter [['group_class', 'not in', ['folder']]]
+    @groups = Group.filter [['group_class', 'not in', ['folder', 'project']]]
     @group_uuids = @groups.collect &:uuid
     @links_from = Link.where link_class: 'permission', tail_uuid: @group_uuids
     @links_to = Link.where link_class: 'permission', head_uuid: @group_uuids
   end
 
   def show
-    return redirect_to(folder_path(@object)) if @object.group_class == 'folder'
-    super
+    if @object.group_class.in?(['project','folder'])
+      redirect_to(project_path(@object))
+    else
+      super
+    end
   end
 end
diff --git a/apps/workbench/app/controllers/folders_controller.rb b/apps/workbench/app/controllers/projects_controller.rb
similarity index 86%
rename from apps/workbench/app/controllers/folders_controller.rb
rename to apps/workbench/app/controllers/projects_controller.rb
index ab7dcec..f73b6c0 100644
--- a/apps/workbench/app/controllers/folders_controller.rb
+++ b/apps/workbench/app/controllers/projects_controller.rb
@@ -1,10 +1,10 @@
-class FoldersController < ApplicationController
+class ProjectsController < ApplicationController
   def model_class
     Group
   end
 
   def index_pane_list
-    %w(Folders)
+    %w(Projects)
   end
 
   def show_pane_list
@@ -14,7 +14,7 @@ class FoldersController < ApplicationController
   def remove_item
     params[:item_uuids] = [params[:item_uuid]]
     remove_items
-    render template: 'folders/remove_items'
+    render template: 'projects/remove_items'
   end
 
   def remove_items
@@ -25,7 +25,7 @@ class FoldersController < ApplicationController
           item.link_class == 'name' and
           item.tail_uuid == @object.uuid)
         # Given uuid is a name link, linking an object to this
-        # folder. First follow the link to find the item we're removing,
+        # project. First follow the link to find the item we're removing,
         # then delete the link.
         links << item
         item = ArvadosBase.find item.head_uuid
@@ -40,7 +40,7 @@ class FoldersController < ApplicationController
         link.destroy
       end
       if item.owner_uuid == @object.uuid
-        # Object is owned by this folder. Remove it from the folder by
+        # Object is owned by this project. Remove it from the project by
         # changing owner to the current user.
         item.update_attributes owner_uuid: current_user.uuid
         @removed_uuids << item.uuid
@@ -49,7 +49,9 @@ class FoldersController < ApplicationController
   end
 
   def find_objects_for_index
-    @objects = Group.where(group_class: 'folder').order('name')
+    @objects = Group.
+      filter([['group_class','in',['project','folder']]]).
+      order('name')
     super
     parent_of = {current_user.uuid => 'me'}
     @objects.each do |ob|
@@ -81,9 +83,9 @@ class FoldersController < ApplicationController
       end
       paths
     end
-    @my_folder_tree =
+    @my_project_tree =
       sorted_paths.call buildtree.call(children_of, 'me')
-    @shared_folder_tree =
+    @shared_project_tree =
       sorted_paths.call({'Shared with me' =>
                           buildtree.call(children_of, false)})
   end
@@ -121,7 +123,7 @@ class FoldersController < ApplicationController
                                       formats: [:html],
                                       locals: {
                                         objects_and_names: @objects_and_names,
-                                        folder: @object
+                                        project: @object
                                       }),
             next_page_href: (next_page_offset and
                              url_for(offset: next_page_offset, partial: true))
@@ -134,13 +136,13 @@ class FoldersController < ApplicationController
   end
 
   def create
-    @new_resource_attrs = (params['folder'] || {}).merge(group_class: 'folder')
-    @new_resource_attrs[:name] ||= 'New folder'
+    @new_resource_attrs = (params['project'] || {}).merge(group_class: 'project')
+    @new_resource_attrs[:name] ||= 'New project'
     super
   end
 
   def update
-    @updates = params['folder']
+    @updates = params['project']
     super
   end
 end
diff --git a/apps/workbench/app/helpers/application_helper.rb b/apps/workbench/app/helpers/application_helper.rb
index fd13bc4..784958b 100644
--- a/apps/workbench/app/helpers/application_helper.rb
+++ b/apps/workbench/app/helpers/application_helper.rb
@@ -136,7 +136,7 @@ module ApplicationHelper
     attrvalue = object.send(attr) if attrvalue.nil?
     if !object.attribute_editable?(attr, :ever) or
         (!object.editable? and
-         !object.owner_uuid.in?(my_folders.collect(&:uuid)))
+         !object.owner_uuid.in?(my_projects.collect(&:uuid)))
       return ((attrvalue && attrvalue.length > 0 && attrvalue) ||
               (attr == 'name' and object.andand.default_name) ||
               '(none)')
@@ -222,7 +222,7 @@ module ApplicationHelper
     if !object or
         !object.attribute_editable?(attr, :ever) or
         (!object.editable? and
-         !object.owner_uuid.in?(my_folders.collect(&:uuid)))
+         !object.owner_uuid.in?(my_projects.collect(&:uuid)))
       return link_to_if_arvados_object attrvalue
     end
 
diff --git a/apps/workbench/app/helpers/folders_helper.rb b/apps/workbench/app/helpers/folders_helper.rb
deleted file mode 100644
index d27e7b4..0000000
--- a/apps/workbench/app/helpers/folders_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module FoldersHelper
-end
diff --git a/apps/workbench/app/helpers/projects_helper.rb b/apps/workbench/app/helpers/projects_helper.rb
new file mode 100644
index 0000000..db5c5ce
--- /dev/null
+++ b/apps/workbench/app/helpers/projects_helper.rb
@@ -0,0 +1,2 @@
+module ProjectsHelper
+end
diff --git a/apps/workbench/app/models/arvados_base.rb b/apps/workbench/app/models/arvados_base.rb
index 590f628..a66b390 100644
--- a/apps/workbench/app/models/arvados_base.rb
+++ b/apps/workbench/app/models/arvados_base.rb
@@ -294,7 +294,7 @@ class ArvadosBase < ActiveRecord::Base
     current_user
   end
 
-  def self.goes_in_folders?
+  def self.goes_in_projects?
     false
   end
 
diff --git a/apps/workbench/app/models/collection.rb b/apps/workbench/app/models/collection.rb
index 2825618..008d108 100644
--- a/apps/workbench/app/models/collection.rb
+++ b/apps/workbench/app/models/collection.rb
@@ -6,7 +6,7 @@ class Collection < ArvadosBase
     !!locator.to_s.match("^#{MD5_EMPTY}(\\+.*)?\$")
   end
 
-  def self.goes_in_folders?
+  def self.goes_in_projects?
     true
   end
 
diff --git a/apps/workbench/app/models/group.rb b/apps/workbench/app/models/group.rb
index 638f6e8..9e627bf 100644
--- a/apps/workbench/app/models/group.rb
+++ b/apps/workbench/app/models/group.rb
@@ -1,5 +1,5 @@
 class Group < ArvadosBase
-  def self.goes_in_folders?
+  def self.goes_in_projects?
     true
   end
 
@@ -13,7 +13,7 @@ class Group < ArvadosBase
   end
 
   def class_for_display
-    group_class == 'folder' ? 'Folder' : super
+    group_class.in?(['folder', 'project']) ? 'Project' : super
   end
 
   def editable?
diff --git a/apps/workbench/app/models/human.rb b/apps/workbench/app/models/human.rb
index 3880f05..7c2d3e4 100644
--- a/apps/workbench/app/models/human.rb
+++ b/apps/workbench/app/models/human.rb
@@ -1,5 +1,5 @@
 class Human < ArvadosBase
-  def self.goes_in_folders?
+  def self.goes_in_projects?
     true
   end
 end
diff --git a/apps/workbench/app/models/job.rb b/apps/workbench/app/models/job.rb
index c853302..2a69c28 100644
--- a/apps/workbench/app/models/job.rb
+++ b/apps/workbench/app/models/job.rb
@@ -1,5 +1,5 @@
 class Job < ArvadosBase
-  def self.goes_in_folders?
+  def self.goes_in_projects?
     true
   end
 
diff --git a/apps/workbench/app/models/pipeline_instance.rb b/apps/workbench/app/models/pipeline_instance.rb
index c9538d1..c3b1475 100644
--- a/apps/workbench/app/models/pipeline_instance.rb
+++ b/apps/workbench/app/models/pipeline_instance.rb
@@ -1,7 +1,7 @@
 class PipelineInstance < ArvadosBase
   attr_accessor :pipeline_template
 
-  def self.goes_in_folders?
+  def self.goes_in_projects?
     true
   end
 
diff --git a/apps/workbench/app/models/pipeline_template.rb b/apps/workbench/app/models/pipeline_template.rb
index 82e3d30..e1af2cb 100644
--- a/apps/workbench/app/models/pipeline_template.rb
+++ b/apps/workbench/app/models/pipeline_template.rb
@@ -1,5 +1,5 @@
 class PipelineTemplate < ArvadosBase
-  def self.goes_in_folders?
+  def self.goes_in_projects?
     true
   end
 
diff --git a/apps/workbench/app/models/specimen.rb b/apps/workbench/app/models/specimen.rb
index 68c0038..7c611e4 100644
--- a/apps/workbench/app/models/specimen.rb
+++ b/apps/workbench/app/models/specimen.rb
@@ -1,5 +1,5 @@
 class Specimen < ArvadosBase
-  def self.goes_in_folders?
+  def self.goes_in_projects?
     true
   end
 end
diff --git a/apps/workbench/app/models/trait.rb b/apps/workbench/app/models/trait.rb
index cf7099d..e7a6ceb 100644
--- a/apps/workbench/app/models/trait.rb
+++ b/apps/workbench/app/models/trait.rb
@@ -1,5 +1,5 @@
 class Trait < ArvadosBase
-  def self.goes_in_folders?
+  def self.goes_in_projects?
     true
   end
 end
diff --git a/apps/workbench/app/views/application/_content.html.erb b/apps/workbench/app/views/application/_content.html.erb
index 0e3fa0b..e634362 100644
--- a/apps/workbench/app/views/application/_content.html.erb
+++ b/apps/workbench/app/views/application/_content.html.erb
@@ -1,10 +1,10 @@
 <% content_for :content_top do %>
-  <% if @object and not @object.is_a?(Group) and @object.class.goes_in_folders? and @object.owner_uuid == current_user.uuid %>
+  <% if @object and not @object.is_a?(Group) and @object.class.goes_in_projects? and @object.owner_uuid == current_user.uuid %>
     <div class="pull-right" style="width: 40%">
       <div class="alert alert-warning alert-dismissable">
         <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
-        <strong>Hey.</strong> This <%= @object.class_for_display.downcase %> belongs to your account, but it's not in any of your folders. If you want it to be easy to find in the future, you should move it to a folder.<br />
-        <%= button_to(choose_folders_path(
+        <strong>Hey.</strong> This <%= @object.class_for_display.downcase %> belongs to your account, but it's not in any of your projects. If you want it to be easy to find in the future, you should move it to a project.<br />
+        <%= button_to(choose_projects_path(
                    title: 'Move to...',
                    editable: true,
                    action_name: 'Move',
@@ -12,7 +12,7 @@
                    action_method: 'patch',
                    action_data: {selection_param: @object.resource_param_name+'[owner_uuid]', success: 'page-refresh'}.to_json),
                   { class: "btn btn-primary btn-sm", remote: true, method: 'get' }) do %>
-          <i class="fa fa-fw fa-folder"></i> Choose a folder...
+          <i class="fa fa-fw fa-folder"></i> Choose a project...
         <% end %>
       </div>
     </div>
diff --git a/apps/workbench/app/views/application/_selection_checkbox.html.erb b/apps/workbench/app/views/application/_selection_checkbox.html.erb
index 9645455..9d279bc 100644
--- a/apps/workbench/app/views/application/_selection_checkbox.html.erb
+++ b/apps/workbench/app/views/application/_selection_checkbox.html.erb
@@ -1,4 +1,4 @@
-<%if object and object.class.goes_in_folders? %>
+<%if object and object.class.goes_in_projects? %>
   <% fn = if defined? friendly_name
             friendly_name
           else
diff --git a/apps/workbench/app/views/application/_show_recent.html.erb b/apps/workbench/app/views/application/_show_recent.html.erb
index 800263f..d94dd84 100644
--- a/apps/workbench/app/views/application/_show_recent.html.erb
+++ b/apps/workbench/app/views/application/_show_recent.html.erb
@@ -15,7 +15,7 @@
 <table class="table table-condensed arv-index">
   <thead>
     <tr>
-      <% if objects.first and objects.first.class.goes_in_folders? %>
+      <% if objects.first and objects.first.class.goes_in_projects? %>
         <th></th>
       <% end %>
       <th></th>
@@ -34,7 +34,7 @@
   <tbody>
     <% objects.each do |object| %>
     <tr data-object-uuid="<%= object.uuid %>">
-      <% if objects.first.class.goes_in_folders? %>
+      <% if objects.first.class.goes_in_projects? %>
         <td>
           <%= render :partial => "selection_checkbox", :locals => {:object => object} %>
         </td>
diff --git a/apps/workbench/app/views/collections/show.html.erb b/apps/workbench/app/views/collections/show.html.erb
index 25cd845..d611dbd 100644
--- a/apps/workbench/app/views/collections/show.html.erb
+++ b/apps/workbench/app/views/collections/show.html.erb
@@ -83,12 +83,12 @@
         </div>
 
 	<div style="height:0.5em;"></div>
-        <% if @folders.andand.any? %>
-          <p>Included in folders:<br />
-          <%= render_arvados_object_list_start(@folders, 'Show all folders',
+        <% if @projects.andand.any? %>
+          <p>Included in projects:<br />
+          <%= render_arvados_object_list_start(@projects, 'Show all projects',
                 links_path(filter: [['head_uuid', '=', @object.uuid],
-                                    ['link_class', '=', 'name']].to_json)) do |folder| %>
-          <%= link_to_if_arvados_object(folder, friendly_name: true) %><br />
+                                    ['link_class', '=', 'name']].to_json)) do |project| %>
+          <%= link_to_if_arvados_object(project, friendly_name: true) %><br />
           <% end %>
           </p>
         <% end %>
diff --git a/apps/workbench/app/views/folders/_index_folders.html.erb b/apps/workbench/app/views/folders/_index_folders.html.erb
deleted file mode 100644
index 734e0f4..0000000
--- a/apps/workbench/app/views/folders/_index_folders.html.erb
+++ /dev/null
@@ -1,31 +0,0 @@
-<div class="container-fluid arv-folder-list">
-  <% tree.each do |foldernode| %>
-    <% rowtype = foldernode[:object].class %>
-    <% next if rowtype != Group and !show_root_node %>
-    <div class="<%= 'folder' if rowtype == Group %> row">
-      <div class="col-md-12" style="padding-left: <%= foldernode[:depth] - (show_root_node ? 0 : 1) %>em;">
-        <% if show_root_node and rowtype == String %>
-          <i class="fa fa-fw fa-folder-open-o"></i>
-          <%= foldernode[:object] %>
-        <% elsif show_root_node and rowtype == User %>
-          <% if foldernode[:object].uuid == current_user.andand.uuid %>
-            <i class="fa fa-fw fa-folder-open-o"></i>
-            My Folders
-          <% else %>
-            <i class="fa fa-fw fa-folder-o"></i>
-            <%= foldernode[:object].friendly_link_name %>
-          <% end %>
-        <% elsif rowtype == Group %>
-          <i class="fa fa-fw fa-folder-o"></i>
-          <% opts = {} %>
-          <% opts[:title] = foldernode[:object].description %>
-          <% opts[:'data-toggle'] = 'tooltip' %>
-          <% opts[:'data-placement'] = 'bottom' %>
-          <%= link_to foldernode[:object], opts do %>
-            <%= foldernode[:object].friendly_link_name %>
-          <% end %>
-        <% end %>
-      </div>
-    </div>
-  <% end %>
-</div>
diff --git a/apps/workbench/app/views/layouts/application.html.erb b/apps/workbench/app/views/layouts/application.html.erb
index 32a0f91..7a7231f 100644
--- a/apps/workbench/app/views/layouts/application.html.erb
+++ b/apps/workbench/app/views/layouts/application.html.erb
@@ -70,7 +70,7 @@
                 <% if @object and \
                       ((@name_link and (o = Group.find?(@name_link.tail_uuid)))\
                        or (o = Group.find?(@object.owner_uuid))) %>
-                  <%= link_to(o.name, folder_path(o.uuid)) %>
+                  <%= link_to(o.name, project_path(o.uuid)) %>
                 <% else %>
                   <%= link_to(
                       controller.controller_name.humanize.downcase,
@@ -82,6 +82,7 @@
                   <span class="glyphicon glyphicon-arrow-right"></span>
                 </li>
                 <li>
+                  
                   <%= link_to_if_arvados_object (@name_link || @object), {friendly_name: true}, {data: {object_uuid: (@name_link.andand.uuid || @object.andand.uuid), name: 'name'}} %>
                 </li>
               <% end %>
diff --git a/apps/workbench/app/views/pipeline_instances/compare.html.erb b/apps/workbench/app/views/pipeline_instances/compare.html.erb
index 953c098..99caf9b 100644
--- a/apps/workbench/app/views/pipeline_instances/compare.html.erb
+++ b/apps/workbench/app/views/pipeline_instances/compare.html.erb
@@ -2,7 +2,7 @@
   <% content_for :breadcrumbs do %>
     <li class="nav-separator"><span class="glyphicon glyphicon-arrow-right"></span></li>
     <li>
-      <%= link_to(o.name, folder_path(o.uuid)) %>
+      <%= link_to(o.name, project_path(o.uuid)) %>
     </li>
     <li class="nav-separator">
       <span class="glyphicon glyphicon-arrow-right"></span>
diff --git a/apps/workbench/app/views/folders/_choose.html.erb b/apps/workbench/app/views/projects/_choose.html.erb
similarity index 57%
rename from apps/workbench/app/views/folders/_choose.html.erb
rename to apps/workbench/app/views/projects/_choose.html.erb
index 2262c07..3481fdd 100644
--- a/apps/workbench/app/views/folders/_choose.html.erb
+++ b/apps/workbench/app/views/projects/_choose.html.erb
@@ -4,25 +4,25 @@
 
       <div class="modal-header">
         <button type="button" class="close" onClick="reset_form()" data-dismiss="modal" aria-hidden="true">×</button>
-        <h4 class="modal-title"><%= params[:title] || 'Choose folder' %></h4>
+        <h4 class="modal-title"><%= params[:title] || 'Choose project' %></h4>
       </div>
 
       <div class="modal-body">
         <div class="selectable-container" style="height: 15em; overflow-y: scroll">
-          <% [@my_folder_tree, @shared_folder_tree].each do |tree| %>
-            <% tree.each do |foldernode| %>
-              <% if foldernode[:object].is_a? String %>
-                <div class="row" style="padding-left: <%= 1 + foldernode[:depth] %>em;">
+          <% [@my_project_tree, @shared_project_tree].each do |tree| %>
+            <% tree.each do |projectnode| %>
+              <% if projectnode[:object].is_a? String %>
+                <div class="row" style="padding-left: <%= 1 + projectnode[:depth] %>em;">
                   <i class="fa fa-fw fa-folder-open-o"></i>
-                  <%= foldernode[:object] %>
+                  <%= projectnode[:object] %>
                 </div>
               <% else %>
-                <div class="<%= 'selectable folder' if !params[:editable] || foldernode[:object].editable? %> row" style="padding-left: <%= 1 + foldernode[:depth] %>em;" data-object-uuid="<%= foldernode[:object].uuid %>">
+                <div class="<%= 'selectable project' if !params[:editable] || projectnode[:object].editable? %> row" style="padding-left: <%= 1 + projectnode[:depth] %>em;" data-object-uuid="<%= projectnode[:object].uuid %>">
                   <i class="fa fa-fw fa-folder-o"></i>
-                  <% if foldernode[:object].uuid == current_user.uuid %>
-                    My Folders
+                  <% if projectnode[:object].uuid == current_user.uuid %>
+                    My Projects
                   <% else %>
-                    <%= foldernode[:object].friendly_link_name || 'New folder' %>
+                    <%= projectnode[:object].friendly_link_name || 'New project' %>
                   <% end %>
                 </div>
               <% end %>
diff --git a/apps/workbench/app/views/folders/_choose.js.erb b/apps/workbench/app/views/projects/_choose.js.erb
similarity index 100%
rename from apps/workbench/app/views/folders/_choose.js.erb
rename to apps/workbench/app/views/projects/_choose.js.erb
diff --git a/apps/workbench/app/views/folders/_index_jobs_and_pipelines.html.erb b/apps/workbench/app/views/projects/_index_jobs_and_pipelines.html.erb
similarity index 100%
rename from apps/workbench/app/views/folders/_index_jobs_and_pipelines.html.erb
rename to apps/workbench/app/views/projects/_index_jobs_and_pipelines.html.erb
diff --git a/apps/workbench/app/views/projects/_index_projects.html.erb b/apps/workbench/app/views/projects/_index_projects.html.erb
new file mode 100644
index 0000000..1e2d768
--- /dev/null
+++ b/apps/workbench/app/views/projects/_index_projects.html.erb
@@ -0,0 +1,31 @@
+<div class="container-fluid arv-project-list">
+  <% tree.each do |projectnode| %>
+    <% rowtype = projectnode[:object].class %>
+    <% next if rowtype != Group and !show_root_node %>
+    <div class="<%= 'project' if rowtype == Group %> row">
+      <div class="col-md-12" style="padding-left: <%= projectnode[:depth] - (show_root_node ? 0 : 1) %>em;">
+        <% if show_root_node and rowtype == String %>
+          <i class="fa fa-fw fa-folder-open-o"></i>
+          <%= projectnode[:object] %>
+        <% elsif show_root_node and rowtype == User %>
+          <% if projectnode[:object].uuid == current_user.andand.uuid %>
+            <i class="fa fa-fw fa-folder-open-o"></i>
+            My Projects
+          <% else %>
+            <i class="fa fa-fw fa-folder-o"></i>
+            <%= projectnode[:object].friendly_link_name %>
+          <% end %>
+        <% elsif rowtype == Group %>
+          <i class="fa fa-fw fa-folder-o"></i>
+          <% opts = {} %>
+          <% opts[:title] = projectnode[:object].description %>
+          <% opts[:'data-toggle'] = 'tooltip' %>
+          <% opts[:'data-placement'] = 'bottom' %>
+          <%= link_to projectnode[:object], opts do %>
+            <%= projectnode[:object].friendly_link_name %>
+          <% end %>
+        <% end %>
+      </div>
+    </div>
+  <% end %>
+</div>
diff --git a/apps/workbench/app/views/folders/_show_contents.html.erb b/apps/workbench/app/views/projects/_show_contents.html.erb
similarity index 84%
rename from apps/workbench/app/views/folders/_show_contents.html.erb
rename to apps/workbench/app/views/projects/_show_contents.html.erb
index 834d070..5132796 100644
--- a/apps/workbench/app/views/folders/_show_contents.html.erb
+++ b/apps/workbench/app/views/projects/_show_contents.html.erb
@@ -1,7 +1,7 @@
 <% content_for :content_top do %>
 
 <h2>
-  <%= render_editable_attribute @object, 'name', nil, { 'data-emptytext' => "New folder" } %>
+  <%= render_editable_attribute @object, 'name', nil, { 'data-emptytext' => "New project" } %>
 </h2>
 
 <div class="arv-description-as-subtitle">
@@ -12,8 +12,8 @@
 
 <% content_for :tab_line_buttons do %>
   <% if @objects_and_names.empty? %>
-    <%= button_to(folder_path(id: @object.uuid, return_to: folders_path), method: 'delete', class: 'btn btn-sm btn-primary', data: {confirm: "Really delete folder '#{@object.name}'?"}) do %>
-      <i class="fa fa-fw fa-trash-o"></i> Delete folder
+    <%= button_to(project_path(id: @object.uuid, return_to: projects_path), method: 'delete', class: 'btn btn-sm btn-primary', data: {confirm: "Really delete project '#{@object.name}'?"}) do %>
+      <i class="fa fa-fw fa-trash-o"></i> Delete project
     <% end %>
   <% end %>
 <% end %>
@@ -45,12 +45,12 @@
       <% if @object.editable? %>
         <%= link_to(
               choose_collections_path(
-                title: 'Add data to folder:',
+                title: 'Add data to project:',
                 multiple: true,
                 action_name: 'Add',
                 action_href: actions_path(id: @object.uuid),
                 action_method: 'post',
-                action_data: {selection_param: 'selection[]', copy_selections_into_folder: @object.uuid, success: 'page-refresh'}.to_json),
+                action_data: {selection_param: 'selection[]', copy_selections_into_project: @object.uuid, success: 'page-refresh'}.to_json),
               { class: "btn btn-primary btn-sm", remote: true, method: 'get', data: {'event-after-select' => 'page-refresh'} }) do %>
           <i class="fa fa-fw fa-plus"></i> Add data
         <% end %>
@@ -69,7 +69,7 @@
     <div class="col-sm-3">
       <form class="form-inline" role="form">
         Show:
-        <select class="form-control form-control-sm filterable-control" data-filterable-attribute="data-kind" data-filterable-target="table.arv-index.arv-folder-contents tbody">
+        <select class="form-control form-control-sm filterable-control" data-filterable-attribute="data-kind" data-filterable-target="table.arv-index.arv-project-contents tbody">
           <option value="">Everything</option>
           <option value="arvados#collection">Data</option>
           <option value="arvados#pipelineInstance arvados#job">Compute jobs</option>
@@ -83,17 +83,17 @@
       </form>
     </div>
     <div class="col-sm-4">
-      <input type="text" class="form-control filterable-control" placeholder="Search folder contents" data-filterable-target="table.arv-index.arv-folder-contents tbody"/>
+      <input type="text" class="form-control filterable-control" placeholder="Search project contents" data-filterable-target="table.arv-index.arv-project-contents tbody"/>
     </div>
   </div>
 
-  <table class="table table-condensed table-fixedlayout arv-index arv-folder-contents" style="overflow-x: hidden">
+  <table class="table table-condensed table-fixedlayout arv-index arv-project-contents" style="overflow-x: hidden">
     <colgroup>
       <col width="40%" />
       <col width="60%" />
     </colgroup>
     <tbody data-infinite-scroller="#Contents-scroll" data-infinite-content-href="<%= url_for(format: :json, partial: :contents_rows, offset: next_page_offset) if next_page_offset %>">
-      <%= render partial: 'show_contents_rows', locals: {folder: @object, objects_and_names: @objects_and_names} %>
+      <%= render partial: 'show_contents_rows', locals: {project: @object, objects_and_names: @objects_and_names} %>
     </tbody>
     <thead>
       <tr>
diff --git a/apps/workbench/app/views/folders/_show_contents_rows.html.erb b/apps/workbench/app/views/projects/_show_contents_rows.html.erb
similarity index 71%
rename from apps/workbench/app/views/folders/_show_contents_rows.html.erb
rename to apps/workbench/app/views/projects/_show_contents_rows.html.erb
index 3c64b53..8b19c00 100644
--- a/apps/workbench/app/views/folders/_show_contents_rows.html.erb
+++ b/apps/workbench/app/views/projects/_show_contents_rows.html.erb
@@ -7,8 +7,8 @@
     <td>
       <%= render partial: 'selection_checkbox', locals: {object: object, friendly_name: ((name_object.name rescue '') || '')} %>
 
-      <% if folder.editable? %>
-        <%= link_to({action: 'remove_item', id: folder.uuid, item_uuid: ((name_link && name_link.uuid) || object.uuid)}, method: :delete, remote: true, data: {confirm: "Remove #{object.class_for_display.downcase} #{name_object.name rescue object.uuid} from this folder?", toggle: 'tooltip', placement: 'top'}, class: 'btn btn-sm btn-default btn-nodecorate', title: 'remove') do %>
+      <% if project.editable? %>
+        <%= link_to({action: 'remove_item', id: project.uuid, item_uuid: ((name_link && name_link.uuid) || object.uuid)}, method: :delete, remote: true, data: {confirm: "Remove #{object.class_for_display.downcase} #{name_object.name rescue object.uuid} from this project?", toggle: 'tooltip', placement: 'top'}, class: 'btn btn-sm btn-default btn-nodecorate', title: 'remove') do %>
           <i class="fa fa-fw fa-ban"></i>
         <% end %>
       <% else %>
diff --git a/apps/workbench/app/views/folders/_show_featured.html.erb b/apps/workbench/app/views/projects/_show_featured.html.erb
similarity index 100%
rename from apps/workbench/app/views/folders/_show_featured.html.erb
rename to apps/workbench/app/views/projects/_show_featured.html.erb
diff --git a/apps/workbench/app/views/folders/_show_permissions.html.erb b/apps/workbench/app/views/projects/_show_permissions.html.erb
similarity index 79%
rename from apps/workbench/app/views/folders/_show_permissions.html.erb
rename to apps/workbench/app/views/projects/_show_permissions.html.erb
index 0eca228..9d93ce0 100644
--- a/apps/workbench/app/views/folders/_show_permissions.html.erb
+++ b/apps/workbench/app/views/projects/_show_permissions.html.erb
@@ -26,7 +26,7 @@
             Inherited permissions
           </div>
           <div class="panel-body">
-            <p>Permissions for this folder are inherited by the owner or parent folder:
+            <p>Permissions for this project are inherited by the owner or parent project:
             </p>
             <p style="margin-left: 4em">
               <% if User == resource_class_for_uuid(@object.owner_uuid) %>
@@ -36,14 +36,14 @@
               <% end %>
               <%= link_to_if_arvados_object @object.owner_uuid, friendly_name: true %>
               <%= button_to('Move to...',
-                  choose_folders_path(
+                  choose_projects_path(
                    title: 'Move to...',
                    editable: true,
                    action_name: 'Move',
-                   action_href: folder_path(@object.uuid),
+                   action_href: project_path(@object.uuid),
                    action_method: 'put',
-                   action_data: {selection_param: 'folder[owner_uuid]', success: 'page-refresh'}.to_json),
-                  { class: "btn btn-default btn-xs arv-move-to-folder", remote: true, method: 'get' }) %>
+                   action_data: {selection_param: 'project[owner_uuid]', success: 'page-refresh'}.to_json),
+                  { class: "btn btn-default btn-xs arv-move-to-project", remote: true, method: 'get' }) %>
             </p>
           </div>
         </div>
diff --git a/apps/workbench/app/views/folders/index.html.erb b/apps/workbench/app/views/projects/index.html.erb
similarity index 80%
rename from apps/workbench/app/views/folders/index.html.erb
rename to apps/workbench/app/views/projects/index.html.erb
index ce5220b..6d79bc4 100644
--- a/apps/workbench/app/views/folders/index.html.erb
+++ b/apps/workbench/app/views/projects/index.html.erb
@@ -6,7 +6,7 @@
 <div>
   <div class="row">
     <div class="col-sm-6">
-      <% if my_folders.empty? %>
+      <% if my_projects.empty? %>
         <div class="panel panel-info">
           <div class="panel-heading">
             <h3 class="panel-title">
@@ -19,7 +19,7 @@
               This site runs Arvados, the open source biomedical analysis platform. <a href="https://arvados.org" target="_blank">Learn more…</a>
             </p>
             <p>
-              <b>To get started,</b> create a folder using the "Add new folder" button below.
+              <b>To get started,</b> create a project using the "Add new project" button below.
             </p>
           </div>
         </div>
@@ -27,17 +27,17 @@
       <div class="panel panel-default">
         <div class="panel-heading">
           <div class="pull-right">
-            <%= button_to folders_path(method: 'post'), class: 'btn btn-xs btn-primary' do %>
+            <%= button_to projects_path(method: 'post'), class: 'btn btn-xs btn-primary' do %>
               <i class="fa fa-fw fa-plus"></i>
-              Add new folder
+              Add new project
             <% end %>
           </div>
           <h3 class="panel-title">
-            My folders
+            My projects
           </h3>
         </div>
         <div class="panel-body scroll-20em">
-          <%= render partial: 'index_folders', locals: {tree: @my_folder_tree, show_root_node: false} %>
+          <%= render partial: 'index_projects', locals: {tree: @my_project_tree, show_root_node: false} %>
         </div>
       </div>
     </div>
@@ -45,11 +45,11 @@
       <div class="panel panel-default">
         <div class="panel-heading">
           <h3 class="panel-title">
-            Shared folders
+            Shared projects
           </h3>
         </div>
         <div class="panel-body scroll-20em">
-          <%= render partial: 'index_folders', locals: {tree: @shared_folder_tree, show_root_node: false} %>
+          <%= render partial: 'index_projects', locals: {tree: @shared_project_tree, show_root_node: false} %>
         </div>
       </div>
     </div>
diff --git a/apps/workbench/app/views/folders/remove_items.js.erb b/apps/workbench/app/views/projects/remove_items.js.erb
similarity index 100%
rename from apps/workbench/app/views/folders/remove_items.js.erb
rename to apps/workbench/app/views/projects/remove_items.js.erb
diff --git a/apps/workbench/config/routes.rb b/apps/workbench/config/routes.rb
index 95de0e0..df7f2e8 100644
--- a/apps/workbench/config/routes.rb
+++ b/apps/workbench/config/routes.rb
@@ -55,7 +55,7 @@ ArvadosWorkbench::Application.routes.draw do
       format: false)
   get '/collections/download/:uuid/:reader_token' => 'collections#show_file_links'
   get '/collections/:uuid/*file' => 'collections#show_file', :format => false
-  resources :folders do
+  resources :projects do
     match 'remove/:item_uuid', on: :member, via: :delete, action: :remove_item
     match 'remove_items', on: :member, via: :delete, action: :remove_items
     get 'choose', on: :collection
@@ -64,7 +64,7 @@ ArvadosWorkbench::Application.routes.draw do
   post 'actions' => 'actions#post'
   get 'websockets' => 'websocket#index'
 
-  root :to => 'folders#index'
+  root :to => 'projects#index'
 
   # Send unroutable requests to an arbitrary controller
   # (ends up at ApplicationController#render_not_found)
diff --git a/apps/workbench/test/functional/collections_controller_test.rb b/apps/workbench/test/functional/collections_controller_test.rb
index 852772a..fb80f88 100644
--- a/apps/workbench/test/functional/collections_controller_test.rb
+++ b/apps/workbench/test/functional/collections_controller_test.rb
@@ -53,11 +53,11 @@ class CollectionsControllerTest < ActionController::TestCase
     assert_equal([['.', 'foo', 3]], assigns(:object).files)
   end
 
-  test "viewing a collection fetches related folders" do
+  test "viewing a collection fetches related projects" do
     show_collection(:foo_file, :active)
-    assert_includes(assigns(:folders).map(&:uuid),
-                    api_fixture('groups')['afolder']['uuid'],
-                    "controller did not find linked folder")
+    assert_includes(assigns(:projects).map(&:uuid),
+                    api_fixture('groups')['aproject']['uuid'],
+                    "controller did not find linked project")
   end
 
   test "viewing a collection fetches related permissions" do
diff --git a/apps/workbench/test/functional/folders_controller_test.rb b/apps/workbench/test/functional/projects_controller_test.rb
similarity index 87%
rename from apps/workbench/test/functional/folders_controller_test.rb
rename to apps/workbench/test/functional/projects_controller_test.rb
index 8459278..b338aa8 100644
--- a/apps/workbench/test/functional/folders_controller_test.rb
+++ b/apps/workbench/test/functional/projects_controller_test.rb
@@ -1,6 +1,6 @@
 require 'test_helper'
 
-class FoldersControllerTest < ActionController::TestCase
+class ProjectsControllerTest < ActionController::TestCase
   test "inactive user is asked to sign user agreements on front page" do
     get :index, {}, session_for(:inactive)
     assert_response :success
diff --git a/apps/workbench/test/integration/collections_test.rb b/apps/workbench/test/integration/collections_test.rb
index 562e2d3..8978f73 100644
--- a/apps/workbench/test/integration/collections_test.rb
+++ b/apps/workbench/test/integration/collections_test.rb
@@ -40,7 +40,7 @@ class CollectionsTest < ActionDispatch::IntegrationTest
 
   test "Collection page renders default name links" do
     uuid = api_fixture('collections')['foo_file']['uuid']
-    coll_name = api_fixture('links')['foo_collection_name_in_afolder']['name']
+    coll_name = api_fixture('links')['foo_collection_name_in_aproject']['name']
     visit page_with_token('active', "/collections/#{uuid}")
     assert(page.has_text?(coll_name), "Collection page did not include name")
     # Now check that the page is otherwise normal, and the collection name
diff --git a/apps/workbench/test/integration/pipeline_instances_test.rb b/apps/workbench/test/integration/pipeline_instances_test.rb
index 9a329f1..f0620cb 100644
--- a/apps/workbench/test/integration/pipeline_instances_test.rb
+++ b/apps/workbench/test/integration/pipeline_instances_test.rb
@@ -23,10 +23,10 @@ class PipelineInstancesTest < ActionDispatch::IntegrationTest
 
     instance_page = current_path
 
-    # put this pipeline instance in "A Folder"
-    find('button', text: 'Choose a folder...').click
+    # put this pipeline instance in "A Project"
+    find('button', text: 'Choose a project...').click
     within('.modal-dialog') do
-      find('.selectable', text: 'A Folder').click
+      find('.selectable', text: 'A Project').click
       find('button', text: 'Move').click
     end
 
@@ -37,9 +37,9 @@ class PipelineInstancesTest < ActionDispatch::IntegrationTest
     end
     find('#persistent-selection-count').click
 
-    # Add this collection to the folder
-    visit '/folders'
-    find('.arv-folder-list a,button', text: 'A Folder').click
+    # Add this collection to the project
+    visit '/projects'
+    find('.arv-project-list a,button', text: 'A Project').click
     find('.btn', text: 'Add data').click
     find('span', text: 'foo_tag').click
     within('.modal-dialog') do
@@ -81,8 +81,8 @@ class PipelineInstancesTest < ActionDispatch::IntegrationTest
     assert_not page.has_text? 'Graph'
   end
 
-  # Create a pipeline instance from within a folder and run
-  test 'Create pipeline inside a folder and run' do
+  # Create a pipeline instance from within a project and run
+  test 'Create pipeline inside a project and run' do
     visit page_with_token('active_trustedclient')
 
     # Go over to the collections page and select something
@@ -92,12 +92,12 @@ class PipelineInstancesTest < ActionDispatch::IntegrationTest
     end
     find('#persistent-selection-count').click
 
-    # Add this collection to the folder using collections menu from top nav
-    visit '/folders'
-    find('.arv-folder-list a,button', text: 'A Folder').click
+    # Add this collection to the project using collections menu from top nav
+    visit '/projects'
+    find('.arv-project-list a,button', text: 'A Project').click
 
     find('#collections-menu').click
-    click_button 'Copy selections into this folder'
+    click_button 'Copy selections into this project'
 
     # create a pipeline instance
     find('.btn', text: 'Run a pipeline').click
diff --git a/apps/workbench/test/integration/folders_test.rb b/apps/workbench/test/integration/projects_test.rb
similarity index 60%
rename from apps/workbench/test/integration/folders_test.rb
rename to apps/workbench/test/integration/projects_test.rb
index c403c2e..73733d2 100644
--- a/apps/workbench/test/integration/folders_test.rb
+++ b/apps/workbench/test/integration/projects_test.rb
@@ -2,17 +2,17 @@ require 'integration_helper'
 require 'selenium-webdriver'
 require 'headless'
 
-class FoldersTest < ActionDispatch::IntegrationTest
+class ProjectsTest < ActionDispatch::IntegrationTest
   setup do
     Capybara.current_driver = Capybara.javascript_driver
   end
 
-  test 'Find a folder and edit its description' do
+  test 'Find a project and edit its description' do
     visit page_with_token 'active', '/'
-    find('.arv-folder-list a,button', text: 'A Folder').
+    find('.arv-project-list a,button', text: 'A Project').
       click
-    within('.container-fluid', text: api_fixture('groups')['afolder']['name']) do
-      find('span', text: api_fixture('groups')['afolder']['name']).click
+    within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
+      find('span', text: api_fixture('groups')['aproject']['name']).click
       within('.arv-description-as-subtitle') do
         find('.fa-pencil').click
         find('.editable-input textarea').set('I just edited this.')
@@ -26,9 +26,9 @@ class FoldersTest < ActionDispatch::IntegrationTest
   end
 
   test 'Add a new name, then edit it, without creating a duplicate' do
-    folder_uuid = api_fixture('groups')['afolder']['uuid']
-    specimen_uuid = api_fixture('specimens')['owned_by_afolder_with_no_name_link']['uuid']
-    visit page_with_token 'active', '/folders/' + folder_uuid
+    project_uuid = api_fixture('groups')['aproject']['uuid']
+    specimen_uuid = api_fixture('specimens')['owned_by_aproject_with_no_name_link']['uuid']
+    visit page_with_token 'active', '/projects/' + project_uuid
     within(".selection-action-container") do
       within (first('tr', text: 'Specimen')) do
         find(".fa-pencil").click
@@ -49,40 +49,40 @@ class FoldersTest < ActionDispatch::IntegrationTest
     end
   end
 
-  test 'Create a folder and move it into a different folder' do
-    visit page_with_token 'active', '/folders'
-    find('.btn', text: "Add new folder").click
+  test 'Create a project and move it into a different project' do
+    visit page_with_token 'active', '/projects'
+    find('.btn', text: "Add new project").click
 
-    # within('.editable', text: 'New folder') do
+    # within('.editable', text: 'New project') do
     within('h2') do
       find('.fa-pencil').click
-      find('.editable-input input').set('Folder 1234')
+      find('.editable-input input').set('Project 1234')
       find('.glyphicon-ok').click
     end
     wait_for_ajax
 
-    visit '/folders'
-    find('.btn', text: "Add new folder").click
+    visit '/projects'
+    find('.btn', text: "Add new project").click
     within('h2') do
       find('.fa-pencil').click
-      find('.editable-input input').set('Folder 5678')
+      find('.editable-input input').set('Project 5678')
       find('.glyphicon-ok').click
     end
     wait_for_ajax
 
     click_link 'Permissions'
     find('input[value="Move to..."]').click
-    find('.selectable', text: 'Folder 1234').click
+    find('.selectable', text: 'Project 1234').click
     find('a,button', text: 'Move').click
     wait_for_ajax
 
     # Wait for the page to refresh and show the new parent in Permissions panel
     click_link 'Permissions'
-    find('.panel', text: 'Folder 1234')
+    find('.panel', text: 'Project 1234')
 
-    assert(find('.panel', text: 'Permissions for this folder are inherited by the owner or parent folder').
-           all('*', text: 'Folder 1234').any?,
-           "Folder 5678 should now be inside folder 1234")
+    assert(find('.panel', text: 'Permissions for this project are inherited by the owner or parent project').
+           all('*', text: 'Project 1234').any?,
+           "Project 5678 should now be inside project 1234")
   end
 
 end
diff --git a/apps/workbench/test/unit/arvados_resource_list_test.rb b/apps/workbench/test/unit/arvados_resource_list_test.rb
index a5681d2..fa4f9c5 100644
--- a/apps/workbench/test/unit/arvados_resource_list_test.rb
+++ b/apps/workbench/test/unit/arvados_resource_list_test.rb
@@ -10,7 +10,7 @@ class ResourceListTest < ActiveSupport::TestCase
 
   test 'links_for on non-empty resource list' do
     use_token :active
-    results = Group.find(api_fixture('groups')['afolder']['uuid']).contents(include_linked: true)
+    results = Group.find(api_fixture('groups')['aproject']['uuid']).contents(include_linked: true)
     assert_equal [], results.links_for(api_fixture('users')['active']['uuid'])
     assert_equal [], results.links_for(api_fixture('jobs')['running_cancelled']['uuid'])
     assert_equal [], results.links_for(api_fixture('jobs')['running']['uuid'], 'bogus-link-class')
@@ -18,25 +18,25 @@ class ResourceListTest < ActiveSupport::TestCase
   end
 
   test 'links_for returns all link classes (simulated results)' do
-    folder_uuid = api_fixture('groups')['afolder']['uuid']
-    specimen_uuid = api_fixture('specimens')['in_afolder']['uuid']
+    project_uuid = api_fixture('groups')['aproject']['uuid']
+    specimen_uuid = api_fixture('specimens')['in_aproject']['uuid']
     api_response = {
       kind: 'arvados#specimenList',
       links: [{kind: 'arvados#link',
                 uuid: 'zzzzz-o0j2j-asdfasdfasdfas0',
-                tail_uuid: folder_uuid,
+                tail_uuid: project_uuid,
                 head_uuid: specimen_uuid,
                 link_class: 'name',
                 name: 'Alice'},
               {kind: 'arvados#link',
                 uuid: 'zzzzz-o0j2j-asdfasdfasdfas1',
-                tail_uuid: folder_uuid,
+                tail_uuid: project_uuid,
                 head_uuid: specimen_uuid,
                 link_class: 'foo',
                 name: 'Bob'},
               {kind: 'arvados#link',
                 uuid: 'zzzzz-o0j2j-asdfasdfasdfas2',
-                tail_uuid: folder_uuid,
+                tail_uuid: project_uuid,
                 head_uuid: specimen_uuid,
                 link_class: nil,
                 name: 'Clydesdale'}],
diff --git a/apps/workbench/test/unit/group_test.rb b/apps/workbench/test/unit/group_test.rb
index 4a7144f..435463a 100644
--- a/apps/workbench/test/unit/group_test.rb
+++ b/apps/workbench/test/unit/group_test.rb
@@ -4,7 +4,7 @@ class GroupTest < ActiveSupport::TestCase
   test "get contents with names" do
     use_token :active
     oi = Group.
-      find(api_fixture('groups')['asubfolder']['uuid']).
+      find(api_fixture('groups')['asubproject']['uuid']).
       contents(include_linked: true)
     assert_operator(0, :<, oi.count,
                     "Expected to find some items belonging to :active user")
@@ -14,14 +14,14 @@ class GroupTest < ActiveSupport::TestCase
                     "Expected to receive name links with contents response")
     oi_uuids = oi.collect { |i| i['uuid'] }
 
-    expect_uuid = api_fixture('specimens')['in_asubfolder']['uuid']
+    expect_uuid = api_fixture('specimens')['in_asubproject']['uuid']
     assert_includes(oi_uuids, expect_uuid,
-                    "Expected '#{expect_uuid}' in asubfolder's contents")
+                    "Expected '#{expect_uuid}' in asubproject's contents")
 
-    expect_uuid = api_fixture('specimens')['in_afolder_linked_from_asubfolder']['uuid']
-    expect_name = api_fixture('links')['specimen_is_in_two_folders']['name']
+    expect_uuid = api_fixture('specimens')['in_aproject_linked_from_asubproject']['uuid']
+    expect_name = api_fixture('links')['specimen_is_in_two_projects']['name']
     assert_includes(oi_uuids, expect_uuid,
-                    "Expected '#{expect_uuid}' in asubfolder's contents")
+                    "Expected '#{expect_uuid}' in asubproject's contents")
     assert_equal(expect_name, oi.name_for(expect_uuid),
                  "Expected name_for '#{expect_uuid}' to be '#{expect_name}'")
   end
diff --git a/apps/workbench/test/unit/helpers/folders_helper_test.rb b/apps/workbench/test/unit/helpers/folders_helper_test.rb
deleted file mode 100644
index 5b4c557..0000000
--- a/apps/workbench/test/unit/helpers/folders_helper_test.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'test_helper'
-
-class FoldersHelperTest < ActionView::TestCase
-end
diff --git a/apps/workbench/test/unit/helpers/projects_helper_test.rb b/apps/workbench/test/unit/helpers/projects_helper_test.rb
new file mode 100644
index 0000000..a591e4e
--- /dev/null
+++ b/apps/workbench/test/unit/helpers/projects_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class ProjectsHelperTest < ActionView::TestCase
+end
diff --git a/services/api/db/migrate/20140607150616_rename_folder_to_project.rb_deferred b/services/api/db/migrate/20140607150616_rename_folder_to_project.rb_deferred
new file mode 100644
index 0000000..2a3c1d3
--- /dev/null
+++ b/services/api/db/migrate/20140607150616_rename_folder_to_project.rb_deferred
@@ -0,0 +1,9 @@
+class RenameFolderToProject < ActiveRecord::Migration
+  def up
+    Group.update_all("group_class = 'project'", "group_class = 'folder'")
+  end
+
+  def down
+    Group.update_all("group_class = 'folder'", "group_class = 'project'")
+  end
+end
diff --git a/services/api/test/fixtures/groups.yml b/services/api/test/fixtures/groups.yml
index 96db93c..e41ca8a 100644
--- a/services/api/test/fixtures/groups.yml
+++ b/services/api/test/fixtures/groups.yml
@@ -44,7 +44,7 @@ testusergroup_admins:
   owner_uuid: zzzzz-tpzed-000000000000000
   name: Administrators of a subset of users
 
-afolder:
+aproject:
   uuid: zzzzz-j7d0g-v955i6s2oi1cbso
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
   created_at: 2014-04-21 15:37:48 -0400
@@ -52,11 +52,11 @@ afolder:
   modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
   modified_at: 2014-04-21 15:37:48 -0400
   updated_at: 2014-04-21 15:37:48 -0400
-  name: A Folder
-  description: Test folder belonging to active user
-  group_class: folder
+  name: A Project
+  description: Test project belonging to active user
+  group_class: project
 
-asubfolder:
+asubproject:
   uuid: zzzzz-j7d0g-axqo7eu9pwvna1x
   owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
   created_at: 2014-04-21 15:37:48 -0400
@@ -64,8 +64,8 @@ asubfolder:
   modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
   modified_at: 2014-04-21 15:37:48 -0400
   updated_at: 2014-04-21 15:37:48 -0400
-  name: A Subfolder
-  description: "Test folder belonging to active user's first test folder"
+  name: A Subproject
+  description: "Test project belonging to active user's first test project"
   group_class: folder
 
 bad_group_has_ownership_cycle_a:
diff --git a/services/api/test/fixtures/links.yml b/services/api/test/fixtures/links.yml
index 1fb1608..397659e 100644
--- a/services/api/test/fixtures/links.yml
+++ b/services/api/test/fixtures/links.yml
@@ -304,7 +304,7 @@ test_timestamps:
   name: test
   properties: {}
 
-specimen_is_in_two_folders:
+specimen_is_in_two_projects:
   uuid: zzzzz-o0j2j-ryhm1bn83ni03sn
   owner_uuid: zzzzz-j7d0g-axqo7eu9pwvna1x
   created_at: 2014-04-21 15:37:48 -0400
@@ -315,10 +315,10 @@ specimen_is_in_two_folders:
   tail_uuid: zzzzz-j7d0g-axqo7eu9pwvna1x
   head_uuid: zzzzz-j58dm-5gid26432uujf79
   link_class: name
-  name: "I'm in a subfolder, too"
+  name: "I'm in a subproject, too"
   properties: {}
 
-template_name_in_afolder:
+template_name_in_aproject:
   uuid: zzzzz-o0j2j-4kpwf3d6rwkeqhl
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
   created_at: 2014-04-29 16:47:26 -0400
@@ -329,10 +329,10 @@ template_name_in_afolder:
   tail_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
   head_uuid: zzzzz-p5p6p-aox0k0ofxrystgw
   link_class: name
-  name: "I'm a template in a folder"
+  name: "I'm a template in a project"
   properties: {}
 
-job_name_in_afolder:
+job_name_in_aproject:
   uuid: zzzzz-o0j2j-1kt6dppqcxbl1yt
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
   created_at: 2014-04-29 16:47:26 -0400
@@ -343,11 +343,11 @@ job_name_in_afolder:
   tail_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
   head_uuid: zzzzz-8i9sb-pshmckwoma9plh7
   link_class: name
-  name: "I'm a job in a folder"
+  name: "I'm a job in a project"
   properties: {}
 
-foo_collection_name_in_afolder:
-  uuid: zzzzz-o0j2j-foofoldername12
+foo_collection_name_in_aproject:
+  uuid: zzzzz-o0j2j-fooprojectname1
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
   created_at: 2014-04-21 15:37:48 -0400
   modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr
@@ -358,7 +358,7 @@ foo_collection_name_in_afolder:
   head_uuid: 1f4b0bc7583c2a7f9102c395f4ffc5e3+45
   link_class: name
   # This should resemble the default name assigned when a
-  # Collection is added to a Folder.
+  # Collection is added to a Project.
   name: "1f4b0bc7583c2a7f9102c395f4ffc5e3+45 added sometime"
   properties: {}
 
diff --git a/services/api/test/fixtures/specimens.yml b/services/api/test/fixtures/specimens.yml
index c48bff7..4528070 100644
--- a/services/api/test/fixtures/specimens.yml
+++ b/services/api/test/fixtures/specimens.yml
@@ -16,25 +16,25 @@ owned_by_spectator:
   created_at: 2014-04-21 15:37:48 -0400
   modified_at: 2014-04-21 15:37:48 -0400
 
-in_afolder:
+in_aproject:
   uuid: zzzzz-j58dm-7r18rnd5nzhg5yk
   owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
   created_at: 2014-04-21 15:37:48 -0400
   modified_at: 2014-04-21 15:37:48 -0400
 
-in_asubfolder:
+in_asubproject:
   uuid: zzzzz-j58dm-c40lddwcqqr1ffs
   owner_uuid: zzzzz-j7d0g-axqo7eu9pwvna1x
   created_at: 2014-04-21 15:37:48 -0400
   modified_at: 2014-04-21 15:37:48 -0400
 
-in_afolder_linked_from_asubfolder:
+in_aproject_linked_from_asubproject:
   uuid: zzzzz-j58dm-5gid26432uujf79
   owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
   created_at: 2014-04-21 15:37:48 -0400
   modified_at: 2014-04-21 15:37:48 -0400
 
-owned_by_afolder_with_no_name_link:
+owned_by_aproject_with_no_name_link:
   uuid: zzzzz-j58dm-ypsjlol9dofwijz
   owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
   created_at: 2014-05-05 04:11:52 -0400
diff --git a/services/api/test/functional/arvados/v1/filters_test.rb b/services/api/test/functional/arvados/v1/filters_test.rb
index a5582e6..2e8e231 100644
--- a/services/api/test/functional/arvados/v1/filters_test.rb
+++ b/services/api/test/functional/arvados/v1/filters_test.rb
@@ -5,12 +5,12 @@ class Arvados::V1::FiltersTest < ActionController::TestCase
     @controller = Arvados::V1::GroupsController.new
     authorize_with :admin
     get :index, {
-      filters: [ ['group_class', 'not in', ['folder']] ],
+      filters: [ ['group_class', 'not in', ['project']] ],
       controller: 'groups',
     }
     assert_response :success
     found = assigns(:objects)
     assert_includes(found.collect(&:group_class), nil,
-                    "'group_class not in ['folder']' filter should pass null")
+                    "'group_class not in ['project']' filter should pass null")
   end
 end
diff --git a/services/api/test/functional/arvados/v1/groups_controller_test.rb b/services/api/test/functional/arvados/v1/groups_controller_test.rb
index f8f9eae..49e9b7d 100644
--- a/services/api/test/functional/arvados/v1/groups_controller_test.rb
+++ b/services/api/test/functional/arvados/v1/groups_controller_test.rb
@@ -14,22 +14,22 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
     assert_response 403
   end
 
-  test "get list of folders" do
+  test "get list of projects" do
     authorize_with :active
-    get :index, filters: [['group_class', '=', 'folder']], format: :json
+    get :index, filters: [['group_class', 'in', ['project', 'folder']]], format: :json
     assert_response :success
     group_uuids = []
     json_response['items'].each do |group|
-      assert_equal 'folder', group['group_class']
+      assert_includes ['folder', 'project'], group['group_class']
       group_uuids << group['uuid']
     end
-    assert_includes group_uuids, groups(:afolder).uuid
-    assert_includes group_uuids, groups(:asubfolder).uuid
+    assert_includes group_uuids, groups(:aproject).uuid
+    assert_includes group_uuids, groups(:asubproject).uuid
     assert_not_includes group_uuids, groups(:system_group).uuid
     assert_not_includes group_uuids, groups(:private).uuid
   end
 
-  test "get list of groups that are not folders" do
+  test "get list of groups that are not projects" do
     authorize_with :active
     get :index, filters: [['group_class', '=', nil]], format: :json
     assert_response :success
@@ -38,8 +38,8 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
       assert_equal nil, group['group_class']
       group_uuids << group['uuid']
     end
-    assert_not_includes group_uuids, groups(:afolder).uuid
-    assert_not_includes group_uuids, groups(:asubfolder).uuid
+    assert_not_includes group_uuids, groups(:aproject).uuid
+    assert_not_includes group_uuids, groups(:asubproject).uuid
     assert_includes group_uuids, groups(:private).uuid
   end
 
@@ -57,7 +57,7 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
   test 'get group-owned objects' do
     authorize_with :active
     get :contents, {
-      id: groups(:afolder).uuid,
+      id: groups(:aproject).uuid,
       format: :json,
       include_linked: true,
     }
@@ -72,7 +72,7 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
   test 'get group-owned objects with limit' do
     authorize_with :active
     get :contents, {
-      id: groups(:afolder).uuid,
+      id: groups(:aproject).uuid,
       limit: 1,
       format: :json,
     }
@@ -84,7 +84,7 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
   test 'get group-owned objects with limit and offset' do
     authorize_with :active
     get :contents, {
-      id: groups(:afolder).uuid,
+      id: groups(:aproject).uuid,
       limit: 1,
       offset: 12345,
       format: :json,
@@ -97,7 +97,7 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
   test 'get group-owned objects with additional filter matching nothing' do
     authorize_with :active
     get :contents, {
-      id: groups(:afolder).uuid,
+      id: groups(:aproject).uuid,
       filters: [['uuid', 'in', ['foo_not_a_uuid','bar_not_a_uuid']]],
       format: :json,
     }
@@ -107,10 +107,10 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
   end
 
   test 'get group-owned objects without include_linked' do
-    unexpected_uuid = specimens(:in_afolder_linked_from_asubfolder).uuid
+    unexpected_uuid = specimens(:in_aproject_linked_from_asubproject).uuid
     authorize_with :active
     get :contents, {
-      id: groups(:asubfolder).uuid,
+      id: groups(:asubproject).uuid,
       format: :json,
     }
     assert_response :success
@@ -119,10 +119,10 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
   end
 
   test 'get group-owned objects with include_linked' do
-    expected_uuid = specimens(:in_afolder_linked_from_asubfolder).uuid
+    expected_uuid = specimens(:in_aproject_linked_from_asubproject).uuid
     authorize_with :active
     get :contents, {
-      id: groups(:asubfolder).uuid,
+      id: groups(:asubproject).uuid,
       include_linked: true,
       format: :json,
     }
@@ -130,7 +130,7 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
     uuids = json_response['items'].collect { |i| i['uuid'] }
     assert_includes uuids, expected_uuid, "Did not get #{expected_uuid}"
 
-    expected_name = links(:specimen_is_in_two_folders).name
+    expected_name = links(:specimen_is_in_two_projects).name
     found_specimen_name = false
     assert(json_response['links'].any?,
            "Expected a non-empty array of links in response")
@@ -158,7 +158,7 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
         # times within a test.
         @json_response = nil
         get :contents, {
-          id: groups(:afolder).uuid,
+          id: groups(:aproject).uuid,
           include_linked: inc_ind,
           limit: limit,
           offset: offset,
@@ -180,7 +180,7 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
           owner_received[item['owner_uuid']] = true
           offset += 1
           if not inc_ind
-            assert_equal groups(:afolder).uuid, item['owner_uuid']
+            assert_equal groups(:aproject).uuid, item['owner_uuid']
           end
         end
         break if offset >= items_available
@@ -197,7 +197,7 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
       test "Raise error on bogus #{arg} parameter #{val.inspect}" do
         authorize_with :active
         get :contents, {
-          :id => groups(:afolder).uuid,
+          :id => groups(:aproject).uuid,
           :format => :json,
           arg => val,
         }
@@ -209,7 +209,7 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
   test 'get writable_by list for owned group' do
     authorize_with :active
     get :show, {
-      id: groups(:afolder).uuid,
+      id: groups(:aproject).uuid,
       format: :json
     }
     assert_response :success
diff --git a/services/api/test/functional/arvados/v1/links_controller_test.rb b/services/api/test/functional/arvados/v1/links_controller_test.rb
index dfce78b..d5b4266 100644
--- a/services/api/test/functional/arvados/v1/links_controller_test.rb
+++ b/services/api/test/functional/arvados/v1/links_controller_test.rb
@@ -271,11 +271,11 @@ class Arvados::V1::LinksControllerTest < ActionController::TestCase
   end
 
   test "refuse duplicate name" do
-    the_name = links(:job_name_in_afolder).name
-    the_folder = links(:job_name_in_afolder).tail_uuid
+    the_name = links(:job_name_in_aproject).name
+    the_project = links(:job_name_in_aproject).tail_uuid
     authorize_with :active
     post :create, link: {
-      tail_uuid: the_folder,
+      tail_uuid: the_project,
       head_uuid: specimens(:owned_by_active_user).uuid,
       link_class: 'name',
       name: the_name,
diff --git a/services/api/test/unit/group_test.rb b/services/api/test/unit/group_test.rb
index 597af62..97977a5 100644
--- a/services/api/test/unit/group_test.rb
+++ b/services/api/test/unit/group_test.rb
@@ -18,7 +18,7 @@ class GroupTest < ActiveSupport::TestCase
     assert_equal false, s.save, "should not save object with #{g.uuid} as owner"
 
     # Use the group as the new owner of an existing object
-    s = specimens(:in_afolder)
+    s = specimens(:in_aproject)
     s.owner_uuid = groups(:bad_group_has_ownership_cycle_b).uuid
     assert s.valid?, "ownership should pass validation"
     assert_equal false, s.save, "should not save object with #{g.uuid} as owner"
diff --git a/services/api/test/unit/link_test.rb b/services/api/test/unit/link_test.rb
index 10f2b5e..56a3804 100644
--- a/services/api/test/unit/link_test.rb
+++ b/services/api/test/unit/link_test.rb
@@ -8,13 +8,13 @@ class LinkTest < ActiveSupport::TestCase
   end
 
   test 'name links with the same tail_uuid must be unique' do
-    a = Link.create!(tail_uuid: groups(:afolder).uuid,
+    a = Link.create!(tail_uuid: groups(:aproject).uuid,
                      head_uuid: specimens(:owned_by_active_user).uuid,
                      link_class: 'name',
                      name: 'foo')
     assert a.valid?, a.errors.to_s
     assert_raises ActiveRecord::RecordNotUnique do
-      b = Link.create!(tail_uuid: groups(:afolder).uuid,
+      b = Link.create!(tail_uuid: groups(:aproject).uuid,
                        head_uuid: specimens(:owned_by_active_user).uuid,
                        link_class: 'name',
                        name: 'foo')
@@ -22,12 +22,12 @@ class LinkTest < ActiveSupport::TestCase
   end
 
   test 'name links with different tail_uuid need not be unique' do
-    a = Link.create!(tail_uuid: groups(:afolder).uuid,
+    a = Link.create!(tail_uuid: groups(:aproject).uuid,
                      head_uuid: specimens(:owned_by_active_user).uuid,
                      link_class: 'name',
                      name: 'foo')
     assert a.valid?, a.errors.to_s
-    b = Link.create!(tail_uuid: groups(:asubfolder).uuid,
+    b = Link.create!(tail_uuid: groups(:asubproject).uuid,
                      head_uuid: specimens(:owned_by_active_user).uuid,
                      link_class: 'name',
                      name: 'foo')
@@ -38,7 +38,7 @@ class LinkTest < ActiveSupport::TestCase
 
   [nil, '', false].each do |name|
     test "name links cannot have name=#{name.inspect}" do
-      a = Link.create(tail_uuid: groups(:afolder).uuid,
+      a = Link.create(tail_uuid: groups(:aproject).uuid,
                       head_uuid: specimens(:owned_by_active_user).uuid,
                       link_class: 'name',
                       name: name)
diff --git a/services/api/test/unit/owner_test.rb b/services/api/test/unit/owner_test.rb
index f159294..c177bc3 100644
--- a/services/api/test/unit/owner_test.rb
+++ b/services/api/test/unit/owner_test.rb
@@ -73,7 +73,7 @@ class OwnerTest < ActiveSupport::TestCase
     end
   end
 
-  ['users(:active)', 'groups(:afolder)'].each do |ofixt|
+  ['users(:active)', 'groups(:aproject)'].each do |ofixt|
     test "delete #{ofixt} that owns other objects" do
       o = eval ofixt
       assert_equal(true, Specimen.where(owner_uuid: o.uuid).any?,

commit a1f5f2c608f87b7bb59a4044cd02184a47494188
Author: Tom Clegg <tom at curoverse.com>
Date:   Sat Jun 7 11:33:50 2014 -0400

    2872: Hide unnecessary scroll bar

diff --git a/apps/workbench/app/assets/stylesheets/application.css.scss b/apps/workbench/app/assets/stylesheets/application.css.scss
index 8f377be..1a821bc 100644
--- a/apps/workbench/app/assets/stylesheets/application.css.scss
+++ b/apps/workbench/app/assets/stylesheets/application.css.scss
@@ -189,7 +189,7 @@ div#wrapper {
   overflow-y: hidden;
 }
 .arv-description-in-table:hover {
-  overflow-y: scroll;
+  overflow-y: auto;
 }
 
 .btn.btn-nodecorate {

commit b2607e91145c2286eb6379abb5d3108eae8f4b30
Author: radhika <radhika at curoverse.com>
Date:   Sat Jun 7 08:52:45 2014 -0400

    2872: updates to the new test that creates and runs pipeline instance from within a folder.

diff --git a/apps/workbench/test/integration/pipeline_instances_test.rb b/apps/workbench/test/integration/pipeline_instances_test.rb
index 449c130..9a329f1 100644
--- a/apps/workbench/test/integration/pipeline_instances_test.rb
+++ b/apps/workbench/test/integration/pipeline_instances_test.rb
@@ -92,14 +92,12 @@ class PipelineInstancesTest < ActionDispatch::IntegrationTest
     end
     find('#persistent-selection-count').click
 
-    # Add this collection to the folder
+    # Add this collection to the folder using collections menu from top nav
     visit '/folders'
     find('.arv-folder-list a,button', text: 'A Folder').click
-    find('.btn', text: 'Add data').click
-    find('span', text: 'foo_tag').click
-    within('.modal-dialog') do
-      find('.btn', text: 'Add').click
-    end
+
+    find('#collections-menu').click
+    click_button 'Copy selections into this folder'
 
     # create a pipeline instance
     find('.btn', text: 'Run a pipeline').click

commit 563f568124876cc8577baec11caf33e73466b975
Author: radhika <radhika at curoverse.com>
Date:   Sat Jun 7 08:41:55 2014 -0400

    2872: added a test that creates a pipeline instance from within a folder and runs it.

diff --git a/apps/workbench/test/integration/pipeline_instances_test.rb b/apps/workbench/test/integration/pipeline_instances_test.rb
index 4c100c2..449c130 100644
--- a/apps/workbench/test/integration/pipeline_instances_test.rb
+++ b/apps/workbench/test/integration/pipeline_instances_test.rb
@@ -23,6 +23,7 @@ class PipelineInstancesTest < ActionDispatch::IntegrationTest
 
     instance_page = current_path
 
+    # put this pipeline instance in "A Folder"
     find('button', text: 'Choose a folder...').click
     within('.modal-dialog') do
       find('.selectable', text: 'A Folder').click
@@ -80,6 +81,59 @@ class PipelineInstancesTest < ActionDispatch::IntegrationTest
     assert_not page.has_text? 'Graph'
   end
 
+  # Create a pipeline instance from within a folder and run
+  test 'Create pipeline inside a folder and run' do
+    visit page_with_token('active_trustedclient')
+
+    # Go over to the collections page and select something
+    visit '/collections'
+    within('tr', text: 'GNU_General_Public_License') do
+      find('input[type=checkbox]').click
+    end
+    find('#persistent-selection-count').click
+
+    # Add this collection to the folder
+    visit '/folders'
+    find('.arv-folder-list a,button', text: 'A Folder').click
+    find('.btn', text: 'Add data').click
+    find('span', text: 'foo_tag').click
+    within('.modal-dialog') do
+      find('.btn', text: 'Add').click
+    end
+
+    # create a pipeline instance
+    find('.btn', text: 'Run a pipeline').click
+    within('.modal-dialog') do
+      assert page.has_text? 'Two Part Pipeline Template'
+      find('.fa-gear').click
+      find('.btn', text: 'Next: choose inputs').click
+    end
+
+    assert find('p', text: 'Provide a value')
+
+    find('div.form-group', text: 'Foo/bar pair').
+      find('.btn', text: 'Choose').
+      click
+
+    within('.modal-dialog') do
+      find('span', text: 'foo_tag').click
+      find('button', text: 'OK').click
+    end
+
+    wait_for_ajax
+
+    # "Run" button present and enabled
+    page.assert_no_selector 'a.disabled,button.disabled', text: 'Run'
+    first('a,button', text: 'Run').click
+
+    # Pipeline is running. We have a "Stop" button instead now.
+    page.assert_no_selector 'a,button', text: 'Run'
+    page.assert_selector 'a,button', text: 'Stop'
+
+    # Since it is test env, no jobs are created to run. So, graph not visible
+    assert_not page.has_text? 'Graph'
+  end
+
   test 'view pipeline with job and see graph' do
     visit page_with_token('active_trustedclient')
 

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list