decidim-core 0.30.0.rc2 → 0.30.0.rc3

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/amendable/amendments/show.erb +1 -1
  3. data/app/cells/decidim/profile_actions/show.erb +1 -1
  4. data/app/cells/decidim/report_button/already_reported_modal.erb +2 -2
  5. data/app/cells/decidim/report_button/flag_modal.erb +13 -27
  6. data/app/cells/decidim/report_button_cell.rb +2 -8
  7. data/app/cells/decidim/report_user_button/already_reported_modal.erb +11 -0
  8. data/app/cells/decidim/report_user_button/flag_modal.erb +46 -0
  9. data/app/cells/decidim/report_user_button/show.erb +2 -0
  10. data/app/cells/decidim/report_user_button_cell.rb +59 -0
  11. data/app/cells/decidim/resource_types_filter/show.erb +1 -1
  12. data/app/cells/decidim/resource_types_filter_cell.rb +6 -6
  13. data/app/cells/decidim/user_activity/show.erb +1 -1
  14. data/app/commands/decidim/create_omniauth_registration.rb +14 -8
  15. data/app/controllers/decidim/profiles_controller.rb +2 -2
  16. data/app/controllers/decidim/user_activities_controller.rb +1 -1
  17. data/app/forms/decidim/account_form.rb +2 -2
  18. data/app/jobs/decidim/hide_child_resources_job.rb +3 -3
  19. data/app/jobs/decidim/migrate/paper_trail_job.rb +33 -0
  20. data/app/models/decidim/user.rb +0 -4
  21. data/app/models/decidim/user_base_entity.rb +4 -0
  22. data/app/packs/src/decidim/sticky_footer.js +19 -0
  23. data/app/packs/stylesheets/decidim/_cards.scss +4 -0
  24. data/app/packs/stylesheets/decidim/_content_blocks.scss +1 -1
  25. data/app/packs/stylesheets/decidim/_login.scss +4 -0
  26. data/app/packs/stylesheets/decidim/_modal_authorization.scss +1 -1
  27. data/app/packs/stylesheets/decidim/_profile.scss +1 -1
  28. data/app/resolvers/decidim/meta_image_url_resolver.rb +1 -0
  29. data/app/services/decidim/static_map_generator.rb +1 -1
  30. data/app/views/decidim/devise/registrations/new.html.erb +1 -1
  31. data/app/views/decidim/devise/sessions/new.html.erb +1 -1
  32. data/app/views/decidim/devise/shared/_omniauth_buttons.html.erb +1 -1
  33. data/app/views/decidim/last_activities/index.html.erb +1 -1
  34. data/app/views/decidim/open_data/index.html.erb +1 -1
  35. data/config/locales/ar.yml +12 -25
  36. data/config/locales/bg.yml +8 -22
  37. data/config/locales/ca.yml +19 -33
  38. data/config/locales/cs.yml +16 -31
  39. data/config/locales/de.yml +6 -21
  40. data/config/locales/el.yml +8 -13
  41. data/config/locales/en.yml +2 -16
  42. data/config/locales/eo.yml +2 -0
  43. data/config/locales/es-MX.yml +14 -28
  44. data/config/locales/es-PY.yml +19 -33
  45. data/config/locales/es.yml +19 -33
  46. data/config/locales/eu.yml +52 -67
  47. data/config/locales/fi-plain.yml +11 -26
  48. data/config/locales/fi.yml +12 -27
  49. data/config/locales/fr-CA.yml +58 -20
  50. data/config/locales/fr.yml +58 -20
  51. data/config/locales/ga-IE.yml +2 -4
  52. data/config/locales/gl.yml +22 -13
  53. data/config/locales/hu.yml +7 -21
  54. data/config/locales/id-ID.yml +22 -13
  55. data/config/locales/is-IS.yml +10 -0
  56. data/config/locales/it.yml +22 -20
  57. data/config/locales/ja.yml +120 -21
  58. data/config/locales/lb.yml +19 -19
  59. data/config/locales/lt.yml +5 -17
  60. data/config/locales/lv.yml +16 -13
  61. data/config/locales/nl.yml +20 -17
  62. data/config/locales/no.yml +16 -16
  63. data/config/locales/pl.yml +4 -18
  64. data/config/locales/pt-BR.yml +8 -21
  65. data/config/locales/pt.yml +16 -16
  66. data/config/locales/ro-RO.yml +15 -20
  67. data/config/locales/ru.yml +23 -8
  68. data/config/locales/sk.yml +27 -16
  69. data/config/locales/sv.yml +11 -25
  70. data/config/locales/tr-TR.yml +21 -20
  71. data/config/locales/uk.yml +12 -1
  72. data/config/locales/zh-CN.yml +16 -13
  73. data/config/locales/zh-TW.yml +6 -16
  74. data/db/migrate/20240722215500_change_object_changes_on_versions.rb +4 -24
  75. data/lib/decidim/content_parsers/user_parser.rb +1 -1
  76. data/lib/decidim/core/version.rb +1 -1
  77. data/lib/decidim/map/provider/static_map/here.rb +34 -0
  78. data/lib/decidim/nicknamizable.rb +1 -1
  79. data/lib/tasks/upgrade/decidim_fix_nickname_uniqueness.rake +23 -20
  80. metadata +11 -13
  81. data/app/cells/decidim/author/flag.erb +0 -6
  82. data/app/cells/decidim/author/flag_user.erb +0 -14
  83. data/app/cells/decidim/flag_modal/flag_user.erb +0 -34
  84. data/app/cells/decidim/flag_modal/show.erb +0 -52
  85. data/app/cells/decidim/flag_modal_cell.rb +0 -56
  86. data/app/cells/decidim/profile_sidebar/show.erb +0 -167
  87. data/app/cells/decidim/profile_sidebar_cell.rb +0 -68
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51f519d636709acef9ba64ba8adb8be0c2d554f9f6f38a9e741f607fed90a6da
4
- data.tar.gz: bb93e7720dd8f5e005cda7dc6fab14d21443d6fcf9c0ba1428b1887a1a785d5f
3
+ metadata.gz: 1741dcb0de873587b3e2ff7eb8faae32fa98107e26b42a1c30e1cff7c876f7b3
4
+ data.tar.gz: 7d5c4a405e277850a00645b6625097cf69a0b4b18dfdfa876be69ca045e71062
5
5
  SHA512:
6
- metadata.gz: 00663f06f72fc52a152b00aac2d0c680a7b9c8d349f737ea5d8f34970ea7f791d71f224579469cfe9ca4aeee3ad7d2aaa1d33bccd898bbd7cc7fa5b8bf158675
7
- data.tar.gz: 1e504e001f0f88b185a8eae321dffe70696bb17055b0826416bdb16ed44ca51d269d8a461cf56318c2278c0eba2976d41f34fa947be2cb2c88d8d49f9bba6acb
6
+ metadata.gz: fd16b3dbf24ccaf8cced5f0b23eb3dd496e5269bb19fb14010c4db9cc21e01f19c9cd19cff38540d1c9fd474d5e28d3e2a191d9a4e7d2bebde039eff54cdcf1b
7
+ data.tar.gz: 598051abedb973a6c95e2504d03a30de39f050a57d414aa625c474a2483cc0db42b78e0c923456aab83b960f2ed0dc42983304bc1798d24e9e80a4f08907ad1d
@@ -6,7 +6,7 @@
6
6
 
7
7
  <div id="amendment-list">
8
8
  <% emendations.each do |emendation| %>
9
- <%= card_for emendation, hide_voting: true, context: { current_user: } %>
9
+ <%= card_for emendation, context: { current_user: } %>
10
10
  <% end %>
11
11
  </div>
12
12
  </section>
@@ -7,6 +7,6 @@
7
7
  <%= cell("decidim/follow_button", profile_holder) %>
8
8
 
9
9
  <% if user_flaggable? && model.try(:id) != current_user.try(:id) %>
10
- <%= cell("decidim/report_button", profile_holder) %>
10
+ <%= cell("decidim/report_user_button", profile_holder) %>
11
11
  <% end %>
12
12
  </div>
@@ -1,10 +1,10 @@
1
1
  <%= decidim_modal id: modal_id, class: "flag-modal" do %>
2
2
  <div data-dialog-container>
3
3
  <%= icon "flag-line" %>
4
- <h2 id="dialog-title-flagModal" tabindex="-1" data-dialog-title><%= t("decidim.shared.flag_modal.title") %></h2>
4
+ <h2 tabindex="-1" data-dialog-title><%= t("decidim.shared.flag_modal.title") %></h2>
5
5
  <div>
6
6
  <div class="form__wrapper flag-modal__form">
7
- <p id="dialog-desc-flagModal" class="flag-modal__form-description"><%= t("decidim.shared.flag_modal.already_reported") %></p>
7
+ <p class="flag-modal__form-description"><%= t("decidim.shared.flag_modal.already_reported") %></p>
8
8
  </div>
9
9
  </div>
10
10
  </div>
@@ -2,7 +2,7 @@
2
2
  <%= form_for report_form, builder:, url: report_path, method: :post, html: { id: nil } do |f| %>
3
3
  <div data-dialog-container>
4
4
  <%= icon "flag-line" %>
5
- <h2 id="dialog-title-<%= modal_id %>" tabindex="-1" data-dialog-title><%= t("decidim.shared.flag_modal.title") %></h2>
5
+ <h2 tabindex="-1" data-dialog-title><%= t("decidim.shared.flag_modal.title") %></h2>
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>
@@ -18,32 +18,18 @@
18
18
  <%= f.text_area :details, rows: 4, label_options: { class: "flag-modal__form-textarea-label", for: nil }, id: nil %>
19
19
 
20
20
  <% if frontend_administrable? %>
21
- <% if user_reportable? %>
22
- <% if current_user&.admin? %>
23
- <%= f.check_box :block,
24
- label: t("decidim.shared.flag_user_modal.block"),
25
- include_hidden: false,
26
- data: {
27
- label_action: t("decidim.shared.flag_user_modal.block"),
28
- label_report: t("decidim.shared.flag_user_modal.report"),
29
- block: "true"
30
- } %>
31
- <%= f.check_box :hide, label: t("decidim.shared.flag_user_modal.hide"), label_options: { class: :invisible, id: "block_and_hide" } %>
32
- <% end %>
33
- <% else %>
34
- <%= f.check_box :hide,
35
- label: t("decidim.shared.flag_modal.hide_content"),
36
- include_hidden: false,
37
- id: hide_checkbox_id,
38
- data: {
39
- label_action: t("decidim.shared.flag_modal.hide"),
40
- label_report: t("decidim.shared.flag_modal.report"),
41
- hide: "true"
42
- },
43
- label_options: {
44
- for: hide_checkbox_id
45
- } %>
46
- <% end %>
21
+ <%= f.check_box :hide,
22
+ label: t("decidim.shared.flag_modal.hide_content"),
23
+ include_hidden: false,
24
+ id: hide_checkbox_id,
25
+ data: {
26
+ label_action: t("decidim.shared.flag_modal.hide"),
27
+ label_report: t("decidim.shared.flag_modal.report"),
28
+ hide: "true"
29
+ },
30
+ label_options: {
31
+ for: hide_checkbox_id
32
+ } %>
47
33
  <% end %>
48
34
 
49
35
  </div>
@@ -11,8 +11,6 @@ module Decidim
11
11
  end
12
12
 
13
13
  def frontend_administrable?
14
- return true if user_reportable? && current_user&.admin?
15
-
16
14
  user_entity? &&
17
15
  model.can_be_administered_by?(current_user) &&
18
16
  (model.respond_to?(:official?) && !model.official?)
@@ -48,16 +46,12 @@ module Decidim
48
46
  options[:modal_id] || "flagModal"
49
47
  end
50
48
 
51
- def user_reportable?
52
- model.is_a?(Decidim::UserReportable)
53
- end
54
-
55
49
  def report_form
56
- @report_form ||= user_reportable? ? Decidim::ReportForm.from_params(reason: "spam") : Decidim::ReportForm.new(reason: "spam")
50
+ @report_form ||= Decidim::ReportForm.new(reason: "spam")
57
51
  end
58
52
 
59
53
  def report_path
60
- @report_path ||= user_reportable? ? decidim.report_user_path(sgid: model.to_sgid.to_s) : decidim.report_path(sgid: model.to_sgid.to_s)
54
+ @report_path ||= decidim.report_path(sgid: model.to_sgid.to_s)
61
55
  end
62
56
 
63
57
  def builder
@@ -0,0 +1,11 @@
1
+ <%= decidim_modal id: modal_id, class: "flag-user-modal" do %>
2
+ <div data-dialog-container>
3
+ <%= icon "flag-line" %>
4
+ <h2 tabindex="-1" data-dialog-title><%= t("decidim.shared.flag_user_modal.title") %></h2>
5
+ <div>
6
+ <div class="form__wrapper flag-modal__form">
7
+ <p class="flag-modal__form-description"><%= t("decidim.shared.flag_user_modal.already_reported") %></p>
8
+ </div>
9
+ </div>
10
+ </div>
11
+ <% end %>
@@ -0,0 +1,46 @@
1
+ <%= decidim_modal id: modal_id, class: "flag-user-modal" do %>
2
+ <%= form_for report_form, builder:, url: report_path, method: :post, html: { id: nil } do |f| %>
3
+ <div data-dialog-container>
4
+ <%= icon "flag-line" %>
5
+ <h2 tabindex="-1" data-dialog-title><%= t("decidim.shared.flag_user_modal.title") %></h2>
6
+ <div>
7
+ <div class="form__wrapper flag-modal__form">
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 %>
19
+
20
+ <% if frontend_administrable? %>
21
+ <%= f.check_box :block,
22
+ label: t("decidim.shared.flag_user_modal.block"),
23
+ include_hidden: false,
24
+ data: {
25
+ label_action: t("decidim.shared.flag_user_modal.block"),
26
+ label_report: t("decidim.shared.flag_user_modal.report"),
27
+ block: "true"
28
+ } %>
29
+ <%= f.check_box :hide, label: t("decidim.shared.flag_user_modal.hide"), label_options: { class: :invisible, id: "block_and_hide" } %>
30
+ <% end %>
31
+ </div>
32
+ </div>
33
+ </div>
34
+
35
+ <div data-dialog-actions>
36
+ <button type="button" class="button button__lg button__transparent-secondary" data-dialog-close="<%= modal_id %>">
37
+ <%= t("decidim.shared.confirm_modal.cancel") %>
38
+ </button>
39
+
40
+ <button type="submit" class="button button__lg button__secondary">
41
+ <span><%= t("decidim.shared.flag_modal.report") %></span>
42
+ <%= icon "arrow-right-line", class: "fill-current" %>
43
+ </button>
44
+ </div>
45
+ <% end %>
46
+ <% end %>
@@ -0,0 +1,2 @@
1
+ <%= render :button %>
2
+ <%= flag_modal unless only_button? %>
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ class ReportUserButtonCell < ButtonCell
5
+ include ActionView::Helpers::FormOptionsHelper
6
+
7
+ def flag_modal
8
+ return render :already_reported_modal if model.reported_by?(current_user)
9
+
10
+ render
11
+ end
12
+
13
+ private
14
+
15
+ def report_form
16
+ @report_form ||= Decidim::ReportForm.from_params(reason: "spam")
17
+ end
18
+
19
+ def report_path
20
+ @report_path ||= decidim.report_user_path(sgid: model.to_sgid.to_s)
21
+ end
22
+
23
+ def user_reportable?
24
+ model.is_a?(Decidim::UserReportable)
25
+ end
26
+
27
+ def frontend_administrable?
28
+ current_user&.admin?
29
+ end
30
+
31
+ def builder
32
+ Decidim::FormBuilder
33
+ end
34
+
35
+ def only_button?
36
+ options[:only_button]
37
+ end
38
+
39
+ def modal_id
40
+ options[:modal_id] || "flagUserModal"
41
+ end
42
+
43
+ def button_classes
44
+ options[:button_classes] || "button button__sm button__text button__text-secondary"
45
+ end
46
+
47
+ def text
48
+ t("decidim.shared.flag_modal.report")
49
+ end
50
+
51
+ def icon_name
52
+ "flag-line"
53
+ end
54
+
55
+ def html_options
56
+ { data: { "dialog-open": current_user ? modal_id : "loginModal" } }
57
+ end
58
+ end
59
+ end
@@ -11,7 +11,7 @@
11
11
  <ul id="dropdown-menu-resource">
12
12
  <% resource_types.each do |resource_type| %>
13
13
  <li role="menuitem">
14
- <%= link_to decidim.last_activities_path(filter: { with_resource_type: resource_type[0] } ), class: "filter#{" is-active" if filter_param == resource_type[0]}" do %>
14
+ <%= link_to filter_url(resource_type[0]), class: "filter#{" is-active" if filter_param == resource_type[0]}" do %>
15
15
  <span class="sr-only"><%= resource_type[1] %></span>
16
16
  <%= text_with_resource_icon(*resource_type) %>
17
17
  <% end %>
@@ -27,8 +27,12 @@ module Decidim
27
27
  options[:id] || "filters"
28
28
  end
29
29
 
30
- def form_path
31
- options[:form_path]
30
+ def filter_url(resource_type)
31
+ if options[:source] == :last_activities
32
+ last_activities_path(filter: { with_resource_type: resource_type })
33
+ else
34
+ profile_activity_path(nickname: params[:nickname], filter: { resource_type: })
35
+ end
32
36
  end
33
37
 
34
38
  def filter_param_key
@@ -39,10 +43,6 @@ module Decidim
39
43
  @filter_param ||= params.dig(:filter, filter_param_key) || all_types_key
40
44
  end
41
45
 
42
- def filter
43
- options[:filter]
44
- end
45
-
46
46
  def all_resource_types_option
47
47
  [all_types_key, I18n.t("all", scope: "decidim.last_activities")]
48
48
  end
@@ -1,5 +1,5 @@
1
1
  <div class="profile__activity">
2
- <%= cell "decidim/resource_types_filter", resource_types, form_path: url_for, filter: %>
2
+ <%= cell "decidim/resource_types_filter", resource_types, source: :profile_activity %>
3
3
  <div class="profile__activity__container" id="activities-container">
4
4
  <% if activities.length == 0 %>
5
5
  <%= cell "decidim/announcement", t("decidim.user_activity.index.no_activities_warning") %>
@@ -47,6 +47,8 @@ module Decidim
47
47
 
48
48
  attr_reader :form, :verified_email
49
49
 
50
+ REGEXP_SANITIZER = /[<>?%&\^*#@()\[\]=+:;"{}\\|]/
51
+
50
52
  def create_or_find_user
51
53
  @user = User.find_or_initialize_by(
52
54
  email: verified_email,
@@ -65,16 +67,11 @@ module Decidim
65
67
  @user.save!
66
68
  else
67
69
  @user.email = (verified_email || form.email)
68
- @user.name = form.name
70
+ @user.name = form.name.gsub(REGEXP_SANITIZER, "")
69
71
  @user.nickname = form.normalized_nickname
70
72
  @user.newsletter_notifications_at = nil
71
73
  @user.password = SecureRandom.hex
72
- if form.avatar_url.present?
73
- url = URI.parse(form.avatar_url)
74
- filename = File.basename(url.path)
75
- file = url.open
76
- @user.avatar.attach(io: file, filename:)
77
- end
74
+ attach_avatar(form.avatar_url) if form.avatar_url.present?
78
75
  @user.tos_agreement = form.tos_agreement
79
76
  @user.accepted_tos_version = Time.current
80
77
  raise NeedTosAcceptance if @user.tos_agreement.blank?
@@ -85,6 +82,15 @@ module Decidim
85
82
  end
86
83
  end
87
84
 
85
+ def attach_avatar(avatar_url)
86
+ url = URI.parse(avatar_url)
87
+ filename = File.basename(url.path)
88
+ file = url.open
89
+ @user.avatar.attach(io: file, filename:)
90
+ rescue OpenURI::HTTPError, Errno::ECONNREFUSED
91
+ # Do not attach the avatar, as it fails to fetch it.
92
+ end
93
+
88
94
  def create_identity
89
95
  @user.identities.create!(
90
96
  provider: form.provider,
@@ -130,7 +136,7 @@ module Decidim
130
136
  provider: form.provider,
131
137
  uid: form.uid,
132
138
  email: form.email,
133
- name: form.name,
139
+ name: form.name.gsub(REGEXP_SANITIZER, ""),
134
140
  nickname: form.normalized_nickname,
135
141
  avatar_url: form.avatar_url,
136
142
  raw_data: form.raw_data,
@@ -24,7 +24,7 @@ module Decidim
24
24
  def show
25
25
  return redirect_to profile_members_path if profile_holder.is_a?(Decidim::UserGroup)
26
26
 
27
- redirect_to profile_activity_path(nickname: params[:nickname])
27
+ redirect_to profile_activity_path(nickname: params[:nickname].downcase)
28
28
  end
29
29
 
30
30
  def tooltip
@@ -116,7 +116,7 @@ module Decidim
116
116
  def profile_holder
117
117
  return if params[:nickname].blank?
118
118
 
119
- @profile_holder ||= Decidim::UserBaseEntity.find_by("LOWER(nickname) = ? AND decidim_organization_id = ?", params[:nickname].downcase, current_organization.id)
119
+ @profile_holder ||= Decidim::UserBaseEntity.find_by("nickname = ? AND decidim_organization_id = ?", params[:nickname].downcase, current_organization.id)
120
120
  end
121
121
  end
122
122
  end
@@ -22,7 +22,7 @@ module Decidim
22
22
  def user
23
23
  return unless params[:nickname]
24
24
 
25
- @user ||= current_organization.users.find_by("LOWER(nickname) = ?", params[:nickname].downcase)
25
+ @user ||= current_organization.users.find_by("nickname = ?", params[:nickname].downcase)
26
26
  end
27
27
 
28
28
  def activities
@@ -21,7 +21,7 @@ module Decidim
21
21
 
22
22
  validates :name, presence: true, format: { with: Decidim::User::REGEXP_NAME }
23
23
  validates :email, presence: true, "valid_email_2/email": { disposable: true }
24
- validates :nickname, presence: true, format: { with: Decidim::User::REGEXP_NICKNAME }
24
+ validates :nickname, presence: true, format: { with: Decidim::UserBaseEntity::REGEXP_NAME }
25
25
 
26
26
  validates :nickname, length: { maximum: Decidim::User.nickname_max_length, allow_blank: true }
27
27
  validates :password, password: { name: :name, email: :email, username: :nickname }, if: -> { password.present? }
@@ -66,7 +66,7 @@ module Decidim
66
66
 
67
67
  def unique_nickname
68
68
  return true if Decidim::UserBaseEntity.where(
69
- "decidim_organization_id = ? AND LOWER(nickname) = ? ",
69
+ "decidim_organization_id = ? AND nickname = ? ",
70
70
  context.current_organization.id,
71
71
  nickname.downcase
72
72
  ).where.not(id: context.current_user.id).empty?
@@ -4,9 +4,9 @@ module Decidim
4
4
  class HideChildResourcesJob < ApplicationJob
5
5
  queue_as :user_report
6
6
 
7
- def perform(resource, _user_id)
8
- spam_user = (resource.organization.users.find_by!(email: Decidim::Ai::SpamDetection.reporting_user_email) if Decidim.module_installed?(:ai))
9
- spam_user = resource.organization.admins.first if spam_user.nil?
7
+ def perform(resource, user_id)
8
+ spam_user = (resource.organization.users.find_by(email: Decidim::Ai::SpamDetection.reporting_user_email) if Decidim.module_installed?(:ai))
9
+ spam_user = resource.organization.admins.find(user_id) if spam_user.nil?
10
10
 
11
11
  tool = Decidim::ModerationTools.new(resource, spam_user)
12
12
 
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Migrate
5
+ # this job is created to help migrating the Paperclip from YAML to JSON
6
+ class PaperTrailJob < Decidim::ApplicationJob
7
+ def perform(id)
8
+ version = PaperTrail::Version.where.not(old_object_changes: nil).find(id)
9
+ # This is an adaptation of PaperTrail internal load_changeset method,having in mind that we
10
+ # need to call also the code from PaperTrail::AttributeSerializer::ObjectChangesAttribute
11
+ object_changes = ActiveSupport::HashWithIndifferentAccess.new(YAML.unsafe_load(version.old_object_changes))
12
+ unless version.item_type.constantize.unscoped.find_by(id: version.item_id).nil?
13
+ # This is the deserialization code from `PaperTrail::AttributeSerializer::ObjectChangesAttribute`
14
+ # where we skip checking the object changeset column type, as we migrate it from YAML to JSON
15
+ changes_to_serialize = object_changes.clone
16
+ if changes_to_serialize.present?
17
+ serializer = PaperTrail::AttributeSerializers::CastAttributeSerializer.new(version.item_type.constantize)
18
+ changes_to_serialize.each do |key, change|
19
+ # `change` is an Array with two elements, representing before and after.
20
+ object_changes[key] = Array(change).map do |value|
21
+ serializer.send(:deserialize, key, value)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ version.update_columns(old_object_changes: nil, object_changes:) # rubocop:disable Rails/SkipsModelValidations
28
+ rescue NameError
29
+ Rails.logger.info "Skipping History of #{version.item_type} with id #{version.item_id}"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -13,8 +13,6 @@ module Decidim
13
13
  include Decidim::UserReportable
14
14
  include Decidim::Traceable
15
15
 
16
- REGEXP_NICKNAME = /\A[\w-]+\z/
17
-
18
16
  class Roles
19
17
  def self.all
20
18
  Decidim.config.user_roles
@@ -53,8 +51,6 @@ module Decidim
53
51
 
54
52
  has_one_attached :download_your_data_file
55
53
 
56
- scope :not_deleted, -> { where(deleted_at: nil) }
57
-
58
54
  scope :managed, -> { where(managed: true) }
59
55
  scope :not_managed, -> { where(managed: false) }
60
56
 
@@ -21,11 +21,13 @@ module Decidim
21
21
 
22
22
  # Regex for name & nickname format validations
23
23
  REGEXP_NAME = /\A(?!.*[<>?%&\^*#@()\[\]=+:;"{}\\|])/
24
+ REGEXP_NICKNAME = /\A[a-z0-9_-]+\z/
24
25
 
25
26
  has_one_attached :avatar
26
27
  validates_avatar :avatar, uploader: Decidim::AvatarUploader
27
28
 
28
29
  validates :name, format: { with: REGEXP_NAME }
30
+ validates :nickname, format: { with: REGEXP_NICKNAME }, unless: -> { deleted? || managed? }
29
31
 
30
32
  scope :confirmed, -> { where.not(confirmed_at: nil) }
31
33
  scope :not_confirmed, -> { where(confirmed_at: nil) }
@@ -34,6 +36,8 @@ module Decidim
34
36
  scope :not_blocked, -> { where(blocked: false) }
35
37
  scope :available, -> { where(deleted_at: nil, blocked: false, managed: false) }
36
38
 
39
+ scope :not_deleted, -> { where(deleted_at: nil) }
40
+
37
41
  # Public: Returns a collection with all the public entities this user is following.
38
42
  #
39
43
  # This cannot be done as with a `has_many :following, through: :following_follows`
@@ -8,12 +8,27 @@
8
8
 
9
9
  const footer = document.querySelector("footer");
10
10
  const stickyButtons = document.querySelector("[data-sticky-buttons]");
11
+ import { screens } from "tailwindcss/defaultTheme"
11
12
 
13
+ /**
14
+ * Checks if a key is in the current viewport
15
+ *
16
+ * @param {('sm'|'md'|'lg'|'xl'|'2xl')} key - The key to check the screen size.
17
+ * @returns {boolean} - Returns true if the screen size corresponds with the key
18
+ */
19
+ const isScreenSize = (key) => {
20
+ return window.matchMedia(`(min-width: ${screens[key]})`).matches;
21
+ }
12
22
  const adjustCtasButtons = () => {
13
23
  if (!stickyButtons) {
14
24
  return;
15
25
  }
16
26
 
27
+ if (isScreenSize("md")) {
28
+ footer.style.marginBottom = "0px";
29
+ return;
30
+ }
31
+
17
32
  const marginBottom = stickyButtons.offsetHeight;
18
33
  footer.style.marginBottom = `${marginBottom}px`;
19
34
  };
@@ -26,4 +41,8 @@ if (stickyButtons) {
26
41
  document.addEventListener("on:toggle", () => {
27
42
  adjustCtasButtons();
28
43
  });
44
+
45
+ window.addEventListener("resize", () => {
46
+ adjustCtasButtons();
47
+ });
29
48
  }
@@ -151,6 +151,10 @@
151
151
  }
152
152
  }
153
153
 
154
+ & > div.success {
155
+ @apply text-sm text-success;
156
+ }
157
+
154
158
  [data-author] + [data-author] {
155
159
  @apply -ml-4;
156
160
  }
@@ -30,7 +30,7 @@
30
30
  }
31
31
 
32
32
  &__span {
33
- @apply text-gray-2 font-semibold uppercase;
33
+ @apply mb-4 text-gray-2 font-semibold uppercase;
34
34
  }
35
35
 
36
36
  & ~ & {
@@ -32,6 +32,10 @@ $google_background_hover: #dcdcdc; // 30% opacity
32
32
 
33
33
  &--developer {
34
34
  @apply flex;
35
+
36
+ svg {
37
+ @apply fill-current;
38
+ }
35
39
  }
36
40
 
37
41
  &--facebook {
@@ -15,7 +15,7 @@
15
15
  }
16
16
 
17
17
  &-container {
18
- @apply mt-8 space-y-8;
18
+ @apply m-8 space-y-8;
19
19
  }
20
20
  }
21
21
  }
@@ -45,7 +45,7 @@
45
45
 
46
46
  &__actions {
47
47
  &-main {
48
- @apply w-fit mx-auto gap-x-4 gap-y-6 md:gap-4;
48
+ @apply grid w-fit mx-auto gap-x-4 gap-y-6 md:gap-4;
49
49
 
50
50
  &__dropdown {
51
51
  @apply divide-y divide-gray-3 z-20 w-64;
@@ -25,6 +25,7 @@ module Decidim
25
25
  # @return [String, nil] - The resolved image blob or nil if no image is found.
26
26
  def resolve
27
27
  return unless blob
28
+ return unless blob.service.exist?(blob.key)
28
29
 
29
30
  resized_variant = blob.variant(resize_to_limit: [1200, 630]).processed
30
31
  Rails.application.routes.url_helpers.rails_representation_url(resized_variant, only_path: true)
@@ -15,7 +15,7 @@ module Decidim
15
15
  def data
16
16
  return if @resource.blank? || map_utility.nil?
17
17
 
18
- Rails.cache.fetch(@resource.cache_key) do
18
+ Rails.cache.fetch(@resource.cache_key_with_version) do
19
19
  map_utility.image_data(
20
20
  latitude: @resource.latitude,
21
21
  longitude: @resource.longitude,
@@ -4,7 +4,7 @@
4
4
  <%= render "decidim/devise/shared/links" %>
5
5
  <% end %>
6
6
 
7
- <%= render(layout: "layouts/decidim/shared/layout_center", locals: { columns: 8 }) do %>
7
+ <%= render(layout: "layouts/decidim/shared/layout_center") do %>
8
8
  <div class="flex justify-center">
9
9
  <h1 class="title-decorator my-12"><%= t("decidim.devise.registrations.new.sign_up") %></h1>
10
10
  </div>
@@ -1,6 +1,6 @@
1
1
  <% add_decidim_page_title(t("devise.sessions.new.log_in")) %>
2
2
 
3
- <%= render(layout: "layouts/decidim/shared/layout_center", locals: { columns: 8 }) do %>
3
+ <%= render(layout: "layouts/decidim/shared/layout_center") do %>
4
4
 
5
5
  <div class="flex justify-center">
6
6
  <h1 class="title-decorator my-12"><%= t("devise.sessions.new.log_in") %></h1>
@@ -5,7 +5,7 @@
5
5
  <% link_classes = "login__omniauth-button login__omniauth-button--#{normalize_provider_name(provider)}" %>
6
6
  <%= link_to decidim.send("user_#{provider}_omniauth_authorize_path"), class: link_classes, method: :post, title: t("devise.shared.links.log_in_with_provider", provider: normalize_provider_name(provider).titleize) do %>
7
7
  <%= oauth_icon provider %>
8
- <span><%= t("devise.shared.links.log_in_with_provider", provider: normalize_provider_name(provider).titleize) %></span>
8
+ <span class="sr-only"><%= t("devise.shared.links.log_in_with_provider", provider: normalize_provider_name(provider).titleize) %></span>
9
9
  <% end %>
10
10
  <% end %>
11
11
  </div>
@@ -5,7 +5,7 @@
5
5
  <h1 class="title-decorator my-12"><%= t("last_activity", scope: "decidim.last_activities.index") %></h1>
6
6
 
7
7
  <div class="profile__activity pb-16">
8
- <%= cell "decidim/resource_types_filter", resource_types, form_path: last_activities_path, filter_param_key: :with_resource_type, filter: %>
8
+ <%= cell "decidim/resource_types_filter", resource_types, source: :last_activities, filter_param_key: :with_resource_type %>
9
9
  <div id="activities">
10
10
  <%= render partial: "activities" %>
11
11
  </div>