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
@@ -8,14 +8,16 @@ module Decidim
8
8
  fetch_form_attributes :registrations_enabled, :registration_form_enabled
9
9
 
10
10
  def run_after_hooks
11
- return unless resource.previous_changes["registrations_enabled"].present? && resource.registrations_enabled?
11
+ if resource.previous_changes["registrations_enabled"].present? && resource.registrations_enabled?
12
+ Decidim::EventsManager.publish(
13
+ event: "decidim.events.meetings.registrations_enabled",
14
+ event_class: Decidim::Meetings::MeetingRegistrationsEnabledEvent,
15
+ resource:,
16
+ followers: resource.followers
17
+ )
18
+ end
12
19
 
13
- Decidim::EventsManager.publish(
14
- event: "decidim.events.meetings.registrations_enabled",
15
- event_class: Decidim::Meetings::MeetingRegistrationsEnabledEvent,
16
- resource:,
17
- followers: resource.followers
18
- )
20
+ promote_from_waitlist!
19
21
  end
20
22
 
21
23
  protected
@@ -33,6 +35,16 @@ module Decidim
33
35
  end
34
36
  super.merge(extra_params)
35
37
  end
38
+
39
+ def promote_from_waitlist!
40
+ slot_changes = resource.previous_changes.keys & %w(available_slots reserved_slots)
41
+ return if slot_changes.empty?
42
+ return if resource.available_slots.zero?
43
+ return unless resource.remaining_slots.positive?
44
+ return unless resource.registrations.on_waiting_list.exists?
45
+
46
+ Decidim::Meetings::PromoteFromWaitlistJob.perform_later(resource.id)
47
+ end
36
48
  end
37
49
  end
38
50
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module Meetings
5
- # This command is executed when a participant or user group creates a Meeting from the public
5
+ # This command is executed when a participant creates a Meeting from the public
6
6
  # views.
7
7
  class CreateMeeting < Decidim::Commands::CreateResource
8
8
  fetch_form_attributes :end_time, :start_time, :address, :latitude, :longitude,
@@ -40,7 +40,7 @@ module Decidim
40
40
  def resource_class = Decidim::Meetings::Meeting
41
41
 
42
42
  def attributes
43
- parsed_title = Decidim::ContentProcessor.parse_with_processor(:hashtag, form.title, current_organization: form.current_organization).rewrite
43
+ parsed_title = Decidim::ContentProcessor.parse(form.title, current_organization: form.current_organization).rewrite
44
44
  parsed_description = Decidim::ContentProcessor.parse(form.description, current_organization: form.current_organization).rewrite
45
45
 
46
46
  super.merge({
@@ -49,7 +49,6 @@ module Decidim
49
49
  location: { I18n.locale => form.location },
50
50
  location_hints: { I18n.locale => form.location_hints },
51
51
  author: form.current_user,
52
- decidim_user_group_id: form.user_group_id,
53
52
  registration_terms: { I18n.locale => form.registration_terms },
54
53
  type_of_meeting: form.clean_type_of_meeting,
55
54
  published_at: Time.current
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Decidim
4
4
  module Meetings
5
- # This command is executed when the user creates an Answer in a meeting poll.
6
- class CreateAnswer < Decidim::Command
5
+ # This command is executed when the user creates an Response in a meeting poll.
6
+ class CreateResponse < Decidim::Command
7
7
  delegate :current_user, to: :form
8
8
 
9
9
  def initialize(form, questionnaire)
@@ -11,14 +11,14 @@ module Decidim
11
11
  @questionnaire = questionnaire
12
12
  end
13
13
 
14
- # Creates the answer if valid.
14
+ # Creates the response if valid.
15
15
  #
16
16
  # Broadcasts :ok if successful, :invalid otherwise.
17
17
  def call
18
18
  return broadcast(:invalid) if form.invalid?
19
19
 
20
20
  transaction do
21
- answer_question
21
+ response_question
22
22
  end
23
23
 
24
24
  broadcast(:ok)
@@ -26,27 +26,27 @@ module Decidim
26
26
 
27
27
  private
28
28
 
29
- attr_reader :form, :answer
29
+ attr_reader :form, :response
30
30
 
31
31
  def invalid?
32
32
  form.invalid?
33
33
  end
34
34
 
35
- def answer_question
36
- answer = Answer.new(
35
+ def response_question
36
+ response = Response.new(
37
37
  user: current_user,
38
38
  questionnaire: @questionnaire,
39
39
  question: form.question
40
40
  )
41
41
 
42
42
  form.selected_choices.each do |choice|
43
- answer.choices.build(
44
- body: choice.body || translated_attribute(AnswerOption.find_by(id: choice.answer_option_id)&.body),
45
- decidim_answer_option_id: choice.answer_option_id
43
+ response.choices.build(
44
+ body: choice.body || translated_attribute(ResponseOption.find_by(id: choice.response_option_id)&.body),
45
+ decidim_response_option_id: choice.response_option_id
46
46
  )
47
47
  end
48
48
 
49
- answer.save!
49
+ response.save!
50
50
  end
51
51
  end
52
52
  end
@@ -11,7 +11,6 @@ module Decidim
11
11
  # form - A form object with params; can be a questionnaire.
12
12
  def initialize(meeting, form)
13
13
  @meeting = meeting
14
- @user_group = Decidim::UserGroup.find_by(id: form.user_group_id)
15
14
  @form = form
16
15
  end
17
16
 
@@ -22,7 +21,7 @@ module Decidim
22
21
  def call
23
22
  return broadcast(:invalid) unless can_join_meeting?
24
23
  return broadcast(:invalid_form) unless form.valid?
25
- return broadcast(:invalid) if answer_questionnaire == :invalid
24
+ return broadcast(:invalid) if response_questionnaire == :invalid
26
25
 
27
26
  meeting.with_lock do
28
27
  create_registration
@@ -38,16 +37,16 @@ module Decidim
38
37
 
39
38
  private
40
39
 
41
- attr_reader :meeting, :user_group, :registration, :form
40
+ attr_reader :meeting, :registration, :form
42
41
 
43
42
  def accept_invitation
44
43
  meeting.invites.find_by(user: current_user)&.accept!
45
44
  end
46
45
 
47
- def answer_questionnaire
46
+ def response_questionnaire
48
47
  return unless questionnaire?
49
48
 
50
- Decidim::Forms::AnswerQuestionnaire.call(form, meeting.questionnaire) do
49
+ Decidim::Forms::ResponseQuestionnaire.call(form, meeting.questionnaire) do
51
50
  on(:ok) do
52
51
  return :valid
53
52
  end
@@ -62,7 +61,6 @@ module Decidim
62
61
  @registration = Decidim::Meetings::Registration.create!(
63
62
  meeting:,
64
63
  user: current_user,
65
- user_group:,
66
64
  public_participation: form.public_participation
67
65
  )
68
66
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ # This command is executed when the user joins a meeting waitlist.
6
+ class JoinWaitlist < Decidim::Command
7
+ delegate :current_user, to: :form
8
+
9
+ def initialize(meeting, form)
10
+ @meeting = meeting
11
+ @form = form
12
+ end
13
+
14
+ def call
15
+ return broadcast(:invalid) unless can_join_waitlist?
16
+ return broadcast(:invalid_form) unless form.valid?
17
+
18
+ meeting.with_lock do
19
+ create_waitlist_entry
20
+ send_waitlist_notification
21
+ end
22
+
23
+ broadcast(:ok)
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :meeting, :user_group, :form
29
+
30
+ def can_join_waitlist?
31
+ meeting.waitlist_enabled? && !meeting.registrations.exists?(user: current_user) && !meeting.has_available_slots?
32
+ end
33
+
34
+ def create_waitlist_entry
35
+ @registration = Decidim::Meetings::Registration.create!(
36
+ meeting:,
37
+ user: current_user,
38
+ public_participation: form.public_participation,
39
+ status: :waiting_list
40
+ )
41
+ end
42
+
43
+ def send_waitlist_notification
44
+ Decidim::EventsManager.publish(
45
+ event: "decidim.events.meetings.meeting_waitlist_added",
46
+ event_class: Decidim::Meetings::MeetingRegistrationNotificationEvent,
47
+ resource: meeting,
48
+ affected_users: [current_user]
49
+ )
50
+ end
51
+ end
52
+ end
53
+ end
@@ -22,8 +22,9 @@ module Decidim
22
22
 
23
23
  @meeting.with_lock do
24
24
  destroy_registration
25
- destroy_questionnaire_answers
25
+ destroy_questionnaire_responses
26
26
  decrement_score
27
+ promote_from_waitlist!
27
28
  end
28
29
  broadcast(:ok)
29
30
  end
@@ -38,18 +39,26 @@ module Decidim
38
39
  registration.destroy!
39
40
  end
40
41
 
41
- def questionnaire_answers
42
+ def questionnaire_responses
42
43
  questionnaire = Decidim::Forms::Questionnaire.find_by(questionnaire_for_id: @meeting)
43
- questionnaire.answers.where(user: @user) if questionnaire.present?
44
+ questionnaire.responses.where(user: @user) if questionnaire.present?
44
45
  end
45
46
 
46
- def destroy_questionnaire_answers
47
- questionnaire_answers.try(:destroy_all)
47
+ def destroy_questionnaire_responses
48
+ questionnaire_responses.try(:destroy_all)
48
49
  end
49
50
 
50
51
  def decrement_score
51
52
  Decidim::Gamification.decrement_score(@user, :attended_meetings)
52
53
  end
54
+
55
+ def promote_from_waitlist!
56
+ return if @meeting.available_slots.zero?
57
+ return unless @meeting.remaining_slots.positive?
58
+ return unless @meeting.registrations.on_waiting_list.exists?
59
+
60
+ Decidim::Meetings::PromoteFromWaitlistJob.perform_later(@meeting.id)
61
+ end
53
62
  end
54
63
  end
55
64
  end
@@ -45,7 +45,7 @@ module Decidim
45
45
  end
46
46
 
47
47
  def update_meeting!
48
- parsed_title = Decidim::ContentProcessor.parse_with_processor(:hashtag, form.title, current_organization: form.current_organization).rewrite
48
+ parsed_title = Decidim::ContentProcessor.parse(form.title, current_organization: form.current_organization).rewrite
49
49
  parsed_description = Decidim::ContentProcessor.parse(form.description, current_organization: form.current_organization).rewrite
50
50
 
51
51
  Decidim.traceability.update!(
@@ -62,7 +62,6 @@ module Decidim
62
62
  location: { I18n.locale => form.location },
63
63
  location_hints: { I18n.locale => form.location_hints },
64
64
  author: current_user,
65
- decidim_user_group_id: form.user_group_id,
66
65
  registration_type: form.registration_type,
67
66
  registration_url: form.registration_url,
68
67
  available_slots: form.available_slots,
@@ -41,7 +41,7 @@ module Decidim
41
41
  taxonomies_part_of_contains: taxonomy_ids_hash(available_root_taxonomies),
42
42
  closed_at_present: %w(true false),
43
43
  is_upcoming_true: %w(true false),
44
- with_any_origin: %w(participants official user_group)
44
+ with_any_origin: %w(participants official)
45
45
  }
46
46
  end
47
47
 
@@ -27,7 +27,7 @@ module Decidim
27
27
 
28
28
  on(:invalid) do
29
29
  flash.now[:alert] = I18n.t("agenda.create.invalid", scope: "decidim.meetings.admin")
30
- render action: "new"
30
+ render action: "new", status: :unprocessable_entity
31
31
  end
32
32
  end
33
33
  end
@@ -51,7 +51,7 @@ module Decidim
51
51
 
52
52
  on(:invalid) do
53
53
  flash.now[:alert] = I18n.t("agenda.update.invalid", scope: "decidim.meetings.admin")
54
- render action: "edit"
54
+ render action: "edit", status: :unprocessable_entity
55
55
  end
56
56
  end
57
57
  end
@@ -30,7 +30,7 @@ module Decidim
30
30
 
31
31
  on(:invalid) do
32
32
  flash.now[:alert] = I18n.t("invites.create.error", scope: "decidim.meetings.admin")
33
- render :index
33
+ render :index, status: :unprocessable_entity
34
34
  end
35
35
  end
36
36
  end
@@ -26,7 +26,7 @@ module Decidim
26
26
 
27
27
  on(:invalid) do
28
28
  flash.now[:alert] = I18n.t("meetings.close.invalid", scope: "decidim.meetings.admin")
29
- render action: "edit"
29
+ render action: "edit", status: :unprocessable_entity
30
30
  end
31
31
  end
32
32
  end
@@ -27,7 +27,7 @@ module Decidim
27
27
 
28
28
  on(:invalid) do
29
29
  flash.now[:alert] = I18n.t("meeting_copies.create.error", scope: "decidim.admin")
30
- render :new
30
+ render :new, status: :unprocessable_entity
31
31
  end
32
32
  end
33
33
  end
@@ -29,7 +29,7 @@ module Decidim
29
29
 
30
30
  on(:invalid) do
31
31
  flash.now[:alert] = I18n.t("meetings.create.invalid", scope: "decidim.meetings.admin")
32
- render action: "new"
32
+ render action: "new", status: :unprocessable_entity
33
33
  end
34
34
  end
35
35
  end
@@ -53,7 +53,7 @@ module Decidim
53
53
 
54
54
  on(:invalid) do
55
55
  flash.now[:alert] = I18n.t("meetings.update.invalid", scope: "decidim.meetings.admin")
56
- render action: "edit"
56
+ render action: "edit", status: :unprocessable_entity
57
57
  end
58
58
  end
59
59
  end
@@ -69,7 +69,7 @@ module Decidim
69
69
 
70
70
  on(:invalid) do
71
71
  flash.now[:alert] = I18n.t("meetings.publish.invalid", scope: "decidim.meetings.admin")
72
- render action: "index"
72
+ render action: "index", status: :unprocessable_entity
73
73
  end
74
74
  end
75
75
  end
@@ -85,7 +85,7 @@ module Decidim
85
85
 
86
86
  on(:invalid) do
87
87
  flash.now[:alert] = I18n.t("meetings.unpublish.invalid", scope: "decidim.meetings.admin")
88
- render action: "index"
88
+ render action: "index", status: :unprocessable_entity
89
89
  end
90
90
  end
91
91
  end
@@ -6,8 +6,8 @@ module Decidim
6
6
  class MeetingsPollController < Admin::ApplicationController
7
7
  include Decidim::TranslatableAttributes
8
8
 
9
- helper_method :questionnaire_for, :questionnaire, :blank_question, :blank_answer_option,
10
- :question_types, :update_url, :answer_options_url, :edit_questionnaire_title,
9
+ helper_method :questionnaire_for, :questionnaire, :blank_question, :blank_response_option,
10
+ :question_types, :update_url, :response_options_url, :edit_questionnaire_title,
11
11
  :meeting, :poll
12
12
 
13
13
  helper Decidim::Forms::Admin::ApplicationHelper
@@ -47,17 +47,17 @@ module Decidim
47
47
  on(:invalid) do
48
48
  # i18n-tasks-use t("decidim.forms.admin.questionnaires.update.invalid")
49
49
  flash.now[:alert] = I18n.t("update.invalid", scope: "decidim.meetings.admin.meetings_poll")
50
- render template: "decidim/meetings/admin/poll/edit"
50
+ render template: "decidim/meetings/admin/poll/edit", status: :unprocessable_entity
51
51
  end
52
52
  end
53
53
  end
54
54
 
55
- def answer_options
55
+ def response_options
56
56
  respond_to do |format|
57
57
  format.json do
58
58
  question_id = params["id"]
59
59
  question = Decidim::Meetings::Question.find_by(id: question_id)
60
- render json: question.answer_options.map { |answer_option| AnswerOptionPresenter.new(answer_option).as_json } if question.present?
60
+ render json: question.response_options.map { |response_option| ResponseOptionPresenter.new(response_option).as_json } if question.present?
61
61
  end
62
62
  end
63
63
  end
@@ -66,10 +66,10 @@ module Decidim
66
66
  poll
67
67
  end
68
68
 
69
- # Returns the url to get the answer options json (for the display conditions form)
69
+ # Returns the url to get the response options json (for the display conditions form)
70
70
  # for the question with id = params[:id]
71
- def answer_options_url(params)
72
- url_for([questionnaire.questionnaire_for, { action: :answer_options, format: :json, **params }])
71
+ def response_options_url(params)
72
+ url_for([questionnaire.questionnaire_for, { action: :response_options, format: :json, **params }])
73
73
  end
74
74
 
75
75
  # Implement this method in your controller to set the title
@@ -88,8 +88,8 @@ module Decidim
88
88
  @blank_question ||= Decidim::Meetings::Admin::QuestionForm.new
89
89
  end
90
90
 
91
- def blank_answer_option
92
- @blank_answer_option ||= Decidim::Meetings::Admin::AnswerOptionForm.new
91
+ def blank_response_option
92
+ @blank_response_option ||= Decidim::Meetings::Admin::ResponseOptionForm.new
93
93
  end
94
94
 
95
95
  def question_types
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module Admin
6
+ # This controller allows an admin to manage meeting registrations and
7
+ # attendances from a participatory space
8
+ class RegistrationsAttendeesController < Admin::ApplicationController
9
+ helper_method :registrations
10
+
11
+ before_action do
12
+ enforce_permission_to(:validate_registration_code, :meeting, meeting:)
13
+ end
14
+
15
+ def index
16
+ @validation_form = ValidateRegistrationCodeForm.new
17
+ end
18
+
19
+ def qr_mark_as_attendee
20
+ registration = registrations.find_by!(code: params[:id])
21
+
22
+ MarkAsAttendee.call(registration) do
23
+ on(:ok) do
24
+ flash[:notice] = I18n.t("registrations_attendees.mark_attendee.success", scope: "decidim.meetings.admin")
25
+ end
26
+
27
+ on(:invalid) do
28
+ flash[:alert] = I18n.t("registrations_attendees.mark_attendee.invalid", scope: "decidim.meetings.admin")
29
+ end
30
+
31
+ redirect_to action: "index"
32
+ end
33
+ end
34
+
35
+ def validate_registration_code
36
+ @validation_form = ValidateRegistrationCodeForm.from_params(params).with_context(current_organization: meeting.organization, meeting:)
37
+
38
+ ValidateRegistrationCode.call(@validation_form, meeting) do
39
+ on(:ok) do
40
+ flash[:notice] = I18n.t("registrations_attendees.validate_registration_code.success", scope: "decidim.meetings.admin")
41
+ redirect_to action: "index"
42
+ end
43
+
44
+ on(:invalid) do
45
+ flash.now[:alert] = I18n.t("registrations_attendees.validate_registration_code.invalid", scope: "decidim.meetings.admin")
46
+ render action: "index", status: :unprocessable_entity
47
+ end
48
+ end
49
+ end
50
+
51
+ def mark_as_attendee
52
+ registration = registrations.find(params[:id])
53
+
54
+ MarkAsAttendee.call(registration) do
55
+ on(:ok) do
56
+ flash[:notice] = I18n.t("registrations_attendees.mark_attendee.success", scope: "decidim.meetings.admin")
57
+ redirect_to action: "index"
58
+ end
59
+
60
+ on(:invalid) do
61
+ flash.now[:alert] = I18n.t("registrations_attendees.mark_attendee.invalid", scope: "decidim.meetings.admin")
62
+ render action: "index", status: :unprocessable_entity
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def meeting
70
+ @meeting ||= Meeting.where(component: current_component).find(params[:meeting_id])
71
+ end
72
+
73
+ def registrations
74
+ meeting.registrations
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -9,14 +9,12 @@ module Decidim
9
9
  enforce_permission_to(:update, :meeting, meeting:)
10
10
 
11
11
  @form = MeetingRegistrationsForm.from_model(meeting)
12
- @validation_form = ValidateRegistrationCodeForm.new
13
12
  end
14
13
 
15
14
  def update
16
15
  enforce_permission_to(:update, :meeting, meeting:)
17
16
 
18
17
  @form = MeetingRegistrationsForm.from_params(params).with_context(current_organization: meeting.organization, meeting:)
19
- @validation_form = ValidateRegistrationCodeForm.new
20
18
 
21
19
  UpdateRegistrations.call(@form, meeting) do
22
20
  on(:ok) do
@@ -26,7 +24,7 @@ module Decidim
26
24
 
27
25
  on(:invalid) do
28
26
  flash.now[:alert] = I18n.t("registrations.update.invalid", scope: "decidim.meetings.admin")
29
- render action: "edit"
27
+ render action: "edit", status: :unprocessable_entity
30
28
  end
31
29
  end
32
30
  end
@@ -41,25 +39,6 @@ module Decidim
41
39
  end
42
40
  end
43
41
 
44
- def validate_registration_code
45
- enforce_permission_to(:update, :meeting, meeting:)
46
-
47
- @form = MeetingRegistrationsForm.from_model(meeting)
48
- @validation_form = ValidateRegistrationCodeForm.from_params(params).with_context(current_organization: meeting.organization, meeting:)
49
-
50
- ValidateRegistrationCode.call(@validation_form, meeting) do
51
- on(:ok) do
52
- flash[:notice] = I18n.t("registrations.validate_registration_code.success", scope: "decidim.meetings.admin")
53
- render action: "edit"
54
- end
55
-
56
- on(:invalid) do
57
- flash.now[:alert] = I18n.t("registrations.validate_registration_code.invalid", scope: "decidim.meetings.admin")
58
- render action: "edit"
59
- end
60
- end
61
- end
62
-
63
42
  private
64
43
 
65
44
  def meeting
@@ -27,7 +27,7 @@ module Decidim
27
27
 
28
28
  on(:invalid) do
29
29
  flash.now[:alert] = I18n.t("meetings.close.invalid", scope: "decidim.meetings.admin")
30
- render action: "edit"
30
+ render action: "edit", status: :unprocessable_entity
31
31
  end
32
32
  end
33
33
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rqrcode"
4
+
3
5
  module Decidim
4
6
  module Meetings
5
7
  # Exposes the meeting resource so users can view them
@@ -16,7 +18,7 @@ module Decidim
16
18
  include Decidim::AttachmentsHelper
17
19
  include Decidim::SanitizeHelper
18
20
 
19
- helper_method :meetings, :meeting, :registration, :search, :tab_panel_items
21
+ helper_method :meetings, :meeting, :registration, :registration_qr_code_image, :search, :tab_panel_items
20
22
 
21
23
  before_action :add_additional_csp_directives, only: [:show]
22
24
 
@@ -39,7 +41,7 @@ module Decidim
39
41
 
40
42
  on(:invalid) do
41
43
  flash.now[:alert] = I18n.t("meetings.create.invalid", scope: "decidim.meetings")
42
- render action: "new"
44
+ render action: "new", status: :unprocessable_entity
43
45
  end
44
46
  end
45
47
  end
@@ -86,7 +88,7 @@ module Decidim
86
88
 
87
89
  on(:invalid) do
88
90
  flash.now[:alert] = I18n.t("meetings.update.invalid", scope: "decidim.meetings")
89
- render :edit
91
+ render :edit, status: :unprocessable_entity
90
92
  end
91
93
  end
92
94
  end
@@ -121,6 +123,12 @@ module Decidim
121
123
  @registration ||= meeting.registrations.find_by(user: current_user)
122
124
  end
123
125
 
126
+ def registration_qr_code_image
127
+ Base64.encode64(
128
+ RQRCode::QRCode.new(registration.validation_code_short_link.short_url).as_png(size: 500).to_s
129
+ ).gsub("\n", "")
130
+ end
131
+
124
132
  def search_collection
125
133
  Meeting
126
134
  .where(component: current_component)
@@ -151,14 +159,6 @@ module Decidim
151
159
  method: :cell,
152
160
  args: ["decidim/meetings/public_participants_list", meeting]
153
161
  },
154
- {
155
- enabled: !meeting.closed? && meeting.user_group_registrations.any?,
156
- id: "organizations",
157
- text: t("attending_organizations", scope: "decidim.meetings.public_participants_list"),
158
- icon: "community-line",
159
- method: :cell,
160
- args: ["decidim/meetings/attending_organizations_list", meeting]
161
- },
162
162
  {
163
163
  enabled: meeting.linked_resources(:proposals, "proposals_from_meeting").present?,
164
164
  id: "included_proposals",