decidim-core 0.27.3 → 0.27.4

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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activities_cell.rb +1 -7
  3. data/app/cells/decidim/collapsible_list/show.erb +1 -1
  4. data/app/cells/decidim/content_blocks/last_activity_cell.rb +1 -4
  5. data/app/cells/decidim/tags_cell.rb +13 -2
  6. data/app/commands/decidim/create_omniauth_registration.rb +2 -2
  7. data/app/controllers/decidim/authorization_modals_controller.rb +1 -1
  8. data/app/controllers/decidim/last_activities_controller.rb +1 -7
  9. data/app/controllers/decidim/short_links_controller.rb +1 -1
  10. data/app/forms/decidim/notifications_settings_form.rb +1 -1
  11. data/app/forms/url_validator.rb +1 -1
  12. data/app/helpers/decidim/layout_helper.rb +4 -1
  13. data/app/helpers/decidim/layout_helper.rb.orig +225 -0
  14. data/app/models/decidim/organization.rb +6 -0
  15. data/app/models/decidim/user.rb +4 -2
  16. data/app/packs/src/decidim/input_character_counter.js +1 -1
  17. data/app/packs/src/decidim/map/controller/drag_marker.js +0 -2
  18. data/app/packs/src/decidim/map/controller/markers.js +0 -1
  19. data/app/packs/src/decidim/map/controller/static.js +0 -1
  20. data/app/packs/src/decidim/map/controller.js +0 -2
  21. data/app/packs/src/decidim/map/factory.js +4 -1
  22. data/app/packs/src/decidim/map/icon.js +0 -1
  23. data/app/packs/src/decidim/map/legacy.js +0 -1
  24. data/app/packs/src/decidim/map/provider/default.js +2 -0
  25. data/app/packs/src/decidim/map/provider/here.js +2 -1
  26. data/app/packs/stylesheets/decidim/modules/_dropdown_menu.scss +9 -0
  27. data/app/queries/decidim/last_activity.rb +96 -0
  28. data/app/queries/decidim/metrics/users_metric_manage.rb +6 -6
  29. data/app/queries/decidim/public_activities.rb +5 -57
  30. data/app/services/decidim/email_notification_generator.rb +7 -1
  31. data/app/services/decidim/send_push_notification.rb +1 -1
  32. data/app/uploaders/decidim/application_uploader.rb +2 -0
  33. data/app/views/decidim/devise/registrations/new.html.erb.orig +231 -0
  34. data/config/environment.rb +0 -0
  35. data/config/locales/ar.yml +3 -3
  36. data/config/locales/bg.yml +4 -0
  37. data/config/locales/ca.yml +7 -6
  38. data/config/locales/cs.yml +23 -7
  39. data/config/locales/de.yml +74 -11
  40. data/config/locales/el.yml +147 -0
  41. data/config/locales/en.yml +1 -0
  42. data/config/locales/eo.yml +3 -0
  43. data/config/locales/es-MX.yml +2 -1
  44. data/config/locales/es-PY.yml +4 -3
  45. data/config/locales/es.yml +7 -6
  46. data/config/locales/eu.yml +5 -5
  47. data/config/locales/fi-plain.yml +22 -0
  48. data/config/locales/fi.yml +1 -0
  49. data/config/locales/fr-CA.yml +6 -5
  50. data/config/locales/fr.yml +7 -6
  51. data/config/locales/ga-IE.yml +4 -0
  52. data/config/locales/gl.yml +8 -1
  53. data/config/locales/hu.yml +23 -2
  54. data/config/locales/id-ID.yml +8 -0
  55. data/config/locales/is-IS.yml +3 -1
  56. data/config/locales/it.yml +10 -6
  57. data/config/locales/ja.yml +16 -1
  58. data/config/locales/kaa.yml +10 -0
  59. data/config/locales/lb.yml +12 -8
  60. data/config/locales/lt.yml +1 -1
  61. data/config/locales/lv.yml +4 -0
  62. data/config/locales/nl.yml +6 -6
  63. data/config/locales/no.yml +8 -4
  64. data/config/locales/pl.yml +4 -0
  65. data/config/locales/pt-BR.yml +5 -1
  66. data/config/locales/pt.yml +5 -1
  67. data/config/locales/ro-RO.yml +4 -0
  68. data/config/locales/ru.yml +2 -0
  69. data/config/locales/sk.yml +21 -7
  70. data/config/locales/sr-CS.yml +8 -0
  71. data/config/locales/sv.yml +11 -11
  72. data/config/locales/tr-TR.yml +4 -0
  73. data/config/locales/uk.yml +2 -0
  74. data/config/locales/zh-CN.yml +4 -0
  75. data/config/locales/zh-TW.yml +17 -3
  76. data/db/migrate/20181030090144_destroy_deleted_users_follows.rb +1 -1
  77. data/db/migrate/20181204110723_remove_following_users_count_from_users.rb +11 -2
  78. data/db/migrate/20181214101250_add_notification_types_to_users.rb +6 -1
  79. data/db/migrate/20190412131728_fix_user_names.rb +9 -2
  80. data/db/migrate/20200211173227_add_direct_message_types_to_users.rb +6 -1
  81. data/db/migrate/20210302150803_invalidate_all_sessions_for_deleted_users.rb +10 -3
  82. data/db/migrate/20210310120640_add_followable_counter_cache_to_users.rb +13 -3
  83. data/db/seeds.rb +4 -3
  84. data/lib/decidim/core/test/shared_examples/map_examples.rb +4 -1
  85. data/lib/decidim/core/version.rb +1 -1
  86. data/lib/decidim/core.rb +17 -0
  87. data/lib/decidim/form_builder.rb +6 -13
  88. data/lib/tasks/upgrade/decidim_user_moderation.rake +14 -0
  89. metadata +17 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6577740d87b562e24f541c4723480ec774239f69a389d4434536ab72be3b116
4
- data.tar.gz: 8366df277375b9fadc5c96b4a9e647a0300057d7148eafcb672f3a57d3abd55f
3
+ metadata.gz: d976829c2218ab34a1bf7c2ad99443edd61c4f37bb56963c8bb21a3e74afddef
4
+ data.tar.gz: acbaa2ce577af7e32648ce22941616e258f48bdbfb304e754344770fafaf9ef4
5
5
  SHA512:
6
- metadata.gz: f6f4e9e7bcf14ccc5943ec41d4468338d1cf62aec7d37b296d0fa37a3c1c7bc97711b06579d10b32167ce291b8d664989f90eecfcba9a94f228ff60e7e8698e0
7
- data.tar.gz: 4fcd0dacd12572c3a4b404709ec632dcc6c5cc964dfd9dc29d92a4dd03da2b10ddc3e5df57a6ed91cb3f90e0dac70c4668ba89040f8cf5788e34d1f8e857d3b8
6
+ metadata.gz: 868d324e0f83442b37750933c39985dffdba9bfd06a2f47ad00836b7c9b89164bafff5d2083d5e24d7f01eb9c47dac46f564a375bacaa17726fe5d472bf6c86b
7
+ data.tar.gz: 1af449d8a281aa2ae5db6c0f03a7be077f7a7eeaa2a300e8bc2a2d36622cd4be5aafb4245878a24e4bd2496542d0166a84287974e72cdeb294acdcd021144b00
@@ -27,13 +27,7 @@ module Decidim
27
27
  end
28
28
 
29
29
  def activities
30
- @activities ||= last_activities.select do |activity|
31
- activity.visible_for?(current_user)
32
- end
33
- end
34
-
35
- def last_activities
36
- @last_activities ||= model.map do |activity|
30
+ @activities ||= model.map do |activity|
37
31
  activity.organization_lazy
38
32
  activity.resource_lazy
39
33
  activity.participatory_space_lazy
@@ -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 %>
@@ -56,10 +56,7 @@ module Decidim
56
56
  end
57
57
 
58
58
  def activities
59
- @activities ||= ActionLog.where(
60
- organization: current_organization,
61
- visibility: %w(public-only all)
62
- ).with_new_resource_type("all").order(created_at: :desc).limit(activities_to_show * 6)
59
+ @activities ||= Decidim::LastActivity.new(current_organization, current_user: current_user).query.limit(activities_to_show * 6)
63
60
  end
64
61
 
65
62
  def activities_to_show
@@ -55,7 +55,7 @@ module Decidim
55
55
  end
56
56
 
57
57
  def category_path
58
- resource_locator(model).index(filter: { category_id: [model.category.id.to_s] })
58
+ resource_locator(model).index(filter: { filter_param(:category) => [model.category.id.to_s] })
59
59
  end
60
60
 
61
61
  def scope?
@@ -86,7 +86,18 @@ module Decidim
86
86
  end
87
87
 
88
88
  def scope_path
89
- resource_locator(model).index(filter: { scope_id: [model.scope.id] })
89
+ resource_locator(model).index(filter: { filter_param(:scope) => [model.scope.id] })
90
+ end
91
+
92
+ def filter_param(name)
93
+ candidates = ["with_any_#{name}".to_sym, "with_#{name}".to_sym]
94
+ return candidates.first unless controller.respond_to?(:default_filter_params, true)
95
+
96
+ available_params = controller.send(:default_filter_params)
97
+ candidates.each do |candidate|
98
+ return candidate if available_params.has_key?(candidate)
99
+ end
100
+ candidates.first
90
101
  end
91
102
  end
92
103
  end
@@ -46,8 +46,6 @@ module Decidim
46
46
  attr_reader :form, :verified_email
47
47
 
48
48
  def create_or_find_user
49
- generated_password = SecureRandom.hex
50
-
51
49
  @user = User.find_or_initialize_by(
52
50
  email: verified_email,
53
51
  organization: organization
@@ -59,6 +57,8 @@ module Decidim
59
57
  # to be marked confirmed.
60
58
  @user.skip_confirmation! if !@user.confirmed? && @user.email == verified_email
61
59
  else
60
+ generated_password = SecureRandom.hex
61
+
62
62
  @user.email = (verified_email || form.email)
63
63
  @user.name = form.name
64
64
  @user.nickname = form.normalized_nickname
@@ -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
@@ -32,13 +32,7 @@ module Decidim
32
32
  end
33
33
 
34
34
  def search_collection
35
- ActionLog
36
- .where(
37
- organization: current_organization,
38
- visibility: %w(public-only all)
39
- )
40
- .with_new_resource_type("all")
41
- .order(created_at: :desc)
35
+ LastActivity.new(current_organization, current_user: current_user).query
42
36
  end
43
37
 
44
38
  def default_filter_params
@@ -29,7 +29,7 @@ module Decidim
29
29
  #
30
30
  # @return [Decidim::ShortLink] The short link matching the identifier
31
31
  def link
32
- @link ||= Decidim::ShortLink.find_by(identifier: params[:id])
32
+ @link ||= Decidim::ShortLink.find_by(identifier: params[:id], organization: current_organization)
33
33
  end
34
34
  end
35
35
  end
@@ -53,7 +53,7 @@ module Decidim
53
53
  end
54
54
 
55
55
  def meet_push_notifications_requirements?
56
- Rails.application.secrets.vapid[:enabled]
56
+ Rails.application.secrets.dig(:vapid, :enabled) || false
57
57
  end
58
58
  end
59
59
  end
@@ -6,7 +6,7 @@
6
6
  #
7
7
  class UrlValidator < ActiveModel::EachValidator
8
8
  def validate_each(record, attribute, value)
9
- record.errors.add 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
@@ -105,7 +105,10 @@ module Decidim
105
105
  # non-nil because otherwise it will be set to the asset host at
106
106
  # ActionView::Helpers::AssetUrlHelper#compute_asset_host.
107
107
  img_path = asset_pack_path(path, host: "", protocol: :relative)
108
- Rails.public_path.join(img_path.sub(%r{^/}, ""))
108
+ path = Rails.public_path.join(img_path.sub(%r{^/}, ""))
109
+ return unless File.exist?(path)
110
+
111
+ path
109
112
  rescue ::Webpacker::Manifest::MissingEntryError
110
113
  nil
111
114
  end
@@ -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
@@ -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
@@ -261,9 +261,10 @@ module Decidim
261
261
  end
262
262
 
263
263
  def needs_password_update?
264
+ return false if organization.users_registration_mode == "disabled"
264
265
  return false unless admin?
265
266
  return false unless Decidim.config.admin_password_strong
266
- return true if password_updated_at.blank?
267
+ return identities.none? if password_updated_at.blank?
267
268
 
268
269
  password_updated_at < Decidim.config.admin_password_expiration_days.days.ago
269
270
  end
@@ -293,7 +294,8 @@ module Decidim
293
294
  event: "decidim.events.core.welcome_notification",
294
295
  event_class: WelcomeNotificationEvent,
295
296
  resource: self,
296
- affected_users: [self]
297
+ affected_users: [self],
298
+ extra: { force_email: true }
297
299
  )
298
300
  end
299
301
 
@@ -33,7 +33,7 @@ export default class InputCharacterCounter {
33
33
  this.$target = $(this.$input.data("remaining-characters"));
34
34
  this.minCharacters = parseInt(this.$input.attr("minlength"), 10);
35
35
  this.maxCharacters = parseInt(this.$input.attr("maxlength"), 10);
36
- this.describeByCounter = typeof this.$input.attr("aria-describedby") === "undefined";
36
+ this.describeByCounter = this.$input.attr("type") !== "hidden" && typeof this.$input.attr("aria-describedby") === "undefined";
37
37
 
38
38
  // Define the closest length for the input "gaps" defined by the threshold.
39
39
  if (this.maxCharacters > 10) {
@@ -1,6 +1,4 @@
1
- import * as L from "leaflet";
2
1
  import MapController from "src/decidim/map/controller"
3
- import "src/decidim/vendor/leaflet-tilelayer-here"
4
2
 
5
3
  export default class MapDragMarkerController extends MapController {
6
4
  start() {
@@ -1,5 +1,4 @@
1
1
  import "src/decidim/vendor/jquery-tmpl"
2
- import * as L from "leaflet";
3
2
  import MapController from "src/decidim/map/controller"
4
3
  import "leaflet.markercluster";
5
4
 
@@ -1,4 +1,3 @@
1
- import * as L from "leaflet";
2
1
  import MapController from "src/decidim/map/controller"
3
2
 
4
3
  const openLink = window.open;
@@ -1,5 +1,3 @@
1
- import * as L from "leaflet";
2
- import "src/decidim/map/icon"
3
1
  import MapControllerRegistry from "src/decidim/map/controller_registry"
4
2
 
5
3
  export default class MapController {
@@ -1,3 +1,5 @@
1
+ import "src/decidim/map/icon"
2
+
1
3
  import MapMarkersController from "src/decidim/map/controller/markers"
2
4
  import MapStaticController from "src/decidim/map/controller/static"
3
5
  import MapDragMarkerController from "src/decidim/map/controller/drag_marker"
@@ -22,7 +24,8 @@ import MapDragMarkerController from "src/decidim/map/controller/drag_marker"
22
24
  * window.Decidim.createMapController = (mapId, config) => {
23
25
  * if (config.type === "custom") {
24
26
  * // Obviously you need to implement CustomMapController for this to
25
- * // work.
27
+ * // work. You can find an example at:
28
+ * // decidim-dev/app/packs/src/decidim/dev/test/custom_map_factory.js
26
29
  * return new window.Decidim.CustomMapController(mapId, config);
27
30
  * }
28
31
  *
@@ -1,4 +1,3 @@
1
- import * as L from "leaflet";
2
1
  import { SVGIcon } from "src/decidim/map/svg-icon";
3
2
 
4
3
  L.DivIcon.SVGIcon = SVGIcon;
@@ -1,6 +1,5 @@
1
1
  /* eslint-disable require-jsdoc */
2
2
 
3
- import * as L from "leaflet";
4
3
  import "src/decidim/map/factory"
5
4
 
6
5
  /**
@@ -1,3 +1,5 @@
1
+ import "leaflet"
2
+
1
3
  /**
2
4
  * NOTE:
3
5
  * This has to load before decidim/map in order for it to apply correctly when
@@ -1,4 +1,5 @@
1
- import * as L from "leaflet"
1
+ import "leaflet"
2
+ import "src/decidim/vendor/leaflet-tilelayer-here"
2
3
 
3
4
  /**
4
5
  * NOTE:
@@ -0,0 +1,9 @@
1
+ .dropdown.menu > li {
2
+ &.is-active > a {
3
+ color: var(--secondary);
4
+ }
5
+
6
+ &.is-dropdown-submenu-parent > a::after {
7
+ border-color: var(--secondary) transparent transparent;
8
+ }
9
+ }
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This query finds the public ActionLog entries that can be shown in the
5
+ # activities views of the application within a Decidim Organization. It is
6
+ # intended to be used in the "Last activities" content block in the homepage,
7
+ # and also in the "Last activities" page, to retrieve public activity of this
8
+ # organization.
9
+ class LastActivity < Decidim::Query
10
+ def initialize(organization, options = {})
11
+ @organization = organization
12
+ @current_user = options[:current_user]
13
+ end
14
+
15
+ def query
16
+ @query ||= begin
17
+ query = base_query
18
+ query = filter_moderated(query)
19
+ query = filter_spaces(query)
20
+ query = filter_deleted(query)
21
+ query.with_new_resource_type("all")
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :organization, :current_user
28
+
29
+ def base_query
30
+ ActionLog
31
+ .where(organization: organization, visibility: visibility)
32
+ .order(created_at: :desc)
33
+ end
34
+
35
+ def visibility
36
+ %w(public-only all)
37
+ end
38
+
39
+ def filter_moderated(query)
40
+ # Filter out the items that have been moderated.
41
+ query.joins(
42
+ <<~SQL.squish
43
+ LEFT JOIN decidim_moderations
44
+ ON decidim_moderations.decidim_reportable_type = decidim_action_logs.resource_type
45
+ AND decidim_moderations.decidim_reportable_id = decidim_action_logs.resource_id
46
+ AND decidim_moderations.hidden_at IS NOT NULL
47
+ SQL
48
+ ).where(decidim_moderations: { id: nil })
49
+ end
50
+
51
+ def filter_spaces(query)
52
+ conditions = []
53
+
54
+ Decidim.participatory_space_manifests.map do |manifest|
55
+ klass = manifest.model_class_name.constantize
56
+
57
+ condition = if klass.include?(Decidim::HasPrivateUsers)
58
+ Arel.sql(
59
+ [
60
+ "decidim_action_logs.participatory_space_type = '#{manifest.model_class_name}'",
61
+ "decidim_action_logs.participatory_space_id IN (#{Arel.sql(klass.visible_for(current_user).select(:id).to_sql)})"
62
+ ].join(" AND ")
63
+ ).to_s
64
+ else
65
+ Arel.sql("decidim_action_logs.participatory_space_type = '#{manifest.model_class_name}'").to_s
66
+ end
67
+
68
+ conditions << "(#{condition})"
69
+ end
70
+ query.where(Arel.sql(conditions.join(" OR ")).to_s)
71
+ end
72
+
73
+ def filter_deleted(query)
74
+ conditions = []
75
+
76
+ ActionLog.public_resource_types.each do |resource_type|
77
+ klass = resource_type.constantize
78
+
79
+ condition = if klass.respond_to?(:not_deleted)
80
+ Arel.sql(
81
+ [
82
+ "decidim_action_logs.resource_type = '#{resource_type}'",
83
+ "decidim_action_logs.resource_id IN (#{Arel.sql(klass.not_deleted.select(:id).to_sql)})"
84
+ ].join(" AND ")
85
+ ).to_s
86
+ else
87
+ Arel.sql("decidim_action_logs.resource_type = '#{resource_type}'").to_s
88
+ end
89
+
90
+ conditions << "(#{condition})"
91
+ end
92
+
93
+ query.where(Arel.sql(conditions.join(" OR ")).to_s)
94
+ end
95
+ end
96
+ end
@@ -11,15 +11,15 @@ module Decidim
11
11
  private
12
12
 
13
13
  def query
14
- return @query if @query
15
-
16
- @query = Decidim::User.where(organization: @organization)
17
- @query = @query.where("created_at <= ?", end_time)
18
- @query
14
+ @query ||= Decidim::User.where(organization: @organization)
15
+ .where("deleted_at IS NULL OR deleted_at > ?", end_time)
16
+ .where("blocked_at IS NULL OR blocked_at > ?", end_time)
17
+ .confirmed
18
+ .where("created_at <= ?", end_time)
19
19
  end
20
20
 
21
21
  def quantity
22
- @quantity ||= @query.where("created_at >= ?", start_time).count
22
+ @quantity ||= query.where("created_at >= ?", start_time).count
23
23
  end
24
24
  end
25
25
  end