decidim-core 0.27.1 → 0.27.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of decidim-core might be problematic. Click here for more details.

Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/newsletter_templates/base_cell.rb +8 -0
  3. data/app/cells/decidim/newsletter_templates/basic_only_text/show.erb +4 -4
  4. data/app/cells/decidim/newsletter_templates/image_text_cta/show.erb +4 -4
  5. data/app/cells/decidim/upload_modal/files.erb +1 -0
  6. data/app/cells/decidim/upload_modal_cell.rb +26 -11
  7. data/app/commands/decidim/attachment_methods.rb +20 -2
  8. data/app/commands/decidim/create_registration.rb +1 -0
  9. data/app/commands/decidim/gallery_methods.rb +1 -1
  10. data/app/commands/decidim/unendorse_resource.rb +1 -1
  11. data/app/commands/decidim/update_account.rb +1 -0
  12. data/app/commands/decidim/update_password.rb +2 -0
  13. data/app/controllers/decidim/devise/sessions_controller.rb +18 -2
  14. data/app/controllers/decidim/groups_controller.rb +5 -0
  15. data/app/controllers/decidim/links_controller.rb +10 -11
  16. data/app/controllers/decidim/profiles_controller.rb +1 -1
  17. data/app/helpers/decidim/cells_helper.rb +1 -0
  18. data/app/helpers/decidim/external_domain_helper.rb +14 -3
  19. data/app/helpers/decidim/icon_helper.rb +3 -3
  20. data/app/helpers/decidim/newsletters_helper.rb +1 -0
  21. data/app/helpers/decidim/sanitize_helper.rb +3 -2
  22. data/app/mailers/decidim/newsletter_mailer.rb +10 -3
  23. data/app/models/decidim/newsletter.rb +28 -0
  24. data/app/models/decidim/scope_type.rb +24 -0
  25. data/app/models/decidim/user.rb +0 -2
  26. data/app/models/decidim/user_base_entity.rb +2 -0
  27. data/app/models/decidim/user_block.rb +2 -2
  28. data/app/models/decidim/user_group.rb +1 -1
  29. data/app/packs/src/decidim/direct_uploads/upload_modal.js +0 -1
  30. data/app/packs/src/decidim/editor/clipboard_override.js +6 -2
  31. data/app/packs/src/decidim/editor.js +63 -33
  32. data/app/packs/src/decidim/form_filter.component.test.js +148 -5
  33. data/app/packs/src/decidim/form_filter.js +26 -4
  34. data/app/packs/stylesheets/decidim/email.scss +7 -0
  35. data/app/packs/stylesheets/decidim/modules/_buttons.scss +10 -6
  36. data/app/packs/stylesheets/decidim/modules/_cards.scss +1 -1
  37. data/app/packs/stylesheets/decidim/modules/_comments.scss +24 -0
  38. data/app/packs/stylesheets/decidim/modules/_input-gallery.scss +2 -1
  39. data/app/packs/stylesheets/decidim/modules/_upload_modal.scss +0 -4
  40. data/app/packs/stylesheets/decidim/vizzs/_linechart.scss +2 -2
  41. data/app/packs/stylesheets/decidim/vizzs/_rowchart.scss +2 -2
  42. data/app/presenters/decidim/admin_log/user_group_presenter.rb +1 -1
  43. data/app/presenters/decidim/admin_log/user_moderation_presenter.rb +1 -1
  44. data/app/presenters/decidim/notification_presenter.rb +1 -1
  45. data/app/presenters/decidim/notification_to_mailer_presenter.rb +1 -0
  46. data/app/presenters/decidim/push_notification_presenter.rb +1 -1
  47. data/app/presenters/decidim/user_group_presenter.rb +1 -1
  48. data/app/presenters/decidim/user_presenter.rb +1 -1
  49. data/app/scrubbers/decidim/admin_input_scrubber.rb +3 -1
  50. data/app/scrubbers/decidim/user_input_scrubber.rb +30 -1
  51. data/app/services/decidim/traceability.rb +1 -0
  52. data/app/uploaders/decidim/application_uploader.rb +1 -1
  53. data/app/uploaders/decidim/avatar_uploader.rb +2 -2
  54. data/app/validators/uploader_image_dimensions_validator.rb +22 -2
  55. data/app/views/decidim/links/_invalid_url_modal.html.erb +17 -0
  56. data/app/views/decidim/links/_modal.html.erb +1 -1
  57. data/app/views/decidim/links/invalid_url.js.erb +24 -0
  58. data/app/views/decidim/links/new.html.erb +1 -1
  59. data/app/views/decidim/messaging/conversations/_conversation.html.erb +2 -6
  60. data/app/views/decidim/newsletter_mailer/newsletter.html.erb +3 -3
  61. data/app/views/decidim/newsletters/show.html.erb +1 -1
  62. data/app/views/layouts/decidim/_mailer_logo.html.erb +2 -2
  63. data/app/views/layouts/decidim/newsletter_base.html.erb +2 -2
  64. data/config/locales/ar.yml +571 -7
  65. data/config/locales/bg.yml +6 -8
  66. data/config/locales/ca.yml +38 -30
  67. data/config/locales/cs.yml +28 -33
  68. data/config/locales/da.yml +4 -0
  69. data/config/locales/de.yml +4 -25
  70. data/config/locales/el.yml +6 -9
  71. data/config/locales/en.yml +22 -15
  72. data/config/locales/eo.yml +2 -1
  73. data/config/locales/es-MX.yml +30 -22
  74. data/config/locales/es-PY.yml +30 -22
  75. data/config/locales/es.yml +36 -28
  76. data/config/locales/et.yml +4 -0
  77. data/config/locales/eu.yml +173 -80
  78. data/config/locales/fa-IR.yml +1 -0
  79. data/config/locales/fi-plain.yml +7 -20
  80. data/config/locales/fi.yml +26 -18
  81. data/config/locales/fr-CA.yml +27 -19
  82. data/config/locales/fr.yml +25 -17
  83. data/config/locales/ga-IE.yml +1 -0
  84. data/config/locales/gl.yml +2 -25
  85. data/config/locales/gn-PY.yml +4 -0
  86. data/config/locales/hr.yml +4 -0
  87. data/config/locales/hu.yml +68 -28
  88. data/config/locales/id-ID.yml +7 -8
  89. data/config/locales/is-IS.yml +2 -2
  90. data/config/locales/it.yml +2 -10
  91. data/config/locales/ja.yml +30 -37
  92. data/config/locales/ka-GE.yml +5 -0
  93. data/config/locales/kaa.yml +1 -0
  94. data/config/locales/lb.yml +0 -8
  95. data/config/locales/lt.yml +0 -38
  96. data/config/locales/lv.yml +5 -7
  97. data/config/locales/nl.yml +1 -27
  98. data/config/locales/no.yml +3 -29
  99. data/config/locales/oc-FR.yml +3 -0
  100. data/config/locales/pl.yml +4 -39
  101. data/config/locales/pt-BR.yml +2 -10
  102. data/config/locales/pt.yml +0 -8
  103. data/config/locales/ro-RO.yml +85 -7
  104. data/config/locales/ru.yml +6 -4
  105. data/config/locales/sk.yml +8 -9
  106. data/config/locales/sl.yml +1 -0
  107. data/config/locales/sr-CS.yml +2 -0
  108. data/config/locales/sv.yml +23 -28
  109. data/config/locales/tr-TR.yml +7 -12
  110. data/config/locales/uk.yml +6 -4
  111. data/config/locales/zh-CN.yml +3 -8
  112. data/config/locales/zh-TW.yml +1872 -0
  113. data/lib/decidim/api/types/localized_string_type.rb +9 -0
  114. data/lib/decidim/api/types/translated_field_type.rb +20 -5
  115. data/lib/decidim/asset_router/pipeline.rb +95 -0
  116. data/lib/decidim/asset_router/storage.rb +82 -0
  117. data/lib/decidim/asset_router.rb +3 -75
  118. data/lib/decidim/attribute_object/form.rb +9 -0
  119. data/lib/decidim/core/test/factories.rb +13 -6
  120. data/lib/decidim/core/test/shared_examples/comments_examples.rb +36 -0
  121. data/lib/decidim/core/test/shared_examples/digest_mail_examples.rb +33 -0
  122. data/lib/decidim/core/test/shared_examples/editor_shared_examples.rb +5 -4
  123. data/lib/decidim/core/test/shared_examples/rich_text_editor_examples.rb +7 -3
  124. data/lib/decidim/core/test.rb +1 -0
  125. data/lib/decidim/core/version.rb +1 -1
  126. data/lib/decidim/dependency_resolver.rb +14 -8
  127. data/lib/decidim/form_builder.rb +5 -4
  128. data/lib/decidim/participatory_space_resourceable.rb +7 -1
  129. data/lib/decidim/publicable.rb +4 -0
  130. metadata +14 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9cfa315481be2ecd36504616728a76d1c445b99a233f36e50db71a7351ac04b
4
- data.tar.gz: d67d9216824b975d528f0e64a8f605eacc4849fb042836d63e5c2fe5bc4da218
3
+ metadata.gz: d6577740d87b562e24f541c4723480ec774239f69a389d4434536ab72be3b116
4
+ data.tar.gz: 8366df277375b9fadc5c96b4a9e647a0300057d7148eafcb672f3a57d3abd55f
5
5
  SHA512:
6
- metadata.gz: 776eba5812d48f147afa41d5b0a2eab52d2c46ce2db9616bd784c2f784be2851eaa1e341439ba1ac13f9b8152e1a3fb09acdbe08e93a9e2e6721e7c9afb09982
7
- data.tar.gz: eb00d8fe971338da9056eb5e6268934614f5803d1d102d643a14c7602e8323266195948a282220c1013c1f0e39511f8c5adc95aa9e843244be01904589eae57e
6
+ metadata.gz: f6f4e9e7bcf14ccc5943ec41d4468338d1cf62aec7d37b296d0fa37a3c1c7bc97711b06579d10b32167ce291b8d664989f90eecfcba9a94f228ff60e7e8698e0
7
+ data.tar.gz: 4fcd0dacd12572c3a4b404709ec632dcc6c5cc964dfd9dc29d92a4dd03da2b10ddc3e5df57a6ed91cb3f90e0dac70c4668ba89040f8cf5788e34d1f8e857d3b8
@@ -25,6 +25,14 @@ module Decidim
25
25
  def recipient_user
26
26
  options[:recipient_user]
27
27
  end
28
+
29
+ def custom_url_for_mail_root
30
+ options[:custom_url_for_mail_root]
31
+ end
32
+
33
+ def decidim
34
+ @decidim ||= EngineRouter.new("decidim", {})
35
+ end
28
36
  end
29
37
  end
30
38
  end
@@ -16,7 +16,7 @@
16
16
  <tr>
17
17
  <th>
18
18
  <center>
19
- <%= render partial: "layouts/decidim/mailer_logo.html", locals: { organization: organization } %>
19
+ <%= render partial: "layouts/decidim/mailer_logo.html", locals: { organization: organization, custom_url_for_mail_root: custom_url_for_mail_root } %>
20
20
  </center>
21
21
  </th>
22
22
  </tr>
@@ -27,7 +27,7 @@
27
27
  <tr>
28
28
  <th>
29
29
  <% if organization.official_img_header.attached? %>
30
- <%= link_to organization.official_url do %>
30
+ <%= link_to newsletter.organization_official_url do %>
31
31
  <%= image_tag organization.attached_uploader(:official_img_header).path, alt: "", style: "max-height: 50px", class: "float-right" %>
32
32
  <% end %>
33
33
  <% end %>
@@ -72,8 +72,8 @@
72
72
  <th class="expander"></th>
73
73
  <th class="small-12 first columns cityhall-bar">
74
74
  <div class="decidim-logo" style="float: right; text-align: right; padding-right: 16px">
75
- <% if @custom_url_for_mail_root.present? %>
76
- <%= link_to organization.name.html_safe, @custom_url_for_mail_root %>
75
+ <% if custom_url_for_mail_root.present? %>
76
+ <%= link_to organization.name.html_safe, custom_url_for_mail_root %>
77
77
  <% else %>
78
78
  <%= link_to organization.name.html_safe, decidim.root_url(host: organization.host) %>
79
79
  <% end %>
@@ -24,7 +24,7 @@ table.button table td {
24
24
  <tr>
25
25
  <th>
26
26
  <center>
27
- <%= render partial: "layouts/decidim/mailer_logo.html", locals: { organization: organization } %>
27
+ <%= render partial: "layouts/decidim/mailer_logo.html", locals: { organization: organization, custom_url_for_mail_root: custom_url_for_mail_root } %>
28
28
  </center>
29
29
  </th>
30
30
  </tr>
@@ -35,7 +35,7 @@ table.button table td {
35
35
  <tr>
36
36
  <th>
37
37
  <% if organization.official_img_header.attached? %>
38
- <%= link_to organization.official_url do %>
38
+ <%= link_to newsletter.organization_official_url do %>
39
39
  <%= image_tag organization.attached_uploader(:official_img_header).url(host: organization.host), alt: "", style: "max-height: 50px", class: "float-right" %>
40
40
  <% end %>
41
41
  <% end %>
@@ -111,8 +111,8 @@ table.button table td {
111
111
  <th class="expander"></th>
112
112
  <th class="small-12 first columns cityhall-bar">
113
113
  <div class="decidim-logo" style="float: right; text-align: right; padding-right: 16px">
114
- <% if @custom_url_for_mail_root.present? %>
115
- <%= link_to organization.name.html_safe, @custom_url_for_mail_root %>
114
+ <% if custom_url_for_mail_root.present? %>
115
+ <%= link_to organization.name.html_safe, custom_url_for_mail_root %>
116
116
  <% else %>
117
117
  <%= link_to organization.name.html_safe, decidim.root_url(host: organization.host) %>
118
118
  <% end %>
@@ -30,6 +30,7 @@
30
30
  add_attribute: add_attribute,
31
31
  resource_name: resource_name,
32
32
  resource_class: resource_class,
33
+ required: required?,
33
34
  optional: optional,
34
35
  max_file_size: max_file_size,
35
36
  multiple: multiple,
@@ -5,6 +5,7 @@ module Decidim
5
5
  class UploadModalCell < Decidim::ViewModel
6
6
  include Cell::ViewModel::Partial
7
7
  include ERB::Util
8
+ include Decidim::SanitizeHelper
8
9
 
9
10
  alias form model
10
11
 
@@ -29,7 +30,7 @@ module Decidim
29
30
  end
30
31
 
31
32
  def label
32
- options[:label]
33
+ form.send(:custom_label, attribute, options[:label], { required: required?, for: nil })
33
34
  end
34
35
 
35
36
  def button_label
@@ -72,8 +73,17 @@ module Decidim
72
73
  options[:multiple] || false
73
74
  end
74
75
 
76
+ # @deprecated Please use `required?` instead.
77
+ #
78
+ # NOTE: When this is removed, also the `optional` option should be removed.
75
79
  def optional
76
- options[:optional]
80
+ !required?
81
+ end
82
+
83
+ def required?
84
+ return !options[:optional] if options.has_key?(:optional)
85
+
86
+ options[:required] == true
77
87
  end
78
88
 
79
89
  # By default Foundation adds form errors next to input, but since input is in the modal
@@ -81,15 +91,24 @@ module Decidim
81
91
  # This should only be necessary when file is required by the form.
82
92
  def input_validation_field
83
93
  object_name = form.object.present? ? "#{form.object.model_name.param_key}[#{add_attribute}_validation]" : "#{add_attribute}_validation"
84
- input = check_box_tag object_name, 1, attachments.present?, class: "hide", label: false, required: !optional
94
+ input = check_box_tag object_name, 1, attachments.present?, class: "hide", label: false, required: required?
85
95
  message = form.send(:abide_error_element, add_attribute) + form.send(:error_and_help_text, add_attribute)
86
96
  input + message
87
97
  end
88
98
 
89
99
  def explanation
90
- return I18n.t("explanation", scope: options[:help_i18n_scope], attribute: attribute) if options[:help_i18n_scope].present?
100
+ i18n_options = {
101
+ scope: options[:help_i18n_scope].presence || "decidim.forms.upload_help",
102
+ attribute: attribute_translation
103
+ }
91
104
 
92
- I18n.t("explanation", scope: "decidim.forms.upload_help", attribute: attribute)
105
+ I18n.t("explanation", **i18n_options)
106
+ end
107
+
108
+ def attribute_translation
109
+ I18n.t(attribute, scope: [:activemodel, :attributes, resource_class.constantize.model_name.param_key].join("."))
110
+ rescue NameError
111
+ I18n.t(attribute, scope: "activemodel.attributes")
93
112
  end
94
113
 
95
114
  def add_attribute
@@ -133,7 +152,7 @@ module Decidim
133
152
  def title_for(attachment)
134
153
  return unless has_title?
135
154
 
136
- translated_attribute(attachment.title)
155
+ decidim_html_escape(decidim_sanitize(translated_attribute(attachment.title)))
137
156
  end
138
157
 
139
158
  def truncated_file_name_for(attachment, max_length = 31)
@@ -145,11 +164,7 @@ module Decidim
145
164
  end
146
165
 
147
166
  def file_name_for(attachment)
148
- filename = determine_filename(attachment)
149
-
150
- return "(#{filename})" if has_title?
151
-
152
- filename
167
+ determine_filename(attachment)
153
168
  end
154
169
 
155
170
  def determine_filename(attachment)
@@ -12,8 +12,8 @@ module Decidim
12
12
  @attachment = Attachment.new(
13
13
  title: { I18n.locale => @form.attachment.title },
14
14
  attached_to: attached_to,
15
- file: @form.attachment.file, # Define attached_to before this
16
- content_type: @form.attachment.file.content_type
15
+ file: signed_id_for(@form.attachment.file),
16
+ content_type: content_type_for(@form.attachment.file)
17
17
  )
18
18
  end
19
19
 
@@ -53,5 +53,23 @@ module Decidim
53
53
  def delete_attachment?
54
54
  @form.attachment&.delete_file.present?
55
55
  end
56
+
57
+ protected
58
+
59
+ def signed_id_for(attachment)
60
+ return attachment[:file] if attachment.is_a?(Hash)
61
+
62
+ attachment
63
+ end
64
+
65
+ def content_type_for(attachment)
66
+ return attachment.content_type if attachment.instance_of?(ActionDispatch::Http::UploadedFile)
67
+
68
+ blob(signed_id_for(attachment)).content_type
69
+ end
70
+
71
+ def blob(signed_id)
72
+ ActiveStorage::Blob.find_signed(signed_id)
73
+ end
56
74
  end
57
75
  end
@@ -41,6 +41,7 @@ module Decidim
41
41
  nickname: form.nickname,
42
42
  password: form.password,
43
43
  password_confirmation: form.password_confirmation,
44
+ password_updated_at: Time.current,
44
45
  organization: form.current_organization,
45
46
  tos_agreement: form.tos_agreement,
46
47
  newsletter_notifications_at: form.newsletter_at,
@@ -24,7 +24,7 @@ module Decidim
24
24
  end
25
25
 
26
26
  def update_attachment_title_for(photo)
27
- Decidim::Attachment.find(photo[:id]).update(title: title_for(photo))
27
+ Decidim::Attachment.find(photo[:id]).update(title: photos_title(photo))
28
28
  end
29
29
 
30
30
  def image?(signed_id)
@@ -31,7 +31,7 @@ module Decidim
31
31
  query = if @current_group.present?
32
32
  @resource.endorsements.where(decidim_user_group_id: @current_group&.id)
33
33
  else
34
- @resource.endorsements.where(author: @current_user)
34
+ @resource.endorsements.where(author: @current_user, decidim_user_group_id: nil)
35
35
  end
36
36
  query.destroy_all
37
37
  end
@@ -55,6 +55,7 @@ module Decidim
55
55
 
56
56
  @user.password = @form.password
57
57
  @user.password_confirmation = @form.password_confirmation
58
+ @user.password_updated_at = Time.current
58
59
  end
59
60
 
60
61
  def notify_followers
@@ -16,6 +16,8 @@ module Decidim
16
16
  return broadcast(:invalid) if form.invalid?
17
17
 
18
18
  user.password = form.password
19
+ user.password_confirmation = form.password
20
+ user.password_updated_at = Time.current
19
21
 
20
22
  if user.save
21
23
  broadcast(:ok)
@@ -6,9 +6,25 @@ module Decidim
6
6
  class SessionsController < ::Devise::SessionsController
7
7
  include Decidim::DeviseControllers
8
8
 
9
- # rubocop: disable Rails/LexicallyScopedActionFilter
10
9
  before_action :check_sign_in_enabled, only: :create
11
- # rubocop: enable Rails/LexicallyScopedActionFilter
10
+
11
+ def create
12
+ super do |user|
13
+ if user.admin?
14
+ # Check that the admin password passes the validation and clear the
15
+ # `password_updated_at` field when the password is weak to force a
16
+ # password update on the user.
17
+ #
18
+ # Handles a case when the user registers through the registration
19
+ # form and they are promoted to an admin after that. In this case,
20
+ # the newly promoted admin user would otherwise have to change their
21
+ # password straight away even if they originally registered with a
22
+ # strong password.
23
+ validator = PasswordValidator.new({ attributes: :password })
24
+ user.update!(password_updated_at: nil) unless validator.validate_each(user, :password, sign_in_params[:password])
25
+ end
26
+ end
27
+ end
12
28
 
13
29
  def destroy
14
30
  current_user.invalidate_all_sessions!
@@ -7,6 +7,7 @@ module Decidim
7
7
  include UserGroups
8
8
 
9
9
  before_action :enforce_user_groups_enabled
10
+ before_action :ensure_user_group_not_blocked
10
11
 
11
12
  def new
12
13
  enforce_permission_to :create, :user_group, current_user: current_user
@@ -78,6 +79,10 @@ module Decidim
78
79
 
79
80
  private
80
81
 
82
+ def ensure_user_group_not_blocked
83
+ raise ActionController::RoutingError, "Blocked User Group" if user_group&.blocked?
84
+ end
85
+
81
86
  def accepted_user_group
82
87
  @accepted_user_group ||= Decidim::UserGroups::AcceptedUserGroups.for(current_user).find_by(nickname: params[:id])
83
88
  end
@@ -7,9 +7,11 @@ module Decidim
7
7
  skip_before_action :store_current_location
8
8
 
9
9
  helper Decidim::ExternalDomainHelper
10
+ helper_method :external_url
10
11
 
11
12
  before_action :parse_url
12
13
  rescue_from Decidim::InvalidUrlError, with: :invalid_url
14
+ rescue_from URI::InvalidURIError, with: :invalid_url
13
15
 
14
16
  def new
15
17
  headers["X-Robots-Tag"] = "noindex"
@@ -19,24 +21,21 @@ module Decidim
19
21
 
20
22
  def invalid_url
21
23
  flash[:alert] = I18n.t("decidim.links.invalid_url")
22
- redirect_to decidim.root_path
24
+ if request.xhr?
25
+ render "invalid_url"
26
+ else
27
+ redirect_to decidim.root_path
28
+ end
23
29
  end
24
30
 
25
31
  def parse_url
32
+ raise Decidim::InvalidUrlError if params[:external_url].blank?
26
33
  raise Decidim::InvalidUrlError unless external_url
27
-
28
- parts = external_url.match %r{^(([a-z]+):)?//([^/]+)(/.*)?$}
29
- raise Decidim::InvalidUrlError unless parts
30
-
31
- @url_parts = {
32
- protocol: parts[1],
33
- domain: parts[3],
34
- path: parts[4]
35
- }
34
+ raise Decidim::InvalidUrlError unless %w(http https).include?(external_url.scheme)
36
35
  end
37
36
 
38
37
  def external_url
39
- @external_url ||= params[:external_url]
38
+ @external_url ||= URI.parse(params[:external_url])
40
39
  end
41
40
  end
42
41
  end
@@ -13,7 +13,7 @@ module Decidim
13
13
  before_action :ensure_profile_holder
14
14
  before_action :ensure_profile_holder_is_a_group, only: [:members]
15
15
  before_action :ensure_profile_holder_is_a_user, only: [:groups, :following]
16
- before_action :ensure_user_not_blocked, only: [:following, :followers, :badges]
16
+ before_action :ensure_user_not_blocked
17
17
 
18
18
  def show
19
19
  return redirect_to profile_timeline_path(nickname: params[:nickname]) if profile_holder == current_user
@@ -35,6 +35,7 @@ module Decidim
35
35
  end
36
36
 
37
37
  def user_flaggable?
38
+ return if (try(:profile_holder) || try(:profile_user) || try(:model)).try(:blocked)
38
39
  return unless context[:controller].try(:flaggable_controller?)
39
40
 
40
41
  true
@@ -3,10 +3,21 @@
3
3
  module Decidim
4
4
  module ExternalDomainHelper
5
5
  def highlight_domain
6
+ highlighted_domain = [
7
+ external_url.host,
8
+ (external_url.port && [80, 443].include?(external_url.port) ? "" : ":#{external_url.port}")
9
+ ].join
10
+
11
+ path = [
12
+ external_url.path,
13
+ (external_url.query ? "?#{external_url.query}" : ""),
14
+ (external_url.fragment ? "##{external_url.fragment}" : "")
15
+ ].join
16
+
6
17
  tag.div do
7
- content_tag(:span, "#{@url_parts[:protocol]}//") +
8
- content_tag(:span, @url_parts[:domain], class: "alert") +
9
- content_tag(:span, @url_parts[:path])
18
+ content_tag(:span, "#{external_url.scheme}://") +
19
+ content_tag(:span, highlighted_domain, class: "text-alert") +
20
+ content_tag(:span, path)
10
21
  end
11
22
  end
12
23
  end
@@ -24,7 +24,7 @@ module Decidim
24
24
  #
25
25
  # Returns an HTML tag with the icon.
26
26
  def manifest_icon(manifest, options = {})
27
- if manifest.icon
27
+ if manifest.respond_to?(:icon) && manifest.icon.present?
28
28
  external_icon manifest.icon, options
29
29
  else
30
30
  icon "question-mark", options
@@ -42,9 +42,9 @@ module Decidim
42
42
  def resource_icon(resource, options = {})
43
43
  if resource.instance_of?(Decidim::Comments::Comment)
44
44
  icon "comment-square", options
45
- elsif resource.respond_to?(:component)
45
+ elsif resource.respond_to?(:component) && resource.component.present?
46
46
  component_icon(resource.component, options)
47
- elsif resource.respond_to?(:manifest)
47
+ elsif resource.respond_to?(:manifest) && resource.manifest.present?
48
48
  manifest_icon(resource.manifest, options)
49
49
  elsif resource.is_a?(Decidim::User)
50
50
  icon "person", options
@@ -31,6 +31,7 @@ module Decidim
31
31
  # this method is used to generate the root link on mail with the utm_codes
32
32
  # If the newsletter_id is nil, it returns the root_url
33
33
  def custom_url_for_mail_root(organization, newsletter_id = nil)
34
+ decidim = EngineRouter.new("decidim", {})
34
35
  if newsletter_id.present?
35
36
  decidim.root_url(host: organization.host) + utm_codes(organization.host, newsletter_id.to_s)
36
37
  else
@@ -112,9 +112,10 @@ module Decidim
112
112
  #
113
113
  # @return ActiveSupport::SafeBuffer
114
114
  def render_sanitized_content(resource, method)
115
- content = present(resource).send(method, links: true, strip_tags: !safe_content?)
115
+ content = present(resource).send(method, links: true, strip_tags: !try(:safe_content?))
116
116
 
117
- return decidim_sanitize(content, {}) unless safe_content?
117
+ return decidim_sanitize(content, {}) unless try(:safe_content?)
118
+ return decidim_sanitize_editor_admin(content, {}) if try(:safe_content_admin?)
118
119
 
119
120
  decidim_sanitize_editor(content)
120
121
  end
@@ -11,14 +11,20 @@ module Decidim
11
11
 
12
12
  helper_method :cell
13
13
 
14
- def newsletter(user, newsletter)
14
+ def newsletter(user, newsletter, preview: false)
15
15
  return if user.email.blank?
16
16
 
17
17
  @organization = user.organization
18
18
  @newsletter = newsletter
19
19
  @user = user
20
-
21
- @custom_url_for_mail_root = custom_url_for_mail_root(@organization, @newsletter.id) if Decidim.config.track_newsletter_links
20
+ @preview = preview
21
+
22
+ @custom_url_for_mail_root =
23
+ if @preview
24
+ "#"
25
+ elsif Decidim.config.track_newsletter_links
26
+ custom_url_for_mail_root(@organization, @newsletter.id)
27
+ end
22
28
  @encrypted_token = Decidim::NewsletterEncryptor.sent_at_encrypted(@user.id, @newsletter.sent_at)
23
29
 
24
30
  with_user(user) do
@@ -40,6 +46,7 @@ module Decidim
40
46
  organization: @organization,
41
47
  newsletter: @newsletter,
42
48
  recipient_user: @user,
49
+ custom_url_for_mail_root: @custom_url_for_mail_root,
43
50
  context: {
44
51
  controller: self
45
52
  }
@@ -56,6 +56,24 @@ module Decidim
56
56
  .find_by(scoped_resource_id: id)
57
57
  end
58
58
 
59
+ def url(**kwargs)
60
+ proxy_url(:newsletter_url, id: id, **kwargs)
61
+ end
62
+
63
+ def notifications_settings_url(**kwargs)
64
+ proxy_url(__method__, **kwargs)
65
+ end
66
+
67
+ def unsubscribe_newsletters_url(**kwargs)
68
+ proxy_url(__method__, **kwargs)
69
+ end
70
+
71
+ def organization_official_url
72
+ return "#" unless sent?
73
+
74
+ organization.official_url || proxy_url(:root_url)
75
+ end
76
+
59
77
  private
60
78
 
61
79
  def author_belongs_to_organization
@@ -63,5 +81,15 @@ module Decidim
63
81
 
64
82
  errors.add(:author, :invalid) unless author.organization == organization
65
83
  end
84
+
85
+ def proxy_url(method, **kwargs)
86
+ return "#" unless sent?
87
+
88
+ router.public_send(method, host: organization.host, **kwargs)
89
+ end
90
+
91
+ def router
92
+ @router ||= EngineRouter.new("decidim", {})
93
+ end
66
94
  end
67
95
  end
@@ -17,8 +17,32 @@ module Decidim
17
17
 
18
18
  validates :name, presence: true
19
19
 
20
+ before_destroy :detach_dynamic_associations
21
+
20
22
  def self.log_presenter_class_for(_log)
21
23
  Decidim::AdminLog::ScopeTypePresenter
22
24
  end
25
+
26
+ private
27
+
28
+ # This method detaches all records that may have association with the scope
29
+ # type. This cannot be done directly using the `dependent` option in the
30
+ # `has_many` relation in order to avoid tight coupling between the modules.
31
+ #
32
+ # This logic does not have to be applied to any classes that have been
33
+ # defined as `has_many` associations within this model already as they are
34
+ # already handled by the `dependent` option.
35
+ def detach_dynamic_associations
36
+ ActiveRecord::Base.descendants.each do |cls|
37
+ next if cls.abstract_class? || !cls.name&.match?(/^Decidim::/)
38
+ next if cls == self.class || cls == Decidim::Scope
39
+
40
+ cls.reflect_on_all_associations(:belongs_to).each do |ref|
41
+ next unless ref.options[:class_name] == self.class.name
42
+
43
+ cls.where(ref.options[:foreign_key] => id).update_all(ref.options[:foreign_key] => nil) # rubocop:disable Rails/SkipsModelValidations
44
+ end
45
+ end
46
+ end
23
47
  end
24
48
  end
@@ -36,8 +36,6 @@ module Decidim
36
36
  has_many :access_tokens, class_name: "Doorkeeper::AccessToken", foreign_key: :resource_owner_id, dependent: :destroy
37
37
  has_many :reminders, foreign_key: "decidim_user_id", class_name: "Decidim::Reminder", dependent: :destroy
38
38
 
39
- has_one :blocking, class_name: "Decidim::UserBlock", foreign_key: :id, primary_key: :block_id, dependent: :destroy
40
-
41
39
  validates :name, presence: true, unless: -> { deleted? }
42
40
  validates :nickname,
43
41
  presence: true,
@@ -17,6 +17,8 @@ module Decidim
17
17
  has_many :notifications, foreign_key: "decidim_user_id", class_name: "Decidim::Notification", dependent: :destroy
18
18
  has_many :following_follows, foreign_key: "decidim_user_id", class_name: "Decidim::Follow", dependent: :destroy
19
19
 
20
+ has_one :blocking, class_name: "Decidim::UserBlock", foreign_key: :id, primary_key: :block_id, dependent: :destroy
21
+
20
22
  # Regex for name & nickname format validations
21
23
  REGEXP_NAME = /\A(?!.*[<>?%&\^*#@()\[\]=+:;"{}\\|])/
22
24
 
@@ -4,7 +4,7 @@ module Decidim
4
4
  class UserBlock < ApplicationRecord
5
5
  MINIMUM_JUSTIFICATION_LENGTH = 15
6
6
 
7
- belongs_to :user, class_name: "Decidim::User", foreign_key: :decidim_user_id
8
- belongs_to :blocking_user, class_name: "Decidim::User"
7
+ belongs_to :user, class_name: "Decidim::UserBaseEntity", foreign_key: :decidim_user_id
8
+ belongs_to :blocking_user, class_name: "Decidim::UserBaseEntity"
9
9
  end
10
10
  end
@@ -21,7 +21,7 @@ module Decidim
21
21
  foreign_key: :decidim_user_id,
22
22
  source: :user
23
23
 
24
- validates :name, presence: true, uniqueness: { scope: :decidim_organization_id }
24
+ validates :name, presence: true, uniqueness: { scope: :decidim_organization_id }, unless: -> { blocked? }
25
25
 
26
26
  validate :correct_state
27
27
  validate :unique_document_number, if: :has_document_number?
@@ -16,7 +16,6 @@ export default class UploadModal {
16
16
  // - addAttribute - Field name / attribute of resource (e.g. avatar)
17
17
  // - resourceName - The resource to which the attribute belongs (e.g. user)
18
18
  // - resourceClass - Ruby class of the resource (e.g. Decidim::User)
19
- // - optional - Defines if file is optional
20
19
  // - multiple - Defines if multiple files can be uploaded
21
20
  // - titled - Defines if file(s) can have titles
22
21
  // - maxFileSize - Defines maximum file size in bytes
@@ -70,7 +70,9 @@ export default class ClipboardOverride extends Clipboard {
70
70
  const text = ev.clipboardData.getData("text/plain");
71
71
  const files = Array.from(ev.clipboardData.files || []);
72
72
  if (!html && files.length > 0) {
73
- this.quill.uploader.upload(range, files);
73
+ if (typeof this.quill.uploader !== "undefined") {
74
+ this.quill.uploader.upload(range, files);
75
+ }
74
76
  return;
75
77
  }
76
78
  if (html && files.length > 0) {
@@ -79,7 +81,9 @@ export default class ClipboardOverride extends Clipboard {
79
81
  doc.body.childElementCount === 1 &&
80
82
  doc.body.firstElementChild.tagName === "IMG"
81
83
  ) {
82
- this.quill.uploader.upload(range, files);
84
+ if (typeof this.quill.uploader !== "undefined") {
85
+ this.quill.uploader.upload(range, files);
86
+ }
83
87
  return;
84
88
  }
85
89
  }