decidim-core 0.29.0 → 0.29.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity_cell.rb +0 -3
  3. data/app/cells/decidim/author/show.erb +5 -4
  4. data/app/cells/decidim/author_cell.rb +26 -0
  5. data/app/cells/decidim/card_s/show.erb +5 -3
  6. data/app/cells/decidim/content_blocks/stats_cell.rb +1 -1
  7. data/app/cells/decidim/diff_cell.rb +4 -0
  8. data/app/cells/decidim/endorsement_buttons_cell.rb +1 -1
  9. data/app/cells/decidim/newsletter_templates/image_text_cta_cell.rb +1 -1
  10. data/app/cells/decidim/translation_bar/show.erb +2 -2
  11. data/app/cells/decidim/translation_bar_cell.rb +1 -1
  12. data/app/commands/decidim/amendable/create_draft.rb +1 -0
  13. data/app/commands/decidim/destroy_account.rb +3 -0
  14. data/app/controllers/concerns/decidim/devise_authentication_methods.rb +1 -1
  15. data/app/controllers/concerns/decidim/direct_upload.rb +82 -0
  16. data/app/controllers/decidim/doorkeeper/credentials_controller.rb +1 -1
  17. data/app/controllers/decidim/links_controller.rb +1 -1
  18. data/app/controllers/decidim/profiles_controller.rb +4 -0
  19. data/app/forms/decidim/upload_validation_form.rb +1 -1
  20. data/app/helpers/concerns/decidim/user_role_checker.rb +46 -0
  21. data/app/helpers/decidim/cta_button_helper.rb +1 -1
  22. data/app/helpers/decidim/layout_helper.rb +28 -0
  23. data/app/helpers/decidim/map_helper.rb +6 -1
  24. data/app/helpers/decidim/sanitize_helper.rb +11 -2
  25. data/app/helpers/decidim/scopes_helper.rb +3 -2
  26. data/app/models/decidim/action_log.rb +11 -1
  27. data/app/models/decidim/attachment.rb +1 -1
  28. data/app/packs/src/decidim/append_redirect_url_to_modals.js +24 -14
  29. data/app/packs/src/decidim/direct_uploads/upload_field.js +21 -8
  30. data/app/packs/src/decidim/direct_uploads/upload_modal.js +3 -0
  31. data/app/packs/src/decidim/index.js +3 -0
  32. data/app/packs/src/decidim/remote_tooltips.js +38 -0
  33. data/app/packs/src/decidim/toggle.js +1 -1
  34. data/app/packs/src/decidim/tooltips.js +42 -22
  35. data/app/packs/stylesheets/decidim/_buttons.scss +1 -1
  36. data/app/packs/stylesheets/decidim/_labels.scss +1 -1
  37. data/app/packs/stylesheets/decidim/_modal_update.scss +4 -0
  38. data/app/packs/stylesheets/decidim/_profile.scss +1 -1
  39. data/app/packs/stylesheets/decidim/_progress-bar.scss +1 -1
  40. data/app/packs/stylesheets/decidim/legacy/conference-diploma.scss +2 -1
  41. data/app/presenters/decidim/attachment_presenter.rb +1 -1
  42. data/app/queries/decidim/last_activity.rb +16 -5
  43. data/app/services/decidim/base_diff_renderer.rb +26 -2
  44. data/app/services/decidim/email_notification_generator.rb +14 -5
  45. data/app/views/decidim/devise/omniauth_registrations/new.html.erb +1 -1
  46. data/app/views/decidim/offline/show.html.erb +1 -1
  47. data/app/views/decidim/pages/_tabbed.html.erb +2 -2
  48. data/app/views/layouts/decidim/_head.html.erb +1 -1
  49. data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile_tablet.html.erb +1 -1
  50. data/config/locales/ar.yml +16 -1
  51. data/config/locales/bg.yml +0 -1
  52. data/config/locales/bn-BD.yml +1 -0
  53. data/config/locales/bs-BA.yml +98 -0
  54. data/config/locales/ca.yml +14 -10
  55. data/config/locales/cs.yml +7 -1
  56. data/config/locales/de.yml +20 -16
  57. data/config/locales/el.yml +7 -1
  58. data/config/locales/en.yml +5 -1
  59. data/config/locales/es-MX.yml +6 -2
  60. data/config/locales/es-PY.yml +6 -2
  61. data/config/locales/es.yml +12 -8
  62. data/config/locales/eu.yml +202 -185
  63. data/config/locales/fi-plain.yml +5 -1
  64. data/config/locales/fi.yml +40 -36
  65. data/config/locales/fr-CA.yml +7 -3
  66. data/config/locales/fr.yml +6 -2
  67. data/config/locales/ga-IE.yml +9 -0
  68. data/config/locales/gl.yml +8 -1
  69. data/config/locales/hu.yml +3 -4
  70. data/config/locales/id-ID.yml +8 -0
  71. data/config/locales/is-IS.yml +8 -1
  72. data/config/locales/it.yml +19 -0
  73. data/config/locales/ja.yml +18 -16
  74. data/config/locales/lb.yml +9 -0
  75. data/config/locales/lt.yml +5 -2
  76. data/config/locales/lv.yml +8 -0
  77. data/config/locales/nl.yml +10 -1
  78. data/config/locales/no.yml +9 -0
  79. data/config/locales/pl.yml +1 -2
  80. data/config/locales/pt-BR.yml +244 -1
  81. data/config/locales/pt.yml +14 -0
  82. data/config/locales/ro-RO.yml +319 -180
  83. data/config/locales/ru.yml +8 -0
  84. data/config/locales/sk.yml +9 -1
  85. data/config/locales/sv.yml +541 -96
  86. data/config/locales/tr-TR.yml +10 -1
  87. data/config/locales/uk.yml +8 -1
  88. data/config/locales/zh-CN.yml +9 -0
  89. data/config/locales/zh-TW.yml +8 -1
  90. data/config/routes.rb +1 -0
  91. data/decidim-core.gemspec +4 -1
  92. data/lib/decidim/api/functions/component_list.rb +1 -1
  93. data/lib/decidim/api/functions/participatory_space_finder_base.rb +11 -1
  94. data/lib/decidim/api/interfaces/participatory_space_interface.rb +1 -1
  95. data/lib/decidim/api/types/component_type.rb +7 -0
  96. data/lib/decidim/api/types/user_group_type.rb +4 -0
  97. data/lib/decidim/api/types/user_type.rb +4 -0
  98. data/lib/decidim/attributes/rich_text.rb +38 -0
  99. data/lib/decidim/attributes/time_with_zone.rb +11 -1
  100. data/lib/decidim/attributes.rb +2 -0
  101. data/lib/decidim/content_parsers/blob_parser.rb +93 -0
  102. data/lib/decidim/content_parsers.rb +1 -0
  103. data/lib/decidim/content_renderers/blob_renderer.rb +90 -0
  104. data/lib/decidim/content_renderers.rb +1 -0
  105. data/lib/decidim/core/engine.rb +35 -1
  106. data/lib/decidim/core/test/factories.rb +28 -0
  107. data/lib/decidim/core/test/shared_examples/authorable_interface_examples.rb +1 -1
  108. data/lib/decidim/core/test/shared_examples/comments_examples.rb +25 -2
  109. data/lib/decidim/core/test/shared_examples/system_endorse_resource_examples.rb +107 -9
  110. data/lib/decidim/core/version.rb +1 -1
  111. data/lib/decidim/core.rb +11 -0
  112. data/lib/decidim/diffy_extension.rb +18 -0
  113. data/lib/decidim/form_builder.rb +1 -1
  114. data/lib/decidim/map/autocomplete.rb +1 -0
  115. data/lib/decidim/organization_settings.rb +4 -1
  116. data/lib/decidim/participatory_space_user.rb +4 -0
  117. data/lib/decidim/query_extensions.rb +0 -26
  118. data/lib/decidim/settings_manifest.rb +2 -0
  119. data/lib/decidim/translatable_attributes.rb +6 -1
  120. data/lib/decidim/view_model.rb +1 -1
  121. data/lib/tasks/upgrade/decidim_attachments.rake +14 -0
  122. data/lib/tasks/upgrade/decidim_fix_categorization.rake +34 -8
  123. metadata +30 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37f39b870aa9a840fbf4c415858b3210d520511f8efb0327bb90e8aa0126f243
4
- data.tar.gz: 16fa93602fc35d288ddeef831f50f3e3878714ea4fae6547064c9be0a148fad0
3
+ metadata.gz: 83ba22655575833dce2ce20e91060aa195abccf10cd5d185dbadbc04e79fcd93
4
+ data.tar.gz: a53c89d6b0c01046408b2872119406ffbd1a80a50b943256b872c8575b069e4a
5
5
  SHA512:
6
- metadata.gz: 0ae69c163d580612c4a8f4f4cf8d9c460aebc24270a67f4a245d25f4fceb2988075664aab8f97977ae19b75d90c42e1cdb03ad2591bd546c5acf9f8562d5852f
7
- data.tar.gz: 23132fff12f59d0bb72b073e6729ccb5f439f5c59493d8a3f8f7026dbb45cf20e56f1cac34b07bc8a2d10e757b4cd6efe60bc93180f22d2a4764db597913a1b6
6
+ metadata.gz: 07db0787cc696b27044eb8765477b338a16f7263cd29d9231dfd8e7145f5c4b63b19753a90b4d35c5e7614c1a69d47104435fcb2ecf12badea9a1163709ccbef
7
+ data.tar.gz: 034e07f956b0ad613172afdd3a56cbb3e37e273555537919073bf75b63dfe8b2ab7ec5502ecf87fb224cd5d525ad009a01e0db6742815ce61eec643c0fa1f1ae
@@ -105,9 +105,6 @@ module Decidim
105
105
  hash << I18n.locale.to_s
106
106
  hash << model.class.name.underscore
107
107
  hash << model.cache_key_with_version if model.respond_to?(:cache_key_with_version)
108
- if (author_cell = author)
109
- hash.push(Digest::MD5.hexdigest(author_cell.send(:cache_hash)))
110
- end
111
108
 
112
109
  hash.join(Decidim.cache_key_separator)
113
110
  end
@@ -1,6 +1,7 @@
1
- <% data = has_tooltip? ? { tooltip: render(:profile_minicard).html_safe } : nil %>
2
- <span class="author" data-author>
3
- <%= content_tag :span, class: "author__container#{" is-compact" if layout == :compact}", data: do %>
1
+ <%# If the options hash has the demo key it means we are in the decidim-design engine, so it is not a real-world scenario with actual users %>
2
+ <% tooltip = options.has_key?(:demo) ? { tooltip: render(:profile_minicard).html_safe } : nil %>
3
+ <%= content_tag(:span, class: :author, data: ) do %>
4
+ <%= content_tag :span, class: "author__container#{" is-compact" if layout == :compact}", data: tooltip do %>
4
5
  <% if layout == :compact %>
5
6
  <%= render :avatar %>
6
7
 
@@ -24,4 +25,4 @@
24
25
  <%= render action %>
25
26
  <% end %>
26
27
  <% end %>
27
- </span>
28
+ <% end %>
@@ -47,8 +47,26 @@ module Decidim
47
47
  @context_actions_options ||= options[:context_actions].map(&:to_sym)
48
48
  end
49
49
 
50
+ def profile_minicard
51
+ render
52
+ end
53
+
50
54
  private
51
55
 
56
+ # If the options hash has the demo key it means we are in the decidim-design engine,
57
+ # so it is not a real-world scenario with actual users
58
+ def data
59
+ @data ||= begin
60
+ internal_data = { author: true }
61
+ if has_tooltip? && !options.has_key?(:demo)
62
+ internal_data["remote_tooltip"] = true
63
+ internal_data["tooltip-url"] = decidim.profile_tooltip_path(raw_model.nickname)
64
+ end
65
+
66
+ internal_data
67
+ end
68
+ end
69
+
52
70
  def layout
53
71
  @layout ||= LAYOUTS.include?(options[:layout]) ? options[:layout] : :default
54
72
  end
@@ -160,5 +178,13 @@ module Decidim
160
178
  def resource_name
161
179
  @resource_name ||= from_context.class.name.demodulize.underscore
162
180
  end
181
+
182
+ def has_tooltip?
183
+ return false if model.deleted?
184
+ return false if model.respond_to?(:blocked?) && model.blocked?
185
+ return true if options.has_key?(:tooltip)
186
+
187
+ model.has_tooltip?
188
+ end
163
189
  end
164
190
  end
@@ -1,10 +1,12 @@
1
- <%= link_to resource_path, class: "card__search", id: dom_id(resource) do %>
1
+ <%= content_tag :div, id: dom_id(resource), class: "card__search" do %>
2
2
  <%= content_tag title_tag, class: "h4 card__search-title" do %>
3
- <%= title %>
3
+ <%= link_to resource_path, class: "card__search" do %>
4
+ <%= title %>
5
+ <% end %>
4
6
  <% end %>
5
7
  <% if metadata_cell.present? %>
6
8
  <div class="card__search-metadata">
7
- <%= cell metadata_cell, resource, links: false %>
9
+ <%= cell metadata_cell, resource, links: false %>
8
10
  </div>
9
11
  <% end %>
10
12
  <% end %>
@@ -17,7 +17,7 @@ module Decidim
17
17
  end
18
18
 
19
19
  def cache_expiry_time
20
- 10.minutes
20
+ Decidim.stats_cache_expiry_time
21
21
  end
22
22
  end
23
23
  end
@@ -70,6 +70,10 @@ module Decidim
70
70
 
71
71
  # DiffRenderer class for the current_version's item; falls back to `BaseDiffRenderer`.
72
72
  def diff_renderer_class
73
+ renderer_class = "#{current_version.item_type}DiffRenderer".safe_constantize
74
+
75
+ return renderer_class if renderer_class
76
+
73
77
  if current_version.item_type.deconstantize == "Decidim"
74
78
  "#{current_version.item_type.pluralize}::DiffRenderer".constantize
75
79
  else
@@ -69,7 +69,7 @@ module Decidim
69
69
  end
70
70
 
71
71
  def user_has_verified_groups?
72
- current_user && Decidim::UserGroups::ManageableUserGroups.for(current_user).verified.any?
72
+ current_user && current_organization.user_groups_enabled? && Decidim::UserGroups::ManageableUserGroups.for(current_user).verified.any?
73
73
  end
74
74
 
75
75
  def endorse_translated
@@ -50,7 +50,7 @@ module Decidim
50
50
  end
51
51
 
52
52
  def main_image_url
53
- newsletter.template.images_container.attached_uploader(:main_image).url(host: organization.host)
53
+ newsletter.template.images_container.attached_uploader(:main_image).url
54
54
  end
55
55
 
56
56
  def organization_primary_color
@@ -1,6 +1,6 @@
1
1
  <div>
2
- <div class="row column">
2
+ <div class="pt-4 pb-4 text-center bg-background">
3
3
  <%= link %>
4
- <span class="translation-button-help"><%= help_text %></span>
4
+ <span class="translation-button-help ml-4"><%= help_text %></span>
5
5
  </div>
6
6
  </div>
@@ -32,7 +32,7 @@ module Decidim
32
32
  parsed_url.query = new_query
33
33
  url = parsed_url.to_s
34
34
 
35
- link_to button_text, url, class: "button small hollow"
35
+ link_to button_text, url, class: "button button__sm button__transparent-secondary"
36
36
  end
37
37
 
38
38
  def button_text
@@ -54,6 +54,7 @@ module Decidim
54
54
  emendation.body = { I18n.locale => form.emendation_params.with_indifferent_access[:body] }
55
55
  emendation.component = amendable.component
56
56
  emendation.add_author(current_user, user_group)
57
+ emendation.category = amendable.category if amendable.respond_to?(:category)
57
58
  emendation.save!
58
59
  emendation
59
60
  end
@@ -37,6 +37,9 @@ module Decidim
37
37
  current_user.name = ""
38
38
  current_user.nickname = ""
39
39
  current_user.email = ""
40
+ current_user.personal_url = ""
41
+ current_user.about = ""
42
+ current_user.notifications_sending_frequency = "none"
40
43
  current_user.delete_reason = @form.delete_reason
41
44
  current_user.admin = false if current_user.admin?
42
45
  current_user.deleted_at = Time.current
@@ -12,7 +12,7 @@ module Decidim
12
12
  if user.present? && user.blocked?
13
13
  check_user_block_status(user)
14
14
  elsif user.needs_password_update?
15
- change_password_path
15
+ decidim.change_password_path
16
16
  elsif first_login_and_not_authorized?(user) && !user.admin? && !pending_redirect?(user)
17
17
  decidim_verifications.first_login_authorizations_path
18
18
  else
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DirectUpload
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include Decidim::NeedsOrganization
9
+ skip_before_action :verify_organization
10
+
11
+ before_action :check_organization!,
12
+ :check_authenticated!,
13
+ :check_user_belongs_to_organization,
14
+ :validate_direct_upload
15
+ end
16
+
17
+ protected
18
+
19
+ def validate_direct_upload
20
+ # We skip the validation if we are in system panel. `current_admin` refers to the main system admin user.
21
+ return if current_admin.present?
22
+
23
+ head :unprocessable_entity unless [
24
+ maximum_allowed_size.try(:to_i) >= blob_args[:byte_size].try(:to_i),
25
+ content_types.any? { |pattern| pattern.match?(blob_args[:content_type]) },
26
+ content_types.any? { |pattern| pattern.match?(MiniMime.lookup_by_extension(extension)&.content_type) },
27
+ allowed_extensions.any? { |pattern| pattern.match?(extension) }
28
+ ].all?
29
+ rescue NoMethodError
30
+ head :unprocessable_entity
31
+ end
32
+
33
+ def extension
34
+ File.extname(blob_args[:filename]).delete(".")
35
+ end
36
+
37
+ def maximum_allowed_size
38
+ current_organization.settings.upload_maximum_file_size
39
+ end
40
+
41
+ def check_organization!
42
+ head :unauthorized if current_organization.blank? && current_admin.blank?
43
+ end
44
+
45
+ def check_authenticated!
46
+ head :unauthorized if current_user.blank? && current_admin.blank?
47
+ end
48
+
49
+ def check_user_belongs_to_organization
50
+ return if current_admin.present?
51
+
52
+ head :unauthorized unless current_organization == current_user.organization
53
+ end
54
+
55
+ def allowed_extensions
56
+ if user_has_elevated_role?
57
+ current_organization.settings.upload_allowed_file_extensions_admin
58
+ else
59
+ current_organization.settings.upload_allowed_file_extensions
60
+ end
61
+ end
62
+
63
+ def content_types
64
+ if user_has_elevated_role?
65
+ current_organization.settings.upload_allowed_content_types_admin
66
+ else
67
+ current_organization.settings.upload_allowed_content_types
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def user_has_elevated_role?
74
+ [
75
+ current_user&.admin?,
76
+ defined?(Decidim::Assemblies::AssembliesWithUserRole) && Decidim::Assemblies::AssembliesWithUserRole.for(current_user).any?,
77
+ defined?(Decidim::Conferences::ConferencesWithUserRole) && Decidim::Conferences::ConferencesWithUserRole.for(current_user).any?,
78
+ defined?(Decidim::ParticipatoryProcessesWithUserRole) && Decidim::ParticipatoryProcessesWithUserRole.for(current_user).any?
79
+ ].any?
80
+ end
81
+ end
82
+ end
@@ -28,7 +28,7 @@ module Decidim
28
28
  end
29
29
 
30
30
  def avatar_url
31
- avatar_url = current_resource_owner.attached_uploader(:avatar).url(host: current_resource_owner.organization.host)
31
+ avatar_url = current_resource_owner.attached_uploader(:avatar).url
32
32
  return unless avatar_url
33
33
 
34
34
  unless %r{^https?://}.match? avatar_url
@@ -41,7 +41,7 @@ module Decidim
41
41
  end
42
42
 
43
43
  def escape_url(external_url)
44
- before_fragment, fragment = external_url.split("#", 2)
44
+ before_fragment, fragment = URI.decode_www_form_component(external_url).split("#", 2)
45
45
  escaped_before_fragment = URI::Parser.new.escape(before_fragment)
46
46
 
47
47
  if fragment
@@ -27,6 +27,10 @@ module Decidim
27
27
  redirect_to profile_activity_path(nickname: params[:nickname])
28
28
  end
29
29
 
30
+ def tooltip
31
+ render json: { data: cell("decidim/author", profile_holder.presenter).profile_minicard }
32
+ end
33
+
30
34
  def following
31
35
  @content_cell = "decidim/following"
32
36
  @title_key = "following"
@@ -10,7 +10,7 @@ module Decidim
10
10
  # Property is named as attribute in upload modal and passthru validator, but
11
11
  # it cannot be named as attribute here.
12
12
  attribute :property, String
13
- attribute :blob, String
13
+ attribute :blob, Decidim::Attributes::Blob
14
14
  attribute :form_class, String
15
15
 
16
16
  validates :resource_class, presence: true
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module UserRoleChecker
5
+ # Shared behaviour for signed_in admins
6
+ extend ActiveSupport::Concern
7
+
8
+ private
9
+
10
+ def user_has_any_role?(user, participatory_space = nil, broad_check: false)
11
+ return false unless user
12
+
13
+ [
14
+ user.admin,
15
+ user.roles.any?,
16
+ participatory_process_user_role?(user, participatory_space, broad_check:),
17
+ assembly_user_role?(user, participatory_space, broad_check:),
18
+ conference_user_role?(user, participatory_space, broad_check:)
19
+ ].any?
20
+ end
21
+
22
+ def participatory_process_user_role?(user, participatory_process = nil, broad_check: false)
23
+ return false unless Decidim.module_installed?(:participatory_processes)
24
+ return Decidim::ParticipatoryProcessUserRole.exists?(user:) if broad_check
25
+ return false unless participatory_process.is_a?(Decidim::ParticipatoryProcess)
26
+
27
+ Decidim::ParticipatoryProcessUserRole.exists?(user:, participatory_process:)
28
+ end
29
+
30
+ def assembly_user_role?(user, assembly = nil, broad_check: false)
31
+ return false unless Decidim.module_installed?(:assemblies)
32
+ return Decidim::AssemblyUserRole.exists?(user:) if broad_check
33
+ return false unless assembly.is_a?(Decidim::Assembly)
34
+
35
+ Decidim::AssemblyUserRole.exists?(user:, assembly:)
36
+ end
37
+
38
+ def conference_user_role?(user, conference = nil, broad_check: false)
39
+ return false unless Decidim.module_installed?(:conferences)
40
+ return Decidim::ConferenceUserRole.exists?(user:) if broad_check
41
+ return false unless conference.is_a?(Decidim::Conference)
42
+
43
+ Decidim::ConferenceUserRole.exists?(user:, conference:)
44
+ end
45
+ end
46
+ end
@@ -16,7 +16,7 @@ module Decidim
16
16
  # Finds the CTA button path to reuse it in other places.
17
17
  def cta_button_path
18
18
  if current_organization.cta_button_path.present?
19
- current_organization.cta_button_path
19
+ "/#{current_organization.cta_button_path}"
20
20
  elsif Decidim::ParticipatoryProcess.where(organization: current_organization).published.any?
21
21
  decidim_participatory_processes.participatory_processes_path
22
22
  elsif current_user
@@ -140,8 +140,36 @@ module Decidim
140
140
  end
141
141
  end
142
142
 
143
+ def current_url(params = request.parameters)
144
+ return url_for(params) if respond_to?(:current_participatory_space) || respond_to?(:current_component)
145
+
146
+ each_decidim_engine do |helpers|
147
+ return helpers.url_for(params)
148
+ rescue ActionController::UrlGenerationError
149
+ # Continue to next engine in case the URL is not available.
150
+ end
151
+
152
+ main_app.url_for(params)
153
+ rescue ActionController::UrlGenerationError
154
+ "#{request.base_url}#{"?#{params.to_query}" unless params.empty?}"
155
+ end
156
+
143
157
  private
144
158
 
159
+ def each_decidim_engine
160
+ Rails.application.railties.each do |engine|
161
+ next unless engine.is_a?(Rails::Engine)
162
+ next unless engine.isolated?
163
+ next unless engine.engine_name.start_with?("decidim_")
164
+ next unless respond_to?(engine.engine_name)
165
+
166
+ yield public_send(engine.engine_name)
167
+ end
168
+ return unless respond_to?(:decidim)
169
+
170
+ yield decidim
171
+ end
172
+
145
173
  def tag_builder
146
174
  @tag_builder ||= ActionView::Helpers::TagHelper::TagBuilder.new(self)
147
175
  end
@@ -35,7 +35,12 @@ module Decidim
35
35
  data: { "external-link": "text-only" }
36
36
  }.merge(map_html_options)
37
37
  return link_to(map_url, html_options) do
38
- image_tag decidim.static_map_path(sgid: resource.to_sgid.to_s), alt: "#{map_service_brand} - #{address_text}"
38
+ # We also add the latitude and the longitude to prevent the Workbox cache to be overly aggressive when updating a map
39
+ image_tag decidim.static_map_path(
40
+ sgid: resource.to_sgid.to_s,
41
+ latitude: resource.latitude,
42
+ longitude: resource.longitude
43
+ ), alt: "#{map_service_brand} - #{address_text}"
39
44
  end
40
45
  end
41
46
  end
@@ -37,13 +37,22 @@ module Decidim
37
37
  end
38
38
  end
39
39
 
40
+ # Converts the blob and blob variant references to blob URLs.
41
+ def decidim_rich_text(html, **)
42
+ renderer = Decidim::ContentProcessor.renderer_klass(:blob).constantize.new(html)
43
+ renderer.render(**)
44
+ end
45
+
40
46
  def decidim_sanitize_editor(html, options = {})
41
47
  content_tag(:div, decidim_sanitize(html, options), class: %w(rich-text-display))
42
48
  end
43
49
 
44
50
  def decidim_sanitize_editor_admin(html, options = {})
45
51
  html = Decidim::IframeDisabler.new(html, options).perform
46
- decidim_sanitize_editor(html, { scrubber: Decidim::AdminInputScrubber.new }.merge(options))
52
+ decidim_sanitize_editor(
53
+ decidim_rich_text(html),
54
+ { scrubber: Decidim::AdminInputScrubber.new }.merge(options)
55
+ )
47
56
  end
48
57
 
49
58
  def decidim_html_escape(text)
@@ -51,7 +60,7 @@ module Decidim
51
60
  end
52
61
 
53
62
  def decidim_url_escape(text)
54
- decidim_html_escape(text).sub(/^javascript:/, "")
63
+ decidim_html_escape(text).sub(/^\s*javascript:/, "")
55
64
  end
56
65
 
57
66
  def decidim_sanitize_translated(text)
@@ -53,13 +53,14 @@ module Decidim
53
53
  # options - An optional Hash with options:
54
54
  #
55
55
  # Returns nothing.
56
- def scopes_select_field(form, name, root: false, options: {})
56
+ def scopes_select_field(form, name, root: false, options: {}, html_options: {})
57
57
  options = options.merge(include_blank: I18n.t("decidim.scopes.prompt")) unless options.has_key?(:include_blank)
58
58
 
59
59
  form.select(
60
60
  name,
61
61
  ordered_scopes_descendants_for_select(root),
62
- options
62
+ options,
63
+ html_options
63
64
  )
64
65
  end
65
66
 
@@ -138,6 +138,7 @@ module Decidim
138
138
  Decidim::Proposals::Proposal
139
139
  Decidim::Surveys::Survey
140
140
  Decidim::Assembly
141
+ Decidim::Conference
141
142
  Decidim::Initiative
142
143
  Decidim::ParticipatoryProcess
143
144
  ).select do |klass|
@@ -154,7 +155,16 @@ module Decidim
154
155
  end
155
156
 
156
157
  def self.publicable_public_resource_types
157
- @publicable_public_resource_types ||= public_resource_types.select { |klass| klass.constantize.column_names.include?("published_at") }
158
+ @publicable_public_resource_types ||= public_resource_types
159
+ .select { |klass| klass.constantize.column_names.include?("published_at") } - publicable_exceptions
160
+ end
161
+
162
+ def self.publicable_exceptions
163
+ @publicable_exceptions = %w(
164
+ Decidim::Blogs::Post
165
+ ).select do |klass|
166
+ klass.safe_constantize.present?
167
+ end
158
168
  end
159
169
 
160
170
  def self.ransackable_scopes(auth_object = nil)
@@ -74,7 +74,7 @@ module Decidim
74
74
  # Returns String.
75
75
  def file_type
76
76
  if file?
77
- url&.split(".")&.last&.downcase
77
+ url&.split(".")&.last&.split("&")&.first&.downcase
78
78
  elsif link?
79
79
  "link"
80
80
  end
@@ -53,21 +53,31 @@ $(() => {
53
53
  }
54
54
 
55
55
  $(document).on("click.zf.trigger", (event) => {
56
- const target = `#${$(event.target).data("open")}`;
57
- const redirectUrl = $(event.target).data("redirectUrl");
58
-
59
- if (target && redirectUrl) {
60
- $("<input type='hidden' />").
61
- attr("id", "redirect_url").
62
- attr("name", "redirect_url").
63
- attr("value", redirectUrl).
64
- appendTo(`${target} form`);
65
-
66
- $(`${target} a`).attr("href", (index, href) => {
67
- const querystring = jQuery.param({"redirect_url": redirectUrl});
68
- return href + (href.match(/\?/) ? "&" : "?") + querystring;
69
- });
56
+ // Try to get the <a> directly or find the closest parent <a>
57
+ const $target = $(event.target).closest("a");
58
+
59
+ // Check if an <a> was found
60
+ if (!$target) {
61
+ return;
62
+ }
63
+
64
+ const dialogTarget = `#${$target.data("dialog-open")}`;
65
+ const redirectUrl = $target.data("redirectUrl");
66
+
67
+ if (!dialogTarget || !redirectUrl) {
68
+ return;
70
69
  }
70
+
71
+ $("<input type='hidden' />").
72
+ attr("id", "redirect_url").
73
+ attr("name", "redirect_url").
74
+ attr("value", redirectUrl).
75
+ appendTo(`${dialogTarget} form`);
76
+
77
+ $(`${dialogTarget} a`).attr("href", (index, href) => {
78
+ const querystring = jQuery.param({"redirect_url": redirectUrl});
79
+ return href + (href.match(/\?/) ? "&" : "?") + querystring;
80
+ });
71
81
  });
72
82
 
73
83
  $(document).on("closed.zf.reveal", (event) => {
@@ -21,6 +21,7 @@ const updateActiveUploads = (modal) => {
21
21
  const files = document.querySelector(`[data-active-uploads=${modal.modal.id}]`)
22
22
  const previousId = Array.from(files.querySelectorAll("[type=hidden][id]"))
23
23
  const isMultiple = modal.options.multiple
24
+ const isTitled = modal.options.titled
24
25
 
25
26
  // fastest way to clean children nodes
26
27
  files.textContent = ""
@@ -34,16 +35,26 @@ const updateActiveUploads = (modal) => {
34
35
  let hidden = ""
35
36
  if (file.hiddenField) {
36
37
  // if there is hiddenField, this file is new
37
- const fileField = isMultiple
38
- ? `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][file]`
39
- : `${modal.options.resourceName}[${modal.options.addAttribute}]`
38
+ let fileField = null;
39
+ if (isMultiple) {
40
+ fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][file]`
41
+ } else if (isTitled) {
42
+ fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][file]`
43
+ } else {
44
+ fileField = `${modal.options.resourceName}[${modal.options.addAttribute}]`;
45
+ }
40
46
 
41
47
  hidden = `<input type="hidden" name="${fileField}" value="${file.hiddenField}" />`
42
48
  } else {
43
49
  // otherwise, we keep the attachmentId
44
- const fileField = isMultiple
45
- ? `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][id]`
46
- : `${modal.options.resourceName}[${modal.options.addAttribute}]`
50
+ let fileField = null;
51
+ if (isMultiple) {
52
+ fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][id]`;
53
+ } else if (isTitled) {
54
+ fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][id]`;
55
+ } else {
56
+ fileField = `${modal.options.resourceName}[${modal.options.addAttribute}]`;
57
+ }
47
58
 
48
59
  // convert all node attributes to string
49
60
  const attributes = Array.from(previousId.find(({ id }) => id === file.attachmentId).attributes).reduce((acc, { name, value }) => `${acc} ${name}="${value}"`, "")
@@ -51,10 +62,12 @@ const updateActiveUploads = (modal) => {
51
62
  hidden += `<input type="hidden" name="${fileField}" value="${file.attachmentId}" />`
52
63
  }
53
64
 
54
- if (modal.options.titled) {
65
+ if (isTitled) {
55
66
  const titleValue = modal.modal.querySelectorAll('input[type="text"]')[ix].value
56
67
  // NOTE - Renaming the attachment is not supported when multiple uploader is disabled
57
- const titleField = `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][title]`
68
+ const titleField = isMultiple
69
+ ? `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][title]`
70
+ : `${modal.options.resourceName}[${modal.options.addAttribute}][title]`
58
71
  hidden += `<input type="hidden" name="${titleField}" value="${escapeQuotes(titleValue)}" />`
59
72
 
60
73
  title = titleValue
@@ -77,6 +77,9 @@ export default class UploadModal {
77
77
  uploader.upload.create((error, blob) => {
78
78
  if (error) {
79
79
  uploader.errors = [error]
80
+ this.uploadItems.replaceChild(this.createUploadItem(file, [error], { value: 100 }), item);
81
+ this.updateDropZone();
82
+
80
83
  } else {
81
84
  // attach the file hash to submit the form, when the file has been uploaded
82
85
  file.hiddenField = blob.signed_id
@@ -69,6 +69,7 @@ import handleNotificationActions from "src/decidim/notifications_actions"
69
69
  import RemoteModal from "src/decidim/remote_modal"
70
70
  import selectActiveIdentity from "src/decidim/identity_selector_dialog"
71
71
  import createTooltip from "src/decidim/tooltips"
72
+ import fetchRemoteTooltip from "src/decidim/remote_tooltips"
72
73
  import createToggle from "src/decidim/toggle"
73
74
  import {
74
75
  createAccordion,
@@ -195,6 +196,8 @@ const initializer = (element = document) => {
195
196
  // Initialize data-toggles
196
197
  element.querySelectorAll("[data-toggle]").forEach((elem) => createToggle(elem))
197
198
 
199
+ element.querySelectorAll("[data-remote-tooltip]").forEach((elem) => fetchRemoteTooltip(elem))
200
+
198
201
  element.querySelectorAll(".new_report").forEach((elem) => changeReportFormBehavior(elem))
199
202
  }
200
203