Newer
Older
dmpopidor / app / controllers / org_admin / templates_controller.rb
@Brian Riley Brian Riley on 17 Apr 2020 13 KB patch template creation (#2459)
# frozen_string_literal: true

module OrgAdmin

  class TemplatesController < ApplicationController

    include Paginable
    include Versionable
    include TemplateMethods

    after_action :verify_authorized

    # The root version of index which returns all templates
    # GET /org_admin/templates
    # -----------------------------------------------------
    def index
      authorize Template
      templates = Template.latest_version.where(customization_of: nil)
      published = templates.select { |t| t.published? || t.draft? }.length

      @orgs              = Org.all
      @title             = _("All Templates")
      @templates         = templates.includes(:org).page(1)
      @query_params      = { sort_field: "templates.title", sort_direction: "asc" }
      @all_count         = templates.length
      @published_count   = published.present? ? published : 0
      @unpublished_count = if published.present?
                             (templates.length - published)
                           else
                             templates.length
                           end
      render :index
    end

    # A version of index that displays only templates that belong to the user's org
    # GET /org_admin/templates/organisational
    # -----------------------------------------------------
    def organisational
      authorize Template
      templates = Template.latest_version_per_org(current_user.org.id)
                          .where(customization_of: nil, org_id: current_user.org.id)
      published = templates.select { |t| t.published? || t.draft? }.length

      @orgs  = current_user.can_super_admin? ? Org.all : nil
      @title = if current_user.can_super_admin?
                 _("%{org_name} Templates") % { org_name: current_user.org.name }
               else
                 _("Own Templates")
               end
      @templates         = templates.page(1)
      @query_params      = { sort_field: "templates.title", sort_direction: "asc" }
      @all_count         = templates.length
      @published_count   = published.present? ? published : 0
      @unpublished_count = if published.present?
                             templates.length - published
                           else
                             templates.length
                           end
      render :index
    end

    # A version of index that displays only templates that are customizable
    # GET /org_admin/templates/customisable
    # -----------------------------------------------------
    def customisable
      authorize Template
      customizations = Template.latest_customized_version_per_org(current_user.org.id)
                               .where(org_id: current_user.org.id)
      funder_templates = Template.latest_customizable.includes(:org)
      # We use this to validate the counts below in the event that a template was
      # customized but the base template org is no longer a funder
      funder_template_families = funder_templates.collect(&:family_id)
      # filter only customizations of valid(published) funder templates
      customizations = customizations.select { |t|
                  funder_template_families.include?(t.customization_of) }
      published = customizations.select { |t| t.published? || t.draft? }.length

      @orgs = current_user.can_super_admin? ? Org.all : []
      @title = _("Customizable Templates")
      @templates = funder_templates
      @customizations = customizations
      @query_params = { sort_field: "templates.title", sort_direction: "asc" }
      @all_count = funder_templates.length
      @published_count = published.present? ? published : 0
      @unpublished_count = if published.present?
                             (customizations.length - published)
                           else
                             customizations.length
                           end
      @not_customized_count = funder_templates.length - customizations.length

      render :index
    end

    # GET /org_admin/templates/[:id]
    def show
      template = Template.find(params[:id])
      authorize template
      # Load the info needed for the overview section if the authorization check passes!
      phases = template.phases
                       .includes(sections: { questions: :question_options })
                       .order("phases.number", "sections.number", "questions.number",
                              "question_options.number")
                       .select("phases.title", "phases.description", "sections.title",
                               "questions.text", "question_options.text")
      if !template.latest?
        # rubocop:disable Metrics/LineLength
        flash[:notice] = _("You are viewing a historical version of this template. You will not be able to make changes.")
        # rubocop:enable Metrics/LineLength
      end
      render "container", locals: {
        partial_path: "show",
        template: template,
        phases: phases,
        referrer: get_referrer(template, request.referrer) }
    end

    # GET /org_admin/templates/:id/edit
    def edit
      template = Template.includes(:org, :phases).find(params[:id])
      authorize template
      # Load the info needed for the overview section if the authorization check passes!
      phases = template.phases.includes(sections: { questions: :question_options }).
                        order("phases.number",
                              "sections.number",
                              "questions.number",
                              "question_options.number").
                        select("phases.title",
                               "phases.description",
                               "sections.title",
                               "questions.text",
                               "question_options.text")
      if !template.latest?
        redirect_to org_admin_template_path(id: template.id)
      else
        render "container", locals: {
          partial_path: "edit",
          template: template,
          phases: phases,
          referrer: get_referrer(template, request.referrer) }
      end
    end

    # GET /org_admin/templates/new
    def new
      authorize Template
      @template = current_org.templates.new
    end

    # POST /org_admin/templates
    def create
      authorize Template
      args = template_params
      # Swap in the appropriate visibility enum value for the checkbox value
      args[:visibility] = args.fetch(:visibility, "0") == "1" ? "organisationally_visible" : "publicly_visible"

      # creates a new template with version 0 and new family_id
      @template = Template.new(args)
      @template.org_id = current_user.org.id
      @template.locale = current_org.language.abbreviation
      @template.links = if params["template-links"].present?
                          ActiveSupport::JSON.decode(params["template-links"])
                        else
                          { "funder": [], "sample_plan": [] }
                        end
      if @template.save
        redirect_to edit_org_admin_template_path(@template),
                    notice: success_message(@template, _("created"))
      else
        flash[:alert] = flash[:alert] = failure_message(@template, _("create"))
        render :new
      end
    end

    # PUT /org_admin/templates/:id (AJAXable)
    # -----------------------------------------------------
    def update
      template = Template.find(params[:id])
      authorize template
      begin
        args = template_params
        # Swap in the appropriate visibility enum value for the checkbox value
        args[:visibility] = args.fetch(:visibility, '0') == '1' ? 'organisationally_visible' : 'publicly_visible'

        template.assign_attributes(args)
        if params["template-links"].present?
          template.links = ActiveSupport::JSON.decode(params["template-links"])
        end
        if template.save
          render(json: {
            status: 200,
            msg: success_message(template, _("saved"))
          })
        else
          render(json: {
            status: :bad_request,
            msg: failure_message(template, _("save"))
          })
        end
      rescue ActiveSupport::JSON.parse_error
        render(json: {
          status: :bad_request,
          msg: _("Error parsing links for a %{template}") %
               { template: template_type(template) }
        })
        return
      rescue => e
        render(json: {
          status: :forbidden,
          msg: e.message
        }) and return
      end
    end

    # DELETE /org_admin/templates/:id
    def destroy
      template = Template.find(params[:id])
      authorize template
      versions = Template.includes(:plans).where(family_id: template.family_id)
      if versions.select { |t| t.plans.length > 0 }.empty?
        versions.each do |version|
          if version.destroy!
            flash[:notice] = success_message(template, _("removed"))
          else
            flash[:alert] = failure_message(template, _("remove"))
          end
        end
      else
        # rubocop:disable Metrics/LineLength
        flash[:alert] = _("You cannot delete a #{template_type(template)} that has been used to create plans.")
        # rubocop:enable Metrics/LineLength
      end
      if request.referrer.present?
        redirect_to request.referrer
      else
        redirect_to org_admin_templates_path
      end
    end

    # GET /org_admin/templates/:id/history
    def history
      template = Template.find(params[:id])
      authorize template
      templates = Template.where(family_id: template.family_id)
      local_referrer = if template.customization_of.present?
                         customisable_org_admin_templates_path
                       else
                         organisational_org_admin_templates_path
                       end
      render "history", locals: {
        templates: templates,
        query_params: { sort_field: "templates.version", sort_direction: "desc" },
        referrer: local_referrer,
        current: templates.maximum(:version)
      }
    end

    # PATCH /org_admin/templates/:id/publish  (AJAX)
    def publish
      template = Template.find(params[:id])
      authorize template
      # rubocop:disable Metrics/LineLength
      publishable, errors = template.publishability
      if publishable
        if template.publish!
          flash[:notice] = _("Your #{template_type(template)} has been published and is now available to users.")
        else
          flash[:alert] = _("Unable to publish your #{template_type(template)}.")
        end
      else
        flash[:alert] = errors
      end
      # rubocop:enable Metrics/LineLength
      redirect_to request.referrer.present? ? request.referrer : org_admin_templates_path
    end

    # PATCH /org_admin/templates/:id/unpublish  (AJAX)
    def unpublish
      template = Template.find(params[:id])
      authorize template
      versions = Template.where(family_id: template.family_id)
      versions.each do |version|
        unless version.update_attributes!(published: false)
          flash[:alert] = _("Unable to unpublish your #{template_type(template)}.")
        end
      end
      unless flash[:alert].present?
        flash[:notice] = _("Successfully unpublished your #{template_type(template)}")
      end
      redirect_to request.referrer.present? ? request.referrer : org_admin_templates_path
    end

    # GET template_export/:id
    # -----------------------------------------------------
    def template_export
      @template = Template.find(params[:id])

      authorize @template
      # now with prefetching (if guidance is added, prefetch annottaions/guidance)
      @template = Template.includes(
        :org,
        phases: {
          sections: {
            questions: [
              :question_options,
              :question_format,
              :annotations
            ]
          }
        }
      ).find(@template.id)

      @formatting = Settings::Template::DEFAULT_SETTINGS[:formatting]

      begin
        file_name = @template.title.gsub(/[^a-zA-Z\d\s]/, "").gsub(/ /, "_") + '_v' + @template.version.to_s
        respond_to do |format|
          format.docx do
            render docx: "template_exports/template_export", filename: "#{file_name}.docx"
          end

          format.pdf do
            # rubocop:disable Metrics/LineLength
            render pdf: file_name,
              template: "template_exports/template_export",
              margin: @formatting[:margin],
              footer: {
                center:    _("Template created using the %{application_name} service. Last modified %{date}") % {
                application_name: Rails.configuration.branding[:application][:name],
                date: l(@template.updated_at.to_date, formats: :short)
              },
              font_size: 8,
              spacing: (@formatting[:margin][:bottom] / 2) - 4,
              right: "[page] of [topage]",
              encoding: "utf8"
            }
            # rubocop:enable Metrics/LineLength
          end
        end
      rescue ActiveRecord::RecordInvalid => e
        # What scenario is this triggered in? it's common to our export pages
        redirect_to public_templates_path,
                    alert: _("Unable to download the DMP Template at this time.")
      end
    end

    private

    def template_params
      params.require(:template).permit(:title, :description, :visibility, :links)
    end

    def get_referrer(template, referrer)
      return org_admin_templates_path unless referrer.present?
      if referrer.end_with?(new_org_admin_template_path) ||
           referrer.end_with?(edit_org_admin_template_path) ||
           referrer.end_with?(org_admin_template_path)

        if template.customization_of.present?
          customisable_org_admin_templates_path
        else
          organisational_org_admin_templates_path
        end
      else
        request.referrer
      end
    end

  end

end