decidim-meetings 0.30.5 → 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 (205) 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/meeting_cell.rb +6 -1
  11. data/app/cells/decidim/meetings/meeting_s_cell.rb +15 -0
  12. data/app/cells/decidim/meetings/question_responses/show.erb +7 -7
  13. data/app/cells/decidim/meetings/question_responses_cell.rb +28 -28
  14. data/app/commands/decidim/meetings/admin/copy_meeting.rb +5 -2
  15. data/app/commands/decidim/meetings/admin/create_agenda.rb +6 -2
  16. data/app/commands/decidim/meetings/admin/create_meeting.rb +7 -3
  17. data/app/commands/decidim/meetings/admin/mark_as_attendee.rb +44 -0
  18. data/app/commands/decidim/meetings/admin/publish_meeting.rb +2 -1
  19. data/app/commands/decidim/meetings/admin/update_agenda.rb +6 -2
  20. data/app/commands/decidim/meetings/admin/update_meeting.rb +15 -6
  21. data/app/commands/decidim/meetings/admin/update_questionnaire.rb +4 -4
  22. data/app/commands/decidim/meetings/admin/update_registrations.rb +19 -7
  23. data/app/commands/decidim/meetings/create_meeting.rb +2 -3
  24. data/app/commands/decidim/meetings/{create_answer.rb → create_response.rb} +11 -11
  25. data/app/commands/decidim/meetings/join_meeting.rb +4 -6
  26. data/app/commands/decidim/meetings/join_waitlist.rb +53 -0
  27. data/app/commands/decidim/meetings/leave_meeting.rb +14 -5
  28. data/app/commands/decidim/meetings/update_meeting.rb +1 -2
  29. data/app/controllers/concerns/decidim/meetings/admin/filterable.rb +1 -1
  30. data/app/controllers/decidim/meetings/admin/agenda_controller.rb +2 -2
  31. data/app/controllers/decidim/meetings/admin/invites_controller.rb +1 -1
  32. data/app/controllers/decidim/meetings/admin/meeting_closes_controller.rb +1 -1
  33. data/app/controllers/decidim/meetings/admin/meeting_copies_controller.rb +1 -1
  34. data/app/controllers/decidim/meetings/admin/meetings_controller.rb +4 -4
  35. data/app/controllers/decidim/meetings/admin/meetings_poll_controller.rb +10 -10
  36. data/app/controllers/decidim/meetings/admin/registrations_attendees_controller.rb +79 -0
  37. data/app/controllers/decidim/meetings/admin/registrations_controller.rb +1 -22
  38. data/app/controllers/decidim/meetings/meeting_closes_controller.rb +1 -1
  39. data/app/controllers/decidim/meetings/meetings_controller.rb +11 -44
  40. data/app/controllers/decidim/meetings/polls/{answers_controller.rb → responses_controller.rb} +7 -7
  41. data/app/controllers/decidim/meetings/registrations_controller.rb +39 -12
  42. data/app/events/decidim/meetings/registration_marked_as_attendee_event.rb +9 -0
  43. data/app/events/decidim/meetings/upcoming_meeting_event.rb +41 -0
  44. data/app/events/decidim/meetings/update_meeting_event.rb +25 -0
  45. data/app/forms/decidim/meetings/admin/close_meeting_form.rb +2 -1
  46. data/app/forms/decidim/meetings/admin/meeting_agenda_items_form.rb +4 -0
  47. data/app/forms/decidim/meetings/admin/meeting_form.rb +28 -3
  48. data/app/forms/decidim/meetings/admin/question_form.rb +3 -3
  49. data/app/forms/decidim/meetings/admin/{answer_option_form.rb → response_option_form.rb} +3 -3
  50. data/app/forms/decidim/meetings/admin/validate_registration_code_form.rb +1 -1
  51. data/app/forms/decidim/meetings/base_meeting_form.rb +0 -2
  52. data/app/forms/decidim/meetings/close_meeting_form.rb +2 -1
  53. data/app/forms/decidim/meetings/join_meeting_form.rb +0 -1
  54. data/app/forms/decidim/meetings/meeting_form.rb +3 -2
  55. data/app/forms/decidim/meetings/response_choice_form.rb +14 -0
  56. data/app/forms/decidim/meetings/{answer_form.rb → response_form.rb} +7 -7
  57. data/app/helpers/decidim/meetings/application_helper.rb +0 -1
  58. data/app/helpers/decidim/meetings/meetings_helper.rb +14 -5
  59. data/app/jobs/decidim/meetings/promote_from_waitlist_job.rb +63 -0
  60. data/app/jobs/decidim/meetings/upcoming_meeting_notification_job.rb +1 -1
  61. data/app/mailers/decidim/meetings/registration_mailer.rb +13 -0
  62. data/app/models/decidim/meetings/agenda_item.rb +5 -0
  63. data/app/models/decidim/meetings/meeting.rb +15 -12
  64. data/app/models/decidim/meetings/question.rb +12 -12
  65. data/app/models/decidim/meetings/questionnaire.rb +1 -1
  66. data/app/models/decidim/meetings/registration.rb +19 -7
  67. data/app/models/decidim/meetings/{answer.rb → response.rb} +6 -6
  68. data/app/models/decidim/meetings/response_choice.rb +15 -0
  69. data/app/models/decidim/meetings/{answer_option.rb → response_option.rb} +5 -5
  70. data/app/packs/src/decidim/meetings/admin/destroy_meeting_alert.js +1 -1
  71. data/app/packs/src/decidim/meetings/admin/meetings_components_form.js +1 -8
  72. data/app/packs/src/decidim/meetings/admin/meetings_form.js +1 -1
  73. data/app/packs/src/decidim/meetings/admin/registrations_form.js +1 -1
  74. data/app/packs/src/decidim/meetings/admin/registrations_invite_form.js +1 -1
  75. data/app/packs/src/decidim/meetings/meetings_form.js +1 -1
  76. data/app/packs/src/decidim/meetings/meetings_polls.js +1 -1
  77. data/app/packs/src/decidim/meetings/poll.component.js +5 -5
  78. data/app/packs/stylesheets/decidim/meetings/_item.scss +5 -1
  79. data/app/packs/stylesheets/decidim/meetings/meetings.scss +4 -4
  80. data/app/permissions/decidim/meetings/admin/agenda_permissions.rb +34 -0
  81. data/app/permissions/decidim/meetings/admin/meeting_permissions.rb +44 -0
  82. data/app/permissions/decidim/meetings/admin/permissions.rb +5 -66
  83. data/app/permissions/decidim/meetings/admin/questionnaire_permissions.rb +30 -0
  84. data/app/permissions/decidim/meetings/meeting_permissions.rb +90 -0
  85. data/app/permissions/decidim/meetings/permissions.rb +9 -105
  86. data/app/presenters/decidim/meetings/admin_log/value_types/meeting_title_description_presenter.rb +1 -1
  87. data/app/presenters/decidim/meetings/agenda_item_presenter.rb +29 -0
  88. data/app/presenters/decidim/meetings/meeting_presenter.rb +12 -15
  89. data/app/presenters/decidim/meetings/registration_presenter.rb +24 -0
  90. data/app/queries/decidim/meetings/{questionnaire_user_answers.rb → questionnaire_user_responses.rb} +5 -5
  91. data/app/serializers/decidim/meetings/registration_serializer.rb +5 -6
  92. data/app/services/decidim/meetings/diff_renderer.rb +0 -1
  93. data/app/views/decidim/meetings/_calendar_modal.html.erb +1 -0
  94. data/app/views/decidim/meetings/admin/agenda/_agenda_item_fields.html.erb +1 -1
  95. data/app/views/decidim/meetings/admin/invites/_form.html.erb +1 -1
  96. data/app/views/decidim/meetings/admin/meeting_closes/_form.html.erb +1 -1
  97. data/app/views/decidim/meetings/admin/meetings/_form.html.erb +3 -2
  98. data/app/views/decidim/meetings/admin/meetings/_linked_spaces.html.erb +1 -1
  99. data/app/views/decidim/meetings/admin/meetings/_meeting-tr.html.erb +9 -14
  100. data/app/views/decidim/meetings/admin/meetings/_meeting_actions.html.erb +200 -69
  101. data/app/views/decidim/meetings/admin/meetings/_meetings-thead.html.erb +0 -3
  102. data/app/views/decidim/meetings/admin/meetings/_reminders.html.erb +19 -0
  103. data/app/views/decidim/meetings/admin/meetings/_services.html.erb +1 -1
  104. data/app/views/decidim/meetings/admin/meetings/index.html.erb +7 -5
  105. data/app/views/decidim/meetings/admin/meetings/manage_trash.html.erb +2 -1
  106. data/app/views/decidim/meetings/admin/poll/_form.html.erb +9 -9
  107. data/app/views/decidim/meetings/admin/poll/_question.html.erb +19 -9
  108. data/app/views/decidim/meetings/admin/poll/_response_option.html.erb +35 -0
  109. data/app/views/decidim/meetings/admin/poll/_response_option_template.html.erb +7 -0
  110. data/app/views/decidim/meetings/admin/poll/edit.html.erb +3 -3
  111. data/app/views/decidim/meetings/admin/registration_form/edit_questions.html.erb +5 -5
  112. data/app/views/decidim/meetings/admin/registrations/edit.html.erb +19 -39
  113. data/app/views/decidim/meetings/admin/registrations_attendees/index.html.erb +126 -0
  114. data/app/views/decidim/meetings/layouts/live_event.html.erb +1 -1
  115. data/app/views/decidim/meetings/meeting_closes/_form.html.erb +2 -2
  116. data/app/views/decidim/meetings/meetings/_form.html.erb +2 -11
  117. data/app/views/decidim/meetings/meetings/_meeting.html.erb +2 -2
  118. data/app/views/decidim/meetings/meetings/_meeting_actions.html.erb +3 -3
  119. data/app/views/decidim/meetings/meetings/_meeting_agenda.html.erb +2 -2
  120. data/app/views/decidim/meetings/meetings/_meeting_aside.html.erb +11 -10
  121. data/app/views/decidim/meetings/meetings/_meeting_poll_actions.html.erb +3 -3
  122. data/app/views/decidim/meetings/meetings/_registration_code_modal.html.erb +16 -0
  123. data/app/views/decidim/meetings/polls/questions/_index_admin.html.erb +1 -1
  124. data/app/views/decidim/meetings/polls/questions/_published_question.html.erb +5 -5
  125. data/app/views/decidim/meetings/polls/responses/_multiple_option.html.erb +13 -0
  126. data/app/views/decidim/meetings/polls/responses/_single_option.html.erb +13 -0
  127. data/app/views/decidim/meetings/polls/{answers → responses}/admin.html.erb +4 -4
  128. data/app/views/decidim/meetings/polls/{answers → responses}/index.html.erb +4 -4
  129. data/app/views/decidim/meetings/registration_mailer/confirmation.html.erb +6 -1
  130. data/config/assets.rb +2 -2
  131. data/config/locales/ar.yml +1 -26
  132. data/config/locales/bg.yml +2 -32
  133. data/config/locales/ca-IT.yml +87 -44
  134. data/config/locales/ca.yml +87 -44
  135. data/config/locales/cs.yml +71 -46
  136. data/config/locales/de.yml +89 -46
  137. data/config/locales/el.yml +1 -25
  138. data/config/locales/en.yml +89 -46
  139. data/config/locales/es-MX.yml +87 -44
  140. data/config/locales/es-PY.yml +87 -44
  141. data/config/locales/es.yml +87 -44
  142. data/config/locales/eu.yml +114 -71
  143. data/config/locales/fi-plain.yml +86 -43
  144. data/config/locales/fi.yml +85 -42
  145. data/config/locales/fr-CA.yml +79 -41
  146. data/config/locales/fr.yml +79 -41
  147. data/config/locales/ga-IE.yml +1 -8
  148. data/config/locales/gl.yml +1 -19
  149. data/config/locales/hu.yml +1 -23
  150. data/config/locales/id-ID.yml +0 -16
  151. data/config/locales/is-IS.yml +0 -10
  152. data/config/locales/it.yml +1 -38
  153. data/config/locales/ja.yml +88 -45
  154. data/config/locales/lb.yml +1 -16
  155. data/config/locales/lt.yml +1 -28
  156. data/config/locales/lv.yml +0 -16
  157. data/config/locales/nl.yml +1 -26
  158. data/config/locales/no.yml +1 -29
  159. data/config/locales/pl.yml +2 -32
  160. data/config/locales/pt-BR.yml +13 -205
  161. data/config/locales/pt.yml +1 -29
  162. data/config/locales/ro-RO.yml +56 -32
  163. data/config/locales/ru.yml +0 -16
  164. data/config/locales/sk.yml +0 -16
  165. data/config/locales/sl.yml +0 -4
  166. data/config/locales/sv.yml +89 -46
  167. data/config/locales/tr-TR.yml +0 -20
  168. data/config/locales/uk.yml +0 -12
  169. data/config/locales/zh-CN.yml +0 -19
  170. data/config/locales/zh-TW.yml +1 -26
  171. data/db/migrate/20181107175558_add_questionnaire_to_existing_meetings.rb +8 -2
  172. data/db/migrate/20200827153856_add_commentable_counter_cache_to_meetings.rb +8 -2
  173. data/db/migrate/20201016065302_fix_meetings_registration_terms.rb +8 -2
  174. data/db/migrate/20210310120731_add_followable_counter_cache_to_meetings.rb +8 -2
  175. data/db/migrate/20250317103343_rename_answer_to_response_in_decidim_meetings.rb +18 -0
  176. data/db/migrate/20250403094034_add_reminder_customization_to_decidim_meetings.rb +9 -0
  177. data/db/migrate/20250408071941_add_status_to_registrations_to_decidim_meetings_registrations.rb +8 -0
  178. data/lib/decidim/api/agenda_item_type.rb +6 -2
  179. data/lib/decidim/api/agenda_type.rb +6 -2
  180. data/lib/decidim/api/linked_resources_interface.rb +1 -1
  181. data/lib/decidim/api/meeting_type.rb +20 -10
  182. data/lib/decidim/api/service_type.rb +3 -0
  183. data/lib/decidim/meetings/admin_engine.rb +9 -1
  184. data/lib/decidim/meetings/component.rb +29 -20
  185. data/lib/decidim/meetings/engine.rb +6 -21
  186. data/lib/decidim/meetings/meeting_serializer.rb +1 -2
  187. data/lib/decidim/meetings/schema_org_event_meeting_serializer.rb +0 -10
  188. data/lib/decidim/meetings/seeds.rb +4 -13
  189. data/lib/decidim/meetings/test/factories.rb +10 -16
  190. data/lib/decidim/meetings/user_responses_serializer.rb +47 -0
  191. data/lib/decidim/meetings/version.rb +1 -1
  192. data/lib/decidim/meetings.rb +7 -9
  193. metadata +50 -35
  194. data/app/cells/decidim/meetings/attending_organizations_list_cell.rb +0 -32
  195. data/app/forms/decidim/meetings/answer_choice_form.rb +0 -14
  196. data/app/models/decidim/meetings/answer_choice.rb +0 -15
  197. data/app/queries/decidim/meetings/metrics/meeting_followers_metric_measure.rb +0 -31
  198. data/app/queries/decidim/meetings/metrics/meetings_metric_manage.rb +0 -48
  199. data/app/views/decidim/meetings/admin/poll/_answer_option.html.erb +0 -35
  200. data/app/views/decidim/meetings/admin/poll/_answer_option_template.html.erb +0 -7
  201. data/app/views/decidim/meetings/polls/answers/_multiple_option.html.erb +0 -13
  202. data/app/views/decidim/meetings/polls/answers/_single_option.html.erb +0 -13
  203. data/lib/decidim/meetings/download_your_data_user_answers_serializer.rb +0 -39
  204. data/lib/decidim/meetings/user_answers_serializer.rb +0 -47
  205. /data/app/views/decidim/meetings/polls/{answers → responses}/create.js.erb +0 -0
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ # Background job that promotes users from the waitlist to registered status,
6
+ # depending on available slots in the meeting.
7
+ class PromoteFromWaitlistJob < ApplicationJob
8
+ queue_as :default
9
+
10
+ def perform(meeting_id)
11
+ @meeting = Decidim::Meetings::Meeting.find_by(id: meeting_id)
12
+ return unless @meeting
13
+
14
+ promote_users_from_waitlist
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :meeting
20
+
21
+ def promote_users_from_waitlist
22
+ meeting.with_lock do
23
+ loop do
24
+ break unless meeting.remaining_slots.positive?
25
+
26
+ next_in_waitlist = meeting.registrations.on_waiting_list.first
27
+ break unless next_in_waitlist
28
+
29
+ promote(next_in_waitlist)
30
+ end
31
+ end
32
+ end
33
+
34
+ def promote(registration)
35
+ return unless registration.waiting_list?
36
+
37
+ registration.update!(status: :registered)
38
+ notify_user(registration)
39
+ end
40
+
41
+ def notify_user(registration)
42
+ send_email_confirmation(registration)
43
+ send_internal_notification(registration)
44
+ end
45
+
46
+ def send_email_confirmation(registration)
47
+ Decidim::Meetings::RegistrationMailer.confirmation(registration.user, meeting, registration).deliver_later
48
+ end
49
+
50
+ def send_internal_notification(registration)
51
+ Decidim::EventsManager.publish(
52
+ event: "decidim.events.meetings.meeting_registration_confirmed",
53
+ event_class: Decidim::Meetings::MeetingRegistrationNotificationEvent,
54
+ resource: meeting,
55
+ affected_users: [registration.user],
56
+ extra: {
57
+ registration_code: registration.code
58
+ }
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
@@ -14,7 +14,7 @@ module Decidim
14
14
  end
15
15
 
16
16
  def self.generate_checksum(meeting)
17
- Digest::MD5.hexdigest("#{meeting.id}-#{meeting.start_time}")
17
+ Digest::SHA256.hexdigest("#{meeting.id}-#{meeting.start_time}")
18
18
  end
19
19
 
20
20
  private
@@ -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
  # A custom mailer for sending notifications to users when
@@ -20,8 +22,11 @@ module Decidim
20
22
  @registration = registration
21
23
  @organization = @meeting.organization
22
24
  @locator = Decidim::ResourceLocatorPresenter.new(@meeting)
25
+ @registration_code_enabled = meeting.component.settings.registration_code_enabled
26
+ @qr_filename = "qr-#{@registration.code.parameterize}.png"
23
27
 
24
28
  add_calendar_attachment
29
+ add_qr_code_attachment if @registration_code_enabled
25
30
 
26
31
  subject = I18n.t("confirmation.subject", scope: "decidim.meetings.mailer.registration_mailer")
27
32
  mail(to: user.email, subject:)
@@ -30,12 +35,20 @@ module Decidim
30
35
 
31
36
  private
32
37
 
38
+ def qr_code
39
+ @qr_code ||= RQRCode::QRCode.new(@registration.validation_code_short_link.short_url)
40
+ end
41
+
33
42
  def add_calendar_attachment
34
43
  calendar = Icalendar::Calendar.new
35
44
  calendar.add_event(Calendar::MeetingToEvent.new(@meeting).event)
36
45
 
37
46
  attachments["meeting-calendar-info.ics"] = calendar.to_ical
38
47
  end
48
+
49
+ def add_qr_code_attachment
50
+ attachments[@qr_filename] = qr_code.as_png(size: 500).to_blob
51
+ end
39
52
  end
40
53
  end
41
54
  end
@@ -33,6 +33,11 @@ module Decidim
33
33
  def self.log_presenter_class_for(_log)
34
34
  Decidim::Meetings::AdminLog::AgendaItemPresenter
35
35
  end
36
+
37
+ # Returns the presenter for this model
38
+ def presenter
39
+ Decidim::Meetings::AgendaItemPresenter.new(self)
40
+ end
36
41
  end
37
42
  end
38
43
  end
@@ -51,10 +51,10 @@ module Decidim
51
51
  has_many :meeting_links, dependent: :destroy, class_name: "Decidim::Meetings::MeetingLink", foreign_key: "decidim_meeting_id"
52
52
  has_many :components, through: :meeting_links, class_name: "Decidim::Component", foreign_key: "decidim_component_id"
53
53
 
54
- enum iframe_access_level: [:all, :signed_in, :registered], _prefix: true
55
- enum iframe_embed_type: [:none, :embed_in_meeting_page, :open_in_live_event_page, :open_in_new_tab], _prefix: true
56
- enum type_of_meeting: TYPE_OF_MEETING
57
- enum registration_type: REGISTRATION_TYPES, _scopes: false
54
+ enum :iframe_access_level, [:all, :signed_in, :registered], prefix: true
55
+ enum :iframe_embed_type, [:none, :embed_in_meeting_page, :open_in_live_event_page, :open_in_new_tab], prefix: true
56
+ enum :type_of_meeting, TYPE_OF_MEETING
57
+ enum :registration_type, REGISTRATION_TYPES, scopes: false
58
58
 
59
59
  component_manifest_name "meetings"
60
60
 
@@ -165,11 +165,6 @@ module Decidim
165
165
  iframe_embed_types.except(:open_in_live_event_page)
166
166
  end
167
167
 
168
- # Return registrations of a particular meeting made by users representing a group
169
- def user_group_registrations
170
- registrations.where.not(decidim_user_group_id: nil)
171
- end
172
-
173
168
  # Returns the presenter for this author, to be used in the views.
174
169
  # Required by ActsAsAuthor.
175
170
  def presenter
@@ -205,20 +200,28 @@ module Decidim
205
200
  false
206
201
  end
207
202
 
203
+ def waitlist_enabled?
204
+ Decidim::Meetings.waiting_list_enabled
205
+ end
206
+
208
207
  def has_available_slots?
209
208
  return true if available_slots.zero?
210
209
 
211
- (available_slots - reserved_slots) > registrations.count
210
+ (available_slots - reserved_slots) > registrations.registered.count
212
211
  end
213
212
 
214
213
  def remaining_slots
215
- available_slots - reserved_slots - registrations.count
214
+ available_slots - reserved_slots - registrations.registered.count
216
215
  end
217
216
 
218
217
  def has_registration_for?(user)
219
218
  registrations.where(user:).any?
220
219
  end
221
220
 
221
+ def pending_location?
222
+ !online? && location.except("machine_translations").values.all?(&:blank?)
223
+ end
224
+
222
225
  def maps_enabled?
223
226
  component.settings.maps_enabled?
224
227
  end
@@ -315,7 +318,7 @@ module Decidim
315
318
  end
316
319
 
317
320
  def authored_proposals
318
- return [] unless Decidim::Meetings.enable_proposal_linking
321
+ return [] unless Decidim.module_installed?(:proposals)
319
322
 
320
323
  Decidim::Proposals::Proposal
321
324
  .joins(:coauthorships)
@@ -9,18 +9,18 @@ module Decidim
9
9
  QUESTION_TYPES = %w(single_option multiple_option).freeze
10
10
 
11
11
  translatable_fields :body
12
- enum status: { unpublished: 0, published: 1, closed: 2 }
12
+ enum :status, { unpublished: 0, published: 1, closed: 2 }
13
13
 
14
14
  belongs_to :questionnaire, class_name: "Decidim::Meetings::Questionnaire", foreign_key: "decidim_questionnaire_id"
15
15
 
16
- has_many :answers,
17
- class_name: "Decidim::Meetings::Answer",
16
+ has_many :responses,
17
+ class_name: "Decidim::Meetings::Response",
18
18
  foreign_key: "decidim_question_id",
19
19
  dependent: :destroy,
20
20
  inverse_of: :question
21
21
 
22
- has_many :answer_options,
23
- class_name: "Decidim::Meetings::AnswerOption",
22
+ has_many :response_options,
23
+ class_name: "Decidim::Meetings::ResponseOption",
24
24
  foreign_key: "decidim_question_id",
25
25
  dependent: :destroy,
26
26
  inverse_of: :question
@@ -28,27 +28,27 @@ module Decidim
28
28
  validates :question_type, inclusion: { in: QUESTION_TYPES }
29
29
 
30
30
  scope :visible, -> { where(status: [:published, :closed]) }
31
- scope :unanswered, -> { where.missing(:answers) }
31
+ scope :unanswered, -> { where.missing(:responses) }
32
32
 
33
33
  def multiple_choice?
34
34
  %w(single_option multiple_option).include?(question_type)
35
35
  end
36
36
 
37
- # Public: returns whether the questionnaire is answered by the user or not.
38
- def answered_by?(user)
39
- questionnaire.answers.where({ user:, question: self }).any? if questionnaire.questions.present? && user.present?
37
+ # Public: returns whether the questionnaire is responded by the user or not.
38
+ def responded_by?(user)
39
+ questionnaire.responses.where({ user:, question: self }).any? if questionnaire.questions.present? && user.present?
40
40
  end
41
41
 
42
42
  def number_of_options
43
- answer_options.size
43
+ response_options.size
44
44
  end
45
45
 
46
46
  def translated_body
47
47
  Decidim::Forms::QuestionPresenter.new(self).translated_body
48
48
  end
49
49
 
50
- def answers_count
51
- questionnaire.answers.where(question: self).count
50
+ def responses_count
51
+ questionnaire.responses.where(question: self).count
52
52
  end
53
53
  end
54
54
  end
@@ -9,7 +9,7 @@ module Decidim
9
9
  belongs_to :questionnaire_for, polymorphic: true
10
10
 
11
11
  has_many :questions, -> { order(:position) }, class_name: "Question", foreign_key: "decidim_questionnaire_id", dependent: :destroy
12
- has_many :answers, class_name: "Answer", foreign_key: "decidim_questionnaire_id", dependent: :destroy
12
+ has_many :responses, class_name: "Response", foreign_key: "decidim_questionnaire_id", dependent: :destroy
13
13
 
14
14
  def all_questions_unpublished?
15
15
  questions.all?(&:unpublished?)
@@ -8,7 +8,6 @@ module Decidim
8
8
 
9
9
  belongs_to :meeting, foreign_key: "decidim_meeting_id", class_name: "Decidim::Meetings::Meeting"
10
10
  belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User"
11
- belongs_to :user_group, foreign_key: "decidim_user_group_id", class_name: "Decidim::UserGroup", optional: true
12
11
 
13
12
  validates :user, uniqueness: { scope: :meeting }
14
13
  validates :code, uniqueness: { allow_blank: true, scope: :meeting }
@@ -16,7 +15,12 @@ module Decidim
16
15
 
17
16
  before_validation :generate_code, on: :create
18
17
 
19
- scope :public_participant, -> { where(decidim_user_group_id: nil, public_participation: true) }
18
+ enum :status, { registered: "registered", waiting_list: "waiting_list" }
19
+
20
+ scope :on_waiting_list, -> { waiting_list.order(:created_at) }
21
+ scope :public_participant, -> { where(public_participation: true) }
22
+
23
+ delegate :component, :organization, to: :meeting
20
24
 
21
25
  def self.user_collection(user)
22
26
  where(decidim_user_id: user.id)
@@ -26,11 +30,6 @@ module Decidim
26
30
  Decidim::Meetings::DownloadYourDataRegistrationSerializer
27
31
  end
28
32
 
29
- # Pluck all Decidim::UserGroup ID's
30
- def self.user_group_ids
31
- pluck(:decidim_user_group_id)
32
- end
33
-
34
33
  # Public: Checks if the registration has been validated.
35
34
  #
36
35
  # Returns Boolean.
@@ -38,6 +37,19 @@ module Decidim
38
37
  validated_at?
39
38
  end
40
39
 
40
+ def presenter
41
+ Decidim::Meetings::RegistrationPresenter.new(self)
42
+ end
43
+
44
+ def validation_code_short_link
45
+ Decidim::ShortLink.to(
46
+ self,
47
+ meeting.component.mounted_admin_engine,
48
+ route_name: :qr_mark_as_attendee_meeting_registrations_attendee,
49
+ params: { meeting_id: meeting.id, id: code }
50
+ )
51
+ end
52
+
41
53
  private
42
54
 
43
55
  def generate_code
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Decidim
4
4
  module Meetings
5
- # The data store for an Answer in the Decidim::Meetings
6
- class Answer < Meetings::ApplicationRecord
5
+ # The data store for an Response in the Decidim::Meetings
6
+ class Response < Meetings::ApplicationRecord
7
7
  include Decidim::DownloadYourData
8
8
 
9
9
  belongs_to :user, class_name: "Decidim::User", foreign_key: "decidim_user_id", optional: true
@@ -11,10 +11,10 @@ module Decidim
11
11
  belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id"
12
12
 
13
13
  has_many :choices,
14
- class_name: "AnswerChoice",
15
- foreign_key: "decidim_answer_id",
14
+ class_name: "ResponseChoice",
15
+ foreign_key: "decidim_response_id",
16
16
  dependent: :destroy,
17
- inverse_of: :answer
17
+ inverse_of: :response
18
18
 
19
19
  validate :user_questionnaire_same_organization
20
20
  validate :question_belongs_to_questionnaire
@@ -24,7 +24,7 @@ module Decidim
24
24
  end
25
25
 
26
26
  def self.export_serializer
27
- Decidim::Meetings::DownloadYourDataUserAnswersSerializer
27
+ Decidim::Forms::DownloadYourDataUserResponsesSerializer
28
28
  end
29
29
 
30
30
  def organization
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ class ResponseChoice < Meetings::ApplicationRecord
6
+ belongs_to :response,
7
+ class_name: "Decidim::Meetings::Response",
8
+ foreign_key: "decidim_response_id"
9
+
10
+ belongs_to :response_option,
11
+ class_name: "Decidim::Meetings::ResponseOption",
12
+ foreign_key: "decidim_response_option_id"
13
+ end
14
+ end
15
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module Meetings
5
- class AnswerOption < Meetings::ApplicationRecord
5
+ class ResponseOption < Meetings::ApplicationRecord
6
6
  include Decidim::TranslatableResource
7
7
 
8
8
  default_scope { order(arel_table[:id].asc) }
@@ -11,13 +11,13 @@ module Decidim
11
11
 
12
12
  belongs_to :question, class_name: "Decidim::Meetings::Question", foreign_key: "decidim_question_id"
13
13
  has_many :choices,
14
- class_name: "AnswerChoice",
15
- foreign_key: "decidim_answer_option_id",
14
+ class_name: "ResponseChoice",
15
+ foreign_key: "decidim_response_option_id",
16
16
  dependent: :destroy,
17
- inverse_of: :answer_option
17
+ inverse_of: :response_option
18
18
 
19
19
  def translated_body
20
- Decidim::Forms::AnswerOptionPresenter.new(self).translated_body
20
+ Decidim::Forms::ResponseOptionPresenter.new(self).translated_body
21
21
  end
22
22
  end
23
23
  end
@@ -2,7 +2,7 @@ const removeNewlineAdjacentSpaces = (text) => {
2
2
  return text.replace(/\n\s/g, "\n");
3
3
  }
4
4
 
5
- $(() => {
5
+ document.addEventListener("turbo:load", () => {
6
6
  const $confirmButton = $(".destroy-meeting-alert");
7
7
 
8
8
  if ($confirmButton.length > 0) {
@@ -1,5 +1,4 @@
1
1
  import TomSelect from "tom-select/dist/cjs/tom-select.popular";
2
- import createTooltip from "src/decidim/tooltips"
3
2
 
4
3
  /**
5
4
  * This module manages the Linked Spaces section from the
@@ -40,12 +39,6 @@ const handleAddButton = () => {
40
39
 
41
40
  body.appendChild(clone);
42
41
 
43
- const tooltips = body.querySelectorAll("[data-tooltip]")
44
-
45
- if (tooltips.length) {
46
- createTooltip(tooltips[tooltips.length - 1]);
47
- }
48
-
49
42
  select.value = "";
50
43
  table.classList.remove("hidden");
51
44
  };
@@ -65,7 +58,7 @@ const setupTomSelect = () => {
65
58
  return null;
66
59
  }
67
60
 
68
- document.addEventListener("DOMContentLoaded", () => {
61
+ document.addEventListener("turbo:load", () => {
69
62
  document.querySelectorAll(".js-remove-component").forEach(handleRemoveButton);
70
63
  const addButton = document.querySelector(".js-add-component")
71
64
 
@@ -5,7 +5,7 @@ import createDynamicFields from "src/decidim/admin/dynamic_fields.component"
5
5
  import createFieldDependentInputs from "src/decidim/admin/field_dependent_inputs.component"
6
6
  import attachGeocoding from "src/decidim/geocoding/attach_input"
7
7
 
8
- $(() => {
8
+ document.addEventListener("turbo:load", () => {
9
9
  const wrapperSelector = ".meeting-services";
10
10
  const fieldSelector = ".meeting-service";
11
11
 
@@ -1,4 +1,4 @@
1
- $(() => {
1
+ document.addEventListener("turbo:load", () => {
2
2
  const $form = $(".edit_meeting_registrations");
3
3
 
4
4
  if ($form.length > 0) {
@@ -1,6 +1,6 @@
1
1
  import createFieldDependentInputs from "src/decidim/admin/field_dependent_inputs.component"
2
2
 
3
- $(() => {
3
+ document.addEventListener("turbo:load", () => {
4
4
  const $attendeeType = $('[name="meeting_registration_invite[existing_user]"');
5
5
 
6
6
  createFieldDependentInputs({
@@ -1,7 +1,7 @@
1
1
  import attachGeocoding from "src/decidim/geocoding/attach_input"
2
2
  import createFieldDependentInputs from "src/decidim/admin/field_dependent_inputs.component"
3
3
 
4
- $(() => {
4
+ document.addEventListener("turbo:load", () => {
5
5
  // Adds the latitude/longitude inputs after the geocoding is done
6
6
  const $meetingAddress = $("#meeting_address");
7
7
  if ($meetingAddress.length > 0) {
@@ -1,7 +1,7 @@
1
1
  import MeetingsPollComponent from "src/decidim/meetings/poll.component"
2
2
  const OPEN_CLASS = "is-open";
3
3
 
4
- $(() => {
4
+ document.addEventListener("turbo:load", () => {
5
5
  // Mount polls component for users
6
6
  const $container = $("[data-decidim-meetings-poll]");
7
7
  const $counter = $("#visible-questions-count");
@@ -28,7 +28,7 @@ export default class PollComponent {
28
28
  this.mounted = false;
29
29
  this.questions = {};
30
30
  this.questionsStatuses = {};
31
- this.answersStatuses = {};
31
+ this.responsesStatuses = {};
32
32
  }
33
33
 
34
34
  /**
@@ -102,7 +102,7 @@ export default class PollComponent {
102
102
 
103
103
  this.questionsStatuses[questionId] = $el.data("status");
104
104
  if (elForm.length > 0) {
105
- this.answersStatuses[questionId] = Object.fromEntries(new FormData(elForm[0]));
105
+ this.responsesStatuses[questionId] = Object.fromEntries(new FormData(elForm[0]));
106
106
  }
107
107
  if ($el[0].open === true) {
108
108
  this.questions[questionId] = OPEN;
@@ -137,7 +137,7 @@ export default class PollComponent {
137
137
  // Current question state
138
138
  const state = this.questions[questionId];
139
139
  const questionStatus = this.questionsStatuses[questionId];
140
- const answersStatuses = this.answersStatuses[questionId];
140
+ const responsesStatuses = this.responsesStatuses[questionId];
141
141
 
142
142
  // New questions have a special class
143
143
  if (!state) {
@@ -151,8 +151,8 @@ export default class PollComponent {
151
151
  document.getElementById(`closed-announcement-${questionId}`).hidden = false;
152
152
  }
153
153
 
154
- if (answersStatuses) {
155
- for (const [key, value] of Object.entries(answersStatuses)) {
154
+ if (responsesStatuses) {
155
+ for (const [key, value] of Object.entries(responsesStatuses)) {
156
156
  if (key.includes("[choices]")) {
157
157
  $el.find(`[name='${key}'][value='${value}']`).prop("checked", true);
158
158
  }
@@ -148,6 +148,10 @@
148
148
  &__list {
149
149
  @apply ml-4 list-disc text-gray-2 text-sm;
150
150
  }
151
+
152
+ &__item {
153
+ @apply items-center gap-2;
154
+ }
151
155
  }
152
156
 
153
157
  &-actions {
@@ -228,7 +232,7 @@
228
232
  }
229
233
  }
230
234
 
231
- &__answer {
235
+ &__response {
232
236
  label {
233
237
  @apply block p-4 ring-4 ring-background rounded transition;
234
238
 
@@ -1,4 +1,4 @@
1
- @import "stylesheets/decidim/meetings/list.scss";
2
- @import "stylesheets/decidim/meetings/item.scss";
3
- @import "stylesheets/decidim/meetings/calendar.scss";
4
- @import "stylesheets/decidim/meetings/live_event.scss";
1
+ @use "stylesheets/decidim/meetings/list";
2
+ @use "stylesheets/decidim/meetings/item";
3
+ @use "stylesheets/decidim/meetings/calendar";
4
+ @use "stylesheets/decidim/meetings/live_event";
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module Admin
6
+ class AgendaPermissions < Decidim::DefaultPermissions
7
+ def permissions
8
+ return permission_action unless user
9
+ return permission_action unless permission_action.scope == :admin
10
+ return permission_action unless subject == :agenda
11
+
12
+ case permission_action.action
13
+ when :create
14
+ toggle_allow(meeting.present?)
15
+ when :update
16
+ toggle_allow(agenda.present? && meeting.present?)
17
+ end
18
+
19
+ permission_action
20
+ end
21
+
22
+ private
23
+
24
+ def agenda
25
+ @agenda ||= context.fetch(:agenda, nil)
26
+ end
27
+
28
+ def meeting
29
+ @meeting ||= context.fetch(:meeting, nil)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module Admin
6
+ class MeetingPermissions < Decidim::DefaultPermissions
7
+ def permissions # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
8
+ return permission_action unless user
9
+ return permission_action unless permission_action.scope == :admin
10
+ return permission_action unless subject == :meeting
11
+
12
+ if meeting && !meeting.official?
13
+ disallow!
14
+
15
+ return permission_action
16
+ end
17
+
18
+ case permission_action.action
19
+ when :close, :copy, :export_registrations, :update, :read_invites
20
+ toggle_allow(meeting.present?)
21
+ when :invite_attendee
22
+ toggle_allow(meeting.present? && meeting.registrations_enabled?)
23
+ when :validate_registration_code
24
+ toggle_allow(
25
+ meeting.present? &&
26
+ meeting.registrations_enabled? &&
27
+ meeting.component.settings.registration_code_enabled
28
+ )
29
+ when :create
30
+ allow!
31
+ end
32
+
33
+ permission_action
34
+ end
35
+
36
+ private
37
+
38
+ def meeting
39
+ @meeting ||= context.fetch(:meeting, nil)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end