Add basic SDG Management content section
Note using `params[:relatable_type].classify` is recognized as a security risk by some tools. However, it's a false positive, since we've added constraints to the URL so that paramenter can only have the values we trust.
This commit is contained in:
@@ -36,7 +36,8 @@
|
||||
}
|
||||
|
||||
&.budgets-link,
|
||||
&.investments-link {
|
||||
&.investments-link,
|
||||
&.budget-investments-link {
|
||||
@include icon(chart-pie, solid);
|
||||
}
|
||||
|
||||
@@ -56,7 +57,8 @@
|
||||
@include icon(envelope, regular);
|
||||
}
|
||||
|
||||
&.legislation-link {
|
||||
&.legislation-link,
|
||||
&.legislation-processes-link {
|
||||
@include icon(file-alt, solid);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,37 @@ class SDGManagement::MenuComponent < ApplicationComponent
|
||||
private
|
||||
|
||||
def links
|
||||
[goals_link]
|
||||
[goals_link, *relatable_links]
|
||||
end
|
||||
|
||||
def goals_link
|
||||
[t("sdg_management.menu.sdg_content"), sdg_management_goals_path, sdg?, class: "goals-link"]
|
||||
end
|
||||
|
||||
def relatable_links
|
||||
SDG::Related::RELATABLE_TYPES.map do |type|
|
||||
[
|
||||
t("sdg_management.menu.#{table_name(type)}"),
|
||||
relatable_type_path(type),
|
||||
controller_name == "relations" && params[:relatable_type] == type.tableize,
|
||||
class: "#{table_name(type).tr("_", "-")}-link"
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def sdg?
|
||||
%w[goals targets local_targets].include?(controller_name)
|
||||
end
|
||||
|
||||
def relatable_type_path(type)
|
||||
{
|
||||
controller: "sdg_management/relations",
|
||||
action: :index,
|
||||
relatable_type: type.tableize
|
||||
}
|
||||
end
|
||||
|
||||
def table_name(type)
|
||||
type.constantize.table_name
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<%= header %>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= model_class.human_attribute_name(:title) %></th>
|
||||
<th><%= SDG::Goal.model_name.human(count: 2).upcase_first %></th>
|
||||
<th><%= SDG::Target.model_name.human(count: 2).upcase_first %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @records.each do |record| %>
|
||||
<tr>
|
||||
<td><%= record.title %></td>
|
||||
<td><%= record.sdg_goal_list %></td>
|
||||
<td><%= record.sdg_target_list %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<%= paginate(@records) %>
|
||||
19
app/components/sdg_management/relations/index_component.rb
Normal file
19
app/components/sdg_management/relations/index_component.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
class SDGManagement::Relations::IndexComponent < ApplicationComponent
|
||||
include Header
|
||||
|
||||
attr_reader :records
|
||||
|
||||
def initialize(records)
|
||||
@records = records
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def title
|
||||
t("sdg_management.menu.#{model_class.table_name}")
|
||||
end
|
||||
|
||||
def model_class
|
||||
records.model
|
||||
end
|
||||
end
|
||||
11
app/controllers/sdg_management/relations_controller.rb
Normal file
11
app/controllers/sdg_management/relations_controller.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class SDGManagement::RelationsController < SDGManagement::BaseController
|
||||
def index
|
||||
@records = relatable_class.accessible_by(current_ability).order(:id).page(params[:page])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def relatable_class
|
||||
params[:relatable_type].classify.constantize
|
||||
end
|
||||
end
|
||||
@@ -15,4 +15,12 @@ module SDG::Relatable
|
||||
def related_sdgs
|
||||
sdg_relations.map(&:related_sdg)
|
||||
end
|
||||
|
||||
def sdg_goal_list
|
||||
sdg_goals.order(:code).map(&:code).join(", ")
|
||||
end
|
||||
|
||||
def sdg_target_list
|
||||
sdg_targets.sort.map(&:code).join(", ")
|
||||
end
|
||||
end
|
||||
|
||||
1
app/views/sdg_management/relations/index.html.erb
Normal file
1
app/views/sdg_management/relations/index.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= render SDGManagement::Relations::IndexComponent.new(@records) %>
|
||||
@@ -287,6 +287,7 @@ en:
|
||||
responsible_name: "Person responsible for the group"
|
||||
poll:
|
||||
name: "Name"
|
||||
title: "Name"
|
||||
starts_at: "Start Date"
|
||||
ends_at: "Closing Date"
|
||||
geozone_restricted: "Restricted by geozone"
|
||||
|
||||
@@ -3,6 +3,11 @@ en:
|
||||
header:
|
||||
title: "SDG content"
|
||||
menu:
|
||||
budget_investments: "Participatory budgets"
|
||||
debates: "Debates"
|
||||
legislation_processes: "Collaborative legislation"
|
||||
polls: "Polls"
|
||||
proposals: "Proposals"
|
||||
sdg_content: "Goals and Targets"
|
||||
local_targets:
|
||||
create:
|
||||
|
||||
@@ -287,6 +287,7 @@ es:
|
||||
responsible_name: "Persona responsable del colectivo"
|
||||
poll:
|
||||
name: "Nombre"
|
||||
title: "Nombre"
|
||||
starts_at: "Fecha de apertura"
|
||||
ends_at: "Fecha de cierre"
|
||||
geozone_restricted: "Restringida por zonas"
|
||||
|
||||
@@ -3,6 +3,11 @@ es:
|
||||
header:
|
||||
title: "Contenido ODS"
|
||||
menu:
|
||||
budget_investments: "Presupuestos participativos"
|
||||
debates: "Debates"
|
||||
legislation_processes: "Legislación colaborativa"
|
||||
polls: "Votaciones"
|
||||
proposals: "Propuestas"
|
||||
sdg_content: "Objetivos y Metas"
|
||||
local_targets:
|
||||
create:
|
||||
|
||||
@@ -4,4 +4,13 @@ namespace :sdg_management do
|
||||
resources :goals, only: [:index]
|
||||
resources :targets, only: [:index]
|
||||
resources :local_targets, except: [:show]
|
||||
|
||||
types = SDG::Related::RELATABLE_TYPES.map(&:tableize)
|
||||
types_constraint = /#{types.join("|")}/
|
||||
|
||||
get "*relatable_type", to: "relations#index", as: "relations", relatable_type: types_constraint
|
||||
|
||||
types.each do |type|
|
||||
get type, to: "relations#index", as: type
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,6 +28,14 @@ describe SDG::Relatable do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#sdg_goal_list" do
|
||||
it "orders goals by code" do
|
||||
relatable.sdg_goals = [SDG::Goal[1], SDG::Goal[3], SDG::Goal[2]]
|
||||
|
||||
expect(relatable.sdg_goal_list).to eq "1, 2, 3"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#sdg_targets" do
|
||||
it "can assign targets to a model" do
|
||||
relatable.sdg_targets = [target, another_target]
|
||||
@@ -46,6 +54,14 @@ describe SDG::Relatable do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#sdg_target_list" do
|
||||
it "orders targets by code" do
|
||||
relatable.sdg_targets = [SDG::Target[2.2], SDG::Target[1.2], SDG::Target[2.1]]
|
||||
|
||||
expect(relatable.sdg_target_list).to eq "1.2, 2.1, 2.2"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#sdg_local_targets" do
|
||||
it "can assign local targets to a model" do
|
||||
relatable.sdg_local_targets = [local_target, another_local_target]
|
||||
|
||||
32
spec/routing/sdg_management_routes_spec.rb
Normal file
32
spec/routing/sdg_management_routes_spec.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe "SDG Management routes" do
|
||||
it "maps routes for relatable classes" do
|
||||
expect(get("/sdg_management/proposals")).to route_to(
|
||||
controller: "sdg_management/relations",
|
||||
action: "index",
|
||||
relatable_type: "proposals"
|
||||
)
|
||||
end
|
||||
|
||||
it "admits named routes" do
|
||||
expect(get(sdg_management_polls_path)).to route_to(
|
||||
controller: "sdg_management/relations",
|
||||
action: "index",
|
||||
relatable_type: "polls"
|
||||
)
|
||||
end
|
||||
|
||||
it "routes relatable types containing a slash" do
|
||||
expect(url_for(
|
||||
controller: "sdg_management/relations",
|
||||
action: "index",
|
||||
relatable_type: "legislation/processes",
|
||||
only_path: true
|
||||
)).to eq "/sdg_management/legislation/processes"
|
||||
end
|
||||
|
||||
it "does not accept non-relatable classes" do
|
||||
expect(get("/sdg_management/tags")).not_to be_routable
|
||||
end
|
||||
end
|
||||
74
spec/system/sdg_management/relations_spec.rb
Normal file
74
spec/system/sdg_management/relations_spec.rb
Normal file
@@ -0,0 +1,74 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe "SDG Relations", :js do
|
||||
before do
|
||||
login_as(create(:administrator).user)
|
||||
Setting["feature.sdg"] = true
|
||||
end
|
||||
|
||||
scenario "navigation" do
|
||||
visit sdg_management_root_path
|
||||
|
||||
within("#side_menu") { click_link "Participatory budgets" }
|
||||
|
||||
expect(page).to have_current_path "/sdg_management/budget/investments"
|
||||
expect(page).to have_css "h2", exact_text: "Participatory budgets"
|
||||
|
||||
within("#side_menu") { click_link "Debates" }
|
||||
|
||||
expect(page).to have_current_path "/sdg_management/debates"
|
||||
expect(page).to have_css "h2", exact_text: "Debates"
|
||||
|
||||
within("#side_menu") { click_link "Collaborative legislation" }
|
||||
|
||||
expect(page).to have_current_path "/sdg_management/legislation/processes"
|
||||
expect(page).to have_css "h2", exact_text: "Collaborative legislation"
|
||||
|
||||
within("#side_menu") { click_link "Polls" }
|
||||
|
||||
expect(page).to have_current_path "/sdg_management/polls"
|
||||
expect(page).to have_css "h2", exact_text: "Polls"
|
||||
|
||||
within("#side_menu") { click_link "Proposals" }
|
||||
|
||||
expect(page).to have_current_path "/sdg_management/proposals"
|
||||
expect(page).to have_css "h2", exact_text: "Proposals"
|
||||
end
|
||||
|
||||
describe "Index" do
|
||||
scenario "list records for the current model" do
|
||||
create(:debate, title: "I'm a debate")
|
||||
create(:proposal, title: "And I'm a proposal")
|
||||
|
||||
visit sdg_management_debates_path
|
||||
|
||||
expect(page).to have_text "I'm a debate"
|
||||
expect(page).not_to have_text "I'm a proposal"
|
||||
|
||||
visit sdg_management_proposals_path
|
||||
|
||||
expect(page).to have_text "I'm a proposal"
|
||||
expect(page).not_to have_text "I'm a debate"
|
||||
end
|
||||
|
||||
scenario "list goals and target for all records" do
|
||||
redistribute = create(:proposal, title: "Resources distribution")
|
||||
redistribute.sdg_goals = [SDG::Goal[1]]
|
||||
redistribute.sdg_targets = [SDG::Target["1.1"]]
|
||||
|
||||
treatment = create(:proposal, title: "Treatment system")
|
||||
treatment.sdg_goals = [SDG::Goal[6]]
|
||||
treatment.sdg_targets = [SDG::Target["6.1"], SDG::Target["6.2"]]
|
||||
|
||||
visit sdg_management_proposals_path
|
||||
|
||||
within("tr", text: "Resources distribution") do
|
||||
expect(page).to have_content "1.1"
|
||||
end
|
||||
|
||||
within("tr", text: "Treatment system") do
|
||||
expect(page).to have_content "6.1, 6.2"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user