app/controllers/application_controller.rb
# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.
require 'authenticated_system'
class ApplicationController < ActionController::Base
include Seek::Errors::ControllerErrorHandling
include Seek::EnabledFeaturesFilter
include Recaptcha::Verify
self.mod_porter_secret = PORTER_SECRET
include CommonSweepers
before_filter :log_extra_exception_data
after_filter :log_event
include AuthenticatedSystem
around_filter :with_current_user
#rescue_from "ActionController::RoutingError", :with=>:render_routing_error
before_filter :profile_for_login_required
before_filter :project_membership_required,:only=>[:create,:new]
before_filter :restrict_guest_user, :only => [:new, :edit, :batch_publishing_preview]
helper :all
layout Seek::Config.main_layout
def with_current_user
User.with_current_user current_user do
yield
end
end
def strip_root_for_xml_requests
#intended to use as a before filter on requests that lack a single root model.
#XML requests are required to have a single root node. This assumes the root node
#will be named xml. Turns a params hash like.. {:xml => {:param_one => "val", :param_two => "val2"}}
# into {:param_one => "val", :param_two => "val2"}
#This should probably be used with prepend_before_filter, since some filters might need this to happen so they can check params.
#see sessions controller for an example usage
params[:xml].each {|k,v| params[k] = v} if request.format.xml? and params[:xml]
end
def set_no_layout
self.class.layout nil
end
def base_host
request.host_with_port
end
def application_root
return "http://#{base_host}"
end
helper_method :application_root
#Overridden from restful_authentication
#Does a second check that there is a profile assigned to the user, and if not goes to the profile
#selection page (GET people/select)
def authorized?
if super
redirect_to(select_people_path) if current_user.person.nil?
true
else
false
end
end
def is_user_activated
if Seek::Config.activation_required_enabled && current_user && !current_user.active?
error("Activation of this account it required for gaining full access", "Activation required?")
false
end
end
def is_current_user_auth
begin
@user = User.where(["id = ?", current_user.try(:id)]).find(params[:id])
rescue ActiveRecord::RecordNotFound
error("User not found (id not authorized)", "is invalid (not owner)")
return false
end
unless @user
error("User not found (or not authorized)", "is invalid (not owner)")
return false
end
end
def is_user_admin_auth
unless User.admin_logged_in?
error("Admin rights required", "is invalid (not admin)")
return false
end
return true
end
def is_admin_or_is_project_manager
unless User.admin_logged_in? || User.project_manager_logged_in?
error("You do not have the permission", "Not admin or #{t('project')} manager")
return false
end
end
def can_manage_announcements?
User.admin_logged_in?
end
def logout_user
current_user.forget_me if logged_in?
cookies.delete :auth_token
cookies.delete :open_id
reset_session
end
#MERGENOTE - put back for now, but needs modularizing, refactoring, and possibly replacing
def resource_in_tab
resource_type = params[:resource_type]
view_type = params[:view_type]
scale_title = params[:scale_title] || ''
if params[:actions_partial_disable] == "true"
actions_partial_disable = true
else
actions_partial_disable = false
end
#params[:resource_ids] is passed as string, e.g. "id1, id2, ..."
resource_ids = (params[:resource_ids] || '').split(',')
clazz = resource_type.constantize
resources = clazz.find_all_by_id(resource_ids)
if clazz.respond_to?(:authorized_partial_asset_collection)
authorized_resources = clazz.authorized_partial_asset_collection(resources,"view")
elsif resource_type == 'Project' || resource_type == 'Institution'
authorized_resources = resources
elsif resource_type == "Person" && Seek::Config.is_virtualliver && User.current_user.nil?
authorized_resources = []
else
authorized_resources = resources.select &:can_view?
end
render :update do |page|
page.replace_html "#{scale_title}_#{resource_type}_#{view_type}",
:partial => "assets/resource_in_tab",
:locals => {:resources => resources,
:scale_title => scale_title,
:authorized_resources => authorized_resources,
:view_type => view_type,
:actions_partial_disable => actions_partial_disable}
end
end
private
def restrict_guest_user
if current_user && current_user.guest?
flash[:error] = "You cannot perform this action as a Guest User. Please sign in or register for an account first."
if !request.env["HTTP_REFERER"].nil?
redirect_to :back
else
redirect_to main_app.root_path
end
end
end
private
def project_membership_required
unless User.logged_in_and_member? || User.admin_logged_in?
flash[:error] = "Only members of known projects, institutions or work groups are allowed to create new content."
respond_to do |format|
format.html do
object = eval("@"+controller_name.singularize)
if !object.nil? && object.try(:can_view?)
redirect_to object
else
path = nil
begin
path = eval("main_app.#{controller_name}_path")
rescue Exception=>e
logger.error("No path found for controller - #{controller_name}",e)
path = main_app.root_path
end
redirect_to path
end
end
format.json { render :json => {:status => 401, :error_message => flash[:error]} }
end
end
end
alias_method :project_membership_required_appended, :project_membership_required
#used to suppress elements that are for virtualliver only or are still currently being worked on
def virtualliver_only
if !Seek::Config.is_virtualliver
error("This feature is is not yet currently available","invalid route")
return false
end
end
def check_allowed_to_manage_types
unless Seek::Config.type_managers_enabled
error("Type management disabled", "...")
return false
end
if User.current_user
if User.current_user.can_manage_types?
return true
else
error("Admin rights required to manage types", "...")
return false
end
else
error("You need to login first.", "...")
return false
end
end
def currently_logged_in
current_user.person
end
def error(notice, message)
flash[:error] = notice
(err = User.new.errors).add(:id, message)
respond_to do |format|
format.html { redirect_to root_url }
end
end
#required for the Savage Beast
def admin?
User.admin_logged_in?
end
def email_enabled?
Seek::Config.email_enabled
end
def profile_for_login_required
if User.current_user
if User.current_user.person.nil?
flash[:notice]="You have successfully registered your account, but now must select a profile, or create your own."
redirect_to main_app.select_people_path
end
end
end
def translate_action action_name
case action_name
when 'show', 'index', 'view', 'search', 'favourite', 'favourite_delete',
'comment', 'comment_delete', 'comments', 'comments_timeline', 'rate',
'tag', 'items', 'statistics', 'tag_suggestions', 'preview','runs','new_object_based_on_existing_one'
'view'
when 'download', 'named_download', 'launch', 'submit_job', 'data', 'execute','plot', 'explore','visualise' ,
'export_as_xgmml', 'download_log', 'download_results', 'input', 'output', 'download_output', 'download_input',
'view_result','compare_versions'
'download'
when 'edit', 'new', 'create', 'update', 'new_version', 'create_version',
'destroy_version', 'edit_version', 'update_version', 'new_item',
'create_item', 'edit_item', 'update_item', 'quick_add', 'resolve_link', 'describe_ports'
'edit'
when 'destroy', 'destroy_item', 'cancel'
'delete'
when 'manage', 'notification', 'read_interaction', 'write_interaction'
'manage'
else
nil
end
end
#handles finding an asset, and responding when it cannot be found. If it can be found the item instance is set (e.g. @project for projects_controller)
def find_requested_item
name = self.controller_name.singularize
object = name.camelize.constantize.find_by_id(params[:id])
if (object.nil?)
respond_to do |format|
flash[:error] = "The #{name.humanize} does not exist!"
format.rdf { render :text=>"Not found",:status => :not_found }
format.xml { render :text=>"<error>404 Not found</error>",:status => :not_found }
format.json { render :text=>"Not found", :status => :not_found }
format.html { redirect_to eval "#{self.controller_name}_path" }
end
else
eval "@#{name} = object"
end
end
#handles finding and authorizing an asset for all controllers that require authorization, and handling if the item cannot be found
def find_and_authorize_requested_item
begin
name = self.controller_name.singularize
action = translate_action(action_name)
return if action.nil?
object = name.camelize.constantize.find(params[:id])
if is_auth?(object, action)
eval "@#{name} = object"
params.delete :sharing unless object.can_manage?(current_user)
else
respond_to do |format|
#remember the location to return to if somebody immediately logs in next
store_return_to_location
if User.current_user.nil?
flash[:error] = "You are not authorized to #{action} this #{name.humanize}, you may need to login first."
else
flash[:error] = "You are not authorized to #{action} this #{name.humanize}."
end
format.html do
case action
when 'publish' then redirect_to object
when 'manage' then redirect_to object
when 'edit' then redirect_to object
when 'download' then redirect_to object
when 'delete' then redirect_to object
else redirect_to eval "#{self.controller_name}_path"
end
end
format.rdf { render :text => "You may not #{action} #{name}:#{params[:id]}", :status => :forbidden }
format.xml { render :text => "You may not #{action} #{name}:#{params[:id]}", :status => :forbidden }
format.json { render :text => "You may not #{action} #{name}:#{params[:id]}", :status => :forbidden }
end
return false
end
rescue ActiveRecord::RecordNotFound
respond_to do |format|
if eval("@#{name}").nil?
flash[:error] = "The #{name.humanize.downcase} does not exist!"
else
flash[:error] = "You are not authorized to view #{name.humanize}"
end
format.rdf { render :text=>"Not found",:status => :not_found }
format.xml { render :text=>"<error>404 Not found</error>",:status => :not_found }
format.json { render :text=>"Not found", :status => :not_found }
format.html { redirect_to eval "#{self.controller_name}_path" }
end
return false
end
end
def is_auth? object, action
if object.can_perform? action
true
elsif params[:code] && (action == 'view' || action == 'download')
object.auth_by_code? params[:code]
else
false
end
end
def log_event
User.with_current_user current_user do
c = self.controller_name.downcase
a = self.action_name.downcase
object = eval("@"+c.singularize)
object=current_user if c=="sessions" #logging in and out is a special case
#don't log if the object is not valid or has not been saved, as this will a validation error on update or create
return if object.nil? || (object.respond_to?("new_record?") && object.new_record?) || (object.respond_to?("errors") && !object.errors.empty?)
case c
when "sessions"
if ["create", "destroy"].include?(a)
ActivityLog.create(:action => a,
:culprit => current_user,
:controller_name => c,
:activity_loggable => object,
:user_agent => request.env["HTTP_USER_AGENT"])
end
when "investigations", "studies", "assays", "specimens", "samples"
if ["show", "create", "update", "destroy"].include?(a)
check_log_exists(a, c, object)
ActivityLog.create(:action => a,
:culprit => current_user,
:referenced => object.projects.first,
:controller_name => c,
:activity_loggable => object,
:data => object.title,
:user_agent => request.env["HTTP_USER_AGENT"])
end
when "data_files", "models", "sops", "publications", "presentations", "events"
a = "create" if a == "upload_for_tool"
a = "update" if a == "new_version"
a = "inline_view" if a == "explore"
if ["show", "create", "update", "destroy", "download", "inline_view"].include?(a)
check_log_exists(a, c, object)
ActivityLog.create(:action => a,
:culprit => current_user,
:referenced => object.projects.first,
:controller_name => c,
:activity_loggable => object,
:data => object.title,
:user_agent => request.env["HTTP_USER_AGENT"])
end
when "people"
if ["show", "create", "update", "destroy"].include?(a)
ActivityLog.create(:action => a,
:culprit => current_user,
:controller_name => c,
:activity_loggable => object,
:data => object.title,
:user_agent => request.env["HTTP_USER_AGENT"])
end
when "search"
if a=="index"
ActivityLog.create(:action => "index",
:culprit => current_user,
:controller_name => c,
:user_agent => request.env["HTTP_USER_AGENT"],
:data => {:search_query => object, :result_count => @results.count})
end
when "content_blobs"
a = "inline_view" if a=="view_pdf_content"
if a=="inline_view" || (a=="download" && params['intent'].to_s != 'inline_view')
activity_loggable = object.asset
ActivityLog.create(:action => a,
:culprit => current_user,
:referenced => object,
:controller_name => c,
:activity_loggable => activity_loggable,
:user_agent => request.env["HTTP_USER_AGENT"],
:data => activity_loggable.title)
end
end
expire_activity_fragment_cache(c, a)
end
end
def expire_activity_fragment_cache(controller,action)
if action!="show"
@@auth_types ||= Seek::Util.authorized_types.collect{|t| t.name.underscore.pluralize}
if action=="download"
expire_download_activity
elsif action=="create" && controller!="sessions"
expire_create_activity
elsif action=="destroy"
expire_create_activity
expire_download_activity
elsif action=="update" && @@auth_types.include?(controller) #may have had is permission changed
expire_create_activity
expire_download_activity
expire_resource_list_item_action_partial
end
end
end
def check_log_exists action,controllername,object
if action=="create"
a=ActivityLog.where(
:activity_loggable_type=>object.class.name,
:activity_loggable_id=>object.id,
:controller_name=>controllername,
:action=>"create").first
logger.error("ERROR: Duplicate create activity log about to be created for #{object.class.name}:#{object.id}") unless a.nil?
end
end
def permitted_filters
#placed this in a separate method so that other controllers could override it if necessary
Seek::Util.persistent_classes.select {|c| c.respond_to? :find_by_id}.map {|c| c.name.underscore}
end
def apply_filters(resources)
filters = params[:filter] || {}
#translate params that are send as an _id, like project_id=12 - which will usually be a consequence of nested routing
params.keys.each do |key|
if (key.end_with?("_id"))
filters[key.gsub("_id", "")]=params[key]
end
end
if filters.size>0
params[:page]||="all"
params[:filtered]=true
end
#apply_filters will be dispatching to methods based on the symbols in params[:filter].
#Permitted filters protects us from shennanigans like params[:filter] => {:destroy => 'This will destroy your data'}
filters.delete_if { |k, v| not (permitted_filters.include? k.to_s) }
resources.select do |res|
filters.all? do |filter, value|
filter = filter.to_s
klass = filter.camelize.constantize
value = klass.find_by_id value.to_i
detect_for_filter(filter, res, value)
end
end
end
def detect_for_filter(filter, resource, value)
case
#first the special cases
when filter == 'investigation' && resource.respond_to?(:assays)
resource.assays.collect { |a| a.study.investigation_id }.include? value.id
when filter == 'study' && resource.respond_to?(:assays)
resource.assays.collect { |a| a.study_id }.include? value.id
when (filter == 'project' && resource.respond_to?(:projects_and_ancestors))
resource.projects_and_ancestors.include? value
when filter == 'person' && resource.class.is_asset?
(resource.creators.include?(value) || resource.contributor== value || resource.contributor.try(:person) == value)
when filter == 'person' && (resource.respond_to?(:contributor) || resource.respond_to?(:creators) || resource.respond_to?(:owner))
people = [resource.contributor, resource.contributor.try(:person)]
people = people | resource.creators if resource.respond_to?(:creators)
people << resource.owner if resource.respond_to?(:owner)
people.compact!
people.include?(value)
#then the general case
when resource.respond_to?("all_related_#{filter.pluralize}")
resource.send("all_related_#{filter.pluralize}").include?(value)
when resource.respond_to?("related_#{filter.pluralize}")
resource.send("related_#{filter.pluralize}").include?(value)
when resource.respond_to?(filter)
resource.send(filter) == value
when resource.respond_to?(filter.pluralize)
resource.send(filter.pluralize).include? value
#defaults to false, if a filter is not recognised then nothing is return
else
false
end
end
def log_extra_exception_data
request.env["exception_notifier.exception_data"] = {
:current_logged_in_user=>current_user
}
end
end