decidim-meetings 0.30.2 → 0.31.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.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/meetings/cancel_registration_meeting_button/cancelation_modal.erb +7 -4
  3. data/app/cells/decidim/meetings/cancel_registration_meeting_button/show.erb +1 -1
  4. data/app/cells/decidim/meetings/cancel_registration_meeting_button_cell.rb +36 -0
  5. data/app/cells/decidim/meetings/dates_and_map_cell.rb +1 -1
  6. data/app/cells/decidim/meetings/join_meeting_button/registration_modal.erb +4 -5
  7. data/app/cells/decidim/meetings/join_meeting_button/show.erb +25 -28
  8. data/app/cells/decidim/meetings/join_meeting_button/waitlist_button.erb +22 -0
  9. data/app/cells/decidim/meetings/join_meeting_button_cell.rb +28 -0
  10. data/app/cells/decidim/meetings/question_responses/show.erb +7 -7
  11. data/app/cells/decidim/meetings/question_responses_cell.rb +28 -28
  12. data/app/commands/decidim/meetings/admin/copy_meeting.rb +5 -2
  13. data/app/commands/decidim/meetings/admin/create_agenda.rb +6 -2
  14. data/app/commands/decidim/meetings/admin/create_meeting.rb +7 -3
  15. data/app/commands/decidim/meetings/admin/mark_as_attendee.rb +44 -0
  16. data/app/commands/decidim/meetings/admin/publish_meeting.rb +2 -1
  17. data/app/commands/decidim/meetings/admin/update_agenda.rb +6 -2
  18. data/app/commands/decidim/meetings/admin/update_meeting.rb +15 -6
  19. data/app/commands/decidim/meetings/admin/update_questionnaire.rb +4 -4
  20. data/app/commands/decidim/meetings/admin/update_registrations.rb +19 -7
  21. data/app/commands/decidim/meetings/create_meeting.rb +2 -3
  22. data/app/commands/decidim/meetings/{create_answer.rb → create_response.rb} +11 -11
  23. data/app/commands/decidim/meetings/join_meeting.rb +4 -6
  24. data/app/commands/decidim/meetings/join_waitlist.rb +53 -0
  25. data/app/commands/decidim/meetings/leave_meeting.rb +14 -5
  26. data/app/commands/decidim/meetings/update_meeting.rb +1 -2
  27. data/app/controllers/concerns/decidim/meetings/admin/filterable.rb +1 -1
  28. data/app/controllers/decidim/meetings/admin/agenda_controller.rb +2 -2
  29. data/app/controllers/decidim/meetings/admin/invites_controller.rb +1 -1
  30. data/app/controllers/decidim/meetings/admin/meeting_closes_controller.rb +1 -1
  31. data/app/controllers/decidim/meetings/admin/meeting_copies_controller.rb +1 -1
  32. data/app/controllers/decidim/meetings/admin/meetings_controller.rb +4 -4
  33. data/app/controllers/decidim/meetings/admin/meetings_poll_controller.rb +10 -10
  34. data/app/controllers/decidim/meetings/admin/registrations_attendees_controller.rb +79 -0
  35. data/app/controllers/decidim/meetings/admin/registrations_controller.rb +1 -22
  36. data/app/controllers/decidim/meetings/meeting_closes_controller.rb +1 -1
  37. data/app/controllers/decidim/meetings/meetings_controller.rb +11 -11
  38. data/app/controllers/decidim/meetings/polls/{answers_controller.rb → responses_controller.rb} +7 -7
  39. data/app/controllers/decidim/meetings/registrations_controller.rb +39 -12
  40. data/app/events/decidim/meetings/registration_marked_as_attendee_event.rb +9 -0
  41. data/app/events/decidim/meetings/upcoming_meeting_event.rb +41 -0
  42. data/app/events/decidim/meetings/update_meeting_event.rb +25 -0
  43. data/app/forms/decidim/meetings/admin/close_meeting_form.rb +2 -1
  44. data/app/forms/decidim/meetings/admin/meeting_agenda_items_form.rb +4 -0
  45. data/app/forms/decidim/meetings/admin/meeting_form.rb +28 -3
  46. data/app/forms/decidim/meetings/admin/question_form.rb +3 -3
  47. data/app/forms/decidim/meetings/admin/{answer_option_form.rb → response_option_form.rb} +3 -3
  48. data/app/forms/decidim/meetings/admin/validate_registration_code_form.rb +1 -1
  49. data/app/forms/decidim/meetings/base_meeting_form.rb +0 -2
  50. data/app/forms/decidim/meetings/close_meeting_form.rb +2 -1
  51. data/app/forms/decidim/meetings/join_meeting_form.rb +0 -1
  52. data/app/forms/decidim/meetings/meeting_form.rb +3 -2
  53. data/app/forms/decidim/meetings/response_choice_form.rb +14 -0
  54. data/app/forms/decidim/meetings/{answer_form.rb → response_form.rb} +7 -7
  55. data/app/helpers/decidim/meetings/application_helper.rb +0 -1
  56. data/app/helpers/decidim/meetings/meetings_helper.rb +14 -5
  57. data/app/jobs/decidim/meetings/promote_from_waitlist_job.rb +63 -0
  58. data/app/jobs/decidim/meetings/upcoming_meeting_notification_job.rb +1 -1
  59. data/app/mailers/decidim/meetings/registration_mailer.rb +13 -0
  60. data/app/models/decidim/meetings/agenda_item.rb +5 -0
  61. data/app/models/decidim/meetings/meeting.rb +15 -12
  62. data/app/models/decidim/meetings/question.rb +12 -12
  63. data/app/models/decidim/meetings/questionnaire.rb +1 -1
  64. data/app/models/decidim/meetings/registration.rb +19 -7
  65. data/app/models/decidim/meetings/{answer.rb → response.rb} +6 -6
  66. data/app/models/decidim/meetings/response_choice.rb +15 -0
  67. data/app/models/decidim/meetings/{answer_option.rb → response_option.rb} +5 -5
  68. data/app/packs/src/decidim/meetings/admin/destroy_meeting_alert.js +1 -1
  69. data/app/packs/src/decidim/meetings/admin/meetings_components_form.js +1 -8
  70. data/app/packs/src/decidim/meetings/admin/meetings_form.js +1 -1
  71. data/app/packs/src/decidim/meetings/admin/registrations_form.js +1 -1
  72. data/app/packs/src/decidim/meetings/admin/registrations_invite_form.js +1 -1
  73. data/app/packs/src/decidim/meetings/meetings_form.js +1 -1
  74. data/app/packs/src/decidim/meetings/meetings_polls.js +1 -1
  75. data/app/packs/src/decidim/meetings/poll.component.js +5 -5
  76. data/app/packs/stylesheets/decidim/meetings/_item.scss +5 -1
  77. data/app/packs/stylesheets/decidim/meetings/meetings.scss +4 -4
  78. data/app/permissions/decidim/meetings/admin/agenda_permissions.rb +34 -0
  79. data/app/permissions/decidim/meetings/admin/meeting_permissions.rb +44 -0
  80. data/app/permissions/decidim/meetings/admin/permissions.rb +5 -66
  81. data/app/permissions/decidim/meetings/admin/questionnaire_permissions.rb +30 -0
  82. data/app/permissions/decidim/meetings/meeting_permissions.rb +90 -0
  83. data/app/permissions/decidim/meetings/permissions.rb +9 -105
  84. data/app/presenters/decidim/meetings/admin_log/value_types/meeting_title_description_presenter.rb +1 -1
  85. data/app/presenters/decidim/meetings/agenda_item_presenter.rb +29 -0
  86. data/app/presenters/decidim/meetings/meeting_presenter.rb +11 -15
  87. data/app/presenters/decidim/meetings/registration_presenter.rb +24 -0
  88. data/app/queries/decidim/meetings/{questionnaire_user_answers.rb → questionnaire_user_responses.rb} +5 -5
  89. data/app/serializers/decidim/meetings/registration_serializer.rb +5 -6
  90. data/app/services/decidim/meetings/diff_renderer.rb +0 -1
  91. data/app/views/decidim/meetings/_calendar_modal.html.erb +1 -0
  92. data/app/views/decidim/meetings/admin/agenda/_agenda_item_fields.html.erb +1 -1
  93. data/app/views/decidim/meetings/admin/invites/_form.html.erb +1 -1
  94. data/app/views/decidim/meetings/admin/meeting_closes/_form.html.erb +1 -1
  95. data/app/views/decidim/meetings/admin/meetings/_form.html.erb +3 -2
  96. data/app/views/decidim/meetings/admin/meetings/_linked_spaces.html.erb +1 -1
  97. data/app/views/decidim/meetings/admin/meetings/_meeting-tr.html.erb +11 -8
  98. data/app/views/decidim/meetings/admin/meetings/_meeting_actions.html.erb +200 -69
  99. data/app/views/decidim/meetings/admin/meetings/_reminders.html.erb +19 -0
  100. data/app/views/decidim/meetings/admin/meetings/_services.html.erb +1 -1
  101. data/app/views/decidim/meetings/admin/meetings/index.html.erb +7 -5
  102. data/app/views/decidim/meetings/admin/meetings/manage_trash.html.erb +2 -1
  103. data/app/views/decidim/meetings/admin/poll/_form.html.erb +6 -6
  104. data/app/views/decidim/meetings/admin/poll/_question.html.erb +13 -13
  105. data/app/views/decidim/meetings/admin/poll/_response_option.html.erb +35 -0
  106. data/app/views/decidim/meetings/admin/poll/_response_option_template.html.erb +7 -0
  107. data/app/views/decidim/meetings/admin/poll/edit.html.erb +3 -3
  108. data/app/views/decidim/meetings/admin/registration_form/edit_questions.html.erb +5 -5
  109. data/app/views/decidim/meetings/admin/registrations/edit.html.erb +19 -39
  110. data/app/views/decidim/meetings/admin/registrations_attendees/index.html.erb +126 -0
  111. data/app/views/decidim/meetings/layouts/live_event.html.erb +1 -1
  112. data/app/views/decidim/meetings/meeting_closes/_form.html.erb +2 -2
  113. data/app/views/decidim/meetings/meetings/_form.html.erb +2 -11
  114. data/app/views/decidim/meetings/meetings/_meeting.html.erb +2 -2
  115. data/app/views/decidim/meetings/meetings/_meeting_actions.html.erb +3 -3
  116. data/app/views/decidim/meetings/meetings/_meeting_agenda.html.erb +2 -2
  117. data/app/views/decidim/meetings/meetings/_meeting_aside.html.erb +11 -10
  118. data/app/views/decidim/meetings/meetings/_meeting_poll_actions.html.erb +3 -3
  119. data/app/views/decidim/meetings/meetings/_registration_code_modal.html.erb +16 -0
  120. data/app/views/decidim/meetings/polls/questions/_index_admin.html.erb +1 -1
  121. data/app/views/decidim/meetings/polls/questions/_published_question.html.erb +5 -5
  122. data/app/views/decidim/meetings/polls/responses/_multiple_option.html.erb +13 -0
  123. data/app/views/decidim/meetings/polls/responses/_single_option.html.erb +13 -0
  124. data/app/views/decidim/meetings/polls/{answers → responses}/admin.html.erb +4 -4
  125. data/app/views/decidim/meetings/polls/{answers → responses}/index.html.erb +4 -4
  126. data/app/views/decidim/meetings/registration_mailer/confirmation.html.erb +6 -1
  127. data/config/assets.rb +2 -2
  128. data/config/locales/ar.yml +1 -26
  129. data/config/locales/bg.yml +2 -32
  130. data/config/locales/ca-IT.yml +86 -40
  131. data/config/locales/ca.yml +86 -40
  132. data/config/locales/cs.yml +71 -43
  133. data/config/locales/de.yml +87 -41
  134. data/config/locales/el.yml +1 -25
  135. data/config/locales/en.yml +89 -43
  136. data/config/locales/es-MX.yml +87 -41
  137. data/config/locales/es-PY.yml +87 -41
  138. data/config/locales/es.yml +87 -41
  139. data/config/locales/eu.yml +86 -40
  140. data/config/locales/fi-plain.yml +86 -40
  141. data/config/locales/fi.yml +85 -39
  142. data/config/locales/fr-CA.yml +79 -38
  143. data/config/locales/fr.yml +79 -38
  144. data/config/locales/ga-IE.yml +1 -7
  145. data/config/locales/gl.yml +1 -19
  146. data/config/locales/hu.yml +1 -22
  147. data/config/locales/id-ID.yml +0 -16
  148. data/config/locales/is-IS.yml +0 -10
  149. data/config/locales/it.yml +1 -29
  150. data/config/locales/ja.yml +88 -42
  151. data/config/locales/lb.yml +1 -15
  152. data/config/locales/lt.yml +1 -28
  153. data/config/locales/lv.yml +0 -16
  154. data/config/locales/nl.yml +1 -26
  155. data/config/locales/no.yml +1 -28
  156. data/config/locales/pl.yml +2 -32
  157. data/config/locales/pt-BR.yml +1 -28
  158. data/config/locales/pt.yml +1 -28
  159. data/config/locales/ro-RO.yml +56 -29
  160. data/config/locales/ru.yml +0 -16
  161. data/config/locales/sk.yml +0 -16
  162. data/config/locales/sl.yml +0 -4
  163. data/config/locales/sv.yml +85 -39
  164. data/config/locales/tr-TR.yml +0 -20
  165. data/config/locales/uk.yml +0 -12
  166. data/config/locales/zh-CN.yml +0 -19
  167. data/config/locales/zh-TW.yml +1 -26
  168. data/db/migrate/20181107175558_add_questionnaire_to_existing_meetings.rb +8 -2
  169. data/db/migrate/20200827153856_add_commentable_counter_cache_to_meetings.rb +8 -2
  170. data/db/migrate/20201016065302_fix_meetings_registration_terms.rb +8 -2
  171. data/db/migrate/20210310120731_add_followable_counter_cache_to_meetings.rb +8 -2
  172. data/db/migrate/20250317103343_rename_answer_to_response_in_decidim_meetings.rb +18 -0
  173. data/db/migrate/20250403094034_add_reminder_customization_to_decidim_meetings.rb +9 -0
  174. data/db/migrate/20250408071941_add_status_to_registrations_to_decidim_meetings_registrations.rb +8 -0
  175. data/lib/decidim/api/agenda_item_type.rb +6 -2
  176. data/lib/decidim/api/agenda_type.rb +6 -2
  177. data/lib/decidim/api/linked_resources_interface.rb +1 -1
  178. data/lib/decidim/api/meeting_type.rb +20 -10
  179. data/lib/decidim/api/service_type.rb +3 -0
  180. data/lib/decidim/meetings/admin_engine.rb +9 -1
  181. data/lib/decidim/meetings/component.rb +29 -8
  182. data/lib/decidim/meetings/engine.rb +6 -21
  183. data/lib/decidim/meetings/meeting_serializer.rb +1 -2
  184. data/lib/decidim/meetings/schema_org_event_meeting_serializer.rb +0 -10
  185. data/lib/decidim/meetings/seeds.rb +4 -13
  186. data/lib/decidim/meetings/test/factories.rb +10 -16
  187. data/lib/decidim/meetings/user_responses_serializer.rb +47 -0
  188. data/lib/decidim/meetings/version.rb +1 -1
  189. data/lib/decidim/meetings.rb +7 -9
  190. metadata +49 -35
  191. data/app/cells/decidim/meetings/attending_organizations_list_cell.rb +0 -32
  192. data/app/forms/decidim/meetings/answer_choice_form.rb +0 -14
  193. data/app/models/decidim/meetings/answer_choice.rb +0 -15
  194. data/app/queries/decidim/meetings/metrics/meeting_followers_metric_measure.rb +0 -31
  195. data/app/queries/decidim/meetings/metrics/meetings_metric_manage.rb +0 -48
  196. data/app/views/decidim/meetings/admin/poll/_answer_option.html.erb +0 -35
  197. data/app/views/decidim/meetings/admin/poll/_answer_option_template.html.erb +0 -7
  198. data/app/views/decidim/meetings/polls/answers/_multiple_option.html.erb +0 -13
  199. data/app/views/decidim/meetings/polls/answers/_single_option.html.erb +0 -13
  200. data/lib/decidim/meetings/download_your_data_user_answers_serializer.rb +0 -39
  201. data/lib/decidim/meetings/user_answers_serializer.rb +0 -47
  202. /data/app/views/decidim/meetings/polls/{answers → responses}/create.js.erb +0 -0
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class AddQuestionnaireToExistingMeetings < ActiveRecord::Migration[5.2]
4
+ class Meeting < ApplicationRecord
5
+ self.table_name = :decidim_meetings_meetings
6
+ include Decidim::HasComponent
7
+ include Decidim::Forms::HasQuestionnaire
8
+ end
9
+
4
10
  def change
5
- Decidim::Meetings::Meeting.transaction do
6
- Decidim::Meetings::Meeting.unscoped.find_each do |meeting|
11
+ Meeting.transaction do
12
+ Meeting.unscoped.find_each do |meeting|
7
13
  if meeting.component.present? && meeting.questionnaire.blank?
8
14
  meeting.update!(
9
15
  questionnaire: Decidim::Forms::Questionnaire.new
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class AddCommentableCounterCacheToMeetings < ActiveRecord::Migration[5.2]
4
+ class Meeting < ApplicationRecord
5
+ self.table_name = :decidim_meetings_meetings
6
+ include Decidim::HasComponent
7
+ include Decidim::Comments::CommentableWithComponent
8
+ end
9
+
4
10
  def change
5
11
  add_column :decidim_meetings_meetings, :comments_count, :integer, null: false, default: 0
6
- Decidim::Meetings::Meeting.reset_column_information
7
- Decidim::Meetings::Meeting.unscoped.find_each(&:update_comments_count)
12
+ Meeting.reset_column_information
13
+ Meeting.unscoped.find_each(&:update_comments_count)
8
14
  end
9
15
  end
@@ -1,11 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FixMeetingsRegistrationTerms < ActiveRecord::Migration[5.2]
4
+ class Meeting < ApplicationRecord
5
+ self.table_name = :decidim_meetings_meetings
6
+ include Decidim::HasComponent
7
+ include Decidim::Authorable
8
+ end
9
+
4
10
  def up
5
11
  reset_column_information
6
12
 
7
13
  PaperTrail.request(enabled: false) do
8
- Decidim::Meetings::Meeting.unscoped.find_each do |meeting|
14
+ Meeting.unscoped.find_each do |meeting|
9
15
  next if meeting.component.nil?
10
16
  # Only user-created meetings have this problem
11
17
  next if meeting.official?
@@ -23,7 +29,7 @@ class FixMeetingsRegistrationTerms < ActiveRecord::Migration[5.2]
23
29
  def down; end
24
30
 
25
31
  def reset_column_information
26
- Decidim::Meetings::Meeting.reset_column_information
32
+ Meeting.reset_column_information
27
33
  Decidim::Component.reset_column_information
28
34
  end
29
35
  end
@@ -1,13 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class AddFollowableCounterCacheToMeetings < ActiveRecord::Migration[5.2]
4
+ class Meeting < ApplicationRecord
5
+ self.table_name = :decidim_meetings_meetings
6
+ include Decidim::HasComponent
7
+ include Decidim::Followable
8
+ end
9
+
4
10
  def change
5
11
  add_column :decidim_meetings_meetings, :follows_count, :integer, null: false, default: 0, index: true
6
12
 
7
13
  reversible do |dir|
8
14
  dir.up do
9
- Decidim::Meetings::Meeting.reset_column_information
10
- Decidim::Meetings::Meeting.unscoped.find_each do |record|
15
+ Meeting.reset_column_information
16
+ Meeting.unscoped.find_each do |record|
11
17
  record.class.reset_counters(record.id, :follows)
12
18
  end
13
19
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RenameAnswerToResponseInDecidimMeetings < ActiveRecord::Migration[7.0]
4
+ def change
5
+ rename_index :decidim_meetings_response_choices, "index_decidim_meetings_answer_choices_answer_id", "index_decidim_meetings_response_choices_response_id"
6
+ rename_index :decidim_meetings_response_choices, "index_decidim_meetings_answer_choices_answer_option_id", "index_decidim_meetings_response_choices_response_option_id"
7
+ rename_index :decidim_meetings_response_options, "index_decidim_meetings_answer_options_question_id", "index_decidim_meetings_response_options_question_id"
8
+ rename_index :decidim_meetings_responses, "index_decidim_meetings_answers_question_id", "index_decidim_meetings_responses_question_id"
9
+ rename_index :decidim_meetings_responses, "index_decidim_meetings_answers_on_decidim_questionnaire_id", "index_decidim_meetings_responses_on_decidim_questionnaire_id"
10
+
11
+ rename_table :decidim_meetings_answers, :decidim_meetings_responses
12
+ rename_table :decidim_meetings_answer_choices, :decidim_meetings_response_choices
13
+ rename_table :decidim_meetings_answer_options, :decidim_meetings_response_options
14
+
15
+ rename_column :decidim_meetings_response_choices, :decidim_answer_id, :decidim_response_id
16
+ rename_column :decidim_meetings_response_choices, :decidim_answer_option_id, :decidim_response_option_id
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddReminderCustomizationToDecidimMeetings < ActiveRecord::Migration[7.0]
4
+ def change
5
+ add_column :decidim_meetings_meetings, :reminder_enabled, :boolean, default: true, null: false
6
+ add_column :decidim_meetings_meetings, :send_reminders_before_hours, :integer
7
+ add_column :decidim_meetings_meetings, :reminder_message_custom_content, :jsonb, default: {}, null: false
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddStatusToRegistrationsToDecidimMeetingsRegistrations < ActiveRecord::Migration[7.0]
4
+ def change
5
+ add_column :decidim_meetings_registrations, :status, :string, default: "registered"
6
+ add_index :decidim_meetings_registrations, :status
7
+ end
8
+ end
@@ -3,11 +3,11 @@
3
3
  module Decidim
4
4
  module Meetings
5
5
  class AgendaItemType < Decidim::Api::Types::BaseObject
6
- implements Decidim::Core::TimestampsInterface
7
-
8
6
  graphql_name "MeetingAgendaItem"
9
7
  description "A meeting agenda item"
10
8
 
9
+ implements Decidim::Core::TimestampsInterface
10
+
11
11
  field :agenda, Decidim::Meetings::AgendaType, "Belonging agenda", null: true
12
12
  field :description, Decidim::Core::TranslatedFieldType, "The description for this agenda item", null: true
13
13
  field :duration, GraphQL::Types::Int, "Duration in number of minutes for this item in this agenda", null: false
@@ -16,6 +16,10 @@ module Decidim
16
16
  field :parent, Decidim::Meetings::AgendaItemType, "Parent agenda item, if available", null: true
17
17
  field :position, GraphQL::Types::Int, "Order position for this agenda item", null: false
18
18
  field :title, Decidim::Core::TranslatedFieldType, "The title for this agenda item", null: true
19
+
20
+ def self.authorized?(object, _context)
21
+ object.agenda.visible?
22
+ end
19
23
  end
20
24
  end
21
25
  end
@@ -11,8 +11,12 @@ module Decidim
11
11
  field :id, GraphQL::Types::ID, "The ID for the agenda", null: false
12
12
  field :items, [Decidim::Meetings::AgendaItemType, { null: true }], "Items and sub-items of the agenda", method: :agenda_items, null: false
13
13
  field :title, Decidim::Core::TranslatedFieldType, "The title for the agenda", null: true
14
- # probably useful in the future, when handling user permissions
15
- # field :visible, !types.Boolean, "Whether this minutes is public or not", property: :visible
14
+
15
+ def self.authorized?(object, context)
16
+ super && object.visible?
17
+ rescue Decidim::PermissionAction::PermissionNotSetError
18
+ false
19
+ end
16
20
  end
17
21
  end
18
22
  end
@@ -8,7 +8,7 @@ module Decidim
8
8
  graphql_name "MeetingsLinkedResourcesInterface"
9
9
  description "An interface that can be used with Resourceable models."
10
10
 
11
- if Decidim::Meetings.enable_proposal_linking
11
+ if Decidim.module_installed?(:proposals)
12
12
  field(
13
13
  :proposals_from_meeting,
14
14
  [Decidim::Proposals::ProposalType, { null: true }],
@@ -9,43 +9,57 @@ module Decidim
9
9
  implements Decidim::Core::AuthorableInterface
10
10
  implements Decidim::Core::TaxonomizableInterface
11
11
  implements Decidim::Core::AttachableInterface
12
+ implements Decidim::Core::FollowableInterface
13
+ implements Decidim::Core::ReferableInterface
14
+ implements Decidim::Core::LocalizableInterface
12
15
  implements Decidim::Core::TimestampsInterface
13
16
  implements Decidim::Meetings::ServicesInterface
14
- implements Decidim::Meetings::LinkedResourcesInterface if Decidim::Meetings.enable_proposal_linking
17
+ implements Decidim::Meetings::LinkedResourcesInterface if Decidim.module_installed?(:proposals)
15
18
  implements Decidim::Forms::QuestionnaireEntityInterface
16
19
 
17
- field :address, GraphQL::Types::String, "The physical address of this meeting (used for geolocation)", null: true
18
20
  field :agenda, Decidim::Meetings::AgendaType, "Agenda for this meeting, if available", null: true
19
- field :attendee_count, GraphQL::Types::Int, "Amount of attendees to this meeting", method: :attendees_count, null: true
21
+ field :attendees_count, GraphQL::Types::Int, "Amount of attendees to this meeting", null: true
20
22
  field :attending_organizations, GraphQL::Types::String, "list of attending organizations", null: true
21
23
  field :audio_url, GraphQL::Types::String, "URL for the audio of the session, if any", null: true
22
24
  field :closed, GraphQL::Types::Boolean, "Whether this meeting is closed or not.", method: :closed?, null: false
25
+ field :closed_at, Decidim::Core::DateTimeType, "The date and time this meeting was closed", null: true
23
26
  field :closing_report, Decidim::Core::TranslatedFieldType, "The closing report of this meeting.", null: true
27
+ field :comments_enabled, GraphQL::Types::Boolean, "Whether the comments are enabled or not", null: false
28
+ field :comments_end_time, Decidim::Core::DateTimeType, "The ending time when the comments will be allowed on this meeting", null: true
29
+ field :comments_start_time, Decidim::Core::DateTimeType, "The starting time when the comments will be allowed on this meeting", null: true
24
30
  field :contribution_count, GraphQL::Types::Int, "Amount of contributions to this meeting", method: :contributions_count, null: true
25
- field :coordinates, Decidim::Core::CoordinatesType, "Physical coordinates for this meeting", null: true
31
+ field :customize_registration_email, GraphQL::Types::Boolean, "The email to customize for the registration", null: true
26
32
  field :description, Decidim::Core::TranslatedFieldType, "The description of this meeting.", null: true
27
33
  field :end_time, Decidim::Core::DateTimeType, "The time this meeting ends", null: false
28
34
  field :id, GraphQL::Types::ID, "ID of this meeting", null: false
35
+ field :iframe_access_level, GraphQL::Types::String, "The iframe access level of the meeting ", null: true
29
36
  field :iframe_embed_type, GraphQL::Types::String, "The type of displaying of the online meeting URL", null: true
30
- field :is_withdrawn, GraphQL::Types::Boolean, "Whether this meeting is withdrawn or not.", method: :withdrawn?, null: false
31
37
  field :location, Decidim::Core::TranslatedFieldType, "The location of this meeting (free format)", null: true
32
38
  field :location_hints, Decidim::Core::TranslatedFieldType, "The location of this meeting (free format)", null: true
33
39
  field :online_meeting_url, GraphQL::Types::String, "The URL of the meeting (when the type is online)", null: false
34
40
  field :private_meeting, GraphQL::Types::Boolean, "Whether the meeting is private or not (it can only be true if transparent)", null: false
35
- field :reference, GraphQL::Types::String, "Reference for this meeting", null: false
41
+ field :public_participants, [Decidim::Core::UserType, { null: true }], "The list of users participating to this meeting", null: false
42
+ field :published_at, Decidim::Core::DateTimeType, "The time this page was published", null: false
36
43
  field :registration_form, Decidim::Forms::QuestionnaireType, description: "If registration requires to fill a form, this is the questionnaire", null: true
37
44
  field :registration_form_enabled, GraphQL::Types::Boolean, "Whether the registrations have a form or not", null: false
38
45
  field :registration_terms, Decidim::Core::TranslatedFieldType, "The registration terms", null: true
46
+ field :registration_type, GraphQL::Types::String, "The type of registration (disabled, on this platform or different platform)", null: false
47
+ field :registration_url, GraphQL::Types::String, "URL for the registration form", null: true
39
48
  field :registrations_enabled, GraphQL::Types::Boolean, "Whether the registrations are enabled or not", null: false
40
49
  field :remaining_slots, GraphQL::Types::Int, "Amount of slots available for this meeting", null: true
41
50
  field :start_time, Decidim::Core::DateTimeType, "The time this meeting starts", null: false
42
51
  field :title, Decidim::Core::TranslatedFieldType, "The title of this meeting.", null: false
43
52
  field :transparent, GraphQL::Types::Boolean, "For private meetings, information is public if transparent", null: false
44
53
  field :type_of_meeting, GraphQL::Types::String, "The type of the meeting (online or in-person)", null: false
54
+ field :url, GraphQL::Types::String, "The URL for this meeting", null: false
45
55
  field :video_url, GraphQL::Types::String, "URL for the video of the session, if any", null: true
46
56
  field :withdrawn, GraphQL::Types::Boolean, "Whether this meeting has been withdrawn or not", method: :withdrawn?, null: true
47
57
  field :withdrawn_at, Decidim::Core::DateTimeType, description: "The date and time this meeting was withdrawn", null: true
48
58
 
59
+ def url
60
+ Decidim::ResourceLocatorPresenter.new(object).url
61
+ end
62
+
49
63
  def registration_form
50
64
  object.questionnaire if object.registration_form_enabled?
51
65
  end
@@ -66,10 +80,6 @@ module Decidim
66
80
  object.audio_url if object.closing_visible?
67
81
  end
68
82
 
69
- def coordinates
70
- [object.latitude, object.longitude]
71
- end
72
-
73
83
  def self.authorized?(object, context)
74
84
  context[:meeting] = object
75
85
 
@@ -6,7 +6,10 @@ module Decidim
6
6
  graphql_name "MeetingService"
7
7
  description "A meeting service"
8
8
 
9
+ implements Decidim::Core::TimestampsInterface
10
+
9
11
  field :description, Decidim::Core::TranslatedFieldType, "The description for the service", null: true
12
+ field :id, GraphQL::Types::ID, "The ID of the service", null: false
10
13
  field :title, Decidim::Core::TranslatedFieldType, "The title for the service", null: true
11
14
  end
12
15
  end
@@ -12,7 +12,7 @@ module Decidim
12
12
  paths["lib/tasks"] = nil
13
13
 
14
14
  routes do
15
- get "/answer_options", to: "registration_form#answer_options", as: :answer_options_meeting
15
+ get "/response_options", to: "registration_form#response_options", as: :response_options_meeting
16
16
 
17
17
  resources :meetings do
18
18
  member do
@@ -34,8 +34,16 @@ module Decidim
34
34
  end
35
35
  collection do
36
36
  get :export
37
+ end
38
+ end
39
+ resources :registrations_attendees, only: [:index] do
40
+ collection do
37
41
  post :validate_registration_code
38
42
  end
43
+ member do
44
+ get :qr_mark_as_attendee
45
+ put :mark_as_attendee
46
+ end
39
47
  end
40
48
  resources :agenda, except: [:index, :destroy]
41
49
  resources :attachment_collections, except: [:show]
@@ -27,22 +27,43 @@ Decidim.register_component(:meetings) do |component|
27
27
  resource.searchable = true
28
28
  end
29
29
 
30
- component.register_stat :meetings_count, primary: true, priority: Decidim::StatsRegistry::MEDIUM_PRIORITY do |components, start_at, end_at|
30
+ component.register_stat :meetings_count,
31
+ primary: true,
32
+ admin: false,
33
+ priority: Decidim::StatsRegistry::HIGH_PRIORITY,
34
+ icon_name: "map-pin-line",
35
+ tooltip_key: "meetings_count_tooltip" do |components, start_at, end_at|
31
36
  meetings = Decidim::Meetings::FilteredMeetings.for(components, start_at, end_at).not_withdrawn
32
37
  meetings.count
33
38
  end
34
39
 
35
- component.register_stat :followers_count, tag: :followers, priority: Decidim::StatsRegistry::LOW_PRIORITY do |components, start_at, end_at|
40
+ component.register_stat :participatory_space_meetings_count,
41
+ priority: Decidim::StatsRegistry::MEDIUM_PRIORITY,
42
+ icon_name: "map-pin-line",
43
+ tooltip_key: "meetings_count_tooltip" do |components, start_at, end_at|
44
+ meetings = Decidim::Meetings::FilteredMeetings.for(components, start_at, end_at).not_withdrawn
45
+ meetings.count
46
+ end
47
+
48
+ component.register_stat :followers_count,
49
+ tag: :followers,
50
+ icon_name: "user-follow-line",
51
+ tooltip_key: "followers_count_tooltip",
52
+ priority: Decidim::StatsRegistry::MEDIUM_PRIORITY do |components, start_at, end_at|
36
53
  meetings_ids = Decidim::Meetings::FilteredMeetings.for(components, start_at, end_at).pluck(:id)
37
54
  Decidim::Follow.where(decidim_followable_type: "Decidim::Meetings::Meeting", decidim_followable_id: meetings_ids).count
38
55
  end
39
56
 
40
- component.register_stat :comments_count, tag: :comments do |components, start_at, end_at|
57
+ component.register_stat :comments_count,
58
+ priority: Decidim::StatsRegistry::HIGH_PRIORITY,
59
+ icon_name: "chat-1-line",
60
+ tooltip_key: "comments_count",
61
+ tag: :comments do |components, start_at, end_at|
41
62
  meetings = Decidim::Meetings::FilteredMeetings.for(components, start_at, end_at).not_hidden
42
63
  meetings.sum(:comments_count)
43
64
  end
44
65
 
45
- component.register_stat :attendees_count, primary: true, priority: Decidim::StatsRegistry::MEDIUM_PRIORITY do |components, start_at, end_at|
66
+ component.register_stat :attendees_count, primary: true, priority: Decidim::StatsRegistry::LOW_PRIORITY do |components, start_at, end_at|
46
67
  meetings = Decidim::Meetings::Meeting.closed.not_hidden.published.where(component: components, closing_visible: true)
47
68
  meetings = meetings.where(closed_at: start_at..) if start_at.present?
48
69
  meetings = meetings.where(closed_at: ..end_at) if end_at.present?
@@ -67,7 +88,7 @@ Decidim.register_component(:meetings) do |component|
67
88
  exports.collection do |component_instance|
68
89
  Decidim::Comments::Export.comments_for_resource(
69
90
  Decidim::Meetings::Meeting, component_instance
70
- ).includes(:author, :user_group, root_commentable: { component: { participatory_space: :organization } })
91
+ ).includes(:author, root_commentable: { component: { participatory_space: :organization } })
71
92
  end
72
93
 
73
94
  exports.include_in_open_data = true
@@ -75,14 +96,14 @@ Decidim.register_component(:meetings) do |component|
75
96
  exports.serializer Decidim::Comments::CommentSerializer
76
97
  end
77
98
 
78
- component.exports :answers do |exports|
99
+ component.exports :responses do |exports|
79
100
  exports.collection do |_component, _user, resource_id|
80
- Decidim::Meetings::QuestionnaireUserAnswers.for(resource_id)
101
+ Decidim::Meetings::QuestionnaireUserResponses.for(resource_id)
81
102
  end
82
103
 
83
104
  exports.formats %w(CSV JSON Excel FormPDF)
84
105
 
85
- exports.serializer Decidim::Meetings::UserAnswersSerializer
106
+ exports.serializer Decidim::Meetings::UserResponsesSerializer
86
107
  end
87
108
 
88
109
  component.actions = %w(join comment)
@@ -25,14 +25,16 @@ module Decidim
25
25
  get :create
26
26
  get :decline_invitation
27
27
  get :join, action: :show
28
- post :answer
28
+ post :respond
29
+ get :join_waitlist, action: :show
30
+ post :join_waitlist
29
31
  end
30
32
  end
31
33
  resources :versions, only: [:show]
32
34
  resource :live_event, only: :show
33
35
  namespace :polls do
34
36
  resources :questions, only: [:index, :update]
35
- resources :answers, only: [:index, :create] do
37
+ resources :responses, only: [:index, :create] do
36
38
  collection do
37
39
  get :admin
38
40
  end
@@ -126,24 +128,7 @@ module Decidim
126
128
  end
127
129
  end
128
130
 
129
- initializer "decidim_meetings.register_metrics" do
130
- Decidim.metrics_registry.register(:meetings) do |metric_registry|
131
- metric_registry.manager_class = "Decidim::Meetings::Metrics::MeetingsMetricManage"
132
-
133
- metric_registry.settings do |settings|
134
- settings.attribute :highlighted, type: :boolean, default: false
135
- settings.attribute :scopes, type: :array, default: %w(home participatory_process)
136
- settings.attribute :weight, type: :integer, default: 5
137
- settings.attribute :stat_block, type: :string, default: "small"
138
- end
139
- end
140
-
141
- Decidim.metrics_operation.register(:followers, :meetings) do |metric_operation|
142
- metric_operation.manager_class = "Decidim::Meetings::Metrics::MeetingFollowersMetricMeasure"
143
- end
144
- end
145
-
146
- initializer "decidim_meetings.webpacker.assets_path" do
131
+ initializer "decidim_meetings.shakapacker.assets_path" do
147
132
  Decidim.register_assets_path File.expand_path("app/packs", root)
148
133
  end
149
134
 
@@ -168,7 +153,7 @@ module Decidim
168
153
  Decidim::AuthorizationTransfer.register(:meetings) do |transfer|
169
154
  transfer.move_records(Decidim::Meetings::Meeting, :decidim_author_id)
170
155
  transfer.move_records(Decidim::Meetings::Registration, :decidim_user_id)
171
- transfer.move_records(Decidim::Meetings::Answer, :decidim_user_id)
156
+ transfer.move_records(Decidim::Meetings::Response, :decidim_user_id)
172
157
  end
173
158
  end
174
159
  end
@@ -55,7 +55,6 @@ module Decidim
55
55
  closing_report: resource.closing_report,
56
56
  attending_organizations: resource.attending_organizations,
57
57
  registration_url: resource.registration_url,
58
- decidim_user_group_id: resource.decidim_user_group_id,
59
58
  decidim_author_type: resource.decidim_author_type,
60
59
  video_url: resource.video_url,
61
60
  audio_url: resource.audio_url,
@@ -88,7 +87,7 @@ module Decidim
88
87
 
89
88
  def author_url(author)
90
89
  if author.respond_to?(:nickname)
91
- profile_url(author) # is a Decidim::User or Decidim::UserGroup
90
+ profile_url(author) # is a Decidim::User
92
91
  else
93
92
  root_url # is a Decidim::Organization
94
93
  end
@@ -40,8 +40,6 @@ module Decidim
40
40
  alias resource meeting
41
41
 
42
42
  def organizer
43
- return organizer_user_group if meeting.decidim_user_group_id?
44
-
45
43
  case meeting.author.class.name
46
44
  when "Decidim::Organization"
47
45
  organizer_organization
@@ -50,14 +48,6 @@ module Decidim
50
48
  end
51
49
  end
52
50
 
53
- def organizer_user_group
54
- {
55
- "@type": "Organization",
56
- name: meeting.author.name,
57
- url: profile_url(meeting.user_group)
58
- }
59
- end
60
-
61
51
  def organizer_organization
62
52
  {
63
53
  "@type": "Organization",
@@ -15,19 +15,19 @@ module Decidim
15
15
  def call
16
16
  component = create_component!
17
17
 
18
- 2.times do
18
+ number_of_records.times do
19
19
  create_meeting!(component:, type: :online)
20
20
  create_meeting!(component:, type: :online_live_event)
21
21
  create_meeting!(component:, type: :hybrid)
22
22
  meeting = create_meeting!(component:, type: :in_person)
23
23
 
24
- 2.times do
24
+ number_of_records.times do
25
25
  create_service!(meeting:)
26
26
  end
27
27
 
28
28
  create_questionnaire_for!(meeting:)
29
29
 
30
- 2.times do |_n|
30
+ number_of_records.times do |_n|
31
31
  create_meeting_registration!(meeting:)
32
32
  end
33
33
 
@@ -35,7 +35,6 @@ module Decidim
35
35
  end
36
36
 
37
37
  create_meeting!(component:, type: [:in_person, :online, :hybrid].sample, author_type: :user)
38
- create_meeting!(component:, type: [:in_person, :online, :hybrid].sample, author_type: :user_group)
39
38
  end
40
39
 
41
40
  def create_component!
@@ -132,14 +131,6 @@ module Decidim
132
131
  params.merge(
133
132
  author: Decidim::User.where(decidim_organization_id: participatory_space.decidim_organization_id).all.sample
134
133
  )
135
- when :user_group
136
- user_group = Decidim::UserGroup.where(decidim_organization_id: participatory_space.decidim_organization_id).verified.sample
137
- author = user_group.users.sample
138
-
139
- params.merge(
140
- author:,
141
- user_group:
142
- )
143
134
  else
144
135
  params # official
145
136
  end
@@ -149,7 +140,7 @@ module Decidim
149
140
  #
150
141
  # @param component [Decidim::Component] The component where this class will be created
151
142
  # @param type [:in_person, :hybrid, :online, :online_live_event] The meeting type
152
- # @param author_type [:official, :user, :user_group] Which type the author of the meeting will be
143
+ # @param author_type [:official, :user] Which type the author of the meeting will be
153
144
  #
154
145
  # @return [Decidim::Meeting]
155
146
  def create_meeting!(component:, type: :in_person, author_type: :official)
@@ -37,6 +37,9 @@ FactoryBot.define do
37
37
  longitude { Faker::Address.longitude }
38
38
  start_time { 1.day.from_now }
39
39
  end_time { start_time.advance(hours: 2) }
40
+ reminder_enabled { true }
41
+ send_reminders_before_hours { 48 }
42
+ reminder_message_custom_content { generate_localized_description(:meeting_reminder_message, skip_injection:) }
40
43
  private_meeting { false }
41
44
  transparent { true }
42
45
  questionnaire { build(:questionnaire) }
@@ -105,15 +108,6 @@ FactoryBot.define do
105
108
 
106
109
  trait(:participant_author) { not_official }
107
110
 
108
- trait :user_group_author do
109
- author do
110
- create(:user, organization: component.organization, skip_injection:) if component
111
- end
112
- user_group do
113
- create(:user_group, :verified, organization: component.organization, users: [author], skip_injection:) if component
114
- end
115
- end
116
-
117
111
  trait :closed do
118
112
  closing_report { generate_localized_title(:meeting_closing_report, skip_injection:) }
119
113
  attendees_count { rand(50) }
@@ -291,8 +285,8 @@ FactoryBot.define do
291
285
  status { 0 }
292
286
  question_type { Decidim::Meetings::Question::QUESTION_TYPES.first }
293
287
  questionnaire factory: :meetings_poll_questionnaire
294
- answer_options do
295
- Array.new(3).collect { build(:meetings_poll_answer_option, question: nil, skip_injection:) }
288
+ response_options do
289
+ Array.new(3).collect { build(:meetings_poll_response_option, question: nil, skip_injection:) }
296
290
  end
297
291
 
298
292
  trait :unpublished do
@@ -308,7 +302,7 @@ FactoryBot.define do
308
302
  end
309
303
  end
310
304
 
311
- factory :meetings_poll_answer, class: "Decidim::Meetings::Answer" do
305
+ factory :meetings_poll_response, class: "Decidim::Meetings::Response" do
312
306
  transient do
313
307
  skip_injection { false }
314
308
  end
@@ -317,7 +311,7 @@ FactoryBot.define do
317
311
  user { create(:user, organization: questionnaire.questionnaire_for.organization, skip_injection:) }
318
312
  end
319
313
 
320
- factory :meetings_poll_answer_option, class: "Decidim::Meetings::AnswerOption" do
314
+ factory :meetings_poll_response_option, class: "Decidim::Meetings::ResponseOption" do
321
315
  transient do
322
316
  skip_injection { false }
323
317
  end
@@ -325,11 +319,11 @@ FactoryBot.define do
325
319
  body { generate_localized_title }
326
320
  end
327
321
 
328
- factory :meetings_poll_answer_choice, class: "Decidim::Meetings::AnswerChoice" do
322
+ factory :meetings_poll_response_choice, class: "Decidim::Meetings::ResponseChoice" do
329
323
  transient do
330
324
  skip_injection { false }
331
325
  end
332
- answer factory: :meetings_poll_answer
333
- answer_option { create(:meetings_poll_answer_option, question: answer.question, skip_injection:) }
326
+ response factory: :meetings_poll_response
327
+ response_option { create(:meetings_poll_response_option, question: response.question, skip_injection:) }
334
328
  end
335
329
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ # This class serializes the responses given by a User for questionnaire so can be
6
+ # exported to CSV, JSON or other formats.
7
+ class UserResponsesSerializer < Decidim::Exporters::Serializer
8
+ include Decidim::TranslationsHelper
9
+
10
+ # Public: Initializes the serializer with a collection of Responses.
11
+ def initialize(responses)
12
+ @responses = responses
13
+ end
14
+
15
+ # Public: Exports a hash with the serialized data for the user responses.
16
+ def serialize
17
+ @responses.each_with_index.inject({}) do |serialized, (response, idx)|
18
+ serialized.update(
19
+ response_translated_attribute_name(:id) => [response.id, response.user.id].join("_"),
20
+ response_translated_attribute_name(:created_at) => response.created_at,
21
+ response_translated_attribute_name(:user_status) => response_translated_attribute_name(response.decidim_user_id.present? ? "registered" : "unregistered"),
22
+ "#{idx + 1}. #{translated_attribute(response.question.body)}" => normalize_body(response)
23
+ )
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :responses
30
+ alias resource responses
31
+
32
+ def normalize_body(response)
33
+ normalize_choices(response.choices)
34
+ end
35
+
36
+ def normalize_choices(choices)
37
+ choices.map do |choice|
38
+ choice.try(:body)
39
+ end
40
+ end
41
+
42
+ def response_translated_attribute_name(attribute)
43
+ I18n.t(attribute.to_sym, scope: "decidim.forms.user_responses_serializer")
44
+ end
45
+ end
46
+ end
47
+ end
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-meetings version.
5
5
  module Meetings
6
6
  def self.version
7
- "0.30.2"
7
+ "0.31.0.rc1"
8
8
  end
9
9
  end
10
10
  end