X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/db1e389b74e37ecf4915168acdc72ea9ef624d9c..a1f5f2c608f87b7bb59a4044cd02184a47494188:/apps/workbench/app/controllers/application_controller.rb diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb index 0ee26f5c59..12f9c70c1a 100644 --- a/apps/workbench/app/controllers/application_controller.rb +++ b/apps/workbench/app/controllers/application_controller.rb @@ -1,19 +1,18 @@ class ApplicationController < ActionController::Base - ERROR_ACTIONS = [:render_exception, :render_not_found] + include ArvadosApiClientHelper + include ApplicationHelper + 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: [:index, :show] + ERROR_ACTIONS) + around_filter :thread_with_mandatory_api_token, except: ERROR_ACTIONS around_filter :thread_with_optional_api_token before_filter :check_user_agreements, except: ERROR_ACTIONS before_filter :check_user_notifications, except: ERROR_ACTIONS - around_filter :using_reader_tokens, only: [:index, :show] before_filter :find_object_by_uuid, except: [:index] + ERROR_ACTIONS - before_filter :check_my_folders, :except => ERROR_ACTIONS theme :select_theme begin @@ -65,29 +64,46 @@ class ApplicationController < ActionController::Base self.render_error status: 404 end - def index + def find_objects_for_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 + @filters ||= [] if params[:filters] filters = params[:filters] if filters.is_a? String filters = Oj.load filters end - else - filters = [] + @filters += filters + end + + @objects ||= model_class + @objects = @objects.filter(@filters).limit(@limit).offset(@offset) + end + + helper_method :next_page_offset + def next_page_offset + if @objects.respond_to?(:result_offset) and + @objects.respond_to?(:result_limit) and + @objects.respond_to?(:items_available) + next_offset = @objects.result_offset + @objects.result_limit + if next_offset < @objects.items_available + next_offset + else + nil + end end + end - @objects ||= model_class.filter(filters).limit(limit).offset(offset).all + def index + find_objects_for_index if !@objects respond_to do |f| f.json { render json: @objects } f.html { render } @@ -102,7 +118,7 @@ class ApplicationController < ActionController::Base respond_to do |f| f.json { render json: @object.attributes.merge(href: url_for(@object)) } f.html { - if request.method == 'GET' + if request.method.in? ['GET', 'HEAD'] render else redirect_to params[:return_to] || @object @@ -112,6 +128,28 @@ class ApplicationController < ActionController::Base end end + def choose + params[:limit] ||= 20 + find_objects_for_index if !@objects + respond_to do |f| + if params[:partial] + f.json { + render json: { + content: render_to_string(partial: "choose_rows.html", + formats: [:html], + locals: { + multiple: params[:multiple] + }), + next_page_href: @next_page_href + } + } + end + f.js { + render partial: 'choose', locals: {multiple: params[:multiple]} + } + end + end + def render_content if !@object return render_not_found("object not found") @@ -123,21 +161,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.resource_param_name.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 @@ -153,6 +191,24 @@ class ApplicationController < ActionController::Base show end + # Clone the given object, merging any attribute values supplied as + # with a create action. + def copy + @new_resource_attrs ||= params[model_class.to_s.underscore.singularize] + @new_resource_attrs ||= {} + @object = @object.dup + @object.update_attributes @new_resource_attrs + if not @new_resource_attrs[:name] and @object.respond_to? :name + if @object.name and @object.name != '' + @object.name = "Copy of #{@object.name}" + else + @object.name = "Copy of unnamed #{@object.class_for_display.downcase}" + end + end + @object.save! + show + end + def destroy if @object.destroy respond_to do |f| @@ -191,7 +247,7 @@ class ApplicationController < ActionController::Base end def show_pane_list - %w(Attributes Metadata JSON API) + %w(Attributes Advanced) end protected @@ -199,8 +255,8 @@ class ApplicationController < ActionController::Base 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) + if request.method.in? ['GET', 'HEAD'] + 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 @@ -214,23 +270,6 @@ class ApplicationController < ActionController::Base false # For convenience to return from callbacks end - def using_reader_tokens(login_optional=false) - if params[:reader_tokens].is_a?(Array) and params[:reader_tokens].any? - Thread.current[:reader_tokens] = params[:reader_tokens] - end - begin - yield - rescue ArvadosApiClient::NotLoggedInException - if login_optional - raise - else - return redirect_to_login - end - ensure - Thread.current[:reader_tokens] = nil - end - end - def using_specific_api_token(api_token) start_values = {} [:arvados_api_token, :user].each do |key| @@ -255,7 +294,13 @@ class ApplicationController < ActionController::Base if params[:uuid].empty? @object = nil else - @object = model_class.find(params[:uuid]) + if (model_class != Link and + resource_class_for_uuid(params[:uuid]) == Link) + @name_link = Link.find(params[:uuid]) + @object = model_class.find(@name_link.head_uuid) + else + @object = model_class.find(params[:uuid]) + end end else @object = model_class.where(uuid: params[:uuid]).first @@ -280,7 +325,7 @@ class ApplicationController < ActionController::Base # call to verify its authenticity. if verify_api_token session[:arvados_api_token] = params[:api_token] - if !request.format.json? and request.method == 'GET' + if !request.format.json? and request.method.in? ['GET', 'HEAD'] # Repeat this request with api_token in the (new) session # cookie instead of the query string. This prevents API # tokens from appearing in (and being inadvisedly copied @@ -323,8 +368,18 @@ class ApplicationController < ActionController::Base end def thread_with_mandatory_api_token - thread_with_api_token do - yield + thread_with_api_token(true) do + if Thread.current[:arvados_api_token] + yield + elsif session[:arvados_api_token] + # Expired session. Clear it before refreshing login so that, + # if this login procedure fails, we end up showing the "please + # log in" page instead of getting stuck in a redirect loop. + session.delete :arvados_api_token + redirect_to_login + else + render 'users/welcome' + end end end @@ -358,7 +413,10 @@ class ApplicationController < ActionController::Base end def check_user_agreements - if current_user && !current_user.is_active && current_user.is_invited + if current_user && !current_user.is_active + if not current_user.is_invited + return render 'users/inactive' + end signatures = UserAgreement.signatures @signed_ua_uuids = UserAgreement.signatures.map &:head_uuid @required_user_agreements = UserAgreement.all.map do |ua| @@ -423,15 +481,6 @@ class ApplicationController < ActionController::Base } } - def check_my_folders - @my_top_level_folders = lambda do - @top_level_folders ||= Group. - filter([['group_class','=','folder'], - ['owner_uuid','=',current_user.uuid]]). - sort_by { |x| x.name || '' } - end - end - def check_user_notifications @notification_count = 0 @notifications = [] @@ -451,4 +500,51 @@ 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 + + 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])). + sort_by do |x| + x.finished_at || x.started_at || x.created_at rescue x.created_at + end + end + + helper_method :get_object + def get_object uuid + if @get_object.nil? and @objects + @get_object = @objects.each_with_object({}) do |object, h| + h[object.uuid] = object + end + end + @get_object ||= {} + @get_object[uuid] + end end