decidim-conferences 0.29.4 → 0.30.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) 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/invite_user_to_join_conference.rb +1 -1
  6. data/app/commands/decidim/conferences/admin/update_conference.rb +1 -1
  7. data/app/controllers/decidim/conferences/admin/component_share_tokens_controller.rb +18 -0
  8. data/app/controllers/decidim/conferences/admin/concerns/conference_admin.rb +1 -1
  9. data/app/controllers/decidim/conferences/admin/conference_share_tokens_controller.rb +18 -0
  10. data/app/controllers/decidim/conferences/admin/conferences_controller.rb +17 -4
  11. data/app/forms/decidim/conferences/admin/conference_copy_form.rb +0 -1
  12. data/app/forms/decidim/conferences/admin/conference_form.rb +3 -8
  13. data/app/helpers/decidim/conferences/conference_program_helper.rb +4 -2
  14. data/app/mailers/decidim/conferences/admin/send_conference_diploma_mailer.rb +1 -11
  15. data/app/models/decidim/conference.rb +19 -0
  16. data/app/models/decidim/conferences/conference_invite.rb +10 -0
  17. data/app/permissions/decidim/conferences/permissions.rb +11 -10
  18. data/app/presenters/decidim/conferences/admin_log/conference_presenter.rb +1 -1
  19. data/app/presenters/decidim/conferences/conference_presenter.rb +19 -0
  20. data/app/serializers/decidim/conferences/conference_serializer.rb +24 -0
  21. data/app/serializers/decidim/conferences/download_your_data_conference_invite_serializer.rb +3 -4
  22. data/app/serializers/decidim/conferences/download_your_data_conference_registration_serializer.rb +4 -4
  23. data/app/serializers/decidim/conferences/open_data_conference_serializer.rb +30 -0
  24. data/app/views/decidim/conferences/admin/conference_copies/_form.html.erb +0 -3
  25. data/app/views/decidim/conferences/admin/conferences/_actions.html.erb +35 -0
  26. data/app/views/decidim/conferences/admin/conferences/_conference_row.html.erb +28 -0
  27. data/app/views/decidim/conferences/admin/conferences/_conferences_thead.html.erb +8 -0
  28. data/app/views/decidim/conferences/admin/conferences/_form.html.erb +27 -8
  29. data/app/views/decidim/conferences/admin/conferences/index.html.erb +14 -52
  30. data/app/views/decidim/conferences/admin/conferences/manage_trash.html.erb +20 -0
  31. data/app/views/decidim/conferences/admin/send_conference_diploma_mailer/diploma_user.html.erb +3 -1
  32. data/app/views/decidim/conferences/conference_program/show.html.erb +3 -1
  33. data/app/views/decidim/conferences/conference_speakers/index.html.erb +4 -1
  34. data/app/views/decidim/conferences/conferences/index.html.erb +3 -2
  35. data/app/views/decidim/conferences/conferences/show.html.erb +6 -6
  36. data/app/views/decidim/conferences/media/index.html.erb +3 -1
  37. data/app/views/decidim/conferences/registration_types/index.html.erb +3 -1
  38. data/app/views/layouts/decidim/admin/conference.html.erb +1 -1
  39. data/config/locales/ar.yml +10 -3
  40. data/config/locales/bg.yml +0 -3
  41. data/config/locales/ca.yml +61 -3
  42. data/config/locales/cs.yml +61 -3
  43. data/config/locales/de.yml +61 -3
  44. data/config/locales/el.yml +0 -3
  45. data/config/locales/en.yml +61 -3
  46. data/config/locales/es-MX.yml +61 -3
  47. data/config/locales/es-PY.yml +61 -3
  48. data/config/locales/es.yml +61 -3
  49. data/config/locales/eu.yml +77 -19
  50. data/config/locales/fi-plain.yml +61 -3
  51. data/config/locales/fi.yml +61 -3
  52. data/config/locales/fr-CA.yml +20 -3
  53. data/config/locales/fr.yml +20 -3
  54. data/config/locales/ga-IE.yml +0 -2
  55. data/config/locales/gl.yml +0 -3
  56. data/config/locales/hu.yml +0 -3
  57. data/config/locales/id-ID.yml +0 -3
  58. data/config/locales/it.yml +0 -3
  59. data/config/locales/ja.yml +61 -3
  60. data/config/locales/lb.yml +0 -3
  61. data/config/locales/lt.yml +0 -3
  62. data/config/locales/lv.yml +0 -3
  63. data/config/locales/nl.yml +0 -3
  64. data/config/locales/no.yml +0 -3
  65. data/config/locales/pl.yml +0 -3
  66. data/config/locales/pt-BR.yml +0 -3
  67. data/config/locales/pt.yml +0 -3
  68. data/config/locales/ro-RO.yml +0 -5
  69. data/config/locales/sk.yml +0 -3
  70. data/config/locales/sv.yml +31 -41
  71. data/config/locales/tr-TR.yml +0 -3
  72. data/config/locales/zh-CN.yml +0 -3
  73. data/config/locales/zh-TW.yml +0 -3
  74. data/db/migrate/20210310134942_add_followable_counter_cache_to_conferences.rb +1 -1
  75. data/db/migrate/20240822161330_add_deleted_at_to_decidim_conferences.rb +8 -0
  76. data/decidim-conferences.gemspec +1 -2
  77. data/lib/decidim/api/conference_media_link_type.rb +4 -4
  78. data/lib/decidim/api/conference_partner_type.rb +4 -4
  79. data/lib/decidim/api/conference_speaker_type.rb +7 -8
  80. data/lib/decidim/api/conference_type.rb +17 -19
  81. data/lib/decidim/conferences/admin_engine.rb +87 -58
  82. data/lib/decidim/conferences/conference_diploma_pdf.rb +242 -0
  83. data/lib/decidim/conferences/engine.rb +6 -1
  84. data/lib/decidim/conferences/menu.rb +23 -9
  85. data/lib/decidim/conferences/participatory_space.rb +13 -0
  86. data/lib/decidim/conferences/seeds.rb +134 -115
  87. data/lib/decidim/conferences/test/factories.rb +10 -9
  88. data/lib/decidim/conferences/version.rb +1 -1
  89. data/lib/decidim/conferences.rb +1 -0
  90. metadata +23 -29
  91. data/app/controllers/decidim/conferences/admin/categories_controller.rb +0 -13
  92. data/config/initializers/wicked_pdf.rb +0 -22
  93. data/config/locales/ca-IT.yml +0 -603
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
17
17
  "homepage_uri" => "https://decidim.org",
18
18
  "source_code_uri" => "https://github.com/decidim/decidim"
19
19
  }
20
- s.required_ruby_version = "~> 3.2.0"
20
+ s.required_ruby_version = "~> 3.3.0"
21
21
 
22
22
  s.name = "decidim-conferences"
23
23
  s.summary = "Decidim conferences module"
@@ -32,7 +32,6 @@ Gem::Specification.new do |s|
32
32
 
33
33
  s.add_dependency "decidim-core", Decidim::Conferences.version
34
34
  s.add_dependency "decidim-meetings", Decidim::Conferences.version
35
- s.add_dependency "wicked_pdf", "~> 2.7"
36
35
 
37
36
  s.add_development_dependency "decidim-admin", Decidim::Conferences.version
38
37
  s.add_development_dependency "decidim-dev", Decidim::Conferences.version
@@ -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