decidim-templates 0.31.7 → 0.31.8
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 +4 -4
- data/app/commands/decidim/templates/admin/questionnaire_copier.rb +10 -8
- data/app/controllers/decidim/templates/admin/application_controller.rb +2 -0
- data/app/controllers/decidim/templates/admin/concerns/templatable.rb +2 -2
- data/app/controllers/decidim/templates/admin/proposal_answer_templates_controller.rb +10 -5
- data/app/controllers/decidim/templates/admin/questionnaire_templates/questionnaires_controller.rb +15 -1
- data/app/controllers/decidim/templates/admin/questionnaire_templates_controller.rb +70 -5
- data/app/permissions/decidim/templates/admin/permissions.rb +48 -4
- data/config/locales/eu.yml +9 -9
- data/config/locales/gl.yml +61 -0
- data/lib/decidim/templates/test/shared_examples/uses_questionnaire_templates.rb +36 -12
- data/lib/decidim/templates/version.rb +1 -1
- metadata +16 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1a717705c80a5671f20a63354adc618c9130dd6916e4820849a3213b39a83f76
|
|
4
|
+
data.tar.gz: 0f4b37b60ed78f0e9dd8ad9ad59c1385cc60636d7a96f18bb6435c68d5bd07b9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7b816297bf9e51f78472533b290ee08545fe8fdf11f6d1440f88dc4ff4273cd4a2bee861f4e631a25a125c163d6f89bc99b2126fd19af15ff4f2e2ce9b956dd1
|
|
7
|
+
data.tar.gz: 63884e90127c69bff4243a7356f22aa926305afed6b87ebb101c22f36213e7541c83dee0295ce56260a499d9da58ec4f7c838a22a19e08409e2f1f6f23081d49
|
|
@@ -7,8 +7,8 @@ module Decidim
|
|
|
7
7
|
module QuestionnaireCopier
|
|
8
8
|
def copy_questionnaire_questions(original_questionnaire, new_questionnaire)
|
|
9
9
|
# start by copying the questions so that they already exist when cross referencing them in the conditions
|
|
10
|
-
original_questionnaire.reload.questions.includes(:response_options, :matrix_rows, :
|
|
11
|
-
|
|
10
|
+
original_questions = original_questionnaire.reload.questions.includes(:response_options, :matrix_rows, display_conditions: [:condition_question, :response_option]).to_a
|
|
11
|
+
original_questions.each do |original_question|
|
|
12
12
|
new_question = original_question.dup
|
|
13
13
|
new_question.questionnaire = new_questionnaire
|
|
14
14
|
new_question.assign_attributes(
|
|
@@ -21,9 +21,12 @@ module Decidim
|
|
|
21
21
|
copy_questionnaire_response_options(original_question, new_question)
|
|
22
22
|
copy_questionnaire_matrix_rows(original_question, new_question)
|
|
23
23
|
end
|
|
24
|
-
# once all questions are copied, copy display conditions
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
# once all questions are copied, copy display conditions. The destination
|
|
25
|
+
# questions are looked up by position (and their response options by body)
|
|
26
|
+
# while cross referencing the conditions, so eager load them to avoid N+1s.
|
|
27
|
+
destination_questions = new_questionnaire.questions.includes(:response_options).to_a
|
|
28
|
+
original_questions.zip(destination_questions).each do |original_question, new_question|
|
|
29
|
+
copy_question_display_conditions(original_question, new_question, destination_questions)
|
|
27
30
|
end
|
|
28
31
|
end
|
|
29
32
|
|
|
@@ -43,12 +46,12 @@ module Decidim
|
|
|
43
46
|
end
|
|
44
47
|
end
|
|
45
48
|
|
|
46
|
-
def copy_question_display_conditions(original_question, destination_question)
|
|
49
|
+
def copy_question_display_conditions(original_question, destination_question, destination_questions)
|
|
47
50
|
original_question.display_conditions.each do |original_display_condition|
|
|
48
51
|
new_display_condition = original_display_condition.dup
|
|
49
52
|
new_display_condition.question = destination_question
|
|
50
53
|
|
|
51
|
-
destination_question_to_be_checked = find_question_by_position(
|
|
54
|
+
destination_question_to_be_checked = find_question_by_position(destination_questions, original_display_condition.condition_question.position)
|
|
52
55
|
new_display_condition.condition_question = destination_question_to_be_checked
|
|
53
56
|
|
|
54
57
|
if original_display_condition.response_option
|
|
@@ -56,7 +59,6 @@ module Decidim
|
|
|
56
59
|
original_display_condition.response_option.body)
|
|
57
60
|
end
|
|
58
61
|
new_display_condition.save!
|
|
59
|
-
destination_question.display_conditions << new_display_condition
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
|
|
@@ -13,6 +13,8 @@ module Decidim
|
|
|
13
13
|
included do
|
|
14
14
|
helper_method :templates, :choose_template?
|
|
15
15
|
|
|
16
|
+
protected
|
|
17
|
+
|
|
16
18
|
def templates
|
|
17
19
|
@templates = current_organization.templates.where(templatable_type:)
|
|
18
20
|
@templates = @templates.where.not(id: params[:id]) if templates_path?
|
|
@@ -29,8 +31,6 @@ module Decidim
|
|
|
29
31
|
templates.any? && templatable.pristine?
|
|
30
32
|
end
|
|
31
33
|
|
|
32
|
-
protected
|
|
33
|
-
|
|
34
34
|
def templatable
|
|
35
35
|
raise NotImplementedError, "Please set the templatable resource in your controller"
|
|
36
36
|
end
|
|
@@ -58,7 +58,7 @@ module Decidim
|
|
|
58
58
|
def fetch
|
|
59
59
|
enforce_permission_to(:read, :template, template:, proposal:)
|
|
60
60
|
|
|
61
|
-
return render json: { msg: I18n.t("templates.fetch.error", scope: "decidim.admin") }, status: :unprocessable_entity if template.blank?
|
|
61
|
+
return render json: { msg: I18n.t("templates.fetch.error", scope: "decidim.admin") }, status: :unprocessable_entity if template.blank? || proposal.blank?
|
|
62
62
|
|
|
63
63
|
state = fetch_proposal_state(template)
|
|
64
64
|
|
|
@@ -136,11 +136,16 @@ module Decidim
|
|
|
136
136
|
end
|
|
137
137
|
|
|
138
138
|
def available_states(component_id = nil)
|
|
139
|
-
Decidim::Proposals::ProposalState.where(decidim_component_id: component_id)
|
|
139
|
+
Decidim::Proposals::ProposalState.where(decidim_component_id: component_id) # rubocop:disable Decidim/OrganizationScopedFinder -- component_id comes from template.templatable_id, templates are scoped to current_organization
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
def proposal
|
|
143
|
-
@proposal ||=
|
|
143
|
+
@proposal ||= begin
|
|
144
|
+
component_ids = current_organization.participatory_spaces
|
|
145
|
+
.flat_map { |s| s.components.where(manifest_name: :proposals).pluck(:id) }
|
|
146
|
+
Decidim::Proposals::Proposal.where(decidim_component_id: component_ids) # rubocop:disable Decidim/OrganizationScopedFinder -- scoped via component_ids derived from current_organization.participatory_spaces
|
|
147
|
+
.find_by(id: params[:proposalId].to_i)
|
|
148
|
+
end
|
|
144
149
|
end
|
|
145
150
|
|
|
146
151
|
def availability_option_as_text(template)
|
|
@@ -155,7 +160,7 @@ module Decidim
|
|
|
155
160
|
|
|
156
161
|
def availability_options
|
|
157
162
|
@availability_options = []
|
|
158
|
-
Decidim::Component.includes(:participatory_space).where(manifest_name: [:proposals])
|
|
163
|
+
Decidim::Component.includes(:participatory_space).where(manifest_name: [:proposals]) # rubocop:disable Decidim/OrganizationScopedFinder -- result is filtered in-memory below by current_organization
|
|
159
164
|
.select { |a| a.participatory_space.decidim_organization_id == current_organization.id }.each do |component|
|
|
160
165
|
@availability_options.push [formatted_name(component), component.id]
|
|
161
166
|
end
|
|
@@ -169,7 +174,7 @@ module Decidim
|
|
|
169
174
|
end
|
|
170
175
|
|
|
171
176
|
def template
|
|
172
|
-
@template ||=
|
|
177
|
+
@template ||= current_organization.templates.find_by(id: params[:id])
|
|
173
178
|
end
|
|
174
179
|
|
|
175
180
|
def collection
|
data/app/controllers/decidim/templates/admin/questionnaire_templates/questionnaires_controller.rb
CHANGED
|
@@ -10,6 +10,8 @@ module Decidim
|
|
|
10
10
|
|
|
11
11
|
helper Decidim::Admin::ExportsHelper
|
|
12
12
|
|
|
13
|
+
protected
|
|
14
|
+
|
|
13
15
|
def questionnaire_for
|
|
14
16
|
template
|
|
15
17
|
end
|
|
@@ -30,10 +32,22 @@ module Decidim
|
|
|
30
32
|
t(:title, scope: "decidim.templates.admin.questionnaire_templates.form", questionnaire_for: translated_attribute(template.name))
|
|
31
33
|
end
|
|
32
34
|
|
|
35
|
+
def response_options_url(params)
|
|
36
|
+
url_for(params.merge(controller: "decidim/templates/admin/questionnaire_templates/questionnaires", action: "response_options", format: :json, template_id: template.id))
|
|
37
|
+
end
|
|
38
|
+
|
|
33
39
|
private
|
|
34
40
|
|
|
35
41
|
def template
|
|
36
|
-
@template ||= current_organization.templates.where(templatable_type: "Decidim::Forms::Questionnaire").find(
|
|
42
|
+
@template ||= current_organization.templates.where(templatable_type: "Decidim::Forms::Questionnaire").find(template_id)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def template_id
|
|
46
|
+
if action_name == "response_options"
|
|
47
|
+
params.require(:template_id)
|
|
48
|
+
else
|
|
49
|
+
params.require(:id)
|
|
50
|
+
end
|
|
37
51
|
end
|
|
38
52
|
end
|
|
39
53
|
end
|
|
@@ -104,13 +104,19 @@ module Decidim
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
def apply
|
|
107
|
-
questionnaire =
|
|
108
|
-
|
|
107
|
+
questionnaire = find_questionnaire(params[:questionnaire_id])
|
|
108
|
+
enforce_permission_to :apply, :questionnaire_template, questionnaire:, component: questionnaire_component(questionnaire)
|
|
109
|
+
template = collection.find_by(id: params.dig(:questionnaire, :questionnaire_template_id))
|
|
110
|
+
|
|
111
|
+
if questionnaire.blank?
|
|
112
|
+
flash[:alert] = I18n.t("templates.apply.error", scope: "decidim.admin")
|
|
113
|
+
return redirect_to(internal_redirect_path || { action: :index })
|
|
114
|
+
end
|
|
109
115
|
|
|
110
116
|
ApplyQuestionnaireTemplate.call(questionnaire, template) do
|
|
111
117
|
on(:ok) do
|
|
112
118
|
flash[:notice] = I18n.t("templates.apply.success", scope: "decidim.admin")
|
|
113
|
-
redirect_to
|
|
119
|
+
redirect_to(internal_redirect_path || { action: :index })
|
|
114
120
|
end
|
|
115
121
|
on(:invalid) do
|
|
116
122
|
flash[:error] = I18n.t("templates.apply.error", scope: "decidim.admin")
|
|
@@ -120,6 +126,7 @@ module Decidim
|
|
|
120
126
|
end
|
|
121
127
|
|
|
122
128
|
def preview
|
|
129
|
+
enforce_permission_to :preview, :questionnaire_template
|
|
123
130
|
respond_to do |format|
|
|
124
131
|
format.js do
|
|
125
132
|
@template = template
|
|
@@ -130,23 +137,81 @@ module Decidim
|
|
|
130
137
|
end
|
|
131
138
|
|
|
132
139
|
def skip
|
|
133
|
-
questionnaire =
|
|
140
|
+
questionnaire = find_questionnaire(params[:questionnaire_id])
|
|
141
|
+
enforce_permission_to :skip, :questionnaire_template, questionnaire:, component: questionnaire_component(questionnaire)
|
|
142
|
+
|
|
143
|
+
return redirect_to(internal_redirect_path || { action: :index }) unless questionnaire
|
|
144
|
+
|
|
134
145
|
# rubocop:disable Rails/SkipsModelValidations
|
|
135
146
|
questionnaire.touch
|
|
136
147
|
# rubocop:enable Rails/SkipsModelValidations
|
|
137
|
-
redirect_to
|
|
148
|
+
redirect_to(internal_redirect_path || { action: :index })
|
|
138
149
|
end
|
|
139
150
|
|
|
151
|
+
protected
|
|
152
|
+
|
|
140
153
|
def edit_questions_template
|
|
141
154
|
"decidim/templates/admin/questionnaire_templates/edit_questions"
|
|
142
155
|
end
|
|
143
156
|
|
|
157
|
+
def response_options_url(params)
|
|
158
|
+
url_for(params.merge(controller: "decidim/templates/admin/questionnaire_templates/questionnaires", action: "response_options", format: :json, template_id: template.id))
|
|
159
|
+
end
|
|
160
|
+
|
|
144
161
|
def after_update_url
|
|
145
162
|
edit_questionnaire_template_path(template)
|
|
146
163
|
end
|
|
147
164
|
|
|
148
165
|
private
|
|
149
166
|
|
|
167
|
+
def find_questionnaire(id)
|
|
168
|
+
questionnaire = Decidim::Forms::Questionnaire.find_by(id:) # rubocop:disable Decidim/OrganizationScopedFinder -- scoping requires polymorphic questionnaire_for chain; org ownership validated via questionnaire_for_in_organization?
|
|
169
|
+
return unless questionnaire
|
|
170
|
+
return unless questionnaire_for_in_organization?(questionnaire)
|
|
171
|
+
|
|
172
|
+
questionnaire
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def questionnaire_component(questionnaire)
|
|
176
|
+
questionnaire&.questionnaire_for.try(:component)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def questionnaire_for_in_organization?(questionnaire)
|
|
180
|
+
questionnaire_for = questionnaire.questionnaire_for
|
|
181
|
+
return false unless questionnaire_for
|
|
182
|
+
return false unless questionnaire_for.respond_to?(:organization)
|
|
183
|
+
|
|
184
|
+
questionnaire_for.organization == current_organization
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Returns the path of the `url` param only when it points to the same
|
|
188
|
+
# origin. Absolute same-origin URLs and root-relative paths are
|
|
189
|
+
# accepted; protocol-relative, external and malformed URLs are rejected
|
|
190
|
+
# so they can never be used to redirect the user to another site.
|
|
191
|
+
def internal_redirect_path
|
|
192
|
+
url = params[:url].to_s
|
|
193
|
+
return if url.blank?
|
|
194
|
+
|
|
195
|
+
uri = URI.parse(url)
|
|
196
|
+
return if uri.host.present? && !same_origin?(uri)
|
|
197
|
+
|
|
198
|
+
safe_path(uri.path)
|
|
199
|
+
rescue URI::Error
|
|
200
|
+
nil
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def same_origin?(uri)
|
|
204
|
+
uri.host == request.host && (uri.scheme.nil? || uri.scheme == request.scheme)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def safe_path(path)
|
|
208
|
+
return if path.blank? || !path.start_with?("/")
|
|
209
|
+
return if path.start_with?("//", "/\\") || path.include?("\\")
|
|
210
|
+
return if path.match?(/[\u0000-\u001f\u007f]/)
|
|
211
|
+
|
|
212
|
+
path
|
|
213
|
+
end
|
|
214
|
+
|
|
150
215
|
def questionnaire
|
|
151
216
|
template.templatable
|
|
152
217
|
end
|
|
@@ -9,11 +9,9 @@ module Decidim
|
|
|
9
9
|
return permission_action unless user
|
|
10
10
|
return permission_action if context[:current_organization] != user.organization
|
|
11
11
|
|
|
12
|
-
if
|
|
12
|
+
if read_template_action? || allows_questionnaire_template_action?
|
|
13
13
|
allow!
|
|
14
|
-
|
|
15
|
-
return permission_action unless user.admin?
|
|
16
|
-
|
|
14
|
+
elsif user.admin?
|
|
17
15
|
case permission_action.subject
|
|
18
16
|
when :template
|
|
19
17
|
allow! if [:read, :create, :update, :destroy, :copy].include? permission_action.action
|
|
@@ -21,6 +19,8 @@ module Decidim
|
|
|
21
19
|
allow! if permission_action.action == :index
|
|
22
20
|
when :questionnaire
|
|
23
21
|
allow!
|
|
22
|
+
when :questionnaire_template
|
|
23
|
+
allow! if [:preview, :apply, :skip].include? permission_action.action
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -42,6 +42,50 @@ module Decidim
|
|
|
42
42
|
|
|
43
43
|
user_roles.exists?(user:)
|
|
44
44
|
end
|
|
45
|
+
|
|
46
|
+
def read_template_action?
|
|
47
|
+
user_has_a_role? && permission_action.subject == :template && permission_action.action == :read
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Allows a user who is able to edit the target questionnaire to preview,
|
|
51
|
+
# apply or skip questionnaire templates. Organization admins can always
|
|
52
|
+
# perform these actions. "preview" has no target questionnaire to scope
|
|
53
|
+
# to, so it is allowed to any user holding a role in a participatory
|
|
54
|
+
# space, as it is only reachable from an already authorized form.
|
|
55
|
+
def allows_questionnaire_template_action?
|
|
56
|
+
return false unless permission_action.subject == :questionnaire_template
|
|
57
|
+
return false unless [:preview, :apply, :skip].include?(permission_action.action)
|
|
58
|
+
return true if user.admin?
|
|
59
|
+
|
|
60
|
+
if (component = context[:component]).present?
|
|
61
|
+
return user_can_manage_component?(component)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
permission_action.action == :preview && user_has_any_role?(user, nil, broad_check: true)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Checks the user can manage the given component by delegating the
|
|
68
|
+
# `:manage, :component` action to the component's own permission chain,
|
|
69
|
+
# so that space admins are authorized while other roles are not.
|
|
70
|
+
def user_can_manage_component?(component)
|
|
71
|
+
[
|
|
72
|
+
component.manifest.permissions_class,
|
|
73
|
+
component.participatory_space.manifest.permissions_class,
|
|
74
|
+
Decidim::Admin::Permissions
|
|
75
|
+
].inject(permission_action_for_manage_component) do |action, permission_class|
|
|
76
|
+
permission_class.new(
|
|
77
|
+
user,
|
|
78
|
+
action,
|
|
79
|
+
context.merge(component:, current_participatory_space: component.participatory_space)
|
|
80
|
+
).permissions
|
|
81
|
+
end.allowed?
|
|
82
|
+
rescue Decidim::PermissionAction::PermissionNotSetError
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def permission_action_for_manage_component
|
|
87
|
+
Decidim::PermissionAction.new(scope: :admin, action: :manage, subject: :component)
|
|
88
|
+
end
|
|
45
89
|
end
|
|
46
90
|
end
|
|
47
91
|
end
|
data/config/locales/eu.yml
CHANGED
|
@@ -27,7 +27,7 @@ eu:
|
|
|
27
27
|
success: Txantiloia zuzen ezabatua.
|
|
28
28
|
empty: Oraindik ez dago txantiloirik.
|
|
29
29
|
fetch:
|
|
30
|
-
error: Ezin da
|
|
30
|
+
error: Ezin da txantiloi hau aurkitu. Mesedez kargatu berriro orria.
|
|
31
31
|
missing_resource: "(baliabide galdua)"
|
|
32
32
|
update:
|
|
33
33
|
error: Arazo bat egon da eztabaida hau eguneratzean.
|
|
@@ -43,7 +43,7 @@ eu:
|
|
|
43
43
|
title: Galdeketaren Izenburua
|
|
44
44
|
template:
|
|
45
45
|
fields:
|
|
46
|
-
created_at:
|
|
46
|
+
created_at: Sortze-data
|
|
47
47
|
name: Txantiloia
|
|
48
48
|
templates:
|
|
49
49
|
admin:
|
|
@@ -61,24 +61,24 @@ eu:
|
|
|
61
61
|
select_template: Hautatu txantiloi-fitxategi bat
|
|
62
62
|
proposal_answer_templates:
|
|
63
63
|
edit:
|
|
64
|
-
title: Editatu proposamenaren erantzun-
|
|
64
|
+
title: Editatu proposamenaren erantzun-txantiloia
|
|
65
65
|
form:
|
|
66
66
|
component_constraint_help: Kontuan izan soilik zerrendatuko direla "proposamenak" motako osagaiak dituzten parte hartzeko espazioak.
|
|
67
67
|
hint1_html: "\n<strong>%{organization}</strong> erakundearen izenak ordezkatuko du."
|
|
68
|
-
hint2_html: "<strong>%{name}</strong> egilearen izenak ordezkatuko du
|
|
69
|
-
hint3_html: "<strong>%{admin}</strong> administratzailearen izenak ordezkatuko du (proposamenari erantzuten diona)
|
|
68
|
+
hint2_html: "<strong>%{name}</strong> egilearen izenak ordezkatuko du"
|
|
69
|
+
hint3_html: "<strong>%{admin}</strong> administratzailearen izenak ordezkatuko du (proposamenari erantzuten diona)"
|
|
70
70
|
hint_html: "<strong>Iradokizuna</strong> Aldagarri hauek erantzunaren txantiloiaren edozein lekutan erabil ditzakezu, txantiloia erabiltzean ordezkatuko baitira"
|
|
71
71
|
save: Gorde
|
|
72
72
|
index:
|
|
73
|
-
component_constraint: Gehitu hertsapena
|
|
73
|
+
component_constraint: Gehitu hertsapena
|
|
74
74
|
confirm_delete: Ziur zaude txantiloi hau ezabatu nahi duzula?
|
|
75
75
|
missing_state: Egoera desagertua
|
|
76
76
|
proposal_state_id: Barne egoera
|
|
77
77
|
title: Proposamenen erantzunak
|
|
78
78
|
new:
|
|
79
|
-
title: Proposamenaren erantzun-
|
|
79
|
+
title: Proposamenaren erantzun-txantiloi berria
|
|
80
80
|
template_chooser:
|
|
81
|
-
select_template: Aukeratu erantzun-txantiloi bat
|
|
81
|
+
select_template: Aukeratu erantzun-txantiloi bat
|
|
82
82
|
questionnaire_templates:
|
|
83
83
|
choose:
|
|
84
84
|
continue: Jarraitu
|
|
@@ -107,7 +107,7 @@ eu:
|
|
|
107
107
|
title: Beste galdetegi-txantiloi bat
|
|
108
108
|
preview:
|
|
109
109
|
current_step: '%{step} urratsa'
|
|
110
|
-
of_total_steps:
|
|
110
|
+
of_total_steps: guztira %{total_steps}
|
|
111
111
|
tos_agreement: Parte hartzean erabilera-irizpideak eta baldintzak onartzen dituzu
|
|
112
112
|
admin_log:
|
|
113
113
|
template:
|
data/config/locales/gl.yml
CHANGED
|
@@ -12,13 +12,26 @@ gl:
|
|
|
12
12
|
menu:
|
|
13
13
|
templates: Padróns
|
|
14
14
|
templates:
|
|
15
|
+
actions:
|
|
16
|
+
new_template: Novo modelo
|
|
15
17
|
apply:
|
|
16
18
|
error: Produciuse un problema ao aplicar o padrón.
|
|
19
|
+
success: O modelo aplicouse correctamente.
|
|
17
20
|
copy:
|
|
18
21
|
error: Produciuse un problema ao copiar o padrón.
|
|
22
|
+
success: O modelo copiouse correctamente.
|
|
19
23
|
create:
|
|
20
24
|
error: Produciuse un problema ao crear o padrón.
|
|
25
|
+
success: O modelo creouse correctamente.
|
|
26
|
+
destroy:
|
|
27
|
+
success: O modelo eliminouse correctamente.
|
|
21
28
|
empty: Aínda non hai padróns.
|
|
29
|
+
fetch:
|
|
30
|
+
error: Non se puido atopar este modelo. Volve cargar a páxina.
|
|
31
|
+
missing_resource: "(falta o recurso)"
|
|
32
|
+
update:
|
|
33
|
+
error: Produciuse un problema ao actualizar este modelo.
|
|
34
|
+
success: O modelo actualizouse correctamente.
|
|
22
35
|
titles:
|
|
23
36
|
template_types:
|
|
24
37
|
questionnaires: Padrón para cuestionarios
|
|
@@ -35,26 +48,74 @@ gl:
|
|
|
35
48
|
templates:
|
|
36
49
|
admin:
|
|
37
50
|
block_user_templates:
|
|
51
|
+
edit:
|
|
52
|
+
title: Editar o modelo de mensaxe de bloqueo de participante
|
|
38
53
|
form:
|
|
39
54
|
save: Gardar
|
|
55
|
+
index:
|
|
56
|
+
confirm_delete: Seguro que queres eliminar este modelo?
|
|
57
|
+
title: Mensaxes de bloqueo de participantes
|
|
58
|
+
new:
|
|
59
|
+
title: Novo modelo de mensaxe de bloqueo de participante
|
|
60
|
+
template_chooser:
|
|
61
|
+
select_template: Seleccionar unha resposta modelo
|
|
40
62
|
proposal_answer_templates:
|
|
63
|
+
edit:
|
|
64
|
+
title: Editar o modelo de resposta a propostas
|
|
41
65
|
form:
|
|
66
|
+
component_constraint_help: Ten en conta que só se mostrarán os espazos participativos que teñan compoñentes do tipo «propostas».
|
|
67
|
+
hint1_html: "<strong>%{organization}</strong> substituirase polo nome da organización"
|
|
68
|
+
hint2_html: "<strong>%{name}</strong> substituirase polo nome da persoa autora"
|
|
69
|
+
hint3_html: "<strong>%{admin}</strong> substituirase polo nome da persoa administradora (a que responde á proposta)"
|
|
70
|
+
hint_html: "<strong>Consello:</strong> podes usar estas variables en calquera lugar do modelo de resposta; substituiranse ao usar o modelo"
|
|
42
71
|
save: Gardar
|
|
72
|
+
index:
|
|
73
|
+
component_constraint: Engadir restrición
|
|
74
|
+
confirm_delete: Seguro que queres eliminar este modelo?
|
|
75
|
+
missing_state: Falta o estado
|
|
76
|
+
proposal_state_id: Estado interno
|
|
77
|
+
title: Respostas a propostas
|
|
78
|
+
new:
|
|
79
|
+
title: Novo modelo de resposta a propostas
|
|
80
|
+
template_chooser:
|
|
81
|
+
select_template: Seleccionar unha resposta modelo
|
|
43
82
|
questionnaire_templates:
|
|
44
83
|
choose:
|
|
84
|
+
continue: Continuar
|
|
85
|
+
description: Podes crear un novo cuestionario ou escoller un modelo predefinido e modificalo máis adiante. Selecciona unha opción.
|
|
45
86
|
label: Escoller padrón
|
|
87
|
+
options:
|
|
88
|
+
create_new_form: Crear un novo formulario
|
|
89
|
+
select_template: Seleccionar modelo
|
|
46
90
|
placeholder: Escoller padrón
|
|
47
91
|
edit:
|
|
48
92
|
edit: Editar
|
|
93
|
+
empty: Aínda non hai preguntas.
|
|
94
|
+
manage_questions: Preguntas
|
|
49
95
|
questionnaire: Cuestionario
|
|
96
|
+
title: Editar o modelo de cuestionario
|
|
97
|
+
edit_questions:
|
|
98
|
+
add_question: Engadir pregunta
|
|
99
|
+
add_separator: Engadir separador
|
|
100
|
+
add_title_and_description: Engadir título e descrición
|
|
50
101
|
form:
|
|
51
102
|
title: Padrón de cuestionario %{questionnaire_for}
|
|
52
103
|
index:
|
|
53
104
|
confirm_delete: Tes a certeza de querer eliminar este padrón?
|
|
54
105
|
title: Padróns de cuestionario
|
|
106
|
+
new:
|
|
107
|
+
title: Novo modelo de cuestionario
|
|
55
108
|
preview:
|
|
56
109
|
current_step: Paso %{step}
|
|
57
110
|
of_total_steps: de %{total_steps}
|
|
58
111
|
tos_agreement: Ao participar, aceptas os termos do servizo
|
|
112
|
+
admin_log:
|
|
113
|
+
template:
|
|
114
|
+
create: "%{user_name} creou o modelo de cuestionario %{resource_name}"
|
|
115
|
+
delete: "%{user_name} eliminou o modelo de cuestionario %{resource_name}"
|
|
116
|
+
duplicate: "%{user_name} duplicou o modelo de cuestionario %{resource_name}"
|
|
117
|
+
update: "%{user_name} actualizou o modelo de cuestionario %{resource_name}"
|
|
59
118
|
template_types:
|
|
119
|
+
block_user: Mensaxes de bloqueo de participantes
|
|
120
|
+
proposal_answer_templates: Respostas a propostas
|
|
60
121
|
questionnaires: Cuestionarios
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
|
|
5
|
-
# rubocop:disable Rails/SkipsModelValidations
|
|
6
|
-
|
|
7
5
|
shared_examples_for "uses questionnaire templates" do |_questionnaire_for|
|
|
8
6
|
describe "choose a template" do
|
|
9
7
|
context "when there are no templates" do
|
|
10
8
|
before do
|
|
11
|
-
questionnaire.
|
|
9
|
+
questionnaire.update!(
|
|
12
10
|
created_at: 1.day.ago,
|
|
13
11
|
updated_at: Time.zone.now
|
|
14
12
|
)
|
|
@@ -26,7 +24,7 @@ shared_examples_for "uses questionnaire templates" do |_questionnaire_for|
|
|
|
26
24
|
|
|
27
25
|
context "when it is an existing questionnaire" do
|
|
28
26
|
before do
|
|
29
|
-
questionnaire.
|
|
27
|
+
questionnaire.update!(
|
|
30
28
|
created_at: 1.day.ago,
|
|
31
29
|
updated_at: Time.zone.now
|
|
32
30
|
)
|
|
@@ -39,9 +37,10 @@ shared_examples_for "uses questionnaire templates" do |_questionnaire_for|
|
|
|
39
37
|
|
|
40
38
|
context "when it is a newly created questionnaire" do
|
|
41
39
|
before do
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
time = Time.zone.now
|
|
41
|
+
questionnaire.update!(
|
|
42
|
+
created_at: time,
|
|
43
|
+
updated_at: time,
|
|
45
44
|
title: {},
|
|
46
45
|
description: {},
|
|
47
46
|
tos: {}
|
|
@@ -82,9 +81,10 @@ shared_examples_for "uses questionnaire templates" do |_questionnaire_for|
|
|
|
82
81
|
let(:questionnaire_question) { questionnaire.questions.first }
|
|
83
82
|
|
|
84
83
|
before do
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
time = Time.zone.now
|
|
85
|
+
questionnaire.update!(
|
|
86
|
+
created_at: time,
|
|
87
|
+
updated_at: time,
|
|
88
88
|
title: {},
|
|
89
89
|
description: {},
|
|
90
90
|
tos: {}
|
|
@@ -106,6 +106,30 @@ shared_examples_for "uses questionnaire templates" do |_questionnaire_for|
|
|
|
106
106
|
expect(page.find("#questions_questions_#{questionnaire_question.id}_body_en").value).to eq(question.body["en"])
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
|
-
end
|
|
110
109
|
|
|
111
|
-
|
|
110
|
+
describe "skip the template" do
|
|
111
|
+
let!(:templates) { create_list(:questionnaire_template, 6, :with_questions, skip_injection: true, organization: questionnaire.questionnaire_for.organization) }
|
|
112
|
+
|
|
113
|
+
before do
|
|
114
|
+
questionnaire.update!(
|
|
115
|
+
created_at: 1.hour.ago,
|
|
116
|
+
updated_at: 1.hour.ago,
|
|
117
|
+
title: {},
|
|
118
|
+
description: {},
|
|
119
|
+
tos: {}
|
|
120
|
+
)
|
|
121
|
+
visit current_path
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "continues to the questionnaire without applying any template" do
|
|
125
|
+
choose("Create new form")
|
|
126
|
+
|
|
127
|
+
expect(page).to have_css("a[data-create-new-form-button]")
|
|
128
|
+
click_on "Continue"
|
|
129
|
+
|
|
130
|
+
expect(page).to have_text("Add question")
|
|
131
|
+
expect(page).to have_no_text("Select template")
|
|
132
|
+
expect(questionnaire.reload.updated_at).not_to eq(questionnaire.created_at)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: decidim-templates
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.31.
|
|
4
|
+
version: 0.31.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vera Rojman
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: decidim-core
|
|
@@ -16,98 +16,98 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - '='
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.31.
|
|
19
|
+
version: 0.31.8
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - '='
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.31.
|
|
26
|
+
version: 0.31.8
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: decidim-forms
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - '='
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 0.31.
|
|
33
|
+
version: 0.31.8
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - '='
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 0.31.
|
|
40
|
+
version: 0.31.8
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: decidim-admin
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - '='
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: 0.31.
|
|
47
|
+
version: 0.31.8
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - '='
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: 0.31.
|
|
54
|
+
version: 0.31.8
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: decidim-dev
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - '='
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: 0.31.
|
|
61
|
+
version: 0.31.8
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - '='
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: 0.31.
|
|
68
|
+
version: 0.31.8
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: decidim-participatory_processes
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
73
|
- - '='
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: 0.31.
|
|
75
|
+
version: 0.31.8
|
|
76
76
|
type: :development
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
80
|
- - '='
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: 0.31.
|
|
82
|
+
version: 0.31.8
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
84
|
name: decidim-proposals
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
87
|
- - '='
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: 0.31.
|
|
89
|
+
version: 0.31.8
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - '='
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: 0.31.
|
|
96
|
+
version: 0.31.8
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: decidim-surveys
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
101
|
- - '='
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: 0.31.
|
|
103
|
+
version: 0.31.8
|
|
104
104
|
type: :development
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
108
|
- - '='
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: 0.31.
|
|
110
|
+
version: 0.31.8
|
|
111
111
|
description: This module provides a solution to create templates for different Decidim
|
|
112
112
|
models, such as Proposals and Questionnaires..
|
|
113
113
|
email:
|