Newer
Older
dmpopidor / lib / dmpopidor / controllers / plans.rb
# frozen_string_literal: true

module Dmpopidor

  module Controllers

    module Plans

      # CHANGES:
      # Added Active Flag on Org
      def new
        @plan = Plan.new
        authorize @plan

        # Get all of the available funders and non-funder orgs
        @funders = Org.funder
                      .joins(:templates)
                      .where(templates: { published: true }).uniq.sort_by(&:name)
        @orgs = (Org.organisation + Org.institution + Org.managing_orgs + Org.where(is_other: true)).flatten
                                                                        .select { |org| org.active == true }
                                                                        .uniq.sort_by(&:name)

        # Get the current user's org
        @default_org = current_user.org if @orgs.include?(current_user.org) || @funders.include?(current_user.org) 

        # Get the default template
        @default_template = Template.default

        if params.key?(:test)
          flash[:notice] = "#{_('This is a')} <strong>#{_('test plan')}</strong>"
        end
        @is_test = params[:test] ||= false
        respond_to :html
      end

      # CHANGES:
      # Added Research Output Support
      def create
        @plan = Plan.new
        authorize @plan

        # We set these ids to -1 on the page to trick ariatiseForm into allowing the
        # autocomplete to be blank if the no org/funder checkboxes are checked off
        org_id = (plan_params[:org_id] == "-1" ? "" : plan_params[:org_id])

        # If the template_id is blank then we need to look up the available templates and
        # return JSON
        if plan_params[:template_id].blank?
          # Something went wrong there should always be a template id
          respond_to do |format|
            flash[:alert] = _("Unable to identify a suitable template for your plan.")
            format.html { redirect_to new_plan_path }
          end
        else
          # Otherwise create the plan
          if current_user.surname.blank?
            @plan.principal_investigator = nil
          else
            @plan.principal_investigator = current_user.name(false)
          end

          @plan.principal_investigator_email = current_user.email

          orcid = current_user.identifier_for(IdentifierScheme.find_by(name: "orcid"))
          @plan.principal_investigator_identifier = orcid.identifier unless orcid.nil?

          @plan.funder_name = plan_params[:funder_name]

          @plan.visibility = if plan_params["visibility"].blank?
                               Rails.application.config.default_plan_visibility
                             else
                               plan_params[:visibility]
                             end

          @plan.template = Template.find(plan_params[:template_id])

          if plan_params[:title].blank?
            @plan.title = if current_user.firstname.blank?
                            _("My Plan") + "(" + @plan.template.title + ")"
                          else
                            current_user.firstname + "'s" + _(" Plan")
                          end
          else
            @plan.title = plan_params[:title]
          end

          if @plan.save
            # pre-select org's guidance and the default org's guidance
            ids = (Org.managing_orgs << current_user.org_id << org_id).flatten.uniq
            ggs = GuidanceGroup.where(org_id: ids, optional_subset: false, published: true)

            if !ggs.blank? then @plan.guidance_groups << ggs end

            default = Template.default

            msg = "#{success_message(@plan, _('created'))}<br />"

            if !default.nil? && default == @plan.template
              # We used the generic/default template
              msg += " #{_('This plan is based on the default template.')}"

            elsif !@plan.template.customization_of.nil?
              # We used a customized version of the the funder template
              # rubocop:disable Metrics/LineLength
              msg += " #{d_('dmpopidor', 'This plan is based on the %{funder_name}: %{template_name} template with customisations by the %{org_name}') % { 
                  funder_name: plan_params[:funder_name],
                  template_name: @plan.template.title,
                  org_name: plan_params[:org_name]
              } }"
              # rubocop:enable Metrics/LineLength
            else
              # We used the specified org's or funder's template
              # rubocop:disable Metrics/LineLength
              msg += " #{d_('dmpopidor', 'This plan is based on the %{org_name}: %{template_name} template') % { org_name: @plan.template.org.name, template_name: @plan.template.title} }"
              # rubocop:enable Metrics/LineLength
            end

            @plan.add_user!(current_user.id, :creator)

            @plan.create_plan_fragments

            # Add default research output if possible
            @plan.research_outputs.create(
              abbreviation: "Default",
              fullname: "Default research output",
              is_default: true,
              type: ResearchOutputType.find_by(label: "Dataset"),
              order: 1
            )

            respond_to do |format|
              flash[:notice] = msg
              format.html { redirect_to plan_path(@plan) }
            end

          else
            # Something went wrong so report the issue to the user
            respond_to do |format|
              flash[:alert] = failure_message(@plan, _("create"))
              format.html { redirect_to new_plan_path }
            end
          end
        end
      end

      # GET /plans/show
      def show
        @plan = Plan.includes(
          template: { phases: { sections: { questions: :answers } } },
          plans_guidance_groups: { guidance_group: :guidances }
        ).find(params[:id])
        @schemas = MadmpSchema.all
        authorize @plan

        @research_outputs = @plan.research_outputs.order(:order)

        @visibility = if @plan.visibility.present?
                        @plan.visibility.to_s
                      else
                        Rails.application.config.default_plan_visibility
                      end

        @editing = (!params[:editing].nil? && @plan.administerable_by?(current_user.id))

        # Get all Guidance Groups applicable for the plan and group them by org
        @all_guidance_groups = @plan.guidance_group_options
        @all_ggs_grouped_by_org = @all_guidance_groups.sort.group_by(&:org)
        @selected_guidance_groups = @plan.guidance_groups

        # Important ones come first on the page - we grab the user's org's GGs and
        # "Organisation" org type GGs
        @important_ggs = []

        if @all_ggs_grouped_by_org.include?(current_user.org)
          @important_ggs << [current_user.org, @all_ggs_grouped_by_org[current_user.org]]
        end
        @all_ggs_grouped_by_org.each do |org, ggs|
          if org.organisation?
            @important_ggs << [org, ggs]
          end

          # If this is one of the already selected guidance groups its important!
          if !(ggs & @selected_guidance_groups).empty?
            @important_ggs << [org, ggs] unless @important_ggs.include?([org, ggs])
          end
        end

        # Sort the rest by org name for the accordion
        @important_ggs = @important_ggs.sort_by { |org, gg| (org.nil? ? "" : org.name) }
        @all_ggs_grouped_by_org = @all_ggs_grouped_by_org.sort_by do |org, gg|
          (org.nil? ? "" : org.name)
        end
        @selected_guidance_groups = @selected_guidance_groups.ids

        @based_on = if @plan.template.customization_of.nil?
                      @plan.template
                    else
                      Template.where(family_id: @plan.template.customization_of).first
                    end
        respond_to :html
      end

      # GET /plans/:plan_id/phases/:id/edit
      # CHANGES :
      # Added Research Output Support
      def edit
        plan = Plan.includes(:research_outputs).find(params[:id])
        authorize plan
        plan, phase = Plan.load_for_phase(params[:id], params[:phase_id])
        guidance_groups = GuidanceGroup.where(published: true, id: plan.guidance_group_ids)
        render_phases_edit(plan, phase, guidance_groups)
      end

      # PUT /plans/1
      # PUT /plans/1.json
      # CHANGES :
      # Added Research Output Support
      # Added DMP, Meta & Project update
      def update
        @plan = Plan.find(params[:id])
        authorize @plan
        attrs = plan_params
        meta = schema_params(MadmpSchema.find_by(name: "MetaStandard"), "meta")
        project = schema_params(MadmpSchema.find_by(name: "ProjectStandard"), "project")
        attrs[:title] = meta["title"]
        # rubocop:disable Metrics/BlockLength
        respond_to do |format|
          begin
            # Save the guidance group selections
            guidance_group_ids = if params[:guidance_group_ids].blank?
                                   []
                                 else
                                   params[:guidance_group_ids].map(&:to_i).uniq
                                 end
            @plan.guidance_groups = GuidanceGroup.where(id: guidance_group_ids)
            @plan.save
            if @plan.update_attributes(attrs)
              @plan.update_plan_fragments(meta, project)

              format.html do
                redirect_to plan_path(@plan),
                            notice: success_message(@plan, _("saved"))
              end
              format.json do
                render json: { code: 1, msg: success_message(@plan, _("saved")) }
              end
            else
              format.html do
                # TODO: Should do a `render :show` here instead but show defines too many
                #       instance variables in the controller
                redirect_to "#{plan_path(@plan)}", alert: failure_message(@plan, _("save"))
              end
              format.json do
                render json: { code: 0, msg: failure_message(@plan, _("save")) }
              end
            end
          rescue Exception
            flash[:alert] = failure_message(@plan, _("save"))
            format.html do
              render_phases_edit(@plan, @plan.phases.first, @plan.guidance_groups)
            end
            format.json do
              render json: { code: 0, msg: flash[:alert] }
            end
          end
        end
        # rubocop:enable Metrics/BlockLength
      end

      # POST /plans/:id/visibility
      def visibility
        plan = Plan.find(params[:id])
        if plan.present?
          authorize plan
          if plan.visibility_allowed?
            plan.visibility = plan_params[:visibility]
            if plan.save
              deliver_if(recipients: plan.owner_and_coowners,
                        key: "owners_and_coowners.visibility_changed") do |r|
                UserMailer.plan_visibility(r, plan).deliver_now()
              end
              redirect_to :back, notice: success_message(plan, _("updated"))
            else
              redirect_to :back, notice: failure_message(plan, _("update"))
            end
          else
            # rubocop:disable Metrics/LineLength
            redirect_to :back, notice: failure_message(
              _("Unable to change the plan's status since it is needed at least %{percentage} percentage responded") % {
                percentage: Rails.application.config.default_plan_percentage_answered
              }
            )
            # rubocop:enable Metrics/LineLength
          end
        else
          redirect_to :back, notice: failure_message(
            _("Unable to find plan id %{plan_id}") % { plan_id: params[:id] }
          )
        end
      end

      # Removing test flag now put the plan in privately visibility
      def set_test
        plan = Plan.find(params[:id])
        authorize plan
        plan.visibility = (params[:is_test] === "1" ? :is_test : :privately_visible)
        # rubocop:disable Metrics/LineLength
        if plan.save
          render json: {
            code: 1,
            msg: (plan.is_test? ? _("Your project is now a test.") : _("Your project is no longer a test."))
          }
        else
          render status: :bad_request, json: {
            code: 0, msg: _("Unable to change the plan's test status")
          }
        end
        # rubocop:enable Metrics/LineLength
      end

      # CHANGES : Research Outputs support
      def download
        @plan = Plan.find(params[:id])
        authorize @plan
        @research_outputs = @plan.research_outputs
        @phase_options = @plan.phases.order(:number).pluck(:title, :id)
        @export_settings = @plan.settings(:export)
        render "download"
      end

      # CHANGES : MADMP_FRAGMENTS SUPPORT
      def destroy
        @plan = Plan.find(params[:id])
        dmp_fragment = @plan.json_fragment
        authorize @plan
        if @plan.destroy
          dmp_fragment.destroy
          respond_to do |format|
            format.html do
              redirect_to plans_url,
                          notice: success_message(@plan, _("deleted"))
            end
          end
        else
          respond_to do |format|
            flash[:alert] = failure_message(@plan, _("delete"))
            format.html { render action: "edit" }
          end
        end
      end

      private
      # CHANGES : Research Outputs support
      def plan_params
        params.require(:plan)
              .permit(:org_id, :org_name, :funder_id, :funder_name, :template_id, 
                      :title, :visibility, :grant_number, :description, :identifier,
                      :principal_investigator_phone, :principal_investigator,
                      :principal_investigator_email, :data_contact,
                      :principal_investigator_identifier, :data_contact_email,
                      :data_contact_phone, :guidance_group_ids,
                      research_outputs_attributes: %i[_destroy])
      end

      # Get the parameters corresponding to the schema
      def schema_params(schema, form_prefix, flat = false)
        s_params = schema.generate_strong_params(flat)
        params.require(:plan)[form_prefix].permit(s_params)
      end

      # CHANGES : maDMP Fragments SUPPORT
      def render_phases_edit(plan, phase, guidance_groups)
        readonly = !plan.editable_by?(current_user.id)
        @schemas = MadmpSchema.all
        # Since the answers have been pre-fetched through plan (see Plan.load_for_phase)
        # we create a hash whose keys are question id and value is the answer associated
        answers = plan.answers.includes(:madmp_fragment).reduce({}) { |m, a| m["#{a.question_id}_#{a.research_output_id}"] = a; m }
        render("/phases/edit", locals: {
          base_template_org: phase.template.base_org,
          plan: plan,
          phase: phase,
          readonly: readonly,
          guidance_groups: guidance_groups,
          answers: answers,
          guidance_presenter: GuidancePresenter.new(plan)
        })
      end

    end

  end

end