decidim-meetings 0.29.2 → 0.30.0.rc1
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/cancel_registration_meeting_button/cancelation_modal.erb +1 -1
- data/app/cells/decidim/meetings/cancel_registration_meeting_button/show.erb +3 -1
- data/app/cells/decidim/meetings/cancel_registration_meeting_button_cell.rb +1 -1
- data/app/cells/decidim/meetings/dates_and_map/show.erb +1 -1
- data/app/cells/decidim/meetings/highlighted_meetings_for_component/show.erb +1 -1
- data/app/cells/decidim/meetings/join_meeting_button/show.erb +5 -2
- data/app/cells/decidim/meetings/meeting_card_metadata_cell.rb +3 -3
- data/app/cells/decidim/meetings/meeting_l_cell.rb +12 -0
- data/app/commands/decidim/meetings/admin/copy_meeting.rb +1 -2
- data/app/commands/decidim/meetings/admin/create_meeting.rb +8 -2
- data/app/commands/decidim/meetings/admin/update_meeting.rb +8 -2
- data/app/commands/decidim/meetings/create_meeting.rb +2 -2
- data/app/commands/decidim/meetings/update_meeting.rb +2 -3
- data/app/controllers/concerns/decidim/meetings/admin/filterable.rb +9 -5
- data/app/controllers/concerns/decidim/meetings/component_filterable.rb +1 -2
- data/app/controllers/decidim/meetings/admin/meetings_controller.rb +14 -22
- data/app/controllers/decidim/meetings/admin/registration_form_controller.rb +8 -0
- data/app/controllers/decidim/meetings/directory/meetings_controller.rb +2 -4
- data/app/controllers/decidim/meetings/meetings_controller.rb +40 -6
- data/app/forms/decidim/meetings/admin/meeting_form.rb +16 -30
- data/app/forms/decidim/meetings/base_meeting_form.rb +6 -0
- data/app/forms/decidim/meetings/meeting_form.rb +2 -30
- data/app/helpers/decidim/meetings/admin/application_helper.rb +11 -1
- data/app/helpers/decidim/meetings/application_helper.rb +35 -1
- data/app/helpers/decidim/meetings/directory/application_helper.rb +9 -48
- data/app/helpers/decidim/meetings/meetings_helper.rb +5 -0
- data/app/models/decidim/meetings/invite.rb +10 -0
- data/app/models/decidim/meetings/meeting.rb +23 -1
- data/app/models/decidim/meetings/meeting_link.rb +25 -0
- data/app/packs/entrypoints/decidim_meetings_admin.js +1 -0
- data/app/packs/src/decidim/meetings/admin/meetings_components_form.js +77 -0
- data/app/packs/src/decidim/meetings/admin/meetings_form.js +8 -0
- data/app/permissions/decidim/meetings/admin/permissions.rb +1 -1
- data/app/permissions/decidim/meetings/permissions.rb +13 -9
- data/app/presenters/decidim/meetings/admin_log/meeting_presenter.rb +1 -1
- data/app/presenters/decidim/meetings/meeting_presenter.rb +13 -1
- data/app/queries/decidim/meetings/filtered_meetings.rb +2 -2
- data/app/queries/decidim/meetings/metrics/meeting_followers_metric_measure.rb +2 -2
- data/app/queries/decidim/meetings/metrics/meetings_metric_manage.rb +6 -6
- data/app/serializers/decidim/meetings/base_download_your_data_serializer.rb +32 -0
- data/app/serializers/decidim/meetings/download_your_data_invite_serializer.rb +6 -26
- data/app/serializers/decidim/meetings/download_your_data_meeting_serializer.rb +15 -0
- data/app/serializers/decidim/meetings/download_your_data_registration_serializer.rb +6 -24
- data/app/views/decidim/meetings/admin/meetings/_component.html.erb +15 -0
- data/app/views/decidim/meetings/admin/meetings/_form.html.erb +7 -9
- data/app/views/decidim/meetings/admin/meetings/_linked_spaces.html.erb +53 -0
- data/app/views/decidim/meetings/admin/meetings/_meeting-tr.html.erb +42 -0
- data/app/views/decidim/meetings/admin/meetings/_meeting_actions.html.erb +70 -0
- data/app/views/decidim/meetings/admin/meetings/_meetings-thead.html.erb +26 -0
- data/app/views/decidim/meetings/admin/meetings/index.html.erb +16 -142
- data/app/views/decidim/meetings/admin/meetings/manage_trash.html.erb +23 -0
- data/app/views/decidim/meetings/admin/registration_form/edit_questions.html.erb +44 -0
- data/app/views/decidim/meetings/admin/registrations/edit.html.erb +1 -0
- data/app/views/decidim/meetings/directory/meetings/index.html.erb +1 -2
- data/app/views/decidim/meetings/live_events/show.html.erb +5 -5
- data/app/views/decidim/meetings/meetings/_form.html.erb +4 -8
- data/app/views/decidim/meetings/meetings/_meeting.html.erb +51 -26
- data/app/views/decidim/meetings/meetings/_meeting_actions.html.erb +34 -0
- data/app/views/decidim/meetings/meetings/_meeting_aside.html.erb +20 -59
- data/app/views/decidim/meetings/meetings/_meeting_poll_actions.html.erb +2 -5
- data/app/views/decidim/meetings/meetings/_schema_org_event_meeting.html.erb +3 -0
- data/app/views/decidim/meetings/meetings/index.html.erb +10 -2
- data/app/views/decidim/meetings/meetings/show.html.erb +5 -5
- data/app/views/decidim/meetings/polls/answers/index.html.erb +5 -5
- data/app/views/decidim/meetings/shared/_filters.html.erb +1 -13
- data/app/views/decidim/meetings/shared/_index.html.erb +1 -1
- data/app/views/decidim/meetings/shared/_index.js.erb +3 -2
- data/app/views/decidim/meetings/shared/_meetings_aside.html.erb +2 -2
- data/app/views/decidim/participatory_spaces/_conference_venues.html.erb +1 -1
- data/config/locales/ar.yml +17 -17
- data/config/locales/bg.yml +5 -25
- data/config/locales/ca.yml +137 -25
- data/config/locales/cs.yml +138 -28
- data/config/locales/de.yml +106 -26
- data/config/locales/el.yml +1 -22
- data/config/locales/en.yml +133 -21
- data/config/locales/es-MX.yml +137 -25
- data/config/locales/es-PY.yml +137 -25
- data/config/locales/es.yml +137 -25
- data/config/locales/eu.yml +138 -26
- data/config/locales/fi-plain.yml +138 -26
- data/config/locales/fi.yml +138 -26
- data/config/locales/fr-CA.yml +79 -26
- data/config/locales/fr.yml +79 -26
- data/config/locales/ga-IE.yml +0 -13
- data/config/locales/gl.yml +1 -12
- data/config/locales/hu.yml +1 -19
- data/config/locales/id-ID.yml +1 -13
- data/config/locales/is-IS.yml +0 -12
- data/config/locales/it.yml +1 -17
- data/config/locales/ja.yml +95 -25
- data/config/locales/lb.yml +0 -11
- data/config/locales/lt.yml +1 -23
- data/config/locales/lv.yml +1 -13
- data/config/locales/nl.yml +1 -17
- data/config/locales/no.yml +1 -16
- data/config/locales/pl.yml +5 -21
- data/config/locales/pt-BR.yml +1 -21
- data/config/locales/pt.yml +1 -17
- data/config/locales/ro-RO.yml +30 -17
- data/config/locales/ru.yml +1 -13
- data/config/locales/sk.yml +1 -13
- data/config/locales/sv.yml +74 -26
- data/config/locales/tr-TR.yml +1 -19
- data/config/locales/uk.yml +0 -12
- data/config/locales/zh-CN.yml +1 -18
- data/config/locales/zh-TW.yml +1 -21
- data/db/migrate/20181107175558_add_questionnaire_to_existing_meetings.rb +1 -1
- data/db/migrate/20200827153856_add_commentable_counter_cache_to_meetings.rb +1 -1
- data/db/migrate/20201016065302_fix_meetings_registration_terms.rb +1 -1
- data/db/migrate/20210310120731_add_followable_counter_cache_to_meetings.rb +1 -1
- data/db/migrate/20240712104245_create_decidim_meetings_meeting_link.rb +12 -0
- data/db/migrate/20240828103603_add_deleted_at_to_decidim_meetings_meetings.rb +8 -0
- data/decidim-meetings.gemspec +1 -1
- data/lib/decidim/api/agenda_item_type.rb +6 -7
- data/lib/decidim/api/agenda_type.rb +3 -4
- data/lib/decidim/api/meeting_type.rb +33 -41
- data/lib/decidim/api/meetings_type.rb +4 -5
- data/lib/decidim/api/service_type.rb +1 -1
- data/lib/decidim/meetings/admin_engine.rb +9 -1
- data/lib/decidim/meetings/component.rb +14 -4
- data/lib/decidim/meetings/download_your_data_user_answers_serializer.rb +13 -7
- data/lib/decidim/meetings/meeting_serializer.rb +70 -59
- data/lib/decidim/meetings/schema_org_event_meeting_serializer.rb +151 -0
- data/lib/decidim/meetings/seeds.rb +2 -4
- data/lib/decidim/meetings/test/factories.rb +8 -0
- data/lib/decidim/meetings/test/translated_event.rb +3 -3
- data/lib/decidim/meetings/version.rb +1 -1
- data/lib/decidim/meetings.rb +1 -0
- metadata +32 -21
- data/app/cells/decidim/meetings/meetings_map/show.erb +0 -16
- data/app/cells/decidim/meetings/meetings_map_cell.rb +0 -32
- data/app/commands/decidim/meetings/admin/destroy_meeting.rb +0 -21
- data/app/helpers/decidim/meetings/map_helper.rb +0 -21
- data/app/views/decidim/meetings/meetings/_actions.html.erb +0 -6
@@ -7,32 +7,53 @@ module Decidim
|
|
7
7
|
|
8
8
|
implements Decidim::Comments::CommentableInterface
|
9
9
|
implements Decidim::Core::AuthorableInterface
|
10
|
-
implements Decidim::Core::
|
11
|
-
implements Decidim::Core::ScopableInterface
|
10
|
+
implements Decidim::Core::TaxonomizableInterface
|
12
11
|
implements Decidim::Core::AttachableInterface
|
13
12
|
implements Decidim::Core::TimestampsInterface
|
14
13
|
implements Decidim::Meetings::ServicesInterface
|
15
14
|
implements Decidim::Meetings::LinkedResourcesInterface if Decidim::Meetings.enable_proposal_linking
|
16
15
|
implements Decidim::Forms::QuestionnaireEntityInterface
|
17
16
|
|
17
|
+
field :address, GraphQL::Types::String, "The physical address of this meeting (used for geolocation)", null: true
|
18
|
+
field :agenda, Decidim::Meetings::AgendaType, "Agenda for this meeting, if available", null: true
|
19
|
+
field :attendee_count, GraphQL::Types::Int, "Amount of attendees to this meeting", method: :attendees_count, null: true
|
20
|
+
field :attending_organizations, GraphQL::Types::String, "list of attending organizations", null: true
|
21
|
+
field :audio_url, GraphQL::Types::String, "URL for the audio of the session, if any", null: true
|
22
|
+
field :closed, GraphQL::Types::Boolean, "Whether this meeting is closed or not.", method: :closed?, null: false
|
23
|
+
field :closing_report, Decidim::Core::TranslatedFieldType, "The closing report of this meeting.", null: true
|
24
|
+
field :contribution_count, GraphQL::Types::Int, "Amount of contributions to this meeting", method: :contributions_count, null: true
|
25
|
+
field :coordinates, Decidim::Core::CoordinatesType, "Physical coordinates for this meeting", null: true
|
26
|
+
field :description, Decidim::Core::TranslatedFieldType, "The description of this meeting.", null: true
|
27
|
+
field :end_time, Decidim::Core::DateTimeType, "The time this meeting ends", null: false
|
18
28
|
field :id, GraphQL::Types::ID, "ID of this meeting", null: false
|
29
|
+
field :iframe_embed_type, GraphQL::Types::String, "The type of displaying of the online meeting URL", null: true
|
30
|
+
field :is_withdrawn, GraphQL::Types::Boolean, "Whether this meeting is withdrawn or not.", method: :withdrawn?, null: false
|
31
|
+
field :location, Decidim::Core::TranslatedFieldType, "The location of this meeting (free format)", null: true
|
32
|
+
field :location_hints, Decidim::Core::TranslatedFieldType, "The location of this meeting (free format)", null: true
|
33
|
+
field :online_meeting_url, GraphQL::Types::String, "The URL of the meeting (when the type is online)", null: false
|
34
|
+
field :private_meeting, GraphQL::Types::Boolean, "Whether the meeting is private or not (it can only be true if transparent)", null: false
|
19
35
|
field :reference, GraphQL::Types::String, "Reference for this meeting", null: false
|
20
|
-
field :
|
21
|
-
field :
|
36
|
+
field :registration_form, Decidim::Forms::QuestionnaireType, description: "If registration requires to fill a form, this is the questionnaire", null: true
|
37
|
+
field :registration_form_enabled, GraphQL::Types::Boolean, "Whether the registrations have a form or not", null: false
|
38
|
+
field :registration_terms, Decidim::Core::TranslatedFieldType, "The registration terms", null: true
|
39
|
+
field :registrations_enabled, GraphQL::Types::Boolean, "Whether the registrations are enabled or not", null: false
|
40
|
+
field :remaining_slots, GraphQL::Types::Int, "Amount of slots available for this meeting", null: true
|
22
41
|
field :start_time, Decidim::Core::DateTimeType, "The time this meeting starts", null: false
|
23
|
-
field :
|
24
|
-
field :
|
42
|
+
field :title, Decidim::Core::TranslatedFieldType, "The title of this meeting.", null: false
|
43
|
+
field :transparent, GraphQL::Types::Boolean, "For private meetings, information is public if transparent", null: false
|
44
|
+
field :type_of_meeting, GraphQL::Types::String, "The type of the meeting (online or in-person)", null: false
|
45
|
+
field :video_url, GraphQL::Types::String, "URL for the video of the session, if any", null: true
|
46
|
+
field :withdrawn, GraphQL::Types::Boolean, "Whether this meeting has been withdrawn or not", method: :withdrawn?, null: true
|
47
|
+
field :withdrawn_at, Decidim::Core::DateTimeType, description: "The date and time this meeting was withdrawn", null: true
|
48
|
+
|
49
|
+
def registration_form
|
50
|
+
object.questionnaire if object.registration_form_enabled?
|
51
|
+
end
|
25
52
|
|
26
53
|
def agenda
|
27
54
|
object.agenda if object.agenda&.visible?
|
28
55
|
end
|
29
56
|
|
30
|
-
field :closed, GraphQL::Types::Boolean, "Whether this meeting is closed or not.", method: :closed?, null: false
|
31
|
-
field :isWithdrawn, GraphQL::Types::Boolean, "Whether this meeting is withdrawn or not.", method: :withdrawn?, null: false
|
32
|
-
field :closing_report, Decidim::Core::TranslatedFieldType, "The closing report of this meeting.", null: true
|
33
|
-
field :video_url, GraphQL::Types::String, "URL for the video of the session, if any", null: true
|
34
|
-
field :audio_url, GraphQL::Types::String, "URL for the audio of the session, if any", null: true
|
35
|
-
|
36
57
|
def closing_report
|
37
58
|
object.closing_report if object.closing_visible?
|
38
59
|
end
|
@@ -45,38 +66,9 @@ module Decidim
|
|
45
66
|
object.audio_url if object.closing_visible?
|
46
67
|
end
|
47
68
|
|
48
|
-
field :withdrawn_at, Decidim::Core::DateTimeType, description: "The date and time this meeting was withdrawn", null: true
|
49
|
-
field :withdrawn, GraphQL::Types::Boolean, "Whether this meeting has been withdrawn or not", method: :withdrawn?, null: true
|
50
|
-
|
51
|
-
field :created_at, Decidim::Core::DateTimeType, description: "The date and time this minutes was created", null: true
|
52
|
-
field :updated_at, Decidim::Core::DateTimeType, description: "The date and time this minutes was updated", null: true
|
53
|
-
|
54
|
-
field :attending_organizations, GraphQL::Types::String, "list of attending organizations", null: true
|
55
|
-
field :attendee_count, GraphQL::Types::Int, "Amount of attendees to this meeting", method: :attendees_count, null: true
|
56
|
-
field :contribution_count, GraphQL::Types::Int, "Amount of contributions to this meeting", method: :contributions_count, null: true
|
57
|
-
|
58
|
-
field :private_meeting, GraphQL::Types::Boolean, "Whether the meeting is private or not (it can only be true if transparent)", null: false
|
59
|
-
field :transparent, GraphQL::Types::Boolean, "For private meetings, information is public if transparent", null: false
|
60
|
-
field :registrations_enabled, GraphQL::Types::Boolean, "Whether the registrations are enabled or not", null: false
|
61
|
-
field :registration_terms, Decidim::Core::TranslatedFieldType, "The registration terms", null: true
|
62
|
-
field :remaining_slots, GraphQL::Types::Int, "Amount of slots available for this meeting", null: true
|
63
|
-
field :registration_form_enabled, GraphQL::Types::Boolean, "Whether the registrations have a form or not", null: false
|
64
|
-
field :registration_form, Decidim::Forms::QuestionnaireType, description: "If registration requires to fill a form, this is the questionnaire", null: true
|
65
|
-
|
66
|
-
def registration_form
|
67
|
-
object.questionnaire if object.registration_form_enabled?
|
68
|
-
end
|
69
|
-
field :location, Decidim::Core::TranslatedFieldType, "The location of this meeting (free format)", null: true
|
70
|
-
field :location_hints, Decidim::Core::TranslatedFieldType, "The location of this meeting (free format)", null: true
|
71
|
-
field :address, GraphQL::Types::String, "The physical address of this meeting (used for geolocation)", null: true
|
72
|
-
field :coordinates, Decidim::Core::CoordinatesType, "Physical coordinates for this meeting", null: true
|
73
|
-
|
74
69
|
def coordinates
|
75
70
|
[object.latitude, object.longitude]
|
76
71
|
end
|
77
|
-
field :type_of_meeting, GraphQL::Types::String, "The type of the meeting (online or in-person)", null: false
|
78
|
-
field :online_meeting_url, GraphQL::Types::String, "The URL of the meeting (when the type is online)", null: false
|
79
|
-
field :iframe_embed_type, GraphQL::Types::String, "The type of displaying of the online meeting URL", null: true
|
80
72
|
|
81
73
|
def self.authorized?(object, context)
|
82
74
|
context[:meeting] = object
|
@@ -6,16 +6,15 @@ module Decidim
|
|
6
6
|
graphql_name "Meetings"
|
7
7
|
description "A meetings component of a participatory space."
|
8
8
|
|
9
|
-
field :
|
9
|
+
field :meeting, Decidim::Meetings::MeetingType, "A single Meeting object", null: true do
|
10
|
+
argument :id, GraphQL::Types::ID, "The id of the Meeting requested", required: true
|
11
|
+
end
|
12
|
+
field :meetings, Decidim::Meetings::MeetingType.connection_type, "A collection of Meetings", null: true, connection: true
|
10
13
|
|
11
14
|
def meetings
|
12
15
|
Meeting.published.visible.where(component: object).includes(:component)
|
13
16
|
end
|
14
17
|
|
15
|
-
field :meeting, Decidim::Meetings::MeetingType, null: true do
|
16
|
-
argument :id, GraphQL::Types::ID, required: true
|
17
|
-
end
|
18
|
-
|
19
18
|
def meeting(**args)
|
20
19
|
Meeting.published.visible.where(component: object).find_by(id: args[:id])
|
21
20
|
end
|
@@ -6,8 +6,8 @@ module Decidim
|
|
6
6
|
graphql_name "MeetingService"
|
7
7
|
description "A meeting service"
|
8
8
|
|
9
|
-
field :title, Decidim::Core::TranslatedFieldType, "The title for the service", null: true
|
10
9
|
field :description, Decidim::Core::TranslatedFieldType, "The description for the service", null: true
|
10
|
+
field :title, Decidim::Core::TranslatedFieldType, "The title for the service", null: true
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -18,13 +18,20 @@ module Decidim
|
|
18
18
|
member do
|
19
19
|
put :publish
|
20
20
|
put :unpublish
|
21
|
+
patch :soft_delete
|
22
|
+
patch :restore
|
21
23
|
end
|
22
24
|
resources :meeting_closes, only: [:edit, :update] do
|
23
25
|
get :proposals_picker, on: :collection
|
24
26
|
end
|
25
27
|
resource :registrations, only: [:edit, :update] do
|
26
28
|
resources :invites, only: [:index, :create]
|
27
|
-
resource :form, only: [:edit, :update], controller: "registration_form"
|
29
|
+
resource :form, only: [:edit, :update], controller: "registration_form" do
|
30
|
+
member do
|
31
|
+
get :edit_questions
|
32
|
+
patch :update_questions
|
33
|
+
end
|
34
|
+
end
|
28
35
|
collection do
|
29
36
|
get :export
|
30
37
|
post :validate_registration_code
|
@@ -35,6 +42,7 @@ module Decidim
|
|
35
42
|
resources :attachments, except: [:show]
|
36
43
|
resources :copies, controller: "meeting_copies", only: [:new, :create]
|
37
44
|
resource :poll, only: [:edit, :update], controller: "meetings_poll"
|
45
|
+
get :manage_trash, on: :collection
|
38
46
|
end
|
39
47
|
root to: "meetings#index"
|
40
48
|
end
|
@@ -8,7 +8,11 @@ Decidim.register_component(:meetings) do |component|
|
|
8
8
|
component.permissions_class_name = "Decidim::Meetings::Permissions"
|
9
9
|
|
10
10
|
component.query_type = "Decidim::Meetings::MeetingsType"
|
11
|
-
component.data_portable_entities = [
|
11
|
+
component.data_portable_entities = [
|
12
|
+
"Decidim::Meetings::Registration",
|
13
|
+
"Decidim::Meetings::Invite",
|
14
|
+
"Decidim::Meetings::Meeting"
|
15
|
+
]
|
12
16
|
|
13
17
|
component.on(:before_destroy) do |instance|
|
14
18
|
raise StandardError, "Cannot remove this component" if Decidim::Meetings::Meeting.where(component: instance).any?
|
@@ -38,13 +42,20 @@ Decidim.register_component(:meetings) do |component|
|
|
38
42
|
meetings.sum(:comments_count)
|
39
43
|
end
|
40
44
|
|
45
|
+
component.register_stat :attendees_count, primary: true, priority: Decidim::StatsRegistry::MEDIUM_PRIORITY do |components, start_at, end_at|
|
46
|
+
meetings = Decidim::Meetings::Meeting.closed.not_hidden.published.where(component: components, closing_visible: true)
|
47
|
+
meetings = meetings.where(closed_at: start_at..) if start_at.present?
|
48
|
+
meetings = meetings.where(closed_at: ..end_at) if end_at.present?
|
49
|
+
meetings.sum(:attendees_count)
|
50
|
+
end
|
51
|
+
|
41
52
|
component.exports :meetings do |exports|
|
42
53
|
exports.collection do |component_instance|
|
43
54
|
Decidim::Meetings::Meeting
|
44
55
|
.not_hidden
|
45
56
|
.visible
|
46
57
|
.where(component: component_instance)
|
47
|
-
.includes(:
|
58
|
+
.includes(:taxonomies, :attachments, component: { participatory_space: :organization })
|
48
59
|
end
|
49
60
|
|
50
61
|
exports.include_in_open_data = true
|
@@ -77,8 +88,7 @@ Decidim.register_component(:meetings) do |component|
|
|
77
88
|
component.actions = %w(join comment)
|
78
89
|
|
79
90
|
component.settings(:global) do |settings|
|
80
|
-
settings.attribute :
|
81
|
-
settings.attribute :scope_id, type: :scope
|
91
|
+
settings.attribute :taxonomy_filters, type: :taxonomy_filters
|
82
92
|
settings.attribute :announcement, type: :text, translated: true, editor: true
|
83
93
|
settings.attribute :default_registration_terms, type: :text, translated: true, editor: true
|
84
94
|
settings.attribute :comments_enabled, type: :boolean, default: true
|
@@ -8,16 +8,16 @@ module Decidim
|
|
8
8
|
def serialize
|
9
9
|
{
|
10
10
|
id: resource.id,
|
11
|
-
user: {
|
12
|
-
name: resource.user.name,
|
13
|
-
email: resource.user.email
|
14
|
-
},
|
15
11
|
questionnaire: {
|
16
|
-
id: resource.question.questionnaire.id
|
12
|
+
id: resource.question.questionnaire.id,
|
13
|
+
title: translated_attribute(resource.question.questionnaire.title),
|
14
|
+
description: translated_attribute(resource.question.questionnaire.description),
|
15
|
+
tos: translated_attribute(resource.question.questionnaire.tos)
|
17
16
|
},
|
18
17
|
question: {
|
19
18
|
id: resource.question.id,
|
20
|
-
body: translated_attribute(resource.question.body)
|
19
|
+
body: translated_attribute(resource.question.body),
|
20
|
+
description: translated_attribute(resource.question.description)
|
21
21
|
},
|
22
22
|
answer: normalize_body(resource)
|
23
23
|
}
|
@@ -26,7 +26,13 @@ module Decidim
|
|
26
26
|
private
|
27
27
|
|
28
28
|
def normalize_body(resource)
|
29
|
-
resource.choices.pluck(:body)
|
29
|
+
attachments_for(resource) || resource.body || resource.choices.pluck(:body)
|
30
|
+
end
|
31
|
+
|
32
|
+
def attachments_for(resource)
|
33
|
+
return if resource.attachments.blank?
|
34
|
+
|
35
|
+
resource.attachments.map(&:url)
|
30
36
|
end
|
31
37
|
end
|
32
38
|
end
|
@@ -8,63 +8,77 @@ module Decidim
|
|
8
8
|
include Decidim::ApplicationHelper
|
9
9
|
include Decidim::ResourceHelper
|
10
10
|
|
11
|
-
# Public: Initializes the serializer with a meeting.
|
12
|
-
def initialize(meeting)
|
13
|
-
@meeting = meeting
|
14
|
-
end
|
15
|
-
|
16
11
|
# Public: Exports a hash with the serialized data for this meeting.
|
17
12
|
def serialize
|
18
13
|
{
|
19
|
-
id:
|
20
|
-
category: {
|
21
|
-
id: meeting.category.try(:id),
|
22
|
-
name: meeting.category.try(:name)
|
23
|
-
},
|
24
|
-
scope: {
|
25
|
-
id: meeting.scope.try(:id),
|
26
|
-
name: meeting.scope.try(:name)
|
27
|
-
},
|
14
|
+
id: resource.id,
|
28
15
|
author: {
|
29
16
|
**author_fields
|
30
17
|
},
|
31
18
|
participatory_space: {
|
32
|
-
id:
|
33
|
-
url: Decidim::ResourceLocatorPresenter.new(
|
19
|
+
id: resource.participatory_space.id,
|
20
|
+
url: Decidim::ResourceLocatorPresenter.new(resource.participatory_space).url
|
34
21
|
},
|
22
|
+
taxonomies:,
|
35
23
|
component: { id: component.id },
|
36
|
-
title:
|
37
|
-
description:
|
38
|
-
start_time:
|
39
|
-
end_time:
|
40
|
-
attendees:
|
41
|
-
contributions:
|
42
|
-
organizations:
|
43
|
-
address:
|
44
|
-
location:
|
45
|
-
reference:
|
46
|
-
|
47
|
-
attachments: meeting.attachments.size,
|
48
|
-
followers: meeting.follows.size,
|
24
|
+
title: resource.title,
|
25
|
+
description: resource.description,
|
26
|
+
start_time: resource.start_time,
|
27
|
+
end_time: resource.end_time,
|
28
|
+
attendees: resource.attendees_count.to_i,
|
29
|
+
contributions: resource.contributions_count.to_i,
|
30
|
+
organizations: resource.attending_organizations,
|
31
|
+
address: resource.address,
|
32
|
+
location: include_location? ? resource.location : nil,
|
33
|
+
reference: resource.reference,
|
34
|
+
attachments: resource.attachments.size,
|
49
35
|
url:,
|
50
36
|
related_proposals:,
|
51
37
|
related_results:,
|
52
|
-
published:
|
53
|
-
withdrawn:
|
54
|
-
withdrawn_at:
|
38
|
+
published: resource.published_at.present?,
|
39
|
+
withdrawn: resource.withdrawn?,
|
40
|
+
withdrawn_at: resource.withdrawn_at,
|
41
|
+
location_hints: resource.location_hints,
|
42
|
+
created_at: resource.created_at,
|
43
|
+
updated_at: resource.updated_at,
|
44
|
+
latitude: resource.latitude,
|
45
|
+
longitude: resource.longitude,
|
46
|
+
follows_count: resource.follows_count,
|
47
|
+
private_meeting: resource.private_meeting,
|
48
|
+
transparent: resource.transparent,
|
49
|
+
registration_form_enabled: resource.registration_form_enabled,
|
50
|
+
comments: {
|
51
|
+
**comment_fields
|
52
|
+
},
|
53
|
+
online_meeting_url: resource.online_meeting_url,
|
54
|
+
closing_visible: resource.closing_visible,
|
55
|
+
closing_report: resource.closing_report,
|
56
|
+
attending_organizations: resource.attending_organizations,
|
57
|
+
registration_url: resource.registration_url,
|
58
|
+
decidim_user_group_id: resource.decidim_user_group_id,
|
59
|
+
decidim_author_type: resource.decidim_author_type,
|
60
|
+
video_url: resource.video_url,
|
61
|
+
audio_url: resource.audio_url,
|
62
|
+
closed_at: resource.closed_at,
|
63
|
+
registration_terms: resource.registration_terms,
|
64
|
+
available_slots: resource.available_slots,
|
65
|
+
registrations_enabled: resource.registrations_enabled,
|
66
|
+
customize_registration_email: resource.customize_registration_email,
|
67
|
+
type_of_meeting: resource.type_of_meeting,
|
68
|
+
iframe_access_level: resource.iframe_access_level,
|
69
|
+
iframe_embed_type: resource.iframe_embed_type,
|
70
|
+
reserved_slots: resource.reserved_slots,
|
71
|
+
registration_type: resource.registration_type
|
55
72
|
}
|
56
73
|
end
|
57
74
|
|
58
75
|
private
|
59
76
|
|
60
|
-
attr_reader :meeting
|
61
|
-
alias resource meeting
|
62
|
-
|
63
77
|
def author_fields
|
64
78
|
{
|
65
|
-
id:
|
66
|
-
name: author_name(
|
67
|
-
url: author_url(
|
79
|
+
id: resource.author.id,
|
80
|
+
name: author_name(resource.author),
|
81
|
+
url: author_url(resource.author)
|
68
82
|
}
|
69
83
|
end
|
70
84
|
|
@@ -80,38 +94,35 @@ module Decidim
|
|
80
94
|
end
|
81
95
|
end
|
82
96
|
|
83
|
-
def profile_url(author)
|
84
|
-
return "" if author.respond_to?(:deleted?) && author.deleted?
|
85
|
-
|
86
|
-
Decidim::Core::Engine.routes.url_helpers.profile_url(author.nickname, host:)
|
87
|
-
end
|
88
|
-
|
89
|
-
def root_url
|
90
|
-
Decidim::Core::Engine.routes.url_helpers.root_url(host:)
|
91
|
-
end
|
92
|
-
|
93
|
-
def host
|
94
|
-
resource.organization.host
|
95
|
-
end
|
96
|
-
|
97
|
-
def component
|
98
|
-
meeting.component
|
99
|
-
end
|
100
|
-
|
101
97
|
def related_proposals
|
102
|
-
|
98
|
+
resource.linked_resources(:proposals, "proposals_from_meeting").map do |proposal|
|
103
99
|
Decidim::ResourceLocatorPresenter.new(proposal).url
|
104
100
|
end
|
105
101
|
end
|
106
102
|
|
107
103
|
def related_results
|
108
|
-
|
104
|
+
resource.linked_resources(:results, "meetings_through_proposals").map do |result|
|
109
105
|
Decidim::ResourceLocatorPresenter.new(result).url
|
110
106
|
end
|
111
107
|
end
|
112
108
|
|
113
109
|
def url
|
114
|
-
Decidim::ResourceLocatorPresenter.new(
|
110
|
+
Decidim::ResourceLocatorPresenter.new(resource).url
|
111
|
+
end
|
112
|
+
|
113
|
+
def comment_fields
|
114
|
+
return {} unless resource.comments
|
115
|
+
|
116
|
+
{
|
117
|
+
start_time: resource.comments_start_time,
|
118
|
+
end_time: resource.comments_end_time,
|
119
|
+
enabled: resource.comments_enabled,
|
120
|
+
count: resource.comments_count
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
def include_location?
|
125
|
+
resource.iframe_access_level == "all"
|
115
126
|
end
|
116
127
|
end
|
117
128
|
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Meetings
|
5
|
+
class SchemaOrgEventMeetingSerializer < Decidim::Exporters::Serializer
|
6
|
+
include Decidim::TranslationsHelper
|
7
|
+
include Decidim::SanitizeHelper
|
8
|
+
include ActionView::Helpers::UrlHelper
|
9
|
+
|
10
|
+
# Public: Initializes the serializer with a meeting.
|
11
|
+
def initialize(meeting)
|
12
|
+
@meeting = meeting
|
13
|
+
end
|
14
|
+
|
15
|
+
# Serializes a meeting for the Schema.org Event type
|
16
|
+
#
|
17
|
+
# @see https://schema.org/Event
|
18
|
+
# @see https://developers.google.com/search/docs/appearance/structured-data/event?hl=en
|
19
|
+
def serialize
|
20
|
+
attributes = {
|
21
|
+
"@context": "https://schema.org",
|
22
|
+
"@type": "Event",
|
23
|
+
name: decidim_escape_translated(meeting.title),
|
24
|
+
description: decidim_escape_translated(meeting.description),
|
25
|
+
startDate: meeting.start_time.iso8601,
|
26
|
+
endDate: meeting.end_time.iso8601,
|
27
|
+
organizer:,
|
28
|
+
eventAttendanceMode: event_attendance_mode,
|
29
|
+
eventStatus: "https://schema.org/EventScheduled",
|
30
|
+
location:
|
31
|
+
}
|
32
|
+
|
33
|
+
attributes = attributes.merge(image:) if meeting.photos.any?
|
34
|
+
attributes
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :meeting
|
40
|
+
alias resource meeting
|
41
|
+
|
42
|
+
def organizer
|
43
|
+
return organizer_user_group if meeting.decidim_user_group_id?
|
44
|
+
|
45
|
+
case meeting.author.class.name
|
46
|
+
when "Decidim::Organization"
|
47
|
+
organizer_organization
|
48
|
+
when "Decidim::User"
|
49
|
+
organizer_user
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def organizer_user_group
|
54
|
+
{
|
55
|
+
"@type": "Organization",
|
56
|
+
name: meeting.author.name,
|
57
|
+
url: profile_url(meeting.user_group)
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def organizer_organization
|
62
|
+
{
|
63
|
+
"@type": "Organization",
|
64
|
+
name: decidim_escape_translated(meeting.author.name),
|
65
|
+
url: EngineRouter.new("decidim", router_options).root_url
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def organizer_user
|
70
|
+
{
|
71
|
+
"@type": "Person",
|
72
|
+
name: decidim_escape_translated(meeting.author.name),
|
73
|
+
url: profile_url(meeting.author)
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def router_options = { host: meeting.organization.host }
|
78
|
+
|
79
|
+
def event_attendance_mode
|
80
|
+
case meeting.type_of_meeting
|
81
|
+
when "online"
|
82
|
+
"https://schema.org/OnlineEventAttendanceMode"
|
83
|
+
when "hybrid"
|
84
|
+
"https://schema.org/MixedEventAttendanceMode"
|
85
|
+
else
|
86
|
+
"https://schema.org/OfflineEventAttendanceMode"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def location
|
91
|
+
case meeting.type_of_meeting
|
92
|
+
when "online"
|
93
|
+
location_virtual
|
94
|
+
when "hybrid"
|
95
|
+
[location_postal_address, location_virtual]
|
96
|
+
else
|
97
|
+
location_postal_address
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def location_postal_address
|
102
|
+
address = {
|
103
|
+
"@type": "PostalAddress",
|
104
|
+
streetAddress: decidim_escape_translated(meeting.address)
|
105
|
+
}
|
106
|
+
|
107
|
+
address = address.merge({ addressLocality: geocoder_city }) if geocoder_city.present?
|
108
|
+
address = address.merge({ addressRegion: geocoder_state }) if geocoder_state.present?
|
109
|
+
address = address.merge({ postalCode: geocoder_postal_code }) if geocoder_postal_code.present?
|
110
|
+
address = address.merge({ addressCountry: geocoder_country }) if geocoder_country.present?
|
111
|
+
|
112
|
+
{
|
113
|
+
"@type": "Place",
|
114
|
+
name: decidim_escape_translated(meeting.location),
|
115
|
+
address:
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def location_virtual
|
120
|
+
{
|
121
|
+
"@type": "VirtualLocation",
|
122
|
+
url: meeting.online_meeting_url
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
def geocoder
|
127
|
+
return if meeting.latitude.blank? || meeting.longitude.blank?
|
128
|
+
|
129
|
+
@geocoder ||= Geocoder.search([meeting.latitude, meeting.longitude]).first
|
130
|
+
end
|
131
|
+
|
132
|
+
def geocoder_city
|
133
|
+
@geocoder_city ||= geocoder&.city
|
134
|
+
end
|
135
|
+
|
136
|
+
def geocoder_state
|
137
|
+
@geocoder_state ||= geocoder&.state
|
138
|
+
end
|
139
|
+
|
140
|
+
def geocoder_postal_code
|
141
|
+
@geocoder_postal_code ||= geocoder&.postal_code
|
142
|
+
end
|
143
|
+
|
144
|
+
def geocoder_country
|
145
|
+
@geocoder_country ||= geocoder&.country
|
146
|
+
end
|
147
|
+
|
148
|
+
def image = meeting.photos.map(&:thumbnail_url)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -63,14 +63,12 @@ module Decidim
|
|
63
63
|
|
64
64
|
params = {
|
65
65
|
component:,
|
66
|
-
scope: random_scope(participatory_space:),
|
67
|
-
category: participatory_space.categories.sample,
|
68
66
|
title: Decidim::Faker::Localized.sentence(word_count: 2),
|
69
67
|
description: Decidim::Faker::Localized.wrapped("<p>", "</p>") do
|
70
68
|
Decidim::Faker::Localized.paragraph(sentence_count: 3)
|
71
69
|
end,
|
72
|
-
location: Decidim::Faker::Localized.sentence,
|
73
|
-
location_hints: Decidim::Faker::Localized.sentence,
|
70
|
+
location: Decidim::Faker::Localized.sentence(word_count: rand(2..20)),
|
71
|
+
location_hints: Decidim::Faker::Localized.sentence(word_count: rand(2..20)),
|
74
72
|
start_time:,
|
75
73
|
end_time:,
|
76
74
|
address: "#{::Faker::Address.street_address} #{::Faker::Address.zip} #{::Faker::Address.city}",
|
@@ -47,6 +47,7 @@ FactoryBot.define do
|
|
47
47
|
component { build(:meeting_component) }
|
48
48
|
iframe_access_level { :all }
|
49
49
|
iframe_embed_type { :none }
|
50
|
+
deleted_at { nil }
|
50
51
|
|
51
52
|
author do
|
52
53
|
component.try(:organization)
|
@@ -73,6 +74,8 @@ FactoryBot.define do
|
|
73
74
|
trait :online do
|
74
75
|
type_of_meeting { :online }
|
75
76
|
online_meeting_url { "https://decidim.org" }
|
77
|
+
latitude { nil }
|
78
|
+
longitude { nil }
|
76
79
|
end
|
77
80
|
|
78
81
|
trait :hybrid do
|
@@ -183,6 +186,11 @@ FactoryBot.define do
|
|
183
186
|
end
|
184
187
|
end
|
185
188
|
|
189
|
+
factory :meeting_link, class: "Decidim::Meetings::MeetingLink" do
|
190
|
+
meeting
|
191
|
+
component
|
192
|
+
end
|
193
|
+
|
186
194
|
factory :registration, class: "Decidim::Meetings::Registration" do
|
187
195
|
transient do
|
188
196
|
skip_injection { false }
|
@@ -4,17 +4,17 @@ shared_examples_for "a translated meeting event" do
|
|
4
4
|
describe "translated notifications" do
|
5
5
|
let(:en_body) { "This is Sparta!" }
|
6
6
|
let(:body) { { en: en_body, machine_translations: { ca: "C'est Sparta!" } } }
|
7
|
-
let(:participatory_process) { create
|
7
|
+
let(:participatory_process) { create(:participatory_process, organization:) }
|
8
8
|
let(:meeting_component) { create(:meeting_component, participatory_space: participatory_process) }
|
9
9
|
let(:translatable) { true }
|
10
10
|
let(:en_version) { resource.description["en"] }
|
11
11
|
let(:machine_translated) { resource.description["machine_translations"]["ca"] }
|
12
12
|
|
13
13
|
let(:resource) do
|
14
|
-
create
|
14
|
+
create(:meeting,
|
15
15
|
component: meeting_component,
|
16
16
|
title: { en: "A nice event", machine_translations: { ca: "Une belle event" } },
|
17
|
-
description: body
|
17
|
+
description: body)
|
18
18
|
end
|
19
19
|
|
20
20
|
it_behaves_like "a translated event"
|
data/lib/decidim/meetings.rb
CHANGED
@@ -16,6 +16,7 @@ module Decidim
|
|
16
16
|
autoload :MeetingSerializer, "decidim/meetings/meeting_serializer"
|
17
17
|
autoload :UserAnswersSerializer, "decidim/meetings/user_answers_serializer"
|
18
18
|
autoload :DownloadYourDataUserAnswersSerializer, "decidim/meetings/download_your_data_user_answers_serializer"
|
19
|
+
autoload :SchemaOrgEventMeetingSerializer, "decidim/meetings/schema_org_event_meeting_serializer"
|
19
20
|
|
20
21
|
include ActiveSupport::Configurable
|
21
22
|
|