Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Changelog

- Redesigned plan-creation page to enforce template access rules and simplify UI [#3534](https://github.com/DMPRoadmap/roadmap/issues/3534)

## v5.0.2
- Bump Ruby to v3.1.4 and use `.ruby-version` in CI
- [#3566](https://github.com/DMPRoadmap/roadmap/pull/3566)
Expand Down
52 changes: 43 additions & 9 deletions app/assets/stylesheets/blocks/_forms.scss
Original file line number Diff line number Diff line change
@@ -1,21 +1,55 @@
@use '../variables/colours' as *;

.form-control {
border: 0px;
}

.form-control input, .form-control textarea, .form-control select{
border: 2px solid $color-border-light;

.form-control input,
.form-control textarea,
.form-control select {
border: 2px solid $color-border-light;
}

.form-check {
padding-left: 0rem;
padding-left: 0rem;
}

.form-inline{
margin-bottom: 5px;
.form-inline {
margin-bottom: 5px;
}

.form-check-label {
padding-left: 5px;
padding-left: 5px;
}

// Added for new plan create page app/views/plans/new.html.erb
.roadmap-form {
margin-bottom: 1rem;

legend {
padding-inline: 2px;
float: none;
width: auto;
padding: 0.5rem;
font-size: 1rem;
background-color: $color-primary-background;
color: $color-primary-text;
}

fieldset {
border: 2px solid #555555;
padding: 1rem;
margin-bottom: 1rem;
}

.form-row {
margin-bottom: 1rem;
}

.form-row:last-child {
margin-bottom: 0;
}

.form-check .form-check-input {
float: left;
margin-left: 0.5rem;
}
}
93 changes: 74 additions & 19 deletions app/controllers/plans_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,15 @@ def index
# rubocop:enable Metrics/AbcSize

# GET /plans/new
# rubocop:disable Metrics/AbcSize
def new
@plan = Plan.new
authorize @plan
org_id = current_user.org&.id

# Get all of the available funders and non-funder orgs
@funders = Org.funder
.includes(identifiers: :identifier_scheme)
.joins(:templates)
.where(templates: { published: true }).uniq.sort_by(&:name)
@orgs = (Org.includes(identifiers: :identifier_scheme).organisation +
Org.includes(identifiers: :identifier_scheme).institution +
Org.includes(identifiers: :identifier_scheme).default_orgs)
@orgs = @orgs.flatten.uniq.sort_by(&:name)

@plan.org_id = current_user.org&.id

# TODO: is this still used? We cannot switch this to use the :plan_params
# strong params because any calls that do not include `plan` in the
# query string will fail
flash[:notice] = "#{_('This is a')} <strong>#{_('test plan')}</strong>" if params.key?(:test)
@is_test = params[:test] ||= false
# Get templates grouped hash
@templates_grouped = templates_available_to_org_user(org_id)
respond_to :html
end
# rubocop:enable Metrics/AbcSize

# POST /plans
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
Expand All @@ -71,6 +55,15 @@ def create
format.html { redirect_to new_plan_path }
end
else
template_id = plan_params[:template_id].to_i
unless validate_template_available_to_org_user?(template_id, current_user.org_id)
respond_to do |format|
flash[:alert] = _('The selected template is not available to your organisation.')
format.html { redirect_to new_plan_path }
end
return
end

@plan.visibility = if plan_params['visibility'].blank?
Rails.configuration.x.plans.default_visibility
else
Expand Down Expand Up @@ -537,5 +530,67 @@ def render_phases_edit(plan, phase, guidance_groups)
guidance_presenter: GuidancePresenter.new(plan)
})
end

# Get templates available to org users
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
def templates_available_to_org_user(org_id)
# looks into the list of families of templates
customizations = Template.latest_customized_version_per_org(org_id)
customization_ids = customizations.select(&:published?).collect(&:customization_of)

# get templates of user's own org
user_org_own_templates = Template.organisationally_visible
.where(org_id: org_id, customization_of: nil)
.published
.uniq.sort_by(&:title)

# get templates of user's customised org
user_org_custom_templates = Template.latest_customized_version_per_org(org_id)
.published
.uniq.sort_by(&:title)

# get funder templates no customised templates
funder_non_customised_templates = Template.published
.joins(:org)
.where(orgs: { org_type: Org.org_type_values_for(:funder) })
# The next line removes templates that belong to a family that
# has customised templates
.where.not(family_id: customization_ids)
.uniq.sort_by(&:title)

# get global templates
global_templates = Template.published
.where(is_default: true)
# The next line removes templates that belong to a family that
# has customised templates
.where.not(family_id: customization_ids)
.uniq.sort_by(&:title)

# create templates-grouped hash
@templates_grouped = {
_("Your Organisation's Templates:") => user_org_own_templates.map do |t|
[t.title, t.id]
end,
_("Your Organisation's Customised Templates:") => user_org_custom_templates.map do |t|
[t.title, t.id]
end,
_('Global Templates:') => global_templates.map do |t|
[t.title, t.id]
end,
_('Funder Templates:') => funder_non_customised_templates.map do |t|
[t.title, t.id]
end
}.reject { |_, val| val.empty? }
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

# Validate that a template_id is available to the org user
def validate_template_available_to_org_user?(template_id, org_id)
return false if template_id.blank? || org_id.blank?

available_templates = templates_available_to_org_user(org_id)
available_template_ids = available_templates.values.flat_map { |group| group.map(&:last) }
available_template_ids.include?(template_id.to_i)
end
end
# rubocop:enable Metrics/ClassLength
Loading