decidim-meetings 0.26.5 → 0.27.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/meetings/content_blocks/highlighted_meetings_cell.rb +1 -1
  3. data/app/cells/decidim/meetings/content_blocks/upcoming_meetings/show.erb +1 -1
  4. data/app/cells/decidim/meetings/content_blocks/upcoming_meetings_cell.rb +2 -2
  5. data/app/cells/decidim/meetings/highlighted_meetings_for_component_cell.rb +10 -1
  6. data/app/cells/decidim/meetings/meeting_list_item_cell.rb +1 -1
  7. data/app/cells/decidim/meetings/meeting_m_cell.rb +1 -1
  8. data/app/cells/decidim/meetings/meeting_s_cell.rb +6 -0
  9. data/app/commands/decidim/meetings/admin/close_meeting.rb +1 -1
  10. data/app/commands/decidim/meetings/admin/copy_meeting.rb +5 -24
  11. data/app/commands/decidim/meetings/admin/create_agenda.rb +1 -1
  12. data/app/commands/decidim/meetings/admin/create_meeting.rb +1 -4
  13. data/app/commands/decidim/meetings/admin/destroy_meeting.rb +1 -1
  14. data/app/commands/decidim/meetings/admin/export_meeting_registrations.rb +1 -1
  15. data/app/commands/decidim/meetings/admin/invite_user_to_join_meeting.rb +9 -11
  16. data/app/commands/decidim/meetings/admin/publish_meeting.rb +1 -1
  17. data/app/commands/decidim/meetings/admin/unpublish_meeting.rb +1 -1
  18. data/app/commands/decidim/meetings/admin/update_agenda.rb +1 -1
  19. data/app/commands/decidim/meetings/admin/update_meeting.rb +1 -4
  20. data/app/commands/decidim/meetings/admin/update_question_status.rb +1 -1
  21. data/app/commands/decidim/meetings/admin/update_questionnaire.rb +10 -7
  22. data/app/commands/decidim/meetings/admin/update_registrations.rb +5 -3
  23. data/app/commands/decidim/meetings/admin/validate_registration_code.rb +1 -1
  24. data/app/commands/decidim/meetings/close_meeting.rb +1 -1
  25. data/app/commands/decidim/meetings/create_answer.rb +1 -1
  26. data/app/commands/decidim/meetings/create_meeting.rb +1 -1
  27. data/app/commands/decidim/meetings/decline_invitation.rb +1 -1
  28. data/app/commands/decidim/meetings/join_meeting.rb +6 -7
  29. data/app/commands/decidim/meetings/leave_meeting.rb +3 -3
  30. data/app/commands/decidim/meetings/update_meeting.rb +1 -1
  31. data/app/commands/decidim/meetings/withdraw_meeting.rb +1 -1
  32. data/app/controllers/concerns/decidim/meetings/admin/filterable.rb +4 -4
  33. data/app/controllers/concerns/decidim/meetings/filterable.rb +1 -7
  34. data/app/controllers/decidim/meetings/admin/meeting_copies_controller.rb +2 -2
  35. data/app/controllers/decidim/meetings/calendars_controller.rb +1 -1
  36. data/app/controllers/decidim/meetings/directory/meetings_controller.rb +17 -16
  37. data/app/controllers/decidim/meetings/meetings_controller.rb +21 -15
  38. data/app/events/decidim/meetings/meeting_registration_notification_event.rb +1 -1
  39. data/app/forms/decidim/meetings/admin/close_meeting_form.rb +1 -1
  40. data/app/forms/decidim/meetings/admin/meeting_copy_form.rb +51 -0
  41. data/app/forms/decidim/meetings/admin/meeting_form.rb +11 -10
  42. data/app/forms/decidim/meetings/admin/meeting_registration_invite_form.rb +1 -1
  43. data/app/forms/decidim/meetings/admin/meeting_registrations_form.rb +3 -0
  44. data/app/forms/decidim/meetings/answer_form.rb +0 -1
  45. data/app/forms/decidim/meetings/close_meeting_form.rb +1 -1
  46. data/app/forms/decidim/meetings/meeting_form.rb +2 -2
  47. data/app/helpers/decidim/meetings/application_helper.rb +8 -6
  48. data/app/helpers/decidim/meetings/directory/application_helper.rb +8 -6
  49. data/app/helpers/decidim/meetings/meetings_helper.rb +1 -3
  50. data/app/jobs/decidim/meetings/send_close_meeting_reminder_job.rb +17 -0
  51. data/app/mailers/decidim/meetings/close_meeting_reminder_mailer.rb +43 -0
  52. data/app/models/decidim/meetings/agenda_item.rb +1 -1
  53. data/app/models/decidim/meetings/answer.rb +2 -2
  54. data/app/models/decidim/meetings/invite.rb +2 -2
  55. data/app/models/decidim/meetings/meeting.rb +36 -21
  56. data/app/models/decidim/meetings/questionnaire.rb +6 -0
  57. data/app/models/decidim/meetings/registration.rb +2 -2
  58. data/app/packs/src/decidim/meetings/admin/meetings_form.js +0 -4
  59. data/app/packs/src/decidim/meetings/admin/registrations_form.js +2 -0
  60. data/app/presenters/decidim/meetings/admin_log/questionnaire_presenter.rb +39 -0
  61. data/app/queries/decidim/meetings/admin/invites.rb +1 -1
  62. data/app/queries/decidim/meetings/filtered_meetings.rb +1 -1
  63. data/app/queries/decidim/meetings/questionnaire_user_answers.rb +1 -1
  64. data/app/serializers/decidim/meetings/{data_portability_invite_serializer.rb → download_your_data_invite_serializer.rb} +2 -2
  65. data/app/serializers/decidim/meetings/{data_portability_registration_serializer.rb → download_your_data_registration_serializer.rb} +2 -2
  66. data/app/services/decidim/meetings/calendar/base_calendar.rb +4 -3
  67. data/app/services/decidim/meetings/calendar/component_calendar.rb +7 -9
  68. data/app/services/decidim/meetings/calendar/organization_calendar.rb +1 -1
  69. data/app/services/decidim/meetings/calendar_renderer.rb +4 -4
  70. data/app/services/decidim/meetings/close_meeting_reminder_generator.rb +68 -0
  71. data/app/services/decidim/meetings/meeting_iframe_embedder.rb +10 -6
  72. data/app/services/decidim/meetings/meeting_search.rb +7 -53
  73. data/app/views/decidim/meetings/_calendar_modal.html.erb +19 -2
  74. data/app/views/decidim/meetings/admin/meeting_copies/_form.html.erb +64 -0
  75. data/app/views/decidim/meetings/admin/meeting_copies/new.html.erb +1 -1
  76. data/app/views/decidim/meetings/admin/meetings/_form.html.erb +5 -17
  77. data/app/views/decidim/meetings/admin/meetings/index.html.erb +1 -1
  78. data/app/views/decidim/meetings/admin/registrations/_form.html.erb +9 -0
  79. data/app/views/decidim/meetings/close_meeting_reminder_mailer/close_meeting_reminder.html.erb +5 -0
  80. data/app/views/decidim/meetings/directory/meetings/_filters.html.erb +7 -7
  81. data/app/views/decidim/meetings/directory/meetings/_meetings.html.erb +6 -1
  82. data/app/views/decidim/meetings/directory/meetings/index.html.erb +1 -1
  83. data/app/views/decidim/meetings/directory/meetings/index.js.erb +7 -1
  84. data/app/views/decidim/meetings/layouts/live_event.html.erb +6 -2
  85. data/app/views/decidim/meetings/meetings/_count.html.erb +1 -1
  86. data/app/views/decidim/meetings/meetings/_filters.html.erb +7 -7
  87. data/app/views/decidim/meetings/meetings/_form.html.erb +4 -2
  88. data/app/views/decidim/meetings/meetings/_meetings.html.erb +11 -6
  89. data/app/views/decidim/meetings/meetings/index.html.erb +2 -2
  90. data/app/views/decidim/meetings/meetings/index.js.erb +2 -2
  91. data/config/locales/am-ET.yml +1 -0
  92. data/config/locales/ar.yml +10 -7
  93. data/config/locales/bg.yml +7 -0
  94. data/config/locales/ca.yml +34 -27
  95. data/config/locales/cs.yml +35 -28
  96. data/config/locales/da.yml +1 -0
  97. data/config/locales/de.yml +16 -77
  98. data/config/locales/el.yml +7 -0
  99. data/config/locales/en.yml +33 -27
  100. data/config/locales/eo.yml +1 -0
  101. data/config/locales/es-MX.yml +34 -27
  102. data/config/locales/es-PY.yml +34 -27
  103. data/config/locales/es.yml +34 -27
  104. data/config/locales/et.yml +1 -0
  105. data/config/locales/eu.yml +164 -204
  106. data/config/locales/fi-plain.yml +34 -27
  107. data/config/locales/fi.yml +34 -27
  108. data/config/locales/fr-CA.yml +31 -26
  109. data/config/locales/fr.yml +31 -26
  110. data/config/locales/ga-IE.yml +5 -9
  111. data/config/locales/gl.yml +23 -2
  112. data/config/locales/hr.yml +1 -0
  113. data/config/locales/hu.yml +20 -147
  114. data/config/locales/id-ID.yml +7 -0
  115. data/config/locales/is-IS.yml +4 -1
  116. data/config/locales/it.yml +14 -11
  117. data/config/locales/ja.yml +36 -29
  118. data/config/locales/ko.yml +1 -0
  119. data/config/locales/lb.yml +12 -6
  120. data/config/locales/lt.yml +1 -664
  121. data/config/locales/lv.yml +7 -0
  122. data/config/locales/mt.yml +1 -0
  123. data/config/locales/nl.yml +64 -91
  124. data/config/locales/no.yml +17 -12
  125. data/config/locales/om-ET.yml +1 -0
  126. data/config/locales/pl.yml +9 -14
  127. data/config/locales/pt-BR.yml +9 -13
  128. data/config/locales/pt.yml +13 -11
  129. data/config/locales/ro-RO.yml +18 -28
  130. data/config/locales/ru.yml +7 -0
  131. data/config/locales/si-LK.yml +1 -0
  132. data/config/locales/sk.yml +7 -0
  133. data/config/locales/sl.yml +4 -0
  134. data/config/locales/so-SO.yml +1 -0
  135. data/config/locales/sr-CS.yml +1 -0
  136. data/config/locales/sv.yml +20 -22
  137. data/config/locales/sw-KE.yml +1 -0
  138. data/config/locales/ti-ER.yml +1 -0
  139. data/config/locales/tr-TR.yml +7 -11
  140. data/config/locales/uk.yml +7 -0
  141. data/config/locales/val-ES.yml +1 -0
  142. data/config/locales/vi.yml +1 -0
  143. data/config/locales/zh-CN.yml +7 -7
  144. data/config/locales/zh-TW.yml +1 -0
  145. data/db/migrate/20210512100333_drop_decidim_meetings_minutes_table.rb +2 -2
  146. data/db/migrate/20210518133236_merge_minutes_with_closing_report_in_meetings_table.rb +1 -1
  147. data/db/migrate/20211105115625_remove_not_null_on_customize_registration_email.rb +7 -0
  148. data/lib/decidim/api/meeting_type.rb +1 -1
  149. data/lib/decidim/meetings/component.rb +7 -7
  150. data/lib/decidim/meetings/{data_portability_user_answers_serializer.rb → download_your_data_user_answers_serializer.rb} +2 -2
  151. data/lib/decidim/meetings/engine.rb +15 -1
  152. data/lib/decidim/meetings/test/factories.rb +3 -1
  153. data/lib/decidim/meetings/test/notifications_handling.rb +1 -1
  154. data/lib/decidim/meetings/test/translated_event.rb +2 -2
  155. data/lib/decidim/meetings/version.rb +1 -1
  156. data/lib/decidim/meetings.rb +5 -1
  157. metadata +29 -26
  158. data/app/services/decidim/meetings/directory/meeting_search.rb +0 -53
  159. data/config/locales/gn-PY.yml +0 -1
  160. data/config/locales/ka-GE.yml +0 -1
  161. data/config/locales/lo-LA.yml +0 -1
  162. data/config/locales/oc-FR.yml +0 -1
@@ -15,34 +15,39 @@ module Decidim
15
15
  helper Decidim::FiltersHelper
16
16
  helper Decidim::Meetings::MapHelper
17
17
  helper Decidim::ResourceHelper
18
+ helper Decidim::ShortLinkHelper
18
19
 
19
20
  helper_method :meetings, :search
20
21
 
21
22
  def calendar
22
- render plain: CalendarRenderer.for(current_organization), content_type: "type/calendar"
23
+ render plain: CalendarRenderer.for(current_organization, params[:filter]), content_type: "type/calendar"
23
24
  end
24
25
 
25
26
  private
26
27
 
27
28
  def meetings
28
- is_past_meetings = params.dig("filter", "date")&.include?("past")
29
- @meetings ||= paginate(search.results.order(start_time: is_past_meetings ? :desc : :asc))
29
+ @meetings ||= paginate(search.result)
30
30
  end
31
31
 
32
- def search_klass
33
- ::Decidim::Meetings::Directory::MeetingSearch
32
+ def search_collection
33
+ Meeting.where(component: meeting_components).published.not_hidden.visible_for(current_user).with_availability(
34
+ filter_params[:availability]
35
+ ).includes(
36
+ :component,
37
+ attachments: :file_attachment
38
+ )
34
39
  end
35
40
 
36
41
  def default_filter_params
37
42
  {
38
- date: "upcoming",
39
- search_text: "",
43
+ with_any_date: "upcoming",
44
+ title_or_description_cont: "",
40
45
  activity: "all",
41
- scope_id: default_filter_scope_params,
42
- space: default_filter_space_params,
43
- type: default_filter_type_params,
44
- origin: default_filter_origin_params,
45
- category_id: default_filter_category_params
46
+ with_any_scope: default_filter_scope_params,
47
+ with_any_space: default_filter_space_params,
48
+ with_any_type: default_filter_type_params,
49
+ with_any_origin: default_filter_origin_params,
50
+ with_any_global_category: default_filter_category_params
46
51
  }
47
52
  end
48
53
 
@@ -69,10 +74,6 @@ module Decidim
69
74
  %w(all global) + current_organization.scopes.pluck(:id).map(&:to_s)
70
75
  end
71
76
 
72
- def context_params
73
- { component: meeting_components, organization: current_organization, current_user: current_user }
74
- end
75
-
76
77
  def meeting_components
77
78
  @meeting_components ||= Decidim::Component
78
79
  .where(manifest_name: "meetings")
@@ -13,6 +13,7 @@ module Decidim
13
13
 
14
14
  helper Decidim::WidgetUrlsHelper
15
15
  helper Decidim::ResourceVersionsHelper
16
+ helper Decidim::ShortLinkHelper
16
17
 
17
18
  helper_method :meetings, :meeting, :registration, :search
18
19
 
@@ -41,13 +42,13 @@ module Decidim
41
42
  end
42
43
 
43
44
  def index
44
- return unless search.results.blank? && params.dig("filter", "date") != %w(past)
45
+ return unless search.result.blank? && params.dig("filter", "date") != %w(past)
45
46
 
46
- @past_meetings = search_klass.new(search_params.merge(date: %w(past)))
47
+ @past_meetings ||= search_with(filter_params.merge(with_any_date: %w(past)))
47
48
 
48
- if @past_meetings.results.present?
49
+ if @past_meetings.result.present?
49
50
  params[:filter] ||= {}
50
- params[:filter][:date] = %w(past)
51
+ params[:filter][:with_any_date] = %w(past)
51
52
  @forced_past_meetings = true
52
53
  @search = @past_meetings
53
54
  end
@@ -108,16 +109,20 @@ module Decidim
108
109
  end
109
110
 
110
111
  def meetings
111
- is_past_meetings = params.dig("filter", "date")&.include?("past")
112
- @meetings ||= paginate(search.results.order(start_time: is_past_meetings ? :desc : :asc))
112
+ @meetings ||= paginate(search.result.order(start_time: :desc))
113
113
  end
114
114
 
115
115
  def registration
116
116
  @registration ||= meeting.registrations.find_by(user: current_user)
117
117
  end
118
118
 
119
- def search_klass
120
- MeetingSearch
119
+ def search_collection
120
+ Meeting.where(component: current_component).published.not_hidden.visible_for(current_user).with_availability(
121
+ filter_params[:with_availability]
122
+ ).includes(
123
+ :component,
124
+ attachments: :file_attachment
125
+ )
121
126
  end
122
127
 
123
128
  def meeting_form
@@ -126,14 +131,15 @@ module Decidim
126
131
 
127
132
  def default_filter_params
128
133
  {
129
- search_text: "",
130
- date: "upcoming",
134
+ search_text_cont: "",
135
+ with_any_date: %w(upcoming),
131
136
  activity: "all",
132
- scope_id: default_filter_scope_params,
133
- category_id: default_filter_category_params,
134
- state: nil,
135
- origin: default_filter_origin_params,
136
- type: default_filter_type_params
137
+ with_availability: "",
138
+ with_any_scope: default_filter_scope_params,
139
+ with_any_category: default_filter_category_params,
140
+ with_any_state: nil,
141
+ with_any_origin: default_filter_origin_params,
142
+ with_any_type: default_filter_type_params
137
143
  }
138
144
  end
139
145
  end
@@ -6,7 +6,7 @@ module Decidim
6
6
  include Decidim::Events::NotificationEvent
7
7
 
8
8
  def notification_title
9
- I18n.t("notification_title", i18n_options).html_safe
9
+ I18n.t("notification_title", **i18n_options).html_safe
10
10
  end
11
11
 
12
12
  def i18n_options
@@ -16,7 +16,7 @@ module Decidim
16
16
  attribute :attending_organizations, String
17
17
  attribute :proposal_ids, Array[Integer]
18
18
  attribute :proposals
19
- attribute :closed_at, Decidim::Attributes::TimeWithZone, default: ->(_form, _attribute) { Time.current }
19
+ attribute :closed_at, Decidim::Attributes::TimeWithZone, default: -> { Time.current }
20
20
 
21
21
  validates :closing_report, translatable_presence: true
22
22
  validates :attendees_count, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 999, only_integer: true }
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ module Admin
6
+ # A form object used to copy a meeting from the admin
7
+ # dashboard.
8
+ #
9
+ class MeetingCopyForm < ::Decidim::Meetings::BaseMeetingForm
10
+ include TranslatableAttributes
11
+
12
+ translatable_attribute :title, String
13
+ translatable_attribute :description, String
14
+ translatable_attribute :location, String
15
+ translatable_attribute :location_hints, String
16
+
17
+ attribute :show_embedded_iframe, Boolean, default: false
18
+ attribute :private_meeting, Boolean
19
+ attribute :transparent, Boolean
20
+ attribute :services, Array[MeetingServiceForm]
21
+
22
+ mimic :meeting
23
+
24
+ validates :online_meeting_url, url: true, if: ->(form) { form.online_meeting? || form.hybrid_meeting? }
25
+ validates :title, translatable_presence: true
26
+ validates :description, translatable_presence: true
27
+ validates :location, translatable_presence: true, if: ->(form) { form.in_person_meeting? || form.hybrid_meeting? }
28
+
29
+ def map_model(model)
30
+ self.services = model.services.map do |service|
31
+ MeetingServiceForm.new(service.attributes)
32
+ end
33
+ end
34
+
35
+ def services_to_persist
36
+ services.reject(&:deleted)
37
+ end
38
+
39
+ def number_of_services
40
+ services.size
41
+ end
42
+
43
+ alias component current_component
44
+
45
+ def questionnaire
46
+ Decidim::Forms::Questionnaire.new
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -14,7 +14,6 @@ module Decidim
14
14
  attribute :transparent, Boolean
15
15
  attribute :registration_type, String
16
16
  attribute :registration_url, String
17
- attribute :available_slots, Integer, default: 0
18
17
  attribute :customize_registration_email, Boolean
19
18
  attribute :iframe_embed_type, String, default: "none"
20
19
  attribute :comments_enabled, Boolean, default: true
@@ -26,13 +25,11 @@ module Decidim
26
25
  translatable_attribute :description, String
27
26
  translatable_attribute :location, String
28
27
  translatable_attribute :location_hints, String
29
- translatable_attribute :registration_email_custom_content, String
30
28
 
31
29
  validates :iframe_embed_type, inclusion: { in: Decidim::Meetings::Meeting.iframe_embed_types }
32
30
  validates :title, translatable_presence: true
33
31
  validates :description, translatable_presence: true
34
32
  validates :registration_type, presence: true
35
- validates :available_slots, numericality: { greater_than_or_equal_to: 0 }, presence: true, if: ->(form) { form.on_this_platform? }
36
33
  validates :registration_url, presence: true, url: true, if: ->(form) { form.on_different_platform? }
37
34
  validates :type_of_meeting, presence: true
38
35
  validates :location, translatable_presence: true, if: ->(form) { form.in_person_meeting? || form.hybrid_meeting? }
@@ -75,14 +72,14 @@ module Decidim
75
72
  #
76
73
  # Returns a Decidim::Scope
77
74
  def scope
78
- @scope ||= @decidim_scope_id ? current_component.scopes.find_by(id: @decidim_scope_id) : current_component.scope
75
+ @scope ||= @attributes["decidim_scope_id"].value ? current_component.scopes.find_by(id: @attributes["decidim_scope_id"].value) : current_component.scope
79
76
  end
80
77
 
81
78
  # Scope identifier
82
79
  #
83
80
  # Returns the scope identifier related to the meeting
84
81
  def decidim_scope_id
85
- @decidim_scope_id || scope&.id
82
+ super || scope&.id
86
83
  end
87
84
 
88
85
  def category
@@ -95,6 +92,15 @@ module Decidim
95
92
  type_of_meeting.presence
96
93
  end
97
94
 
95
+ def type_of_meeting_select
96
+ Decidim::Meetings::Meeting::TYPE_OF_MEETING.map do |type|
97
+ [
98
+ I18n.t("type_of_meeting.#{type}", scope: "decidim.meetings"),
99
+ type
100
+ ]
101
+ end
102
+ end
103
+
98
104
  def iframe_access_level_select
99
105
  Decidim::Meetings::Meeting.iframe_access_levels.map do |level, _value|
100
106
  [
@@ -113,11 +119,6 @@ module Decidim
113
119
  end
114
120
  end
115
121
 
116
- # Support for copy meeting
117
- def questionnaire
118
- Decidim::Forms::Questionnaire.new
119
- end
120
-
121
122
  def on_this_platform?
122
123
  registration_type == "on_this_platform"
123
124
  end
@@ -12,7 +12,7 @@ module Decidim
12
12
  attribute :existing_user, Boolean, default: false
13
13
 
14
14
  validates :name, presence: true, unless: proc { |object| object.existing_user }
15
- validates :email, presence: true, 'valid_email_2/email': { disposable: true }, unless: proc { |object| object.existing_user }
15
+ validates :email, presence: true, "valid_email_2/email": { disposable: true }, unless: proc { |object| object.existing_user }
16
16
  validates :user, presence: true, if: proc { |object| object.existing_user }
17
17
 
18
18
  def user
@@ -11,9 +11,12 @@ module Decidim
11
11
 
12
12
  attribute :registrations_enabled, Boolean
13
13
  attribute :registration_form_enabled, Boolean
14
+ attribute :customize_registration_email, Boolean
14
15
  attribute :available_slots, Integer
15
16
  attribute :reserved_slots, Integer
17
+
16
18
  translatable_attribute :registration_terms, String
19
+ translatable_attribute :registration_email_custom_content, String
17
20
 
18
21
  validates :registration_terms, translatable_presence: true, if: ->(form) { form.registrations_enabled? }
19
22
  validates :available_slots, :reserved_slots, presence: true, if: ->(form) { form.registrations_enabled? }
@@ -10,7 +10,6 @@ module Decidim
10
10
  attribute :body, String
11
11
  attribute :choices, Array[AnswerChoiceForm]
12
12
  attribute :current_user, Decidim::User
13
- attribute :answer, Decidim::Meetings::Answer
14
13
 
15
14
  validates :selected_choices, presence: true
16
15
  validate :max_choices, if: -> { question.max_choices }
@@ -7,7 +7,7 @@ module Decidim
7
7
  attribute :closing_report, String
8
8
  attribute :proposal_ids, Array[Integer]
9
9
  attribute :proposals
10
- attribute :closed_at, Decidim::Attributes::TimeWithZone, default: ->(_form, _attribute) { Time.current }
10
+ attribute :closed_at, Decidim::Attributes::TimeWithZone, default: -> { Time.current }
11
11
  attribute :attendees_count, Integer, default: 0
12
12
 
13
13
  validates :closing_report, presence: true
@@ -60,14 +60,14 @@ module Decidim
60
60
  #
61
61
  # Returns a Decidim::Scope
62
62
  def scope
63
- @scope ||= @decidim_scope_id ? current_component.scopes.find_by(id: @decidim_scope_id) : current_component.scope
63
+ @scope ||= @attributes["decidim_scope_id"].value ? current_component.scopes.find_by(id: @attributes["decidim_scope_id"].value) : current_component.scope
64
64
  end
65
65
 
66
66
  # Scope identifier
67
67
  #
68
68
  # Returns the scope identifier related to the meeting
69
69
  def decidim_scope_id
70
- @decidim_scope_id || scope&.id
70
+ super || scope&.id
71
71
  end
72
72
 
73
73
  def category
@@ -16,7 +16,7 @@ module Decidim
16
16
  def filter_origin_values
17
17
  origin_values = []
18
18
  origin_values << TreePoint.new("official", t("decidim.meetings.meetings.filters.origin_values.official"))
19
- origin_values << TreePoint.new("citizens", t("decidim.meetings.meetings.filters.origin_values.citizens")) # todo
19
+ origin_values << TreePoint.new("participants", t("decidim.meetings.meetings.filters.origin_values.participants")) # todo
20
20
  if current_organization.user_groups_enabled?
21
21
  origin_values << TreePoint.new("user_group", t("decidim.meetings.meetings.filters.origin_values.user_groups")) # todo
22
22
  end
@@ -41,11 +41,13 @@ module Decidim
41
41
  end
42
42
 
43
43
  def filter_date_values
44
- [
45
- ["all", t("decidim.meetings.meetings.filters.date_values.all")],
46
- ["upcoming", t("decidim.meetings.meetings.filters.date_values.upcoming")],
47
- ["past", t("decidim.meetings.meetings.filters.date_values.past")]
48
- ]
44
+ TreeNode.new(
45
+ TreePoint.new("", t("decidim.meetings.meetings.filters.date_values.all")),
46
+ [
47
+ TreePoint.new("upcoming", t("decidim.meetings.meetings.filters.date_values.upcoming")),
48
+ TreePoint.new("past", t("decidim.meetings.meetings.filters.date_values.past"))
49
+ ]
50
+ )
49
51
  end
50
52
 
51
53
  # Options to filter meetings by activity.
@@ -27,11 +27,13 @@ module Decidim
27
27
  end
28
28
 
29
29
  def filter_date_values
30
- [
31
- ["all", t("decidim.meetings.meetings.filters.date_values.all")],
32
- ["upcoming", t("decidim.meetings.meetings.filters.date_values.upcoming")],
33
- ["past", t("decidim.meetings.meetings.filters.date_values.past")]
34
- ]
30
+ TreeNode.new(
31
+ TreePoint.new("", t("decidim.meetings.meetings.filters.date_values.all")),
32
+ [
33
+ TreePoint.new("upcoming", t("decidim.meetings.meetings.filters.date_values.upcoming")),
34
+ TreePoint.new("past", t("decidim.meetings.meetings.filters.date_values.past"))
35
+ ]
36
+ )
35
37
  end
36
38
 
37
39
  def directory_filter_scopes_values
@@ -107,7 +109,7 @@ module Decidim
107
109
  def directory_filter_origin_values
108
110
  origin_values = []
109
111
  origin_values << TreePoint.new("official", t("decidim.meetings.meetings.filters.origin_values.official"))
110
- origin_values << TreePoint.new("citizens", t("decidim.meetings.meetings.filters.origin_values.citizens"))
112
+ origin_values << TreePoint.new("participants", t("decidim.meetings.meetings.filters.origin_values.participants"))
111
113
  origin_values << TreePoint.new("user_group", t("decidim.meetings.meetings.filters.origin_values.user_groups")) if current_organization.user_groups_enabled?
112
114
 
113
115
  TreeNode.new(
@@ -30,12 +30,10 @@ module Decidim
30
30
  # Returns a String.
31
31
  def meeting_type_badge_css_class(type)
32
32
  case type
33
- when "private"
33
+ when "private", "withdraw"
34
34
  "alert"
35
35
  when "transparent"
36
36
  "secondary"
37
- when "withdraw"
38
- "alert"
39
37
  end
40
38
  end
41
39
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ class SendCloseMeetingReminderJob < ApplicationJob
6
+ queue_as :close_meeting_reminder
7
+
8
+ def perform(record)
9
+ return if record.remindable.closed?
10
+
11
+ ::Decidim::ReminderDelivery.create(reminder: record.reminder)
12
+ ::Decidim::Meetings::CloseMeetingReminderMailer.close_meeting_reminder(record).deliver_now
13
+ record.update(state: "completed")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ # A custom mailer for sending notifications for overdue meetings
6
+ class CloseMeetingReminderMailer < Decidim::ApplicationMailer
7
+ include Decidim::TranslationsHelper
8
+ include ActionView::Helpers::SanitizeHelper
9
+ include Decidim::ApplicationHelper
10
+
11
+ helper Decidim::ResourceHelper
12
+ helper Decidim::TranslationsHelper
13
+ helper Decidim::ApplicationHelper
14
+
15
+ helper_method :routes
16
+
17
+ # Send the user an email reminder to close the meetings
18
+ #
19
+ # record - the reminder record specific to a past meeting.
20
+ def close_meeting_reminder(record)
21
+ @reminder = record.reminder
22
+ @user = record.reminder.user
23
+ with_user(@user) do
24
+ @meeting = record.remindable
25
+ @organization = @user.organization
26
+ mail(
27
+ to: @user.email,
28
+ subject: I18n.t(
29
+ "decidim.meetings.close_meeting_reminder_mailer.close_meeting_reminder.subject",
30
+ organization_name: @organization.name
31
+ )
32
+ )
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def routes
39
+ @routes ||= Decidim::EngineRouter.main_proxy(@reminder.component)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -11,7 +11,7 @@ module Decidim
11
11
 
12
12
  translatable_fields :title, :description
13
13
 
14
- belongs_to :agenda, foreign_key: "decidim_agenda_id", class_name: "Decidim::Meetings::Agenda"
14
+ belongs_to :agenda, -> { order(:position) }, foreign_key: "decidim_agenda_id", class_name: "Decidim::Meetings::Agenda"
15
15
 
16
16
  has_many :agenda_item_children, foreign_key: "parent_id", class_name: "Decidim::Meetings::AgendaItem", inverse_of: :parent, dependent: :destroy
17
17
  belongs_to :parent, class_name: "Decidim::Meetings::AgendaItem", inverse_of: :agenda_item_children, optional: true
@@ -4,7 +4,7 @@ module Decidim
4
4
  module Meetings
5
5
  # The data store for an Answer in the Decidim::Meetings
6
6
  class Answer < Meetings::ApplicationRecord
7
- include Decidim::DataPortability
7
+ include Decidim::DownloadYourData
8
8
 
9
9
  belongs_to :user, class_name: "Decidim::User", foreign_key: "decidim_user_id", optional: true
10
10
  belongs_to :questionnaire, class_name: "Decidim::Meetings::Questionnaire", foreign_key: "decidim_questionnaire_id"
@@ -24,7 +24,7 @@ module Decidim
24
24
  end
25
25
 
26
26
  def self.export_serializer
27
- Decidim::Meetings::DataPortabilityUserAnswersSerializer
27
+ Decidim::Meetings::DownloadYourDataUserAnswersSerializer
28
28
  end
29
29
 
30
30
  def organization
@@ -6,7 +6,7 @@ module Decidim
6
6
  class Invite < Meetings::ApplicationRecord
7
7
  include Decidim::Traceable
8
8
  include Decidim::Loggable
9
- include Decidim::DataPortability
9
+ include Decidim::DownloadYourData
10
10
 
11
11
  belongs_to :meeting, foreign_key: "decidim_meeting_id", class_name: "Decidim::Meetings::Meeting"
12
12
  belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User"
@@ -14,7 +14,7 @@ module Decidim
14
14
  validates :user, uniqueness: { scope: :meeting }
15
15
 
16
16
  def self.export_serializer
17
- Decidim::Meetings::DataPortabilityInviteSerializer
17
+ Decidim::Meetings::DownloadYourDataInviteSerializer
18
18
  end
19
19
 
20
20
  def self.log_presenter_class_for(_log)
@@ -25,6 +25,7 @@ module Decidim
25
25
  include Decidim::Authorable
26
26
  include Decidim::TranslatableResource
27
27
  include Decidim::Publicable
28
+ include Decidim::FilterableResource
28
29
 
29
30
  TYPE_OF_MEETING = %w(in_person online hybrid).freeze
30
31
  REGISTRATION_TYPE = %w(registration_disabled on_this_platform on_different_platform).freeze
@@ -59,16 +60,31 @@ module Decidim
59
60
  scope :upcoming, -> { where(arel_table[:end_time].gteq(Time.current)) }
60
61
  scope :withdrawn, -> { where(state: "withdrawn") }
61
62
  scope :except_withdrawn, -> { where.not(state: "withdrawn").or(where(state: nil)) }
63
+ scope :with_availability, lambda { |state_key|
64
+ case state_key
65
+ when "withdrawn"
66
+ withdrawn
67
+ else
68
+ except_withdrawn
69
+ end
70
+ }
71
+ scope_search_multi :with_any_date, [:upcoming, :past]
72
+ scope :with_any_space, lambda { |*target_space|
73
+ target_spaces = target_space.compact.compact_blank
74
+
75
+ return self if target_spaces.blank? || target_spaces.include?("all")
62
76
 
63
- scope :visible_meeting_for, lambda { |user|
77
+ joins(:component).where(
78
+ decidim_components: { participatory_space_type: target_spaces.map(&:classify) }
79
+ )
80
+ }
81
+
82
+ scope :visible_for, lambda { |user|
64
83
  (all.distinct if user&.admin?) ||
65
84
  if user.present?
66
- spaces = Decidim.participatory_space_registry.manifests.filter_map do |manifest|
67
- table_name = manifest.model_class_name.constantize.try(:table_name)
68
- next if table_name.blank?
69
-
85
+ spaces = Decidim.participatory_space_registry.manifests.map do |manifest|
70
86
  {
71
- name: table_name.singularize,
87
+ name: manifest.model_class_name.constantize.table_name.singularize,
72
88
  class_name: manifest.model_class_name
73
89
  }
74
90
  end
@@ -112,6 +128,10 @@ module Decidim
112
128
 
113
129
  scope :visible, -> { where("decidim_meetings_meetings.private_meeting != ? OR decidim_meetings_meetings.transparent = ?", true, true) }
114
130
 
131
+ scope :authored_by, ->(author) { where(decidim_author_id: author) }
132
+
133
+ scope_search_multi :with_any_type, TYPE_OF_MEETING.map(&:to_sym)
134
+
115
135
  TYPE_OF_MEETING.each do |type|
116
136
  scope type.to_sym, -> { where(type_of_meeting: type.to_sym) }
117
137
  scope "not_#{type}".to_sym, -> { where.not(type_of_meeting: type.to_sym) }
@@ -218,7 +238,7 @@ module Decidim
218
238
  end
219
239
 
220
240
  def current_user_can_visit_meeting?(user)
221
- Decidim::Meetings::Meeting.visible_meeting_for(user).exists?(id: id)
241
+ Decidim::Meetings::Meeting.visible_for(user).exists?(id: id)
222
242
  end
223
243
 
224
244
  def iframe_access_level_allowed_for_user?(user)
@@ -345,13 +365,9 @@ module Decidim
345
365
  order(Arel::Nodes::InfixOperation.new("", field, Arel.sql("DESC")))
346
366
  end
347
367
 
348
- ransacker :type do
349
- Arel.sql(%("decidim_meetings_meetings"."type_of_meeting"))
350
- end
351
-
352
- ransacker :title do
353
- Arel.sql(%{cast("decidim_meetings_meetings"."title" as text)})
354
- end
368
+ # Create i18n ransackers for :title and :description.
369
+ # Create the :search_text ransacker alias for searching from both of these.
370
+ ransacker_i18n_multi :search_text, [:title, :description]
355
371
 
356
372
  ransacker :id_string do
357
373
  Arel.sql(%{cast("decidim_meetings_meetings"."id" as text)})
@@ -361,13 +377,12 @@ module Decidim
361
377
  Arel.sql("(start_time > NOW())")
362
378
  end
363
379
 
364
- ransacker :origin do
365
- Arel.sql("CASE
366
- WHEN decidim_author_type = 'Decidim::Organization' THEN 'official'
367
- WHEN decidim_author_type = 'Decidim::UserBaseEntity' AND decidim_user_group_id IS NOT NULL THEN 'user_group'
368
- WHEN decidim_author_type = 'Decidim::UserBaseEntity' AND decidim_user_group_id IS NULL THEN 'citizen'
369
- ELSE 'unknown' END
370
- ")
380
+ def self.ransackable_scopes(_auth_object = nil)
381
+ [:with_any_type, :with_any_date, :with_any_space, :with_any_origin, :with_any_scope, :with_any_category, :with_any_global_category]
382
+ end
383
+
384
+ def self.ransack(params = {}, options = {})
385
+ MeetingSearch.new(self, params, options)
371
386
  end
372
387
 
373
388
  private
@@ -4,6 +4,8 @@ module Decidim
4
4
  module Meetings
5
5
  # The data store for a Questionnaire in the Decidim::Meetings component.
6
6
  class Questionnaire < Meetings::ApplicationRecord
7
+ include Decidim::Traceable
8
+
7
9
  belongs_to :questionnaire_for, polymorphic: true
8
10
 
9
11
  has_many :questions, -> { order(:position) }, class_name: "Question", foreign_key: "decidim_questionnaire_id", dependent: :destroy
@@ -18,6 +20,10 @@ module Decidim
18
20
  def all_questions_unpublished?
19
21
  questions.all?(&:unpublished?)
20
22
  end
23
+
24
+ def self.log_presenter_class_for(_log)
25
+ Decidim::Meetings::AdminLog::QuestionnairePresenter
26
+ end
21
27
  end
22
28
  end
23
29
  end
@@ -4,7 +4,7 @@ module Decidim
4
4
  module Meetings
5
5
  # The data store for a Registration in the Decidim::Meetings component.
6
6
  class Registration < Meetings::ApplicationRecord
7
- include Decidim::DataPortability
7
+ include Decidim::DownloadYourData
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"
@@ -23,7 +23,7 @@ module Decidim
23
23
  end
24
24
 
25
25
  def self.export_serializer
26
- Decidim::Meetings::DataPortabilityRegistrationSerializer
26
+ Decidim::Meetings::DownloadYourDataRegistrationSerializer
27
27
  end
28
28
 
29
29
  # Pluck all Decidim::UserGroup ID's