SysMO-DB/seek

View on GitHub
app/controllers/projects_controller.rb

Summary

Maintainability
C
1 day
Test Coverage
require 'seek/custom_exception'

class ProjectsController < ApplicationController
  include WhiteListHelper
  include IndexPager
  include CommonSweepers

  before_filter :find_requested_item, :only=>[:show,:admin, :edit,:update, :destroy,:asset_report,:admin_members,:update_members]
  before_filter :find_assets, :only=>[:index]
  before_filter :is_user_admin_auth, :except=>[:index, :show, :edit, :update, :request_institutions, :admin, :asset_report,:admin_members,:update_members,:resource_in_tab]
  before_filter :editable_by_user, :only=>[:edit,:update]
  before_filter :administerable_by_user, :only =>[:admin,:admin_members,:update_members]
  before_filter :auth_params,:only=>[:update]
  before_filter :member_of_this_project, :only=>[:asset_report],:unless=>:admin?

  skip_before_filter :project_membership_required

  cache_sweeper :projects_sweeper,:only=>[:update,:create,:destroy]
  include Seek::BreadCrumbs

  respond_to :html

  def auto_complete_for_organism_name
    render :json => Project.organism_counts.map(&:name).to_json
  end  

  def asset_report
    @no_sidebar=true
    project_assets = @project.assets | @project.assays | @project.studies | @project.investigations
    @types=[DataFile,Model,Sop,Presentation,Investigation,Study,Assay]
    @public_assets = {}
    @semi_public_assets = {}
    @restricted_assets = {}
    @types.each do |type|
      action = type.is_isa? ? "view" : "download"
      @public_assets[type] = type.all_authorized_for action, nil, @project
      #to reduce the initial list - will start with all assets that can be seen by the first user fouund to be in a project
      user = User.all.detect{|user| !user.try(:person).nil? && !user.person.projects.empty?}
      projects_shared = user.nil? ? [] : type.all_authorized_for("download", user, @project)
      #now select those with a policy set to downloadable to all-sysmo-users
      projects_shared  = projects_shared.select do |item|
        access_type = type.is_isa? ? Policy::VISIBLE : Policy::ACCESSIBLE
        (item.policy.sharing_scope == Policy::ALL_SYSMO_USERS && item.policy.access_type == access_type)
      end
      #just those shared with sysmo but NOT shared publicly
      @semi_public_assets[type]  = projects_shared  - @public_assets[type]

      all = project_assets.select{|a|a.class==type}
      @restricted_assets[type] = all - (@semi_public_assets[type] | @public_assets[type])
    end

    #inlinked assets - either not linked to an assay or publication, or in the case of assays not linked to a publication or other assets
    @types_for_unlinked = [DataFile, Model, Sop, Assay]
    @unlinked_to_publication={}
    @unlinked_to_assay={}
    @unlinked_assets={}
    @types_for_unlinked.each do |type|
      @unlinked_assets[type] = []
      @unlinked_to_publication[type] = []
      @unlinked_to_assay[type] = []
    end
    project_assets.each do |asset|
      if @types_for_unlinked.include?(asset.class)
        if asset.related_publications.empty?
          @unlinked_to_publication[asset.class] << asset
        end
        if (!asset.respond_to?(:assays) || asset.assays.empty?) && (!asset.is_isa? || asset.assets.empty?)
          @unlinked_to_assay[asset.class] << asset
        end
      end
    end
    #get those that are unlinked to either
    @types_for_unlinked.each do |type|
      @unlinked_assets[type]=@unlinked_to_assay[type] & @unlinked_to_publication[type]
    end


    respond_to do |format|
      format.html {render :template=>"projects/asset_report/report"}
    end
  end

  def admin
    
    respond_to do |format|
      format.html # admin.html.erb
    end
  end

  # GET /projects/1
  # GET /projects/1.xml
  def show
    respond_to do |format|
      format.html # show.html.erb
      format.rdf { render :template=>'rdf/show'}
      format.xml
      format.json { render :text=>@project.to_json}
    end
  end

  # GET /projects/new
  # GET /projects/new.xml
  def new
    @project = Project.new

    possible_unsaved_data = "unsaved_#{@project.class.name}_#{@project.id}".to_sym
    if session[possible_unsaved_data]
      # if user was redirected to this 'edit' page from avatar upload page - use session
      # data; alternatively, user has followed some other route - hence, unsaved session
      # data is most probably not relevant anymore
      if params[:use_unsaved_session_data]
        # NB! these parameters are admin settings and can *occasionally* be used by super-users -
        # regular users won't (and MUST NOT) be able to use these; it's not likely for admins
        # or super-users to modify these along with participating institutions - therefore,
        # it's better not to update these from session
        #
        # this was also causing a bug: when "upload new avatar" pressed, then new picture
        # uploaded and redirected back to edit profile page; at this poing *new* records
        # in the DB for institutions that participate in this project would already be created, which is an
        # error (if the following line is ever to be removed, the bug needs investigation)
        session[possible_unsaved_data][:project].delete(:institution_ids)

        # update those attributes of a project that we want to be updated from the session
        @project.attributes = session[possible_unsaved_data][:project]
        @project.organism_list = session[possible_unsaved_data][:organism][:list] if session[possible_unsaved_data][:organism]
      end

      # clear the session data anyway
      session[possible_unsaved_data] = nil
    end
  end

  # GET /projects/1/edit
  def edit
    
    possible_unsaved_data = "unsaved_#{@project.class.name}_#{@project.id}".to_sym
    if session[possible_unsaved_data]
      # if user was redirected to this 'edit' page from avatar upload page - use session
      # data; alternatively, user has followed some other route - hence, unsaved session
      # data is most probably not relevant anymore
      if params[:use_unsaved_session_data]
        # NB! these parameters are admin settings and can *occasionally* be used by super-users -
        # regular users won't (and MUST NOT) be able to use these; it's not likely for admins
        # or super-users to modify these along with participating institutions - therefore,
        # it's better not to update these from session
        #
        # this was also causing a bug: when "upload new avatar" pressed, then new picture
        # uploaded and redirected back to edit profile page; at this poing *new* records
        # in the DB for institutions that participate in this project would already be created, which is an
        # error (if the following line is ever to be removed, the bug needs investigation)
        session[possible_unsaved_data][:project].delete(:institution_ids)
        
        # update those attributes of a project that we want to be updated from the session
        @project.attributes = session[possible_unsaved_data][:project]
        @project.organism_list = session[possible_unsaved_data][:organism][:list] if session[possible_unsaved_data][:organism]
      end
      
      # clear the session data anyway
      session[possible_unsaved_data] = nil
    end
  end

  # POST /projects
  # POST /projects.xml
  def create
    @project = Project.new(params[:project])

    @project.default_policy.set_attributes_with_sharing params[:sharing], [@project]


    respond_to do |format|
      if @project.save
        flash[:notice] = "#{t('project')} was successfully created."
        format.html { redirect_to(@project) }
        format.xml  { render :xml => @project, :status => :created, :location => @project }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @project.errors, :status => :unprocessable_entity }
      end
    end
  end

  # PUT /projects/1
  # PUT /projects/1.xml
  def update
    
    # extra check required to see if any avatar was actually selected (or it remains to be the default one)
    avatar_id = params[:project].delete(:avatar_id).to_i
    @project.avatar_id = ((avatar_id.kind_of?(Numeric) && avatar_id > 0) ? avatar_id : nil)

    @project.default_policy = (@project.default_policy || Policy.default).set_attributes_with_sharing params[:sharing], [@project] if params[:sharing]

    begin
      respond_to do |format|
        if @project.update_attributes(params[:project])
          expire_resource_list_item_content
          flash[:notice] = "#{t('project')} was successfully updated."
          format.html { redirect_to(@project) }
          format.xml  { head :ok }
        else
          format.html { render :action => "edit" }
          format.xml  { render :xml => @project.errors, :status => :unprocessable_entity }
        end
      end
    rescue WorkGroupDeleteError=>e
      respond_to do |format|
        flash[:error] = e.message
        format.html { redirect_to(@project) }
      end
    end
  end

  def manage
    @projects = Project.all
    respond_to do |format|
      format.html
      format.xml{render :xml=>@projects}
    end
  end
  # DELETE /projects/1
  # DELETE /projects/1.xml
  def destroy
    respond_to do |format|
      if @project.can_delete?
        @project.destroy
        format.html { redirect_to(projects_path) }
        format.xml { head :ok }
      else
        flash.now[:error]="Unable to delete #{t('project')} with children"
        format.html { redirect_to(@project) }
        format.xml { render :xml=>@project.errors, :status=>:unprocessable_entity }
      end
    end
  end
  
  
  # returns a list of institutions for a project in JSON format
  def request_institutions
    # listing institutions for a project is public data, but still
    # we require login to protect from unwanted requests
    
    project_id = white_list(params[:id])
    institution_list = nil
    
    begin
      project = Project.find(project_id)
      institution_list = project.work_groups.collect{|w| [w.institution.title, w.institution.id, w.id]}
      success = true
    rescue ActiveRecord::RecordNotFound
      # project wasn't found
      success = false
    end
    
    respond_to do |format|
      format.json {
        if success
          render :json => {:status => 200, :institution_list => institution_list }
        else
          render :json => {:status => 404, :error => "Couldn't find #{t('project')} with ID #{project_id}."}
        end
      }
    end
  end

  def admin_members
    respond_with(@project)
  end

  def update_members
    groups_to_remove = params[:group_memberships_to_remove] || []
    people_and_institutions_to_add = params[:people_and_institutions_to_add] || []
    groups_to_remove.each do |group|
      g = GroupMembership.find(group)
      g.destroy unless g.nil?
    end

    people_and_institutions_to_add.each do |new_info|
      json = JSON.parse(new_info)
      person_id = json["person_id"]
      institution_id = json["institution_id"]
      person = Person.find(person_id)
      institution = Institution.find(institution_id)
      unless person.nil? || institution.nil?
        work_group = WorkGroup.where(:project_id=>@project.id,:institution_id => institution_id).first
        work_group ||= WorkGroup.new(:project=>@project,:institution=>institution)
        group_membership = GroupMembership.new :work_group=>work_group,:person=>person
        work_group.save!
        group_membership.save!
      end
    end

    flash[:notice]="The members and institutions of the #{t('project')}.downcase '#{@project.title}' have been updated"

    respond_with(@project) do |format|
      format.html {redirect_to project_path(@project)}
    end
  end

  private  

  def editable_by_user
    @project = Project.find(params[:id])
    unless User.admin_logged_in? || @project.can_be_edited_by?(current_user)
      error("Insufficient privileges", "is invalid (insufficient_privileges)")
      return false
    end
  end

  def member_of_this_project
    @project = Project.find(params[:id])
    if @project.nil? || !@project.has_member?(current_user)
      flash[:error]="You are not a member of this #{t('project')}, so cannot access this page."
      redirect_to project_path(@project)
      false
    else
      true
    end

  end

  def administerable_by_user
    @project = Project.find(params[:id])
    unless User.admin_logged_in? || @project.can_be_administered_by?(current_user)
      error("Insufficient privileges", "is invalid (insufficient_privileges)")
      return false
    end
  end

  def auth_params
    restricted_params={:sharing => User.admin_logged_in?,
                       :site_root_uri => User.admin_logged_in?,
                       :site_username => User.admin_logged_in?,
                       :site_password => User.admin_logged_in?,
                       :institution_ids => (User.admin_logged_in? || @project.can_be_administered_by?(current_user))}
    restricted_params.each do |param, allowed|
      params[:project].delete(param) if params[:project] and not allowed
      params.delete param if params and not allowed
    end
  end
end