Skip to content

Commit

Permalink
Create multiple surveys within same Survey component (decidim#13420)
Browse files Browse the repository at this point in the history
Co-authored-by: Ivan Vergés <[email protected]>
  • Loading branch information
ElviaBth and microstudi authored Jan 8, 2025
1 parent 2c7da23 commit e506575
Show file tree
Hide file tree
Showing 103 changed files with 2,888 additions and 1,197 deletions.
5 changes: 5 additions & 0 deletions config/i18n-tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ ignore_unused:
- booleans.*
- '{date,time.formats}.*'
- datetime.distance_in_words.*
- decidim.admin.actions.share
- decidim.admin.participatory_process_steps.default_title
- decidim.admin.admin_log.changeset.*
- decidim.authorization_handlers.{direct,multistep}
Expand All @@ -147,6 +148,10 @@ ignore_unused:
- decidim.forms.file_help.*
- decidim.forms.user_answers_serializer.*
- decidim.forms.admin_log.questionnaire.update
- decidim.surveys.admin_log.survey.create
- decidim.surveys.admin_log.survey.delete
- decidim.surveys.admin_log.survey.publish
- decidim.surveys.admin_log.survey.update
- decidim.proposals.answers.*
- decidim.proposals.collaborative_drafts.show.see_other_versions
- decidim.proposals.collaborative_drafts.wizard_aside.back
Expand Down
1 change: 1 addition & 0 deletions decidim-core/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1794,6 +1794,7 @@ en:
no_stats: There are no statistics yet.
pages_count: Pages
participants_count: Participants
surveys_count: Surveys
users_count: Participants
tags:
filter_results_for_taxonomy: 'Filter results for: %{resource}'
Expand Down
1 change: 1 addition & 0 deletions decidim-core/lib/decidim/core/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ class Engine < ::Rails::Engine
Decidim.icons.register(name: "treasure-map-line", icon: "treasure-map-line", category: "system", description: "", engine: :core)
Decidim.icons.register(name: "chat-new-line", icon: "chat-new-line", category: "system", description: "", engine: :core)
Decidim.icons.register(name: "history", icon: "history-line", category: "system", description: "History timeline", engine: :core)
Decidim.icons.register(name: "survey-line", icon: "survey-line", category: "system", description: "Survey line", engine: :core)
Decidim.icons.register(name: "draft-line", icon: "draft-line", category: "system", description: "", engine: :core)
Decidim.icons.register(name: "user-voice-line", icon: "user-voice-line", category: "system", description: "", engine: :core)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,100 +25,19 @@ def call
Decidim.traceability.perform_action!("update",
@questionnaire,
@user) do
Decidim::Forms::Questionnaire.transaction do
if @questionnaire.questions_editable?
update_questionnaire_questions
delete_answers unless @questionnaire.published?
end

update_questionnaire
end
update_questionnaire
end

broadcast(:ok)
end

private

def update_questionnaire_questions
@form.questions.each do |form_question|
update_questionnaire_question(form_question)
end
end

def update_questionnaire_question(form_question)
question_attributes = {
body: form_question.body,
description: form_question.description,
position: form_question.position,
mandatory: form_question.mandatory,
question_type: form_question.question_type,
max_choices: form_question.max_choices,
max_characters: form_question.max_characters
}

update_nested_model(form_question, question_attributes, @questionnaire.questions) do |question|
form_question.answer_options.each do |form_answer_option|
answer_option_attributes = {
body: form_answer_option.body,
free_text: form_answer_option.free_text
}

update_nested_model(form_answer_option, answer_option_attributes, question.answer_options)
end

form_question.display_conditions.each do |form_display_condition|
type = form_display_condition.condition_type

display_condition_attributes = {
condition_question: form_display_condition.condition_question,
condition_type: form_display_condition.condition_type,
condition_value: type == "match" ? form_display_condition.condition_value : nil,
answer_option: %w(equal not_equal).include?(type) ? form_display_condition.answer_option : nil,
mandatory: form_display_condition.mandatory
}

next if form_display_condition.deleted? && form_display_condition.id.blank?

update_nested_model(form_display_condition, display_condition_attributes, question.display_conditions)
end

form_question.matrix_rows_by_position.each_with_index do |form_matrix_row, idx|
matrix_row_attributes = {
body: form_matrix_row.body,
position: form_matrix_row.position || idx
}

update_nested_model(form_matrix_row, matrix_row_attributes, question.matrix_rows)
end
end
end

def update_nested_model(form, attributes, parent_association)
record = parent_association.find_by(id: form.id) || parent_association.build(attributes)

yield record if block_given?

if record.persisted?
if form.deleted?
record.destroy!
else
record.update!(attributes)
end
else
record.save!
end
end

def update_questionnaire
@questionnaire.update!(title: @form.title,
description: @form.description,
tos: @form.tos)
end

def delete_answers
@questionnaire.answers.destroy_all
end
end
end
end
Expand Down
109 changes: 109 additions & 0 deletions decidim-forms/app/commands/decidim/forms/admin/update_questions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# frozen_string_literal: true

module Decidim
module Forms
module Admin
# This command is executed when the user changes a Questionnaire questions from the admin
# panel.
class UpdateQuestions < Decidim::Command
# Initializes a UpdateQuestions Command.
#
# form - The form from which to get the data.
# questionnaire - The current instance of the questionnaire questions to be updated.
def initialize(form, questionnaire)
@form = form
@questionnaire = questionnaire
end

# Updates the questionnaire if valid.
#
# Broadcasts :ok if successful, :invalid otherwise.
def call
return broadcast(:invalid) if @form.invalid?

Decidim.traceability.perform_action!("update",
@questionnaire,
@form.current_user) do
Decidim::Forms::Questionnaire.transaction do
update_questionnaire_questions if @questionnaire.questions_editable?
end
end

broadcast(:ok)
end

private

def update_questionnaire_questions
@form.questions.each do |form_question|
update_questionnaire_question(form_question)
end
end

def update_questionnaire_question(form_question)
question_attributes = {
body: form_question.body,
description: form_question.description,
position: form_question.position,
mandatory: form_question.mandatory,
question_type: form_question.question_type,
max_choices: form_question.max_choices,
max_characters: form_question.max_characters
}

update_nested_model(form_question, question_attributes, @questionnaire.questions) do |question|
form_question.answer_options.each do |form_answer_option|
answer_option_attributes = {
body: form_answer_option.body,
free_text: form_answer_option.free_text
}

update_nested_model(form_answer_option, answer_option_attributes, question.answer_options)
end

form_question.display_conditions.each do |form_display_condition|
type = form_display_condition.condition_type

display_condition_attributes = {
condition_question: form_display_condition.condition_question,
condition_type: form_display_condition.condition_type,
condition_value: type == "match" ? form_display_condition.condition_value : nil,
answer_option: %w(equal not_equal).include?(type) ? form_display_condition.answer_option : nil,
mandatory: form_display_condition.mandatory
}

next if form_display_condition.deleted? && form_display_condition.id.blank?

update_nested_model(form_display_condition, display_condition_attributes, question.display_conditions)
end

form_question.matrix_rows_by_position.each_with_index do |form_matrix_row, idx|
matrix_row_attributes = {
body: form_matrix_row.body,
position: form_matrix_row.position || idx
}

update_nested_model(form_matrix_row, matrix_row_attributes, question.matrix_rows)
end
end
end

def update_nested_model(form, attributes, parent_association)
record = parent_association.find_by(id: form.id) || parent_association.build(attributes)

yield record if block_given?

if record.persisted?
if form.deleted?
record.destroy!
else
record.update!(attributes)
end
else
record.save!
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,12 @@ def edit

@form = form(Admin::QuestionnaireForm).from_model(questionnaire)

render template: "decidim/forms/admin/questionnaires/edit"
render template: edit_template
end

def update
enforce_permission_to(:update, :questionnaire, questionnaire:)

params["published_at"] = Time.current if params.has_key? "save_and_publish"
@form = form(Admin::QuestionnaireForm).from_params(params)

Admin::UpdateQuestionnaire.call(@form, questionnaire, current_user) do
Expand All @@ -57,7 +56,30 @@ def update
on(:invalid) do
# i18n-tasks-use t("decidim.forms.admin.questionnaires.update.invalid")
flash.now[:alert] = I18n.t("update.invalid", scope: i18n_flashes_scope)
render template: "decidim/forms/admin/questionnaires/edit"
render template: edit_template
end
end
end

def edit_questions
@form = form(Admin::QuestionsForm).from_model(questionnaire)

render template: edit_questions_template
end

# i18n-tasks-use t("decidim.forms.admin.questionnaires.questions_form.update.success")
# i18n-tasks-use t("decidim.forms.admin.questionnaires.update.invalid")
def update_questions
@form = form(Admin::QuestionsForm).from_params(params)
Admin::UpdateQuestions.call(@form, questionnaire) do
on(:ok) do
flash[:notice] = I18n.t("update.success", scope: i18n_questions_flashes_scope)
redirect_to after_update_url
end

on(:invalid) do
flash.now[:alert] = I18n.t("update.invalid", scope: i18n_flashes_scope)
render template: edit_questions_template
end
end
end
Expand Down Expand Up @@ -96,6 +118,12 @@ def public_url
raise "#{self.class.name} is expected to implement #public_url"
end

# Implement this method in your controller to set the URL
# where the user will be render while editing the questionnaire questions
def edit_questions_template
"decidim/forms/admin/questionnaires/edit_questions"
end

# Returns the url to get the answer options json (for the display conditions form)
# for the question with id = params[:id]
def answer_options_url(params)
Expand All @@ -110,10 +138,18 @@ def edit_questionnaire_title

private

def edit_template
"decidim/forms/admin/questionnaires/edit"
end

def i18n_flashes_scope
"decidim.forms.admin.questionnaires"
end

def i18n_questions_flashes_scope
"decidim.forms.admin.questionnaires.questions_form"
end

def questionnaire
@questionnaire ||= Questionnaire.find_by(questionnaire_for:)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ def index
def show
enforce_permission_to :show, :questionnaire_answers

@participant = participant(participants_query.participant(params[:session_token]))
@participant = participant(participants_query.participant(params[:id]))

render template: "decidim/forms/admin/questionnaires/answers/show"
end

def export_response
enforce_permission_to :export_response, :questionnaire_answers

session_token = params[:session_token]
session_token = params[:id]
answers = QuestionnaireUserAnswers.for(questionnaire)

# i18n-tasks-use t("decidim.forms.admin.questionnaires.answers.export_response.title")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,7 @@ class QuestionnaireForm < Decidim::Form
translatable_attribute :description, Decidim::Attributes::RichText
translatable_attribute :tos, Decidim::Attributes::RichText

attribute :published_at, Decidim::Attributes::TimeWithZone
attribute :questions, Array[QuestionForm]

validates :title, :tos, translatable_presence: true

def map_model(model)
self.questions = model.questions.map do |question|
QuestionForm.from_model(question)
end
end
end
end
end
Expand Down
18 changes: 18 additions & 0 deletions decidim-forms/app/forms/decidim/forms/admin/questions_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module Decidim
module Forms
module Admin
# This class holds a Form to update questionnaires questions from Decidim's admin panel.
class QuestionsForm < Decidim::Form
attribute :questions, Array[QuestionForm]

def map_model(model)
self.questions = model.questions.map do |question|
QuestionForm.from_model(question)
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def participant_ids
end

def current_idx
participant_ids.index(params[:session_token])
participant_ids.index(params[:id])
end
end
end
Expand Down
Loading

0 comments on commit e506575

Please sign in to comment.