decidim-consultations 0.11.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +51 -0
  3. data/Rakefile +3 -0
  4. data/app/assets/config/decidim_consultations_manifest.js +5 -0
  5. data/app/assets/config/decidim_consultations_manifest.scss +3 -0
  6. data/app/assets/images/decidim/consultations/icon.svg +70 -0
  7. data/app/assets/images/decidim/consultations/icon2.svg +88 -0
  8. data/app/assets/javascripts/decidim/consultations/show_more.js.es6 +12 -0
  9. data/app/assets/javascripts/decidim/consultations/social_share.js +2 -0
  10. data/app/assets/javascripts/decidim/consultations/utils.js.es6 +19 -0
  11. data/app/assets/javascripts/decidim/consultations/vote_dialog.js +36 -0
  12. data/app/assets/stylesheets/decidim/consultations/_question.scss +46 -0
  13. data/app/assets/stylesheets/decidim/consultations/_stats.scss +22 -0
  14. data/app/assets/stylesheets/decidim/consultations/_video_wrapper.scss +14 -0
  15. data/app/assets/stylesheets/decidim/consultations/banners/_block-banner.scss +39 -0
  16. data/app/assets/stylesheets/decidim/consultations/consultations.scss +6 -0
  17. data/app/assets/stylesheets/decidim/consultations/consultations/_consultations-banner.scss +13 -0
  18. data/app/assets/stylesheets/decidim/consultations/consultations/_consultations-card.scss +17 -0
  19. data/app/assets/stylesheets/decidim/consultations/consultations/_consultations-header.scss +115 -0
  20. data/app/assets/stylesheets/decidim/consultations/consultations/_consultations-home-banner.scss +15 -0
  21. data/app/assets/stylesheets/decidim/consultations/consultations/_consultations-home-intro.scss +30 -0
  22. data/app/assets/stylesheets/decidim/consultations/consultations/_consultations-intro.scss +11 -0
  23. data/app/assets/stylesheets/decidim/consultations/consultations/_consultations-title.scss +26 -0
  24. data/app/assets/stylesheets/decidim/consultations/consultations/_consultations_nav.scss +7 -0
  25. data/app/assets/stylesheets/decidim/consultations/consultations/introductory_image.scss +3 -0
  26. data/app/assets/stylesheets/decidim/consultations/highlighted-content-banner.css.scss +42 -0
  27. data/app/assets/stylesheets/decidim/consultations/social_share.css.scss +18 -0
  28. data/app/commands/decidim/consultations/admin/create_consultation.rb +61 -0
  29. data/app/commands/decidim/consultations/admin/create_question.rb +68 -0
  30. data/app/commands/decidim/consultations/admin/create_response.rb +39 -0
  31. data/app/commands/decidim/consultations/admin/publish_consultation.rb +34 -0
  32. data/app/commands/decidim/consultations/admin/publish_consultation_results.rb +34 -0
  33. data/app/commands/decidim/consultations/admin/publish_question.rb +34 -0
  34. data/app/commands/decidim/consultations/admin/unpublish_consultation.rb +34 -0
  35. data/app/commands/decidim/consultations/admin/unpublish_consultation_results.rb +34 -0
  36. data/app/commands/decidim/consultations/admin/unpublish_question.rb +34 -0
  37. data/app/commands/decidim/consultations/admin/update_consultation.rb +64 -0
  38. data/app/commands/decidim/consultations/admin/update_question.rb +72 -0
  39. data/app/commands/decidim/consultations/admin/update_response.rb +46 -0
  40. data/app/commands/decidim/consultations/unvote_question.rb +37 -0
  41. data/app/commands/decidim/consultations/vote_question.rb +42 -0
  42. data/app/constraints/decidim/consultations/current_component.rb +28 -0
  43. data/app/constraints/decidim/consultations/current_question.rb +36 -0
  44. data/app/controllers/concerns/decidim/consultations/action_authorization.rb +38 -0
  45. data/app/controllers/concerns/decidim/consultations/admin/consultation_admin.rb +24 -0
  46. data/app/controllers/concerns/decidim/consultations/admin/question_admin.rb +28 -0
  47. data/app/controllers/concerns/decidim/consultations/needs_consultation.rb +54 -0
  48. data/app/controllers/concerns/decidim/consultations/needs_question.rb +79 -0
  49. data/app/controllers/concerns/decidim/consultations/orderable.rb +52 -0
  50. data/app/controllers/decidim/consultations/admin/application_controller.rb +14 -0
  51. data/app/controllers/decidim/consultations/admin/categories_controller.rb +13 -0
  52. data/app/controllers/decidim/consultations/admin/component_permissions_controller.rb +13 -0
  53. data/app/controllers/decidim/consultations/admin/components_controller.rb +13 -0
  54. data/app/controllers/decidim/consultations/admin/consultation_publications_controller.rb +44 -0
  55. data/app/controllers/decidim/consultations/admin/consultation_results_publications_controller.rb +44 -0
  56. data/app/controllers/decidim/consultations/admin/consultations_controller.rb +100 -0
  57. data/app/controllers/decidim/consultations/admin/question_attachments_controller.rb +25 -0
  58. data/app/controllers/decidim/consultations/admin/question_publications_controller.rb +44 -0
  59. data/app/controllers/decidim/consultations/admin/questions_controller.rb +88 -0
  60. data/app/controllers/decidim/consultations/admin/responses_controller.rb +85 -0
  61. data/app/controllers/decidim/consultations/consultation_widgets_controller.rb +25 -0
  62. data/app/controllers/decidim/consultations/consultations_controller.rb +75 -0
  63. data/app/controllers/decidim/consultations/question_votes_controller.rb +40 -0
  64. data/app/controllers/decidim/consultations/question_widgets_controller.rb +26 -0
  65. data/app/controllers/decidim/consultations/questions_controller.rb +23 -0
  66. data/app/forms/decidim/consultations/admin/consultation_form.rb +56 -0
  67. data/app/forms/decidim/consultations/admin/question_form.rb +74 -0
  68. data/app/forms/decidim/consultations/admin/response_form.rb +18 -0
  69. data/app/forms/decidim/consultations/vote_form.rb +28 -0
  70. data/app/helpers/decidim/consultations/admin/consultations_helper.rb +14 -0
  71. data/app/helpers/decidim/consultations/admin/questions_helper.rb +14 -0
  72. data/app/helpers/decidim/consultations/consultations_helper.rb +17 -0
  73. data/app/models/concerns/decidim/consultations/override_categorization.rb +21 -0
  74. data/app/models/concerns/decidim/consultations/publicable_results.rb +51 -0
  75. data/app/models/decidim/consultation.rb +79 -0
  76. data/app/models/decidim/consultations/abilities/admin/consultation_admin_ability.rb +34 -0
  77. data/app/models/decidim/consultations/abilities/admin/question_admin_ability.rb +30 -0
  78. data/app/models/decidim/consultations/abilities/admin/response_admin_ability.rb +26 -0
  79. data/app/models/decidim/consultations/abilities/current_user_ability.rb +45 -0
  80. data/app/models/decidim/consultations/abilities/everyone_ability.rb +23 -0
  81. data/app/models/decidim/consultations/application_record.rb +9 -0
  82. data/app/models/decidim/consultations/question.rb +127 -0
  83. data/app/models/decidim/consultations/response.rb +19 -0
  84. data/app/models/decidim/consultations/vote.rb +26 -0
  85. data/app/presenters/decidim/consultations/question_stats_presenter.rb +52 -0
  86. data/app/queries/decidim/consultations/admin/admin_users.rb +40 -0
  87. data/app/queries/decidim/consultations/organization_active_consultations.rb +20 -0
  88. data/app/queries/decidim/consultations/organization_consultations.rb +20 -0
  89. data/app/queries/decidim/consultations/organization_questions.rb +20 -0
  90. data/app/services/decidim/consultations/consultation_search.rb +51 -0
  91. data/app/views/decidim/consultations/_consultation.html.erb +32 -0
  92. data/app/views/decidim/consultations/admin/consultations/_form.html.erb +58 -0
  93. data/app/views/decidim/consultations/admin/consultations/edit.html.erb +42 -0
  94. data/app/views/decidim/consultations/admin/consultations/index.html.erb +74 -0
  95. data/app/views/decidim/consultations/admin/consultations/new.html.erb +11 -0
  96. data/app/views/decidim/consultations/admin/questions/_form.html.erb +84 -0
  97. data/app/views/decidim/consultations/admin/questions/edit.html.erb +32 -0
  98. data/app/views/decidim/consultations/admin/questions/index.html.erb +74 -0
  99. data/app/views/decidim/consultations/admin/questions/new.html.erb +13 -0
  100. data/app/views/decidim/consultations/admin/responses/_form.html.erb +11 -0
  101. data/app/views/decidim/consultations/admin/responses/edit.html.erb +18 -0
  102. data/app/views/decidim/consultations/admin/responses/index.html.erb +48 -0
  103. data/app/views/decidim/consultations/admin/responses/new.html.erb +13 -0
  104. data/app/views/decidim/consultations/consultation_widgets/show.html.erb +5 -0
  105. data/app/views/decidim/consultations/consultations/_consultation_card.html.erb +24 -0
  106. data/app/views/decidim/consultations/consultations/_consultation_details.html.erb +17 -0
  107. data/app/views/decidim/consultations/consultations/_consultations.html.erb +16 -0
  108. data/app/views/decidim/consultations/consultations/_count.html.erb +1 -0
  109. data/app/views/decidim/consultations/consultations/_filters.html.erb +25 -0
  110. data/app/views/decidim/consultations/consultations/_filters_small_view.html.erb +23 -0
  111. data/app/views/decidim/consultations/consultations/_highlighted_questions.html.erb +13 -0
  112. data/app/views/decidim/consultations/consultations/_question.html.erb +40 -0
  113. data/app/views/decidim/consultations/consultations/_regular_questions.html.erb +14 -0
  114. data/app/views/decidim/consultations/consultations/finished.html.erb +15 -0
  115. data/app/views/decidim/consultations/consultations/index.html.erb +27 -0
  116. data/app/views/decidim/consultations/consultations/index.js.erb +10 -0
  117. data/app/views/decidim/consultations/consultations/show.html.erb +11 -0
  118. data/app/views/decidim/consultations/question_votes/update_vote_button.js.erb +9 -0
  119. data/app/views/decidim/consultations/question_widgets/show.html.erb +2 -0
  120. data/app/views/decidim/consultations/questions/_results.html.erb +15 -0
  121. data/app/views/decidim/consultations/questions/_right_column.html.erb +20 -0
  122. data/app/views/decidim/consultations/questions/_statistics.html.erb +31 -0
  123. data/app/views/decidim/consultations/questions/_technical_data.html.erb +38 -0
  124. data/app/views/decidim/consultations/questions/_vote_button.html.erb +76 -0
  125. data/app/views/decidim/consultations/questions/_vote_modal.html.erb +27 -0
  126. data/app/views/decidim/consultations/questions/_vote_modal_confirm.html.erb +33 -0
  127. data/app/views/decidim/consultations/questions/show.html.erb +58 -0
  128. data/app/views/layouts/decidim/_consultation_header.html.erb +16 -0
  129. data/app/views/layouts/decidim/_consultation_voting_data.html.erb +10 -0
  130. data/app/views/layouts/decidim/_question_components.html.erb +38 -0
  131. data/app/views/layouts/decidim/_question_header.html.erb +46 -0
  132. data/app/views/layouts/decidim/admin/consultation.html.erb +28 -0
  133. data/app/views/layouts/decidim/admin/consultations.html.erb +16 -0
  134. data/app/views/layouts/decidim/admin/question.html.erb +72 -0
  135. data/app/views/layouts/decidim/consultation.html.erb +17 -0
  136. data/app/views/layouts/decidim/consultation_choose.html.erb +7 -0
  137. data/app/views/layouts/decidim/question.html.erb +19 -0
  138. data/config/locales/ca.yml +248 -0
  139. data/config/locales/en.yml +249 -0
  140. data/config/locales/es.yml +248 -0
  141. data/config/locales/eu.yml +248 -0
  142. data/config/locales/fi.yml +248 -0
  143. data/config/locales/fr.yml +248 -0
  144. data/config/locales/gl.yml +248 -0
  145. data/config/locales/it.yml +248 -0
  146. data/config/locales/nl.yml +248 -0
  147. data/config/locales/pl.yml +258 -0
  148. data/config/locales/pt-BR.yml +248 -0
  149. data/config/locales/pt.yml +248 -0
  150. data/config/locales/ru.yml +6 -0
  151. data/config/locales/sv.yml +248 -0
  152. data/config/locales/uk.yml +6 -0
  153. data/db/migrate/20180109092205_create_decidim_consultations.rb +37 -0
  154. data/db/migrate/20180112053247_create_decidim_consultations_questions.rb +35 -0
  155. data/db/migrate/20180115132000_add_end_voting_date_to_decidim_consultations.rb +18 -0
  156. data/db/migrate/20180115170933_add_slug_to_decidim_consultations_questions.rb +32 -0
  157. data/db/migrate/20180119084217_create_decidim_consultations_votes.rb +17 -0
  158. data/db/migrate/20180119084331_add_votes_count_to_decidim_consultations_question.rb +7 -0
  159. data/db/migrate/20180122113155_add_origin_scope_to_decidim_consultations_questions.rb +8 -0
  160. data/db/migrate/20180122113400_add_origin_title_to_decidim_consultations_questions.rb +8 -0
  161. data/db/migrate/20180122113447_add_origin_string_to_decidim_consultations_questions.rb +7 -0
  162. data/db/migrate/20180126142459_add_enable_highlighted_banner_to_decidim_consultations.rb +7 -0
  163. data/db/migrate/20180129063438_add_i_frame_url_to_decidim_consultations_questions.rb +8 -0
  164. data/db/migrate/20180129063700_create_decidim_consultations_responses.rb +14 -0
  165. data/db/migrate/20180129122226_rename_decidim_consultations_vote_to_decidim_consultations_endorsement.rb +7 -0
  166. data/db/migrate/20180129122504_rename_decidim_consultations_questions_votes_count_to_endorsements_count.rb +7 -0
  167. data/db/migrate/20180130060754_add_responses_count_to_decidim_consultations_questions.rb +22 -0
  168. data/db/migrate/20180130110449_rename_vote_related_attributes.rb +9 -0
  169. data/db/migrate/20180130142018_add_decidim_user_group_id_to_decidim_consultations_endorsements.rb +7 -0
  170. data/db/migrate/20180130142411_rename_vote_indexes_to_endorsement_indexes.rb +36 -0
  171. data/db/migrate/20180131083844_add_response_to_decidim_consultations_endorsements.rb +10 -0
  172. data/db/migrate/20180201135823_rename_endorsement_to_vote.rb +23 -0
  173. data/db/migrate/20180202050920_add_hero_image_to_decidim_consultations_question.rb +8 -0
  174. data/db/migrate/20180202085309_add_results_published_at_to_decidim_consultations.rb +7 -0
  175. data/db/migrate/20180202133655_add_votes_count_to_decidim_consultations_response.rb +7 -0
  176. data/db/migrate/20180212100503_remove_enable_highlighted_banner_flag.rb +7 -0
  177. data/db/migrate/20180219092120_add_order_to_decidim_consultations_questions.rb +7 -0
  178. data/db/migrate/20180320100658_add_introductory_image_to_decidim_consultations.rb +7 -0
  179. data/db/seeds/Exampledocument.pdf +0 -0
  180. data/db/seeds/city.jpeg +0 -0
  181. data/db/seeds/city2.jpeg +0 -0
  182. data/db/seeds/homepage_image.jpg +0 -0
  183. data/lib/decidim/consultations.rb +18 -0
  184. data/lib/decidim/consultations/admin.rb +10 -0
  185. data/lib/decidim/consultations/admin_engine.rb +72 -0
  186. data/lib/decidim/consultations/engine.rb +83 -0
  187. data/lib/decidim/consultations/participatory_space.rb +171 -0
  188. data/lib/decidim/consultations/test/factories.rb +104 -0
  189. data/lib/decidim/consultations/version.rb +9 -0
  190. metadata +288 -0
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Consultations
5
+ class QuestionVotesController < Decidim::ApplicationController
6
+ include NeedsQuestion
7
+ include Decidim::FormFactory
8
+
9
+ before_action :authenticate_user!
10
+
11
+ def create
12
+ authorize! :vote, current_question
13
+
14
+ vote_form = form(VoteForm).from_params(params, current_question: current_question)
15
+ VoteQuestion.call(vote_form) do
16
+ on(:ok) do
17
+ current_question.reload
18
+ render :update_vote_button
19
+ end
20
+
21
+ on(:invalid) do
22
+ render json: {
23
+ error: I18n.t("question_votes.create.error", scope: "decidim.consultations")
24
+ }, status: 422
25
+ end
26
+ end
27
+ end
28
+
29
+ def destroy
30
+ authorize! :unvote, current_question
31
+ UnvoteQuestion.call(current_question, current_user) do
32
+ on(:ok) do
33
+ current_question.reload
34
+ render :update_vote_button
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Consultations
5
+ # This controller provides a widget that allows embedding the question
6
+ class QuestionWidgetsController < Decidim::WidgetsController
7
+ include NeedsQuestion
8
+
9
+ helper Decidim::SanitizeHelper
10
+
11
+ private
12
+
13
+ def model
14
+ @model ||= current_question
15
+ end
16
+
17
+ def current_participatory_space
18
+ model
19
+ end
20
+
21
+ def iframe_url
22
+ @iframe_url ||= question_question_widget_url(model)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Consultations
5
+ # A controller that holds the logic to show questions in a
6
+ # public layout.
7
+ class QuestionsController < Decidim::ApplicationController
8
+ layout "layouts/decidim/question"
9
+
10
+ include NeedsQuestion
11
+
12
+ helper Decidim::SanitizeHelper
13
+ helper Decidim::IconHelper
14
+ helper Decidim::Comments::CommentsHelper
15
+ helper Decidim::AttachmentsHelper
16
+ helper Decidim::ResourceReferenceHelper
17
+
18
+ def show
19
+ authorize! :read, current_question
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Consultations
5
+ module Admin
6
+ # A form object used to create consultations from the admin dashboard.
7
+ class ConsultationForm < Form
8
+ include TranslatableAttributes
9
+
10
+ mimic :consultation
11
+
12
+ translatable_attribute :title, String
13
+ translatable_attribute :subtitle, String
14
+ translatable_attribute :description, String
15
+ attribute :slug, String
16
+ attribute :banner_image
17
+ attribute :remove_banner_image
18
+ attribute :introductory_video_url, String
19
+ attribute :introductory_image, String
20
+ attribute :decidim_highlighted_scope_id, Integer
21
+ attribute :start_voting_date, Date
22
+ attribute :end_voting_date, Date
23
+
24
+ validates :slug, presence: true, format: { with: Decidim::Consultation.slug_format }
25
+ validates :title, :subtitle, :description, translatable_presence: true
26
+ validates :decidim_highlighted_scope_id, presence: true
27
+ validates :start_voting_date, presence: true, date: { before_or_equal_to: :end_voting_date }
28
+ validates :end_voting_date, presence: true, date: { after_or_equal_to: :start_voting_date }
29
+ validate :slug_uniqueness
30
+
31
+ validates :banner_image,
32
+ file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } },
33
+ file_content_type: { allow: ["image/jpeg", "image/png"] }
34
+ validates :introductory_image,
35
+ file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } },
36
+ file_content_type: { allow: ["image/jpeg", "image/png"] }
37
+
38
+ def highlighted_scope
39
+ @highlighted_scope ||= current_organization.scopes.find_by(id: decidim_highlighted_scope_id)
40
+ end
41
+
42
+ private
43
+
44
+ def slug_uniqueness
45
+ return unless OrganizationConsultations
46
+ .new(current_organization)
47
+ .query
48
+ .where(slug: slug)
49
+ .where.not(id: context[:consultation_id]).any?
50
+
51
+ errors.add(:slug, :taken)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Consultations
5
+ module Admin
6
+ # A form object used to create questions for a consultation from the admin dashboard.
7
+ class QuestionForm < Form
8
+ include TranslatableAttributes
9
+
10
+ mimic :question
11
+
12
+ translatable_attribute :title, String
13
+ translatable_attribute :promoter_group, String
14
+ translatable_attribute :participatory_scope, String
15
+ translatable_attribute :question_context, String
16
+ translatable_attribute :subtitle, String
17
+ translatable_attribute :what_is_decided, String
18
+ translatable_attribute :origin_scope, String
19
+ translatable_attribute :origin_title, String
20
+ attribute :origin_url, String
21
+ attribute :slug, String
22
+ attribute :remove_hero_image
23
+ attribute :hero_image
24
+ attribute :banner_image
25
+ attribute :remove_banner_image
26
+ attribute :hashtag, String
27
+ attribute :decidim_scope_id, Integer
28
+ attribute :external_voting, Boolean, default: false
29
+ attribute :i_frame_url, String
30
+ attribute :order, Integer
31
+
32
+ validates :slug, presence: true, format: { with: Decidim::Consultations::Question.slug_format }
33
+ validates :title, :promoter_group, :participatory_scope, :subtitle, :what_is_decided, translatable_presence: true
34
+ validates :decidim_scope_id, presence: true
35
+ validates :hero_image,
36
+ file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } },
37
+ file_content_type: { allow: ["image/jpeg", "image/png"] }
38
+ validates :banner_image,
39
+ file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } },
40
+ file_content_type: { allow: ["image/jpeg", "image/png"] }
41
+ validate :slug_uniqueness
42
+ validates :origin_scope, :origin_title, translatable_presence: true, if: :has_origin_data?
43
+ validates :i_frame_url, presence: true, if: :external_voting
44
+ validates :order, numericality: { only_integer: true, allow_nil: true, allow_blank: true }
45
+
46
+ private
47
+
48
+ def slug_uniqueness
49
+ return unless OrganizationQuestions
50
+ .new(current_organization)
51
+ .query
52
+ .where(slug: slug)
53
+ .where.not(id: context[:question_id]).any?
54
+
55
+ errors.add(:slug, :taken)
56
+ end
57
+
58
+ def has_origin_data?
59
+ has_value?(origin_title) || has_value?(origin_scope) || origin_url.present?
60
+ end
61
+
62
+ def has_value?(translatable_attribute)
63
+ return false if translatable_attribute.nil?
64
+
65
+ Decidim.available_locales.each do |locale|
66
+ return true if translatable_attribute.with_indifferent_access[locale].present?
67
+ end
68
+
69
+ false
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Consultations
5
+ module Admin
6
+ # A form object used to create responses for a question from the admin dashboard.
7
+ class ResponseForm < Form
8
+ include TranslatableAttributes
9
+
10
+ mimic :response
11
+
12
+ translatable_attribute :title, String
13
+
14
+ validates :title, translatable_presence: true
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Consultations
5
+ class VoteForm < Form
6
+ mimic :vote
7
+
8
+ attribute :decidim_consultations_response_id, Integer
9
+
10
+ validates :decidim_consultations_response_id, presence: true
11
+ validate :response_exists
12
+
13
+ def response
14
+ @response ||= Response.find_by(id: decidim_consultations_response_id)
15
+ end
16
+
17
+ private
18
+
19
+ def response_exists
20
+ return unless response.nil?
21
+ errors.add(
22
+ :decidim_consultations_response_id,
23
+ I18n.t("decidim_consultations_response_id.not_found", scope: "activemodel.errors.vote")
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Consultations
5
+ module Admin
6
+ # Helper for consultation controller
7
+ module ConsultationsHelper
8
+ def consultation_example_slug
9
+ "consultation-#{Time.now.utc.year}-#{Time.now.utc.month}-1"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Consultations
5
+ module Admin
6
+ # Helper for questions controller
7
+ module QuestionsHelper
8
+ def question_example_slug
9
+ "question-#{Time.now.utc.year}-#{Time.now.utc.month}-1"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Consultations
5
+ module ConsultationsHelper
6
+ # Returns options for state filter selector.
7
+ def options_for_state_filter
8
+ [
9
+ ["all", t("consultations.filters.all", scope: "decidim")],
10
+ ["active", t("consultations.filters.active", scope: "decidim")],
11
+ ["upcoming", t("consultations.filters.upcoming", scope: "decidim")],
12
+ ["finished", t("consultations.filters.finished", scope: "decidim")]
13
+ ]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Consultations
7
+ module OverrideCategorization
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ # This relationship exists only by compatibility reasons.
12
+ # Consultations are not intended to have categories.
13
+ has_many :categories,
14
+ foreign_key: "decidim_participatory_space_id",
15
+ foreign_type: "decidim_participatory_space_type",
16
+ dependent: :destroy,
17
+ as: :participatory_space
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Consultations
7
+ # This concern contains the logic related to results publication and promotion.
8
+ module PublicableResults
9
+ extend ActiveSupport::Concern
10
+
11
+ class_methods do
12
+ # Public: Scope to return only records with its results published.
13
+ #
14
+ # Returns an ActiveRecord::Relation.
15
+ def results_published
16
+ where.not(results_published_at: nil)
17
+ end
18
+
19
+ # Public: Scope to return only records with its results unpublished.
20
+ #
21
+ # Returns an ActiveRecord::Relation.
22
+ def results_unpublished
23
+ where(results_published_at: nil)
24
+ end
25
+ end
26
+
27
+ # Public: Checks whether the record has its results published or not.
28
+ #
29
+ # Returns true if published, false otherwise.
30
+ def results_published?
31
+ results_published_at.present?
32
+ end
33
+
34
+ #
35
+ # Public: Publishes the results of the given component
36
+ #
37
+ # Returns true if the record was properly saved, false otherwise.
38
+ def publish_results!
39
+ update!(results_published_at: Time.current)
40
+ end
41
+
42
+ #
43
+ # Public: Unpublishes the results
44
+ #
45
+ # Returns true if the record was properly saved, false otherwise.
46
+ def unpublish_results!
47
+ update!(results_published_at: nil)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # The data store for a Consultation in the Decidim::Consultations component.
5
+ class Consultation < ApplicationRecord
6
+ include Decidim::Participable
7
+ include Decidim::Publicable
8
+ include Decidim::Consultations::PublicableResults
9
+
10
+ belongs_to :organization,
11
+ foreign_key: "decidim_organization_id",
12
+ class_name: "Decidim::Organization"
13
+
14
+ belongs_to :highlighted_scope,
15
+ foreign_key: "decidim_highlighted_scope_id",
16
+ class_name: "Decidim::Scope"
17
+
18
+ has_many :questions,
19
+ foreign_key: "decidim_consultation_id",
20
+ class_name: "Decidim::Consultations::Question",
21
+ inverse_of: :consultation,
22
+ dependent: :destroy
23
+
24
+ validates :slug, uniqueness: { scope: :organization }
25
+ validates :slug, presence: true, format: { with: Decidim::Consultation.slug_format }
26
+
27
+ mount_uploader :banner_image, Decidim::BannerImageUploader
28
+ mount_uploader :introductory_image, Decidim::BannerImageUploader
29
+
30
+ scope :upcoming, -> { published.where("start_voting_date > ?", Time.now.utc) }
31
+ scope :active, lambda {
32
+ published
33
+ .where("start_voting_date <= ?", Time.now.utc)
34
+ .where("end_voting_date >= ?", Time.now.utc)
35
+ }
36
+ scope :finished, -> { published.where("end_voting_date < ?", Time.now.utc) }
37
+ scope :order_by_most_recent, -> { order(created_at: :desc) }
38
+
39
+ def to_param
40
+ slug
41
+ end
42
+
43
+ def upcoming?
44
+ start_voting_date > Time.now.utc
45
+ end
46
+
47
+ def active?
48
+ start_voting_date <= Time.now.utc && end_voting_date >= Time.now.utc
49
+ end
50
+
51
+ def finished?
52
+ end_voting_date < Time.now.utc
53
+ end
54
+
55
+ def highlighted_questions
56
+ questions.published.where(decidim_scope_id: decidim_highlighted_scope_id)
57
+ end
58
+
59
+ def regular_questions
60
+ questions.published.where.not(decidim_scope_id: decidim_highlighted_scope_id).group_by(&:scope)
61
+ end
62
+
63
+ # This method exists with the only purpose of getting rid of whats seems to be an issue in
64
+ # the new scope picker: This engine is a bit special: consultations and questions are a kind of
65
+ # nested participatory spaces. When a new question is created the consultation is the participatory space.
66
+ # Since seems that the scope picker is asking to the current participatory space for its scope
67
+ # this method is necessary to exist an return nil in order to be able to browse the scope hierarchy
68
+ def scope
69
+ nil
70
+ end
71
+
72
+ def self.order_randomly(seed)
73
+ transaction do
74
+ connection.execute("SELECT setseed(#{connection.quote(seed)})")
75
+ select('"decidim_consultations".*, RANDOM()').order("RANDOM()").load
76
+ end
77
+ end
78
+ end
79
+ end