decidim-core 0.30.1 → 0.30.3

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity/show.erb +6 -6
  3. data/app/cells/decidim/author/badge.erb +6 -0
  4. data/app/cells/decidim/author/show.erb +5 -1
  5. data/app/cells/decidim/author_cell.rb +10 -0
  6. data/app/cells/decidim/content_blocks/participatory_space_main_data/title.erb +11 -2
  7. data/app/cells/decidim/footer_topics/show.erb +2 -2
  8. data/app/cells/decidim/group_admins/show.erb +3 -1
  9. data/app/cells/decidim/group_members/show.erb +6 -2
  10. data/app/cells/decidim/images_panel/show.erb +5 -2
  11. data/app/cells/decidim/profile/avatar.erb +0 -2
  12. data/app/cells/decidim/profile/badge.erb +3 -3
  13. data/app/cells/decidim/profile/details.erb +2 -1
  14. data/app/cells/decidim/report_button/flag_modal.erb +11 -9
  15. data/app/cells/decidim/report_user_button/flag_modal.erb +11 -10
  16. data/app/cells/decidim/upload_modal/files.erb +4 -4
  17. data/app/cells/decidim/upload_modal_cell.rb +5 -3
  18. data/app/cells/decidim/user_activity_cell.rb +6 -1
  19. data/app/commands/decidim/amendable/accept.rb +2 -1
  20. data/app/commands/decidim/create_omniauth_registration.rb +1 -1
  21. data/app/controllers/decidim/amendments_controller.rb +3 -3
  22. data/app/forms/decidim/user_group_form.rb +2 -0
  23. data/app/helpers/decidim/amendments_helper.rb +2 -1
  24. data/app/helpers/decidim/filters_helper.rb +25 -0
  25. data/app/helpers/decidim/layout_helper.rb +6 -0
  26. data/app/helpers/decidim/map_helper.rb +1 -1
  27. data/app/helpers/decidim/menu_helper.rb +8 -2
  28. data/app/packs/images/decidim/default-avatar.svg +1 -1
  29. data/app/packs/src/decidim/callout.js +13 -8
  30. data/app/packs/src/decidim/confirm.js +15 -3
  31. data/app/packs/src/decidim/datepicker/generate_datepicker.js +2 -0
  32. data/app/packs/src/decidim/datepicker/generate_timepicker.js +2 -0
  33. data/app/packs/src/decidim/direct_uploads/upload_field.js +3 -4
  34. data/app/packs/src/decidim/direct_uploads/upload_modal.js +8 -9
  35. data/app/packs/src/decidim/dropdown_menu.js +18 -0
  36. data/app/packs/src/decidim/editor/common/suggestion.js +11 -1
  37. data/app/packs/src/decidim/form_filter.js +6 -0
  38. data/app/packs/src/decidim/index.js +1 -0
  39. data/app/packs/stylesheets/decidim/_activity.scss +4 -4
  40. data/app/packs/stylesheets/decidim/_author.scss +8 -0
  41. data/app/packs/stylesheets/decidim/_cards.scss +10 -2
  42. data/app/packs/stylesheets/decidim/_filters.scss +1 -1
  43. data/app/packs/stylesheets/decidim/_header.scss +11 -3
  44. data/app/packs/stylesheets/decidim/_layout.scss +2 -2
  45. data/app/packs/stylesheets/decidim/_modal.scss +1 -5
  46. data/app/packs/stylesheets/decidim/_modal_update.scss +5 -1
  47. data/app/packs/stylesheets/decidim/_profile.scss +6 -6
  48. data/app/packs/stylesheets/decidim/editor.scss +3 -1
  49. data/app/permissions/decidim/permissions.rb +13 -1
  50. data/app/validators/etiquette_validator.rb +2 -2
  51. data/app/validators/password_validator.rb +3 -1
  52. data/app/validators/translated_etiquette_validator.rb +2 -0
  53. data/app/views/decidim/application/_document.html.erb +2 -2
  54. data/app/views/decidim/errors/internal_server_error.html.erb +1 -1
  55. data/app/views/decidim/errors/not_found.html.erb +1 -1
  56. data/app/views/decidim/newsletters/unsubscribe.html.erb +16 -4
  57. data/app/views/decidim/searches/_filters.html.erb +48 -13
  58. data/app/views/decidim/shared/_component_announcement.html.erb +1 -1
  59. data/app/views/decidim/shared/_confirm_modal.html.erb +3 -5
  60. data/app/views/decidim/shared/_filters.html.erb +7 -5
  61. data/app/views/layouts/decidim/_application.html.erb +1 -1
  62. data/app/views/layouts/decidim/footer/_main.html.erb +1 -1
  63. data/app/views/layouts/decidim/footer/_main_intro.html.erb +1 -1
  64. data/app/views/layouts/decidim/footer/_mini.html.erb +2 -2
  65. data/app/views/layouts/decidim/header/_main.html.erb +2 -2
  66. data/app/views/layouts/decidim/header/_main_links_desktop.html.erb +6 -0
  67. data/app/views/layouts/decidim/header/_main_links_dropdown.html.erb +2 -0
  68. data/app/views/layouts/decidim/header/_main_links_mobile_account.html.erb +1 -1
  69. data/app/views/layouts/decidim/header/_mobile_language_choose.html.erb +1 -1
  70. data/config/locales/ar.yml +0 -3
  71. data/config/locales/bg-BG.yml +2 -2
  72. data/config/locales/bg.yml +0 -5
  73. data/config/locales/ca-IT.yml +36 -13
  74. data/config/locales/ca.yml +36 -13
  75. data/config/locales/cs.yml +20 -6
  76. data/config/locales/de.yml +54 -21
  77. data/config/locales/el.yml +0 -4
  78. data/config/locales/en.yml +28 -5
  79. data/config/locales/es-MX.yml +28 -5
  80. data/config/locales/es-PY.yml +28 -5
  81. data/config/locales/es.yml +28 -5
  82. data/config/locales/eu.yml +101 -78
  83. data/config/locales/fi-plain.yml +76 -5
  84. data/config/locales/fi.yml +77 -6
  85. data/config/locales/fr-CA.yml +28 -5
  86. data/config/locales/fr-LU.yml +3 -3
  87. data/config/locales/fr.yml +28 -5
  88. data/config/locales/gl.yml +0 -3
  89. data/config/locales/hu.yml +0 -5
  90. data/config/locales/id-ID.yml +0 -3
  91. data/config/locales/is-IS.yml +0 -1
  92. data/config/locales/it.yml +165 -3
  93. data/config/locales/ja.yml +37 -5
  94. data/config/locales/lb-LU.yml +2 -2
  95. data/config/locales/lb.yml +0 -3
  96. data/config/locales/lt.yml +0 -5
  97. data/config/locales/lv.yml +0 -3
  98. data/config/locales/nl.yml +0 -3
  99. data/config/locales/no.yml +0 -3
  100. data/config/locales/pl.yml +0 -5
  101. data/config/locales/pt-BR.yml +0 -4
  102. data/config/locales/pt.yml +0 -3
  103. data/config/locales/ro-RO.yml +0 -4
  104. data/config/locales/ru.yml +0 -3
  105. data/config/locales/sk-SK.yml +3 -3
  106. data/config/locales/sk.yml +2 -3
  107. data/config/locales/sv.yml +27 -4
  108. data/config/locales/tr-TR.yml +0 -3
  109. data/config/locales/uk.yml +0 -2
  110. data/config/locales/zh-CN.yml +0 -3
  111. data/config/locales/zh-TW.yml +0 -5
  112. data/lib/decidim/asset_router/storage.rb +8 -8
  113. data/lib/decidim/assets/tailwind/tailwind.config.js.erb +2 -1
  114. data/lib/decidim/core/test/factories.rb +2 -2
  115. data/lib/decidim/core/test/shared_examples/announcements_examples.rb +4 -0
  116. data/lib/decidim/core/test/shared_examples/comments_examples.rb +7 -5
  117. data/lib/decidim/core/version.rb +1 -1
  118. data/lib/decidim/form_builder.rb +15 -1
  119. metadata +8 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 763791440d8acd9bfdb66f0afe4d1a738f362815fd237b20b703a6fdc89edfb8
4
- data.tar.gz: e6ff16714f702a13b60bee8d4bce8fe03978be54bbeea60c779bb5aaf735661f
3
+ metadata.gz: df5ddb6f2f1e3c2557f89a0a2b9bab41c3108ad0ce0e728bfb317502cc6aa1a9
4
+ data.tar.gz: ed7e8f76a6f52fd5040a7bfbb462276d891406a13198df8571d6e154fb504a2f
5
5
  SHA512:
6
- metadata.gz: 6ca3eaa4281c6b5f676f1df20466d18f8cd1893a26645f5bdd1f00ed9efe413849250801ae49d3e8572a7ca66fe24c0fe09935fb972719b67083df9ff13b4143
7
- data.tar.gz: '068568c77bcf227bb7ef783606a63e858d59992001cc2e36d88c97212bf89fac3acefde0568f3557b4af7aa518326db6f5f10e5188d2195211a1a2b356228789'
6
+ metadata.gz: e3ee0aa2b315e4038a9737c46b8453b075d4d058569d060b777c530a4cb62b71edd37f99bddecdf50fc810e7d9995a89729c60b10cbd10363b6c14d217b069d4
7
+ data.tar.gz: 7f2b0baa65e1c613ee50d8ff74c4d1bb0a38c2224d51bfa76b34da5a44ec2024fd0e4bcba51a6664d283786ce62f0a4dbd05f4125011b8136750599106e6369d
@@ -1,19 +1,19 @@
1
1
  <div class="activity" data-activity>
2
- <div class="activity__time">
2
+ <p class="activity__time">
3
3
  <%= created_at %>
4
- </div>
4
+ </p>
5
5
  <div class="activity__content">
6
- <span>
6
+ <div>
7
7
  <% if title.present? %>
8
- <span>
8
+ <p>
9
9
  <%= title_icon %>
10
10
  <%= title %>
11
- </span>
11
+ </p>
12
12
  <% end %>
13
13
  <a href="<%= resource_link_path %>">
14
14
  <%= html_truncate decidim_sanitize(resource_link_text, strip_tags: true), length: 80 %>
15
15
  </a>
16
- </span>
16
+ </div>
17
17
  <% unless hide_participatory_space? %>
18
18
  <span>
19
19
  <%= participatory_space_link %>
@@ -0,0 +1,6 @@
1
+ <% if show_badge? %>
2
+ <span class="author__badge">
3
+ <%= icon "star-s-fill" %>
4
+ <span class="sr-only"><%= officialization_text %></span>
5
+ </span>
6
+ <% end %>
@@ -6,7 +6,10 @@
6
6
  <%= render :avatar %>
7
7
 
8
8
  <span>
9
- <%= render :name %>
9
+ <span class="flex gap-2">
10
+ <%= render :name %>
11
+ <%= render :badge %>
12
+ </span>
10
13
 
11
14
  <% context_actions.each do |action| %>
12
15
  <%= render action %>
@@ -17,6 +20,7 @@
17
20
  <% else %>
18
21
  <%= render :avatar %>
19
22
  <%= render :name %>
23
+ <%= render :badge %>
20
24
  <% end %>
21
25
  <% end %>
22
26
 
@@ -186,5 +186,15 @@ module Decidim
186
186
 
187
187
  model.has_tooltip?
188
188
  end
189
+
190
+ def show_badge?
191
+ return false unless model.respond_to? :officialized?
192
+
193
+ model.officialized?
194
+ end
195
+
196
+ def officialization_text
197
+ translated_attribute(model.officialized_as).presence || t("decidim.profiles.show.officialized")
198
+ end
189
199
  end
190
200
  end
@@ -11,11 +11,11 @@
11
11
  <div class="content-block__description editor-content" <%= "data-component='accordion'" if should_truncate %>>
12
12
  <% if should_truncate %>
13
13
  <% seed = SecureRandom.hex(4) %>
14
- <div id="panel-view-more-<%= seed %>" aria-hidden="true">
14
+ <div id="panel-view-more-<%= seed %>" aria-hidden="true" inert>
15
15
  <%= description_text %>
16
16
  </div>
17
17
 
18
- <button class="button button__sm button__text-secondary mt-2" data-controls="panel-view-more-<%= seed %>" aria-expanded="false">
18
+ <button class="button button__sm button__text-secondary mt-2" data-controls="panel-view-more-<%= seed %>" aria-expanded="false" onclick="document.querySelector('div[id^=panel-view-more]').toggleAttribute('inert')">
19
19
  <span>
20
20
  <%= t("view_more", scope: "layouts.decidim.announcements") %>
21
21
  </span>
@@ -30,3 +30,12 @@
30
30
  <% end %>
31
31
  </div>
32
32
  <% end %>
33
+ <script>
34
+ const button = document.querySelector('button[data-controls^="panel-view-more"]')
35
+ button.addEventListener('keydown', function(e){
36
+ // press space or enter
37
+ if (e.keyCode === 32 || e.keyCode === 13){
38
+ document.querySelector('div[id^=panel-view-more]').toggleAttribute('inert')
39
+ }
40
+ })
41
+ </script>
@@ -1,5 +1,5 @@
1
- <nav role="navigation" aria-label="Help">
2
- <h2 class="h4 mb-4">Help</h2>
1
+ <nav role="navigation" aria-label="<%= t("layouts.decidim.footer.help") %>">
2
+ <h2 class="h4 mb-4"><%= t("layouts.decidim.footer.help") %></h2>
3
3
  <ul class="space-y-4 break-inside-avoid">
4
4
  <% topics.each do |topic| %>
5
5
  <%= topic_item(topic, class: "font-semibold underline") %>
@@ -21,7 +21,9 @@
21
21
  <%= link_to(
22
22
  decidim.demote_group_manage_user_path(model.nickname, membership),
23
23
  method: :post,
24
- data: { confirm: t("decidim.group_admins.actions.are_you_sure") },
24
+ data: { confirm: t("decidim.group_admins.actions.confirm_remove_from_admin"),
25
+ confirm_title: t("decidim.group_admins.actions.confirm_modal.title_remove"),
26
+ confirm_button: t("decidim.group_admins.actions.confirm_modal.ok_remove") },
25
27
  class: "button button__sm button__transparent-secondary"
26
28
  ) do %>
27
29
  <span><%= t("decidim.group_admins.actions.demote_admin") %></span>
@@ -24,14 +24,18 @@
24
24
  t("decidim.group_members.actions.promote_to_admin"),
25
25
  decidim.promote_group_manage_user_path(model.nickname, membership),
26
26
  method: :post,
27
- data: { confirm: t("decidim.group_members.actions.are_you_sure") },
27
+ data: { confirm: t("decidim.group_members.actions.confirm_promote_to_admin"),
28
+ confirm_title: t("decidim.group_admins.actions.confirm_modal.title_add"),
29
+ confirm_button: t("decidim.group_admins.actions.confirm_modal.ok_add") },
28
30
  class: "button button__sm button__transparent-secondary"
29
31
  ) %>
30
32
 
31
33
  <%= link_to(
32
34
  decidim.group_manage_user_path(model.nickname, membership),
33
35
  method: :delete,
34
- data: { confirm: t("decidim.group_members.actions.are_you_sure") },
36
+ data: { confirm: t("decidim.group_members.actions.confirm_remove_from_group"),
37
+ confirm_title: t("decidim.group_members.actions.confirm_modal.title_remove"),
38
+ confirm_button: t("decidim.group_members.actions.confirm_modal.ok_remove") },
35
39
  class: "button button__sm button__transparent-secondary"
36
40
  ) do %>
37
41
  <span><%= t("decidim.group_members.actions.remove_from_group") %></span>
@@ -1,7 +1,10 @@
1
1
  <div class="grid grid-cols-1 md:grid-cols-4 gap-6">
2
- <% photos.each_with_index do |photo, index| %>
2
+ <% photos.each_with_index do |photo| %>
3
3
  <%= link_to photo.big_url, target: "_blank", rel: "noopener", class: "overflow-hidden rounded aspect-video" do %>
4
- <%= image_tag photo.thumbnail_url, class:"w-full h-full object-cover", alt: strip_tags(translated_attribute(photo.title)) %>
4
+ <%= image_tag photo.thumbnail_url,
5
+ class: "w-full h-full object-cover",
6
+ role: "presentation",
7
+ "data-filename": strip_tags(translated_attribute(photo.title)) %>
5
8
  <% end %>
6
9
  <% end %>
7
10
  </div>
@@ -2,6 +2,4 @@
2
2
  <div class="profile__avatar">
3
3
  <%= image_tag avatar_url, alt: t("decidim.author.avatar", name: decidim_sanitize(presented_profile.name)) %>
4
4
  </div>
5
-
6
- <%= render :badge if show_badge? %>
7
5
  </div>
@@ -1,4 +1,4 @@
1
- <div class="profile__avatar-badge">
1
+ <span class="profile__details-badge">
2
2
  <%= icon "star-s-fill" %>
3
- <span class="sr-only"><%= officialization_text %></span>
4
- </div>
3
+ <%= officialization_text %>
4
+ </span>
@@ -1,7 +1,8 @@
1
1
  <div class="profile__details">
2
- <h1 class="h3">
2
+ <h1 class="h3 flex gap-2">
3
3
  <%= presented_profile.name %>
4
4
  <span class="sr-only"><%= tab_items.find { |tab_item| is_active_link?(tab_item[:path]) }&.dig(:text) %> (<%= presented_profile.name %>)</span>
5
+ <%= render :badge if show_badge? %>
5
6
  </h1>
6
7
  <div class="profile__details-data">
7
8
  <% details_items.each do |detail| %>
@@ -6,16 +6,18 @@
6
6
  <div>
7
7
  <div class="form__wrapper flag-modal__form">
8
8
  <p id="dialog-desc-<%= modal_id %>" class="flag-modal__form-description"><%= t("decidim.shared.flag_modal.description") %></p>
9
- <p class="flag-modal__form-reason"><%= t("decidim.shared.flag_modal.reason") %>:</p>
10
- <%= f.collection_radio_buttons :reason, [
11
- [:spam, t("decidim.shared.flag_modal.spam")],
12
- [:offensive, t("decidim.shared.flag_modal.offensive")],
13
- [:does_not_belong, t("decidim.shared.flag_modal.does_not_belong", organization_name: current_organization_name)]
14
- ], :first, :last do |builder|
15
- builder.label(for: nil, class: "form__wrapper-checkbox-label") { builder.radio_button(id: nil) + builder.text }
16
- end %>
9
+ <fieldset class="mt-6">
10
+ <legend class="flag-modal__form-reason"><%= t("decidim.shared.flag_modal.reason") %>:</legend>
11
+ <%= f.collection_radio_buttons :reason, [
12
+ [:spam, t("decidim.shared.flag_modal.spam")],
13
+ [:offensive, t("decidim.shared.flag_modal.offensive")],
14
+ [:does_not_belong, t("decidim.shared.flag_modal.does_not_belong", organization_name: current_organization_name)]
15
+ ], :first, :last do |builder|
16
+ builder.label(for: "#{builder.value.to_s}-#{modal_id}", class: "form__wrapper-checkbox-label") { builder.radio_button(id: "#{builder.value.to_s}-#{modal_id}") + builder.text }
17
+ end %>
18
+ </fieldset>
17
19
 
18
- <%= f.text_area :details, rows: 4, label_options: { class: "flag-modal__form-textarea-label", for: nil }, id: nil %>
20
+ <%= f.text_area :details, rows: 4, label_options: { class: "flag-modal__form-textarea-label", for: "additional-comments-#{modal_id}" }, id: "additional-comments-#{modal_id}" %>
19
21
 
20
22
  <% if frontend_administrable? %>
21
23
  <%= f.check_box :hide,
@@ -6,16 +6,17 @@
6
6
  <div>
7
7
  <div class="form__wrapper flag-modal__form">
8
8
  <p class="flag-modal__form-description"><%= t("decidim.shared.flag_user_modal.description") %></p>
9
- <p class="flag-modal__form-reason"><%= t("decidim.shared.flag_modal.reason") %>:</p>
10
- <%= f.collection_radio_buttons :reason, [
11
- [:spam, t("decidim.shared.flag_user_modal.spam")],
12
- [:offensive, t("decidim.shared.flag_user_modal.offensive")],
13
- [:does_not_belong, t("decidim.shared.flag_user_modal.does_not_belong", organization_name: current_organization_name)]
14
- ], :first, :last do |builder|
15
- builder.label(for: nil, class: "form__wrapper-checkbox-label") { builder.radio_button(id: nil) + builder.text }
16
- end %>
17
-
18
- <%= f.text_area :details, rows: 4, label_options: { class: "flag-modal__form-textarea-label", for: nil }, id: nil %>
9
+ <fieldset class="mt-6">
10
+ <legend class="flag-modal__form-reason"><%= t("decidim.shared.flag_modal.reason") %>:</legend>
11
+ <%= f.collection_radio_buttons :reason, [
12
+ [:spam, t("decidim.shared.flag_user_modal.spam")],
13
+ [:offensive, t("decidim.shared.flag_user_modal.offensive")],
14
+ [:does_not_belong, t("decidim.shared.flag_user_modal.does_not_belong", organization_name: current_organization_name)]
15
+ ], :first, :last do |builder|
16
+ builder.label(for: "#{builder.value.to_s}-#{modal_id}", class: "form__wrapper-checkbox-label") { builder.radio_button(id: "#{builder.value.to_s}-#{modal_id}") + builder.text }
17
+ end %>
18
+ </fieldset>
19
+ <%= f.text_area :details, rows: 4, label_options: { class: "flag-modal__form-textarea-label", for: "additional-comments-#{modal_id}" }, id: "additional-comments-#{modal_id}" %>
19
20
 
20
21
  <% if frontend_administrable? %>
21
22
  <%= f.check_box :block,
@@ -25,19 +25,19 @@
25
25
 
26
26
  <div class="attachment-details" data-attachment-id="<%= attachment.id %>" data-title="<%= title_for(attachment) %>" data-filename="<%= file_name_for(attachment) %>" data-state="uploaded">
27
27
  <% if file_attachment_path(attachment) && blob(attachment).image? %>
28
- <div><%= image_tag(file_attachment_path(attachment), alt: title_for(attachment) || file_name_for(attachment)) %></div>
28
+ <div><%= image_tag(file_attachment_path(attachment), alt: "") %></div>
29
29
  <% elsif uploader_default_image_path(attribute).present? %>
30
30
  <div><%= image_tag uploader_default_image_path(attribute) %></div>
31
31
  <% end %>
32
32
 
33
33
  <% if has_title? %>
34
- <span><%= title_for(attachment) %> (<%= truncated_file_name_for(attachment) %>)</span>
34
+ <span><%= title_for(attachment) %></span>
35
35
  <%= form.hidden_field attribute, multiple: true, value: attachment.id, id: attachment.id %>
36
36
  <% else %>
37
37
  <% if blob(attachment).image? %>
38
- <span><%= truncated_file_name_for(attachment, 15) %></span>
38
+ <span><%= title_for(attachment) %></span>
39
39
  <% else %>
40
- <%= link_to truncated_file_name_for(attachment), file_attachment_path(attachment) %>
40
+ <%= link_to title_for(attachment), file_attachment_path(attachment), class: "w-full break-all mb-2" %>
41
41
  <% end %>
42
42
  <% end %>
43
43
  </div>
@@ -132,9 +132,11 @@ module Decidim
132
132
  end
133
133
 
134
134
  def title_for(attachment)
135
- return unless has_title?
136
-
137
- decidim_html_escape(decidim_sanitize(translated_attribute(attachment.title)))
135
+ if has_title?
136
+ decidim_html_escape(decidim_sanitize(translated_attribute(attachment.title)))
137
+ else
138
+ decidim_html_escape(decidim_sanitize(determine_filename(attachment)))
139
+ end
138
140
  end
139
141
 
140
142
  def truncated_file_name_for(attachment, max_length = 31)
@@ -11,7 +11,12 @@ module Decidim
11
11
  end
12
12
 
13
13
  def activities
14
- context[:activities]
14
+ resource_ids_to_filter = context[:activities].select { |log| log[:action] == "delete" && log[:resource_type] == "Decidim::Comments::Comment" }.map(&:resource_id)
15
+ if resource_ids_to_filter.any?
16
+ context[:activities].where.not("resource_id in (?) AND resource_type = ?", resource_ids_to_filter, "Decidim::Comments::Comment")
17
+ else
18
+ context[:activities]
19
+ end
15
20
  end
16
21
 
17
22
  def resource_types
@@ -62,7 +62,8 @@ module Decidim
62
62
  @amendable.save!
63
63
  @amendable
64
64
  end
65
- @amendable.add_coauthor(@amender, user_group: @user_group)
65
+
66
+ @amendable.add_coauthor(@amender, user_group: @user_group) if @amendable.is_a?(Decidim::Coauthorable)
66
67
  end
67
68
 
68
69
  def notify_emendation_state_change!
@@ -69,7 +69,7 @@ module Decidim
69
69
  @user.email = (verified_email || form.email)
70
70
  @user.name = form.name.gsub(REGEXP_SANITIZER, "")
71
71
  @user.nickname = form.normalized_nickname
72
- @user.newsletter_notifications_at = nil
72
+ @user.newsletter_notifications_at = form.newsletter_at
73
73
  @user.password = SecureRandom.hex
74
74
  attach_avatar(form.avatar_url) if form.avatar_url.present?
75
75
  @user.tos_agreement = form.tos_agreement
@@ -107,7 +107,7 @@ module Decidim
107
107
  end
108
108
 
109
109
  def reject
110
- enforce_permission_to :reject, :amendment, current_component: amendable.component
110
+ enforce_permission_to :reject, :amendment, amendable:, current_component: amendable.component
111
111
 
112
112
  @form = form(Decidim::Amendable::RejectForm).from_model(amendment)
113
113
 
@@ -143,13 +143,13 @@ module Decidim
143
143
  end
144
144
 
145
145
  def review
146
- enforce_permission_to :accept, :amendment, current_component: amendable.component
146
+ enforce_permission_to :accept, :amendment, amendable:, current_component: amendable.component
147
147
 
148
148
  @form = form(Decidim::Amendable::ReviewForm).from_params(params)
149
149
  end
150
150
 
151
151
  def accept
152
- enforce_permission_to :accept, :amendment, current_component: amendable.component
152
+ enforce_permission_to :accept, :amendment, amendable:, current_component: amendable.component
153
153
 
154
154
  @form = form(Decidim::Amendable::ReviewForm).from_params(params)
155
155
 
@@ -16,10 +16,12 @@ module Decidim
16
16
  attribute :phone
17
17
 
18
18
  validates :name, presence: true
19
+ validates :name, format: { with: Decidim::UserBaseEntity::REGEXP_NAME }
19
20
  validates :email, presence: true, "valid_email_2/email": { disposable: true }
20
21
  validates :nickname, presence: true
21
22
 
22
23
  validates :nickname, length: { maximum: Decidim::User.nickname_max_length, allow_blank: true }
24
+ validates :nickname, format: { with: Decidim::UserBaseEntity::REGEXP_NICKNAME }
23
25
  validates :avatar, passthru: { to: Decidim::UserGroup }
24
26
 
25
27
  validate :unique_document_number
@@ -61,8 +61,9 @@ module Decidim
61
61
  # Checks if the user can accept and reject the emendation
62
62
  def allowed_to_accept_and_reject?(emendation)
63
63
  return unless emendation.amendment.evaluating?
64
+ return current_user.admin? if emendation.amendable.respond_to?(:official?) && emendation.amendable.official?
64
65
 
65
- emendation.amendable.created_by?(current_user) || current_user.admin?
66
+ emendation.amendable.created_by?(current_user)
66
67
  end
67
68
 
68
69
  # Checks if the user can promote the emendation
@@ -32,6 +32,31 @@ module Decidim
32
32
  end
33
33
  end
34
34
 
35
+ def filter_search_label(label, id)
36
+ I18n.t("decidim.searches.filters.resource", label:, collection: filter_for_resource(id))
37
+ end
38
+
39
+ def filter_for_resource(skip_to_id)
40
+ case skip_to_id
41
+ when "proposals"
42
+ I18n.t("decidim/proposals/proposal.other", scope: "activerecord.models")
43
+ when "meetings"
44
+ I18n.t("decidim/meetings/meeting.other", scope: "activerecord.models")
45
+ when "debates"
46
+ I18n.t("decidim/debates/debate.other", scope: "activerecord.models")
47
+ when "sortitions"
48
+ I18n.t("decidim/sortitions/sortition.other", scope: "activerecord.models")
49
+ when "surveys"
50
+ I18n.t("decidim/surveys/survey.other", scope: "activerecord.models")
51
+ when "projects"
52
+ I18n.t("decidim/budgets/project.other", scope: "activerecord.models")
53
+ when "initiatives"
54
+ I18n.t("decidim/initiative.other", scope: "activerecord.models")
55
+ else
56
+ ""
57
+ end
58
+ end
59
+
35
60
  private
36
61
 
37
62
  # Creates a unique namespace for a filter form to prevent duplicate IDs in
@@ -60,6 +60,12 @@ module Decidim
60
60
  html_properties = options.with_indifferent_access.transform_keys(&:dasherize).slice("width", "height", "aria-label", "role", "aria-hidden", "class", "style")
61
61
  html_properties = default_html_properties.merge(html_properties)
62
62
 
63
+ if name == "wechat-line"
64
+ html_properties = html_properties.merge({ "aria-label" => I18n.t("decidim.author.comments.other") }).except("aria-hidden")
65
+ elsif name == "heart-line"
66
+ html_properties = html_properties.merge({ "aria-label" => I18n.t("decidim.author.endorsements.other") }).except("aria-hidden")
67
+ end
68
+
63
69
  href = Decidim.cors_enabled ? "" : asset_pack_path("media/images/remixicon.symbol.svg")
64
70
 
65
71
  content_tag :svg, html_properties do
@@ -81,7 +81,7 @@ module Decidim
81
81
 
82
82
  help = content_tag(:div, class: "map__skip-container") do
83
83
  sr_content = content_tag(:p, t("screen_reader_explanation", scope: "decidim.map.dynamic"), class: "sr-only")
84
- link = link_to(t("skip_button", scope: "decidim.map.dynamic"), "##{bottom_id}", class: "map__skip")
84
+ link = link_to(t("skip_button", scope: "decidim.map.dynamic"), "##{bottom_id}", class: "map__skip", "data-skip-to-content": true)
85
85
 
86
86
  sr_content + link
87
87
  end
@@ -68,8 +68,14 @@ module Decidim
68
68
  @menu_highlighted_participatory_process ||= (
69
69
  # The queries already include the order by weight
70
70
  Decidim::ParticipatoryProcesses::OrganizationParticipatoryProcesses.new(current_organization) |
71
- Decidim::ParticipatoryProcesses::PromotedParticipatoryProcesses.new
72
- ).first
71
+ Decidim::ParticipatoryProcesses::PromotedParticipatoryProcesses.new
72
+ ).select(&:published?).map { |process| remove_private_space_if_not_private_user(process) }&.compact&.first
73
+ end
74
+
75
+ def remove_private_space_if_not_private_user(process)
76
+ return nil if process.private_space == true && !process.can_participate?(current_user)
77
+
78
+ process
73
79
  end
74
80
 
75
81
  def home_content_block_menu
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" width="200" height="200" x="0" y="0" enable-background="new 0 0 200 200" version="1.1" viewBox="0 0 200 200" xml:space="preserve"><g><path fill="#C5C5C5" d="M200-0.5H0v200h26.099c2.885-35.293,24.07-64.381,52.419-74.319C64.372,117.596,54.75,102.674,54.75,85.5 c0-24.853,20.146-45,45-45c24.853,0,45,20.147,45,45c0,17.095-9.533,31.963-23.572,39.58c28.5,9.837,49.828,39.002,52.724,74.42 H200V-0.5z"/><path fill="#FFF" d="M121.178,125.08c14.039-7.617,23.572-22.485,23.572-39.58c0-24.853-20.147-45-45-45 c-24.854,0-45,20.147-45,45c0,17.174,9.622,32.096,23.768,39.681c-28.349,9.938-49.534,39.026-52.419,74.319h147.803 C171.006,164.082,149.678,134.917,121.178,125.08z"/></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" width="200" height="200" x="0" y="0" enable-background="new 0 0 200 200" version="1.1" viewBox="0 0 200 200" xml:space="preserve"><g><path fill="#908e8e" d="M200-0.5H0v200h26.099c2.885-35.293,24.07-64.381,52.419-74.319C64.372,117.596,54.75,102.674,54.75,85.5 c0-24.853,20.146-45,45-45c24.853,0,45,20.147,45,45c0,17.095-9.533,31.963-23.572,39.58c28.5,9.837,49.828,39.002,52.724,74.42 H200V-0.5z"/><path fill="#FFF" d="M121.178,125.08c14.039-7.617,23.572-22.485,23.572-39.58c0-24.853-20.147-45-45-45 c-24.854,0-45,20.147-45,45c0,17.174,9.622,32.096,23.768,39.681c-28.349,9.938-49.534,39.026-52.419,74.319h147.803 C171.006,164.082,149.678,134.917,121.178,125.08z"/></g></svg>
@@ -4,13 +4,18 @@
4
4
  * reload without this.
5
5
  */
6
6
 
7
- $(() => {
8
- const $callout = $('.callout[role="alert"]:first');
9
- if ($callout.length > 0) {
10
- setTimeout(() => {
11
- // The content insertion is to try to hint some of the screen readers
12
- // that the alert content has changed and needs to be announced.
13
- $callout.attr("tabindex", "0").focus().html(`${$callout.html()}&nbsp;`);
14
- }, 500);
7
+ document.addEventListener("turbo:load", () => {
8
+ const callout = document.querySelector(".flash[role='alert']");
9
+ if (!callout) {
10
+ return;
15
11
  }
12
+
13
+ setTimeout(() => {
14
+ callout.setAttribute("tabindex", "0");
15
+ callout.focus();
16
+
17
+ // The content insertion is to try to hint some of the screen readers
18
+ // that the alert content has changed and needs to be announced.
19
+ callout.innerHTML += "&nbsp;";
20
+ }, 500);
16
21
  });
@@ -10,13 +10,25 @@ const { Rails } = window;
10
10
  class ConfirmDialog {
11
11
  constructor(sourceElement) {
12
12
  this.$modal = $("#confirm-modal");
13
- if (sourceElement) {
14
- this.$source = $(sourceElement);
15
- }
16
13
  this.$content = $("[data-confirm-modal-content]", this.$modal);
14
+ this.$title = $("[data-dialog-title]", this.$modal);
17
15
  this.$buttonConfirm = $("[data-confirm-ok]", this.$modal);
18
16
  this.$buttonCancel = $("[data-confirm-cancel]", this.$modal);
19
17
 
18
+ if (sourceElement) {
19
+ this.$source = $(sourceElement);
20
+
21
+ const confirmTitle = this.$source.data("confirm-title");
22
+ const confirmButton = this.$source.data("confirm-button");
23
+
24
+ this.$title.text(confirmTitle)
25
+
26
+ const $buttonSpan = this.$buttonConfirm.find("span");
27
+ if ($buttonSpan.length > 0) {
28
+ $buttonSpan.text(confirmButton);
29
+ }
30
+ }
31
+
20
32
  window.Decidim.currentDialogs["confirm-modal"].open()
21
33
  }
22
34
 
@@ -13,11 +13,13 @@ export default function generateDatePicker(input, row, formats) {
13
13
  const date = document.createElement("input");
14
14
  date.setAttribute("id", `${input.id}_date`);
15
15
  date.setAttribute("type", "text");
16
+ date.setAttribute("aria-label", input.dataset.dateLabel);
16
17
 
17
18
  const calendar = document.createElement("button");
18
19
  calendar.innerHTML = icon("calendar-line");
19
20
  calendar.setAttribute("class", "datepicker__calendar-button");
20
21
  calendar.setAttribute("type", "button");
22
+ calendar.setAttribute("aria-label", input.dataset.buttonDateLabel);
21
23
 
22
24
  dateColumn.appendChild(date);
23
25
  dateColumn.appendChild(calendar);
@@ -13,11 +13,13 @@ export default function generateTimePicker(input, row, formats) {
13
13
  const time = document.createElement("input");
14
14
  time.setAttribute("id", `${input.id}_time`);
15
15
  time.setAttribute("type", "text");
16
+ time.setAttribute("aria-label", input.dataset.timeLabel);
16
17
 
17
18
  const clock = document.createElement("button");
18
19
  clock.innerHTML = icon("time-line")
19
20
  clock.setAttribute("class", "datepicker__clock-button");
20
21
  clock.setAttribute("type", "button");
22
+ clock.setAttribute("aria-label", input.dataset.buttonTimeLabel);
21
23
 
22
24
  timeColumn.appendChild(time);
23
25
  timeColumn.appendChild(clock);
@@ -1,5 +1,4 @@
1
1
  import UploadModal from "src/decidim/direct_uploads/upload_modal";
2
- import { truncateFilename } from "src/decidim/direct_uploads/upload_utility";
3
2
  import { escapeHtml, escapeQuotes } from "src/decidim/utilities/text";
4
3
 
5
4
  const updateModalTitle = (modal) => {
@@ -30,7 +29,7 @@ const updateActiveUploads = (modal) => {
30
29
  const [removeFiles, addFiles] = [modal.items.filter(({ removable }) => removable), modal.items.filter(({ removable }) => !removable)]
31
30
 
32
31
  addFiles.forEach((file, ix) => {
33
- let title = truncateFilename(file.name, 19)
32
+ let title = file.name
34
33
 
35
34
  let hidden = ""
36
35
  if (file.hiddenField) {
@@ -79,8 +78,8 @@ const updateActiveUploads = (modal) => {
79
78
 
80
79
  const template = `
81
80
  <div ${attachmentIdOrHiddenField} data-filename="${escapeQuotes(file.name)}" data-title="${escapeQuotes(title)}">
82
- ${(/image/).test(file.type) && `<div><img src="data:," alt="${escapeQuotes(file.name)}" /></div>` || ""}
83
- <span>${escapeHtml(title)} (${escapeHtml(truncateFilename(file.name))})</span>
81
+ ${(/image/).test(file.type) && "<div><img src=\"data:,\" role=\"presentation\" /></div>" || ""}
82
+ <span>${escapeHtml(title)}</span>
84
83
  ${hidden}
85
84
  </div>
86
85
  `