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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1da39e457c98c89de1856218c786f7511d40b80e489436739413c1eb9c4d1dc
4
- data.tar.gz: 411a06dcef30050371fd8800d80b60635a444df204610b2232eb06d3c0b3e14e
3
+ metadata.gz: 1a717705c80a5671f20a63354adc618c9130dd6916e4820849a3213b39a83f76
4
+ data.tar.gz: 0f4b37b60ed78f0e9dd8ad9ad59c1385cc60636d7a96f18bb6435c68d5bd07b9
5
5
  SHA512:
6
- metadata.gz: bd9050e63099e48e69d089f0bae67bb3542c390b52b335324a60638571c8456d72edb46177528459ff876efd72f864e7af860274fb6bf90d2d741b4180970b2d
7
- data.tar.gz: '009afe45f8e742d65509b78e630faee50e9fd7fcee0156fdf129cbeb9984b07132e545189314c3bb8257e8f6b6afac81dc18956f820a8a2a123e10eb36817c95'
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, :display_conditions)
11
- original_questionnaire.questions.each do |original_question|
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
- original_questionnaire.questions.zip(new_questionnaire.questions.load).each do |original_question, new_question|
26
- copy_question_display_conditions(original_question, new_question)
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(destination_question.questionnaire.questions, original_display_condition.condition_question.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
 
@@ -17,6 +17,8 @@ module Decidim
17
17
  ::Decidim::Templates::Admin::Permissions,
18
18
  ::Decidim::Admin::Permissions)
19
19
 
20
+ protected
21
+
20
22
  def permission_class_chain
21
23
  ::Decidim.permissions_registry.chain_for(::Decidim::Templates::Admin::ApplicationController)
22
24
  end
@@ -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 ||= Decidim::Proposals::Proposal.find(params[:proposalId])
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 ||= Template.find_by(id: params[:id])
177
+ @template ||= current_organization.templates.find_by(id: params[:id])
173
178
  end
174
179
 
175
180
  def collection
@@ -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(params[:id])
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 = Decidim::Forms::Questionnaire.find_by(id: params[:questionnaire_id])
108
- template = Decidim::Templates::Template.find_by(id: params.dig(:questionnaire, :questionnaire_template_id))
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 URI.parse(params[:url]).path
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 = Decidim::Forms::Questionnaire.find_by(id: params[:questionnaire_id])
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 URI.parse(params[:url]).path
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 user_has_a_role? && (permission_action.subject == :template && permission_action.action == :read)
12
+ if read_template_action? || allows_questionnaire_template_action?
13
13
  allow!
14
- else
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
@@ -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 plantilla hau aurkitu. Kargatu orria, mesedez.
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: Noiz sortua
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-txantilioia
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-txantilioi berria
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: '%{total_steps} etik'
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:
@@ -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.update_columns(
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.update_columns(
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
- questionnaire.update_columns(
43
- created_at: Time.zone.now,
44
- updated_at: Time.zone.now,
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
- questionnaire.update_columns(
86
- created_at: Time.zone.now,
87
- updated_at: Time.zone.now,
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
- # rubocop:enable Rails/SkipsModelValidations
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
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-templates version.
5
5
  module Templates
6
6
  def self.version
7
- "0.31.7"
7
+ "0.31.8"
8
8
  end
9
9
  end
10
10
  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.7
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-07-30 00:00:00.000000000 Z
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.7
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.7
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.7
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.7
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.7
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.7
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.7
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.7
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.7
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.7
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.7
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.7
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.7
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.7
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: