decidim-core 0.29.1 → 0.29.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) 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/diff_cell.rb +4 -0
  7. data/app/cells/decidim/newsletter_templates/image_text_cta_cell.rb +1 -1
  8. data/app/cells/decidim/translation_bar/show.erb +2 -2
  9. data/app/cells/decidim/translation_bar_cell.rb +1 -1
  10. data/app/commands/decidim/destroy_account.rb +3 -0
  11. data/app/controllers/decidim/doorkeeper/credentials_controller.rb +1 -1
  12. data/app/controllers/decidim/links_controller.rb +1 -1
  13. data/app/controllers/decidim/profiles_controller.rb +4 -0
  14. data/app/helpers/concerns/decidim/user_role_checker.rb +46 -0
  15. data/app/helpers/decidim/cta_button_helper.rb +1 -1
  16. data/app/helpers/decidim/map_helper.rb +6 -1
  17. data/app/helpers/decidim/sanitize_helper.rb +11 -2
  18. data/app/models/decidim/attachment.rb +1 -1
  19. data/app/packs/src/decidim/append_redirect_url_to_modals.js +14 -6
  20. data/app/packs/src/decidim/direct_uploads/upload_field.js +21 -8
  21. data/app/packs/src/decidim/index.js +3 -0
  22. data/app/packs/src/decidim/remote_tooltips.js +38 -0
  23. data/app/packs/src/decidim/toggle.js +1 -1
  24. data/app/packs/src/decidim/tooltips.js +42 -22
  25. data/app/packs/stylesheets/decidim/_labels.scss +1 -1
  26. data/app/packs/stylesheets/decidim/_profile.scss +1 -1
  27. data/app/packs/stylesheets/decidim/_progress-bar.scss +1 -1
  28. data/app/packs/stylesheets/decidim/legacy/conference-diploma.scss +2 -1
  29. data/app/presenters/decidim/attachment_presenter.rb +1 -1
  30. data/app/services/decidim/base_diff_renderer.rb +26 -2
  31. data/app/services/decidim/email_notification_generator.rb +14 -5
  32. data/app/views/decidim/pages/_tabbed.html.erb +2 -2
  33. data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile_tablet.html.erb +1 -1
  34. data/config/locales/ar.yml +16 -0
  35. data/config/locales/bn-BD.yml +1 -0
  36. data/config/locales/bs-BA.yml +98 -0
  37. data/config/locales/ca.yml +13 -9
  38. data/config/locales/cs.yml +5 -0
  39. data/config/locales/de.yml +18 -14
  40. data/config/locales/el.yml +7 -0
  41. data/config/locales/en.yml +4 -0
  42. data/config/locales/es-MX.yml +5 -1
  43. data/config/locales/es-PY.yml +5 -1
  44. data/config/locales/es.yml +11 -7
  45. data/config/locales/eu.yml +198 -181
  46. data/config/locales/fi-plain.yml +4 -0
  47. data/config/locales/fi.yml +39 -35
  48. data/config/locales/fr-CA.yml +6 -2
  49. data/config/locales/fr.yml +5 -1
  50. data/config/locales/ga-IE.yml +9 -0
  51. data/config/locales/gl.yml +8 -0
  52. data/config/locales/hu.yml +3 -3
  53. data/config/locales/id-ID.yml +8 -0
  54. data/config/locales/is-IS.yml +8 -1
  55. data/config/locales/it.yml +19 -0
  56. data/config/locales/ja.yml +15 -13
  57. data/config/locales/lb.yml +9 -0
  58. data/config/locales/lt.yml +5 -1
  59. data/config/locales/lv.yml +8 -0
  60. data/config/locales/nl.yml +10 -1
  61. data/config/locales/no.yml +9 -0
  62. data/config/locales/pl.yml +1 -1
  63. data/config/locales/pt-BR.yml +2 -1
  64. data/config/locales/pt.yml +14 -0
  65. data/config/locales/ro-RO.yml +258 -135
  66. data/config/locales/ru.yml +8 -0
  67. data/config/locales/sk.yml +9 -1
  68. data/config/locales/sv.yml +7 -7
  69. data/config/locales/tr-TR.yml +10 -1
  70. data/config/locales/uk.yml +8 -1
  71. data/config/locales/zh-CN.yml +9 -0
  72. data/config/locales/zh-TW.yml +8 -0
  73. data/config/routes.rb +1 -0
  74. data/decidim-core.gemspec +4 -1
  75. data/lib/decidim/api/functions/component_list.rb +1 -1
  76. data/lib/decidim/api/functions/participatory_space_finder_base.rb +11 -1
  77. data/lib/decidim/api/interfaces/participatory_space_interface.rb +1 -1
  78. data/lib/decidim/api/types/component_type.rb +7 -0
  79. data/lib/decidim/api/types/user_group_type.rb +4 -0
  80. data/lib/decidim/api/types/user_type.rb +4 -0
  81. data/lib/decidim/attributes/rich_text.rb +38 -0
  82. data/lib/decidim/attributes/time_with_zone.rb +11 -1
  83. data/lib/decidim/attributes.rb +2 -0
  84. data/lib/decidim/content_parsers/blob_parser.rb +93 -0
  85. data/lib/decidim/content_parsers.rb +1 -0
  86. data/lib/decidim/content_renderers/blob_renderer.rb +90 -0
  87. data/lib/decidim/content_renderers.rb +1 -0
  88. data/lib/decidim/core/engine.rb +29 -1
  89. data/lib/decidim/core/test/factories.rb +28 -0
  90. data/lib/decidim/core/test/shared_examples/authorable_interface_examples.rb +1 -1
  91. data/lib/decidim/core/test/shared_examples/comments_examples.rb +15 -2
  92. data/lib/decidim/core/version.rb +1 -1
  93. data/lib/decidim/diffy_extension.rb +18 -0
  94. data/lib/decidim/form_builder.rb +1 -1
  95. data/lib/decidim/map/autocomplete.rb +1 -0
  96. data/lib/decidim/participatory_space_user.rb +4 -0
  97. data/lib/decidim/query_extensions.rb +0 -26
  98. data/lib/decidim/settings_manifest.rb +2 -0
  99. data/lib/decidim/translatable_attributes.rb +6 -1
  100. data/lib/tasks/upgrade/decidim_fix_categorization.rake +34 -8
  101. metadata +28 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52a3dc5c9feb9a081fb911d75e25eba2847b1d159837b23853140d96f3c6754b
4
- data.tar.gz: 18e41bc984ad456282a1b0c9d3ec3fef7fc89376b330b79198fe3e650cee1301
3
+ metadata.gz: 83ba22655575833dce2ce20e91060aa195abccf10cd5d185dbadbc04e79fcd93
4
+ data.tar.gz: a53c89d6b0c01046408b2872119406ffbd1a80a50b943256b872c8575b069e4a
5
5
  SHA512:
6
- metadata.gz: 1c3e01dc59cb7b999828421bfad4e0bdbbddc813200b4b99de17bd89c4b6a664ad0f98af080ec0f84bcd335b89f99fa871c851d56429ee998979ad68bbec4c3c
7
- data.tar.gz: 593fff36a401ad9116314865972323861374a70b53883c5c7421f902899d3ea0b934f64aaec1f2c9ed8879440fd647344e60a67beab12e76924851f7b377e788
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 %>
@@ -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
@@ -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
@@ -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
@@ -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"
@@ -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
@@ -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)
@@ -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,10 +53,18 @@ $(() => {
53
53
  }
54
54
 
55
55
  $(document).on("click.zf.trigger", (event) => {
56
- const target = `#${$(event.target).data("dialogOpen")}`;
57
- const redirectUrl = $(event.target).data("redirectUrl");
56
+ // Try to get the <a> directly or find the closest parent <a>
57
+ const $target = $(event.target).closest("a");
58
58
 
59
- if (!target || !redirectUrl) {
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) {
60
68
  return;
61
69
  }
62
70
 
@@ -64,10 +72,10 @@ $(() => {
64
72
  attr("id", "redirect_url").
65
73
  attr("name", "redirect_url").
66
74
  attr("value", redirectUrl).
67
- appendTo(`${target} form`);
75
+ appendTo(`${dialogTarget} form`);
68
76
 
69
- $(`${target} a`).attr("href", (index, href) => {
70
- const querystring = jQuery.param({ "redirect_url": redirectUrl });
77
+ $(`${dialogTarget} a`).attr("href", (index, href) => {
78
+ const querystring = jQuery.param({"redirect_url": redirectUrl});
71
79
  return href + (href.match(/\?/) ? "&" : "?") + querystring;
72
80
  });
73
81
  });
@@ -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
@@ -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
 
@@ -0,0 +1,38 @@
1
+ import createTooltip from "src/decidim/tooltips"
2
+
3
+ /**
4
+ * Given the following HTML structure,
5
+ * <span data-remote-tooltip="true" tooltip-url="some url" data-author="true">
6
+ * <span></span>
7
+ * </span>
8
+ *
9
+ * This function will check if the HTMLElement where is attached to has a child, and will add a data tooltip attribute
10
+ * to the respective child in order to attach the fetched HTML content fetched under a json key as the content of the
11
+ * HTML tooltip. The DOM structure is expected to be like follows:
12
+ *
13
+ * <span data-remote-tooltip="true" tooltip-url="some url" data-author="true">
14
+ * <span data-tooltip="HTML content from json data field"></span>
15
+ * </span>
16
+ *
17
+ * @param {HTMLElement} node The element holding the initialization data
18
+ * @returns {void}
19
+ */
20
+ export default async function(node) {
21
+ const container = node.firstElementChild;
22
+
23
+ if (container) {
24
+ const response = await fetch(node.dataset.tooltipUrl, {
25
+ headers: {
26
+ "Content-Type": "application/json"
27
+ }
28
+ });
29
+ if (response.ok) {
30
+ const json = await response.json();
31
+
32
+ container.dataset.tooltip = json.data;
33
+ createTooltip(container);
34
+ } else {
35
+ console.error(response.status, response.statusText);
36
+ }
37
+ }
38
+ }
@@ -9,7 +9,7 @@ export default function createToggle(component) {
9
9
  const { toggle } = component.dataset
10
10
 
11
11
  if (!component.id) {
12
- // when component has no id, we enforce to have it one
12
+ // when component has no id, we enforce it to have one
13
13
  component.id = `toggle-${Math.random().toString(36).substring(7)}`
14
14
  }
15
15
 
@@ -63,9 +63,32 @@ export default function(node) {
63
63
  // append to dom hidden, to apply css transitions
64
64
  tooltip.setAttribute("aria-hidden", true)
65
65
 
66
- const append = () => {
67
- // do nothing if the tooltip is already present at the DOM
66
+ // used to detect if the user is on a mobile device by checking the user agent
67
+ const useMobile = (/Mobi|Android/i).test(navigator.userAgent);
68
+
69
+ // used not to collapse tooltip
70
+ let removeTooltip = () => {
71
+ tooltip.setAttribute("aria-hidden", "true");
72
+ }
73
+
74
+ // used to allow clicks outside the tooltip to take place on the page or device
75
+ const OutsideClick = (event) => {
76
+ if (!tooltip.contains(event.target) && event.target !== node) {
77
+ removeTooltip();
78
+ }
79
+ }
80
+
81
+ // function called again to allow clicks outside the tooltip to collapse the tooltip
82
+ removeTooltip = () => {
83
+ tooltip.setAttribute("aria-hidden", "true");
84
+ document.removeEventListener("click", OutsideClick)
85
+ }
86
+
87
+ const toggleTooltip = (event) => {
88
+ event.preventDefault();
89
+ // if the tooltip is visible in the DOM, hide it otherwise display
68
90
  if (tooltip.getAttribute("aria-hidden") === "false") {
91
+ tooltip.setAttribute("aria-hidden", "true");
69
92
  return
70
93
  }
71
94
 
@@ -106,27 +129,24 @@ export default function(node) {
106
129
  tooltip.style.left = `${positionX}px`
107
130
 
108
131
  tooltip.setAttribute("aria-hidden", false)
109
- }
110
-
111
- // in order to revoke the remove event when the mouse is over the trigger/tooltip
112
- let cancelRemove = false
113
132
 
114
- const remove = () => {
115
- cancelRemove = false
116
- // give some sleep time before hiding the element from the DOM
117
- setTimeout(() => !cancelRemove && tooltip.setAttribute("aria-hidden", true), 500);
133
+ // sleep time before hiding the element from the DOM
134
+ setTimeout(() => document.addEventListener("click", OutsideClick))
118
135
  }
119
136
 
120
- // keyboard listener is at root-level
121
- window.addEventListener("keydown", (event) => event.key === "Escape" && remove())
122
-
123
- node.addEventListener("mouseenter", append)
124
- node.addEventListener("mouseleave", remove)
125
- node.addEventListener("focus", append)
126
- node.addEventListener("blur", remove)
127
- tooltip.addEventListener("mouseenter", () => tooltip.setAttribute("aria-hidden", false))
128
- tooltip.addEventListener("mouseleave", remove)
129
-
130
- node.addEventListener("mouseover", () => (cancelRemove = true))
131
- tooltip.addEventListener("mouseover", () => (cancelRemove = true))
137
+ if (useMobile) {
138
+ // mobile use to click and toggle the tooltip
139
+ node.addEventListener("click", toggleTooltip);
140
+ window.addEventListener("keydown", (event) => event.key === "Escape" && removeTooltip())
141
+ } else {
142
+ // desktop use for hover and blur over tooltip
143
+ node.addEventListener("mouseenter", toggleTooltip)
144
+ node.addEventListener("mouseleave", removeTooltip)
145
+ node.addEventListener("focus", toggleTooltip)
146
+ node.addEventListener("blur", removeTooltip)
147
+
148
+ // tooltip hover listeners to prevent hiding when hovered
149
+ tooltip.addEventListener("mouseenter", () => tooltip.setAttribute("aria-hidden", false))
150
+ tooltip.addEventListener("mouseleave", removeTooltip)
151
+ }
132
152
  }
@@ -6,7 +6,7 @@
6
6
  --warning: #ad4910;
7
7
  --bg-warning: #ffeebd;
8
8
 
9
- @apply bg-background text-gray-2 rounded inline-flex items-center gap-1 px-2 font-semibold text-sm;
9
+ @apply bg-background text-gray-2 rounded inline-flex items-center gap-1 px-2 py-1 h-min font-semibold text-sm;
10
10
 
11
11
  &.success {
12
12
  @apply bg-[var(--bg-success)] text-[var(--success)];
@@ -45,7 +45,7 @@
45
45
 
46
46
  &__actions {
47
47
  &-main {
48
- @apply w-fit grid grid-cols-2 md:grid-cols-1 mx-auto gap-x-4 gap-y-6 md:gap-4;
48
+ @apply w-fit mx-auto gap-x-4 gap-y-6 md:gap-4;
49
49
 
50
50
  &__dropdown {
51
51
  @apply divide-y divide-gray-3 z-20 w-64;
@@ -29,7 +29,7 @@
29
29
  }
30
30
 
31
31
  &__sm {
32
- @apply w-16;
32
+ @apply w-16 pt-2;
33
33
  }
34
34
 
35
35
  &__sm &__number {
@@ -37,7 +37,6 @@
37
37
  }
38
38
 
39
39
  .conference-diploma .diploma__logo {
40
- border: 1px solid #333;
41
40
  padding: 2rem;
42
41
  }
43
42
 
@@ -47,10 +46,12 @@
47
46
  max-height: 100%;
48
47
  max-width: 100%;
49
48
  margin: 0;
49
+ padding-top: 10%;
50
50
  }
51
51
 
52
52
  .conference-diploma .diploma__border {
53
53
  margin: 0;
54
+ margin-top: 15%;
54
55
  -moz-border-image: url("../images/decidim/pattern.png") 20 repeat;
55
56
  -webkit-border-image: url("../images/decidim/pattern.png") 20 repeat;
56
57
  -o-border-image: url("../images/decidim/pattern.png") 20 repeat;
@@ -6,7 +6,7 @@ module Decidim
6
6
  #
7
7
  class AttachmentPresenter < SimpleDelegator
8
8
  def attachment_file_url
9
- attachment.attached_uploader(:file).url(host: attached_to.organization.host)
9
+ attachment.attached_uploader(:file).url
10
10
  end
11
11
 
12
12
  def attachment