decidim-core 0.26.5 → 0.26.8

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/announcement_cell.rb +1 -1
  3. data/app/cells/decidim/collapsible_list/show.erb +1 -1
  4. data/app/cells/decidim/content_blocks/cta_cell.rb +1 -1
  5. data/app/cells/decidim/content_blocks/hero/show.erb +1 -1
  6. data/app/cells/decidim/content_blocks/highlighted_content_banner/show.erb +1 -1
  7. data/app/cells/decidim/content_blocks/sub_hero_cell.rb +1 -1
  8. data/app/controllers/decidim/authorization_modals_controller.rb +1 -1
  9. data/app/controllers/decidim/links_controller.rb +8 -11
  10. data/app/forms/url_validator.rb +1 -1
  11. data/app/helpers/decidim/cells_helper.rb +1 -0
  12. data/app/helpers/decidim/external_domain_helper.rb +14 -3
  13. data/app/helpers/decidim/layout_helper.rb +15 -4
  14. data/app/helpers/decidim/layout_helper.rb.orig +225 -0
  15. data/app/helpers/decidim/sanitize_helper.rb +15 -5
  16. data/app/models/decidim/organization.rb +6 -0
  17. data/app/models/decidim/scope_type.rb +28 -0
  18. data/app/packs/src/decidim/editor/clipboard_override.js +6 -2
  19. data/app/packs/src/decidim/editor.js +63 -33
  20. data/app/packs/src/decidim/map/controller/drag_marker.js +0 -2
  21. data/app/packs/src/decidim/map/controller/markers.js +0 -1
  22. data/app/packs/src/decidim/map/controller/static.js +0 -1
  23. data/app/packs/src/decidim/map/controller.js +0 -2
  24. data/app/packs/src/decidim/map/factory.js +4 -1
  25. data/app/packs/src/decidim/map/icon.js +0 -1
  26. data/app/packs/src/decidim/map/legacy.js +0 -1
  27. data/app/packs/src/decidim/map/provider/default.js +2 -0
  28. data/app/packs/src/decidim/map/provider/here.js +2 -1
  29. data/app/packs/stylesheets/decidim/_editor.scss +129 -0
  30. data/app/packs/stylesheets/decidim/extras/_quill.scss +0 -6
  31. data/app/packs/stylesheets/decidim/modules/_buttons.scss +10 -6
  32. data/app/packs/stylesheets/decidim/modules/_cards.scss +1 -1
  33. data/app/packs/stylesheets/decidim/modules/_comments.scss +24 -0
  34. data/app/packs/stylesheets/decidim/modules/_dropdown_menu.scss +9 -0
  35. data/app/packs/stylesheets/decidim/vizzs/_linechart.scss +2 -2
  36. data/app/packs/stylesheets/decidim/vizzs/_rowchart.scss +2 -2
  37. data/app/presenters/decidim/notification_presenter.rb +1 -1
  38. data/app/presenters/decidim/user_group_presenter.rb +1 -1
  39. data/app/presenters/decidim/user_presenter.rb +1 -1
  40. data/app/queries/decidim/metrics/users_metric_manage.rb +6 -6
  41. data/app/scrubbers/decidim/admin_input_scrubber.rb +27 -0
  42. data/app/scrubbers/decidim/user_input_scrubber.rb +32 -5
  43. data/app/services/decidim/traceability.rb +1 -0
  44. data/app/views/decidim/devise/registrations/new.html.erb.orig +231 -0
  45. data/app/views/decidim/links/_invalid_url_modal.html.erb +17 -0
  46. data/app/views/decidim/links/_modal.html.erb +1 -1
  47. data/app/views/decidim/links/invalid_url.js.erb +24 -0
  48. data/app/views/decidim/links/new.html.erb +1 -1
  49. data/app/views/decidim/messaging/conversations/_conversation.html.erb +1 -5
  50. data/app/views/decidim/pages/_standalone.html.erb +1 -1
  51. data/app/views/decidim/pages/_tabbed.html.erb +1 -1
  52. data/config/environment.rb +0 -0
  53. data/config/locales/ar.yml +423 -8
  54. data/config/locales/bg.yml +1 -4
  55. data/config/locales/ca.yml +26 -23
  56. data/config/locales/cs.yml +40 -32
  57. data/config/locales/da.yml +3 -0
  58. data/config/locales/de.yml +39 -23
  59. data/config/locales/el.yml +100 -2
  60. data/config/locales/en.yml +16 -13
  61. data/config/locales/eo.yml +5 -1
  62. data/config/locales/es-MX.yml +21 -18
  63. data/config/locales/es-PY.yml +23 -20
  64. data/config/locales/es.yml +25 -22
  65. data/config/locales/et.yml +3 -0
  66. data/config/locales/eu.yml +91 -67
  67. data/config/locales/fa-IR.yml +1 -0
  68. data/config/locales/fi-plain.yml +16 -13
  69. data/config/locales/fi.yml +18 -15
  70. data/config/locales/fr-CA.yml +28 -22
  71. data/config/locales/fr.yml +27 -21
  72. data/config/locales/ga-IE.yml +1 -0
  73. data/config/locales/gl.yml +4 -23
  74. data/config/locales/gn-PY.yml +3 -0
  75. data/config/locales/hr.yml +3 -0
  76. data/config/locales/hu.yml +66 -21
  77. data/config/locales/id-ID.yml +6 -4
  78. data/config/locales/is-IS.yml +5 -2
  79. data/config/locales/it.yml +5 -15
  80. data/config/locales/ja.yml +16 -15
  81. data/config/locales/ka-GE.yml +3 -0
  82. data/config/locales/kaa.yml +5 -0
  83. data/config/locales/lb.yml +8 -12
  84. data/config/locales/lt.yml +1 -34
  85. data/config/locales/lv.yml +0 -3
  86. data/config/locales/nl.yml +5 -24
  87. data/config/locales/no.yml +5 -28
  88. data/config/locales/oc-FR.yml +2 -0
  89. data/config/locales/pl.yml +0 -33
  90. data/config/locales/pt-BR.yml +3 -7
  91. data/config/locales/pt.yml +1 -5
  92. data/config/locales/ro-RO.yml +5 -8
  93. data/config/locales/ru.yml +3 -3
  94. data/config/locales/sk.yml +18 -10
  95. data/config/locales/sl.yml +1 -0
  96. data/config/locales/sr-CS.yml +10 -0
  97. data/config/locales/sv.yml +11 -34
  98. data/config/locales/tr-TR.yml +3 -7
  99. data/config/locales/uk.yml +3 -3
  100. data/config/locales/zh-CN.yml +0 -4
  101. data/config/locales/zh-TW.yml +1726 -0
  102. data/db/migrate/20181030090144_destroy_deleted_users_follows.rb +1 -1
  103. data/db/migrate/20181204110723_remove_following_users_count_from_users.rb +11 -2
  104. data/db/migrate/20181214101250_add_notification_types_to_users.rb +6 -1
  105. data/db/migrate/20190412131728_fix_user_names.rb +13 -6
  106. data/db/migrate/20200211173227_add_direct_message_types_to_users.rb +6 -1
  107. data/db/migrate/20210302150803_invalidate_all_sessions_for_deleted_users.rb +10 -3
  108. data/db/migrate/20210310120640_add_followable_counter_cache_to_users.rb +13 -3
  109. data/lib/decidim/core/test/shared_examples/comments_examples.rb +36 -0
  110. data/lib/decidim/core/test/shared_examples/editor_shared_examples.rb +10 -0
  111. data/lib/decidim/core/test/shared_examples/map_examples.rb +4 -1
  112. data/lib/decidim/core/test/shared_examples/rich_text_editor_examples.rb +7 -3
  113. data/lib/decidim/core/test.rb +1 -0
  114. data/lib/decidim/core/version.rb +1 -1
  115. data/lib/decidim/core.rb +43 -0
  116. data/lib/decidim/dependency_resolver.rb +272 -0
  117. data/lib/decidim/form_builder.rb +6 -14
  118. data/lib/decidim/publicable.rb +4 -0
  119. data/lib/tasks/upgrade/decidim_user_moderation.rake +14 -0
  120. metadata +18 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8937550934aed6ed69fa84837edbbc2902c5fd6779654a5180a63abbfe25d700
4
- data.tar.gz: ba86b8da585b54db8845cf1d5e476086db70d03920f99ddf3a32cbe023521992
3
+ metadata.gz: 7ddc4e551d59fc9b95f5121333870c4e91a77eb2f53d03f659d63e16bea7ca7f
4
+ data.tar.gz: dd766575d88dfe729ce7f8ea151967642b40e875c0596443962d0a6d8d5cd101
5
5
  SHA512:
6
- metadata.gz: de40caba191fb9ff0b88bbf9055fbb4592fcc12352c6e35d21d812e87399b1772254d669438ade3ada960ca994247ab87b3eb4743e80df9121cb3bb07c6a9f18
7
- data.tar.gz: c115b3d2e531d0b0d8e43f7b41a863dc51eb2c076bc8c4f2c8731e642fb191115b2fdf6aebe6a3cf28c48fb746c3e9cec85ea9a5be630d382ee695cff238278f
6
+ metadata.gz: dcbcc2625fe80e646c9f5e05c0e187485004542bcb73c33978bd8f2b1f78f31c2e0a4f4dba6904ba0b660e5b4408977a21d13e0c02fe2d46cba8bdb13c399321
7
+ data.tar.gz: a1abc1c25bfe4c99875d00822beeefeadb20bd214672772b1491d07e515ce3990b6b24c96d5400e4bc71d1accb8c8484d7e57c6ed006f63fd19e23f8378234ce
@@ -62,7 +62,7 @@ module Decidim
62
62
  end
63
63
 
64
64
  def clean(value)
65
- decidim_sanitize(translated_attribute(value))
65
+ decidim_sanitize_admin(translated_attribute(value))
66
66
  end
67
67
  end
68
68
  end
@@ -16,7 +16,7 @@
16
16
  </span>
17
17
  </div>
18
18
  <% else %>
19
- <div class="collapsible-list <%= list_class %>">
19
+ <div class="<%= list_class %>">
20
20
  <% list.each do |element| %>
21
21
  <% if cell_name %>
22
22
  <%= cell cell_name, element, cell_options %>
@@ -16,7 +16,7 @@ module Decidim
16
16
  end
17
17
 
18
18
  def translated_description
19
- @translated_description ||= decidim_sanitize_editor(translated_attribute(model.settings.description))
19
+ @translated_description ||= decidim_sanitize_editor_admin(translated_attribute(model.settings.description))
20
20
  end
21
21
 
22
22
  def button_url
@@ -6,7 +6,7 @@
6
6
  <% if translated_welcome_text.blank? %>
7
7
  <%= t("decidim.pages.home.hero.welcome", organization: current_organization.name) %>
8
8
  <% else %>
9
- <%= decidim_sanitize translated_welcome_text %>
9
+ <%= decidim_sanitize_admin translated_welcome_text %>
10
10
  <% end %>
11
11
  </h1>
12
12
  </div>
@@ -7,7 +7,7 @@
7
7
  <%= translated_attribute current_organization.highlighted_content_banner_title %>
8
8
  </h1>
9
9
  <span class="text-highlight">
10
- <%= decidim_sanitize_editor translated_attribute current_organization.highlighted_content_banner_short_description %>
10
+ <%= decidim_sanitize_editor_admin translated_attribute current_organization.highlighted_content_banner_short_description %>
11
11
  </span>
12
12
  </div>
13
13
  <div class="columns large-2">
@@ -15,7 +15,7 @@ module Decidim
15
15
  private
16
16
 
17
17
  def organization_description
18
- desc = decidim_sanitize(translated_attribute(current_organization.description))
18
+ desc = decidim_sanitize_admin(translated_attribute(current_organization.description))
19
19
 
20
20
  # Strip the surrounding paragraph tag because it is not allowed within
21
21
  # a <hN> element.
@@ -17,7 +17,7 @@ module Decidim
17
17
  end
18
18
 
19
19
  def current_component
20
- @current_component ||= Decidim::Component.find(params[:component_id])
20
+ @current_component ||= Decidim::Component.where(participatory_space: current_organization.participatory_spaces).find(params[:component_id])
21
21
  end
22
22
 
23
23
  def authorization_action
@@ -21,24 +21,21 @@ module Decidim
21
21
 
22
22
  def invalid_url
23
23
  flash[:alert] = I18n.t("decidim.links.invalid_url")
24
- redirect_to decidim.root_path
24
+ if request.xhr?
25
+ render "invalid_url"
26
+ else
27
+ redirect_to decidim.root_path
28
+ end
25
29
  end
26
30
 
27
31
  def parse_url
32
+ raise Decidim::InvalidUrlError if params[:external_url].blank?
28
33
  raise Decidim::InvalidUrlError unless external_url
29
-
30
- parts = external_url.match %r{\A(([a-z]+):)?//([^/]+)(/.*)?\z}
31
- raise Decidim::InvalidUrlError unless parts
32
-
33
- @url_parts = {
34
- protocol: parts[1],
35
- domain: parts[3],
36
- path: parts[4]
37
- }
34
+ raise Decidim::InvalidUrlError unless %w(http https).include?(external_url.scheme)
38
35
  end
39
36
 
40
37
  def external_url
41
- @external_url ||= URI.parse(params[:external_url]).to_s
38
+ @external_url ||= URI.parse(params[:external_url])
42
39
  end
43
40
  end
44
41
  end
@@ -6,7 +6,7 @@
6
6
  #
7
7
  class UrlValidator < ActiveModel::EachValidator
8
8
  def validate_each(record, attribute, value)
9
- record.errors[attribute] << (options[:message] || "must be a valid URL") unless url_valid?(value)
9
+ record.errors.add attribute, :url_format, **options unless url_valid?(value)
10
10
  end
11
11
 
12
12
  # a URL may be technically well-formed but may
@@ -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
@@ -74,8 +74,11 @@ module Decidim
74
74
  classes = _icon_classes(options) + ["external-icon"]
75
75
 
76
76
  if path.split(".").last == "svg"
77
+ icon_path = application_path(path)
78
+ return unless icon_path
79
+
77
80
  attributes = { class: classes.join(" ") }.merge(options)
78
- asset = File.read(application_path(path))
81
+ asset = File.read(icon_path)
79
82
  asset.gsub("<svg ", "<svg#{tag_builder.tag_options(attributes)} ").html_safe
80
83
  else
81
84
  image_pack_tag(path, class: classes.join(" "), style: "display: none")
@@ -83,9 +86,17 @@ module Decidim
83
86
  end
84
87
 
85
88
  def application_path(path)
86
- img_path = asset_pack_path(path)
87
- img_path = URI(img_path).path if Decidim.cors_enabled
88
- Rails.root.join("public/#{img_path}")
89
+ # Force the path to be returned without the protocol and host even when a
90
+ # custom asset host has been defined. The host parameter needs to be a
91
+ # non-nil because otherwise it will be set to the asset host at
92
+ # ActionView::Helpers::AssetUrlHelper#compute_asset_host.
93
+ img_path = asset_pack_path(path, host: "", protocol: :relative)
94
+ path = Rails.public_path.join(img_path.sub(%r{^/}, ""))
95
+ return unless File.exist?(path)
96
+
97
+ path
98
+ rescue ::Webpacker::Manifest::MissingEntryError
99
+ nil
89
100
  end
90
101
 
91
102
  # Allows to create role attribute according to accessibility rules
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # View helpers related to the layout.
5
+ module LayoutHelper
6
+ include Decidim::ModalHelper
7
+ include Decidim::TooltipHelper
8
+
9
+ # Public: Generates a set of meta tags that generate the different favicon
10
+ # versions for an organization.
11
+ #
12
+ # Returns a safe String with the versions.
13
+ def favicon
14
+ return if current_organization.favicon.blank?
15
+
16
+ safe_join(Decidim::OrganizationFaviconUploader::SIZES.map do |version, size|
17
+ favicon_link_tag(current_organization.attached_uploader(:favicon).variant_url(version, host: current_organization.host), sizes: "#{size}x#{size}")
18
+ end)
19
+ end
20
+
21
+ def apple_favicon
22
+ icon_image = current_organization.attached_uploader(:favicon).variant_url(:medium, host: current_organization.host)
23
+ return unless icon_image
24
+
25
+ favicon_link_tag(icon_image, rel: "apple-touch-icon", type: "image/png")
26
+ end
27
+
28
+ def legacy_favicon
29
+ variant = :favicon if current_organization.favicon.content_type != "image/vnd.microsoft.icon"
30
+ icon_image = current_organization.attached_uploader(:favicon).variant_url(variant, host: current_organization.host)
31
+ return unless icon_image
32
+
33
+ favicon_link_tag(icon_image, rel: "icon", sizes: "any", type: nil)
34
+ end
35
+
36
+ # Outputs an SVG-based icon.
37
+ #
38
+ # name - The String with the icon name.
39
+ # options - The Hash options used to customize the icon (default {}):
40
+ # :width - The Number of width in pixels (optional).
41
+ # :height - The Number of height in pixels (optional).
42
+ # :title - The title for the SVG element (optional, similar to alt for img)
43
+ # :aria_label - The String to set as aria label (optional).
44
+ # :aria_hidden - The Truthy value to enable aria_hidden (optional).
45
+ # :role - The String to set as the role (optional).
46
+ # :class - The String to add as a CSS class (optional).
47
+ #
48
+ # Returns a String.
49
+ def redesigned_icon(name, options = {})
50
+ default_html_properties = {
51
+ "width" => "1em",
52
+ "height" => "1em",
53
+ "role" => "img",
54
+ "aria-hidden" => "true"
55
+ }
56
+
57
+ html_properties = options.with_indifferent_access.transform_keys(&:dasherize).slice("width", "height", "aria-label", "role", "aria-hidden", "class", "style")
58
+ html_properties = default_html_properties.merge(html_properties)
59
+
60
+ href = Decidim.cors_enabled ? "" : asset_pack_path("media/images/remixicon.symbol.svg")
61
+
62
+ content_tag :svg, html_properties do
63
+ content_tag :use, nil, "href" => "#{href}#ri-#{name}", tabindex: -1
64
+ end
65
+ end
66
+
67
+ def legacy_icon(name, options = {})
68
+ options = options.with_indifferent_access
69
+ html_properties = {}
70
+
71
+ html_properties["width"] = options[:width]
72
+ html_properties["height"] = options[:height]
73
+ html_properties["aria-label"] = options[:aria_label] || options[:"aria-label"]
74
+ html_properties["role"] = options[:role] || "img"
75
+ html_properties["aria-hidden"] = options[:aria_hidden] || options[:"aria-hidden"]
76
+
77
+ html_properties["class"] = (["icon--#{name}"] + _icon_classes(options)).join(" ")
78
+
79
+ title = options["title"] || html_properties["aria-label"]
80
+ if title.blank? && html_properties["role"] == "img"
81
+ # This will make the accessibility audit tools happy as with the "img"
82
+ # role, the alternative text (aria-label) and title are required for the
83
+ # element. This will also force the SVG to be hidden because otherwise
84
+ # the screen reader would announce the icon name which can be in
85
+ # different language (English) than the page language which is not
86
+ # allowed.
87
+ title = name
88
+ html_properties["aria-label"] = title
89
+ html_properties["aria-hidden"] = true
90
+ end
91
+
92
+ href = Decidim.cors_enabled ? "" : asset_pack_path("media/images/icons.svg")
93
+
94
+ content_tag :svg, html_properties do
95
+ inner = content_tag :title, title
96
+ inner += content_tag :use, nil, "href" => "#{href}#icon-#{name}"
97
+
98
+ inner
99
+ end
100
+ end
101
+
102
+ def icon(*args)
103
+ redesign_enabled? ? redesigned_icon(*args) : legacy_icon(*args)
104
+ end
105
+
106
+ # Outputs a SVG icon from an external file. It apparently renders an image
107
+ # tag, but then a JS script kicks in and replaces it with an inlined SVG
108
+ # version.
109
+ #
110
+ # path - The asset's path
111
+ #
112
+ # Returns an <img /> tag with the SVG icon.
113
+ def external_icon(path, options = {})
114
+ classes = _icon_classes(options) + ["external-icon"]
115
+
116
+ if path.split(".").last == "svg"
117
+ icon_path = application_path(path)
118
+ return unless icon_path
119
+
120
+ attributes = { class: classes.join(" ") }.merge(options)
121
+ asset = File.read(icon_path)
122
+ asset.gsub("<svg ", "<svg#{tag_builder.tag_options(attributes)} ").html_safe
123
+ else
124
+ image_pack_tag(path, class: classes.join(" "), style: "display: none")
125
+ end
126
+ end
127
+
128
+ def application_path(path)
129
+ # Force the path to be returned without the protocol and host even when a
130
+ # custom asset host has been defined. The host parameter needs to be a
131
+ # non-nil because otherwise it will be set to the asset host at
132
+ # ActionView::Helpers::AssetUrlHelper#compute_asset_host.
133
+ img_path = asset_pack_path(path, host: "", protocol: :relative)
134
+ path = Rails.public_path.join(img_path.sub(%r{^/}, ""))
135
+ return unless File.exist?(path)
136
+
137
+ path
138
+ rescue ::Webpacker::Manifest::MissingEntryError
139
+ nil
140
+ end
141
+
142
+ # Allows to create role attribute according to accessibility rules
143
+ #
144
+ # Returns role attribute string if role option is specified
145
+ def role(options = {})
146
+ "role=\"#{options[:role]}\" " if options[:role]
147
+ end
148
+
149
+ def _icon_classes(options = {})
150
+ classes = options[:remove_icon_class] ? [] : ["icon"]
151
+ classes += [options[:class]]
152
+ classes.compact
153
+ end
154
+
155
+ def extended_navigation_bar(items, max_items: 5)
156
+ return unless items.any?
157
+
158
+ extra_items = items.slice((max_items + 1)..-1) || []
159
+ active_item = items.find { |item| item[:active] }
160
+
161
+ controller.view_context.render partial: "decidim/shared/extended_navigation_bar", locals: {
162
+ items:,
163
+ extra_items:,
164
+ active_item:,
165
+ max_items:
166
+ }
167
+ end
168
+
169
+ # Renders a view with the customizable CSS variables in two flavours:
170
+ # 1. as a hexadecimal valid CSS color (ie: #ff0000)
171
+ # 2. as a disassembled RGB components (ie: 255 0 0)
172
+ #
173
+ # Example:
174
+ #
175
+ # --primary: #ff0000;
176
+ # --primary-rgb: 255,0,0
177
+ #
178
+ # Hexadecimal variables can be used as a normal CSS color:
179
+ #
180
+ # color: var(--primary)
181
+ #
182
+ # While the disassembled variant can be used where you need to manipulate
183
+ # the color somehow (ie: adding a background transparency):
184
+ #
185
+ # background-color: rgba(var(--primary-rgb), 0.5)
186
+ def organization_colors
187
+ css = current_organization.colors.each.map { |k, v| "--#{k}: #{v};--#{k}-rgb: #{v[1..2].hex} #{v[3..4].hex} #{v[5..6].hex};" }.join
188
+ render partial: "layouts/decidim/organization_colors", locals: { css: }
189
+ end
190
+
191
+ <<<<<<< HEAD
192
+ def current_user_unread_data
193
+ return {} if current_user.blank?
194
+
195
+ {}.tap do |d|
196
+ d.merge!(unread_notifications: true) if current_user.notifications.any?
197
+ d.merge!(unread_conversations: true) if current_user.unread_conversations.any?
198
+ d.merge!(unread_items: d.present?)
199
+ end
200
+ end
201
+
202
+ ||||||| parent of 53b6893e5c (Use local emojibase data instead of CDN)
203
+ =======
204
+ # Public: Gets the name of the webpacker entrypoint that will be used
205
+ # for the locale of the Emojibase NPM package, used with @picmo/popup-picker
206
+ #
207
+ # Returns a string with the entrypoint name
208
+ def emojibase_entrypoint_locale
209
+ entrypoint = Decidim::Webpacker.configuration.entrypoints.keys.select do |entry|
210
+ entry == "decidim_emojibase_#{I18n.locale}"
211
+ end
212
+
213
+ return "decidim_emojibase_en" if entrypoint.empty?
214
+
215
+ entrypoint.first
216
+ end
217
+
218
+ >>>>>>> 53b6893e5c (Use local emojibase data instead of CDN)
219
+ private
220
+
221
+ def tag_builder
222
+ @tag_builder ||= ActionView::Helpers::TagHelper::TagBuilder.new(self)
223
+ end
224
+ end
225
+ end
@@ -16,13 +16,18 @@ module Decidim
16
16
  #
17
17
  # Returns an HTML-safe String.
18
18
  def decidim_sanitize(html, options = {})
19
+ scrubber = options[:scrubber] || Decidim::UserInputScrubber.new
19
20
  if options[:strip_tags]
20
- strip_tags sanitize(html, scrubber: Decidim::UserInputScrubber.new)
21
+ strip_tags sanitize(html, scrubber: scrubber)
21
22
  else
22
- sanitize(html, scrubber: Decidim::UserInputScrubber.new)
23
+ sanitize(html, scrubber: scrubber)
23
24
  end
24
25
  end
25
26
 
27
+ def decidim_sanitize_admin(html, options = {})
28
+ decidim_sanitize(html, { scrubber: Decidim::AdminInputScrubber.new }.merge(options))
29
+ end
30
+
26
31
  def decidim_sanitize_newsletter(html, options = {})
27
32
  if options[:strip_tags]
28
33
  strip_tags sanitize(html, scrubber: Decidim::NewsletterScrubber.new)
@@ -32,7 +37,11 @@ module Decidim
32
37
  end
33
38
 
34
39
  def decidim_sanitize_editor(html, options = {})
35
- 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
+ end
42
+
43
+ def decidim_sanitize_editor_admin(html, options = {})
44
+ decidim_sanitize_editor(html, { scrubber: Decidim::AdminInputScrubber.new }.merge(options))
36
45
  end
37
46
 
38
47
  def decidim_html_escape(text)
@@ -102,9 +111,10 @@ module Decidim
102
111
  #
103
112
  # @return ActiveSupport::SafeBuffer
104
113
  def render_sanitized_content(resource, method)
105
- content = present(resource).send(method, links: true, strip_tags: !safe_content?)
114
+ content = present(resource).send(method, links: true, strip_tags: !try(:safe_content?))
106
115
 
107
- return decidim_sanitize(content, {}) unless safe_content?
116
+ return decidim_sanitize(content, {}) unless try(:safe_content?)
117
+ return decidim_sanitize_editor_admin(content, {}) if try(:safe_content_admin?)
108
118
 
109
119
  decidim_sanitize_editor(content)
110
120
  end
@@ -87,6 +87,12 @@ module Decidim
87
87
  @top_scopes ||= scopes.top_level
88
88
  end
89
89
 
90
+ def participatory_spaces
91
+ @participatory_spaces ||= Decidim.participatory_space_manifests.flat_map do |manifest|
92
+ manifest.participatory_spaces.call(self)
93
+ end
94
+ end
95
+
90
96
  def public_participatory_spaces
91
97
  @public_participatory_spaces ||= Decidim.participatory_space_manifests.flat_map do |manifest|
92
98
  manifest.participatory_spaces.call(self).public_spaces
@@ -15,5 +15,33 @@ module Decidim
15
15
  has_many :scopes, class_name: "Decidim::Scope", inverse_of: :scope_type, dependent: :nullify
16
16
 
17
17
  validates :name, presence: true
18
+
19
+ before_destroy :detach_dynamic_associations
20
+
21
+ def self.log_presenter_class_for(_log)
22
+ Decidim::AdminLog::ScopeTypePresenter
23
+ end
24
+
25
+ private
26
+
27
+ # This method detaches all records that may have association with the scope
28
+ # type. This cannot be done directly using the `dependent` option in the
29
+ # `has_many` relation in order to avoid tight coupling between the modules.
30
+ #
31
+ # This logic does not have to be applied to any classes that have been
32
+ # defined as `has_many` associations within this model already as they are
33
+ # already handled by the `dependent` option.
34
+ def detach_dynamic_associations
35
+ ActiveRecord::Base.descendants.each do |cls|
36
+ next if cls.abstract_class? || !cls.name&.match?(/^Decidim::/)
37
+ next if [self.class, Decidim::Scope].include?(cls)
38
+
39
+ cls.reflect_on_all_associations(:belongs_to).each do |ref|
40
+ next unless ref.options[:class_name] == self.class.name
41
+
42
+ cls.where(ref.options[:foreign_key] => id).update_all(ref.options[:foreign_key] => nil) # rubocop:disable Rails/SkipsModelValidations
43
+ end
44
+ end
45
+ end
18
46
  end
19
47
  end
@@ -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
  }