decidim-core 0.27.0 → 0.27.2

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 (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
@@ -10,4 +10,133 @@
10
10
  margin-bottom: $paragraph-margin-bottom;
11
11
  text-rendering: $paragraph-text-rendering;
12
12
  }
13
+
14
+ ul,
15
+ ol{
16
+ margin-bottom: $paragraph-margin-bottom;
17
+ }
18
+ }
19
+
20
+ .ql-editor-display{
21
+ ul,
22
+ ol{
23
+ li{
24
+ list-style-type: none;
25
+ margin-bottom: .5rem;
26
+
27
+ &::before{
28
+ content: "\2022";
29
+ display: inline-block;
30
+ white-space: nowrap;
31
+ width: 1.2em;
32
+ }
33
+
34
+ &.ql-indent-1{
35
+ padding-left: 3em;
36
+ }
37
+
38
+ &.ql-indent-2{
39
+ padding-left: 6em;
40
+ }
41
+
42
+ &.ql-indent-3{
43
+ padding-left: 9em;
44
+ }
45
+
46
+ &.ql-indent-4{
47
+ padding-left: 12em;
48
+ }
49
+
50
+ &.ql-indent-5{
51
+ padding-left: 15em;
52
+ }
53
+
54
+ &.ql-indent-6{
55
+ padding-left: 18em;
56
+ }
57
+
58
+ &.ql-indent-7{
59
+ padding-left: 21em;
60
+ }
61
+
62
+ &.ql-indent-8{
63
+ padding-left: 24em;
64
+ }
65
+
66
+ &.ql-indent-9{
67
+ padding-left: 27em;
68
+ }
69
+ }
70
+ }
71
+
72
+ ol{
73
+ li{
74
+ counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
75
+ counter-increment: list-0;
76
+
77
+ &::before{ content: counter(list-0, decimal) ". "; }
78
+
79
+ &.ql-indent-1{
80
+ counter-increment: list-1;
81
+ counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
82
+
83
+ &::before{ content: counter(list-1, lower-alpha) ". "; }
84
+ }
85
+
86
+ &.ql-indent-2{
87
+ counter-increment: list-2;
88
+ counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
89
+
90
+ &::before{ content: counter(list-2, lower-roman) ". "; }
91
+ }
92
+
93
+ &.ql-indent-3{
94
+ counter-increment: list-3;
95
+ counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
96
+
97
+ &::before{ content: counter(list-3, decimal) ". "; }
98
+ }
99
+
100
+ &.ql-indent-4{
101
+ counter-increment: list-4;
102
+ counter-reset: list-5 list-6 list-7 list-8 list-9;
103
+
104
+ &::before{ content: counter(list-4, lower-alpha) ". "; }
105
+ }
106
+
107
+ &.ql-indent-5{
108
+ counter-increment: list-5;
109
+ counter-reset: list-6 list-7 list-8 list-9;
110
+
111
+ &::before{ content: counter(list-5, lower-roman) ". "; }
112
+ }
113
+
114
+ &.ql-indent-6{
115
+ counter-increment: list-6;
116
+ counter-reset: list-7 list-8 list-9;
117
+
118
+ &::before{ content: counter(list-6, decimal) ". "; }
119
+ }
120
+
121
+ &.ql-indent-7{
122
+ counter-increment: list-7;
123
+ counter-reset: list-8 list-9;
124
+
125
+ &::before{ content: counter(list-7, lower-alpha) ". "; }
126
+ }
127
+
128
+ &.ql-indent-8{
129
+ counter-increment: list-8;
130
+ counter-reset: list-9;
131
+
132
+ &::before{ content: counter(list-8, lower-roman) ". "; }
133
+ }
134
+
135
+ &.ql-indent-9{
136
+ counter-increment: list-9;
137
+
138
+ &::before{ content: counter(list-9, decimal) ". "; }
139
+ }
140
+ }
141
+ }
13
142
  }
@@ -25,6 +25,13 @@ body{
25
25
  -moz-box-sizing: border-box;
26
26
  -webkit-box-sizing: border-box;
27
27
  box-sizing: border-box;
28
+
29
+ &.preview{
30
+ .see-on-website,
31
+ .unsubscribe{
32
+ cursor: not-allowed;
33
+ }
34
+ }
28
35
  }
29
36
 
30
37
  .ExternalClass{
@@ -11,9 +11,3 @@
11
11
  margin-top: 0;
12
12
  margin-bottom: 0;
13
13
  }
14
-
15
- .ql-reset-decidim{
16
- display: inline;
17
- padding: 0;
18
- white-space: inherit;
19
- }
@@ -20,7 +20,7 @@ module Decidim
20
20
 
21
21
  def action_string
22
22
  case action
23
- when "verify", "verify_via_csv", "reject"
23
+ when "verify", "verify_via_csv", "reject", "block", "unblock"
24
24
  "decidim.admin_log.user_group.#{action}"
25
25
  else
26
26
  super
@@ -52,7 +52,7 @@ module Decidim
52
52
  end
53
53
 
54
54
  def unreported_user
55
- @unreported_user ||= Decidim::User.find_by(id: action_log.extra.dig("extra", "user_id"))
55
+ @unreported_user ||= Decidim::UserBaseEntity.find_by(id: action_log.extra.dig("extra", "user_id"))
56
56
  end
57
57
 
58
58
  def has_diff?
@@ -42,12 +42,19 @@ module Decidim
42
42
  end
43
43
 
44
44
  def component_stats(conditions)
45
+ stats = {}
45
46
  Decidim.component_manifests.flat_map do |component|
46
- component.stats.except([:supports_count])
47
- .filter(conditions)
48
- .with_context(published_components)
49
- .map { |name, data| [name, data] }
47
+ component
48
+ .stats.except([:supports_count])
49
+ .filter(conditions)
50
+ .with_context(published_components)
51
+ .each do |name, data|
52
+ stats[name] ||= 0
53
+ stats[name] += data
54
+ end
50
55
  end
56
+
57
+ stats.to_a
51
58
  end
52
59
 
53
60
  def published_components
@@ -19,7 +19,7 @@ module Decidim
19
19
  end
20
20
 
21
21
  def url
22
- resource.reported_content_url
22
+ event_class_instance.resource_url
23
23
  end
24
24
  end
25
25
  end
@@ -15,18 +15,17 @@ module Decidim
15
15
  end
16
16
 
17
17
  def statistics(grouped_stats)
18
- statistics = []
18
+ statistics = {}
19
+
19
20
  grouped_stats.each do |_manifest_name, stats|
20
- stats.each_with_index.each do |stat, _index|
21
- stat.each_with_index.map do |_item, subindex|
22
- next unless (subindex % 3).zero?
23
- next if stat[subindex + 2].zero?
21
+ stats.each do |_space_manifest, component_manifest, count|
22
+ next if count.zero?
24
23
 
25
- statistics << { stat_title: stat[subindex + 1], stat_number: stat[subindex + 2] }
26
- end
24
+ statistics[component_manifest] ||= 0
25
+ statistics[component_manifest] += count
27
26
  end
28
27
  end
29
- statistics
28
+ statistics.map { |key, number| { stat_title: key, stat_number: number } }
30
29
  end
31
30
  end
32
31
  end
@@ -5,7 +5,6 @@ module Decidim
5
5
  # Decorator for users
6
6
  #
7
7
  class UserPresenter < SimpleDelegator
8
- include Rails.application.routes.mounted_helpers
9
8
  include ActionView::Helpers::UrlHelper
10
9
  include Decidim::TranslatableAttributes
11
10
 
@@ -27,7 +26,7 @@ module Decidim
27
26
  def profile_url
28
27
  return "" if respond_to?(:deleted?) && deleted?
29
28
 
30
- decidim.profile_url(__getobj__.nickname, host: __getobj__.organization.host)
29
+ decidim.profile_url(__getobj__.nickname)
31
30
  end
32
31
 
33
32
  def avatar
@@ -36,13 +35,13 @@ module Decidim
36
35
 
37
36
  def avatar_url(variant = nil)
38
37
  return default_avatar_url if __getobj__.blocked?
39
- return avatar.default_url unless avatar.attached?
38
+ return default_avatar_url unless avatar.attached?
40
39
 
41
40
  avatar.path(variant: variant)
42
41
  end
43
42
 
44
43
  def default_avatar_url
45
- attached_uploader(:avatar).default_url
44
+ avatar.default_url
46
45
  end
47
46
 
48
47
  def profile_path
@@ -77,5 +76,11 @@ module Decidim
77
76
  def has_tooltip?
78
77
  true
79
78
  end
79
+
80
+ private
81
+
82
+ def decidim
83
+ @decidim ||= Decidim::EngineRouter.new("decidim", { host: __getobj__.organization.host })
84
+ end
80
85
  end
81
86
  end
@@ -107,6 +107,7 @@ module Decidim
107
107
  LEFT JOIN decidim_participatory_space_private_users AS #{manifest.name}_private_users
108
108
  ON #{manifest.name}_private_users.privatable_to_type = '#{manifest.model_class_name}'
109
109
  AND #{table}.id = #{manifest.name}_private_users.privatable_to_id
110
+ AND #{table}.private_space = 't'
110
111
  SQL
111
112
  ).to_s
112
113
  ).where(
@@ -47,7 +47,7 @@ module Decidim
47
47
  return unless attached?
48
48
 
49
49
  representable = variant(key)
50
- AssetRouter.new(representable).url(**options)
50
+ AssetRouter::Storage.new(representable).url(**options)
51
51
  end
52
52
 
53
53
  def path(options = {})
@@ -12,11 +12,11 @@ module Decidim
12
12
  end
13
13
 
14
14
  def default_url(*)
15
- ActionController::Base.helpers.asset_pack_path("media/images/default-avatar.svg")
15
+ AssetRouter::Pipeline.new("media/images/default-avatar.svg", model: model).url
16
16
  end
17
17
 
18
18
  def default_multiuser_url(*)
19
- ActionController::Base.helpers.asset_pack_path("media/images/avatar-multiuser.png")
19
+ AssetRouter::Pipeline.new("media/images/avatar-multiuser.png", model: model).url
20
20
  end
21
21
  end
22
22
  end
@@ -3,12 +3,16 @@
3
3
  # This validator takes care of ensuring the validated content is
4
4
  # respectful, doesn't use caps, and overall is meaningful.
5
5
  class EtiquetteValidator < ActiveModel::EachValidator
6
+ include ActionView::Helpers::SanitizeHelper
7
+
6
8
  def validate_each(record, attribute, value)
7
9
  return if value.blank?
8
10
 
9
- validate_caps(record, attribute, value)
10
- validate_marks(record, attribute, value)
11
- validate_caps_first(record, attribute, value)
11
+ text_value = strip_tags(value)
12
+
13
+ validate_caps(record, attribute, text_value)
14
+ validate_marks(record, attribute, text_value)
15
+ validate_caps_first(record, attribute, text_value)
12
16
  end
13
17
 
14
18
  private
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # We need to define the validator in the `ActiveModel::Validations` namespace as
4
+ # this is what the `file_validators` uses and this is what gets priority over
5
+ # the root namespace class.
6
+ module ActiveModel
7
+ module Validations
8
+ # This validator provides content type validation for uploaded files. Extends
9
+ # the validator provided by the `file_validators` gem by fixing some weird
10
+ # messages in that validator.
11
+ class FileContentTypeValidator
12
+ private
13
+
14
+ alias mark_invalid_original mark_invalid unless private_method_defined?(:mark_invalid_original)
15
+
16
+ # This fixes the "weird" error messages such as "(?-mix:image\\/.*)" because
17
+ # this is the notation Regexp#to_s will return. Instead, we want to show the
18
+ # inner contents of the regular expressions.
19
+ #
20
+ # @see ActiveModel::Validations::FileContentTypeValidator#mark_invalid
21
+ def mark_invalid(record, attribute, error, option_types)
22
+ mark_invalid_original(record, attribute, error, invalid_types(option_types))
23
+ end
24
+
25
+ # Converts the configured content type matches to extensions if extensions are
26
+ # recognized for the specified content type and if not, the content type
27
+ # string itself.
28
+ #
29
+ # @param option_types [Array<Regexp, String>] Array of configured types.
30
+ # @param allowed_extensions [Array<String>, nil] Array of allowed extensions
31
+ # or nil if all extensions are "allowed" (in case we want to show the
32
+ # denylist error or hard code the types).
33
+ # @return [Array<String>] The invalid types as strings.
34
+ def invalid_types(option_types, allowed_extensions = nil)
35
+ extensions = []
36
+ content_types = []
37
+ option_types.each do |type|
38
+ # Since the original content types may have endings such as image/.*?, we
39
+ # want to replace the regexp pattern just with a star with the fallback
40
+ # option.
41
+ content_type = (type.try(:source) || type.to_s).gsub(".*?", "*")
42
+ if (exts = content_type_extensions(content_type, allowed_extensions))
43
+ extensions += exts
44
+ else
45
+ content_types << content_type
46
+ end
47
+ end
48
+
49
+ extensions.sort + content_types.sort
50
+ end
51
+
52
+ # Resolves the content type extensions through MiniMime or looks up all the
53
+ # possible extensions for wildcard content types such as `image/*`. For the
54
+ # wildcard extension types only those listed in the `allowed_extensions` will
55
+ # be returned.
56
+ #
57
+ # @param content_type [String] The content type, such as application/pdf or
58
+ # image/*.
59
+ # @param allowed_extensions [Array, nil] Array of allowed extensions
60
+ # or nil if all extensions are "allowed" (in case we want to show the
61
+ # denylist error or hard code the types).
62
+ # @return [Array<String>, nil] An array of the allowed extensions or nil when
63
+ # no extensions could be found.
64
+ def content_type_extensions(content_type, allowed_extensions = nil)
65
+ extensions =
66
+ if content_type.ends_with?("/*")
67
+ main_type = content_type.split("/")[0]
68
+ extensions_matching(%r{#{main_type}/.*})
69
+ else
70
+ extensions_matching(content_type)
71
+ end
72
+ return if extensions.count.zero?
73
+ return extensions unless allowed_extensions
74
+
75
+ extensions & allowed_extensions
76
+ end
77
+
78
+ # Looks up the extensions matching the content type lookup based on the
79
+ # MiniMime content types.
80
+ #
81
+ # @param content_type_lookup [String, Regexp] The lookup to be matched
82
+ # against.
83
+ # @return [Array<String>] The array of extensions.
84
+ def extensions_matching(content_type_lookup)
85
+ extensions = []
86
+ File.open(MiniMime::Configuration.ext_db_path).each do |line|
87
+ info = line.split(/\s+/)
88
+ next unless info[1].match?(content_type_lookup)
89
+
90
+ extensions << info[0]
91
+ end
92
+
93
+ extensions
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ # This is just to make Zeitwerk happy. The actual validator that gets priority
100
+ # is the class above as all validators in the `ActiveModel::Validations`
101
+ # namespace will be used primarily and only if a matching validator is not found
102
+ # in that namespace, the ones at the root level are used.
103
+ class FileContentTypeValidator < ActiveModel::Validations::FileContentTypeValidator; end
@@ -62,9 +62,20 @@ class PassthruValidator < ActiveModel::EachValidator
62
62
  elsif dummy.respond_to?(:organization=) && record.respond_to?(:organization)
63
63
  dummy.organization = record.organization
64
64
  end
65
+ validation_record_context(dummy, record)
65
66
  dummy
66
67
  end
67
68
 
69
+ def validation_record_context(dummy, record)
70
+ if dummy.is_a?(Decidim::Form)
71
+ dummy.with_context(
72
+ current_organization: record.try(:current_organization) || record.try(:ganization),
73
+ current_participatory_space: record.try(:current_participatory_space) || record.try(:participatory_space),
74
+ current_component: record.try(:current_component) || record.try(:component)
75
+ )
76
+ end
77
+ end
78
+
68
79
  def target_validators(attribute)
69
80
  target_class.validators_on(target_attribute(attribute))
70
81
  end
@@ -41,4 +41,26 @@ class UploaderContentTypeValidator < ActiveModel::Validations::FileContentTypeVa
41
41
  # rubocop: enable Metrics/CyclomaticComplexity
42
42
 
43
43
  def check_validity!; end
44
+
45
+ private
46
+
47
+ # This fixes the "weird" error messages such as "(?-mix:image\\/.*)" because
48
+ # this is the notation Regexp#to_s will return. Instead, we want to show the
49
+ # inner contents of the regular expressions.
50
+ #
51
+ # @see ActiveModel::Validations::FileContentTypeValidator#mark_invalid
52
+ def mark_invalid(record, attribute, error, option_types)
53
+ allowed_extensions = nil
54
+ if error == :allowed_file_content_types
55
+ uploader = record.attached_uploader(attribute) if record.respond_to?(:attached_uploader)
56
+ allowed_extensions =
57
+ # Note that this may be a private method in some uploaders.
58
+ if uploader && uploader.respond_to?(:extension_allowlist, true)
59
+ uploader.send(:extension_allowlist)
60
+ else
61
+ Decidim.organization_settings(record).upload_allowed_file_extensions
62
+ end
63
+ end
64
+ mark_invalid_original(record, attribute, error, invalid_types(option_types, allowed_extensions))
65
+ end
44
66
  end
@@ -9,7 +9,7 @@
9
9
  <%= image_tag present(current_user).avatar.default_multiuser_url, alt: t("decidim.author.avatar_multiuser") %>
10
10
  <% end %>
11
11
  </div>
12
- <span class="text-medium mt-xs"><%= Date::ABBR_DAYNAMES[conversation.created_at.wday] %> <%= conversation.created_at.wday %></span>
12
+ <span class="text-medium mt-xs"><%= Date::ABBR_DAYNAMES[conversation.created_at.wday] %> <%= l conversation.created_at, format: :decidim_short %></span>
13
13
  </div>
14
14
  </li>
15
15
  <li class="card-data__item card--list__item card-data__item--expand absolutes">
@@ -1,16 +1,16 @@
1
1
  <%= decidim_sanitize_newsletter cell.to_s %>
2
2
 
3
3
  <% content_for :note do %>
4
- <%== t ".note", organization_name: h(@organization.name), link: decidim.notifications_settings_url(host: @organization.host) %>
4
+ <%== t ".note", organization_name: h(@organization.name), link: @newsletter.notifications_settings_url %>
5
5
  <% end %>
6
6
 
7
7
  <% content_for :unsubscribe do %>
8
- <%== t ".unsubscribe", link: decidim.unsubscribe_newsletters_url(host: @organization.host, u: @encrypted_token ) %>
8
+ <%== t ".unsubscribe", link: @newsletter.unsubscribe_newsletters_url(u: @encrypted_token) %>
9
9
  <% end %>
10
10
 
11
11
  <% content_for :see_on_website do %>
12
12
  <center style="display: none">
13
13
  <%== CGI.unescapeHTML truncate(cell.body, max_length: 50) %>
14
14
  </center>
15
- <%== t ".see_on_website", link: decidim.newsletter_url(host: @organization.host, id: @newsletter) %>
15
+ <%== t ".see_on_website", link: @newsletter.url %>
16
16
  <% end %>
@@ -5,7 +5,7 @@
5
5
  newsletter: newsletter,
6
6
  recipient_user: @user
7
7
  ) %>
8
- <%= decidim_sanitize_editor @cell.to_s %>
8
+ <%= decidim_sanitize_newsletter @cell.to_s %>
9
9
 
10
10
  <% content_for :note do %>
11
11
  <%== t "note", scope: "decidim.newsletter_mailer.newsletter", organization_name: h(@organization.name), link: decidim.notifications_settings_url(host: @organization.host) %>
@@ -4,7 +4,7 @@
4
4
 
5
5
  <% if @event_instance.resource_path.present? && @event_instance.resource_title.present? %>
6
6
  <p class="email-button email-button__cta">
7
- <%= link_to @event_instance.resource_title, @event_instance.resource_url %>
7
+ <%= link_to decidim_sanitize(@event_instance.resource_title, strip_tags: true), @event_instance.resource_url %>
8
8
  </p>
9
9
  <% end %>
10
10
 
@@ -5,7 +5,7 @@
5
5
  </p>
6
6
  <% if notification.resource_path.present? && notification.resource_title.present? %>
7
7
  <p class="email-button email-button__cta">
8
- <%= link_to notification.resource_title, notification.resource_url %>
8
+ <%= link_to decidim_sanitize(notification.resource_title, strip_tags: true), notification.resource_url %>
9
9
  </p>
10
10
  <% end %>
11
11
  </div>
@@ -1,6 +1,6 @@
1
1
  <% if organization %>
2
- <% if @custom_url_for_mail_root.present? %>
3
- <% url = @custom_url_for_mail_root %>
2
+ <% if defined?(custom_url_for_mail_root) && custom_url_for_mail_root.present? %>
3
+ <% url = custom_url_for_mail_root %>
4
4
  <% else %>
5
5
  <% url = decidim.root_url(host: organization.host) %>
6
6
  <% end %>
@@ -6,7 +6,7 @@
6
6
  <%= stylesheet_pack_tag "decidim_email" %>
7
7
  </head>
8
8
 
9
- <body>
9
+ <%= content_tag :body, class: @preview ? "preview" : nil do %>
10
10
  <!-- <style> -->
11
11
  <table class="body">
12
12
  <% if content_for?(:see_on_website) %>
@@ -42,5 +42,5 @@
42
42
  </td>
43
43
  </tr>
44
44
  </table>
45
- </body>
45
+ <% end %>
46
46
  </html>
@@ -305,6 +305,8 @@ ar:
305
305
  title: غير مخوّل
306
306
  unconfirmed:
307
307
  title: تأكيد البريد الإلكتروني
308
+ show:
309
+ close_modal: إغلاق مشروط
308
310
  block_user_mailer:
309
311
  notify:
310
312
  hello: مرحبًا،
@@ -654,18 +656,6 @@ ar:
654
656
  how: كيف يمكنك كسب ذلك
655
657
  page_description: الشارات عبارة عن اعتراف بإجراءات المشارك والتقدم في المنصة. عند البدء في الاكتشاف والمشاركة والتفاعل في المنصة ، ستربح شارات مختلفة. فيما يلي قائمة بالشارات وبعض الطرق التي يمكنك من خلالها كسبها.
656
658
  title: شارات
657
- invitations:
658
- conditions:
659
- - استخدم رابط "دعوة الأصدقاء" في صفحة المستخدم الخاصة بك لدعوة أصدقائك
660
- - تخصيص ، إذا كنت تريد ، الرسالة التي ترسلها
661
- - ستصل إلى أعلى مستوى عن طريق إرسال الدعوات وتسجيلها.
662
- description: تُمنح هذه الشارة عند قيامك بدعوة بعض الأشخاص وقضاء بعض الوقت للتسجيل في %{organization_name} وتصبح مشاركًا. شكرًا لك على تعريف شخص %{organization_name} للآخرين والمساعدة في توسيع نطاق المجتمع!
663
- description_another: قام هذا المشارك بدعوة %{score} شخصًا.
664
- description_own: لقد قمت بدعوة %{score} شخصًا.
665
- name: دعوات
666
- next_level_in: دعوة %{score} أشخاص آخرين للوصول إلى المستوى التالي!
667
- unearned_another: هذا المشارك لم يدع أي شخص حتى الآن
668
- unearned_own: لم تقم بدعوة أي شخص حتى الآن.
669
659
  description: الشارات عبارة عن اعتراف بإجراءات المشارك والتقدم في المنصة. عند البدء في الاكتشاف والمشاركة والتفاعل في المنصة ، ستربح شارات مختلفة.
670
660
  level: المستوى %{level}
671
661
  reached_top: لقد وصلت إلى المستوى الأعلى لهذه الشارة.
@@ -769,6 +759,7 @@ ar:
769
759
  links:
770
760
  warning:
771
761
  cancel: إلغاء
762
+ close_modal: إغلاق مشروط
772
763
  log:
773
764
  base_presenter:
774
765
  create: "%{user_name} تم الإنشاء %{resource_name}"
@@ -844,7 +835,6 @@ ar:
844
835
  newsletter_mailer:
845
836
  newsletter:
846
837
  note: لقد تلقيت هذه الرسالة الإلكترونية لأنك مشترك في الرسائل الإخبارية على %{organization_name}. يمكنك تغيير الإعدادات الخاصة بك على هاتفك <a href="%{link}">الصفحة إخطارات</a>.
847
- see_on_website: لا يمكن عرض هذا البريد الإلكتروني بشكل صحيح؟ اطلع عليه على <a href="%{link}" target="_blank">الموقع</a>.
848
838
  unsubscribe: لإلغاء الاشتراك في تلقي هذا النوع من البريد الإلكتروني ، <a href="%{link}" target="_blank" class="unsubscribe">إلغاء الاشتراك</a>.
849
839
  newsletter_templates:
850
840
  image_text_cta_settings_form:
@@ -988,7 +978,9 @@ ar:
988
978
  report:
989
979
  content_original_language: لغة المحتوى الأصلية
990
980
  hello: مرحبا %{name}،
981
+ reason: السبب
991
982
  report_html: <p>تم الإبلاغ عن المحتوى <a href="%{url}">التالي</a> .</p>
983
+ see_report: معاينة التقرير
992
984
  subject: تم الإبلاغ عن مورد
993
985
  reports:
994
986
  create:
@@ -1078,9 +1070,6 @@ ar:
1078
1070
  pages_count: صفحات
1079
1071
  participants_count: المشاركون
1080
1072
  users_count: المشاركون
1081
- user_activity:
1082
- index:
1083
- no_activities_warning: هذا المشارك لم يقم بأي نشاط حتى الآن.
1084
1073
  user_conversations:
1085
1074
  index:
1086
1075
  add_users_placeholder: البحث…
@@ -1268,7 +1257,6 @@ ar:
1268
1257
  not_locked: لم يكن مغلقا
1269
1258
  too_many_marks: يستخدم الكثير من علامات الترقيم المتتالية (مثل! و؟)
1270
1259
  too_much_caps: يستخدم عددًا كبيرًا جدًا من الأحرف الكبيرة (أكثر من 25٪ من النص)
1271
- too_short: قصير جدًا (أقل من 15 حرفًا)
1272
1260
  forms:
1273
1261
  required: مطلوب
1274
1262
  invisible_captcha: