decidim-core 0.27.0 → 0.27.2

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 (121) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/amendable/announcement_cell.rb +1 -1
  3. data/app/cells/decidim/card_m_cell.rb +1 -1
  4. data/app/cells/decidim/newsletter_templates/base_cell.rb +8 -0
  5. data/app/cells/decidim/newsletter_templates/basic_only_text/show.erb +4 -4
  6. data/app/cells/decidim/newsletter_templates/image_text_cta/show.erb +4 -4
  7. data/app/cells/decidim/upload_modal_cell.rb +12 -7
  8. data/app/commands/decidim/unendorse_resource.rb +1 -1
  9. data/app/controllers/decidim/devise/invitations_controller.rb +9 -2
  10. data/app/controllers/decidim/groups_controller.rb +5 -0
  11. data/app/controllers/decidim/last_activities_controller.rb +5 -2
  12. data/app/controllers/decidim/links_controller.rb +4 -2
  13. data/app/controllers/decidim/profiles_controller.rb +1 -1
  14. data/app/forms/decidim/account_form.rb +2 -2
  15. data/app/forms/decidim/amendable/form.rb +2 -1
  16. data/app/forms/decidim/registration_form.rb +2 -2
  17. data/app/forms/decidim/upload_validation_form.rb +51 -7
  18. data/app/helpers/decidim/icon_helper.rb +3 -3
  19. data/app/helpers/decidim/layout_helper.rb +12 -4
  20. data/app/helpers/decidim/newsletters_helper.rb +1 -0
  21. data/app/helpers/decidim/sanitize_helper.rb +1 -1
  22. data/app/mailers/decidim/newsletter_mailer.rb +10 -3
  23. data/app/mailers/decidim/notification_mailer.rb +1 -0
  24. data/app/mailers/decidim/notifications_digest_mailer.rb +1 -0
  25. data/app/models/decidim/newsletter.rb +28 -0
  26. data/app/models/decidim/user.rb +0 -2
  27. data/app/models/decidim/user_base_entity.rb +2 -0
  28. data/app/models/decidim/user_block.rb +2 -2
  29. data/app/models/decidim/user_group.rb +1 -1
  30. data/app/packs/src/decidim/editor/clipboard_override.js +143 -0
  31. data/app/packs/src/decidim/editor/clipboard_utilities.js +119 -0
  32. data/app/packs/src/decidim/editor/linebreak_module.js +0 -8
  33. data/app/packs/src/decidim/editor.js +9 -2
  34. data/app/packs/src/decidim/form_filter.component.test.js +148 -5
  35. data/app/packs/src/decidim/form_filter.js +26 -4
  36. data/app/packs/stylesheets/decidim/_editor.scss +129 -0
  37. data/app/packs/stylesheets/decidim/email.scss +7 -0
  38. data/app/packs/stylesheets/decidim/extras/_quill.scss +0 -6
  39. data/app/presenters/decidim/admin_log/user_group_presenter.rb +1 -1
  40. data/app/presenters/decidim/admin_log/user_moderation_presenter.rb +1 -1
  41. data/app/presenters/decidim/home_stats_presenter.rb +11 -4
  42. data/app/presenters/decidim/push_notification_presenter.rb +1 -1
  43. data/app/presenters/decidim/stats_presenter.rb +7 -8
  44. data/app/presenters/decidim/user_presenter.rb +9 -4
  45. data/app/queries/decidim/public_activities.rb +1 -0
  46. data/app/uploaders/decidim/application_uploader.rb +1 -1
  47. data/app/uploaders/decidim/avatar_uploader.rb +2 -2
  48. data/app/validators/etiquette_validator.rb +7 -3
  49. data/app/validators/file_content_type_validator.rb +103 -0
  50. data/app/validators/passthru_validator.rb +11 -0
  51. data/app/validators/uploader_content_type_validator.rb +22 -0
  52. data/app/views/decidim/messaging/conversations/_conversation.html.erb +1 -1
  53. data/app/views/decidim/newsletter_mailer/newsletter.html.erb +3 -3
  54. data/app/views/decidim/newsletters/show.html.erb +1 -1
  55. data/app/views/decidim/notification_mailer/event_received.html.erb +1 -1
  56. data/app/views/decidim/notifications_digest_mailer/_email_content.html.erb +1 -1
  57. data/app/views/layouts/decidim/_mailer_logo.html.erb +2 -2
  58. data/app/views/layouts/decidim/newsletter_base.html.erb +2 -2
  59. data/config/locales/ar.yml +5 -17
  60. data/config/locales/bg.yml +5 -17
  61. data/config/locales/ca.yml +20 -24
  62. data/config/locales/cs.yml +12 -17
  63. data/config/locales/de.yml +2 -18
  64. data/config/locales/el.yml +4 -18
  65. data/config/locales/en.yml +11 -15
  66. data/config/locales/es-MX.yml +13 -17
  67. data/config/locales/es-PY.yml +13 -17
  68. data/config/locales/es.yml +22 -26
  69. data/config/locales/eu.yml +28 -35
  70. data/config/locales/fi-plain.yml +11 -15
  71. data/config/locales/fi.yml +12 -16
  72. data/config/locales/fr-CA.yml +11 -18
  73. data/config/locales/fr.yml +11 -18
  74. data/config/locales/ga-IE.yml +0 -2
  75. data/config/locales/gl.yml +2 -17
  76. data/config/locales/gn-PY.yml +1 -0
  77. data/config/locales/hu.yml +4 -18
  78. data/config/locales/id-ID.yml +5 -17
  79. data/config/locales/is-IS.yml +0 -1
  80. data/config/locales/it.yml +1 -18
  81. data/config/locales/ja.yml +25 -29
  82. data/config/locales/ka-GE.yml +1 -0
  83. data/config/locales/lb.yml +0 -17
  84. data/config/locales/lo-LA.yml +1 -0
  85. data/config/locales/lt.yml +0 -17
  86. data/config/locales/lv.yml +5 -17
  87. data/config/locales/nl.yml +0 -17
  88. data/config/locales/no.yml +2 -19
  89. data/config/locales/pl.yml +4 -18
  90. data/config/locales/pt-BR.yml +0 -17
  91. data/config/locales/pt.yml +0 -17
  92. data/config/locales/ro-RO.yml +49 -16
  93. data/config/locales/ru.yml +5 -3
  94. data/config/locales/sk.yml +5 -17
  95. data/config/locales/sv.yml +22 -18
  96. data/config/locales/tr-TR.yml +4 -18
  97. data/config/locales/uk.yml +5 -1
  98. data/config/locales/zh-CN.yml +3 -17
  99. data/lib/decidim/api/types/localized_string_type.rb +9 -0
  100. data/lib/decidim/api/types/translated_field_type.rb +20 -5
  101. data/lib/decidim/asset_router/pipeline.rb +93 -0
  102. data/lib/decidim/asset_router/storage.rb +82 -0
  103. data/lib/decidim/asset_router.rb +3 -75
  104. data/lib/decidim/attribute_object/form.rb +9 -0
  105. data/lib/decidim/attributes/localized_date.rb +1 -1
  106. data/lib/decidim/attributes/time_with_zone.rb +5 -2
  107. data/lib/decidim/core/engine.rb +7 -5
  108. data/lib/decidim/core/test/factories.rb +13 -6
  109. data/lib/decidim/core/test/shared_examples/comments_examples.rb +1 -1
  110. data/lib/decidim/core/test/shared_examples/editor_shared_examples.rb +30 -0
  111. data/lib/decidim/core/test/shared_examples/mcell_examples.rb +17 -0
  112. data/lib/decidim/core/test.rb +2 -0
  113. data/lib/decidim/core/version.rb +1 -1
  114. data/lib/decidim/dependency_resolver.rb +14 -8
  115. data/lib/decidim/file_validator_humanizer.rb +1 -1
  116. data/lib/decidim/form_builder.rb +11 -4
  117. data/lib/decidim/participatory_space_resourceable.rb +7 -1
  118. data/lib/decidim/resourceable.rb +5 -4
  119. data/lib/decidim/settings_manifest.rb +1 -1
  120. metadata +17 -8
  121. data/app/packs/images/decidim/gamification/badges/decidim_gamification_badges_invitations.svg +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d066aea0f8b2cede42dcd7ac23f6a22d8685f45e776cd5f7e2a8a39a8498ee9
4
- data.tar.gz: a5536659a0c2c25cedde842b9c3f747e428a9b9b667d69bbf04c64bb9ef98125
3
+ metadata.gz: 391ba34f55c860208f7644dd10b1b3e2b6465ed45b9e9f9fd194ce1036516995
4
+ data.tar.gz: 93636f8d556d73547fcdff45986fcc85ba4a22e9c399fdeca0c550ee6c241363
5
5
  SHA512:
6
- metadata.gz: a818266bb8122ab36bedb0aac0991a98ac0eb460482b4d1c4188eab66136fadda00ffe7d8452735598a2ee8a2198670d25d57afe6a2b4c7a3761bd21cac41655
7
- data.tar.gz: e5c1b93af9f057e33a773e443a3badc07c20af25be4f874ad1ed6219cbf92ed82bd0e91d9aeb48534ef90952087f0be26a718b872afe71254085ca9629e8dd25
6
+ metadata.gz: 157c005fbea98fe374586f91f15f17bc54450d6e4b6f7136d51582d5cae7af9442d250edbbf4fe2a4308aac902ba0eb28c49681153dcda6b6948f8589414e930
7
+ data.tar.gz: 8ebc9089ad86980956ded3024d490f419bf8015ae0dc4b3fe6e003d415010de8ed28751bd0c168cf526c8a17f5db50467c9223242c2150cf600304990e1eda04
@@ -36,7 +36,7 @@ module Decidim::Amendable
36
36
  end
37
37
 
38
38
  def proposal_link(resource = model.amendable, text = nil)
39
- text ||= %(<strong>#{present(model.amendable).title}</strong>)
39
+ text ||= %(<strong>#{decidim_sanitize(present(model.amendable).title, strip_tags: true)}</strong>)
40
40
  link_to resource_locator(resource).path do
41
41
  text
42
42
  end
@@ -57,7 +57,7 @@ module Decidim
57
57
  end
58
58
 
59
59
  def title
60
- translated_attribute model.title
60
+ decidim_html_escape(translated_attribute(model.title))
61
61
  end
62
62
 
63
63
  def description
@@ -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 %>
@@ -87,9 +87,18 @@ module Decidim
87
87
  end
88
88
 
89
89
  def explanation
90
- return I18n.t("explanation", scope: options[:help_i18n_scope], attribute: attribute) if options[:help_i18n_scope].present?
90
+ i18n_options = {
91
+ scope: options[:help_i18n_scope].presence || "decidim.forms.upload_help",
92
+ attribute: attribute_translation
93
+ }
91
94
 
92
- I18n.t("explanation", scope: "decidim.forms.upload_help", attribute: attribute)
95
+ I18n.t("explanation", **i18n_options)
96
+ end
97
+
98
+ def attribute_translation
99
+ I18n.t(attribute, scope: [:activemodel, :attributes, resource_class.constantize.model_name.param_key].join("."))
100
+ rescue NameError
101
+ I18n.t(attribute, scope: "activemodel.attributes")
93
102
  end
94
103
 
95
104
  def add_attribute
@@ -145,11 +154,7 @@ module Decidim
145
154
  end
146
155
 
147
156
  def file_name_for(attachment)
148
- filename = determine_filename(attachment)
149
-
150
- return "(#{filename})" if has_title?
151
-
152
- filename
157
+ determine_filename(attachment)
153
158
  end
154
159
 
155
160
  def determine_filename(attachment)
@@ -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
@@ -21,7 +21,7 @@ module Decidim
21
21
  # invitation. Using the param `invite_redirect` we can redirect the user
22
22
  # to a custom path after it has accepted the invitation.
23
23
  def after_accept_path_for(resource)
24
- params[:invite_redirect] || after_sign_in_path_for(resource)
24
+ invite_redirect_path || after_sign_in_path_for(resource)
25
25
  end
26
26
 
27
27
  # When a managed user accepts the invitation is promoted to non-managed user.
@@ -32,7 +32,6 @@ module Decidim
32
32
  resource.update!(newsletter_notifications_at: Time.current) if update_resource_params[:newsletter_notifications]
33
33
  resource.update!(managed: false) if resource.managed?
34
34
  resource.update!(accepted_tos_version: resource.organization.tos_version)
35
- Decidim::Gamification.increment_score(resource.invited_by, :invitations) if resource.invited_by
36
35
  end
37
36
 
38
37
  resource
@@ -40,6 +39,14 @@ module Decidim
40
39
 
41
40
  protected
42
41
 
42
+ def invite_redirect_path
43
+ path = params[:invite_redirect]
44
+ return unless path
45
+ return unless path.starts_with?(%r{^/[a-z0-9]+})
46
+
47
+ path
48
+ end
49
+
43
50
  def configure_permitted_parameters
44
51
  devise_parameter_sanitizer.permit(:accept_invitation, keys: [:nickname, :tos_agreement, :newsletter_notifications])
45
52
  end
@@ -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
@@ -33,8 +33,11 @@ module Decidim
33
33
 
34
34
  def search_collection
35
35
  ActionLog
36
- .where(visibility: %w(public-only all))
37
- .where(organization: current_organization)
36
+ .where(
37
+ organization: current_organization,
38
+ visibility: %w(public-only all)
39
+ )
40
+ .with_new_resource_type("all")
38
41
  .order(created_at: :desc)
39
42
  end
40
43
 
@@ -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"
@@ -25,7 +27,7 @@ module Decidim
25
27
  def parse_url
26
28
  raise Decidim::InvalidUrlError unless external_url
27
29
 
28
- parts = external_url.match %r{^(([a-z]+):)?//([^/]+)(/.*)?$}
30
+ parts = external_url.match %r{\A(([a-z]+):)?//([^/]+)(/.*)?\z}
29
31
  raise Decidim::InvalidUrlError unless parts
30
32
 
31
33
  @url_parts = {
@@ -36,7 +38,7 @@ module Decidim
36
38
  end
37
39
 
38
40
  def external_url
39
- @external_url ||= params[:external_url]
41
+ @external_url ||= URI.parse(params[:external_url]).to_s
40
42
  end
41
43
  end
42
44
  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
@@ -19,9 +19,9 @@ module Decidim
19
19
  attribute :personal_url
20
20
  attribute :about
21
21
 
22
- validates :name, presence: true
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: Decidim::User::REGEXP_NICKNAME
24
+ validates :nickname, presence: true, format: { with: Decidim::User::REGEXP_NICKNAME }
25
25
 
26
26
  validates :nickname, length: { maximum: Decidim::User.nickname_max_length, allow_blank: true }
27
27
  validates :password, confirmation: true
@@ -66,7 +66,8 @@ module Decidim
66
66
  errors = amendable_form_errors.details[key] - @original_form.errors.details[key]
67
67
 
68
68
  errors.map do |hash|
69
- @amendable_form.errors.add(key, hash[:error]) unless @amendable_form.errors.details[key].include? error: hash[:error]
69
+ error = hash.delete(:error)
70
+ @amendable_form.errors.add(key, error, **hash) unless @amendable_form.errors.details[key].include?(error: error)
70
71
  end
71
72
  end
72
73
  end
@@ -14,8 +14,8 @@ module Decidim
14
14
  attribute :tos_agreement, Boolean
15
15
  attribute :current_locale, String
16
16
 
17
- validates :name, presence: true
18
- validates :nickname, presence: true, format: Decidim::User::REGEXP_NICKNAME, length: { maximum: Decidim::User.nickname_max_length }
17
+ validates :name, presence: true, format: { with: Decidim::User::REGEXP_NAME }
18
+ validates :nickname, presence: true, format: { with: Decidim::User::REGEXP_NICKNAME }, length: { maximum: Decidim::User.nickname_max_length }
19
19
  validates :email, presence: true, "valid_email_2/email": { disposable: true }
20
20
  validates :password, confirmation: true
21
21
  validates :password, password: { name: :name, email: :email, username: :nickname }
@@ -7,8 +7,8 @@ module Decidim
7
7
  include Decidim::HasUploadValidations
8
8
 
9
9
  attribute :resource_class, String
10
- # Property is named as attribute in upload modal and passthru validator, but it
11
- # cannot be named as attribute here.
10
+ # Property is named as attribute in upload modal and passthru validator, but
11
+ # it cannot be named as attribute here.
12
12
  attribute :property, String
13
13
  attribute :blob, String
14
14
  attribute :form_class, String
@@ -16,9 +16,22 @@ module Decidim
16
16
  validates :resource_class, presence: true
17
17
  validates :property, presence: true
18
18
  validates :blob, presence: true
19
- validate :file, if: ->(form) { form.resource_class.present? && form.property.present? && form.blob.present? }
19
+ validate :file_validators, if: ->(form) { form.resource_class.present? && form.property.present? && form.blob.present? }
20
20
 
21
- def file
21
+ alias organization current_organization
22
+
23
+ # This is a "trick" to provide the attachment context (i.e. admin or
24
+ # participant) to the attachment records being validated. This is to show
25
+ # the invalid content type / file extension errors with the correct file
26
+ # extensions that may be shown in the help text next to the upload
27
+ # drag'n'drop field.
28
+ def attached_to
29
+ @attached_to ||= AttachmentContextProxy.new(organization, attachment_context)
30
+ end
31
+
32
+ private
33
+
34
+ def file_validators
22
35
  org = organization
23
36
  PassthruValidator.new(
24
37
  attributes: [property],
@@ -31,8 +44,6 @@ module Decidim
31
44
  ).validate_each(self, property.to_sym, blob)
32
45
  end
33
46
 
34
- private
35
-
36
47
  def validate_with
37
48
  if form_object_class && form_object_class._validators[property.to_sym].is_a?(Array) && form_object_class._validators[property.to_sym].size.positive?
38
49
  passthru = form_object_class._validators[property.to_sym].find { |v| v.is_a?(PassthruValidator) }
@@ -49,6 +60,39 @@ module Decidim
49
60
  end
50
61
  end
51
62
 
52
- alias organization current_organization
63
+ # The attachment context (i.e. admin or participant) is determined using the
64
+ # form class name and checking if it contains the `Admin` namespace in it.
65
+ # And example use case is the attachment forms in the admin panel.
66
+ def attachment_context
67
+ return :participant unless form_object_class
68
+ return :admin if form_object_class.name.include? "::Admin::"
69
+
70
+ :participant
71
+ end
72
+
73
+ # This class provides ability to interpret the attachment context based on
74
+ # the details available within the context of this class. Normally the
75
+ # attachment context would be defined by the record to which the attachment
76
+ # are added to, e.g. proposals (participant contenxt) or participatory
77
+ # processes (admin context). Unfortunately this information is not available
78
+ # when the parameters are passed to the upload validation.
79
+ class AttachmentContextProxy
80
+ attr_reader :organization, :attachment_context
81
+
82
+ delegate :id, :_read_attribute, to: :organization
83
+
84
+ def initialize(organization, attachment_context)
85
+ @organization = organization
86
+ @attachment_context = attachment_context
87
+ end
88
+
89
+ def self.primary_key
90
+ :id
91
+ end
92
+
93
+ def self.polymorphic_name
94
+ "Decidim::Organization"
95
+ end
96
+ end
53
97
  end
54
98
  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
@@ -88,8 +88,11 @@ module Decidim
88
88
  classes = _icon_classes(options) + ["external-icon"]
89
89
 
90
90
  if path.split(".").last == "svg"
91
+ icon_path = application_path(path)
92
+ return unless icon_path
93
+
91
94
  attributes = { class: classes.join(" ") }.merge(options)
92
- asset = File.read(application_path(path))
95
+ asset = File.read(icon_path)
93
96
  asset.gsub("<svg ", "<svg#{tag_builder.tag_options(attributes)} ").html_safe
94
97
  else
95
98
  image_pack_tag(path, class: classes.join(" "), style: "display: none")
@@ -97,9 +100,14 @@ module Decidim
97
100
  end
98
101
 
99
102
  def application_path(path)
100
- img_path = asset_pack_path(path)
101
- img_path = URI(img_path).path if Decidim.cors_enabled
102
- Rails.root.join("public/#{img_path}")
103
+ # Force the path to be returned without the protocol and host even when a
104
+ # custom asset host has been defined. The host parameter needs to be a
105
+ # non-nil because otherwise it will be set to the asset host at
106
+ # ActionView::Helpers::AssetUrlHelper#compute_asset_host.
107
+ img_path = asset_pack_path(path, host: "", protocol: :relative)
108
+ Rails.public_path.join(img_path.sub(%r{^/}, ""))
109
+ rescue ::Webpacker::Manifest::MissingEntryError
110
+ nil
103
111
  end
104
112
 
105
113
  # Allows to create role attribute according to accessibility rules
@@ -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
@@ -37,7 +37,7 @@ module Decidim
37
37
  end
38
38
 
39
39
  def decidim_sanitize_editor(html, options = {})
40
- content_tag(:div, decidim_sanitize(html, options), class: %w(ql-editor ql-reset-decidim))
40
+ content_tag(:div, decidim_sanitize(html, options), class: %w(ql-editor-display))
41
41
  end
42
42
 
43
43
  def decidim_sanitize_editor_admin(html, options = {})
@@ -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
  }
@@ -5,6 +5,7 @@ module Decidim
5
5
  # a events are received.
6
6
  class NotificationMailer < Decidim::ApplicationMailer
7
7
  helper Decidim::ResourceHelper
8
+ helper Decidim::SanitizeHelper
8
9
 
9
10
  def event_received(event, event_class_name, resource, user, user_role, extra) # rubocop:disable Metrics/ParameterLists
10
11
  with_user(user) do
@@ -5,6 +5,7 @@ module Decidim
5
5
  # a events are received.
6
6
  class NotificationsDigestMailer < Decidim::ApplicationMailer
7
7
  helper Decidim::ResourceHelper
8
+ helper Decidim::SanitizeHelper
8
9
  SIZE_LIMIT = 10
9
10
 
10
11
  def digest_mail(user, notification_ids)
@@ -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
@@ -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?