decidim-meetings 0.24.3 → 0.25.0.rc4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/cells/decidim/meetings/join_meeting_button/registration_confirm.erb +6 -0
- data/app/cells/decidim/meetings/join_meeting_button/show.erb +8 -6
- data/app/cells/decidim/meetings/meeting_m_cell.rb +4 -0
- data/app/cells/decidim/meetings/meeting_url/show.erb +7 -2
- data/app/cells/decidim/meetings/meeting_url_cell.rb +14 -2
- data/app/cells/decidim/meetings/meetings_map/show.erb +1 -1
- data/app/cells/decidim/meetings/online_meeting_cell.rb +36 -0
- data/app/cells/decidim/meetings/online_meeting_link/show.erb +44 -0
- data/app/cells/decidim/meetings/online_meeting_link_cell.rb +27 -0
- data/app/cells/decidim/meetings/public_participants_list/show.erb +16 -0
- data/app/cells/decidim/meetings/public_participants_list_cell.rb +31 -0
- data/app/cells/decidim/meetings/question_responses/show.erb +7 -0
- data/app/cells/decidim/meetings/question_responses_cell.rb +72 -0
- data/app/commands/decidim/meetings/admin/close_meeting.rb +3 -0
- data/app/commands/decidim/meetings/admin/copy_meeting.rb +0 -9
- data/app/commands/decidim/meetings/admin/create_meeting.rb +4 -20
- data/app/commands/decidim/meetings/admin/destroy_meeting.rb +2 -0
- data/app/commands/decidim/meetings/admin/publish_meeting.rb +72 -0
- data/app/commands/decidim/meetings/admin/unpublish_meeting.rb +45 -0
- data/app/commands/decidim/meetings/admin/update_meeting.rb +6 -3
- data/app/commands/decidim/meetings/admin/update_question_status.rb +57 -0
- data/app/commands/decidim/meetings/admin/update_questionnaire.rb +93 -0
- data/app/commands/decidim/meetings/close_meeting.rb +2 -0
- data/app/commands/decidim/meetings/create_answer.rb +52 -0
- data/app/commands/decidim/meetings/create_meeting.rb +3 -1
- data/app/commands/decidim/meetings/join_meeting.rb +2 -1
- data/app/commands/decidim/meetings/update_meeting.rb +2 -1
- data/app/controllers/concerns/decidim/meetings/admin/filterable.rb +55 -0
- data/app/controllers/concerns/decidim/meetings/polls_resources.rb +29 -0
- data/app/controllers/decidim/meetings/admin/application_controller.rb +6 -2
- data/app/controllers/decidim/meetings/admin/invites_controller.rb +0 -2
- data/app/controllers/decidim/meetings/admin/meeting_closes_controller.rb +1 -1
- data/app/controllers/decidim/meetings/admin/meetings_controller.rb +46 -1
- data/app/controllers/decidim/meetings/admin/meetings_poll_controller.rb +107 -0
- data/app/controllers/decidim/meetings/calendars_controller.rb +8 -0
- data/app/controllers/decidim/meetings/live_events_controller.rb +29 -0
- data/app/controllers/decidim/meetings/meeting_closes_controller.rb +1 -1
- data/app/controllers/decidim/meetings/polls/answers_controller.rb +37 -0
- data/app/controllers/decidim/meetings/polls/questions_controller.rb +45 -0
- data/app/events/decidim/meetings/create_meeting_event.rb +16 -0
- data/app/forms/decidim/meetings/admin/answer_option_form.rb +24 -0
- data/app/forms/decidim/meetings/admin/close_meeting_form.rb +3 -1
- data/app/forms/decidim/meetings/admin/meeting_form.rb +12 -1
- data/app/forms/decidim/meetings/admin/question_form.rb +44 -0
- data/app/forms/decidim/meetings/admin/questionnaire_form.rb +19 -0
- data/app/forms/decidim/meetings/answer_choice_form.rb +14 -0
- data/app/forms/decidim/meetings/answer_form.rb +65 -0
- data/app/forms/decidim/meetings/close_meeting_form.rb +4 -0
- data/app/forms/decidim/meetings/join_meeting_form.rb +1 -0
- data/app/forms/decidim/meetings/meeting_form.rb +9 -0
- data/app/helpers/decidim/meetings/admin/filterable_helper.rb +13 -0
- data/app/helpers/decidim/meetings/application_helper.rb +3 -2
- data/app/helpers/decidim/meetings/meetings_helper.rb +21 -0
- data/app/models/decidim/meetings/answer.rb +48 -0
- data/app/models/decidim/meetings/answer_choice.rb +15 -0
- data/app/models/decidim/meetings/answer_option.rb +24 -0
- data/app/models/decidim/meetings/meeting.rb +88 -42
- data/app/models/decidim/meetings/poll.rb +19 -0
- data/app/models/decidim/meetings/question.rb +54 -0
- data/app/models/decidim/meetings/questionnaire.rb +23 -0
- data/app/models/decidim/meetings/registration.rb +2 -0
- data/app/packs/entrypoints/decidim_meetings.js +6 -0
- data/app/packs/entrypoints/decidim_meetings_admin.js +5 -0
- data/app/{assets/images/decidim/gamification/badges/attended_meetings.svg → packs/images/decidim/gamification/badges/decidim_gamification_badges_attended_meetings.svg} +0 -0
- data/app/{assets/images/decidim/meetings/icon.svg → packs/images/decidim/meetings/decidim_meetings.svg} +0 -0
- data/app/packs/src/decidim/meetings/admin/agendas.js +159 -0
- data/app/{assets/javascripts/decidim/meetings/admin/destroy_meeting_alert.js.es6 → packs/src/decidim/meetings/admin/destroy_meeting_alert.js} +0 -0
- data/app/{assets/javascripts/decidim/meetings/admin/meetings_form.js.es6 → packs/src/decidim/meetings/admin/meetings_form.js} +7 -4
- data/app/{assets/javascripts/decidim/meetings/admin/registrations_form.js.es6 → packs/src/decidim/meetings/admin/registrations_form.js} +0 -0
- data/app/{assets/javascripts/decidim/meetings/admin/registrations_invite_form.es6 → packs/src/decidim/meetings/admin/registrations_invite_form.js} +3 -3
- data/app/packs/src/decidim/meetings/meetings_form.js +54 -0
- data/app/packs/src/decidim/meetings/meetings_polls.js +41 -0
- data/app/packs/src/decidim/meetings/poll.component.js +166 -0
- data/app/packs/stylesheets/decidim/meetings/_header.scss +52 -0
- data/app/packs/stylesheets/decidim/meetings/_main.scss +178 -0
- data/app/packs/stylesheets/decidim/meetings/_meetings.scss +2 -0
- data/app/permissions/decidim/meetings/admin/permissions.rb +16 -8
- data/app/permissions/decidim/meetings/permissions.rb +42 -17
- data/app/presenters/decidim/meetings/admin_log/meeting_presenter.rb +5 -1
- data/app/presenters/decidim/meetings/meeting_presenter.rb +25 -25
- data/app/queries/decidim/meetings/filtered_meetings.rb +1 -1
- data/app/queries/decidim/meetings/metrics/meetings_metric_manage.rb +1 -1
- data/app/queries/decidim/meetings/questionnaire_user_answers.rb +29 -0
- data/app/serializers/decidim/meetings/data_portability_invite_serializer.rb +2 -1
- data/app/serializers/decidim/meetings/data_portability_registration_serializer.rb +2 -1
- data/app/serializers/decidim/meetings/registration_serializer.rb +3 -12
- data/app/services/decidim/meetings/calendar/component_calendar.rb +2 -2
- data/app/services/decidim/meetings/calendar/meeting_calendar.rb +33 -0
- data/app/services/decidim/meetings/calendar_renderer.rb +2 -0
- data/app/services/decidim/meetings/meeting_iframe_embedder.rb +86 -0
- data/app/services/decidim/meetings/meeting_search.rb +2 -2
- data/app/views/decidim/meetings/admin/agenda/_form.html.erb +1 -1
- data/app/views/decidim/meetings/admin/invite_join_meeting_mailer/invite.html.erb +4 -4
- data/app/views/decidim/meetings/admin/invites/_form.html.erb +1 -1
- data/app/views/decidim/meetings/admin/meeting_closes/_form.html.erb +16 -1
- data/app/views/decidim/meetings/admin/meeting_copies/_form.html.erb +1 -1
- data/app/views/decidim/meetings/admin/meetings/_form.html.erb +16 -1
- data/app/views/decidim/meetings/admin/meetings/index.html.erb +39 -11
- data/app/views/decidim/meetings/admin/poll/_answer_option.html.erb +34 -0
- data/app/views/decidim/meetings/admin/poll/_answer_option_template.html.erb +7 -0
- data/app/views/decidim/meetings/admin/poll/_form.html.erb +63 -0
- data/app/views/decidim/meetings/admin/poll/_question.html.erb +96 -0
- data/app/views/decidim/meetings/admin/poll/edit.html.erb +9 -0
- data/app/views/decidim/meetings/admin/registrations/_form.html.erb +1 -1
- data/app/views/decidim/meetings/directory/meetings/index.html.erb +4 -4
- data/app/views/decidim/meetings/directory/meetings/index.js.erb +2 -0
- data/app/views/decidim/meetings/layouts/live_event.html.erb +50 -0
- data/app/views/decidim/meetings/live_events/show.html.erb +12 -0
- data/app/views/decidim/meetings/meeting_closes/_form.html.erb +8 -1
- data/app/views/decidim/meetings/meeting_closes/edit.html.erb +1 -1
- data/app/views/decidim/meetings/meetings/_calendar_modal.html.erb +26 -0
- data/app/views/decidim/meetings/meetings/_filters.html.erb +1 -1
- data/app/views/decidim/meetings/meetings/_form.html.erb +6 -1
- data/app/views/decidim/meetings/meetings/_linked_meetings.html.erb +1 -1
- data/app/views/decidim/meetings/meetings/_meeting_minutes.html.erb +7 -18
- data/app/views/decidim/meetings/meetings/edit.html.erb +1 -1
- data/app/views/decidim/meetings/meetings/index.html.erb +4 -4
- data/app/views/decidim/meetings/meetings/new.html.erb +1 -1
- data/app/views/decidim/meetings/meetings/show.html.erb +13 -14
- data/app/views/decidim/meetings/polls/answers/_multiple_option.html.erb +13 -0
- data/app/views/decidim/meetings/polls/answers/_single_option.html.erb +19 -0
- data/app/views/decidim/meetings/polls/answers/create.js.erb +5 -0
- data/app/views/decidim/meetings/polls/questions/_closed_question.html.erb +7 -0
- data/app/views/decidim/meetings/polls/questions/_index.html.erb +5 -0
- data/app/views/decidim/meetings/polls/questions/_index_admin.html.erb +43 -0
- data/app/views/decidim/meetings/polls/questions/_published_question.html.erb +29 -0
- data/app/views/decidim/meetings/polls/questions/_question.html.erb +7 -0
- data/app/views/decidim/meetings/polls/questions/index.js.erb +5 -0
- data/app/views/decidim/meetings/polls/questions/index_admin.js.erb +5 -0
- data/app/views/decidim/meetings/registration_mailer/confirmation.html.erb +5 -2
- data/app/views/devise/mailer/join_meeting.html.erb +6 -6
- data/config/assets.rb +11 -0
- data/config/locales/ar.yml +3 -24
- data/config/locales/ca.yml +36 -27
- data/config/locales/cs.yml +72 -26
- data/config/locales/de.yml +22 -27
- data/config/locales/el.yml +3 -24
- data/config/locales/en.yml +112 -26
- data/config/locales/es-MX.yml +13 -27
- data/config/locales/es-PY.yml +13 -27
- data/config/locales/es.yml +13 -27
- data/config/locales/eu.yml +3 -24
- data/config/locales/fi-plain.yml +70 -26
- data/config/locales/fi.yml +70 -26
- data/config/locales/fr-CA.yml +19 -26
- data/config/locales/fr-LU.yml +534 -0
- data/config/locales/fr.yml +19 -26
- data/config/locales/gl.yml +46 -24
- data/config/locales/hu.yml +5 -24
- data/config/locales/id-ID.yml +3 -24
- data/config/locales/is-IS.yml +2 -22
- data/config/locales/it.yml +117 -23
- data/config/locales/ja.yml +148 -58
- data/config/locales/lb-LU.yml +1 -0
- data/config/locales/lv.yml +3 -24
- data/config/locales/nl.yml +70 -25
- data/config/locales/no.yml +5 -26
- data/config/locales/pl.yml +22 -33
- data/config/locales/pt-BR.yml +178 -23
- data/config/locales/pt.yml +3 -24
- data/config/locales/ro-RO.yml +38 -27
- data/config/locales/ru.yml +3 -24
- data/config/locales/sk.yml +3 -24
- data/config/locales/sv.yml +36 -27
- data/config/locales/tr-TR.yml +5 -27
- data/config/locales/uk.yml +3 -24
- data/config/locales/zh-CN.yml +3 -24
- data/db/migrate/20210217124802_add_registration_custom_content_to_meetings.rb +8 -0
- data/db/migrate/20210413050756_add_published_at_to_meetings.rb +7 -0
- data/db/migrate/20210413050917_update_published_at_to_existing_meetings.rb +20 -0
- data/db/migrate/20210430123416_add_public_participation_to_decidim_meetings_registrations.rb +7 -0
- data/db/migrate/20210506180226_merge_meetings_minutes_into_meetings_table.rb +48 -0
- data/db/migrate/20210512055802_create_decidim_meetings_polls.rb +10 -0
- data/db/migrate/20210512100333_drop_decidim_meetings_minutes_table.rb +77 -0
- data/db/migrate/20210518133236_merge_minutes_with_closing_report_in_meetings_table.rb +57 -0
- data/db/migrate/20210520084247_create_decidim_meetings_questionnaires.rb +11 -0
- data/db/migrate/20210520084253_create_decidim_meetings_questions.rb +15 -0
- data/db/migrate/20210520084321_create_decidim_meetings_answers.rb +13 -0
- data/db/migrate/20210520084330_create_decidim_meetings_answer_options.rb +10 -0
- data/db/migrate/20210520084337_create_decidim_meetings_answer_choices.rb +13 -0
- data/db/migrate/20210520134834_add_status_to_meetings_questions.rb +7 -0
- data/db/migrate/20210602040614_add_setting_embed_iframe_to_meetings.rb +7 -0
- data/lib/decidim/api/linked_resources_interface.rb +8 -1
- data/lib/decidim/api/meeting_type.rb +19 -4
- data/lib/decidim/api/meetings_type.rb +2 -2
- data/lib/decidim/meetings/admin_engine.rb +5 -1
- data/lib/decidim/meetings/api.rb +0 -1
- data/lib/decidim/meetings/component.rb +41 -6
- data/lib/decidim/meetings/data_portability_user_answers_serializer.rb +33 -0
- data/lib/decidim/meetings/engine.rb +10 -11
- data/lib/decidim/meetings/meeting_serializer.rb +1 -0
- data/lib/decidim/meetings/polls.rb +10 -0
- data/lib/decidim/meetings/test/factories.rb +85 -9
- data/lib/decidim/meetings/user_answers_serializer.rb +47 -0
- data/lib/decidim/meetings/version.rb +1 -1
- data/lib/decidim/meetings.rb +10 -0
- metadata +103 -113
- data/app/assets/config/decidim_meetings_manifest.js +0 -9
- data/app/assets/javascripts/decidim/meetings/admin/agendas.js.es6 +0 -158
- data/app/assets/javascripts/decidim/meetings/meetings_form.js.es6 +0 -54
- data/app/commands/decidim/meetings/admin/create_minutes.rb +0 -55
- data/app/commands/decidim/meetings/admin/update_minutes.rb +0 -63
- data/app/controllers/decidim/meetings/admin/minutes_controller.rb +0 -69
- data/app/forms/decidim/meetings/admin/minutes_form.rb +0 -20
- data/app/models/decidim/meetings/minutes.rb +0 -22
- data/app/presenters/decidim/meetings/admin_log/minutes_presenter.rb +0 -42
- data/app/views/decidim/meetings/admin/minutes/_form.html.erb +0 -23
- data/app/views/decidim/meetings/admin/minutes/edit.html.erb +0 -7
- data/app/views/decidim/meetings/admin/minutes/new.html.erb +0 -7
- data/app/views/decidim/meetings/meetings/_online_meeting_link.html.erb +0 -11
- data/config/locales/ja-JP.yml +0 -494
- data/lib/decidim/api/minutes_type.rb +0 -20
@@ -8,6 +8,9 @@ module Decidim
|
|
8
8
|
include TranslatableAttributes
|
9
9
|
|
10
10
|
translatable_attribute :closing_report, String
|
11
|
+
attribute :video_url, String
|
12
|
+
attribute :audio_url, String
|
13
|
+
attribute :closing_visible, Boolean, default: true
|
11
14
|
attribute :attendees_count, Integer, default: 0
|
12
15
|
attribute :contributions_count, Integer, default: 0
|
13
16
|
attribute :attending_organizations, String
|
@@ -18,7 +21,6 @@ module Decidim
|
|
18
21
|
validates :closing_report, translatable_presence: true
|
19
22
|
validates :attendees_count, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
20
23
|
validates :contributions_count, numericality: true, allow_blank: true
|
21
|
-
validates :attending_organizations, presence: true
|
22
24
|
|
23
25
|
# Private: Gets the proposals from the meeting and injects them to the form.
|
24
26
|
#
|
@@ -22,11 +22,14 @@ module Decidim
|
|
22
22
|
attribute :registration_type, String
|
23
23
|
attribute :registration_url, String
|
24
24
|
attribute :available_slots, Integer, default: 0
|
25
|
+
attribute :customize_registration_email, Boolean
|
26
|
+
attribute :show_embedded_iframe, Boolean, default: false
|
25
27
|
|
26
28
|
translatable_attribute :title, String
|
27
29
|
translatable_attribute :description, String
|
28
30
|
translatable_attribute :location, String
|
29
31
|
translatable_attribute :location_hints, String
|
32
|
+
translatable_attribute :registration_email_custom_content, String
|
30
33
|
|
31
34
|
validates :title, translatable_presence: true
|
32
35
|
validates :description, translatable_presence: true
|
@@ -38,7 +41,7 @@ module Decidim
|
|
38
41
|
|
39
42
|
validates :address, presence: true, if: ->(form) { form.needs_address? }
|
40
43
|
validates :address, geocoding: true, if: ->(form) { form.has_address? && !form.geocoded? && form.needs_address? }
|
41
|
-
validates :online_meeting_url,
|
44
|
+
validates :online_meeting_url, url: true, if: ->(form) { form.online_meeting? || form.hybrid_meeting? }
|
42
45
|
validates :start_time, presence: true, date: { before: :end_time }
|
43
46
|
validates :end_time, presence: true, date: { after: :start_time }
|
44
47
|
|
@@ -47,6 +50,7 @@ module Decidim
|
|
47
50
|
validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
|
48
51
|
validates :decidim_scope_id, scope_belongs_to_component: true, if: ->(form) { form.decidim_scope_id.present? }
|
49
52
|
validates :clean_type_of_meeting, presence: true
|
53
|
+
validate :embeddable_meeting_url
|
50
54
|
|
51
55
|
delegate :categories, to: :current_component
|
52
56
|
|
@@ -150,6 +154,13 @@ module Decidim
|
|
150
154
|
]
|
151
155
|
end
|
152
156
|
end
|
157
|
+
|
158
|
+
def embeddable_meeting_url
|
159
|
+
if online_meeting_url.present? && show_embedded_iframe
|
160
|
+
embedder_service = Decidim::Meetings::MeetingIframeEmbedder.new(online_meeting_url)
|
161
|
+
errors.add(:show_embedded_iframe, :not_embeddable) unless embedder_service.embeddable?
|
162
|
+
end
|
163
|
+
end
|
153
164
|
end
|
154
165
|
end
|
155
166
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Meetings
|
5
|
+
module Admin
|
6
|
+
# This class holds a Form to update questionnaire questions from Decidim's admin panel.
|
7
|
+
class QuestionForm < Decidim::Form
|
8
|
+
include TranslatableAttributes
|
9
|
+
|
10
|
+
attribute :position, Integer
|
11
|
+
attribute :mandatory, Boolean, default: false
|
12
|
+
attribute :question_type, String
|
13
|
+
attribute :answer_options, Array[AnswerOptionForm]
|
14
|
+
attribute :max_choices, Integer
|
15
|
+
attribute :deleted, Boolean, default: false
|
16
|
+
|
17
|
+
translatable_attribute :body, String
|
18
|
+
translatable_attribute :description, String
|
19
|
+
|
20
|
+
validates :position, numericality: { greater_than_or_equal_to: 0 }
|
21
|
+
validates :question_type, inclusion: { in: Decidim::Meetings::Question::QUESTION_TYPES }
|
22
|
+
validates :max_choices, numericality: { only_integer: true, greater_than: 1, less_than_or_equal_to: ->(form) { form.number_of_options } }, allow_blank: true
|
23
|
+
validates :body, translatable_presence: true, if: :requires_body?
|
24
|
+
validates :answer_options, presence: true
|
25
|
+
|
26
|
+
def to_param
|
27
|
+
return id if id.present?
|
28
|
+
|
29
|
+
"questionnaire-question-id"
|
30
|
+
end
|
31
|
+
|
32
|
+
def number_of_options
|
33
|
+
answer_options.size
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def requires_body?
|
39
|
+
!deleted
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Meetings
|
5
|
+
module Admin
|
6
|
+
# This class holds a Form to update questionnaires from Decidim's admin panel.
|
7
|
+
class QuestionnaireForm < Decidim::Form
|
8
|
+
attribute :published_at, Decidim::Attributes::TimeWithZone
|
9
|
+
attribute :questions, Array[Decidim::Meetings::Admin::QuestionForm]
|
10
|
+
|
11
|
+
def map_model(model)
|
12
|
+
self.questions = model.questions.map do |question|
|
13
|
+
Decidim::Meetings::Admin::QuestionForm.from_model(question)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Meetings
|
5
|
+
# This class holds a Form to save the chosen option for an answer
|
6
|
+
class AnswerChoiceForm < Decidim::Form
|
7
|
+
attribute :body, String
|
8
|
+
attribute :position, Integer
|
9
|
+
attribute :answer_option_id, Integer
|
10
|
+
|
11
|
+
validates :answer_option_id, presence: true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Meetings
|
5
|
+
# This class holds a Form to save the questionnaire answers from Decidim's public page
|
6
|
+
class AnswerForm < Decidim::Form
|
7
|
+
include Decidim::TranslationsHelper
|
8
|
+
|
9
|
+
attribute :question_id, String
|
10
|
+
attribute :body, String
|
11
|
+
attribute :choices, Array[AnswerChoiceForm]
|
12
|
+
attribute :current_user, Decidim::User
|
13
|
+
attribute :answer, Decidim::Meetings::Answer
|
14
|
+
|
15
|
+
validates :selected_choices, presence: true
|
16
|
+
validate :max_choices, if: -> { question.max_choices }
|
17
|
+
|
18
|
+
attr_writer :question
|
19
|
+
|
20
|
+
def question
|
21
|
+
@question ||= Decidim::Meetings::Question.find(question_id)
|
22
|
+
end
|
23
|
+
|
24
|
+
def answer
|
25
|
+
@answer ||= Decidim::Meetings::Answer.find_by(decidim_user_id: current_user.id, decidim_question_id: question_id) if current_user
|
26
|
+
end
|
27
|
+
|
28
|
+
def label(idx)
|
29
|
+
base = "#{idx + 1}. #{translated_attribute(question.body)}"
|
30
|
+
base += " (#{max_choices_label})" if question.max_choices
|
31
|
+
base
|
32
|
+
end
|
33
|
+
|
34
|
+
# Public: Map the correct fields.
|
35
|
+
#
|
36
|
+
# Returns nothing.
|
37
|
+
def map_model(model)
|
38
|
+
self.question_id = model.decidim_question_id
|
39
|
+
self.question = model.question
|
40
|
+
|
41
|
+
self.choices = model.choices.map do |choice|
|
42
|
+
AnswerChoiceForm.from_model(choice)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def selected_choices
|
47
|
+
choices.select(&:body)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def max_choices
|
53
|
+
errors.add(:choices, :too_many) if selected_choices.size > question.max_choices
|
54
|
+
end
|
55
|
+
|
56
|
+
def mandatory_label
|
57
|
+
"*"
|
58
|
+
end
|
59
|
+
|
60
|
+
def max_choices_label
|
61
|
+
I18n.t("questionnaires.question.max_choices", scope: "decidim.forms", n: question.max_choices)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -8,8 +8,12 @@ module Decidim
|
|
8
8
|
attribute :proposal_ids, Array[Integer]
|
9
9
|
attribute :proposals
|
10
10
|
attribute :closed_at, Decidim::Attributes::TimeWithZone, default: ->(_form, _attribute) { Time.current }
|
11
|
+
attribute :attendees_count, Integer, default: 0
|
11
12
|
|
12
13
|
validates :closing_report, presence: true
|
14
|
+
validates :attendees_count,
|
15
|
+
presence: true,
|
16
|
+
numericality: { greater_than_or_equal_to: 0, only_integer: true }
|
13
17
|
|
14
18
|
# Private: Gets the proposals from the meeting and injects them to the form.
|
15
19
|
#
|
@@ -24,6 +24,7 @@ module Decidim
|
|
24
24
|
attribute :registration_url, String
|
25
25
|
attribute :available_slots, Integer, default: 0
|
26
26
|
attribute :registration_terms, String
|
27
|
+
attribute :show_embedded_iframe, Boolean, default: false
|
27
28
|
|
28
29
|
validates :title, presence: true
|
29
30
|
validates :description, presence: true
|
@@ -44,6 +45,7 @@ module Decidim
|
|
44
45
|
validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
|
45
46
|
validates :decidim_scope_id, scope_belongs_to_component: true, if: ->(form) { form.decidim_scope_id.present? }
|
46
47
|
validates :clean_type_of_meeting, presence: true
|
48
|
+
validate :embeddable_meeting_url
|
47
49
|
|
48
50
|
delegate :categories, to: :current_component
|
49
51
|
|
@@ -141,6 +143,13 @@ module Decidim
|
|
141
143
|
def registrations_enabled
|
142
144
|
on_this_platform?
|
143
145
|
end
|
146
|
+
|
147
|
+
def embeddable_meeting_url
|
148
|
+
if online_meeting_url.present? && show_embedded_iframe
|
149
|
+
embedder_service = Decidim::Meetings::MeetingIframeEmbedder.new(online_meeting_url)
|
150
|
+
errors.add(:show_embedded_iframe, :not_embeddable) unless embedder_service.embeddable?
|
151
|
+
end
|
152
|
+
end
|
144
153
|
end
|
145
154
|
end
|
146
155
|
end
|
@@ -17,8 +17,9 @@ module Decidim
|
|
17
17
|
origin_values = []
|
18
18
|
origin_values << TreePoint.new("official", t("decidim.meetings.meetings.filters.origin_values.official"))
|
19
19
|
origin_values << TreePoint.new("citizens", t("decidim.meetings.meetings.filters.origin_values.citizens")) # todo
|
20
|
-
|
21
|
-
|
20
|
+
if current_organization.user_groups_enabled?
|
21
|
+
origin_values << TreePoint.new("user_group", t("decidim.meetings.meetings.filters.origin_values.user_groups")) # todo
|
22
|
+
end
|
22
23
|
# if current_organization.user_groups_enabled? and component_settings enabled enabled
|
23
24
|
|
24
25
|
TreeNode.new(
|
@@ -119,6 +119,27 @@ module Decidim
|
|
119
119
|
def current_user_groups?
|
120
120
|
current_organization.user_groups_enabled? && Decidim::UserGroups::ManageableUserGroups.for(current_user).verified.any?
|
121
121
|
end
|
122
|
+
|
123
|
+
# Public: URL to create an event in Google Calendars based on meeting
|
124
|
+
# data.
|
125
|
+
#
|
126
|
+
# meeting - a Decidim::Meeting instance.
|
127
|
+
#
|
128
|
+
# Returns a String.
|
129
|
+
def google_calendar_event_url(meeting)
|
130
|
+
meeting_url = resource_locator(meeting).url
|
131
|
+
meeting = present(meeting)
|
132
|
+
params = {
|
133
|
+
text: meeting.title,
|
134
|
+
dates: meeting.dates_param,
|
135
|
+
details: I18n.t(
|
136
|
+
"decidim.meetings.meetings.calendar_modal.full_details_html",
|
137
|
+
link: link_to(meeting_url, meeting_url)
|
138
|
+
)
|
139
|
+
}
|
140
|
+
base_url = "https://calendar.google.com/calendar/u/0/r/eventedit"
|
141
|
+
"#{base_url}?#{params.to_param}"
|
142
|
+
end
|
122
143
|
end
|
123
144
|
end
|
124
145
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Meetings
|
5
|
+
# The data store for an Answer in the Decidim::Meetings
|
6
|
+
class Answer < Meetings::ApplicationRecord
|
7
|
+
include Decidim::DataPortability
|
8
|
+
|
9
|
+
belongs_to :user, class_name: "Decidim::User", foreign_key: "decidim_user_id", optional: true
|
10
|
+
belongs_to :questionnaire, class_name: "Decidim::Meetings::Questionnaire", foreign_key: "decidim_questionnaire_id"
|
11
|
+
belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id"
|
12
|
+
|
13
|
+
has_many :choices,
|
14
|
+
class_name: "AnswerChoice",
|
15
|
+
foreign_key: "decidim_answer_id",
|
16
|
+
dependent: :destroy,
|
17
|
+
inverse_of: :answer
|
18
|
+
|
19
|
+
validate :user_questionnaire_same_organization
|
20
|
+
validate :question_belongs_to_questionnaire
|
21
|
+
|
22
|
+
def self.user_collection(user)
|
23
|
+
where(decidim_user_id: user.id)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.export_serializer
|
27
|
+
Decidim::Meetings::DataPortabilityUserAnswersSerializer
|
28
|
+
end
|
29
|
+
|
30
|
+
def organization
|
31
|
+
user.organization if user.present?
|
32
|
+
questionnaire&.questionnaire_for.try(:organization)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def user_questionnaire_same_organization
|
38
|
+
return if user.nil? || user&.organization == questionnaire.questionnaire_for&.organization
|
39
|
+
|
40
|
+
errors.add(:user, :invalid)
|
41
|
+
end
|
42
|
+
|
43
|
+
def question_belongs_to_questionnaire
|
44
|
+
errors.add(:questionnaire, :invalid) if question&.questionnaire != questionnaire
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Meetings
|
5
|
+
class AnswerChoice < Meetings::ApplicationRecord
|
6
|
+
belongs_to :answer,
|
7
|
+
class_name: "Decidim::Meetings::Answer",
|
8
|
+
foreign_key: "decidim_answer_id"
|
9
|
+
|
10
|
+
belongs_to :answer_option,
|
11
|
+
class_name: "Decidim::Meetings::AnswerOption",
|
12
|
+
foreign_key: "decidim_answer_option_id"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Meetings
|
5
|
+
class AnswerOption < Meetings::ApplicationRecord
|
6
|
+
include Decidim::TranslatableResource
|
7
|
+
|
8
|
+
default_scope { order(arel_table[:id].asc) }
|
9
|
+
|
10
|
+
translatable_fields :body
|
11
|
+
|
12
|
+
belongs_to :question, class_name: "Decidim::Meetings::Question", foreign_key: "decidim_question_id"
|
13
|
+
has_many :choices,
|
14
|
+
class_name: "AnswerChoice",
|
15
|
+
foreign_key: "decidim_answer_option_id",
|
16
|
+
dependent: :destroy,
|
17
|
+
inverse_of: :answer_option
|
18
|
+
|
19
|
+
def translated_body
|
20
|
+
Decidim::Forms::AnswerOptionPresenter.new(self).translated_body
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -13,7 +13,7 @@ module Decidim
|
|
13
13
|
include Decidim::ScopableResource
|
14
14
|
include Decidim::HasCategory
|
15
15
|
include Decidim::Followable
|
16
|
-
include Decidim::Comments::
|
16
|
+
include Decidim::Comments::CommentableWithComponent
|
17
17
|
include Decidim::Searchable
|
18
18
|
include Decidim::Traceable
|
19
19
|
include Decidim::Loggable
|
@@ -23,6 +23,7 @@ module Decidim
|
|
23
23
|
include Decidim::Reportable
|
24
24
|
include Decidim::Authorable
|
25
25
|
include Decidim::TranslatableResource
|
26
|
+
include Decidim::Publicable
|
26
27
|
|
27
28
|
TYPE_OF_MEETING = %w(in_person online hybrid).freeze
|
28
29
|
REGISTRATION_TYPE = %w(registration_disabled on_this_platform on_different_platform).freeze
|
@@ -32,8 +33,16 @@ module Decidim
|
|
32
33
|
has_many :registrations, class_name: "Decidim::Meetings::Registration", foreign_key: "decidim_meeting_id", dependent: :destroy
|
33
34
|
has_many :invites, class_name: "Decidim::Meetings::Invite", foreign_key: "decidim_meeting_id", dependent: :destroy
|
34
35
|
has_many :services, class_name: "Decidim::Meetings::Service", foreign_key: "decidim_meeting_id", dependent: :destroy
|
35
|
-
has_one :minutes, class_name: "Decidim::Meetings::Minutes", foreign_key: "decidim_meeting_id", dependent: :destroy
|
36
36
|
has_one :agenda, class_name: "Decidim::Meetings::Agenda", foreign_key: "decidim_meeting_id", dependent: :destroy
|
37
|
+
has_one :poll, class_name: "Decidim::Meetings::Poll", foreign_key: "decidim_meeting_id", dependent: :destroy
|
38
|
+
has_many(
|
39
|
+
:public_participants,
|
40
|
+
-> { merge(Registration.public_participant) },
|
41
|
+
through: :registrations,
|
42
|
+
class_name: "Decidim::User",
|
43
|
+
foreign_key: :decidim_user_id,
|
44
|
+
source: :user
|
45
|
+
)
|
37
46
|
|
38
47
|
component_manifest_name "meetings"
|
39
48
|
|
@@ -41,42 +50,54 @@ module Decidim
|
|
41
50
|
|
42
51
|
geocoded_by :address
|
43
52
|
|
53
|
+
scope :published, -> { where.not(published_at: nil) }
|
44
54
|
scope :past, -> { where(arel_table[:end_time].lteq(Time.current)) }
|
45
55
|
scope :upcoming, -> { where(arel_table[:end_time].gteq(Time.current)) }
|
46
56
|
|
47
57
|
scope :visible_meeting_for, lambda { |user|
|
48
|
-
(all.distinct if user&.admin?) ||
|
58
|
+
(all.published.distinct if user&.admin?) ||
|
49
59
|
if user.present?
|
50
|
-
spaces =
|
51
|
-
|
52
|
-
|
60
|
+
spaces = Decidim.participatory_space_registry.manifests.map do |manifest|
|
61
|
+
{
|
62
|
+
name: manifest.model_class_name.constantize.table_name.singularize,
|
63
|
+
class_name: manifest.model_class_name
|
64
|
+
}
|
65
|
+
end
|
66
|
+
user_role_queries = spaces.map do |space|
|
67
|
+
roles_table = "#{space[:name]}_user_roles"
|
68
|
+
next unless connection.table_exists?(roles_table)
|
69
|
+
|
53
70
|
"SELECT decidim_components.id FROM decidim_components
|
54
71
|
WHERE CONCAT(decidim_components.participatory_space_id, '-', decidim_components.participatory_space_type)
|
55
72
|
IN
|
56
|
-
(SELECT CONCAT(
|
57
|
-
FROM
|
73
|
+
(SELECT CONCAT(#{roles_table}.#{space[:name]}_id, '-#{space[:class_name]}')
|
74
|
+
FROM #{roles_table} WHERE #{roles_table}.decidim_user_id = ?)
|
58
75
|
"
|
59
76
|
end
|
60
77
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
78
|
+
query = "
|
79
|
+
decidim_meetings_meetings.private_meeting = ?
|
80
|
+
OR decidim_meetings_meetings.transparent = ?
|
81
|
+
OR decidim_meetings_meetings.id IN (
|
82
|
+
SELECT decidim_meetings_registrations.decidim_meeting_id FROM decidim_meetings_registrations WHERE decidim_meetings_registrations.decidim_user_id = ?
|
83
|
+
)
|
84
|
+
OR decidim_meetings_meetings.decidim_component_id IN (
|
85
|
+
SELECT decidim_components.id FROM decidim_components
|
67
86
|
WHERE CONCAT(decidim_components.participatory_space_id, '-', decidim_components.participatory_space_type)
|
68
87
|
IN
|
69
88
|
(SELECT CONCAT(decidim_participatory_space_private_users.privatable_to_id, '-', decidim_participatory_space_private_users.privatable_to_type)
|
70
89
|
FROM decidim_participatory_space_private_users WHERE decidim_participatory_space_private_users.decidim_user_id = ?)
|
71
90
|
)
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
91
|
+
"
|
92
|
+
if user_role_queries.any?
|
93
|
+
query = "#{query} OR decidim_meetings_meetings.decidim_component_id IN
|
94
|
+
(#{user_role_queries.compact.join(" UNION ")})
|
95
|
+
"
|
96
|
+
end
|
97
|
+
|
98
|
+
where(query, false, true, user.id, user.id, *user_role_queries.compact.map { user.id }).published.distinct
|
78
99
|
else
|
79
|
-
visible
|
100
|
+
published.visible
|
80
101
|
end
|
81
102
|
}
|
82
103
|
|
@@ -84,6 +105,7 @@ module Decidim
|
|
84
105
|
|
85
106
|
TYPE_OF_MEETING.each do |type|
|
86
107
|
scope type.to_sym, -> { where(type_of_meeting: type.to_sym) }
|
108
|
+
scope "not_#{type}".to_sym, -> { where.not(type_of_meeting: type.to_sym) }
|
87
109
|
end
|
88
110
|
|
89
111
|
searchable_fields({
|
@@ -93,8 +115,8 @@ module Decidim
|
|
93
115
|
D: [:description, :address],
|
94
116
|
datetime: :start_time
|
95
117
|
},
|
96
|
-
index_on_create: ->(meeting) { meeting.visible? },
|
97
|
-
index_on_update: ->(meeting) { meeting.visible? })
|
118
|
+
index_on_create: ->(meeting) { meeting.visible? && meeting.published? },
|
119
|
+
index_on_update: ->(meeting) { meeting.visible? && meeting.published? })
|
98
120
|
|
99
121
|
# we create a salt for the meeting only on new meetings to prevent changing old IDs for existing (Ether)PADs
|
100
122
|
before_create :set_default_salt
|
@@ -145,14 +167,8 @@ module Decidim
|
|
145
167
|
registrations.where(user: user).any?
|
146
168
|
end
|
147
169
|
|
148
|
-
|
149
|
-
|
150
|
-
component.settings.comments_enabled?
|
151
|
-
end
|
152
|
-
|
153
|
-
# Public: Overrides the `accepts_new_comments?` Commentable concern method.
|
154
|
-
def accepts_new_comments?
|
155
|
-
commentable? && !component.current_settings.comments_blocked
|
170
|
+
def maps_enabled?
|
171
|
+
component.settings.maps_enabled?
|
156
172
|
end
|
157
173
|
|
158
174
|
# Public: Overrides the `allow_resource_permissions?` Resourceable concern method.
|
@@ -175,11 +191,6 @@ module Decidim
|
|
175
191
|
followers
|
176
192
|
end
|
177
193
|
|
178
|
-
# Public: Whether the object can have new comments or not.
|
179
|
-
def user_allowed_to_comment?(user)
|
180
|
-
can_participate?(user)
|
181
|
-
end
|
182
|
-
|
183
194
|
def can_participate?(user)
|
184
195
|
can_participate_in_space?(user) && can_participate_in_meeting?(user)
|
185
196
|
end
|
@@ -216,6 +227,8 @@ module Decidim
|
|
216
227
|
end
|
217
228
|
|
218
229
|
def authored_proposals
|
230
|
+
return [] unless Decidim::Meetings.enable_proposal_linking
|
231
|
+
|
219
232
|
Decidim::Proposals::Proposal
|
220
233
|
.joins(:coauthorships)
|
221
234
|
.where(
|
@@ -241,12 +254,10 @@ module Decidim
|
|
241
254
|
[normalized_author.name]
|
242
255
|
end
|
243
256
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
def online_meeting?
|
249
|
-
type_of_meeting == "online"
|
257
|
+
TYPE_OF_MEETING.each do |type|
|
258
|
+
define_method("#{type}_meeting?") do
|
259
|
+
type_of_meeting == type
|
260
|
+
end
|
250
261
|
end
|
251
262
|
|
252
263
|
def registration_disabled?
|
@@ -269,6 +280,41 @@ module Decidim
|
|
269
280
|
!!attendees_count && attendees_count.positive?
|
270
281
|
end
|
271
282
|
|
283
|
+
def self.sort_by_translated_title_asc
|
284
|
+
field = Arel::Nodes::InfixOperation.new("->>", arel_table[:title], Arel::Nodes.build_quoted(I18n.locale))
|
285
|
+
order(Arel::Nodes::InfixOperation.new("", field, Arel.sql("ASC")))
|
286
|
+
end
|
287
|
+
|
288
|
+
def self.sort_by_translated_title_desc
|
289
|
+
field = Arel::Nodes::InfixOperation.new("->>", arel_table[:title], Arel::Nodes.build_quoted(I18n.locale))
|
290
|
+
order(Arel::Nodes::InfixOperation.new("", field, Arel.sql("DESC")))
|
291
|
+
end
|
292
|
+
|
293
|
+
ransacker :type do
|
294
|
+
Arel.sql(%("decidim_meetings_meetings"."type_of_meeting"))
|
295
|
+
end
|
296
|
+
|
297
|
+
ransacker :title do
|
298
|
+
Arel.sql(%{cast("decidim_meetings_meetings"."title" as text)})
|
299
|
+
end
|
300
|
+
|
301
|
+
ransacker :id_string do
|
302
|
+
Arel.sql(%{cast("decidim_meetings_meetings"."id" as text)})
|
303
|
+
end
|
304
|
+
|
305
|
+
ransacker :is_upcoming do
|
306
|
+
Arel.sql("(start_time > NOW())")
|
307
|
+
end
|
308
|
+
|
309
|
+
ransacker :origin do
|
310
|
+
Arel.sql("CASE
|
311
|
+
WHEN decidim_author_type = 'Decidim::Organization' THEN 'official'
|
312
|
+
WHEN decidim_author_type = 'Decidim::UserBaseEntity' AND decidim_user_group_id IS NOT NULL THEN 'user_group'
|
313
|
+
WHEN decidim_author_type = 'Decidim::UserBaseEntity' AND decidim_user_group_id IS NULL THEN 'citizen'
|
314
|
+
ELSE 'unknown' END
|
315
|
+
")
|
316
|
+
end
|
317
|
+
|
272
318
|
private
|
273
319
|
|
274
320
|
def can_participate_in_meeting?(user)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Meetings
|
5
|
+
class Poll < Meetings::ApplicationRecord
|
6
|
+
has_one :questionnaire,
|
7
|
+
class_name: "Decidim::Meetings::Questionnaire",
|
8
|
+
dependent: :destroy,
|
9
|
+
inverse_of: :questionnaire_for,
|
10
|
+
as: :questionnaire_for
|
11
|
+
|
12
|
+
belongs_to :meeting, foreign_key: "decidim_meeting_id", class_name: "Decidim::Meetings::Meeting"
|
13
|
+
|
14
|
+
delegate :organization, to: :meeting
|
15
|
+
|
16
|
+
QUESTION_TYPES = %w(single_option multiple_option).freeze
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|