SysMO-DB/seek

View on GitHub
app/controllers/people_controller.rb

Summary

Maintainability
D
3 days
Test Coverage
class PeopleController < ApplicationController

  include Seek::AnnotationCommon
  include Seek::Publishing::PublishingCommon
  include Seek::Publishing::GatekeeperPublish
  include Seek::FacetedBrowsing

  before_filter :find_and_authorize_requested_item, :only => [:show, :edit, :update, :destroy]
  before_filter :current_user_exists,:only=>[:select,:userless_project_selected_ajax,:create,:new]
  before_filter :is_during_registration,:only=>[:select]
  before_filter :is_user_admin_auth,:only=>[:destroy,:new]
  before_filter :removed_params,:only=>[:update,:create]
  before_filter :administerable_by_user, :only => [:admin, :administer_update]
  before_filter :do_projects_belong_to_project_manager_projects,:only=>[:administer_update]
  before_filter :editable_by_user, :only => [:edit, :update]
  skip_before_filter :project_membership_required, :only => [:create, :new]
  skip_before_filter :profile_for_login_required,:only=>[:select,:userless_project_selected_ajax,:create]
  skip_after_filter :request_publish_approval,:log_publishing, :only => [:create,:update]

  after_filter :reset_notifications, :only => [:administer_update]

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

  def reset_notifications
    # disable sending notifications for non_project members
    if !@person.member? && @person.notifiee_info.receive_notifications
      @person.notifiee_info.receive_notifications = false
      @person.notifiee_info.save
    end
  end
  def auto_complete_for_tools_name
    render :json => Person.tool_counts.map(&:name).to_json
  end

  def auto_complete_for_expertise_name
    render :json => Person.expertise_counts.map(&:name).to_json
  end


  
  protect_from_forgery :only=>[]
  
  # GET /people
  # GET /people.xml
  def index
    if (params[:discipline_id])
      @discipline=Discipline.find(params[:discipline_id])
      #FIXME: strips out the disciplines that don't match
      @people=Person.where(["disciplines.id=?",@discipline.id]).includes(:disciplines)
      #need to reload the people to get their full discipline list - otherwise only get those matched above. Must be a better solution to this
      @people.each(&:reload)
    elsif (params[:project_role_id])
      @project_role=ProjectRole.find(params[:project_role_id])
      @people=Person.includes(:group_memberships)
      #FIXME: this needs double checking, (a) not sure its right, (b) can be paged when using find.
      @people=@people.select{|p| !(p.group_memberships & @project_role.group_memberships).empty?}
    end

    unless @people
      if (params[:page].blank? || params[:page]=='latest' || params[:page]=="all")
        @people = Person.active
      else
        @people = Person.all
      end
      @people=@people.select{|p| !p.group_memberships.empty?}
      @people = apply_filters(@people).select(&:can_view?)#.select{|p| !p.group_memberships.empty?}

      unless Seek::Config.faceted_browsing_enabled && Seek::Config.facet_enable_for_pages["people"] && ie_support_faceted_browsing?
        @people=Person.paginate_after_fetch(@people,
                                            :page=>(params[:page] || Seek::Config.default_page('people')),
                                            :reorder=>false,
                                            :latest_limit => Seek::Config.limit_latest)
      end
    else
      @people = @people.select(&:can_view?).reject {|p| p.projects.empty?}
    end

    respond_to do |format|
      format.html # index.html.erb
      format.xml
    end
  end

  # GET /people/1
  # GET /people/1.xml
  def show                
    respond_to do |format|
      format.html # show.html.erb
      format.rdf { render :template=>'rdf/show'}
      format.xml
    end
  end

  # GET /people/new
  # GET /people/new.xml
  def new    
    @person = Person.new
    respond_to do |format|
      format.html { render :action=>"new" }
      format.xml  { render :xml => @person }
    end
  end

  # GET /people/1/edit
  def edit
    possible_unsaved_data = "unsaved_#{@person.class.name}_#{@person.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 regular users won't (and MUST NOT)
        # be able to use these; admins on the other hand are most likely to never change
        # any avatars - therefore, it's better to hide these attributes so they never get
        # updated from the 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 person's work group memberships would already be created, which is an
        # error (if the following 3 lines are ever to be removed, the bug needs investigation)
        session[possible_unsaved_data][:person].delete(:work_group_ids)        
        
        # update those attributes of a person that we want to be updated from the session
        @person.attributes = session[possible_unsaved_data][:person]
      end
      
      # clear the session data anyway
      session[possible_unsaved_data] = nil
    end
  end

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

  #GET /people/select
  #
  #Page for after registration that allows you to select yourself from a list of
  #people yet to be assigned, or create a new one if you don't exist
  def select
    @userless_projects=Project.with_userless_people

    #strip out project with no people with email addresses
    #TODO: can be made more efficient by putting into a direct query in Project.with_userless_people - but not critical as this is only used during registration
    @userless_projects = @userless_projects.select do |proj|
      !proj.people.find{|person| !person.email.nil? && person.user.nil?}.nil?
    end

    @userless_projects.sort!{|a,b|a.title<=>b.title}
    @person = Person.new(params[:openid_details]) #Add some default values gathered from OpenID, if provided.

  end

  # POST /people
  # POST /people.xml
  def create
    @person = Person.new(params[:person])

    redirect_action="new"

    set_tools_and_expertise(@person, params)
   
    registration = false
    registration = true if (current_user.person.nil?) #indicates a profile is being created during the registration process

    if registration    
      current_user.person=@person      
      @userless_projects=Project.with_userless_people
      @userless_projects.sort!{|a,b|a.title<=>b.title}
      is_sysmo_member=params[:sysmo_member]

      if (is_sysmo_member)
        member_details = ''
        member_details.concat(project_or_institution_details 'projects')
        member_details.concat(project_or_institution_details 'institutions')
      end

      redirect_action="select"
    end

    respond_to do |format|
      if @person.save && current_user.save
        #send notification email to admin and project managers, if a new member is registering as a new person
        if Seek::Config.email_enabled && registration && is_sysmo_member
          #send mail to admin
          Mailer.contact_admin_new_user_no_profile(member_details, current_user, base_host).deliver

          #send mail to project managers
          project_managers = project_managers_of_selected_projects params[:projects]
          project_managers.each do |project_manager|
            Mailer.contact_project_manager_new_user_no_profile(project_manager, member_details, current_user, base_host).deliver
          end
        end
        if (!current_user.active?)
          Mailer.signup(current_user, base_host).deliver
          flash[:notice]="An email has been sent to you to confirm your email address. You need to respond to this email before you can login"
          logout_user
          format.html { redirect_to :controller => "users", :action => "activation_required" }
        else
          flash[:notice] = 'Person was successfully created.'
          if @person.only_first_admin_person?
            format.html { redirect_to registration_form_admin_path(:during_setup=>"true") }
          else
            format.html { redirect_to(@person) }
          end

          format.xml { render :xml => @person, :status => :created, :location => @person }
        end

      else
        format.html { render redirect_action }
        format.xml { render :xml => @person.errors, :status => :unprocessable_entity }
      end
    end
  end

  # PUT /people/1
  # PUT /people/1.xml
  def update
    @person.disciplines.clear if params[:discipline_ids].nil?

    # extra check required to see if any avatar was actually selected (or it remains to be the default one)
    
    avatar_id = params[:person].delete(:avatar_id).to_i
    @person.avatar_id = ((avatar_id.kind_of?(Numeric) && avatar_id > 0) ? avatar_id : nil)
    
    set_tools_and_expertise(@person,params)    

    if !@person.notifiee_info.nil?
      @person.notifiee_info.receive_notifications = (params[:receive_notifications] ? true : false) 
      @person.notifiee_info.save if @person.notifiee_info.changed?
    end

    
    respond_to do |format|
      if @person.update_attributes(params[:person]) && set_group_membership_project_role_ids(@person,params)
        @person.save #this seems to be required to get the tags to be set correctly - update_attributes alone doesn't [SYSMO-158]
        @person.touch #this makes sure any caches based on the cache key are invalided where the person would not normally be updated, such as changing disciplines or tags
        flash[:notice] = 'Person was successfully updated.'
        format.html { redirect_to(@person) }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @person.errors, :status => :unprocessable_entity }
      end
    end
  end

    # PUT /people/1
  # PUT /people/1.xml
  def administer_update
    had_no_projects = @person.work_groups.empty?

    passed_params=    {:roles                 =>  User.admin_logged_in?,
                       :roles_mask            => User.admin_logged_in?,
                       :can_edit_projects     => (User.admin_logged_in? || (User.project_manager_logged_in? && !(@person.projects & current_user.try(:person).try(:projects).to_a).empty?)),
                       :can_edit_institutions => (User.admin_logged_in? || (User.project_manager_logged_in? && !(@person.projects & current_user.try(:person).try(:projects).to_a).empty?)),
                       :work_group_ids        => (User.admin_logged_in? || User.project_manager_logged_in?)}
    temp = params.clone
    params[:person] = {}
    passed_params.each do |param, allowed|
      params[:person]["#{param}"] = temp[:person]["#{param}"] if temp[:person]["#{param}"] and allowed
      params["#{param}"] = temp["#{param}"] if temp["#{param}"] and allowed
    end

    respond_to do |format|
      if @person.update_attributes(params[:person])
        set_roles(@person, params) if User.admin_logged_in?

        @person.save #this seems to be required to get the tags to be set correctly - update_attributes alone doesn't [SYSMO-158]
        @person.touch
        if Seek::Config.email_enabled && had_no_projects && !@person.work_groups.empty? && @person != current_user.person
          Mailer.notify_user_projects_assigned(@person).deliver
        end

        flash[:notice] = 'Person was successfully updated.'
        format.html { redirect_to(@person) }
        format.xml  { head :ok }
      else
        format.html { render :action => "admin" }
        format.xml  { render :xml => @person.errors, :status => :unprocessable_entity }
      end
    end
  end

  def set_group_membership_project_role_ids person,params
    #FIXME: Consider updating to Rails 2.3 and use Nested forms to handle this more cleanly.
    prefix="group_membership_role_ids_"
    person.group_memberships.each do |gr|
      key=prefix+gr.id.to_s
      gr.project_roles.clear
      if params[key.to_sym]
        params[key.to_sym].each do |r|
          r=ProjectRole.find(r)
          gr.project_roles << r
        end
      end
    end
  end

  # DELETE /people/1
  # DELETE /people/1.xml
  def destroy
    @person.destroy

    respond_to do |format|
      format.html { redirect_to(people_url) }
      format.xml  { head :ok }
    end
  end

  def userless_project_selected_ajax
    project_id=params[:project_id]
    unless project_id=="0"
      proj=Project.find(project_id)
      #ignore people with no email address
      @people=proj.userless_people.select{|person| !person.email.blank? }
      @people.sort!{|a,b| a.last_name<=>b.last_name}
      render :partial=>"userless_people_list",:locals=>{:people=>@people}
    else
      render :text=>""
    end
    
  end
  
  def get_work_group
    people = nil
    project_id=params[:project_id]
    institution_id=params[:institution_id]    
    if institution_id == "0"
      project = Project.find(project_id)
      people = project.people
    else
      workgroup = WorkGroup.find_by_project_id_and_institution_id(project_id,institution_id)
      people = workgroup.people
    end
    people_list = people.collect{|p| [h(p.name), p.email, p.id]}
    respond_to do |format|
      format.json {
        render :json => {:status => 200, :people_list => people_list }
      }
    end
  end

  private
  
  def set_tools_and_expertise person,params
      exp_changed = person.tag_with_params params,"expertise"
      tools_changed = person.tag_with_params params,"tool"
      if immediately_clear_tag_cloud?
        expire_annotation_fragments("expertise") if exp_changed
        expire_annotation_fragments("tool") if tools_changed
      else
         RebuildTagCloudsJob.create_job
      end

  end

  def set_roles person, params
    roles = person.is_admin? ? [['admin']] : []
    if params[:roles]
      params[:roles].each_key do |key|
        project_ids=params[:roles][key]

        roles << [key,project_ids]
      end
    end
    person.roles=roles
  end


  def is_user_admin_or_personless
    unless User.admin_logged_in? || current_user.person.nil?
      error("You do not have permission to create new people","Is invalid (not admin)")
      return false
    end
  end

  def current_user_exists
    if !current_user
      redirect_to(:root)
    end
    !!current_user
  end

  #checks the params attributes and strips those that cannot be set by non-admins, or other policy
  def removed_params
    # make sure to update people/_form if this changes
    #                   param                 => allowed access?
    removed_params = [:roles, :roles_mask, :can_edit_projects, :can_edit_institutions, :work_group_ids]

    removed_params.each do |param|
      params[:person].delete(param) if params[:person]
      params.delete param if params
    end
  end
  def project_or_institution_details projects_or_institutions
    details = ''
    unless params[projects_or_institutions].blank?
      params[projects_or_institutions].each do |project_or_institution|
        project_or_institution_details= project_or_institution.split(',')
        if project_or_institution_details[0] == 'Others'
          details.concat("Other #{projects_or_institutions.singularize.humanize.pluralize}: #{params["other_#{projects_or_institutions}"]}; ")
        else
          details.concat("#{projects_or_institutions.singularize.humanize.capitalize}: #{project_or_institution_details[0]}, Id: #{project_or_institution_details[1]}; ")
        end
      end
    end
    details
  end

  def project_managers_of_selected_projects projects_param
    project_manager_list = []
    unless projects_param.blank?
      projects_param.each do |project_param|
        project = Project.find_by_id(project_param)
        project_managers = project.try(:project_managers)
        project_manager_list |= project_managers unless project_managers.nil?
      end
    end
    project_manager_list
  end

  def do_projects_belong_to_project_manager_projects
      if (params[:person] and params[:person][:work_group_ids])
        if User.project_manager_logged_in? && !User.admin_logged_in?
          projects = []
          params[:person][:work_group_ids].each do |id|
            work_group = WorkGroup.find_by_id(id)
            project = work_group.try(:project)
            projects << project unless project.nil?
          end
        project_manager_projects = Seek::Config.project_hierarchy_enabled==true ? current_user.person.projects_and_descendants : current_user.person.projects
          flag = true
          projects.each do |project|
          unless @person.projects.include?(project) || project.can_be_administered_by?(current_user)
            flag = false
          end
          end
          if flag == false
          error("#{t('project')} manager can not assign person to the #{t('project').pluralize} that they are not in","Is invalid")
          end
          return flag
        end
    end
  end

  def editable_by_user
    unless @person.can_be_edited_by?(current_user)
      error("Insufficient privileges", "is invalid (insufficient_privileges)")
      return false
    end
  end

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

  def is_during_registration
    if User.logged_in_and_registered?
      error("You cannot register a new profile to yourself as you are already registered","Is invalid (already registered)")
      return false
    end
  end
end