decidim-initiatives 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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/initiatives/content_blocks/highlighted_initiatives_settings_form/show.erb +1 -1
  3. data/app/commands/decidim/initiatives/create_initiative.rb +17 -5
  4. data/app/commands/decidim/initiatives/update_initiative.rb +23 -9
  5. data/app/controllers/concerns/decidim/initiatives/needs_initiative.rb +1 -1
  6. data/app/controllers/decidim/initiatives/admin/application_controller.rb +2 -0
  7. data/app/controllers/decidim/initiatives/admin/component_share_tokens_controller.rb +18 -0
  8. data/app/controllers/decidim/initiatives/admin/initiative_share_tokens_controller.rb +18 -0
  9. data/app/controllers/decidim/initiatives/admin/initiatives_controller.rb +8 -8
  10. data/app/controllers/decidim/initiatives/create_initiative_controller.rb +3 -2
  11. data/app/controllers/decidim/initiatives/initiative_types_controller.rb +1 -1
  12. data/app/controllers/decidim/initiatives/initiatives_controller.rb +4 -1
  13. data/app/forms/decidim/initiatives/admin/initiative_answer_form.rb +2 -2
  14. data/app/forms/decidim/initiatives/admin/initiative_form.rb +1 -1
  15. data/app/forms/decidim/initiatives/admin/initiative_type_form.rb +2 -2
  16. data/app/forms/decidim/initiatives/vote_form.rb +1 -1
  17. data/app/helpers/decidim/initiatives/initiative_helper.rb +1 -39
  18. data/app/helpers/decidim/initiatives/initiatives_helper.rb +8 -5
  19. data/app/helpers/decidim/initiatives/scopes_helper.rb +43 -0
  20. data/app/jobs/decidim/initiatives/export_initiatives_job.rb +5 -1
  21. data/app/models/decidim/initiative.rb +40 -27
  22. data/app/models/decidim/initiatives_type_scope.rb +9 -0
  23. data/app/packs/entrypoints/decidim_initiatives_admin.js +2 -0
  24. data/app/packs/entrypoints/decidim_initiatives_admin.scss +1 -0
  25. data/app/packs/stylesheets/decidim/initiatives/admin/initiatives.scss +13 -0
  26. data/app/permissions/decidim/initiatives/admin/permissions.rb +9 -2
  27. data/app/permissions/decidim/initiatives/permissions.rb +24 -3
  28. data/app/queries/decidim/initiatives/outdated_validating_initiatives.rb +1 -1
  29. data/app/queries/decidim/initiatives/support_period_finished_initiatives.rb +2 -2
  30. data/app/serializers/decidim/initiatives/download_your_data_initiative_serializer.rb +15 -0
  31. data/app/serializers/decidim/initiatives/initiative_serializer.rb +5 -24
  32. data/app/serializers/decidim/initiatives/open_data_initiative_serializer.rb +54 -0
  33. data/app/services/decidim/initiatives/data_encryptor.rb +1 -1
  34. data/app/services/decidim/initiatives/pdf_signature_example.rb +0 -2
  35. data/app/views/decidim/initiatives/admin/committee_requests/index.html.erb +1 -0
  36. data/app/views/decidim/initiatives/admin/initiatives/edit.html.erb +1 -2
  37. data/app/views/decidim/initiatives/admin/initiatives/index.html.erb +8 -1
  38. data/app/views/decidim/initiatives/committee_requests/new.html.erb +1 -2
  39. data/app/views/decidim/initiatives/create_initiative/fill_data.html.erb +3 -3
  40. data/app/views/decidim/initiatives/create_initiative/select_initiative_type.html.erb +10 -2
  41. data/app/views/decidim/initiatives/initiatives/_form.html.erb +3 -3
  42. data/app/views/decidim/initiatives/initiatives/_new_initiative_button.html.erb +16 -13
  43. data/app/views/decidim/initiatives/initiatives/_vote_button.html.erb +9 -3
  44. data/app/views/decidim/initiatives/initiatives/_vote_cabin.html.erb +0 -1
  45. data/app/views/decidim/initiatives/initiatives/show.html.erb +7 -8
  46. data/app/views/layouts/decidim/admin/_manage_initiatives.html.erb +11 -0
  47. data/app/views/layouts/decidim/admin/initiatives.html.erb +1 -9
  48. data/config/assets.rb +1 -3
  49. data/config/locales/ar.yml +10 -5
  50. data/config/locales/bg.yml +2 -22
  51. data/config/locales/bn-BD.yml +1 -0
  52. data/config/locales/bs-BA.yml +1 -0
  53. data/config/locales/ca.yml +56 -22
  54. data/config/locales/cs.yml +55 -21
  55. data/config/locales/de.yml +56 -22
  56. data/config/locales/el.yml +2 -6
  57. data/config/locales/en.yml +53 -19
  58. data/config/locales/es-MX.yml +55 -21
  59. data/config/locales/es-PY.yml +55 -21
  60. data/config/locales/es.yml +56 -22
  61. data/config/locales/eu.yml +97 -63
  62. data/config/locales/fi-plain.yml +57 -23
  63. data/config/locales/fi.yml +66 -32
  64. data/config/locales/fr-CA.yml +9 -22
  65. data/config/locales/fr.yml +9 -22
  66. data/config/locales/ga-IE.yml +0 -2
  67. data/config/locales/gl.yml +2 -7
  68. data/config/locales/hu.yml +2 -20
  69. data/config/locales/id-ID.yml +2 -5
  70. data/config/locales/is-IS.yml +0 -1
  71. data/config/locales/it.yml +2 -10
  72. data/config/locales/ja.yml +55 -21
  73. data/config/locales/lb.yml +2 -7
  74. data/config/locales/lt.yml +2 -22
  75. data/config/locales/lv.yml +2 -6
  76. data/config/locales/nl.yml +2 -8
  77. data/config/locales/no.yml +2 -12
  78. data/config/locales/pl.yml +2 -21
  79. data/config/locales/pt-BR.yml +2 -26
  80. data/config/locales/pt.yml +2 -10
  81. data/config/locales/ro-RO.yml +2 -18
  82. data/config/locales/ru.yml +0 -1
  83. data/config/locales/sk.yml +2 -5
  84. data/config/locales/sl.yml +0 -1
  85. data/config/locales/sv.yml +13 -22
  86. data/config/locales/tr-TR.yml +2 -10
  87. data/config/locales/uk.yml +0 -1
  88. data/config/locales/zh-CN.yml +2 -8
  89. data/config/locales/zh-TW.yml +2 -21
  90. data/db/migrate/20241127104718_add_taxonomy_to_initiatives_type_scope.rb +7 -0
  91. data/decidim-initiatives.gemspec +2 -8
  92. data/lib/decidim/api/initiative_api_type.rb +9 -10
  93. data/lib/decidim/api/initiative_committee_member_type.rb +3 -4
  94. data/lib/decidim/api/initiative_type.rb +15 -21
  95. data/lib/decidim/api/initiative_type_interface.rb +1 -5
  96. data/lib/decidim/exporters/initiative_votes_pdf.rb +163 -0
  97. data/lib/decidim/initiatives/admin_engine.rb +68 -49
  98. data/lib/decidim/initiatives/application_form_pdf.rb +181 -0
  99. data/lib/decidim/initiatives/engine.rb +6 -0
  100. data/lib/decidim/initiatives/menu.rb +8 -0
  101. data/lib/decidim/initiatives/participatory_space.rb +8 -1
  102. data/lib/decidim/initiatives/test/factories.rb +5 -8
  103. data/lib/decidim/initiatives/version.rb +1 -1
  104. data/lib/decidim/initiatives.rb +7 -1
  105. metadata +30 -74
  106. data/app/cells/decidim/initiatives_votes/vote/show.erb +0 -39
  107. data/app/cells/decidim/initiatives_votes/vote_cell.rb +0 -58
  108. data/app/packs/entrypoints/decidim_initiatives_initiatives_votes.js +0 -1
  109. data/app/packs/entrypoints/decidim_initiatives_initiatives_votes.scss +0 -1
  110. data/app/packs/entrypoints/decidim_initiatives_print.js +0 -1
  111. data/app/packs/entrypoints/decidim_initiatives_print.scss +0 -1
  112. data/app/packs/stylesheets/decidim/initiatives/initiatives-votes.scss +0 -96
  113. data/app/packs/stylesheets/decidim/initiatives/print-initiative.scss +0 -172
  114. data/app/views/decidim/initiatives/admin/initiatives/_signatures.html.erb +0 -87
  115. data/app/views/decidim/initiatives/admin/initiatives/export_pdf_signatures.html.erb +0 -8
  116. data/app/views/decidim/initiatives/admin/initiatives/export_pdf_signatures.pdf.erb +0 -5
  117. data/app/views/decidim/initiatives/initiatives/_linked_initiatives.html.erb +0 -0
  118. data/app/views/decidim/initiatives/initiatives/print.html.erb +0 -161
  119. data/app/views/layouts/decidim/admin/initiatives_votes.pdf.erb +0 -11
  120. data/config/initializers/wicked_pdf.rb +0 -23
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Exporters
5
+ # Inherits from abstract PDF exporter. This class is used to set
6
+ # the parameters used to create a PDF when exporting Survey Answers.
7
+ #
8
+ class InitiativeVotesPDF < PDF
9
+ def initialize(collection, initiative, serializer = Serializer)
10
+ @initiative = initiative
11
+ super(collection, serializer)
12
+ end
13
+
14
+ protected
15
+
16
+ attr_reader :initiative
17
+
18
+ def page_orientation
19
+ :landscape
20
+ end
21
+
22
+ def add_data!
23
+ composer.formatted_text([translated_attribute(initiative.title)], style: :initiative_title)
24
+
25
+ add_initiative_data
26
+ add_signature_data
27
+ end
28
+
29
+ def styles
30
+ {
31
+ initiative_title: { font: bold_font, text_align: :center, font_size: 12, margin: [0, 0, 10, 0] },
32
+ initiative_th: { font: bold_font, font_size: 10 },
33
+ initiative_td: { font:, font_size: 10 },
34
+ vote_th: { font: bold_font, font_size: 11 },
35
+ vote_td: { font:, font_size: 10 }
36
+ }
37
+ end
38
+
39
+ def add_initiative_data
40
+ data_header = [
41
+ layout.text(I18n.t("models.initiatives_votes.fields.initiative_id", scope: "decidim.admin"), style: :initiative_th),
42
+ layout.text(I18n.t("models.initiatives_votes.fields.initiative_title", scope: "decidim.admin"), style: :initiative_th),
43
+ layout.text(I18n.t("models.initiatives_votes.fields.initiative_start_date", scope: "decidim.admin"), style: :initiative_th),
44
+ layout.text(I18n.t("models.initiatives_votes.fields.initiative_end_date", scope: "decidim.admin"), style: :initiative_th),
45
+ layout.text(I18n.t("models.initiatives_votes.fields.initiative_signatures_count", scope: "decidim.admin"), style: :initiative_th),
46
+ layout.text(I18n.t("models.initiatives_votes.fields.initiative_scope", scope: "decidim.admin"), style: :initiative_th)
47
+ ]
48
+
49
+ data_row = [
50
+ layout.text(initiative.reference, style: :initiative_td),
51
+ layout.text(translated_attribute(initiative.title), style: :initiative_td),
52
+ layout.text(I18n.l(initiative.signature_start_date, format: :long), style: :initiative_td),
53
+ layout.text(I18n.l(initiative.signature_end_date, format: :long), style: :initiative_td),
54
+ layout.text(collection.count.to_s, style: :initiative_td),
55
+ layout.text(scope(initiative), style: :initiative_td)
56
+ ]
57
+
58
+ column_widths = [-1, -1.75, -0.55, -0.55, -1, -1]
59
+
60
+ cells = [
61
+ [layout.table([data_header], column_widths:, cell_style: row_style)],
62
+ [layout.table([data_row], column_widths:, cell_style: row_style)]
63
+ ]
64
+ composer.table(cells, cell_style:)
65
+ end
66
+
67
+ def add_signature_data
68
+ cells = [[layout.table([header], column_widths: signature_column_widths, cell_style: row_style)]]
69
+
70
+ collection.map.with_index do |vote, index|
71
+ cells.push([layout.table(vote_row(vote, index), column_widths: signature_column_widths, cell_style: row_style)])
72
+ end
73
+
74
+ composer.table(cells, margin: [20, 0, 0, 0], cell_style:)
75
+ end
76
+
77
+ def signature_column_widths
78
+ if collect_user_extra_fields
79
+ [-0.5, -1, -0.75, -0.75, -0.75, -0.75, -0.75, -0.5, -0.75]
80
+ else
81
+ [-0.5, -1, -0.75, -0.75, -0.75]
82
+ end
83
+ end
84
+
85
+ def vote_row(model, index)
86
+ cell = [
87
+ layout.text((index + 1).to_s, style: :vote_td),
88
+ layout.text(model.author.nickname, style: :vote_td),
89
+ layout.text(I18n.l(model.created_at, format: "%Y-%m-%d %H:%M:%S %Z"), style: :vote_td),
90
+ layout.text(truncate(model.hash_id), style: :vote_td)
91
+ ]
92
+
93
+ if collect_user_extra_fields
94
+ metadata ||= model.encrypted_metadata ? encryptor.decrypt(model.encrypted_metadata) : {}
95
+
96
+ cell += [
97
+ layout.text(metadata[:name_and_surname].presence || "", style: :vote_td),
98
+ layout.text(metadata[:document_number].presence || "", style: :vote_td),
99
+ layout.text(metadata[:date_of_birth].presence || "", style: :vote_td),
100
+ layout.text(metadata[:postal_code].presence || "", style: :vote_td)
101
+ ]
102
+ end
103
+
104
+ cell += [
105
+ layout.text(truncate(model.timestamp.presence || ""), style: :vote_td)
106
+ ]
107
+ [cell]
108
+ end
109
+
110
+ def scope(model)
111
+ return I18n.t("decidim.initiatives.unavailable_scope") if model.scope.blank?
112
+
113
+ translated_attribute(model.scope.name)
114
+ end
115
+
116
+ def header
117
+ header = [
118
+ layout.text(I18n.t("models.initiatives_votes.fields.signature_count", scope: "decidim.admin"), style: :vote_th),
119
+ layout.text(I18n.t("models.initiatives_votes.fields.nickname", scope: "decidim.admin"), style: :vote_th),
120
+ layout.text(I18n.t("models.initiatives_votes.fields.date_and_time", scope: "decidim.admin"), style: :vote_th),
121
+ layout.text(I18n.t("models.initiatives_votes.fields.hash", scope: "decidim.admin"), style: :vote_th)
122
+ ]
123
+
124
+ if collect_user_extra_fields
125
+ header += [
126
+ layout.text(I18n.t("models.initiatives_votes.fields.name_and_surname", scope: "decidim.admin"), style: :vote_th),
127
+ layout.text(I18n.t("models.initiatives_votes.fields.document_number", scope: "decidim.admin"), style: :vote_th),
128
+ layout.text(I18n.t("models.initiatives_votes.fields.date_of_birth", scope: "decidim.admin"), style: :vote_th),
129
+ layout.text(I18n.t("models.initiatives_votes.fields.postal_code", scope: "decidim.admin"), style: :vote_th)
130
+ ]
131
+ end
132
+
133
+ header += [
134
+ layout.text(I18n.t("models.initiatives_votes.fields.timestamp", scope: "decidim.admin"), style: :vote_th)
135
+ ]
136
+ header
137
+ end
138
+
139
+ def collect_user_extra_fields = initiative.type.collect_user_extra_fields
140
+
141
+ def cell_style
142
+ lambda do |cell|
143
+ cell.style.margin = 0
144
+ cell.style.padding = 0
145
+ cell.style.border(width: 1)
146
+ cell.style.background_color = "cccccc" if cell.row.zero?
147
+ end
148
+ end
149
+
150
+ def encryptor
151
+ @encryptor ||= Decidim::Initiatives::DataEncryptor.new(secret: "personal user metadata")
152
+ end
153
+
154
+ def truncate(text, length = 50)
155
+ text.truncate(length)
156
+ end
157
+
158
+ def row_style
159
+ { margin: 0, padding: [10, 0, 10, 5], border: { width: 0 } }
160
+ end
161
+ end
162
+ end
163
+ end
@@ -15,75 +15,94 @@ module Decidim
15
15
  paths["lib/tasks"] = nil
16
16
 
17
17
  routes do
18
- resources :initiatives_types, except: :show do
19
- resource :permissions, controller: "initiatives_types_permissions"
20
- resources :initiatives_type_scopes, except: [:index, :show]
21
- end
22
-
23
- resources :initiatives_settings, only: [:edit, :update], controller: "initiatives_settings"
24
-
25
- resources :initiatives, only: [:index, :edit, :update], param: :slug do
26
- member do
27
- get :send_to_technical_validation
28
- post :publish
29
- delete :unpublish
30
- delete :discard
31
- get :export_votes
32
- get :export_pdf_signatures
33
- post :accept
34
- delete :reject
18
+ constraints(->(request) { Decidim::Admin::OrganizationDashboardConstraint.new(request).matches? }) do
19
+ resources :initiatives_types, except: :show do
20
+ resource :permissions, controller: "initiatives_types_permissions"
21
+ resources :initiatives_type_scopes, except: [:index, :show]
35
22
  end
36
23
 
37
- collection do
38
- get :export
39
- end
24
+ resources :initiatives_settings, only: [:edit, :update], controller: "initiatives_settings"
40
25
 
41
- resources :attachments, controller: "initiative_attachments", except: [:show]
42
-
43
- resources :committee_requests, only: [:index] do
26
+ resources :initiatives, only: [:index, :edit, :update], param: :slug do
44
27
  member do
45
- get :approve
46
- delete :revoke
28
+ get :send_to_technical_validation
29
+ post :publish
30
+ delete :unpublish
31
+ delete :discard
32
+ get :export_votes
33
+ get :export_pdf_signatures
34
+ post :accept
35
+ delete :reject
47
36
  end
48
- end
49
37
 
50
- resource :permissions, controller: "initiatives_permissions"
38
+ collection do
39
+ get :export
40
+ end
51
41
 
52
- resource :answer, only: [:edit, :update]
53
- end
42
+ resources :attachments, controller: "initiative_attachments", except: [:show]
54
43
 
55
- scope "/initiatives/:initiative_slug" do
56
- resources :components do
57
- resource :permissions, controller: "component_permissions"
58
- member do
59
- put :publish
60
- put :unpublish
61
- get :share
44
+ resources :committee_requests, only: [:index] do
45
+ member do
46
+ get :approve
47
+ delete :revoke
48
+ end
62
49
  end
63
- resources :exports, only: :create
50
+
51
+ resource :permissions, controller: "initiatives_permissions"
52
+
53
+ resource :answer, only: [:edit, :update]
64
54
  end
65
55
 
66
- resources :moderations do
67
- member do
68
- put :unreport
69
- put :hide
70
- put :unhide
56
+ scope "/initiatives/:initiative_slug" do
57
+ resources :components do
58
+ collection do
59
+ put :reorder
60
+ get :manage_trash, to: "components#manage_trash"
61
+ end
62
+ resource :permissions, controller: "component_permissions"
63
+ member do
64
+ put :publish
65
+ put :unpublish
66
+ get :share
67
+ put :hide
68
+ patch :soft_delete
69
+ patch :restore
70
+ end
71
+ resources :component_share_tokens, except: [:show], path: "share_tokens", as: "share_tokens"
72
+ resources :exports, only: :create
71
73
  end
72
- resources :reports, controller: "moderations/reports", only: [:index, :show]
74
+
75
+ resources :moderations do
76
+ member do
77
+ put :unreport
78
+ put :hide
79
+ put :unhide
80
+ end
81
+ patch :bulk_action, on: :collection
82
+ resources :reports, controller: "moderations/reports", only: [:index, :show]
83
+ end
84
+
85
+ resources :initiative_share_tokens, except: [:show], path: "share_tokens"
73
86
  end
74
- end
75
87
 
76
- scope "/initiatives/:initiative_slug/components/:component_id/manage" do
77
- Decidim.component_manifests.each do |manifest|
78
- next unless manifest.admin_engine
88
+ scope "/initiatives/:initiative_slug/components/:component_id/manage" do
89
+ Decidim.component_manifests.each do |manifest|
90
+ next unless manifest.admin_engine
79
91
 
80
- constraints CurrentComponent.new(manifest) do
81
- mount manifest.admin_engine, at: "/", as: "decidim_admin_initiative_#{manifest.name}"
92
+ constraints CurrentComponent.new(manifest) do
93
+ mount manifest.admin_engine, at: "/", as: "decidim_admin_initiative_#{manifest.name}"
94
+ end
82
95
  end
83
96
  end
84
97
  end
85
98
  end
86
99
 
100
+ initializer "decidim_initiatives_admin.mount_routes" do |_app|
101
+ Decidim::Core::Engine.routes do
102
+ mount Decidim::Initiatives::AdminEngine, at: "/admin", as: "decidim_admin_initiatives"
103
+ end
104
+ end
105
+
87
106
  initializer "decidim_initiatives_admin.menu" do
88
107
  Decidim::Initiatives::Menu.register_admin_menu_modules!
89
108
  Decidim::Initiatives::Menu.register_admin_initiatives_components_menu!
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hexapdf"
4
+
5
+ module Decidim
6
+ module Initiatives
7
+ class ApplicationFormPDF
8
+ include Decidim::OrganizationHelper
9
+ def initialize(initiative)
10
+ @initiative = initiative
11
+ end
12
+
13
+ def render
14
+ composer.styles(**styles)
15
+
16
+ add_logo
17
+ add_organization_data_box
18
+ add_author_box
19
+ add_promoter_box
20
+
21
+ composer.new_page
22
+
23
+ add_initiative_metadata_box
24
+ add_attachments_box
25
+ add_signature_box
26
+ add_legal_box
27
+
28
+ composer.write_to_string
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :initiative
34
+
35
+ delegate :document, to: :composer
36
+ delegate :layout, to: :document
37
+
38
+ def page = document.pages.first
39
+
40
+ def page_width = page.box(:media).width
41
+
42
+ def page_height = page.box(:media).height
43
+
44
+ def styles
45
+ {
46
+ h1: { font: bold_font, text_align: :center, font_size: 16, margin: [10, 0, 10, 0] },
47
+ title: { font: bold_font, text_align: :center, font_size: 12, margin: [10, 0, 10, 0] },
48
+ text: { font: bold_font, text_align: :left, font_size: 12, margin: [0, 0, 10, 0] },
49
+ td: { font:, text_align: :left, font_size: 12, margin: [0, 0, 10, 0] }
50
+ }
51
+ end
52
+
53
+ def add_author_box
54
+ cells = [
55
+ [{ content: layout.text(I18n.t("author_title", scope: "decidim.initiatives.initiatives.print"), style: :title), col_span: 3 }],
56
+ [{ content: layout.text(I18n.t("id_number", scope: "decidim.initiatives.initiatives.print"), style: :td), col_span: 3, padding: [5, 5, 20, 5] }],
57
+ [{ content: layout.text(I18n.t("full_name", scope: "decidim.initiatives.initiatives.print"), style: :td), col_span: 3, padding: [5, 5, 20, 5] }],
58
+ [{ content: layout.text(I18n.t("address", scope: "decidim.initiatives.initiatives.print"), style: :td), col_span: 3, padding: [5, 5, 20, 5] }],
59
+ [
60
+ { content: layout.text(I18n.t("city", scope: "decidim.initiatives.initiatives.print"), style: :td), padding: [5, 5, 20, 5] },
61
+ { content: layout.text(I18n.t("province", scope: "decidim.initiatives.initiatives.print"), style: :td), padding: [5, 5, 20, 5] },
62
+ { content: layout.text(I18n.t("postal_code", scope: "decidim.initiatives.initiatives.print"), style: :td), padding: [5, 5, 20, 5] }
63
+ ],
64
+ [
65
+ { content: layout.text(I18n.t("phone_number", scope: "decidim.initiatives.initiatives.print"), style: :td), padding: [5, 5, 20, 5] },
66
+ { content: layout.text(I18n.t("email", scope: "decidim.initiatives.initiatives.print"), style: :td), col_span: 2, padding: [5, 5, 20, 5] }
67
+ ]
68
+ ]
69
+ composer.table(cells, cell_style: { border: { width: 1 } }, style: { margin: [10, 0] })
70
+ end
71
+
72
+ def add_signature_box
73
+ cells = [
74
+ layout.text(I18n.t("place_date", scope: "decidim.initiatives.initiatives.print"), style: :title),
75
+ layout.text(I18n.t("signature", scope: "decidim.initiatives.initiatives.print"), style: :title)
76
+ ]
77
+ composer.table([cells], cell_style: { border: { width: 0 } }, style: { margin: [10, 0] })
78
+ end
79
+
80
+ def add_attachments_box
81
+ cells = [
82
+ [{ content: layout.text(I18n.t("initiative.attachments", scope: "decidim.initiatives.initiatives.print"), style: :title), col_span: 10 }]
83
+ ]
84
+
85
+ 6.times do
86
+ cols = [
87
+ { content: layout.text(""), padding: 15 },
88
+ { content: layout.text(""), padding: 15, col_span: 9 }
89
+ ]
90
+ cells.push(cols)
91
+ end
92
+
93
+ composer.table(cells, cell_style: { border: { width: 1 } }, style: { margin: [10, 0] })
94
+ end
95
+
96
+ def add_promoter_box
97
+ cells = [
98
+ [{ content: layout.text(I18n.t("members_header", scope: "decidim.initiatives.initiatives.print"), style: :title), col_span: 3 }],
99
+ [
100
+ { content: layout.text(I18n.t("full_name", scope: "decidim.initiatives.initiatives.print"), style: :td), padding: [5, 5, 20, 5] },
101
+ { content: layout.text(I18n.t("id_number", scope: "decidim.initiatives.initiatives.print"), style: :td), padding: [5, 5, 20, 5] },
102
+ { content: layout.text(I18n.t("address", scope: "decidim.initiatives.initiatives.print"), style: :td), padding: [5, 5, 20, 5] }
103
+ ]
104
+ ]
105
+
106
+ 4.times do
107
+ cols = []
108
+ 3.times do
109
+ cols.push({ content: layout.text(""), padding: 15 })
110
+ end
111
+ cells.push(cols)
112
+ end
113
+
114
+ composer.table(cells, cell_style: { border: { width: 1 } }, style: { margin: [10, 0] })
115
+ end
116
+
117
+ def add_legal_box
118
+ composer.text(I18n.t("legal_text", scope: "decidim.initiatives.initiatives.print"), style: {
119
+ font:, font_size: 10, margin: [100, 0]
120
+ })
121
+ end
122
+
123
+ def add_initiative_metadata_box
124
+ composer.text(I18n.t("initiative.type", scope: "decidim.initiatives.initiatives.print"), style: :text)
125
+ composer.text(translated_attribute(initiative.type.title), style: :td)
126
+ composer.text(I18n.t("initiative.title", scope: "decidim.initiatives.initiatives.print"), style: :text)
127
+ composer.text(translated_attribute(initiative.title), style: :td)
128
+ composer.text(I18n.t("initiative.description", scope: "decidim.initiatives.initiatives.print"), style: :text)
129
+ composer.text(translated_attribute(initiative.description), style: :td)
130
+ end
131
+
132
+ def add_organization_data_box
133
+ composer.text(organization_name(initiative.organization), style: :h1)
134
+ cells = [
135
+ layout.text(I18n.t("general_title", scope: "decidim.initiatives.initiatives.print"), style: :title)
136
+ ]
137
+ composer.table([cells], cell_style: { border: { width: 1 } })
138
+ end
139
+
140
+ def add_logo
141
+ return if initiative.organization.logo.blank?
142
+
143
+ attached_image = initiative.organization.attached_uploader(:logo).variant(:thumb)
144
+ logo = document.images.add(StringIO.new(attached_image.download))
145
+ height, width = compute_dimensions(attached_image, 160, 80)
146
+
147
+ cells = ["", layout.image(logo, width:, height:), ""]
148
+ composer.table([cells], cell_style: { border: { width: 0 } })
149
+ end
150
+
151
+ def compute_dimensions(attached_image, max_width, max_height)
152
+ metadata = attached_image.blob.metadata.with_indifferent_access
153
+ aspect_ratio = metadata[:width] / metadata[:height]
154
+
155
+ if metadata[:width] >= metadata[:height]
156
+ max_height = (max_height / aspect_ratio).round
157
+ else
158
+ max_width = (max_width / aspect_ratio).round
159
+ end
160
+
161
+ return max_height, max_width
162
+ end
163
+
164
+ def font
165
+ @font ||= load_font("source-sans-pro-v21-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-regular.ttf")
166
+ end
167
+
168
+ def bold_font
169
+ @bold_font ||= load_font("source-sans-pro-v21-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-700.ttf")
170
+ end
171
+
172
+ def load_font(path)
173
+ document.fonts.add(Decidim::Core::Engine.root.join("app/packs/fonts/decidim/").join(path))
174
+ end
175
+
176
+ def composer
177
+ @composer ||= ::HexaPDF::Composer.new
178
+ end
179
+ end
180
+ end
181
+ end
@@ -86,6 +86,12 @@ module Decidim
86
86
  end
87
87
  end
88
88
 
89
+ initializer "decidim_initiatives.mount_routes" do
90
+ Decidim::Core::Engine.routes do
91
+ mount Decidim::Initiatives::Engine, at: "/", as: "decidim_initiatives"
92
+ end
93
+ end
94
+
89
95
  initializer "decidim_initiatives.register_icons" do
90
96
  Decidim.icons.register(name: "Decidim::Initiative", icon: "lightbulb-flash-line", description: "Initiative", category: "activity", engine: :initiatives)
91
97
  Decidim.icons.register(name: "apps-line", icon: "apps-line", category: "system", description: "", engine: :initiatives)
@@ -66,6 +66,7 @@ module Decidim
66
66
  active: is_active_link?(manage_component_path(component)) ||
67
67
  is_active_link?(decidim_admin_initiatives.edit_component_path(current_participatory_space, component)) ||
68
68
  is_active_link?(decidim_admin_initiatives.edit_component_permissions_path(current_participatory_space, component)) ||
69
+ is_active_link?(decidim_admin_initiatives.component_share_tokens_path(current_participatory_space, component)) ||
69
70
  participatory_space_active_link?(component),
70
71
  if: component.manifest.admin_engine # && user_role_config.component_is_accessible?(component.manifest_name)
71
72
  end
@@ -106,6 +107,13 @@ module Decidim
106
107
  decidim_admin_initiatives.moderations_path(current_participatory_space),
107
108
  icon_name: "flag-line",
108
109
  if: allowed_to?(:read, :moderation)
110
+
111
+ menu.add_item :initiatives_share_tokens,
112
+ I18n.t("menu.share_tokens", scope: "decidim.admin"),
113
+ decidim_admin_initiatives.initiative_share_tokens_path(current_participatory_space),
114
+ active: is_active_link?(decidim_admin_initiatives.initiative_share_tokens_path(current_participatory_space)),
115
+ icon_name: "share-line",
116
+ if: allowed_to?(:read, :share_tokens, current_participatory_space:)
109
117
  end
110
118
  end
111
119
 
@@ -38,12 +38,19 @@ Decidim.register_participatory_space(:initiatives) do |participatory_space|
38
38
  participatory_space.model_class_name = "Decidim::Initiative"
39
39
  participatory_space.permissions_class_name = "Decidim::Initiatives::Permissions"
40
40
 
41
+ participatory_space.data_portable_entities = [
42
+ "Decidim::Initiative"
43
+ ]
44
+
41
45
  participatory_space.exports :initiatives do |export|
42
46
  export.collection do
43
- Decidim::Initiative
47
+ Decidim::Initiative.public_spaces
44
48
  end
45
49
 
50
+ export.include_in_open_data = true
51
+
46
52
  export.serializer Decidim::Initiatives::InitiativeSerializer
53
+ export.open_data_serializer Decidim::Initiatives::OpenDataInitiativeSerializer
47
54
  end
48
55
 
49
56
  participatory_space.seeds do
@@ -108,6 +108,7 @@ FactoryBot.define do
108
108
  end
109
109
  type { create(:initiatives_type, skip_injection:) }
110
110
  scope { create(:scope, organization: type.organization, skip_injection:) }
111
+ taxonomy { create(:taxonomy, organization: type.organization, skip_injection:) }
111
112
  supports_required { 1000 }
112
113
 
113
114
  trait :with_user_extra_fields_collection do
@@ -124,8 +125,8 @@ FactoryBot.define do
124
125
  description { generate_localized_description(:initiative_description, skip_injection:) }
125
126
  organization
126
127
  author { create(:user, :confirmed, organization:, skip_injection:) }
127
- published_at { Time.current }
128
- state { "published" }
128
+ state { "open" }
129
+ published_at { Time.current.utc }
129
130
  signature_type { "online" }
130
131
  signature_start_date { Date.current - 1.day }
131
132
  signature_end_date { Date.current + 120.days }
@@ -156,12 +157,8 @@ FactoryBot.define do
156
157
  signature_end_date { nil }
157
158
  end
158
159
 
159
- trait :published do
160
- state { "published" }
161
- end
162
-
163
- trait :unpublished do
164
- published_at { nil }
160
+ trait :open do
161
+ state { "open" }
165
162
  end
166
163
 
167
164
  trait :accepted do
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-initiatives version.
5
5
  module Initiatives
6
6
  def self.version
7
- "0.29.1"
7
+ "0.30.0.rc1"
8
8
  end
9
9
  end
10
10
  end
@@ -7,8 +7,14 @@ require "decidim/initiatives/admin_engine"
7
7
  require "decidim/initiatives/participatory_space"
8
8
 
9
9
  module Decidim
10
+ module Exporters
11
+ autoload :InitiativeVotesPDF, "decidim/exporters/initiative_votes_pdf"
12
+ end
13
+
10
14
  # Base module for the initiatives engine.
11
15
  module Initiatives
16
+ autoload :ApplicationFormPDF, "decidim/initiatives/application_form_pdf"
17
+
12
18
  include ActiveSupport::Configurable
13
19
 
14
20
  # Public setting that defines whether creation is allowed to any validated
@@ -32,7 +38,7 @@ module Decidim
32
38
 
33
39
  # Components enabled for a new initiative
34
40
  config_accessor :default_components do
35
- [:pages, :meetings]
41
+ [:pages, :meetings, :blogs]
36
42
  end
37
43
 
38
44
  # Notifies when the given percentage of supports is reached for an