decidim-core 0.28.1 → 0.28.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/address/online.erb +2 -2
  3. data/app/cells/decidim/address_cell.rb +4 -0
  4. data/app/cells/decidim/announcement/show.erb +2 -2
  5. data/app/cells/decidim/author/show.erb +5 -5
  6. data/app/cells/decidim/card/show.erb +1 -1
  7. data/app/cells/decidim/card_g/show.erb +1 -1
  8. data/app/cells/decidim/card_g_cell.rb +5 -2
  9. data/app/cells/decidim/card_l/image.erb +2 -2
  10. data/app/cells/decidim/card_l_cell.rb +5 -2
  11. data/app/cells/decidim/card_metadata/show.erb +2 -2
  12. data/app/cells/decidim/content_blocks/hero_cell.rb +1 -1
  13. data/app/cells/decidim/content_blocks/highlighted_content_banner/show.erb +1 -1
  14. data/app/cells/decidim/content_blocks/participatory_space_hero_cell.rb +2 -2
  15. data/app/cells/decidim/data_consent/category.erb +1 -1
  16. data/app/cells/decidim/nav_links/show.erb +2 -2
  17. data/app/cells/decidim/notification/moderated.erb +12 -0
  18. data/app/cells/decidim/notification_cell.rb +5 -1
  19. data/app/cells/decidim/profile/details.erb +1 -1
  20. data/app/cells/decidim/progress_bar/show.erb +1 -1
  21. data/app/cells/decidim/progress_bar_cell.rb +2 -0
  22. data/app/cells/decidim/report_button/flag_modal.erb +5 -1
  23. data/app/cells/decidim/resource_types_filter/show.erb +3 -3
  24. data/app/cells/decidim/statistic/show.erb +2 -2
  25. data/app/cells/decidim/upload_modal/modal.erb +3 -4
  26. data/app/commands/decidim/create_omniauth_registration.rb +10 -4
  27. data/app/controllers/concerns/decidim/devise_controllers.rb +1 -0
  28. data/app/controllers/concerns/decidim/force_authentication.rb +1 -1
  29. data/app/controllers/concerns/decidim/paginable.rb +1 -1
  30. data/app/controllers/concerns/decidim/use_organization_time_zone.rb +1 -1
  31. data/app/controllers/decidim/application_controller.rb +1 -0
  32. data/app/controllers/decidim/gamification/badges_controller.rb +2 -0
  33. data/app/controllers/decidim/links_controller.rb +15 -2
  34. data/app/helpers/concerns/decidim/flash_helper_extensions.rb +2 -2
  35. data/app/helpers/decidim/check_boxes_tree_helper.rb +1 -2
  36. data/app/helpers/decidim/paginate_helper.rb +3 -5
  37. data/app/mailers/decidim/application_mailer.rb +40 -6
  38. data/app/models/decidim/attachment.rb +3 -3
  39. data/app/models/decidim/component.rb +4 -1
  40. data/app/models/decidim/content_block.rb +2 -2
  41. data/app/models/decidim/user.rb +12 -12
  42. data/app/packs/src/decidim/a11y.js +14 -0
  43. data/app/packs/src/decidim/abide_form_validator_fixer.js +44 -0
  44. data/app/packs/src/decidim/direct_uploads/upload_modal.js +2 -6
  45. data/app/packs/src/decidim/index.js +29 -1
  46. data/app/packs/src/decidim/input_character_counter.js +1 -1
  47. data/app/packs/stylesheets/decidim/_accordion.scss +2 -2
  48. data/app/packs/stylesheets/decidim/_cards.scss +2 -2
  49. data/app/packs/stylesheets/decidim/_dropdown.scss +9 -9
  50. data/app/packs/stylesheets/decidim/_forms.scss +4 -4
  51. data/app/packs/stylesheets/decidim/_layout.scss +3 -3
  52. data/app/packs/stylesheets/decidim/_modal_update.scss +1 -3
  53. data/app/packs/stylesheets/decidim/_tooltip.scss +10 -10
  54. data/app/packs/stylesheets/decidim/editor.scss +1 -1
  55. data/app/presenters/decidim/admin_log/organization_presenter.rb +1 -1
  56. data/app/presenters/decidim/log/resource_presenter.rb +7 -1
  57. data/app/services/decidim/download_your_data_exporter.rb +36 -25
  58. data/app/services/decidim/log/diff_changeset_calculator.rb +1 -1
  59. data/app/services/decidim/open_data_exporter.rb +8 -7
  60. data/app/views/decidim/account/show.html.erb +2 -2
  61. data/app/views/decidim/application/_document.html.erb +2 -2
  62. data/app/views/decidim/endorsements/update_buttons_and_counters.js.erb +2 -1
  63. data/app/views/decidim/gamification/badges/index.html.erb +34 -33
  64. data/app/views/decidim/links/_modal.html.erb +1 -1
  65. data/app/views/decidim/links/new.html.erb +3 -1
  66. data/app/views/decidim/manifests/show.json.erb +5 -5
  67. data/app/views/decidim/messaging/conversations/create.js.erb +1 -1
  68. data/app/views/decidim/notifications_settings/show.html.erb +6 -6
  69. data/app/views/decidim/pages/_tabbed.html.erb +2 -2
  70. data/app/views/decidim/searches/_filters.html.erb +2 -2
  71. data/app/views/decidim/shared/_filters.html.erb +2 -2
  72. data/app/views/decidim/shared/_orders.html.erb +2 -2
  73. data/app/views/decidim/shared/filters/_collection.html.erb +5 -3
  74. data/app/views/decidim/shared/filters/_dropdown_label.html.erb +21 -19
  75. data/app/views/layouts/decidim/_logo.html.erb +1 -1
  76. data/app/views/layouts/decidim/_wrapper.html.erb +1 -1
  77. data/app/views/layouts/decidim/footer/_main_intro.html.erb +1 -1
  78. data/app/views/layouts/decidim/footer/_main_links.html.erb +3 -1
  79. data/app/views/layouts/decidim/header/_main_links_desktop.html.erb +5 -3
  80. data/app/views/layouts/decidim/header/_main_links_mobile_account.html.erb +1 -1
  81. data/app/views/layouts/decidim/header/_menu_breadcrumb_items.html.erb +2 -0
  82. data/app/views/layouts/decidim/shared/_layout_user_profile.html.erb +2 -2
  83. data/config/locales/ar.yml +1 -5
  84. data/config/locales/bg.yml +878 -1
  85. data/config/locales/ca.yml +5 -3
  86. data/config/locales/cs.yml +3 -1
  87. data/config/locales/de.yml +3 -1
  88. data/config/locales/el.yml +8 -1
  89. data/config/locales/en.yml +3 -1
  90. data/config/locales/es-MX.yml +8 -6
  91. data/config/locales/es-PY.yml +8 -6
  92. data/config/locales/es.yml +32 -30
  93. data/config/locales/eu.yml +4 -2
  94. data/config/locales/fi-plain.yml +6 -4
  95. data/config/locales/fi.yml +32 -30
  96. data/config/locales/fr-CA.yml +2 -0
  97. data/config/locales/fr.yml +2 -0
  98. data/config/locales/ga-IE.yml +8 -0
  99. data/config/locales/gl.yml +1 -0
  100. data/config/locales/hu.yml +1 -2
  101. data/config/locales/is-IS.yml +3 -0
  102. data/config/locales/it.yml +7 -1
  103. data/config/locales/ja.yml +3 -1
  104. data/config/locales/kaa.yml +5 -0
  105. data/config/locales/lb.yml +7 -1
  106. data/config/locales/lt.yml +8 -2
  107. data/config/locales/lv.yml +8 -1
  108. data/config/locales/nl.yml +7 -1
  109. data/config/locales/no.yml +7 -1
  110. data/config/locales/pl.yml +35 -0
  111. data/config/locales/pt-BR.yml +0 -1
  112. data/config/locales/pt.yml +7 -1
  113. data/config/locales/ro-RO.yml +8 -0
  114. data/config/locales/ru.yml +8 -0
  115. data/config/locales/sk.yml +8 -1
  116. data/config/locales/sl.yml +8 -0
  117. data/config/locales/sv.yml +132 -88
  118. data/config/locales/tr-TR.yml +21 -4
  119. data/config/locales/uk.yml +10 -0
  120. data/config/locales/zh-CN.yml +0 -1
  121. data/config/locales/zh-TW.yml +8 -1
  122. data/db/migrate/20181025082245_add_timestamps_to_components.rb +5 -1
  123. data/decidim-core.gemspec +0 -1
  124. data/lib/decidim/asset_router/storage.rb +214 -11
  125. data/lib/decidim/core/engine.rb +8 -0
  126. data/lib/decidim/core/seeds.rb +1 -1
  127. data/lib/decidim/core/test/shared_examples/attachable_interface_examples.rb +1 -1
  128. data/lib/decidim/core/test/shared_examples/comments_examples.rb +76 -6
  129. data/lib/decidim/core/test/shared_examples/follows_examples.rb +8 -3
  130. data/lib/decidim/core/test/shared_examples/logo_email.rb +2 -2
  131. data/lib/decidim/core/test/shared_examples/paginated_resource_examples.rb +5 -5
  132. data/lib/decidim/core/version.rb +1 -1
  133. data/lib/decidim/core.rb +6 -1
  134. data/lib/decidim/events/base_event.rb +4 -0
  135. data/lib/decidim/organization_settings.rb +10 -2
  136. data/lib/decidim/seven_zip_wrapper.rb +29 -0
  137. data/lib/tasks/upgrade/decidim_fix_categorization.rake +101 -1
  138. metadata +13 -25
  139. data/app/services/decidim/zip_stream/writer.rb +0 -39
@@ -71,6 +71,7 @@ tr:
71
71
  accessibility:
72
72
  external_link: Dış bağlantı
73
73
  logo: "%{organization} resmi logosu"
74
+ opens_in_new_tab: Yeni Seçmede Aç
74
75
  skip_button: Ana içeriğe geç
75
76
  account:
76
77
  delete:
@@ -416,6 +417,7 @@ tr:
416
417
  hide_password: Şifrenizi Gizleyin
417
418
  show_password: Şifreyi Göster
418
419
  shown_password: Şifreniz Gösteriliyor
420
+ placeholder_email: selam@example.org
419
421
  doorkeeper:
420
422
  authorizations:
421
423
  new:
@@ -746,6 +748,7 @@ tr:
746
748
  admin_in_group: Bunu, %{group} grubunun yöneticisi olduğunuz için alıyorsunuz.
747
749
  greeting: Merhaba, %{recipient}!
748
750
  intro: "%{manager}, %{group} adına yeni bir görüşme başlattı. Görmek için buraya tıklayın:"
751
+ outro: Platformun tadını çıkarın!
749
752
  subject: "%{manager}, %{group} olarak yeni bir görüşme başlattı"
750
753
  comanagers_new_message:
751
754
  admin_in_group: Bunu, %{group} grubunun yöneticisi olduğunuz için alıyorsunuz.
@@ -755,20 +758,24 @@ tr:
755
758
  new_conversation:
756
759
  greeting: Merhaba %{recipient}!
757
760
  intro: "%{sender} sizinle yeni bir sohbet başlattı. Görmek için buraya tıklayın:"
761
+ outro: Platformun tadını çıkarın!
758
762
  subject: "%{sender} sizinle bir konuşma başlattı"
759
763
  new_group_conversation:
760
764
  admin_in_group: Bunu, %{group} grubunun yöneticisi olduğunuz için alıyorsunuz.
761
765
  greeting: Merhaba, %{recipient}!
762
766
  intro: "%{sender}, %{group} adına yeni bir görüşme başlattı. Görmek için buraya tıklayın:"
767
+ outro: Platformun tadını çıkarın!
763
768
  subject: "%{sender}, %{group} olarak yeni bir görüşme başlattı"
764
769
  new_group_message:
765
770
  admin_in_group: Bunu, %{group} grubunun yöneticisi olduğunuz için alıyorsunuz.
766
771
  greeting: Merhaba, %{recipient}!
767
772
  intro: "%{sender}, %{group} grubuyla bir görüşmede yeni mesajlar yayınladı. Görmek için buraya tıklayın:"
773
+ outro: Platformun tadını çıkarın!
768
774
  subject: "%{group}, %{sender} adlı kişiden yeni mesajlar aldı"
769
775
  new_message:
770
776
  greeting: Merhaba %{recipient}!
771
777
  intro: "%{sender}, konuşmanıza yeni mesajlar gönderdi. Görmek için buraya tıklayın:"
778
+ outro: Platformun tadını çıkarın!
772
779
  subject: '%{sender}, size yeni mesaj gönderdi'
773
780
  conversations:
774
781
  add_conversation_users:
@@ -844,6 +851,8 @@ tr:
844
851
  subject: '%{organization_name} ile ilgili bilgileri almaya devam etmek istiyor musunuz?'
845
852
  notifications:
846
853
  no_notifications: Henüz bildirim yok.
854
+ show:
855
+ moderated: İçerik Denetlendi
847
856
  notifications_settings:
848
857
  show:
849
858
  direct_messages: Herkesten doğrudan mesaj alın
@@ -1019,6 +1028,7 @@ tr:
1019
1028
  filters:
1020
1029
  areas: alanlar
1021
1030
  select_an_area: Bir bölge seçin
1031
+ progress: "Süreç\nİlerleme"
1022
1032
  reference:
1023
1033
  reference: 'Referans: %{reference}'
1024
1034
  represent_user_group:
@@ -1182,13 +1192,13 @@ tr:
1182
1192
  sessions:
1183
1193
  already_signed_out: Başarıyla Çıkış Yapıldı
1184
1194
  new:
1185
- log_in: Giriş Yapın
1195
+ log_in: "Giriş \nGiriş Yapın "
1186
1196
  signed_out:
1187
1197
  shared:
1188
1198
  links:
1189
1199
  back: Geri
1190
1200
  forgot_your_password: Parolanızı mı unuttunuz?
1191
- log_in: Giriş Yapın
1201
+ log_in: "Giriş \nGiriş Yapın "
1192
1202
  log_in_with_provider: '%{provider} ile giriş yapın'
1193
1203
  sign_up: kaydol
1194
1204
  minimum_password_length:
@@ -1229,6 +1239,8 @@ tr:
1229
1239
  timestamp_error_message: Üzgünüm, bu çok hızlıydı! Lütfen yeniden gönderin.
1230
1240
  layouts:
1231
1241
  decidim:
1242
+ announcements:
1243
+ view_less: Daha Az Göster
1232
1244
  data_consent:
1233
1245
  details:
1234
1246
  columns:
@@ -1239,11 +1251,11 @@ tr:
1239
1251
  cc_by_license: Kar Amaçsız Lisanslama (Creative Commons License)
1240
1252
  decidim_logo: Decidim Logo
1241
1253
  download_open_data: Açık Veri dosyalarını indir
1242
- log_in: Giriş Yapın
1254
+ log_in: "Giriş \nGiriş Yapın "
1243
1255
  made_with_open_source: Web sitesi <a target="_blank" href="https://github.com/decidim/decidim">ücretsiz yazılımla yapılmıştır</a>.
1244
1256
  sign_up: Kaydol
1245
1257
  header:
1246
- log_in: Giriş Yapın
1258
+ log_in: "Giriş \nGiriş Yapın "
1247
1259
  impersonation_warning:
1248
1260
  close_session: Oturumu kapat
1249
1261
  description_html: Kullanıcının kimliğine bürünüyorsunuz <b>%{user_name}</b>.
@@ -1253,6 +1265,9 @@ tr:
1253
1265
  notifications_dashboard:
1254
1266
  mark_all_as_read: Tümünü okundu olarak işaretle
1255
1267
  mark_as_read: Okundu olarak işaretle
1268
+ shared:
1269
+ layout_item:
1270
+ prev: Önceki
1256
1271
  social_media_links:
1257
1272
  facebook: "Facebook'ta %{organization}"
1258
1273
  github: "GitHub'da %{organization}"
@@ -1332,6 +1347,7 @@ tr:
1332
1347
  long_dashed: "%Y-%m-%d %H:%M:%S"
1333
1348
  short: "%d/%m/%Y %H:%M"
1334
1349
  time_of_day: "%H:%M"
1350
+ tooltip: "%d-%m-%Y %H:%M %p %Z (GMT %:z)\n%Gün-%Ay-%Yıl %Saat:%Dakika %p %Zone (GMT %:z)"
1335
1351
  versions:
1336
1352
  directions:
1337
1353
  left: Silme
@@ -1343,6 +1359,7 @@ tr:
1343
1359
  option_split: Yan yana
1344
1360
  option_unescaped: Yakalandı
1345
1361
  option_unified: Birleşik
1362
+ toggle: Görünümü değiştir
1346
1363
  views:
1347
1364
  pagination:
1348
1365
  next_title: Sonraki sayfaya git
@@ -201,6 +201,7 @@ uk:
201
201
  registrations:
202
202
  new:
203
203
  already_have_an_account?: Вже маєте обліковий запис?
204
+ log_in: Увійти
204
205
  newsletter: Отримувати іноді розсилання зі свіжими новинами
205
206
  newsletter_title: Дозвіл на зв'язок
206
207
  sign_up: Зареєструватися
@@ -476,6 +477,12 @@ uk:
476
477
  passwords:
477
478
  edit:
478
479
  new_password: Пароль
480
+ sessions:
481
+ new:
482
+ log_in: Увійти
483
+ shared:
484
+ links:
485
+ log_in: Увійти
479
486
  doorkeeper:
480
487
  scopes:
481
488
  public: Оприлюднені відомості про вас.
@@ -493,8 +500,11 @@ uk:
493
500
  edit_link:
494
501
  edit: Редагувати
495
502
  footer:
503
+ log_in: Увійти
496
504
  made_with_open_source: Цей веб-сайт створено за допомогою <a target="_blank" href="https://github.com/decidim/decidim">безкоштовного програмного забезпечення</a>.
497
505
  sign_up: Зареєструватися
506
+ header:
507
+ log_in: Увійти
498
508
  impersonation_warning:
499
509
  close_session: Завершити сеанс
500
510
  description_html: Ви виступаєте в ролі учасника <b>%{user_name}</b>.
@@ -192,7 +192,6 @@ zh-CN:
192
192
  '1': 创建您的修改
193
193
  '2': 比较您的修改
194
194
  '3': 完成您的修改
195
- '4': 发布您的修改
196
195
  anonymous_user: 匿名的
197
196
  application:
198
197
  document:
@@ -315,7 +315,6 @@ zh-TW:
315
315
  '1': 創建您的修正案
316
316
  '2': 比較您的修正案
317
317
  '3': 完成您的修正案
318
- '4': 發佈您的修正案
319
318
  anonymous_user: 匿名
320
319
  application:
321
320
  document:
@@ -514,6 +513,7 @@ zh-TW:
514
513
  registrations:
515
514
  new:
516
515
  already_have_an_account?: 已經有帳號了嗎?
516
+ log_in: 登入
517
517
  newsletter: 偶爾收到包含相關信息的時事通訊。
518
518
  newsletter_title: 聯絡人權限
519
519
  sign_up: 註冊登記
@@ -819,6 +819,7 @@ zh-TW:
819
819
  filename: 檔案名稱
820
820
  remove: 移除
821
821
  replace: 替換
822
+ save: 儲存
822
823
  title: 標題
823
824
  title_required: 標題為必填欄位!
824
825
  uploaded: 已上傳
@@ -1565,12 +1566,16 @@ zh-TW:
1565
1566
  signed_up_but_unconfirmed: 確認信件已送至您的 Email 信箱,請點擊信件內連結以啓動您的帳號。
1566
1567
  update_needs_confirmation: 您已經成功的更新帳號資訊,但我們仍需確認您的電子信箱,請至新信箱收信並點擊連結以確認您的新電子郵件帳號。
1567
1568
  updated: 您的帳號已成功升級。
1569
+ sessions:
1570
+ new:
1571
+ log_in: 登入
1568
1572
  shared:
1569
1573
  links:
1570
1574
  back: 返回
1571
1575
  didn_t_receive_confirmation_instructions: 沒有收到確認的說明嗎?
1572
1576
  didn_t_receive_unlock_instructions: 沒有收到解鎖說明?
1573
1577
  forgot_your_password: 忘記密碼了嗎?
1578
+ log_in: 登入
1574
1579
  sign_up: 註冊登記
1575
1580
  minimum_password_length:
1576
1581
  other: "One\n(最少%{count} 個字符)\n\nOther\n(最少%{count} 個字符)"
@@ -1695,9 +1700,11 @@ zh-TW:
1695
1700
  data_consent_settings: Cookie 設定
1696
1701
  decidim_logo: Decidim 商標
1697
1702
  download_open_data: 下載開放數據檔案
1703
+ log_in: 登入
1698
1704
  made_with_open_source: 使用<a target="_blank" href="https://github.com/decidim/decidim">自由軟體</a>建立的網站。
1699
1705
  sign_up: 註冊
1700
1706
  header:
1707
+ log_in: 登入
1701
1708
  main_menu: 主選單
1702
1709
  user_menu: 使用者選單
1703
1710
  impersonation_warning:
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class AddTimestampsToComponents < ActiveRecord::Migration[5.2]
4
+ class Component < ApplicationRecord
5
+ self.table_name = :decidim_components
6
+ end
7
+
4
8
  def change
5
9
  add_timestamps :decidim_components, null: true
6
10
  # rubocop:disable Rails/SkipsModelValidations
7
- Decidim::Component.update_all(created_at: Time.current, updated_at: Time.current)
11
+ Component.update_all(created_at: Time.current, updated_at: Time.current)
8
12
  # rubocop:enable Rails/SkipsModelValidations
9
13
  change_column_null :decidim_components, :created_at, false
10
14
  change_column_null :decidim_components, :updated_at, false
data/decidim-core.gemspec CHANGED
@@ -79,7 +79,6 @@ Gem::Specification.new do |s|
79
79
  s.add_dependency "request_store", "~> 1.5.0"
80
80
  s.add_dependency "rubyXL", "~> 3.4"
81
81
  s.add_dependency "rubyzip", "~> 2.0"
82
- s.add_dependency "seven_zip_ruby", "~> 1.3"
83
82
  s.add_dependency "shakapacker", "~> 7.1.0"
84
83
  s.add_dependency "valid_email2", "~> 4.0"
85
84
  s.add_dependency "web-push", "~> 3.0"
@@ -6,6 +6,23 @@ module Decidim
6
6
  # saved through ActiveStorage. This handles the different cases for routing
7
7
  # to the remote routes when using an assets CDN or to local routes when
8
8
  # using the local disk storage driver.
9
+ #
10
+ # Note that when the assets are stored in a remote storage service, such as
11
+ # Amazon S3, Google Cloud Storage or Azure Storage, this generates the asset
12
+ # URL directly to the storage service itself bypassing the Rails server and
13
+ # saving CPU time from serving the asset redirect requests. This causes a
14
+ # significant performance improvement on pages that display a lot of images.
15
+ # It will also produce a less significant performance improvement when using
16
+ # the local disk storage because in this situation, the images are served
17
+ # using one request instead of two when served directly from the storage
18
+ # service rather than through the asset redirect URL.
19
+ #
20
+ # When implementing changes to the logic, please keep the remote storage
21
+ # options and performance implications in mind because the specs for this
22
+ # utility do not cover the remote storage options because the extra
23
+ # configuration needed to test, the service itself needed for testing and
24
+ # the extra dependency overhead for adding these remote storage gems when
25
+ # they are not needed.
9
26
  class Storage
10
27
  # Initializes the router.
11
28
  #
@@ -13,25 +30,36 @@ module Decidim
13
30
  # to
14
31
  def initialize(asset)
15
32
  @asset = asset
33
+ @blob =
34
+ case asset
35
+ when ActiveStorage::Blob
36
+ asset
37
+ else
38
+ asset&.blob
39
+ end
16
40
  end
17
41
 
18
42
  # Generates the correct URL to the asset with the provided options.
19
43
  #
20
44
  # @param options The options for the URL that are the normal route options
21
45
  # Rails route helpers accept
46
+ # @return [String] The URL of the asset
22
47
  def url(**options)
23
- if asset.is_a? ActiveStorage::Attached
24
- routes.rails_blob_url(asset.blob, **default_options.merge(options))
25
- elsif asset.is_a? ActiveStorage::Blob
26
- routes.rails_blob_url(asset, **default_options.merge(options))
27
- else
48
+ case asset
49
+ when ActiveStorage::Attached
50
+ ensure_current_host(asset.record, **options)
51
+ blob_url(**options)
52
+ when ActiveStorage::Blob
53
+ blob_url(**options)
54
+ else # ActiveStorage::VariantWithRecord, ActiveStorage::Variant
55
+ ensure_current_host(nil, **options)
28
56
  representation_url(**options)
29
57
  end
30
58
  end
31
59
 
32
60
  private
33
61
 
34
- attr_reader :asset
62
+ attr_reader :asset, :blob
35
63
 
36
64
  # Provides the route helpers depending on whether the URL is generated to
37
65
  # the local host or an external CDN (remote).
@@ -80,24 +108,199 @@ module Decidim
80
108
  }.compact
81
109
  end
82
110
 
83
- # Converts the variation URLs last part to the correct file extension in
84
- # case the variation has a different format than the original image.
111
+ # Most of the times the current host should be set through the controller
112
+ # already when the logic below is unnecessary. This logic is needed e.g.
113
+ # for serializers where the request context is not available.
114
+ #
115
+ # @param record The record for which to check the organization
116
+ # @param opts Options for building the URL
117
+ # @return [void]
118
+ def ensure_current_host(record, **opts)
119
+ return if asset_url_available?
120
+
121
+ options = remote? ? remote_storage_options : routes.default_url_options
122
+ options = options.merge(opts)
123
+
124
+ if opts[:host].blank? && record.present?
125
+ organization = organization_for(record)
126
+ options[:host] = organization.host if organization
127
+ end
128
+
129
+ uri =
130
+ if options[:protocol] == "https" || options[:scheme] == "https"
131
+ URI::HTTPS.build(options)
132
+ else
133
+ URI::HTTP.build(options)
134
+ end
135
+
136
+ ActiveStorage::Current.host = uri.to_s
137
+ end
138
+
139
+ # Determines the organization for the passed record.
85
140
  #
86
- # @return [String] The converted representation URL
141
+ # @param record The record for which to fetch the organization
142
+ # @return [Decidim::Organization, nil] The organization for the record or
143
+ # `nil` if the organization cannot be determined
144
+ def organization_for(record)
145
+ if record.is_a?(Decidim::Organization)
146
+ record
147
+ elsif record.respond_to?(:organization)
148
+ record.organization
149
+ end
150
+ end
151
+
152
+ # Returns the URL for the given blob object.
153
+ #
154
+ # @param blob The blob object
155
+ # @param options Options for building the URL
156
+ # @return [String, nil] The URL to the blob object or `nil` if the blob is
157
+ # not defined.
158
+ def blob_url(**options)
159
+ return unless blob
160
+
161
+ if options[:only_path] || remote? || !asset_url_available?
162
+ routes.rails_blob_url(blob, **default_options.merge(options))
163
+ else
164
+ blob.url(**options)
165
+ end
166
+ end
167
+
168
+ # Returns a representation URL for the asset either directly through the
169
+ # storage service or through the Rails representation URL in case the
170
+ # path URL is requested or if the asset variant has not been processed yet
171
+ # and is not therefore yet stored at the storage service.
172
+ #
173
+ # @return [String] The representation URL for the image variant
87
174
  def representation_url(**options)
175
+ return rails_representation_url(**options) if options[:only_path] || remote?
176
+
177
+ representation_url = variant_url(**options)
178
+ return representation_url if representation_url.present?
179
+
180
+ # In case the representation has not been processed yet, it may not have
181
+ # a representation URL yet and it therefore needs to be served through
182
+ # the local representation URL for the first time (or until it has been
183
+ # processed).
184
+ if options[:host]
185
+ rails_representation_url(**options)
186
+ else
187
+ representation_url(**options.merge(only_path: true))
188
+ end
189
+ end
190
+
191
+ # Returns the local Rails representation URL meaning that the asset will
192
+ # be served through the service itself. This may be necessary if the asset
193
+ # variant (e.g. a thumbnail) has not been processed yet because the
194
+ # variant representation has not been requested before.
195
+ #
196
+ # Due to performance reasons it is advised to avoid requesting the assets
197
+ # through the Rails representation URLs when possible because that causes
198
+ # a lot of requests to the Rails backend and slowness to the service under
199
+ # heavy loads.
200
+ #
201
+ # Converts the variation URLs last part to the correct file extension in
202
+ # case the variation has a different format than the original image. The
203
+ # conversion needs to be only done for the Rails representation URLs
204
+ # because once the image is stored at the storage service, it already has
205
+ # the correct file extension.
206
+ #
207
+ # @param options The options for building the URL
208
+ # @return [String, nil] The converted representation URL or `nil` if the
209
+ # asset is not defined.
210
+ def rails_representation_url(**options)
211
+ return unless asset
212
+
88
213
  representation_url = routes.rails_representation_url(asset, **default_options.merge(options))
214
+
89
215
  variation = asset.try(:variation)
90
216
  return representation_url unless variation
91
217
 
92
218
  format = variation.try(:format)
93
219
  return representation_url unless format
220
+ return unless blob
94
221
 
95
- original_ext = File.extname(asset.blob.filename.to_s)
222
+ original_ext = File.extname(blob.filename.to_s)
96
223
  return representation_url if original_ext == ".#{format}"
97
224
 
98
- basename = File.basename(asset.blob.filename.to_s, original_ext)
225
+ basename = File.basename(blob.filename.to_s, original_ext)
99
226
  representation_url.sub(/#{basename}\.#{original_ext.tr(".", "")}$/, "#{basename}.#{format}")
100
227
  end
228
+
229
+ # Fetches the image variant's URL at the storage service if the variant
230
+ # has already been processed and is stored at the storage service. If the
231
+ # variant has not been processed yet, returns `nil` in which case the
232
+ # variant has to be served through the service's own representation URL
233
+ # causing it to be processed and stored at the storage service.
234
+ #
235
+ # @param options The options for building the URL
236
+ # @return [String, nil] The variant URL at the storage service or `nil` if
237
+ # the variant has not been processed yet and does not yet exist at the
238
+ # storage service or `nil` when the asset is not defined
239
+ def variant_url(**options)
240
+ return unless asset
241
+ return unless asset_url_available?
242
+ return unless asset_exist?
243
+
244
+ case asset
245
+ when ActiveStorage::VariantWithRecord
246
+ # This is used when `ActiveStorage.track_variants` is enabled through
247
+ # `config.active_storage.track_variants`. In case the variant has not
248
+ # been processed yet, the `#url` method would return nil.
249
+ #
250
+ # Note that if the `asset.processed?` returns `true`, the variant
251
+ # record has been created in the database but it does not mean that
252
+ # it has been uploaded to the storage service yet. Likely a bug in
253
+ # ActiveStorage but to be sure that the asset is uploaded to the
254
+ # storage service, we also check that.
255
+ asset.url(**options) if asset.processed?
256
+ else # ActiveStorage::Variant
257
+ # Check whether the variant exists at the storage service before
258
+ # returning its URL. Otherwise the URL would be returned even when the
259
+ # variant is not yet processed causing 404 errors for the images on
260
+ # the page.
261
+ #
262
+ # Note that the `ActiveStorage::Variant#url` method only accepts
263
+ # certain keyword arguments where as the other objects allow any
264
+ # keyword arguments.
265
+ possible_kwargs = asset.method(:url).parameters.select { |p| p[0] == :key }.map { |p| p[1] }
266
+ asset.url(**options.slice(*possible_kwargs))
267
+ end
268
+ end
269
+
270
+ # Determines if the asset exists at the storage service.
271
+ #
272
+ # @return [Boolean] A boolean answering the question "does this asset
273
+ # exist at the storage service?".
274
+ def asset_exist?
275
+ return false if asset.key.blank?
276
+
277
+ blob.service.exist?(asset.key)
278
+ end
279
+
280
+ # Determines if the current host is required to build the asset URL.
281
+ #
282
+ # @return [Boolean] A boolean indicating if the current host is required
283
+ # to build the asset URL.
284
+ def current_host_required?
285
+ return false unless blob
286
+ return false unless defined?(ActiveStorage::Service::DiskService)
287
+
288
+ blob.service.is_a?(ActiveStorage::Service::DiskService)
289
+ end
290
+
291
+ # Determines if the asset URL can be generated.
292
+ #
293
+ # @return [Boolean] A boolean indicating if the asset URL can be
294
+ # generated.
295
+ def asset_url_available?
296
+ # If the service is an external service, the URL can be generated
297
+ # regardless of the current host being set.
298
+ return true unless current_host_required?
299
+
300
+ # For the disk service, the URL can be only generated if the current
301
+ # host has been set.
302
+ ActiveStorage::Current.host.present?
303
+ end
101
304
  end
102
305
  end
103
306
  end
@@ -237,6 +237,14 @@ module Decidim
237
237
  app.config.action_mailer.deliver_later_queue_name = :mailers
238
238
  end
239
239
 
240
+ initializer "decidim_core.signed_global_id", after: "global_id" do |app|
241
+ next if app.config.global_id.fetch(:expires_in, nil).present?
242
+
243
+ config.after_initialize do
244
+ SignedGlobalID.expires_in = nil
245
+ end
246
+ end
247
+
240
248
  initializer "decidim_core.middleware" do |app|
241
249
  if app.config.public_file_server.enabled
242
250
  headers = app.config.public_file_server.headers || {}
@@ -209,7 +209,7 @@ module Decidim
209
209
 
210
210
  oauth_application.organization_logo.attach(io: File.open(File.join(seeds_root, "homepage_image.jpg")), filename: "organization_logo.jpg", content_type: "image/jpeg")
211
211
 
212
- Decidim::System::CreateDefaultContentBlocks.call(organization)
212
+ Decidim::ContentBlocksCreator.new(organization).create_default!
213
213
 
214
214
  hero_content_block = Decidim::ContentBlock.find_by(organization:, manifest_name: :hero, scope_name: :homepage)
215
215
  hero_content_block.images_container.background_image = create_blob!(seeds_file: "homepage_image.jpg", filename: "homepage_image.jpg", content_type: "image/jpeg")
@@ -10,7 +10,7 @@ shared_examples_for "attachable interface" do
10
10
 
11
11
  it "includes the attachment urls" do
12
12
  attachment_urls = response["attachments"].map { |attachment| attachment["url"] }
13
- expect(attachment_urls).to include(*attachments.map(&:url))
13
+ expect(attachment_urls).to include_blob_urls(*attachments.map(&:file).map(&:blob))
14
14
  end
15
15
  end
16
16
  end