decidim-challenges 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE-AGPLv3.txt +661 -0
- data/README.md +49 -0
- data/Rakefile +9 -0
- data/app/cells/decidim/challenges/challenge_cell.rb +21 -0
- data/app/cells/decidim/challenges/challenge_g/show.erb +47 -0
- data/app/cells/decidim/challenges/challenge_g_cell.rb +69 -0
- data/app/cells/decidim/problems/problem_cell.rb +21 -0
- data/app/cells/decidim/problems/problem_g/show.erb +35 -0
- data/app/cells/decidim/problems/problem_g_cell.rb +61 -0
- data/app/cells/decidim/solutions/solution_cell.rb +21 -0
- data/app/cells/decidim/solutions/solution_g/show.erb +30 -0
- data/app/cells/decidim/solutions/solution_g_cell.rb +84 -0
- data/app/commands/decidim/challenges/admin/create_challenge.rb +67 -0
- data/app/commands/decidim/challenges/admin/destroy_challenge.rb +49 -0
- data/app/commands/decidim/challenges/admin/export_challenge_surveys.rb +45 -0
- data/app/commands/decidim/challenges/admin/publish_challenge.rb +40 -0
- data/app/commands/decidim/challenges/admin/unpublish_challenge.rb +40 -0
- data/app/commands/decidim/challenges/admin/update_challenge.rb +72 -0
- data/app/commands/decidim/challenges/admin/update_surveys.rb +43 -0
- data/app/commands/decidim/challenges/survey_challenge.rb +67 -0
- data/app/commands/decidim/command_utils.rb +20 -0
- data/app/commands/decidim/problems/admin/create_problem.rb +66 -0
- data/app/commands/decidim/problems/admin/destroy_problem.rb +49 -0
- data/app/commands/decidim/problems/admin/publish_problem.rb +40 -0
- data/app/commands/decidim/problems/admin/unpublish_problem.rb +40 -0
- data/app/commands/decidim/problems/admin/update_problem.rb +69 -0
- data/app/commands/decidim/solutions/admin/create_solution.rb +65 -0
- data/app/commands/decidim/solutions/admin/destroy_solution.rb +43 -0
- data/app/commands/decidim/solutions/admin/publish_solution.rb +40 -0
- data/app/commands/decidim/solutions/admin/unpublish_solution.rb +40 -0
- data/app/commands/decidim/solutions/admin/update_solution.rb +67 -0
- data/app/commands/decidim/solutions/create_solution.rb +67 -0
- data/app/controllers/concerns/decidim/challenges/admin/filterable.rb +27 -0
- data/app/controllers/concerns/decidim/challenges/orderable_challenges.rb +37 -0
- data/app/controllers/concerns/decidim/problems/admin/filterable.rb +27 -0
- data/app/controllers/concerns/decidim/problems/orderable_problems.rb +37 -0
- data/app/controllers/concerns/decidim/solutions/admin/filterable.rb +27 -0
- data/app/controllers/concerns/decidim/solutions/orderable_solutions.rb +37 -0
- data/app/controllers/concerns/decidim/with_default_filters.rb +23 -0
- data/app/controllers/concerns/decidim/with_sdgs.rb +19 -0
- data/app/controllers/decidim/challenges/admin/application_controller.rb +15 -0
- data/app/controllers/decidim/challenges/admin/challenge_publications_controller.rb +53 -0
- data/app/controllers/decidim/challenges/admin/challenges_controller.rb +108 -0
- data/app/controllers/decidim/challenges/admin/survey_form_controller.rb +43 -0
- data/app/controllers/decidim/challenges/admin/surveys_controller.rb +104 -0
- data/app/controllers/decidim/challenges/application_controller.rb +13 -0
- data/app/controllers/decidim/challenges/challenges_controller.rb +70 -0
- data/app/controllers/decidim/challenges/surveys_controller.rb +61 -0
- data/app/controllers/decidim/problems/admin/application_controller.rb +15 -0
- data/app/controllers/decidim/problems/admin/problem_publications_controller.rb +53 -0
- data/app/controllers/decidim/problems/admin/problems_controller.rb +102 -0
- data/app/controllers/decidim/problems/application_controller.rb +13 -0
- data/app/controllers/decidim/problems/problems_controller.rb +62 -0
- data/app/controllers/decidim/sdgs/admin/application_controller.rb +15 -0
- data/app/controllers/decidim/sdgs/admin/sdgs_controller.rb +18 -0
- data/app/controllers/decidim/sdgs/application_controller.rb +13 -0
- data/app/controllers/decidim/sdgs/sdgs_controller.rb +24 -0
- data/app/controllers/decidim/solutions/admin/application_controller.rb +15 -0
- data/app/controllers/decidim/solutions/admin/solution_publications_controller.rb +53 -0
- data/app/controllers/decidim/solutions/admin/solutions_controller.rb +106 -0
- data/app/controllers/decidim/solutions/application_controller.rb +13 -0
- data/app/controllers/decidim/solutions/solutions_controller.rb +115 -0
- data/app/forms/decidim/challenges/admin/challenge_surveys_form.rb +35 -0
- data/app/forms/decidim/challenges/admin/challenges_form.rb +78 -0
- data/app/forms/decidim/problems/admin/problems_form.rb +90 -0
- data/app/forms/decidim/solutions/admin/solutions_form.rb +71 -0
- data/app/forms/decidim/solutions/solutions_form.rb +50 -0
- data/app/helpers/decidim/challenges/application_helper.rb +25 -0
- data/app/helpers/decidim/challenges/challenge_cells_helper.rb +57 -0
- data/app/helpers/decidim/challenges/challenges_helper.rb +39 -0
- data/app/helpers/decidim/problems/application_helper.rb +10 -0
- data/app/helpers/decidim/problems/problem_cells_helper.rb +23 -0
- data/app/helpers/decidim/problems/problems_helper.rb +24 -0
- data/app/helpers/decidim/sdgs/sdgs_helper.rb +28 -0
- data/app/helpers/decidim/show_filters_helper.rb +13 -0
- data/app/helpers/decidim/solutions/application_helper.rb +27 -0
- data/app/helpers/decidim/solutions/solution_cells_helper.rb +56 -0
- data/app/helpers/decidim/solutions/solutions_helper.rb +21 -0
- data/app/models/decidim/application_record.rb +8 -0
- data/app/models/decidim/challenges/challenge.rb +82 -0
- data/app/models/decidim/challenges/survey.rb +19 -0
- data/app/models/decidim/problems/problem.rb +118 -0
- data/app/models/decidim/sdgs/sdg.rb +47 -0
- data/app/models/decidim/solutions/application_record.rb +10 -0
- data/app/models/decidim/solutions/solution.rb +69 -0
- data/app/packs/entrypoints/decidim_challenges.js +4 -0
- data/app/packs/entrypoints/decidim_challenges.scss +1 -0
- data/app/packs/entrypoints/decidim_problems.js +1 -0
- data/app/packs/entrypoints/decidim_problems.scss +1 -0
- data/app/packs/entrypoints/decidim_sdgs.js +6 -0
- data/app/packs/entrypoints/decidim_sdgs.scss +3 -0
- data/app/packs/entrypoints/decidim_shared.js +1 -0
- data/app/packs/entrypoints/decidim_shared.scss +1 -0
- data/app/packs/entrypoints/decidim_solutions.js +4 -0
- data/app/packs/entrypoints/decidim_solutions.scss +1 -0
- data/app/packs/fonts/Oswald-Medium.ttf +0 -0
- data/app/packs/images/decidim/challenges/decidim_challenges_icon.svg +1 -0
- data/app/packs/images/decidim/sdgs/ods-01.svg +11 -0
- data/app/packs/images/decidim/sdgs/ods-02.svg +11 -0
- data/app/packs/images/decidim/sdgs/ods-03.svg +11 -0
- data/app/packs/images/decidim/sdgs/ods-04.svg +12 -0
- data/app/packs/images/decidim/sdgs/ods-05.svg +12 -0
- data/app/packs/images/decidim/sdgs/ods-06.svg +20 -0
- data/app/packs/images/decidim/sdgs/ods-07.svg +18 -0
- data/app/packs/images/decidim/sdgs/ods-08.svg +13 -0
- data/app/packs/images/decidim/sdgs/ods-09.svg +25 -0
- data/app/packs/images/decidim/sdgs/ods-10.svg +13 -0
- data/app/packs/images/decidim/sdgs/ods-11.svg +41 -0
- data/app/packs/images/decidim/sdgs/ods-12.svg +11 -0
- data/app/packs/images/decidim/sdgs/ods-13.svg +57 -0
- data/app/packs/images/decidim/sdgs/ods-14.svg +14 -0
- data/app/packs/images/decidim/sdgs/ods-15.svg +11 -0
- data/app/packs/images/decidim/sdgs/ods-16.svg +14 -0
- data/app/packs/images/decidim/sdgs/ods-17.svg +39 -0
- data/app/packs/images/decidim/sdgs/sdg-wheel.png +0 -0
- data/app/packs/images/decidim/sdgs/un_emblem_square.png +0 -0
- data/app/packs/src/decidim/challenges/add_challenge.js +3 -0
- data/app/packs/src/decidim/challenges/admin/challenges_form.js +3 -0
- data/app/packs/src/decidim/sdgs/filter.js +75 -0
- data/app/packs/stylesheets/decidim/challenges/challenges.scss +3 -0
- data/app/packs/stylesheets/decidim/problems/problems.scss +3 -0
- data/app/packs/stylesheets/decidim/sdgs/ods.scss +185 -0
- data/app/packs/stylesheets/decidim/sdgs/sdgs_filter/button.scss +27 -0
- data/app/packs/stylesheets/decidim/sdgs/sdgs_filter/modal.scss +60 -0
- data/app/packs/stylesheets/decidim/shared/_cards.scss +52 -0
- data/app/packs/stylesheets/decidim/shared/base.scss +9 -0
- data/app/packs/stylesheets/decidim/solutions/solutions.scss +3 -0
- data/app/permissions/decidim/challenges/admin/permissions.rb +88 -0
- data/app/permissions/decidim/challenges/permissions.rb +27 -0
- data/app/permissions/decidim/problems/admin/permissions.rb +52 -0
- data/app/permissions/decidim/problems/permissions.rb +16 -0
- data/app/permissions/decidim/solutions/admin/permissions.rb +58 -0
- data/app/permissions/decidim/solutions/permissions.rb +17 -0
- data/app/presenters/decidim/challenges/challenge_presenter.rb +134 -0
- data/app/presenters/decidim/problems/problem_presenter.rb +108 -0
- data/app/presenters/decidim/solutions/solution_presenter.rb +198 -0
- data/app/serializers/decidim/challenges/data_serializer.rb +46 -0
- data/app/serializers/decidim/challenges/survey_serializer.rb +37 -0
- data/app/types/decidim/challenges/challenge_type.rb +28 -0
- data/app/types/decidim/challenges/challenges_type.rb +32 -0
- data/app/types/decidim/problems/problem_type.rb +29 -0
- data/app/types/decidim/problems/problems_type.rb +32 -0
- data/app/types/decidim/solutions/solution_type.rb +25 -0
- data/app/types/decidim/solutions/solutions_type.rb +32 -0
- data/app/views/decidim/challenges/admin/challenges/_form.html.erb +78 -0
- data/app/views/decidim/challenges/admin/challenges/edit.html.erb +24 -0
- data/app/views/decidim/challenges/admin/challenges/index.html.erb +88 -0
- data/app/views/decidim/challenges/admin/challenges/new.html.erb +18 -0
- data/app/views/decidim/challenges/admin/surveys/_form.html.erb +9 -0
- data/app/views/decidim/challenges/admin/surveys/edit.html.erb +39 -0
- data/app/views/decidim/challenges/admin/surveys/index.html.erb +49 -0
- data/app/views/decidim/challenges/admin/surveys/show.html.erb +43 -0
- data/app/views/decidim/challenges/challenges/_challenge.html.erb +1 -0
- data/app/views/decidim/challenges/challenges/_challenges.html.erb +11 -0
- data/app/views/decidim/challenges/challenges/_related_problems.html.erb +14 -0
- data/app/views/decidim/challenges/challenges/_sidebar_data.html.erb +60 -0
- data/app/views/decidim/challenges/challenges/index.html.erb +29 -0
- data/app/views/decidim/challenges/challenges/index.js.erb +5 -0
- data/app/views/decidim/challenges/challenges/show.html.erb +41 -0
- data/app/views/decidim/problems/admin/problems/_form.html.erb +79 -0
- data/app/views/decidim/problems/admin/problems/edit.html.erb +24 -0
- data/app/views/decidim/problems/admin/problems/index.html.erb +93 -0
- data/app/views/decidim/problems/admin/problems/new.html.erb +18 -0
- data/app/views/decidim/problems/problems/_problem.html.erb +1 -0
- data/app/views/decidim/problems/problems/_problems.html.erb +11 -0
- data/app/views/decidim/problems/problems/_sidebar_data.html.erb +70 -0
- data/app/views/decidim/problems/problems/index.html.erb +28 -0
- data/app/views/decidim/problems/problems/index.js.erb +5 -0
- data/app/views/decidim/problems/problems/show.html.erb +38 -0
- data/app/views/decidim/sdgs/sdgs/_objectives.html.erb +320 -0
- data/app/views/decidim/sdgs/sdgs/_ods.html.erb +132 -0
- data/app/views/decidim/sdgs/sdgs/index.html.erb +64 -0
- data/app/views/decidim/sdgs/sdgs_filter/_filter_selector.html.erb +7 -0
- data/app/views/decidim/sdgs/sdgs_filter/_modal.html.erb +35 -0
- data/app/views/decidim/shared/_related_solutions.html.erb +14 -0
- data/app/views/decidim/solutions/admin/solutions/_documents.html.erb +13 -0
- data/app/views/decidim/solutions/admin/solutions/_form.html.erb +70 -0
- data/app/views/decidim/solutions/admin/solutions/_photos.html.erb +13 -0
- data/app/views/decidim/solutions/admin/solutions/edit.html.erb +24 -0
- data/app/views/decidim/solutions/admin/solutions/index.html.erb +97 -0
- data/app/views/decidim/solutions/admin/solutions/new.html.erb +18 -0
- data/app/views/decidim/solutions/admin/solutions/show.html.erb +114 -0
- data/app/views/decidim/solutions/solutions/_form.html.erb +22 -0
- data/app/views/decidim/solutions/solutions/_sidebar_data.html.erb +114 -0
- data/app/views/decidim/solutions/solutions/_solution.html.erb +1 -0
- data/app/views/decidim/solutions/solutions/_solutions.html.erb +13 -0
- data/app/views/decidim/solutions/solutions/index.html.erb +37 -0
- data/app/views/decidim/solutions/solutions/index.js.erb +5 -0
- data/app/views/decidim/solutions/solutions/new.html.erb +20 -0
- data/app/views/decidim/solutions/solutions/show.html.erb +72 -0
- data/config/assets.rb +27 -0
- data/config/i18n-tasks.yml +10 -0
- data/config/locales/ca.yml +1244 -0
- data/config/locales/cs.yml +668 -0
- data/config/locales/en.yml +1245 -0
- data/config/locales/es.yml +1250 -0
- data/config/locales/oc.yml +1229 -0
- data/db/migrate/20201014125000_create_decidim_challenges_challenges.rb +23 -0
- data/db/migrate/20201103155100_create_decidim_problems_problems.rb +25 -0
- data/db/migrate/20201116111200_create_decidim_solutions_solutions.rb +21 -0
- data/db/migrate/20210413083244_create_decidim_challenges_surveys.rb +14 -0
- data/db/migrate/20210413083507_add_survey_attributes_to_challenges.rb +7 -0
- data/db/migrate/20210413083604_add_questionnaire_to_existing_challenges.rb +15 -0
- data/db/migrate/20210413112229_add_challenge_reference_to_solutions.rb +7 -0
- data/db/migrate/20210427091033_remove_require_null_in_problems_for_solutions.rb +7 -0
- data/db/migrate/20220407110503_add_card_image_to_challenges.rb +7 -0
- data/db/migrate/20240919094714_add_public_fields_to_solutions.rb +10 -0
- data/lib/decidim/challenges/admin.rb +10 -0
- data/lib/decidim/challenges/admin_engine.rb +37 -0
- data/lib/decidim/challenges/component.rb +65 -0
- data/lib/decidim/challenges/engine.rb +39 -0
- data/lib/decidim/challenges/test/factories.rb +59 -0
- data/lib/decidim/challenges/version.rb +14 -0
- data/lib/decidim/challenges.rb +13 -0
- data/lib/decidim/problems/admin.rb +10 -0
- data/lib/decidim/problems/admin_engine.rb +25 -0
- data/lib/decidim/problems/component.rb +51 -0
- data/lib/decidim/problems/engine.rb +31 -0
- data/lib/decidim/problems/test/factories.rb +39 -0
- data/lib/decidim/sdgs/admin.rb +10 -0
- data/lib/decidim/sdgs/admin_engine.rb +28 -0
- data/lib/decidim/sdgs/component.rb +44 -0
- data/lib/decidim/sdgs/engine.rb +23 -0
- data/lib/decidim/sdgs/test/factories.rb +13 -0
- data/lib/decidim/solutions/admin.rb +10 -0
- data/lib/decidim/solutions/admin_engine.rb +25 -0
- data/lib/decidim/solutions/component.rb +52 -0
- data/lib/decidim/solutions/engine.rb +31 -0
- data/lib/decidim/solutions/test/factories.rb +28 -0
- metadata +316 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Solutions
|
5
|
+
module Admin
|
6
|
+
# Controller that allows managing admin problems.
|
7
|
+
#
|
8
|
+
class SolutionsController < Decidim::Solutions::Admin::ApplicationController
|
9
|
+
include Decidim::ApplicationHelper
|
10
|
+
|
11
|
+
helper Challenges::ApplicationHelper
|
12
|
+
helper Decidim::PaginateHelper
|
13
|
+
|
14
|
+
helper_method :solution, :solution, :form_presenter
|
15
|
+
|
16
|
+
def index
|
17
|
+
enforce_permission_to :read, :solutions
|
18
|
+
@solutions = solutions
|
19
|
+
end
|
20
|
+
|
21
|
+
def show
|
22
|
+
enforce_permission_to :show, :solution
|
23
|
+
@solution = Decidim::Solutions::Solution.find(params[:id])
|
24
|
+
end
|
25
|
+
|
26
|
+
def new
|
27
|
+
enforce_permission_to :create, :solution
|
28
|
+
@form = form(Decidim::Solutions::Admin::SolutionsForm).instance
|
29
|
+
end
|
30
|
+
|
31
|
+
def edit
|
32
|
+
enforce_permission_to(:edit, :solution, solution:)
|
33
|
+
@form = form(Decidim::Solutions::Admin::SolutionsForm).from_model(solution)
|
34
|
+
end
|
35
|
+
|
36
|
+
def create
|
37
|
+
enforce_permission_to :create, :solution
|
38
|
+
@form = form(Decidim::Solutions::Admin::SolutionsForm).from_params(params.merge({ author_id: current_user.id }))
|
39
|
+
|
40
|
+
Decidim::Solutions::Admin::CreateSolution.call(@form) do
|
41
|
+
on(:ok) do
|
42
|
+
flash[:notice] = I18n.t("solutions.create.success", scope: "decidim.solutions.admin")
|
43
|
+
redirect_to solutions_path(assembly_slug: -1, component_id: -1)
|
44
|
+
end
|
45
|
+
|
46
|
+
on(:invalid) do
|
47
|
+
flash.now[:alert] = I18n.t("solutions.create.error", scope: "decidim.solutions.admin")
|
48
|
+
render action: "new"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def update
|
54
|
+
enforce_permission_to(:edit, :solution, solution:)
|
55
|
+
@form = form(Decidim::Solutions::Admin::SolutionsForm).from_params(params.merge({ author_id: current_user.id }))
|
56
|
+
|
57
|
+
Decidim::Solutions::Admin::UpdateSolution.call(@form, solution) do
|
58
|
+
on(:ok) do |_solution|
|
59
|
+
flash[:notice] = t("solutions.update.success", scope: "decidim.solutions.admin")
|
60
|
+
redirect_to solutions_path(assembly_slug: -1, component_id: -1)
|
61
|
+
end
|
62
|
+
|
63
|
+
on(:invalid) do
|
64
|
+
flash.now[:alert] = t("solutions.update.error", scope: "decidim.solutions.admin")
|
65
|
+
render :edit
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def destroy
|
71
|
+
enforce_permission_to(:destroy, :solution, solution:)
|
72
|
+
|
73
|
+
Decidim::Solutions::Admin::DestroySolution.call(solution, current_user) do
|
74
|
+
on(:ok) do
|
75
|
+
flash[:notice] = I18n.t("solutions.destroy.success", scope: "decidim.solutions.admin")
|
76
|
+
redirect_to solutions_path(assembly_slug: -1, component_id: -1)
|
77
|
+
end
|
78
|
+
|
79
|
+
on(:invalid) do
|
80
|
+
flash.now[:alert] = t("solutions.destroy.error", scope: "decidim.solutions.admin")
|
81
|
+
redirect_to solutions_path(assembly_slug: -1, component_id: -1)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def collection
|
89
|
+
@collection ||= Solution.where(component: current_component)
|
90
|
+
end
|
91
|
+
|
92
|
+
def solutions
|
93
|
+
@solutions ||= collection.page(params[:page]).per(10)
|
94
|
+
end
|
95
|
+
|
96
|
+
def solution
|
97
|
+
@solution ||= collection.find(params[:id])
|
98
|
+
end
|
99
|
+
|
100
|
+
def form_presenter
|
101
|
+
@form_presenter ||= present(@form, presenter_class: Decidim::Solutions::SolutionPresenter)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Solutions
|
5
|
+
# This controller is the abstract class from which all other controllers of
|
6
|
+
# this engine inherit.
|
7
|
+
#
|
8
|
+
# Note that it inherits from `Decidim::Components::BaseController`, which
|
9
|
+
# override its layout and provide all kinds of useful methods.
|
10
|
+
class ApplicationController < Decidim::Components::BaseController
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Solutions
|
5
|
+
# Controller that allows browsing solutions.
|
6
|
+
#
|
7
|
+
class SolutionsController < Decidim::Solutions::ApplicationController
|
8
|
+
include Decidim::ApplicationHelper
|
9
|
+
include FilterResource
|
10
|
+
include Paginable
|
11
|
+
include OrderableSolutions
|
12
|
+
include FormFactory
|
13
|
+
include WithSdgs
|
14
|
+
include WithDefaultFilters
|
15
|
+
|
16
|
+
helper Decidim::CheckBoxesTreeHelper
|
17
|
+
helper Decidim::Sdgs::SdgsHelper
|
18
|
+
helper Decidim::ShowFiltersHelper
|
19
|
+
helper SolutionsHelper
|
20
|
+
helper Decidim::Challenges::ApplicationHelper
|
21
|
+
|
22
|
+
helper_method :solutions, :form_presenter, :has_sdgs?, :has_problem?, :default_filter_scope_params
|
23
|
+
|
24
|
+
def index
|
25
|
+
@solutions = search.result
|
26
|
+
@solutions = reorder(@solutions)
|
27
|
+
@solutions = paginate(@solutions)
|
28
|
+
end
|
29
|
+
|
30
|
+
def show
|
31
|
+
@solution = solution
|
32
|
+
if @solution.problem.present?
|
33
|
+
@sectorial_scope = sectorial_scope
|
34
|
+
@technological_scope = technological_scope
|
35
|
+
end
|
36
|
+
@sdg_index = sdg_index if @solution.problem.present? || @solution.challenge.present?
|
37
|
+
@challenge_scope = challenge_scope
|
38
|
+
end
|
39
|
+
|
40
|
+
def new
|
41
|
+
enforce_permission_to :create, :solution
|
42
|
+
@form = form(Decidim::Solutions::SolutionsForm).instance
|
43
|
+
end
|
44
|
+
|
45
|
+
def create
|
46
|
+
enforce_permission_to :create, :solution
|
47
|
+
@form = form(Decidim::Solutions::SolutionsForm).from_params(params.merge({ author_id: current_user.id }))
|
48
|
+
|
49
|
+
Decidim::Solutions::CreateSolution.call(@form) do
|
50
|
+
on(:ok) do
|
51
|
+
flash[:notice] = I18n.t("solutions.create.success", scope: "decidim.solutions")
|
52
|
+
redirect_to solutions_path
|
53
|
+
end
|
54
|
+
|
55
|
+
on(:invalid) do
|
56
|
+
flash.now[:alert] = I18n.t("solutions.create.error", scope: "decidim.solutions")
|
57
|
+
render action: "new"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def default_filter_params
|
65
|
+
{
|
66
|
+
search_text_cont: "",
|
67
|
+
with_any_territorial_scope: default_filter_scope_params,
|
68
|
+
with_any_sdgs_codes: [],
|
69
|
+
related_to: "",
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def solutions
|
74
|
+
@solutions ||= reorder(paginate(search.result))
|
75
|
+
end
|
76
|
+
|
77
|
+
def solution
|
78
|
+
@solution ||= Solution.find(params[:id])
|
79
|
+
end
|
80
|
+
|
81
|
+
def sdg_index
|
82
|
+
challenge = @solution.problem ? @solution.problem.challenge : @solution.challenge
|
83
|
+
@sdg_index ||= challenge.sdg_code ? (1 + Decidim::Sdgs::Sdg.index_from_code(challenge.sdg_code.to_sym)).to_s.rjust(2, "0") : nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def challenge_scope
|
87
|
+
@challenge_scope ||= if @solution.problem.present?
|
88
|
+
current_organization.scopes.find_by(id: @solution.problem.challenge.decidim_scope_id)
|
89
|
+
else
|
90
|
+
current_organization.scopes.find_by(id: @solution.challenge&.decidim_scope_id)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def sectorial_scope
|
95
|
+
@sectorial_scope ||= current_organization.scopes.find_by(id: @solution.problem.decidim_sectorial_scope_id)
|
96
|
+
end
|
97
|
+
|
98
|
+
def technological_scope
|
99
|
+
@technological_scope ||= current_organization.scopes.find_by(id: @solution.problem.decidim_technological_scope_id)
|
100
|
+
end
|
101
|
+
|
102
|
+
def search_collection
|
103
|
+
::Decidim::Solutions::Solution.where(component: current_component).published
|
104
|
+
end
|
105
|
+
|
106
|
+
def form_presenter
|
107
|
+
@form_presenter ||= present(@form, presenter_class: Decidim::Solutions::SolutionPresenter)
|
108
|
+
end
|
109
|
+
|
110
|
+
def has_problem?
|
111
|
+
current_participatory_space.components.where(manifest_name: "problems").present? && @solution.problem.present?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Challenges
|
5
|
+
module Admin
|
6
|
+
# This class holds a Form to update challenges surveys from Decidim's admin panel.
|
7
|
+
class ChallengeSurveysForm < Decidim::Form
|
8
|
+
mimic :challenge
|
9
|
+
|
10
|
+
attribute :survey_enabled, Boolean
|
11
|
+
|
12
|
+
# We need this method to ensure the form object will always have an ID,
|
13
|
+
# and thus its `to_param` method will always return a significant value.
|
14
|
+
# If we remove this method, get an error onn the `update` action and try
|
15
|
+
# to resubmit the form, the form will not hold an ID, so the `to_param`
|
16
|
+
# method will return an empty string and Rails will treat this as a
|
17
|
+
# `create` action, thus raising an error since this action is not defined
|
18
|
+
# for the controller we're using.
|
19
|
+
#
|
20
|
+
# TL;DR: if you remove this method, we'll get errors, so don't.
|
21
|
+
def id
|
22
|
+
return super if super.present?
|
23
|
+
|
24
|
+
challenge.id
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def challenge
|
30
|
+
@challenge ||= context[:challenge]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Challenges
|
5
|
+
module Admin
|
6
|
+
# A form object used to create challenges from the admin dashboard.
|
7
|
+
#
|
8
|
+
class ChallengesForm < Decidim::Form
|
9
|
+
include TranslatableAttributes
|
10
|
+
include Decidim::Sdgs::SdgsHelper
|
11
|
+
include Decidim::HasUploadValidations
|
12
|
+
|
13
|
+
mimic :challenge
|
14
|
+
|
15
|
+
translatable_attribute :title, String do |field, _locale|
|
16
|
+
validates field, length: { in: 5..150 }, if: proc { |resource| resource.send(field).present? }
|
17
|
+
end
|
18
|
+
translatable_attribute :local_description, String
|
19
|
+
translatable_attribute :global_description, String
|
20
|
+
|
21
|
+
attribute :decidim_component_id, Integer
|
22
|
+
attribute :decidim_scope_id, Integer
|
23
|
+
attribute :tags, String
|
24
|
+
attribute :sdg_code, String
|
25
|
+
attribute :state, Integer
|
26
|
+
attribute :end_date, Decidim::Attributes::LocalizedDate
|
27
|
+
attribute :start_date, Decidim::Attributes::LocalizedDate
|
28
|
+
attribute :coordinating_entities, String
|
29
|
+
attribute :collaborating_entities, String
|
30
|
+
|
31
|
+
attribute :card_image
|
32
|
+
attribute :remove_card_image, Boolean, default: false
|
33
|
+
|
34
|
+
validates :title, :local_description, :global_description, translatable_presence: true
|
35
|
+
validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
|
36
|
+
validate :valid_state
|
37
|
+
|
38
|
+
validates :start_date, presence: true, date: { before_or_equal_to: :end_date }
|
39
|
+
validates :end_date, presence: true, date: { after_or_equal_to: :start_date }
|
40
|
+
|
41
|
+
validates :card_image,
|
42
|
+
presence: false,
|
43
|
+
passthru: { to: Decidim::Attachment }
|
44
|
+
|
45
|
+
alias organization current_organization
|
46
|
+
|
47
|
+
def select_states_collection
|
48
|
+
Decidim::Challenges::Challenge::VALID_STATES.map.with_index do |state, idx|
|
49
|
+
[I18n.t(state, scope: "decidim.challenges.states"), idx]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def select_sdg_collection
|
54
|
+
Decidim::Sdgs::Sdg::SDGS.map do |sdg_code|
|
55
|
+
[I18n.t("#{sdg_code}.objectives.subtitle", scope: "decidim.components.sdgs"), sdg_code]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Finds the Scope from the given decidim_scope_id, uses participatory space scope if missing.
|
60
|
+
#
|
61
|
+
# Returns a Decidim::Scope
|
62
|
+
def scope
|
63
|
+
@scope ||= current_organization.scopes.find_by(id: decidim_scope_id)
|
64
|
+
end
|
65
|
+
|
66
|
+
def map_model(model); end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def valid_state
|
71
|
+
return if state && Decidim::Challenges::Challenge::VALID_STATES[state].present?
|
72
|
+
|
73
|
+
errors.add(:state, :invalid)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Problems
|
5
|
+
module Admin
|
6
|
+
# A form object used to create problems from the admin dashboard.
|
7
|
+
#
|
8
|
+
class ProblemsForm < Decidim::Form
|
9
|
+
include TranslatableAttributes
|
10
|
+
|
11
|
+
mimic :problem
|
12
|
+
|
13
|
+
translatable_attribute :title, String do |field, _locale|
|
14
|
+
validates field, length: { in: 5..150 }, if: proc { |resource| resource.send(field).present? }
|
15
|
+
end
|
16
|
+
translatable_attribute :description, String
|
17
|
+
|
18
|
+
attribute :decidim_challenges_challenge_id, Integer
|
19
|
+
attribute :decidim_sectorial_scope_id, Integer
|
20
|
+
attribute :decidim_technological_scope_id, Integer
|
21
|
+
attribute :tags, String
|
22
|
+
attribute :causes, String
|
23
|
+
attribute :groups_affected, String
|
24
|
+
attribute :state, Integer
|
25
|
+
attribute :end_date, Decidim::Attributes::LocalizedDate
|
26
|
+
attribute :start_date, Decidim::Attributes::LocalizedDate
|
27
|
+
attribute :proposing_entities, String
|
28
|
+
attribute :collaborating_entities, String
|
29
|
+
|
30
|
+
validates :title, :description, translatable_presence: true
|
31
|
+
validates :sectorial_scope, presence: true, if: ->(form) { form.decidim_sectorial_scope_id.present? }
|
32
|
+
validates :technological_scope, presence: true, if: ->(form) { form.decidim_technological_scope_id.present? }
|
33
|
+
validate :valid_state
|
34
|
+
validates :decidim_challenges_challenge_id, presence: true
|
35
|
+
|
36
|
+
validates :start_date, presence: true, date: { before_or_equal_to: :end_date }
|
37
|
+
validates :end_date, presence: true, date: { after_or_equal_to: :start_date }
|
38
|
+
|
39
|
+
alias organization current_organization
|
40
|
+
|
41
|
+
# Return a problem's valid states list
|
42
|
+
def select_states_collection
|
43
|
+
Decidim::Problems::Problem::VALID_STATES.map.with_index do |state, idx|
|
44
|
+
[I18n.t(state, scope: "decidim.problems.states"), idx]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return a challenge's list filtered by participatory's space component
|
49
|
+
def select_challenge_collection
|
50
|
+
participatory_space = Decidim::Component.find(current_component.id).participatory_space
|
51
|
+
challenge_component = Decidim::Component.where(participatory_space:).where(manifest_name: "challenges")
|
52
|
+
Decidim::Challenges::Challenge.where(component: challenge_component).map do |ch|
|
53
|
+
[translated_attribute(ch.title), ch.id]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Finds the Sectorial Scope from the given decidim_sectorial_scope_id
|
58
|
+
#
|
59
|
+
# Returns a Decidim::Scope
|
60
|
+
def sectorial_scope
|
61
|
+
@sectorial_scope ||= current_organization.scopes.find_by(id: decidim_sectorial_scope_id)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Finds the Technological Scope from the given decidim_technological_scope_id
|
65
|
+
#
|
66
|
+
# Returns a Decidim::Scope
|
67
|
+
def technological_scope
|
68
|
+
@technological_scope ||= current_organization.scopes.find_by(id: decidim_technological_scope_id)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Finds the Challenge from the given decidim_challenges_challenge_id
|
72
|
+
#
|
73
|
+
# Returns a Decidim::Challenges::Challenge
|
74
|
+
def challenge
|
75
|
+
@challenge ||= @decidim_challenges_challenge_id ? Decidim::Challenges::Challenge.find(@decidim_challenges_challenge_id) : false
|
76
|
+
end
|
77
|
+
|
78
|
+
def map_model(model); end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def valid_state
|
83
|
+
return if state.present? && Decidim::Problems::Problem::VALID_STATES[state].present?
|
84
|
+
|
85
|
+
errors.add(:state, :invalid)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Solutions
|
5
|
+
module Admin
|
6
|
+
# A form object used to create solutions from the admin dashboard.
|
7
|
+
#
|
8
|
+
class SolutionsForm < Decidim::Form
|
9
|
+
include TranslatableAttributes
|
10
|
+
|
11
|
+
mimic :solution
|
12
|
+
|
13
|
+
translatable_attribute :title, String do |field, _locale|
|
14
|
+
validates field, length: { in: 5..150 }, if: proc { |resource| resource.send(field).present? }
|
15
|
+
end
|
16
|
+
translatable_attribute :description, String
|
17
|
+
|
18
|
+
attribute :decidim_problems_problem_id, Integer
|
19
|
+
attribute :decidim_challenges_challenge_id, Integer
|
20
|
+
attribute :author_id, Integer
|
21
|
+
attribute :tags, String
|
22
|
+
translatable_attribute :objectives, String
|
23
|
+
translatable_attribute :indicators, String
|
24
|
+
translatable_attribute :beneficiaries, String
|
25
|
+
translatable_attribute :requirements, String
|
26
|
+
translatable_attribute :financing_type, String
|
27
|
+
|
28
|
+
validates :title, :description, translatable_presence: true
|
29
|
+
validates :decidim_challenges_challenge_id, presence: false
|
30
|
+
validates :decidim_problems_problem_id, presence: false
|
31
|
+
validates :author_id, presence: true
|
32
|
+
|
33
|
+
alias organization current_organization
|
34
|
+
|
35
|
+
# Return a problem's list filtered by participatory's space component
|
36
|
+
def select_problem_collection
|
37
|
+
participatory_space = Decidim::Component.find(current_component.id).participatory_space
|
38
|
+
problem_component = Decidim::Component.where(participatory_space:).where(manifest_name: "problems")
|
39
|
+
Decidim::Problems::Problem.where(component: problem_component).map do |p|
|
40
|
+
[translated_attribute(p.title), p.id]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return a challenges's list filtered by participatory's space component
|
45
|
+
def select_challenge_collection
|
46
|
+
participatory_space = Decidim::Component.find(current_component.id).participatory_space
|
47
|
+
challenge_component = Decidim::Component.where(participatory_space:).where(manifest_name: "challenges")
|
48
|
+
Decidim::Challenges::Challenge.where(component: challenge_component).map do |p|
|
49
|
+
[translated_attribute(p.title), p.id]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Finds the Problem from the given decidim_problems_problem_id
|
54
|
+
#
|
55
|
+
# Returns a Decidim::Problems::Problem
|
56
|
+
def problem
|
57
|
+
@problem ||= decidim_problems_problem_id.present? ? Decidim::Problems::Problem.find(decidim_problems_problem_id) : false
|
58
|
+
end
|
59
|
+
|
60
|
+
# Finds the Challenge from the given decidim_challenges_challenge_id
|
61
|
+
#
|
62
|
+
# Returns a Decidim::Challenges::Challenge
|
63
|
+
def challenge
|
64
|
+
@challenge ||= @problem.present? ? Decidim::Challenges::Challenge.find(@problem.challenge.id) : false
|
65
|
+
end
|
66
|
+
|
67
|
+
def map_model(model); end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Solutions
|
5
|
+
# A form object to be used when public users want to create a solution.
|
6
|
+
#
|
7
|
+
class SolutionsForm < Decidim::Form
|
8
|
+
include Decidim::AttachmentAttributes
|
9
|
+
include Decidim::HasUploadValidations
|
10
|
+
|
11
|
+
mimic :solution
|
12
|
+
|
13
|
+
attribute :title, String
|
14
|
+
attribute :description, Decidim::Attributes::CleanString
|
15
|
+
attribute :project_status, String
|
16
|
+
attribute :project_url, String
|
17
|
+
attribute :coordinating_entity, String
|
18
|
+
attribute :decidim_challenges_challenge_id, Integer
|
19
|
+
attribute :decidim_problems_problem_id, Integer
|
20
|
+
attribute :author_id, Integer
|
21
|
+
attribute :attachment, AttachmentForm
|
22
|
+
|
23
|
+
attachments_attribute :documents
|
24
|
+
|
25
|
+
validates :title, :description, presence: true, etiquette: true
|
26
|
+
validates :project_status, :coordinating_entity, presence: true
|
27
|
+
validates :decidim_challenges_challenge_id, :author_id, presence: true
|
28
|
+
validates :decidim_problems_problem_id, presence: false
|
29
|
+
|
30
|
+
def map_model(model)
|
31
|
+
self.title = translated_attribute(model.title)
|
32
|
+
self.description = translated_attribute(model.description)
|
33
|
+
|
34
|
+
self.documents = model.attachments
|
35
|
+
return unless model.categorization
|
36
|
+
end
|
37
|
+
|
38
|
+
# Finds the Challenge from the given decidim_challenges_challenge_id
|
39
|
+
#
|
40
|
+
# Returns a Decidim::Challenges::Challenge
|
41
|
+
def challenge
|
42
|
+
@challenge ||= @problem.present? ? Decidim::Challenges::Challenge.find(@problem.challenge.id) : Decidim::Challenges::Challenge.find(@decidim_challenges_challenge_id)
|
43
|
+
end
|
44
|
+
|
45
|
+
def author
|
46
|
+
Decidim::User.find(author_id)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Challenges
|
5
|
+
# Custom helpers, scoped to the challenges engine.
|
6
|
+
#
|
7
|
+
module ApplicationHelper
|
8
|
+
def component_name
|
9
|
+
i18n_key = "decidim.components.challenges.name"
|
10
|
+
(defined?(current_component) && translated_attribute(current_component&.name).presence) || t(i18n_key)
|
11
|
+
end
|
12
|
+
|
13
|
+
def filter_custom_state_values
|
14
|
+
Decidim::CheckBoxesTreeHelper::TreeNode.new(
|
15
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("", t("decidim.challenges.challenges_helper.filter_state_values.all")),
|
16
|
+
[
|
17
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("proposal", t("decidim.challenges.challenges_helper.filter_state_values.proposal")),
|
18
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("execution", t("decidim.challenges.challenges_helper.filter_state_values.execution")),
|
19
|
+
Decidim::CheckBoxesTreeHelper::TreePoint.new("finished", t("decidim.challenges.challenges_helper.filter_state_values.finished")),
|
20
|
+
]
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Challenges
|
5
|
+
# Custom helpers, scoped to the challenges engine.
|
6
|
+
#
|
7
|
+
module ChallengeCellsHelper
|
8
|
+
include Decidim::Challenges::ApplicationHelper
|
9
|
+
include Decidim::Challenges::Engine.routes.url_helpers
|
10
|
+
include Decidim::LayoutHelper
|
11
|
+
include Decidim::ApplicationHelper
|
12
|
+
include Decidim::TranslationsHelper
|
13
|
+
include Decidim::ResourceReferenceHelper
|
14
|
+
include Decidim::TranslatableAttributes
|
15
|
+
include Decidim::CardHelper
|
16
|
+
include WithSdgs
|
17
|
+
|
18
|
+
delegate :title, :state, :published_state?, :withdrawn?, :amendable?, :emendation?, to: :model
|
19
|
+
|
20
|
+
def has_actions?
|
21
|
+
return context[:has_actions] if context[:has_actions].present?
|
22
|
+
|
23
|
+
challenges_controller? && index_action? && current_settings.votes_enabled? && !model.draft?
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_footer?
|
27
|
+
return context[:has_footer] if context[:has_footer].present?
|
28
|
+
|
29
|
+
challenges_controller? && index_action? && current_settings.votes_enabled? && !model.draft?
|
30
|
+
end
|
31
|
+
|
32
|
+
def challenges_controller?
|
33
|
+
context[:controller].instance_of?(::Decidim::Challenges::ChallengesController)
|
34
|
+
end
|
35
|
+
|
36
|
+
def index_action?
|
37
|
+
context[:controller].action_name == "index"
|
38
|
+
end
|
39
|
+
|
40
|
+
def current_settings
|
41
|
+
model.component.current_settings
|
42
|
+
end
|
43
|
+
|
44
|
+
def component_settings
|
45
|
+
model.component.settings
|
46
|
+
end
|
47
|
+
|
48
|
+
def current_component
|
49
|
+
model.component
|
50
|
+
end
|
51
|
+
|
52
|
+
def from_context
|
53
|
+
options[:from]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Challenges
|
5
|
+
# Custom helpers, scoped to the challenges engine.
|
6
|
+
#
|
7
|
+
module ChallengesHelper
|
8
|
+
def filter_sections
|
9
|
+
items = []
|
10
|
+
items.append(method: :with_any_state, collection: filter_custom_state_values, label_scope: "decidim.shared.filters", id: "state")
|
11
|
+
items.append(method: :related_to, collection: linked_classes_filter_values_for(Decidim::Challenges::Challenge), label_scope: "decidim.shared.filters", id: "related_to",
|
12
|
+
type: :radio_buttons)
|
13
|
+
if current_participatory_space.has_subscopes?
|
14
|
+
items.append(method: :with_any_scope, collection: filter_global_scopes_values, label_scope: "decidim.shared.filters",
|
15
|
+
id: "scope")
|
16
|
+
end
|
17
|
+
|
18
|
+
items.reject { |item| item[:collection].blank? }
|
19
|
+
end
|
20
|
+
|
21
|
+
def challenge_associated_solutions(challenge)
|
22
|
+
solutions_component = Decidim::Component.find_by(participatory_space: challenge.participatory_space, manifest_name: "solutions")
|
23
|
+
return [] unless solutions_component&.published?
|
24
|
+
|
25
|
+
problems_component = Decidim::Component.find_by(participatory_space: challenge.participatory_space, manifest_name: "problems")
|
26
|
+
if problems_component&.published?
|
27
|
+
challenge.problems.published.map { |problem| problem.solutions.published }.flatten
|
28
|
+
else
|
29
|
+
challenge.solutions.published
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def truncate_description(description)
|
34
|
+
translated_description = raw translated_attribute description
|
35
|
+
decidim_sanitize(html_truncate(translated_description, length: 200))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|