2961: Don't call #render_index from #index on folders_controller because #index
[arvados.git] / apps / workbench / app / controllers / application_controller.rb
index 6a761f6cb07c29a9b1360f8a2e57c43d761be46a..72eb4df1e8f5f6054ea5f1c8752ab13ce635c6e3 100644 (file)
@@ -1,12 +1,17 @@
 class ApplicationController < ActionController::Base
+  include ArvadosApiClientHelper
+
   respond_to :html, :json, :js
   protect_from_forgery
+
+  ERROR_ACTIONS = [:render_error, :render_not_found]
+
   around_filter :thread_clear
-  around_filter :thread_with_mandatory_api_token, :except => [:render_exception, :render_not_found]
+  around_filter :thread_with_mandatory_api_token, except: ERROR_ACTIONS
   around_filter :thread_with_optional_api_token
-  before_filter :find_object_by_uuid, :except => [:index, :render_exception, :render_not_found]
-  before_filter :check_user_agreements, :except => [:render_exception, :render_not_found]
-  before_filter :check_user_notifications, :except => [:render_exception, :render_not_found]
+  before_filter :check_user_agreements, except: ERROR_ACTIONS
+  before_filter :check_user_notifications, except: ERROR_ACTIONS
+  before_filter :find_object_by_uuid, except: [:index] + ERROR_ACTIONS
   theme :select_theme
 
   begin
@@ -58,25 +63,45 @@ class ApplicationController < ActionController::Base
     self.render_error status: 404
   end
 
+  def render_index
+    respond_to do |f|
+      f.json { render json: @objects }
+      f.html {
+        if params['tab_pane']
+          comparable = self.respond_to? :compare
+          render(partial: 'show_' + params['tab_pane'].downcase,
+                 locals: { comparable: comparable, objects: @objects })
+        else
+          render
+        end
+      }
+      f.js { render }
+    end
+  end
+
   def index
+    @limit ||= 200
     if params[:limit]
-      limit = params[:limit].to_i
-    else
-      limit = 200
+      @limit = params[:limit].to_i
     end
 
+    @offset ||= 0
     if params[:offset]
-      offset = params[:offset].to_i
-    else
-      offset = 0
+      @offset = params[:offset].to_i
     end
 
-    @objects ||= model_class.limit(limit).offset(offset).all
-    respond_to do |f|
-      f.json { render json: @objects }
-      f.html { render }
-      f.js { render }
+    @filters ||= []
+    if params[:filters]
+      filters = params[:filters]
+      if filters.is_a? String
+        filters = Oj.load filters
+      end
+      @filters += filters
     end
+
+    @objects ||= model_class
+    @objects = @objects.filter(@filters).limit(@limit).offset(@offset).all
+    render_index
   end
 
   def show
@@ -84,12 +109,18 @@ class ApplicationController < ActionController::Base
       return render_not_found("object not found")
     end
     respond_to do |f|
-      f.json { render json: @object }
+      f.json { render json: @object.attributes.merge(href: url_for(@object)) }
       f.html {
-        if request.method == 'GET'
-          render
+        if params['tab_pane']
+          comparable = self.respond_to? :compare
+          render(partial: 'show_' + params['tab_pane'].downcase,
+                 locals: { comparable: comparable, objects: @objects })
         else
-          redirect_to params[:return_to] || @object
+          if request.method == 'GET'
+            render
+          else
+            redirect_to params[:return_to] || @object
+          end
         end
       }
       f.js { render }
@@ -107,21 +138,21 @@ class ApplicationController < ActionController::Base
   end
 
   def update
-    updates = params[@object.class.to_s.underscore.singularize.to_sym]
-    updates.keys.each do |attr|
+    @updates ||= params[@object.class.to_s.underscore.singularize.to_sym]
+    @updates.keys.each do |attr|
       if @object.send(attr).is_a? Hash
-        if updates[attr].is_a? String
-          updates[attr] = Oj.load updates[attr]
+        if @updates[attr].is_a? String
+          @updates[attr] = Oj.load @updates[attr]
         end
         if params[:merge] || params["merge_#{attr}".to_sym]
           # Merge provided Hash with current Hash, instead of
           # replacing.
-          updates[attr] = @object.send(attr).with_indifferent_access.
-            deep_merge(updates[attr].with_indifferent_access)
+          @updates[attr] = @object.send(attr).with_indifferent_access.
+            deep_merge(@updates[attr].with_indifferent_access)
         end
       end
     end
-    if @object.update_attributes updates
+    if @object.update_attributes @updates
       show
     else
       self.render_error status: 422
@@ -129,18 +160,20 @@ class ApplicationController < ActionController::Base
   end
 
   def create
-    new_resource_attrs = params[model_class.to_s.underscore.singularize]
-    new_resource_attrs ||= {}
-    new_resource_attrs.reject! { |k,v| k.to_s == 'uuid' }
-    @object ||= model_class.new new_resource_attrs
-    @object.save!
-
-    respond_to do |f|
-      f.json { render json: @object }
-      f.html {
-        redirect_to(params[:return_to] || @object)
-      }
-      f.js { render }
+    @new_resource_attrs ||= params[model_class.to_s.underscore.singularize]
+    @new_resource_attrs ||= {}
+    @new_resource_attrs.reject! { |k,v| k.to_s == 'uuid' }
+    @object ||= model_class.new @new_resource_attrs, params["options"]
+    if @object.save
+      respond_to do |f|
+        f.json { render json: @object.attributes.merge(href: url_for(@object)) }
+        f.html {
+          redirect_to @object
+        }
+        f.js { render }
+      end
+    else
+      self.render_error status: 422
     end
   end
 
@@ -159,8 +192,18 @@ class ApplicationController < ActionController::Base
   end
 
   def current_user
+    return Thread.current[:user] if Thread.current[:user]
+
     if Thread.current[:arvados_api_token]
-      Thread.current[:user] ||= User.current
+      if session[:user]
+        if session[:user][:is_active] != true
+          Thread.current[:user] = User.current
+        else
+          Thread.current[:user] = User.new(session[:user])
+        end
+      else
+        Thread.current[:user] = User.current
+      end
     else
       logger.error "No API token in Thread"
       return nil
@@ -186,13 +229,51 @@ class ApplicationController < ActionController::Base
   end
 
   protected
-    
+
+  def redirect_to_login
+    respond_to do |f|
+      f.html {
+        if request.method == 'GET'
+          redirect_to arvados_api_client.arvados_login_url(return_to: request.url)
+        else
+          flash[:error] = "Either you are not logged in, or your session has timed out. I can't automatically log you in and re-attempt this request."
+          redirect_to :back
+        end
+      }
+      f.json {
+        @errors = ['You do not seem to be logged in. You did not supply an API token with this request, and your session (if any) has timed out.']
+        self.render_error status: 422
+      }
+    end
+    false  # For convenience to return from callbacks
+  end
+
+  def using_specific_api_token(api_token)
+    start_values = {}
+    [:arvados_api_token, :user].each do |key|
+      start_values[key] = Thread.current[key]
+    end
+    Thread.current[:arvados_api_token] = api_token
+    Thread.current[:user] = nil
+    begin
+      yield
+    ensure
+      start_values.each_key { |key| Thread.current[key] = start_values[key] }
+    end
+  end
+
   def find_object_by_uuid
     if params[:id] and params[:id].match /\D/
       params[:uuid] = params.delete :id
     end
-    if params[:uuid].is_a? String
-      @object = model_class.find(params[:uuid])
+    if not model_class
+      @object = nil
+    elsif params[:uuid].is_a? String
+      if params[:uuid].empty?
+        @object = nil
+      else
+        @object = model_class.find(params[:uuid])
+      end
     else
       @object = model_class.where(uuid: params[:uuid]).first
     end
@@ -216,6 +297,16 @@ class ApplicationController < ActionController::Base
         # call to verify its authenticity.
         if verify_api_token
           session[:arvados_api_token] = params[:api_token]
+          u = User.current
+          session[:user] = {
+            uuid: u.uuid,
+            email: u.email,
+            first_name: u.first_name,
+            last_name: u.last_name,
+            is_active: u.is_active,
+            is_admin: u.is_admin,
+            prefs: u.prefs
+          }
           if !request.format.json? and request.method == 'GET'
             # Repeat this request with api_token in the (new) session
             # cookie instead of the query string.  This prevents API
@@ -245,20 +336,7 @@ class ApplicationController < ActionController::Base
       end
       if try_redirect_to_login
         unless login_optional
-          respond_to do |f|
-            f.html {
-              if request.method == 'GET'
-                redirect_to $arvados_api_client.arvados_login_url(return_to: request.url)
-              else
-                flash[:error] = "Either you are not logged in, or your session has timed out. I can't automatically log you in and re-attempt this request."
-                redirect_to :back
-              end
-            }
-            f.json {
-              @errors = ['You do not seem to be logged in. You did not supply an API token with this request, and your session (if any) has timed out.']
-              self.render_error status: 422
-            }
-          end
+          redirect_to_login
         else
           # login is optional for this route so go on to the regular controller
           Thread.current[:arvados_api_token] = nil
@@ -284,7 +362,7 @@ class ApplicationController < ActionController::Base
       yield
     else
       # We skipped thread_with_mandatory_api_token. Use the optional version.
-      thread_with_api_token(true) do 
+      thread_with_api_token(true) do
         yield
       end
     end
@@ -337,7 +415,7 @@ class ApplicationController < ActionController::Base
   @@notification_tests = []
 
   @@notification_tests.push lambda { |controller, current_user|
-    AuthorizedKey.limit(1).where(authorized_user_uuid: current_user.uuid).each do   
+    AuthorizedKey.limit(1).where(authorized_user_uuid: current_user.uuid).each do
       return nil
     end
     return lambda { |view|
@@ -373,11 +451,13 @@ class ApplicationController < ActionController::Base
   }
 
   def check_user_notifications
+    return if params['tab_pane']
+
     @notification_count = 0
     @notifications = []
 
     if current_user
-      @showallalerts = false      
+      @showallalerts = false
       @@notification_tests.each do |t|
         a = t.call(self, current_user)
         if a
@@ -391,4 +471,30 @@ class ApplicationController < ActionController::Base
       @notification_count = ''
     end
   end
+
+  helper_method :my_folders
+  def my_folders
+    return @my_folders if @my_folders
+    @my_folders = []
+    root_of = {}
+    Group.filter([['group_class','=','folder']]).each do |g|
+      root_of[g.uuid] = g.owner_uuid
+      @my_folders << g
+    end
+    done = false
+    while not done
+      done = true
+      root_of = root_of.each_with_object({}) do |(child, parent), h|
+        if root_of[parent]
+          h[child] = root_of[parent]
+          done = false
+        else
+          h[child] = parent
+        end
+      end
+    end
+    @my_folders = @my_folders.select do |g|
+      root_of[g.uuid] == current_user.uuid
+    end
+  end
 end