decidim-surveys 0.29.5 → 0.30.0.rc1
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/cells/decidim/surveys/survey_card_metadata_cell.rb +43 -0
 - data/app/cells/decidim/surveys/survey_cell.rb +26 -0
 - data/app/cells/decidim/surveys/survey_l_cell.rb +34 -0
 - data/app/cells/decidim/surveys/survey_s_cell.rb +21 -0
 - data/app/commands/decidim/surveys/admin/create_survey.rb +21 -0
 - data/app/commands/decidim/surveys/admin/publish_survey.rb +58 -0
 - data/app/commands/decidim/surveys/admin/unpublish_survey.rb +44 -0
 - data/app/commands/decidim/surveys/admin/update_survey.rb +62 -0
 - data/app/commands/decidim/surveys/publish_answers.rb +55 -0
 - data/app/commands/decidim/surveys/unpublish_answers.rb +55 -0
 - data/app/controllers/concerns/decidim/surveys/admin/filterable.rb +23 -0
 - data/app/controllers/decidim/surveys/admin/answers_controller.rb +73 -0
 - data/app/controllers/decidim/surveys/admin/publish_answers_controller.rb +66 -0
 - data/app/controllers/decidim/surveys/admin/surveys_controller.rb +99 -11
 - data/app/controllers/decidim/surveys/surveys_controller.rb +50 -5
 - data/app/forms/decidim/surveys/admin/survey_form.rb +18 -0
 - data/app/helpers/decidim/surveys/application_helper.rb +34 -0
 - data/app/helpers/decidim/surveys/publish_answers_helper.rb +104 -0
 - data/app/helpers/decidim/surveys/survey_helper.rb +4 -0
 - data/app/models/decidim/surveys/survey.rb +67 -12
 - data/app/permissions/decidim/surveys/admin/permissions.rb +10 -3
 - data/app/permissions/decidim/surveys/permissions.rb +0 -2
 - data/app/presenters/decidim/surveys/admin_log/survey_presenter.rb +34 -0
 - data/app/queries/decidim/surveys/metrics/answers_metric_manage.rb +3 -3
 - data/app/queries/decidim/surveys/metrics/survey_participants_metric_measure.rb +2 -2
 - data/app/views/decidim/surveys/admin/answers/index.html.erb +49 -0
 - data/app/views/decidim/surveys/admin/answers/show.html.erb +43 -0
 - data/app/views/decidim/surveys/admin/publish_answers/_toggle_button.html.erb +22 -0
 - data/app/views/decidim/surveys/admin/publish_answers/index.html.erb +44 -0
 - data/app/views/decidim/surveys/admin/surveys/_form.html.erb +28 -0
 - data/app/views/decidim/surveys/admin/surveys/edit.html.erb +4 -21
 - data/app/views/decidim/surveys/admin/surveys/edit_questions.html.erb +47 -0
 - data/app/views/decidim/surveys/admin/surveys/index.html.erb +51 -0
 - data/app/views/decidim/surveys/surveys/_published_questions_answers.html.erb +13 -0
 - data/app/views/decidim/surveys/surveys/_surveys.html.erb +13 -0
 - data/app/views/decidim/surveys/surveys/index.html.erb +27 -0
 - data/app/views/decidim/surveys/surveys/index.js.erb +4 -0
 - data/app/views/decidim/surveys/surveys/not_allowed.html.erb +22 -0
 - data/config/locales/ar.yml +4 -3
 - data/config/locales/bg.yml +0 -14
 - data/config/locales/ca.yml +83 -14
 - data/config/locales/cs.yml +87 -14
 - data/config/locales/de.yml +83 -14
 - data/config/locales/el.yml +0 -11
 - data/config/locales/en.yml +83 -14
 - data/config/locales/es-MX.yml +83 -14
 - data/config/locales/es-PY.yml +83 -14
 - data/config/locales/es.yml +83 -14
 - data/config/locales/eu.yml +83 -14
 - data/config/locales/fi-plain.yml +83 -14
 - data/config/locales/fi.yml +83 -14
 - data/config/locales/fr-CA.yml +74 -14
 - data/config/locales/fr.yml +74 -14
 - data/config/locales/ga-IE.yml +0 -4
 - data/config/locales/gl.yml +0 -10
 - data/config/locales/hu.yml +0 -3
 - data/config/locales/id-ID.yml +0 -2
 - data/config/locales/is-IS.yml +0 -2
 - data/config/locales/it.yml +3 -14
 - data/config/locales/ja.yml +86 -19
 - data/config/locales/lb.yml +0 -10
 - data/config/locales/lt.yml +0 -11
 - data/config/locales/lv.yml +0 -3
 - data/config/locales/nl.yml +0 -10
 - data/config/locales/no.yml +0 -10
 - data/config/locales/pl.yml +0 -14
 - data/config/locales/pt-BR.yml +0 -14
 - data/config/locales/pt.yml +0 -10
 - data/config/locales/ro-RO.yml +0 -11
 - data/config/locales/ru.yml +0 -2
 - data/config/locales/sk.yml +0 -3
 - data/config/locales/sv.yml +0 -14
 - data/config/locales/tr-TR.yml +0 -16
 - data/config/locales/uk.yml +0 -2
 - data/config/locales/zh-CN.yml +0 -6
 - data/config/locales/zh-TW.yml +0 -11
 - data/db/migrate/20240828103030_add_deleted_at_to_decidim_surveys_surveys.rb +8 -0
 - data/db/migrate/20240925124312_add_settings_to_decidim_surveys_surveys.rb +42 -0
 - data/db/migrate/20250115193836_add_allow_survey_editing.rb +7 -0
 - data/decidim-surveys.gemspec +1 -1
 - data/lib/decidim/api/survey_type.rb +2 -2
 - data/lib/decidim/api/surveys_type.rb +4 -5
 - data/lib/decidim/surveys/admin_engine.rb +15 -5
 - data/lib/decidim/surveys/component.rb +7 -24
 - data/lib/decidim/surveys/engine.rb +2 -2
 - data/lib/decidim/surveys/seeds.rb +115 -10
 - data/lib/decidim/surveys/test/factories.rb +24 -0
 - data/lib/decidim/surveys/version.rb +1 -1
 - metadata +48 -19
 - data/app/commands/decidim/surveys/create_survey.rb +0 -19
 - data/app/views/decidim/surveys/admin/component/_actions.html.erb +0 -44
 - data/config/locales/ca-IT.yml +0 -77
 
| 
         @@ -3,43 +3,131 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module Decidim
         
     | 
| 
       4 
4 
     | 
    
         
             
              module Surveys
         
     | 
| 
       5 
5 
     | 
    
         
             
                module Admin
         
     | 
| 
       6 
     | 
    
         
            -
                  # This controller allows the user  
     | 
| 
      
 6 
     | 
    
         
            +
                  # This controller allows the user create and edit questionnaires for Surveys.
         
     | 
| 
       7 
7 
     | 
    
         
             
                  class SurveysController < Admin::ApplicationController
         
     | 
| 
       8 
8 
     | 
    
         
             
                    include Decidim::Forms::Admin::Concerns::HasQuestionnaire
         
     | 
| 
       9 
     | 
    
         
            -
                    include Decidim::Forms::Admin::Concerns:: 
     | 
| 
      
 9 
     | 
    
         
            +
                    include Decidim::Forms::Admin::Concerns::HasQuestionnaireAnswersUrlHelper
         
     | 
| 
      
 10 
     | 
    
         
            +
                    include Decidim::Surveys::Admin::Filterable
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    helper_method :surveys
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    def index; end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    def create
         
     | 
| 
      
 17 
     | 
    
         
            +
                      enforce_permission_to(:create, :questionnaire)
         
     | 
| 
      
 18 
     | 
    
         
            +
                      Decidim::Surveys::Admin::CreateSurvey.call(current_component) do
         
     | 
| 
      
 19 
     | 
    
         
            +
                        on(:ok) do |survey|
         
     | 
| 
      
 20 
     | 
    
         
            +
                          flash[:notice] = I18n.t("create.success", scope: "decidim.surveys.admin.surveys")
         
     | 
| 
      
 21 
     | 
    
         
            +
                          redirect_to edit_survey_path(survey)
         
     | 
| 
      
 22 
     | 
    
         
            +
                        end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                        on(:invalid) do
         
     | 
| 
      
 25 
     | 
    
         
            +
                          flash.now[:alert] = I18n.t("create.invalid", scope: "decidim.surveys.admin.surveys")
         
     | 
| 
      
 26 
     | 
    
         
            +
                          render action: "index"
         
     | 
| 
      
 27 
     | 
    
         
            +
                        end
         
     | 
| 
      
 28 
     | 
    
         
            +
                      end
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
       10 
30 
     | 
    
         | 
| 
       11 
31 
     | 
    
         
             
                    def edit
         
     | 
| 
       12 
32 
     | 
    
         
             
                      enforce_permission_to(:update, :questionnaire, questionnaire:)
         
     | 
| 
      
 33 
     | 
    
         
            +
                      @form = form(Admin::SurveyForm).from_model(survey)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    def update
         
     | 
| 
      
 37 
     | 
    
         
            +
                      enforce_permission_to(:update, :questionnaire, questionnaire:)
         
     | 
| 
      
 38 
     | 
    
         
            +
                      @form = form(Admin::SurveyForm).from_params(params)
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                      Admin::UpdateSurvey.call(@form, survey, current_user) do
         
     | 
| 
      
 41 
     | 
    
         
            +
                        on(:ok) do
         
     | 
| 
      
 42 
     | 
    
         
            +
                          flash[:notice] = I18n.t("update.success", scope: "decidim.surveys.admin.surveys")
         
     | 
| 
      
 43 
     | 
    
         
            +
                          redirect_to(surveys_path) && return
         
     | 
| 
      
 44 
     | 
    
         
            +
                        end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                        on(:invalid) do
         
     | 
| 
      
 47 
     | 
    
         
            +
                          flash.now[:alert] = I18n.t("update.invalid", scope: "decidim.surveys.admin.surveys")
         
     | 
| 
      
 48 
     | 
    
         
            +
                          render action: "edit"
         
     | 
| 
      
 49 
     | 
    
         
            +
                        end
         
     | 
| 
      
 50 
     | 
    
         
            +
                      end
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    def publish
         
     | 
| 
      
 54 
     | 
    
         
            +
                      enforce_permission_to(:update, :questionnaire, questionnaire:)
         
     | 
| 
      
 55 
     | 
    
         
            +
                      Decidim::Surveys::Admin::PublishSurvey.call(survey, current_user) do
         
     | 
| 
      
 56 
     | 
    
         
            +
                        on(:ok) do
         
     | 
| 
      
 57 
     | 
    
         
            +
                          flash[:notice] = I18n.t("publish.success", scope: "decidim.surveys.admin.surveys")
         
     | 
| 
      
 58 
     | 
    
         
            +
                          redirect_to surveys_path
         
     | 
| 
      
 59 
     | 
    
         
            +
                        end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                        on(:invalid) do
         
     | 
| 
      
 62 
     | 
    
         
            +
                          flash.now[:alert] = I18n.t("publish.invalid", scope: "decidim.surveys.admin.surveys")
         
     | 
| 
      
 63 
     | 
    
         
            +
                          render action: "index"
         
     | 
| 
      
 64 
     | 
    
         
            +
                        end
         
     | 
| 
      
 65 
     | 
    
         
            +
                      end
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    def unpublish
         
     | 
| 
      
 69 
     | 
    
         
            +
                      enforce_permission_to(:update, :questionnaire, questionnaire:)
         
     | 
| 
      
 70 
     | 
    
         
            +
                      Decidim::Surveys::Admin::UnpublishSurvey.call(survey, current_user) do
         
     | 
| 
      
 71 
     | 
    
         
            +
                        on(:ok) do
         
     | 
| 
      
 72 
     | 
    
         
            +
                          flash[:notice] = I18n.t("unpublish.success", scope: "decidim.surveys.admin.surveys")
         
     | 
| 
      
 73 
     | 
    
         
            +
                          redirect_to surveys_path
         
     | 
| 
      
 74 
     | 
    
         
            +
                        end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                        on(:invalid) do
         
     | 
| 
      
 77 
     | 
    
         
            +
                          flash.now[:alert] = I18n.t("unpublish.invalid", scope: "decidim.surveys.admin.surveys")
         
     | 
| 
      
 78 
     | 
    
         
            +
                          render action: "index"
         
     | 
| 
      
 79 
     | 
    
         
            +
                        end
         
     | 
| 
      
 80 
     | 
    
         
            +
                      end
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
       13 
82 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
      
 83 
     | 
    
         
            +
                    def destroy
         
     | 
| 
      
 84 
     | 
    
         
            +
                      enforce_permission_to(:destroy, :questionnaire, questionnaire:)
         
     | 
| 
      
 85 
     | 
    
         
            +
                      Decidim::Commands::DestroyResource.call(survey, current_user) do
         
     | 
| 
      
 86 
     | 
    
         
            +
                        on(:ok) do
         
     | 
| 
      
 87 
     | 
    
         
            +
                          flash[:notice] = I18n.t("destroy.success", scope: "decidim.surveys.admin.surveys")
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                          redirect_to surveys_path
         
     | 
| 
      
 90 
     | 
    
         
            +
                        end
         
     | 
| 
      
 91 
     | 
    
         
            +
                      end
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                    def edit_questions_template
         
     | 
| 
      
 95 
     | 
    
         
            +
                      "decidim/surveys/admin/surveys/edit_questions"
         
     | 
| 
       15 
96 
     | 
    
         
             
                    end
         
     | 
| 
       16 
97 
     | 
    
         | 
| 
       17 
98 
     | 
    
         
             
                    def questionnaire_for
         
     | 
| 
       18 
99 
     | 
    
         
             
                      survey
         
     | 
| 
       19 
100 
     | 
    
         
             
                    end
         
     | 
| 
       20 
101 
     | 
    
         | 
| 
      
 102 
     | 
    
         
            +
                    def after_update_url
         
     | 
| 
      
 103 
     | 
    
         
            +
                      surveys_path
         
     | 
| 
      
 104 
     | 
    
         
            +
                    end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                    def questionnaire_participants_url
         
     | 
| 
      
 107 
     | 
    
         
            +
                      Decidim::EngineRouter.admin_proxy(survey.component).survey_answers_path(survey)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
       21 
110 
     | 
    
         
             
                    # Specify the public url from which the survey can be viewed and answered
         
     | 
| 
       22 
111 
     | 
    
         
             
                    def public_url
         
     | 
| 
       23 
112 
     | 
    
         
             
                      Decidim::EngineRouter.main_proxy(current_component).survey_path(survey)
         
     | 
| 
       24 
113 
     | 
    
         
             
                    end
         
     | 
| 
       25 
114 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
                    # Specify where to redirect after exporting a user response
         
     | 
| 
       27 
     | 
    
         
            -
                    def questionnaire_participant_answers_url(session_token)
         
     | 
| 
       28 
     | 
    
         
            -
                      Decidim::EngineRouter.admin_proxy(survey.component).show_survey_path(session_token:)
         
     | 
| 
       29 
     | 
    
         
            -
                    end
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
115 
     | 
    
         
             
                    def edit_questionnaire_title
         
     | 
| 
       32 
116 
     | 
    
         
             
                      t(:title, scope: "decidim.forms.admin.questionnaires.form", questionnaire_for: translated_attribute(current_component.name))
         
     | 
| 
       33 
117 
     | 
    
         
             
                    end
         
     | 
| 
       34 
118 
     | 
    
         | 
| 
       35 
119 
     | 
    
         
             
                    private
         
     | 
| 
       36 
120 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
                    def  
     | 
| 
       38 
     | 
    
         
            -
                       
     | 
| 
      
 121 
     | 
    
         
            +
                    def surveys
         
     | 
| 
      
 122 
     | 
    
         
            +
                      @surveys ||= filtered_collection
         
     | 
| 
       39 
123 
     | 
    
         
             
                    end
         
     | 
| 
       40 
124 
     | 
    
         | 
| 
       41 
125 
     | 
    
         
             
                    def survey
         
     | 
| 
       42 
     | 
    
         
            -
                      @survey ||=  
     | 
| 
      
 126 
     | 
    
         
            +
                      @survey ||= collection.find(params[:id])
         
     | 
| 
      
 127 
     | 
    
         
            +
                    end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                    def collection
         
     | 
| 
      
 130 
     | 
    
         
            +
                      @collection ||= Decidim::Surveys::Survey.where(component: current_component)
         
     | 
| 
       43 
131 
     | 
    
         
             
                    end
         
     | 
| 
       44 
132 
     | 
    
         
             
                  end
         
     | 
| 
       45 
133 
     | 
    
         
             
                end
         
     | 
| 
         @@ -7,12 +7,24 @@ module Decidim 
     | 
|
| 
       7 
7 
     | 
    
         
             
                  include Decidim::Forms::Concerns::HasQuestionnaire
         
     | 
| 
       8 
8 
     | 
    
         
             
                  include Decidim::ComponentPathHelper
         
     | 
| 
       9 
9 
     | 
    
         
             
                  include Decidim::Surveys::SurveyHelper
         
     | 
| 
      
 10 
     | 
    
         
            +
                  include FilterResource
         
     | 
| 
      
 11 
     | 
    
         
            +
                  include Paginable
         
     | 
| 
       10 
12 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                   
     | 
| 
      
 13 
     | 
    
         
            +
                  helper PublishAnswersHelper
         
     | 
| 
      
 14 
     | 
    
         
            +
                  helper_method :authorizations, :surveys, :show_published_questions_answers?
         
     | 
| 
       12 
15 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                   
     | 
| 
      
 16 
     | 
    
         
            +
                  before_action :check_permissions, except: [:index]
         
     | 
| 
      
 17 
     | 
    
         
            +
                  before_action :check_editable, only: [:edit]
         
     | 
| 
       14 
18 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                   
     | 
| 
      
 19 
     | 
    
         
            +
                  def index; end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def edit
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @form = form(Decidim::Forms::QuestionnaireForm).from_model(questionnaire)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @form.add_answers!(questionnaire:, session_token:, ip_hash:)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @form.allow_editing_answers = questionnaire.questionnaire_for&.allow_editing_answers?
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    render template: "decidim/forms/questionnaires/edit"
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
       16 
28 
     | 
    
         | 
| 
       17 
29 
     | 
    
         
             
                  def check_permissions
         
     | 
| 
       18 
30 
     | 
    
         
             
                    render :no_permission unless action_authorized_to(:answer, resource: survey).ok?
         
     | 
| 
         @@ -24,8 +36,27 @@ module Decidim 
     | 
|
| 
       24 
36 
     | 
    
         | 
| 
       25 
37 
     | 
    
         
             
                  protected
         
     | 
| 
       26 
38 
     | 
    
         | 
| 
      
 39 
     | 
    
         
            +
                  def check_editable
         
     | 
| 
      
 40 
     | 
    
         
            +
                    return if allow_editing_answers?
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    flash.now[:error] = t("decidim.forms.step_navigation.show.disallowed")
         
     | 
| 
      
 43 
     | 
    
         
            +
                    render :not_allowed
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  def allow_editing_answers?
         
     | 
| 
      
 47 
     | 
    
         
            +
                    visitor_can_edit_answers? && survey.open?
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  def show_published_questions_answers?
         
     | 
| 
      
 51 
     | 
    
         
            +
                    survey.closed? && survey.questionnaire.questions.pluck(:survey_answers_published_at).any?
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
       27 
54 
     | 
    
         
             
                  def allow_answers?
         
     | 
| 
       28 
     | 
    
         
            -
                    !current_component.published? ||  
     | 
| 
      
 55 
     | 
    
         
            +
                    !current_component.published? || survey.open?
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def allow_unregistered?
         
     | 
| 
      
 59 
     | 
    
         
            +
                    survey.allow_unregistered
         
     | 
| 
       29 
60 
     | 
    
         
             
                  end
         
     | 
| 
       30 
61 
     | 
    
         | 
| 
       31 
62 
     | 
    
         
             
                  def form_path
         
     | 
| 
         @@ -38,8 +69,22 @@ module Decidim 
     | 
|
| 
       38 
69 
     | 
    
         
             
                    "decidim.surveys.surveys"
         
     | 
| 
       39 
70 
     | 
    
         
             
                  end
         
     | 
| 
       40 
71 
     | 
    
         | 
| 
      
 72 
     | 
    
         
            +
                  def surveys
         
     | 
| 
      
 73 
     | 
    
         
            +
                    paginate(search.result).published
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
       41 
76 
     | 
    
         
             
                  def survey
         
     | 
| 
       42 
     | 
    
         
            -
                    @survey ||=  
     | 
| 
      
 77 
     | 
    
         
            +
                    @survey ||= search_collection.find_by(id: params[:id])
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  def search_collection
         
     | 
| 
      
 81 
     | 
    
         
            +
                    @search_collection ||= Decidim::Surveys::Survey.where(component: current_component)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  def default_filter_params
         
     | 
| 
      
 85 
     | 
    
         
            +
                    {
         
     | 
| 
      
 86 
     | 
    
         
            +
                      with_any_state: %w(open)
         
     | 
| 
      
 87 
     | 
    
         
            +
                    }
         
     | 
| 
       43 
88 
     | 
    
         
             
                  end
         
     | 
| 
       44 
89 
     | 
    
         
             
                end
         
     | 
| 
       45 
90 
     | 
    
         
             
              end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Decidim
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Surveys
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Admin
         
     | 
| 
      
 6 
     | 
    
         
            +
                  class SurveyForm < Decidim::Forms::Admin::QuestionnaireForm
         
     | 
| 
      
 7 
     | 
    
         
            +
                    translatable_attribute :announcement, String
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    attribute :allow_answers, Boolean
         
     | 
| 
      
 10 
     | 
    
         
            +
                    attribute :allow_unregistered, Boolean
         
     | 
| 
      
 11 
     | 
    
         
            +
                    attribute :clean_after_publish, Boolean
         
     | 
| 
      
 12 
     | 
    
         
            +
                    attribute :allow_editing_answers, Boolean
         
     | 
| 
      
 13 
     | 
    
         
            +
                    attribute :starts_at, Decidim::Attributes::TimeWithZone
         
     | 
| 
      
 14 
     | 
    
         
            +
                    attribute :ends_at, Decidim::Attributes::TimeWithZone
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Decidim
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Surveys
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Custom helpers, scoped to the surveys engine.
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                module ApplicationHelper
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include PaginateHelper
         
     | 
| 
      
 9 
     | 
    
         
            +
                  include Decidim::SanitizeHelper
         
     | 
| 
      
 10 
     | 
    
         
            +
                  include Decidim::CheckBoxesTreeHelper
         
     | 
| 
      
 11 
     | 
    
         
            +
                  include Decidim::RichTextEditorHelper
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  # Returns a TreeNode to be used in the list filters to filter surveys by
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # its state.
         
     | 
| 
      
 15 
     | 
    
         
            +
                  def filter_surveys_date_values
         
     | 
| 
      
 16 
     | 
    
         
            +
                    [
         
     | 
| 
      
 17 
     | 
    
         
            +
                      ["all", t("all", scope: "decidim.surveys.surveys.filters")],
         
     | 
| 
      
 18 
     | 
    
         
            +
                      ["open", { checked: true }, t("open", scope: "decidim.surveys.surveys.filters.state_values")],
         
     | 
| 
      
 19 
     | 
    
         
            +
                      ["closed", t("closed", scope: "decidim.surveys.surveys.filters.state_values")]
         
     | 
| 
      
 20 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def filter_sections
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @filter_sections ||= [{
         
     | 
| 
      
 25 
     | 
    
         
            +
                      method: :with_any_state,
         
     | 
| 
      
 26 
     | 
    
         
            +
                      collection: filter_surveys_date_values,
         
     | 
| 
      
 27 
     | 
    
         
            +
                      label: t("decidim.proposals.proposals.filters.state"),
         
     | 
| 
      
 28 
     | 
    
         
            +
                      id: "state",
         
     | 
| 
      
 29 
     | 
    
         
            +
                      type: :radio_buttons
         
     | 
| 
      
 30 
     | 
    
         
            +
                    }]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,104 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Decidim
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Surveys
         
     | 
| 
      
 5 
     | 
    
         
            +
                module PublishAnswersHelper
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def question_answer_is_publicable(question_type)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    ignored_question_types = %w(short_answer long_answer separator files).freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    ignored_question_types.exclude?(question_type)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  # Renders the chart for the given question.
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # Uses chartkick to render the chart.
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # @param question_id [Integer] the question id for Decidim:
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def chart_for_question(question_id)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    question = Decidim::Forms::Question.includes(answers: { choices: [:answer_option, :matrix_row] }).find(question_id)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    Chartkick.options = {
         
     | 
| 
      
 20 
     | 
    
         
            +
                      library: { animation: { easing: "easeOutQuart" } },
         
     | 
| 
      
 21 
     | 
    
         
            +
                      colors: colors_list
         
     | 
| 
      
 22 
     | 
    
         
            +
                    }
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    case question.question_type
         
     | 
| 
      
 25 
     | 
    
         
            +
                    when "single_option", "multiple_option"
         
     | 
| 
      
 26 
     | 
    
         
            +
                      options_column_chart_wrapper(question)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    when "matrix_single", "matrix_multiple"
         
     | 
| 
      
 28 
     | 
    
         
            +
                      matrix_stack_chart_wrapper(question)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    when "sorting"
         
     | 
| 
      
 30 
     | 
    
         
            +
                      sorting_stack_chart_wrapper(question)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    else
         
     | 
| 
      
 32 
     | 
    
         
            +
                      "Unknown question type"
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  private
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def sorting_stack_chart_wrapper(question)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    counts = Hash.new { |hash, key| hash[key] = Hash.new(0) }
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    question.answers.each do |answer|
         
     | 
| 
      
 42 
     | 
    
         
            +
                      answer.choices.each do |choice|
         
     | 
| 
      
 43 
     | 
    
         
            +
                        name = translated_attribute(choice.answer_option.body)
         
     | 
| 
      
 44 
     | 
    
         
            +
                        row = choice.position + 1
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                        counts[row][name] += 1
         
     | 
| 
      
 47 
     | 
    
         
            +
                      end
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    tally = counts.map do |name, row_data|
         
     | 
| 
      
 51 
     | 
    
         
            +
                      {
         
     | 
| 
      
 52 
     | 
    
         
            +
                        name:,
         
     | 
| 
      
 53 
     | 
    
         
            +
                        data: row_data.map { |row, count| [row, count] }
         
     | 
| 
      
 54 
     | 
    
         
            +
                      }
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    bar_chart(tally.sort_by { |data| data[:name] }, stacked: true, download: true)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def matrix_stack_chart_wrapper(question)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    counts = Hash.new { |hash, key| hash[key] = Hash.new(0) }
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    question.answers.each do |answer|
         
     | 
| 
      
 64 
     | 
    
         
            +
                      answer.choices.each do |choice|
         
     | 
| 
      
 65 
     | 
    
         
            +
                        name = translated_attribute(choice.answer_option.body)
         
     | 
| 
      
 66 
     | 
    
         
            +
                        row = translated_attribute(choice.matrix_row.body)
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                        counts[name][row] += 1
         
     | 
| 
      
 69 
     | 
    
         
            +
                      end
         
     | 
| 
      
 70 
     | 
    
         
            +
                    end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    tally = counts.map do |name, row_data|
         
     | 
| 
      
 73 
     | 
    
         
            +
                      {
         
     | 
| 
      
 74 
     | 
    
         
            +
                        name:,
         
     | 
| 
      
 75 
     | 
    
         
            +
                        data: row_data.map { |row, count| [row, count] }
         
     | 
| 
      
 76 
     | 
    
         
            +
                      }
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    column_chart(tally, stacked: true, legend: :right, download: true)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def options_column_chart_wrapper(question)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    tally = question.answers.map { |answer| answer.choices.map { |choice| translated_attribute(choice.answer_option.body) } }.tally
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    column_chart(tally, download: true)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  def colors_list
         
     | 
| 
      
 89 
     | 
    
         
            +
                    %w(
         
     | 
| 
      
 90 
     | 
    
         
            +
                      #3366CC
         
     | 
| 
      
 91 
     | 
    
         
            +
                      #DC3912
         
     | 
| 
      
 92 
     | 
    
         
            +
                      #FF9900
         
     | 
| 
      
 93 
     | 
    
         
            +
                      #109618
         
     | 
| 
      
 94 
     | 
    
         
            +
                      #3B3EAC
         
     | 
| 
      
 95 
     | 
    
         
            +
                      #0099C6
         
     | 
| 
      
 96 
     | 
    
         
            +
                      #DD4477
         
     | 
| 
      
 97 
     | 
    
         
            +
                      #66AA00
         
     | 
| 
      
 98 
     | 
    
         
            +
                      #B82E2E
         
     | 
| 
      
 99 
     | 
    
         
            +
                      #316395
         
     | 
| 
      
 100 
     | 
    
         
            +
                    )
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
              end
         
     | 
| 
      
 104 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -26,6 +26,10 @@ module Decidim 
     | 
|
| 
       26 
26 
     | 
    
         
             
                  def authorizations
         
     | 
| 
       27 
27 
     | 
    
         
             
                    @authorizations ||= action_authorized_to(:answer, resource: questionnaire_for)
         
     | 
| 
       28 
28 
     | 
    
         
             
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def filter_date_values
         
     | 
| 
      
 31 
     | 
    
         
            +
                    flat_filter_values(:all, :open, :closed, scope: "decidim.surveys.surveys.filters.date_values")
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
       29 
33 
     | 
    
         
             
                end
         
     | 
| 
       30 
34 
     | 
    
         
             
              end
         
     | 
| 
       31 
35 
     | 
    
         
             
            end
         
     | 
| 
         @@ -7,33 +7,88 @@ module Decidim 
     | 
|
| 
       7 
7 
     | 
    
         
             
                  include Decidim::Resourceable
         
     | 
| 
       8 
8 
     | 
    
         
             
                  include Decidim::Forms::HasQuestionnaire
         
     | 
| 
       9 
9 
     | 
    
         
             
                  include Decidim::HasComponent
         
     | 
| 
      
 10 
     | 
    
         
            +
                  include Decidim::FilterableResource
         
     | 
| 
      
 11 
     | 
    
         
            +
                  include Decidim::Publicable
         
     | 
| 
       10 
12 
     | 
    
         | 
| 
       11 
13 
     | 
    
         
             
                  component_manifest_name "surveys"
         
     | 
| 
       12 
14 
     | 
    
         | 
| 
       13 
15 
     | 
    
         
             
                  delegate :title, to: :questionnaire
         
     | 
| 
      
 16 
     | 
    
         
            +
                  delegate :description, to: :questionnaire
         
     | 
| 
      
 17 
     | 
    
         
            +
                  delegate :tos, to: :questionnaire
         
     | 
| 
       14 
18 
     | 
    
         | 
| 
       15 
19 
     | 
    
         
             
                  validates :questionnaire, presence: true
         
     | 
| 
       16 
20 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
                   
     | 
| 
       18 
     | 
    
         
            -
                     
     | 
| 
      
 21 
     | 
    
         
            +
                  scope :open, lambda {
         
     | 
| 
      
 22 
     | 
    
         
            +
                    where(allow_answers: true)
         
     | 
| 
      
 23 
     | 
    
         
            +
                      .where(starts_at: nil, ends_at: nil).or(
         
     | 
| 
      
 24 
     | 
    
         
            +
                        where("starts_at <= ? AND (ends_at IS NULL OR ends_at > ?)", Time.current, Time.current)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      ).or(
         
     | 
| 
      
 26 
     | 
    
         
            +
                        where("ends_at > ? AND (starts_at IS NULL OR starts_at <= ?)", Time.current, Time.current)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      )
         
     | 
| 
      
 28 
     | 
    
         
            +
                  }
         
     | 
| 
      
 29 
     | 
    
         
            +
                  scope :closed, lambda {
         
     | 
| 
      
 30 
     | 
    
         
            +
                    where(allow_answers: false).or(
         
     | 
| 
      
 31 
     | 
    
         
            +
                      where("starts_at > ?", Time.current).or(
         
     | 
| 
      
 32 
     | 
    
         
            +
                        where(ends_at: ...Time.current)
         
     | 
| 
      
 33 
     | 
    
         
            +
                      )
         
     | 
| 
      
 34 
     | 
    
         
            +
                    )
         
     | 
| 
      
 35 
     | 
    
         
            +
                  }
         
     | 
| 
      
 36 
     | 
    
         
            +
                  scope :published, -> { where.not(published_at: nil) }
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  scope_search_multi :with_any_state, [:open, :closed]
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def open?
         
     | 
| 
      
 41 
     | 
    
         
            +
                    return false if allow_answers.blank?
         
     | 
| 
      
 42 
     | 
    
         
            +
                    return true if time_indefinite?
         
     | 
| 
      
 43 
     | 
    
         
            +
                    return true if started_but_no_end?
         
     | 
| 
      
 44 
     | 
    
         
            +
                    return true if no_start_but_ends_later?
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    return within_time_range? if time_range_defined?
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    false
         
     | 
| 
       19 
49 
     | 
    
         
             
                  end
         
     | 
| 
       20 
50 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                  def  
     | 
| 
       22 
     | 
    
         
            -
                     
     | 
| 
      
 51 
     | 
    
         
            +
                  def closed?
         
     | 
| 
      
 52 
     | 
    
         
            +
                    !open?
         
     | 
| 
       23 
53 
     | 
    
         
             
                  end
         
     | 
| 
       24 
54 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                  def  
     | 
| 
       26 
     | 
    
         
            -
                     
     | 
| 
      
 55 
     | 
    
         
            +
                  def self.ransackable_scopes(_auth_object = nil)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    [:with_any_state]
         
     | 
| 
       27 
57 
     | 
    
         
             
                  end
         
     | 
| 
       28 
58 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                  def  
     | 
| 
       30 
     | 
    
         
            -
                     
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                    return true if starts_at.blank? && ends_at.future?
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def self.ransackable_attributes(_auth_object = nil)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    %w(ends_at starts_at allow_answers)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
       33 
62 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def self.log_presenter_class_for(_log)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    Decidim::Surveys::AdminLog::SurveyPresenter
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
       35 
66 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
      
 67 
     | 
    
         
            +
                  # Public: Overrides the `allow_resource_permissions?` Resourceable concern method.
         
     | 
| 
      
 68 
     | 
    
         
            +
                  def allow_resource_permissions?
         
     | 
| 
      
 69 
     | 
    
         
            +
                    true
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  private
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  def time_indefinite?
         
     | 
| 
      
 75 
     | 
    
         
            +
                    starts_at.blank? && ends_at.blank?
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  def started_but_no_end?
         
     | 
| 
      
 79 
     | 
    
         
            +
                    ends_at.blank? && starts_at.past?
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def no_start_but_ends_later?
         
     | 
| 
      
 83 
     | 
    
         
            +
                    starts_at.blank? && ends_at.future?
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                  def time_range_defined?
         
     | 
| 
      
 87 
     | 
    
         
            +
                    starts_at.present? && ends_at.present?
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  def within_time_range?
         
     | 
| 
      
 91 
     | 
    
         
            +
                    Time.zone.now.between?(starts_at, ends_at)
         
     | 
| 
       37 
92 
     | 
    
         
             
                  end
         
     | 
| 
       38 
93 
     | 
    
         
             
                end
         
     | 
| 
       39 
94 
     | 
    
         
             
              end
         
     | 
| 
         @@ -4,8 +4,6 @@ module Decidim 
     | 
|
| 
       4 
4 
     | 
    
         
             
              module Surveys
         
     | 
| 
       5 
5 
     | 
    
         
             
                module Admin
         
     | 
| 
       6 
6 
     | 
    
         
             
                  class Permissions < Decidim::DefaultPermissions
         
     | 
| 
       7 
     | 
    
         
            -
                    include Decidim::UserRoleChecker
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
7 
     | 
    
         
             
                    def permissions
         
     | 
| 
       10 
8 
     | 
    
         
             
                      return permission_action unless user
         
     | 
| 
       11 
9 
     | 
    
         | 
| 
         @@ -14,7 +12,7 @@ module Decidim 
     | 
|
| 
       14 
12 
     | 
    
         
             
                      case permission_action.subject
         
     | 
| 
       15 
13 
     | 
    
         
             
                      when :questionnaire
         
     | 
| 
       16 
14 
     | 
    
         
             
                        case permission_action.action
         
     | 
| 
       17 
     | 
    
         
            -
                        when :export_answers, :update, :create, :destroy 
     | 
| 
      
 15 
     | 
    
         
            +
                        when :export_answers, :update, :create, :destroy
         
     | 
| 
       18 
16 
     | 
    
         
             
                          permission_action.allow!
         
     | 
| 
       19 
17 
     | 
    
         
             
                        end
         
     | 
| 
       20 
18 
     | 
    
         
             
                      when :questionnaire_answers
         
     | 
| 
         @@ -22,6 +20,15 @@ module Decidim 
     | 
|
| 
       22 
20 
     | 
    
         
             
                        when :index, :show, :export_response
         
     | 
| 
       23 
21 
     | 
    
         
             
                          permission_action.allow!
         
     | 
| 
       24 
22 
     | 
    
         
             
                        end
         
     | 
| 
      
 23 
     | 
    
         
            +
                      when :questionnaire_publish_answers
         
     | 
| 
      
 24 
     | 
    
         
            +
                        case permission_action.action
         
     | 
| 
      
 25 
     | 
    
         
            +
                        when :index, :update, :destroy
         
     | 
| 
      
 26 
     | 
    
         
            +
                          if context.fetch(:survey).allow_answers
         
     | 
| 
      
 27 
     | 
    
         
            +
                            permission_action.disallow!
         
     | 
| 
      
 28 
     | 
    
         
            +
                          else
         
     | 
| 
      
 29 
     | 
    
         
            +
                            permission_action.allow!
         
     | 
| 
      
 30 
     | 
    
         
            +
                          end
         
     | 
| 
      
 31 
     | 
    
         
            +
                        end
         
     | 
| 
       25 
32 
     | 
    
         
             
                      end
         
     | 
| 
       26 
33 
     | 
    
         | 
| 
       27 
34 
     | 
    
         
             
                      permission_action
         
     | 
| 
         @@ -4,8 +4,6 @@ module Decidim 
     | 
|
| 
       4 
4 
     | 
    
         
             
              module Surveys
         
     | 
| 
       5 
5 
     | 
    
         
             
                class Permissions < Decidim::DefaultPermissions
         
     | 
| 
       6 
6 
     | 
    
         
             
                  def permissions
         
     | 
| 
       7 
     | 
    
         
            -
                    return permission_action unless user || context[:current_settings].allow_unregistered?
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
7 
     | 
    
         
             
                    return Decidim::Surveys::Admin::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :admin
         
     | 
| 
       10 
8 
     | 
    
         
             
                    return permission_action if permission_action.scope != :public
         
     | 
| 
       11 
9 
     | 
    
         | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Decidim
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Surveys
         
     | 
| 
      
 5 
     | 
    
         
            +
                module AdminLog
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # This class holds the logic to present a `Decidim::Surveys::Survey`
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # for the `AdminLog` log.
         
     | 
| 
      
 8 
     | 
    
         
            +
                  #
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # Usage should be automatic and you should not need to call this class
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # directly, but here is an example:
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #    action_log = Decidim::ActionLog.last
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #    view_helpers # => this comes from the views
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #    SurveyPresenter.new(action_log, view_helpers).present
         
     | 
| 
      
 15 
     | 
    
         
            +
                  class SurveyPresenter < Decidim::Log::BasePresenter
         
     | 
| 
      
 16 
     | 
    
         
            +
                    private
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    # i18n-tasks-use t("decidim.surveys.admin_log.survey.create")
         
     | 
| 
      
 19 
     | 
    
         
            +
                    # i18n-tasks-use t("decidim.surveys.admin_log.survey.delete")
         
     | 
| 
      
 20 
     | 
    
         
            +
                    # i18n-tasks-use t("decidim.surveys.admin_log.survey.publish")
         
     | 
| 
      
 21 
     | 
    
         
            +
                    # i18n-tasks-use t("decidim.surveys.admin_log.survey.unpublish")
         
     | 
| 
      
 22 
     | 
    
         
            +
                    # i18n-tasks-use t("decidim.surveys.admin_log.survey.update")
         
     | 
| 
      
 23 
     | 
    
         
            +
                    def action_string
         
     | 
| 
      
 24 
     | 
    
         
            +
                      case action
         
     | 
| 
      
 25 
     | 
    
         
            +
                      when "delete", "create", "update", "publish", "unpublish"
         
     | 
| 
      
 26 
     | 
    
         
            +
                        "decidim.surveys.admin_log.survey.#{action}"
         
     | 
| 
      
 27 
     | 
    
         
            +
                      else
         
     | 
| 
      
 28 
     | 
    
         
            +
                        super
         
     | 
| 
      
 29 
     | 
    
         
            +
                      end
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -16,7 +16,7 @@ module Decidim 
     | 
|
| 
       16 
16 
     | 
    
         
             
                        quantity_value = results[:quantity] || 0
         
     | 
| 
       17 
17 
     | 
    
         
             
                        space_type, space_id, survey_id = key
         
     | 
| 
       18 
18 
     | 
    
         
             
                        record = Decidim::Metric.find_or_initialize_by(day: @day.to_s, metric_type: @metric_name,
         
     | 
| 
       19 
     | 
    
         
            -
                                                                       organization: @organization,  
     | 
| 
      
 19 
     | 
    
         
            +
                                                                       organization: @organization, decidim_taxonomy_id: nil,
         
     | 
| 
       20 
20 
     | 
    
         
             
                                                                       participatory_space_type: space_type, participatory_space_id: space_id,
         
     | 
| 
       21 
21 
     | 
    
         
             
                                                                       related_object_type: Decidim::Surveys::Survey.name, related_object_id: survey_id)
         
     | 
| 
       22 
22 
     | 
    
         
             
                        record.assign_attributes(cumulative: cumulative_value, quantity: quantity_value)
         
     | 
| 
         @@ -32,13 +32,13 @@ module Decidim 
     | 
|
| 
       32 
32 
     | 
    
         
             
                      @query = retrieve_surveys.each_with_object({}) do |survey, grouped_answers|
         
     | 
| 
       33 
33 
     | 
    
         
             
                        answers = Decidim::Forms::Answer.joins(:questionnaire)
         
     | 
| 
       34 
34 
     | 
    
         
             
                                                        .where(questionnaire: retrieve_questionnaires(survey))
         
     | 
| 
       35 
     | 
    
         
            -
                                                        .where( 
     | 
| 
      
 35 
     | 
    
         
            +
                                                        .where(decidim_forms_answers: { created_at: ..end_time })
         
     | 
| 
       36 
36 
     | 
    
         
             
                        next grouped_answers unless answers
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
                        group_key = generate_group_key(survey)
         
     | 
| 
       39 
39 
     | 
    
         
             
                        grouped_answers[group_key] ||= { cumulative: 0, quantity: 0 }
         
     | 
| 
       40 
40 
     | 
    
         
             
                        grouped_answers[group_key][:cumulative] += answers.count
         
     | 
| 
       41 
     | 
    
         
            -
                        grouped_answers[group_key][:quantity] += answers.where( 
     | 
| 
      
 41 
     | 
    
         
            +
                        grouped_answers[group_key][:quantity] += answers.where(decidim_forms_answers: { created_at: start_time.. }).count
         
     | 
| 
       42 
42 
     | 
    
         
             
                      end
         
     | 
| 
       43 
43 
     | 
    
         
             
                      @query
         
     | 
| 
       44 
44 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -17,11 +17,11 @@ module Decidim 
     | 
|
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
                      answers = Decidim::Forms::Answer.joins(:questionnaire)
         
     | 
| 
       19 
19 
     | 
    
         
             
                                                      .where(questionnaire: questionnaires)
         
     | 
| 
       20 
     | 
    
         
            -
                                                      .where( 
     | 
| 
      
 20 
     | 
    
         
            +
                                                      .where(decidim_forms_answers: { created_at: ..end_time })
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                      {
         
     | 
| 
       23 
23 
     | 
    
         
             
                        cumulative_users: answers.pluck(:decidim_user_id).uniq,
         
     | 
| 
       24 
     | 
    
         
            -
                        quantity_users: answers.where( 
     | 
| 
      
 24 
     | 
    
         
            +
                        quantity_users: answers.where(decidim_forms_answers: { created_at: start_time.. }).pluck(:decidim_user_id).uniq
         
     | 
| 
       25 
25 
     | 
    
         
             
                      }
         
     | 
| 
       26 
26 
     | 
    
         
             
                    end
         
     | 
| 
       27 
27 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -0,0 +1,49 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <div class="card" id="answers">
         
     | 
| 
      
 2 
     | 
    
         
            +
              <div class="item_show__header">
         
     | 
| 
      
 3 
     | 
    
         
            +
                <h1 class="item_show__header-title">
         
     | 
| 
      
 4 
     | 
    
         
            +
                  <%= t ".title", total: @total %>
         
     | 
| 
      
 5 
     | 
    
         
            +
                  <%= link_to t("actions.back", scope: "decidim.forms.admin.questionnaires"), questionnaire_url, class: "button button__sm button__secondary new" %>
         
     | 
| 
      
 6 
     | 
    
         
            +
                </h1>
         
     | 
| 
      
 7 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 8 
     | 
    
         
            +
              <div class="table-scroll">
         
     | 
| 
      
 9 
     | 
    
         
            +
                <table class="table-list">
         
     | 
| 
      
 10 
     | 
    
         
            +
                  <thead>
         
     | 
| 
      
 11 
     | 
    
         
            +
                    <tr>
         
     | 
| 
      
 12 
     | 
    
         
            +
                      <th>#</th>
         
     | 
| 
      
 13 
     | 
    
         
            +
                      <th><%= first_table_th(@participants.first) %></th>
         
     | 
| 
      
 14 
     | 
    
         
            +
                      <th><%= t("user_status", scope: "decidim.forms.user_answers_serializer") %></th>
         
     | 
| 
      
 15 
     | 
    
         
            +
                      <th><%= t("ip_hash", scope: "decidim.forms.user_answers_serializer") %></th>
         
     | 
| 
      
 16 
     | 
    
         
            +
                      <th><%= t("completion", scope: "decidim.forms.user_answers_serializer") %></th>
         
     | 
| 
      
 17 
     | 
    
         
            +
                      <th><%= t("created_at", scope: "decidim.forms.user_answers_serializer") %></th>
         
     | 
| 
      
 18 
     | 
    
         
            +
                      <th></th>
         
     | 
| 
      
 19 
     | 
    
         
            +
                    </tr>
         
     | 
| 
      
 20 
     | 
    
         
            +
                  </thead>
         
     | 
| 
      
 21 
     | 
    
         
            +
                  <tbody>
         
     | 
| 
      
 22 
     | 
    
         
            +
                    <% @participants.each_with_index do |participant, idx| %>
         
     | 
| 
      
 23 
     | 
    
         
            +
                      <tr>
         
     | 
| 
      
 24 
     | 
    
         
            +
                        <td><%= idx + 1 + page_offset %></td>
         
     | 
| 
      
 25 
     | 
    
         
            +
                        <td>
         
     | 
| 
      
 26 
     | 
    
         
            +
                          <% if allowed_to? :show, :questionnaire_answers %>
         
     | 
| 
      
 27 
     | 
    
         
            +
                            <%= link_to first_table_td(participant), questionnaire_participant_answers_url(participant.session_token) %>
         
     | 
| 
      
 28 
     | 
    
         
            +
                          <% else %>
         
     | 
| 
      
 29 
     | 
    
         
            +
                            <%= first_table_td(participant) %></td>
         
     | 
| 
      
 30 
     | 
    
         
            +
                          <% end %>
         
     | 
| 
      
 31 
     | 
    
         
            +
                        <td><%= participant.status %></td>
         
     | 
| 
      
 32 
     | 
    
         
            +
                        <td><%= participant.ip_hash %></td>
         
     | 
| 
      
 33 
     | 
    
         
            +
                        <td><%= display_percentage(participant.completion) %></td>
         
     | 
| 
      
 34 
     | 
    
         
            +
                        <td><%= l participant.answered_at, format: :short %></td>
         
     | 
| 
      
 35 
     | 
    
         
            +
                        <td class="table-list__actions">
         
     | 
| 
      
 36 
     | 
    
         
            +
                          <% if allowed_to? :show, :questionnaire_answers %>
         
     | 
| 
      
 37 
     | 
    
         
            +
                            <%= icon_link_to "eye-line", questionnaire_participant_answers_url(participant.session_token), t("actions.show", scope: "decidim.forms.admin.questionnaires.answers"), class: "action-icon--eye", target: "_blank", data: { "external-link": false } %>
         
     | 
| 
      
 38 
     | 
    
         
            +
                          <% end %>
         
     | 
| 
      
 39 
     | 
    
         
            +
                          <% if allowed_to? :export_response, :questionnaire_answers %>
         
     | 
| 
      
 40 
     | 
    
         
            +
                            <%= icon_link_to "download-line", questionnaire_export_response_url(participant.session_token), t("actions.export", scope: "decidim.forms.admin.questionnaires.answers"), class: "action-icon--data-transfer-download" %>
         
     | 
| 
      
 41 
     | 
    
         
            +
                          <% end %>
         
     | 
| 
      
 42 
     | 
    
         
            +
                        </td>
         
     | 
| 
      
 43 
     | 
    
         
            +
                      </tr>
         
     | 
| 
      
 44 
     | 
    
         
            +
                    <% end %>
         
     | 
| 
      
 45 
     | 
    
         
            +
                  </tbody>
         
     | 
| 
      
 46 
     | 
    
         
            +
                </table>
         
     | 
| 
      
 47 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 48 
     | 
    
         
            +
            </div>
         
     | 
| 
      
 49 
     | 
    
         
            +
            <%= decidim_paginate @query %>
         
     |