decidim-conferences 0.29.1 → 0.30.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/conferences/content_blocks/highlighted_conferences_settings_form/show.erb +1 -1
  3. data/app/commands/decidim/conferences/admin/copy_conference.rb +2 -12
  4. data/app/commands/decidim/conferences/admin/create_conference.rb +1 -1
  5. data/app/commands/decidim/conferences/admin/update_conference.rb +1 -1
  6. data/app/controllers/decidim/conferences/admin/component_share_tokens_controller.rb +18 -0
  7. data/app/controllers/decidim/conferences/admin/concerns/conference_admin.rb +1 -1
  8. data/app/controllers/decidim/conferences/admin/conference_share_tokens_controller.rb +18 -0
  9. data/app/controllers/decidim/conferences/admin/conferences_controller.rb +17 -4
  10. data/app/forms/decidim/conferences/admin/conference_copy_form.rb +0 -1
  11. data/app/forms/decidim/conferences/admin/conference_form.rb +7 -12
  12. data/app/forms/decidim/conferences/admin/conference_speaker_form.rb +1 -1
  13. data/app/forms/decidim/conferences/admin/registration_type_form.rb +1 -1
  14. data/app/helpers/decidim/conferences/conference_program_helper.rb +4 -2
  15. data/app/mailers/decidim/conferences/admin/send_conference_diploma_mailer.rb +1 -7
  16. data/app/models/decidim/conference.rb +19 -0
  17. data/app/models/decidim/conferences/conference_invite.rb +10 -0
  18. data/app/permissions/decidim/conferences/permissions.rb +11 -4
  19. data/app/presenters/decidim/conferences/admin_log/conference_presenter.rb +1 -1
  20. data/app/presenters/decidim/conferences/conference_presenter.rb +19 -0
  21. data/app/serializers/decidim/conferences/conference_serializer.rb +24 -0
  22. data/app/serializers/decidim/conferences/download_your_data_conference_invite_serializer.rb +3 -4
  23. data/app/serializers/decidim/conferences/download_your_data_conference_registration_serializer.rb +4 -4
  24. data/app/serializers/decidim/conferences/open_data_conference_serializer.rb +30 -0
  25. data/app/views/decidim/conferences/admin/conference_copies/_form.html.erb +0 -3
  26. data/app/views/decidim/conferences/admin/conferences/_actions.html.erb +35 -0
  27. data/app/views/decidim/conferences/admin/conferences/_conference_row.html.erb +28 -0
  28. data/app/views/decidim/conferences/admin/conferences/_conferences_thead.html.erb +8 -0
  29. data/app/views/decidim/conferences/admin/conferences/_form.html.erb +27 -8
  30. data/app/views/decidim/conferences/admin/conferences/index.html.erb +14 -52
  31. data/app/views/decidim/conferences/admin/conferences/manage_trash.html.erb +20 -0
  32. data/app/views/decidim/conferences/admin/media_links/new.html.erb +1 -1
  33. data/app/views/decidim/conferences/admin/send_conference_diploma_mailer/diploma_user.html.erb +13 -7
  34. data/app/views/decidim/conferences/conference_program/show.html.erb +3 -1
  35. data/app/views/decidim/conferences/conference_speakers/index.html.erb +4 -1
  36. data/app/views/decidim/conferences/conferences/index.html.erb +3 -2
  37. data/app/views/decidim/conferences/conferences/show.html.erb +6 -6
  38. data/app/views/decidim/conferences/media/index.html.erb +3 -1
  39. data/app/views/decidim/conferences/registration_types/index.html.erb +3 -1
  40. data/app/views/layouts/decidim/admin/conference.html.erb +1 -1
  41. data/app/views/layouts/decidim/diploma.html.erb +1 -1
  42. data/config/locales/ar.yml +12 -24
  43. data/config/locales/bg.yml +0 -24
  44. data/config/locales/bn-BD.yml +1 -0
  45. data/config/locales/bs-BA.yml +14 -0
  46. data/config/locales/ca.yml +74 -14
  47. data/config/locales/cs.yml +76 -16
  48. data/config/locales/de.yml +75 -15
  49. data/config/locales/el.yml +0 -24
  50. data/config/locales/en.yml +82 -22
  51. data/config/locales/es-MX.yml +75 -15
  52. data/config/locales/es-PY.yml +78 -18
  53. data/config/locales/es.yml +71 -11
  54. data/config/locales/eu.yml +229 -169
  55. data/config/locales/fi-plain.yml +76 -16
  56. data/config/locales/fi.yml +82 -22
  57. data/config/locales/fr-CA.yml +35 -16
  58. data/config/locales/fr.yml +35 -16
  59. data/config/locales/ga-IE.yml +0 -2
  60. data/config/locales/gl.yml +0 -24
  61. data/config/locales/hu.yml +0 -24
  62. data/config/locales/id-ID.yml +0 -24
  63. data/config/locales/it.yml +0 -24
  64. data/config/locales/ja.yml +73 -13
  65. data/config/locales/lb.yml +0 -22
  66. data/config/locales/lt.yml +0 -24
  67. data/config/locales/lv.yml +0 -24
  68. data/config/locales/nl.yml +0 -24
  69. data/config/locales/no.yml +0 -24
  70. data/config/locales/pl.yml +0 -24
  71. data/config/locales/pt-BR.yml +0 -24
  72. data/config/locales/pt.yml +0 -24
  73. data/config/locales/ro-RO.yml +13 -15
  74. data/config/locales/sk.yml +0 -24
  75. data/config/locales/sv.yml +66 -25
  76. data/config/locales/tr-TR.yml +0 -24
  77. data/config/locales/zh-CN.yml +0 -24
  78. data/config/locales/zh-TW.yml +0 -24
  79. data/db/migrate/20210310134942_add_followable_counter_cache_to_conferences.rb +1 -1
  80. data/db/migrate/20240822161330_add_deleted_at_to_decidim_conferences.rb +8 -0
  81. data/decidim-conferences.gemspec +2 -7
  82. data/lib/decidim/api/conference_media_link_type.rb +4 -4
  83. data/lib/decidim/api/conference_partner_type.rb +4 -4
  84. data/lib/decidim/api/conference_speaker_type.rb +7 -8
  85. data/lib/decidim/api/conference_type.rb +17 -19
  86. data/lib/decidim/conferences/admin_engine.rb +87 -58
  87. data/lib/decidim/conferences/conference_diploma_pdf.rb +242 -0
  88. data/lib/decidim/conferences/engine.rb +6 -1
  89. data/lib/decidim/conferences/menu.rb +23 -9
  90. data/lib/decidim/conferences/participatory_space.rb +13 -0
  91. data/lib/decidim/conferences/seeds.rb +134 -115
  92. data/lib/decidim/conferences/test/factories.rb +10 -9
  93. data/lib/decidim/conferences/version.rb +1 -1
  94. data/lib/decidim/conferences.rb +1 -0
  95. metadata +26 -43
  96. data/app/controllers/decidim/conferences/admin/categories_controller.rb +0 -13
  97. data/config/initializers/wicked_pdf.rb +0 -23
@@ -4,15 +4,15 @@ module Decidim
4
4
  module Conferences
5
5
  # This type represents a conference.
6
6
  class ConferenceMediaLinkType < Decidim::Api::Types::BaseObject
7
+ implements Decidim::Core::TimestampsInterface
8
+
7
9
  description "A conference media link"
8
10
 
11
+ field :date, Decidim::Core::DateType, "Relevant date for the media link", null: true
9
12
  field :id, GraphQL::Types::ID, "Internal ID for this media link", null: false
10
- field :title, Decidim::Core::TranslatedFieldType, "Title for this media link", null: true
11
13
  field :link, GraphQL::Types::String, "URL for this media link", null: true
12
- field :date, Decidim::Core::DateType, "Relevant date for the media link", null: true
14
+ field :title, Decidim::Core::TranslatedFieldType, "Title for this media link", null: true
13
15
  field :weight, GraphQL::Types::Int, "Order of appearance in which it should be presented", null: true
14
- field :created_at, Decidim::Core::DateTimeType, "The time this entry was created", null: true
15
- field :updated_at, Decidim::Core::DateTimeType, "The time this entry was updated", null: true
16
16
  end
17
17
  end
18
18
  end
@@ -4,16 +4,16 @@ module Decidim
4
4
  module Conferences
5
5
  # This type represents a conference.
6
6
  class ConferencePartnerType < Decidim::Api::Types::BaseObject
7
+ implements Decidim::Core::TimestampsInterface
8
+
7
9
  description "A conference partner"
8
10
 
9
11
  field :id, GraphQL::Types::ID, "ID of the resource", null: false
12
+ field :link, GraphQL::Types::String, "Relevant URL for this partner", null: true
13
+ field :logo, GraphQL::Types::String, "Link to the partner's logo", null: true
10
14
  field :name, GraphQL::Types::String, "Partner name", null: true
11
15
  field :partner_type, GraphQL::Types::String, "Partner type", null: true
12
16
  field :weight, GraphQL::Types::Int, "Order of appearance in which it should be presented", null: true
13
- field :link, GraphQL::Types::String, "Relevant URL for this partner", null: true
14
- field :logo, GraphQL::Types::String, "Link to the partner's logo", null: true
15
- field :created_at, Decidim::Core::DateTimeType, "The time this partner was created", null: true
16
- field :updated_at, Decidim::Core::DateTimeType, "The time this partner was updated", null: true
17
17
 
18
18
  def logo
19
19
  object.attached_uploader(:logo).url
@@ -4,21 +4,20 @@ module Decidim
4
4
  module Conferences
5
5
  # This type represents a conference.
6
6
  class ConferenceSpeakerType < Decidim::Api::Types::BaseObject
7
+ implements Decidim::Core::TimestampsInterface
8
+
7
9
  description "A conference speaker"
8
10
 
9
- field :id, GraphQL::Types::ID, "Internal ID of the speaker", null: false
11
+ field :affiliation, Decidim::Core::TranslatedFieldType, "Affiliation of the speaker", null: true
12
+ field :avatar, GraphQL::Types::String, "Avatar of the speaker", null: true
10
13
  field :full_name, GraphQL::Types::String, "Full name of the speaker", null: true
14
+ field :id, GraphQL::Types::ID, "Internal ID of the speaker", null: false
15
+ field :personal_url, GraphQL::Types::String, "Personal URL of the speaker", null: true
11
16
  field :position, Decidim::Core::TranslatedFieldType, "Position of the speaker in the conference", null: true
12
- field :affiliation, Decidim::Core::TranslatedFieldType, "Affiliation of the speaker", null: true
13
- field :twitter_handle, GraphQL::Types::String, "X handle", null: true
14
17
  field :short_bio, Decidim::Core::TranslatedFieldType, "Short biography of the speaker", null: true
15
- field :personal_url, GraphQL::Types::String, "Personal URL of the speaker", null: true
16
- field :avatar, GraphQL::Types::String, "Avatar of the speaker", null: true
18
+ field :twitter_handle, GraphQL::Types::String, "X handle", null: true
17
19
  field :user, Decidim::Core::UserType, "Decidim user corresponding to this speaker", null: true
18
20
 
19
- field :created_at, Decidim::Core::DateTimeType, "The time this member was created ", null: true
20
- field :updated_at, Decidim::Core::DateTimeType, "The time this member was updated", null: true
21
-
22
21
  def avatar
23
22
  object.attached_uploader(:avatar).url
24
23
  end
@@ -7,36 +7,34 @@ module Decidim
7
7
  implements Decidim::Core::ParticipatorySpaceInterface
8
8
  implements Decidim::Core::ScopableInterface
9
9
  implements Decidim::Core::AttachableInterface
10
+ implements Decidim::Core::TaxonomizableInterface
11
+ implements Decidim::Core::TimestampsInterface
10
12
  implements Decidim::Core::CategoriesContainerInterface
11
13
 
12
14
  description "A conference"
13
15
 
14
- field :id, GraphQL::Types::ID, "Internal ID for this conference", null: false
15
- field :short_description, Decidim::Core::TranslatedFieldType, "The short description of this conference", null: true
16
+ field :available_slots, GraphQL::Types::Int, "The number of available slots in this conference", null: true
17
+ field :banner_image, GraphQL::Types::String, "The banner image for this conference", null: true
16
18
  field :description, Decidim::Core::TranslatedFieldType, "The description of this conference", null: true
17
- field :slug, GraphQL::Types::String, "The slug of this conference", null: true
19
+ field :end_date, Decidim::Core::DateType, "The date this conference ends", null: true
18
20
  field :hashtag, GraphQL::Types::String, "The hashtag for this conference", null: true
19
- field :slogan, Decidim::Core::TranslatedFieldType, "The slogan of the conference", null: true
21
+ field :hero_image, GraphQL::Types::String, "The hero image for this conference", null: true
22
+ field :id, GraphQL::Types::ID, "Internal ID for this conference", null: false
20
23
  field :location, String, "The location of this conference", null: true
21
- field :created_at, Decidim::Core::DateTimeType, "The time this conference was created", null: true
22
- field :updated_at, Decidim::Core::DateTimeType, "The time this conference was updated", null: true
24
+ field :media_links, [Decidim::Conferences::ConferenceMediaLinkType, { null: true }], "List of media links in this conference", null: true
25
+ field :objectives, Decidim::Core::TranslatedFieldType, "The objectives of the conference", null: true
26
+ field :partners, [Decidim::Conferences::ConferencePartnerType, { null: true }], "List of partners in this conference", null: true
27
+ field :promoted, GraphQL::Types::Boolean, "If this conference is promoted (therefore in the homepage)", null: true
23
28
  field :published_at, Decidim::Core::DateTimeType, "The time this conference was published", null: true
24
29
  field :reference, GraphQL::Types::String, "Reference prefix for this conference", null: true
25
-
26
- field :hero_image, GraphQL::Types::String, "The hero image for this conference", null: true
27
- field :banner_image, GraphQL::Types::String, "The banner image for this conference", null: true
28
- field :promoted, GraphQL::Types::Boolean, "If this conference is promoted (therefore in the homepage)", null: true
29
- field :objectives, Decidim::Core::TranslatedFieldType, "The objectives of the conference", null: true
30
- field :show_statistics, GraphQL::Types::Boolean, "If this conference shows the statistics", null: true
31
- field :start_date, Decidim::Core::DateType, "The date this conference starts", null: true
32
- field :end_date, Decidim::Core::DateType, "The date this conference ends", null: true
33
- field :registrations_enabled, GraphQL::Types::Boolean, "If the registrations are enabled in this conference", null: true
34
- field :available_slots, GraphQL::Types::Int, "The number of available slots in this conference", null: true
35
30
  field :registration_terms, Decidim::Core::TranslatedFieldType, "The registration terms of this conference", null: true
36
-
31
+ field :registrations_enabled, GraphQL::Types::Boolean, "If the registrations are enabled in this conference", null: true
32
+ field :short_description, Decidim::Core::TranslatedFieldType, "The short description of this conference", null: true
33
+ field :show_statistics, GraphQL::Types::Boolean, "If this conference shows the statistics", null: true
34
+ field :slogan, Decidim::Core::TranslatedFieldType, "The slogan of the conference", null: true
35
+ field :slug, GraphQL::Types::String, "The slug of this conference", null: true
37
36
  field :speakers, [Decidim::Conferences::ConferenceSpeakerType, { null: true }], "List of speakers in this conference", null: true
38
- field :partners, [Decidim::Conferences::ConferencePartnerType, { null: true }], "List of partners in this conference", null: true
39
- field :media_links, [Decidim::Conferences::ConferenceMediaLinkType, { null: true }], "List of media links in this conference", null: true
37
+ field :start_date, Decidim::Core::DateType, "The date this conference starts", null: true
40
38
 
41
39
  def hero_image
42
40
  object.attached_uploader(:hero_image).url
@@ -15,89 +15,118 @@ module Decidim
15
15
  paths["lib/tasks"] = nil
16
16
 
17
17
  routes do
18
- resources :conferences, param: :slug, except: [:show, :destroy] do
19
- resource :publish, controller: "conference_publications", only: [:create, :destroy]
20
- resources :copies, controller: "conference_copies", only: [:new, :create]
21
- resources :speakers, controller: "conference_speakers" do
22
- member do
23
- put :publish
24
- put :unpublish
18
+ constraints(->(request) { Decidim::Admin::OrganizationDashboardConstraint.new(request).matches? }) do
19
+ resources :conferences, param: :slug, except: [:show, :destroy] do
20
+ resource :publish, controller: "conference_publications", only: [:create, :destroy]
21
+ resources :copies, controller: "conference_copies", only: [:new, :create]
22
+ resources :speakers, controller: "conference_speakers" do
23
+ member do
24
+ put :publish
25
+ put :unpublish
26
+ end
25
27
  end
26
- end
27
- resources :partners, controller: "partners", except: [:show]
28
- resources :media_links, controller: "media_links"
29
- resources :registration_types, controller: "registration_types" do
30
- resource :publish, controller: "registration_type_publications", only: [:create, :destroy]
31
- collection do
32
- get :conference_meetings
28
+ resources :partners, controller: "partners", except: [:show]
29
+ resources :media_links, controller: "media_links"
30
+ resources :registration_types, controller: "registration_types" do
31
+ resource :publish, controller: "registration_type_publications", only: [:create, :destroy]
32
+ collection do
33
+ get :conference_meetings
34
+ end
33
35
  end
34
- end
35
- resources :conference_invites, only: [:index, :new, :create]
36
- resources :conference_registrations, only: :index do
37
- member do
38
- post :confirm
36
+ resources :conference_invites, only: [:index, :new, :create]
37
+ resources :conference_registrations, only: :index do
38
+ member do
39
+ post :confirm
40
+ end
41
+ collection do
42
+ get :export
43
+ end
39
44
  end
40
- collection do
41
- get :export
45
+ resource :diploma, only: [:edit, :update] do
46
+ member do
47
+ post :send, to: "diplomas#send_diplomas"
48
+ end
42
49
  end
43
- end
44
- resource :diploma, only: [:edit, :update] do
45
- member do
46
- post :send, to: "diplomas#send_diplomas"
50
+ resources :user_roles, controller: "conference_user_roles" do
51
+ member do
52
+ post :resend_invitation, to: "conference_user_roles#resend_invitation"
53
+ end
47
54
  end
48
- end
49
- resources :user_roles, controller: "conference_user_roles" do
55
+
50
56
  member do
51
- post :resend_invitation, to: "conference_user_roles#resend_invitation"
57
+ patch :soft_delete
58
+ patch :restore
52
59
  end
53
- end
54
60
 
55
- resources :attachment_collections, controller: "conference_attachment_collections", except: [:show]
56
- resources :attachments, controller: "conference_attachments", except: [:show]
57
- end
61
+ collection do
62
+ get :manage_trash, to: "conferences#manage_trash"
63
+ end
58
64
 
59
- scope "/conferences/:conference_slug" do
60
- resources :categories, except: [:show]
65
+ resources :attachment_collections, controller: "conference_attachment_collections", except: [:show]
66
+ resources :attachments, controller: "conference_attachments", except: [:show]
67
+ end
61
68
 
62
- resources :components do
63
- resource :permissions, controller: "component_permissions"
64
- member do
65
- put :publish
66
- put :unpublish
67
- get :share
68
- end
69
- resources :exports, only: :create
70
- resources :imports, only: [:new, :create] do
71
- get :example, on: :collection
69
+ scope "/conferences/:conference_slug" do
70
+ resources :components do
71
+ collection do
72
+ put :reorder
73
+ end
74
+ resource :permissions, controller: "component_permissions"
75
+ member do
76
+ put :publish
77
+ put :unpublish
78
+ get :share
79
+ patch :soft_delete
80
+ patch :restore
81
+ end
82
+ collection do
83
+ get :manage_trash, to: "components#manage_trash"
84
+ put :hide
85
+ end
86
+ resources :component_share_tokens, except: [:show], path: "share_tokens", as: "share_tokens"
87
+ resources :exports, only: :create
88
+ resources :imports, only: [:new, :create] do
89
+ get :example, on: :collection
90
+ end
91
+ resources :reminders, only: [:new, :create]
72
92
  end
73
- resources :reminders, only: [:new, :create]
74
- end
75
93
 
76
- resources :moderations do
77
- member do
78
- put :unreport
79
- put :hide
80
- put :unhide
94
+ resources :moderations do
95
+ member do
96
+ put :unreport
97
+ put :hide
98
+ put :unhide
99
+ end
100
+ patch :bulk_action, on: :collection
101
+ resources :reports, controller: "moderations/reports", only: [:index, :show]
81
102
  end
82
- resources :reports, controller: "moderations/reports", only: [:index, :show]
103
+
104
+ resources :conference_share_tokens, except: [:show], path: "share_tokens"
83
105
  end
84
- end
85
106
 
86
- scope "/conferences/:conference_slug/components/:component_id/manage" do
87
- Decidim.component_manifests.each do |manifest|
88
- next unless manifest.admin_engine
107
+ scope "/conferences/:conference_slug/components/:component_id/manage" do
108
+ Decidim.component_manifests.each do |manifest|
109
+ next unless manifest.admin_engine
89
110
 
90
- constraints CurrentComponent.new(manifest) do
91
- mount manifest.admin_engine, at: "/", as: "decidim_admin_conference_#{manifest.name}"
111
+ constraints CurrentComponent.new(manifest) do
112
+ mount manifest.admin_engine, at: "/", as: "decidim_admin_conference_#{manifest.name}"
113
+ end
92
114
  end
93
115
  end
94
116
  end
95
117
  end
96
118
 
119
+ initializer "decidim_conferences_admin.mount_routes" do
120
+ Decidim::Core::Engine.routes do
121
+ mount Decidim::Conferences::AdminEngine, at: "/admin", as: "decidim_admin_conferences"
122
+ end
123
+ end
124
+
97
125
  initializer "decidim_conferences_admin.menu" do
98
126
  Decidim::Conferences::Menu.register_admin_conferences_components_menu!
99
127
  Decidim::Conferences::Menu.register_conferences_admin_registrations_menu!
100
128
  Decidim::Conferences::Menu.register_conferences_admin_attachments_menu!
129
+ Decidim::Conferences::Menu.register_conference_admin_menu!
101
130
  Decidim::Conferences::Menu.register_conferences_admin_menu!
102
131
  Decidim::Conferences::Menu.register_admin_menu_modules!
103
132
  end
@@ -0,0 +1,242 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hexapdf"
4
+ require "open-uri"
5
+
6
+ module Decidim
7
+ module Conferences
8
+ class ConferenceDiplomaPDF
9
+ include ActionView::Helpers::AssetUrlHelper
10
+ include Shakapacker::Helper
11
+ include Decidim::TranslatableAttributes
12
+ include Decidim::SanitizeHelper
13
+
14
+ def initialize(conference, user)
15
+ @conference = conference
16
+ @user = user
17
+ end
18
+
19
+ def render
20
+ composer.styles(**styles)
21
+
22
+ create_border
23
+ add_logo
24
+
25
+ add_text
26
+
27
+ add_hr_line
28
+ add_verified_by_text
29
+ add_signature_picture
30
+ add_verified_by_signature
31
+
32
+ document.write_to_string
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :conference
38
+
39
+ delegate :document, to: :composer
40
+
41
+ def page_orientation = :landscape
42
+
43
+ def page = document.pages.first
44
+
45
+ def page_width = page.box(:media).width
46
+
47
+ def page_height = page.box(:media).height
48
+
49
+ def layout = document.layout
50
+
51
+ def box_width = page_width * 0.9
52
+
53
+ def box_height = page_height * 0.7
54
+
55
+ def styles
56
+ {
57
+ h1: { font: bold_font, text_align: :center, font_size: 18 },
58
+ h2: { font: bold_font, text_align: :center, font_size: 15 },
59
+ text: { font:, text_align: :center, font_size: 10 },
60
+ verified: { font:, text_align: :center, font_size: 8 },
61
+ bold: { font: bold_font, text_align: :center, font_size: 10 }
62
+ }
63
+ end
64
+
65
+ def composer
66
+ @composer ||= ::HexaPDF::Composer.new(page_orientation:)
67
+ end
68
+
69
+ def add_text
70
+ width = 710
71
+
72
+ composer.text(translated_attribute(conference.title), style: :h1, position: [30, box_height - 130], width:)
73
+ composer.text(I18n.t("decidim.conferences.admin.send_conference_diploma_mailer.diploma_user.certificate_of_attendance"),
74
+ style: :h2, position: [30, box_height - 160], width:)
75
+
76
+ text = I18n.t("decidim.conferences.admin.send_conference_diploma_mailer.diploma_user.certificate_of_attendance_description",
77
+ user: @user.name,
78
+ title: translated_attribute(@conference.title),
79
+ location: @conference.location,
80
+ start: I18n.l(@conference.start_date, format: :decidim_short),
81
+ end: I18n.l(@conference.end_date, format: :decidim_short))
82
+
83
+ texts_to_bold = [
84
+ @user.name,
85
+ translated_attribute(@conference.title),
86
+ I18n.l(@conference.start_date, format: :decidim_short),
87
+ I18n.l(@conference.end_date, format: :decidim_short)
88
+ ]
89
+
90
+ texts_to_bold = texts_to_bold.map { |bold| decidim_sanitize(bold, strip_tags: true) }
91
+
92
+ text = decidim_sanitize(text, strip_tags: true)
93
+ text_fragments = create_text_fragments(text, texts_to_bold)
94
+
95
+ result = HexaPDF::Layout::TextLayouter.new.fit(text_fragments, 710, 30)
96
+
97
+ x_width = (page_width - result.lines.first.width) / 2
98
+
99
+ result.draw(composer.canvas, x_width, 260)
100
+ end
101
+
102
+ # Function to create text fragments with bold parts
103
+ def create_text_fragments(text, texts_to_bold)
104
+ fragments = []
105
+ current_position = 0
106
+
107
+ texts_to_bold.sort_by! { |bold_text| text.index(bold_text) }
108
+
109
+ texts_to_bold.each do |bold_text|
110
+ bold_position = text.index(bold_text, current_position)
111
+
112
+ if bold_position > current_position
113
+ part = text[current_position...bold_position]
114
+ fragments << HexaPDF::Layout::TextFragment.create(part, styles[:text])
115
+ end
116
+
117
+ fragments << HexaPDF::Layout::TextFragment.create(bold_text, styles[:bold])
118
+
119
+ current_position = bold_position + bold_text.length
120
+ end
121
+
122
+ if current_position < text.length
123
+ part = text[current_position..-1]
124
+ fragments << HexaPDF::Layout::TextFragment.create(part, font: styles[:text])
125
+ end
126
+
127
+ fragments
128
+ end
129
+
130
+ def compute_dimensions(attached_image, max_width, max_height)
131
+ # Retrieve the image metadata
132
+ metadata = attached_image.blob.metadata
133
+
134
+ # If metadata does not contain width and height, return the max dimensions
135
+ return max_height, max_width unless metadata.has_key?(:width) && metadata.has_key?(:height)
136
+
137
+ # Calculate the aspect ratio of the image
138
+ aspect_ratio = metadata[:width].to_f / metadata[:height]
139
+
140
+ # Adjust the width based on the max height while preserving the aspect ratio
141
+ adjusted_width = (max_height * aspect_ratio).round
142
+
143
+ # Return the max height and the adjusted width
144
+ return max_height, adjusted_width
145
+ end
146
+
147
+ def font
148
+ @font ||= load_font("source-sans-pro-v21-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-regular.ttf")
149
+ end
150
+
151
+ def bold_font
152
+ @bold_font ||= load_font("source-sans-pro-v21-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-700.ttf")
153
+ end
154
+
155
+ def load_font(path)
156
+ document.fonts.add(Decidim::Core::Engine.root.join("app/packs/fonts/decidim/").join(path))
157
+ end
158
+
159
+ def add_verified_by_text
160
+ composer.text(I18n.t("decidim.conferences.admin.send_conference_diploma_mailer.diploma_user.attendance_verified_by"),
161
+ style: :bold, position: [30, 160], width: 710)
162
+ end
163
+
164
+ def add_signature_picture
165
+ attached_image = conference.attached_uploader(:signature).variant(:original)
166
+ logo = document.images.add(StringIO.new(attached_image.download))
167
+ max_height, max_width = compute_dimensions(attached_image, 80, 40)
168
+
169
+ box_x = (page_width - max_width) / 2
170
+ box_y = 150
171
+ frame = ::HexaPDF::Layout::Frame.new(box_x, box_y, 80, 40)
172
+
173
+ box = document.layout.box do |canvas, _box|
174
+ canvas.image(logo, at: [0, 0], width: max_width, height: max_height)
175
+ end
176
+
177
+ result = frame.fit(box)
178
+ frame.draw(page.canvas, result)
179
+ end
180
+
181
+ def add_hr_line
182
+ frame = ::HexaPDF::Layout::Frame.new(66, 210, 710, 10)
183
+
184
+ box = document.layout.box do |canvas, _box|
185
+ canvas.line_width = 0.5
186
+ canvas.stroke_color(0, 0, 0)
187
+ canvas.line(0, 10, 710, 10).stroke
188
+ end
189
+ result = frame.fit(box)
190
+ frame.draw(page.canvas, result)
191
+ end
192
+
193
+ def add_verified_by_signature
194
+ composer.text([
195
+ I18n.l(conference.sign_date, format: :decidim_short),
196
+ conference.signature_name
197
+ ].join(", "), style: :verified, position: [30, 95], width: 710)
198
+ end
199
+
200
+ def add_logo
201
+ attached_image = conference.attached_uploader(:main_logo).variant(:original)
202
+ logo = document.images.add(StringIO.new(attached_image.download))
203
+ max_height, max_width = compute_dimensions(attached_image, 160, 80)
204
+
205
+ box_x = (page_width - max_width) / 2
206
+ box_y = page_height - 220
207
+ frame = ::HexaPDF::Layout::Frame.new(box_x, box_y, 160, 80)
208
+
209
+ box = document.layout.box do |canvas, _box|
210
+ canvas.image(logo, at: [0, 0], width: max_width, height: max_height)
211
+ end
212
+
213
+ result = frame.fit(box)
214
+ frame.draw(page.canvas, result)
215
+ end
216
+
217
+ def create_border
218
+ background_image = StringIO.new(Rails.root.join("public/#{image_pack_path("media/images/pattern.png")}").read)
219
+
220
+ box_x = (page_width - box_width) / 2
221
+ box_y = (page_height - box_height) / 2
222
+
223
+ frame = ::HexaPDF::Layout::Frame.new(box_x, box_y, box_width, box_height)
224
+
225
+ box = document.layout.image_box(background_image, width: box_width, height: box_height)
226
+ result = frame.fit(box)
227
+ frame.draw(page.canvas, result)
228
+
229
+ frame = ::HexaPDF::Layout::Frame.new(box_x, box_y, box_width, box_height)
230
+
231
+ box = document.layout.box do |canvas, _box|
232
+ canvas.fill_color("FFFFFF")
233
+ canvas.rectangle(10, 10, box_width - 20, box_height - 20)
234
+ canvas.fill
235
+ end
236
+
237
+ result = frame.fit(box)
238
+ frame.draw(page.canvas, result)
239
+ end
240
+ end
241
+ end
242
+ end
@@ -3,7 +3,6 @@
3
3
  require "rails"
4
4
  require "active_support/all"
5
5
  require "decidim/core"
6
- require "wicked_pdf"
7
6
 
8
7
  require "decidim/conferences/query_extensions"
9
8
  require "decidim/conferences/content_blocks/registry_manager"
@@ -51,6 +50,12 @@ module Decidim
51
50
  end
52
51
  end
53
52
 
53
+ initializer "decidim_conferences.mount_routes" do
54
+ Decidim::Core::Engine.routes do
55
+ mount Decidim::Conferences::Engine, at: "/", as: "decidim_conferences"
56
+ end
57
+ end
58
+
54
59
  initializer "decidim_conferences.register_icons" do
55
60
  Decidim.icons.register(name: "Decidim::Conference", icon: "mic-line", description: "Conference", category: "activity", engine: :conferences)
56
61
  Decidim.icons.register(name: "conference_speaker", icon: "user-voice-line", description: "Speaker", category: "conferences", engine: :conferences)
@@ -48,6 +48,7 @@ module Decidim
48
48
  active: is_active_link?(manage_component_path(component)) ||
49
49
  is_active_link?(decidim_admin_conferences.edit_component_path(current_participatory_space, component)) ||
50
50
  is_active_link?(decidim_admin_conferences.edit_component_permissions_path(current_participatory_space, component)) ||
51
+ is_active_link?(decidim_admin_conferences.component_share_tokens_path(current_participatory_space, component)) ||
51
52
  participatory_space_active_link?(component),
52
53
  if: component.manifest.admin_engine && user_role_config.component_is_accessible?(component.manifest_name)
53
54
  end
@@ -100,8 +101,8 @@ module Decidim
100
101
  end
101
102
  end
102
103
 
103
- def self.register_conferences_admin_menu!
104
- Decidim.menu :conferences_admin_menu do |menu|
104
+ def self.register_conference_admin_menu!
105
+ Decidim.menu :conference_admin_menu do |menu|
105
106
  menu.add_item :edit_conference,
106
107
  I18n.t("info", scope: "decidim.admin.menu.conferences_submenu"),
107
108
  decidim_admin_conferences.edit_conference_path(current_participatory_space),
@@ -118,12 +119,6 @@ module Decidim
118
119
  ["decidim/conferences/admin/components", %w(index new edit)]),
119
120
  submenu: { target_menu: :admin_conferences_components_menu }
120
121
 
121
- menu.add_item :categories,
122
- I18n.t("categories", scope: "decidim.admin.menu.conferences_submenu"),
123
- decidim_admin_conferences.categories_path(current_participatory_space),
124
- icon_name: "price-tag-3-line",
125
- if: allowed_to?(:read, :category, conference: current_participatory_space)
126
-
127
122
  menu.add_item :attachments,
128
123
  I18n.t("attachments", scope: "decidim.admin.menu.conferences_submenu"),
129
124
  decidim_admin_conferences.conference_attachments_path(current_participatory_space),
@@ -172,6 +167,25 @@ module Decidim
172
167
  decidim_admin_conferences.moderations_path(current_participatory_space),
173
168
  icon_name: "flag-line",
174
169
  if: allowed_to?(:read, :moderation, conference: current_participatory_space)
170
+
171
+ menu.add_item :conference_share_tokens,
172
+ I18n.t("menu.share_tokens", scope: "decidim.admin"),
173
+ decidim_admin_conferences.conference_share_tokens_path(current_participatory_space),
174
+ active: is_active_link?(decidim_admin_conferences.conference_share_tokens_path(current_participatory_space)),
175
+ icon_name: "share-line",
176
+ if: allowed_to?(:read, :share_tokens, current_participatory_space:)
177
+ end
178
+ end
179
+
180
+ def self.register_conferences_admin_menu!
181
+ Decidim.menu :admin_conferences_menu do |menu|
182
+ menu.add_item :conferences,
183
+ I18n.t("menu.conferences", scope: "decidim.admin"),
184
+ decidim_admin_conferences.conferences_path,
185
+ position: 1,
186
+ active: is_active_link?(decidim_admin_conferences.conferences_path),
187
+ icon_name: "government-line",
188
+ if: allowed_to?(:read, :conference_list)
175
189
  end
176
190
  end
177
191
 
@@ -182,7 +196,7 @@ module Decidim
182
196
  decidim_admin_conferences.conferences_path,
183
197
  icon_name: "live-line",
184
198
  position: 2.8,
185
- active: :inclusive,
199
+ active: is_active_link?(decidim_admin_conferences.conferences_path, :inclusive),
186
200
  if: allowed_to?(:enter, :space_area, space_name: :conferences)
187
201
  end
188
202
  end
@@ -36,6 +36,19 @@ Decidim.register_participatory_space(:conferences) do |participatory_space|
36
36
  context.layout = "layouts/decidim/admin/conference"
37
37
  end
38
38
 
39
+ participatory_space.exports :conferences do |export|
40
+ export.collection do
41
+ Decidim::Conference
42
+ .public_spaces
43
+ .includes(:taxonomies, :attachment_collections)
44
+ end
45
+
46
+ export.include_in_open_data = true
47
+
48
+ export.serializer Decidim::Conferences::ConferenceSerializer
49
+ export.open_data_serializer Decidim::Conferences::OpenDataConferenceSerializer
50
+ end
51
+
39
52
  participatory_space.register_on_destroy_account do |user|
40
53
  Decidim::ConferenceUserRole.where(user:).destroy_all
41
54
  Decidim::ConferenceSpeaker.where(user:).destroy_all