decidim-core 0.27.5 → 0.27.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity_cell.rb +2 -2
  3. data/app/cells/decidim/card_cell.rb +2 -2
  4. data/app/cells/decidim/card_m/top.erb +1 -1
  5. data/app/cells/decidim/card_m_cell.rb +1 -1
  6. data/app/cells/decidim/follow_button_cell.rb +1 -1
  7. data/app/cells/decidim/notification/moderated.erb +24 -0
  8. data/app/cells/decidim/notification_cell.rb +5 -1
  9. data/app/cells/decidim/scopes_picker/scope_picker_values.erb +1 -1
  10. data/app/cells/decidim/tags_cell.rb +3 -1
  11. data/app/cells/decidim/user_profile_cell.rb +1 -1
  12. data/app/commands/decidim/create_omniauth_registration.rb +2 -4
  13. data/app/commands/decidim/messaging/reply_to_conversation.rb +3 -0
  14. data/app/commands/decidim/messaging/start_conversation.rb +3 -0
  15. data/app/controllers/concerns/decidim/devise_authentication_methods.rb +36 -0
  16. data/app/controllers/concerns/decidim/force_authentication.rb +1 -1
  17. data/app/controllers/concerns/decidim/paginable.rb +1 -1
  18. data/app/controllers/concerns/decidim/use_organization_time_zone.rb +1 -1
  19. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +1 -22
  20. data/app/controllers/decidim/devise/sessions_controller.rb +1 -24
  21. data/app/controllers/decidim/links_controller.rb +13 -1
  22. data/app/controllers/decidim/widgets_controller.rb +6 -0
  23. data/app/events/decidim/welcome_notification_event.rb +6 -9
  24. data/app/helpers/decidim/cells_paginate_helper.rb +1 -1
  25. data/app/helpers/decidim/check_boxes_tree_helper.rb +4 -4
  26. data/app/helpers/decidim/newsletters_helper.rb +83 -16
  27. data/app/helpers/decidim/resource_helper.rb +1 -1
  28. data/app/helpers/decidim/sanitize_helper.rb +9 -0
  29. data/app/helpers/decidim/user_profile_helper.rb +7 -2
  30. data/app/mailers/decidim/application_mailer.rb +40 -6
  31. data/app/mailers/decidim/messaging/conversation_mailer.rb +3 -72
  32. data/app/models/decidim/push_notification_message.rb +39 -0
  33. data/app/packs/images/decidim/.keep +0 -0
  34. data/app/packs/src/decidim/input_hashtags.js +1 -1
  35. data/app/packs/src/decidim/input_mentions.js +1 -1
  36. data/app/packs/src/decidim/input_multiple_mentions.js +1 -1
  37. data/app/packs/src/decidim/vizzs/index.js +1 -1
  38. data/app/packs/stylesheets/decidim/plugins/leaflet.scss +118 -114
  39. data/app/presenters/decidim/admin_log/oauth_application_resource_presenter.rb +1 -1
  40. data/app/presenters/decidim/admin_log/organization_presenter.rb +1 -1
  41. data/app/presenters/decidim/log/resource_presenter.rb +7 -1
  42. data/app/presenters/decidim/notification_to_mailer_presenter.rb +9 -0
  43. data/app/services/decidim/events_manager.rb +6 -0
  44. data/app/services/decidim/log/diff_changeset_calculator.rb +1 -1
  45. data/app/services/decidim/push_notification_message_sender.rb +36 -0
  46. data/app/services/decidim/send_push_notification.rb +22 -8
  47. data/app/views/decidim/devise/registrations/new.html.erb +2 -2
  48. data/app/views/decidim/links/new.html.erb +2 -0
  49. data/app/views/decidim/notifications_digest_mailer/_email_content.html.erb +7 -0
  50. data/app/views/decidim/shared/_address_details.html.erb +2 -2
  51. data/app/views/layouts/decidim/_js_configuration.html.erb +1 -0
  52. data/config/locales/ar.yml +4 -3
  53. data/config/locales/bg.yml +484 -1
  54. data/config/locales/ca.yml +23 -22
  55. data/config/locales/cs.yml +0 -1
  56. data/config/locales/de.yml +28 -27
  57. data/config/locales/el.yml +5 -4
  58. data/config/locales/en.yml +1 -0
  59. data/config/locales/eo.yml +3 -0
  60. data/config/locales/es-MX.yml +6 -5
  61. data/config/locales/es-PY.yml +6 -5
  62. data/config/locales/es.yml +25 -24
  63. data/config/locales/eu.yml +12 -2
  64. data/config/locales/fi-plain.yml +1 -0
  65. data/config/locales/fi.yml +4 -3
  66. data/config/locales/fr-CA.yml +5 -4
  67. data/config/locales/fr.yml +3 -2
  68. data/config/locales/ga-IE.yml +5 -0
  69. data/config/locales/gl.yml +3 -0
  70. data/config/locales/he-IL.yml +1 -0
  71. data/config/locales/hu.yml +41 -9
  72. data/config/locales/it.yml +5 -4
  73. data/config/locales/ja.yml +5 -3
  74. data/config/locales/lb.yml +5 -4
  75. data/config/locales/lt.yml +4 -4
  76. data/config/locales/lv.yml +4 -1
  77. data/config/locales/nl.yml +4 -1
  78. data/config/locales/no.yml +5 -4
  79. data/config/locales/pl.yml +168 -1
  80. data/config/locales/pt-BR.yml +113 -22
  81. data/config/locales/pt.yml +5 -4
  82. data/config/locales/ro-RO.yml +1 -3
  83. data/config/locales/ru.yml +13 -0
  84. data/config/locales/sk.yml +6 -1
  85. data/config/locales/sl.yml +5 -0
  86. data/config/locales/sv.yml +26 -3
  87. data/config/locales/tr-TR.yml +3 -3
  88. data/config/locales/uk.yml +15 -0
  89. data/config/locales/zh-CN.yml +0 -4
  90. data/config/locales/zh-TW.yml +4 -4
  91. data/decidim-core.gemspec +78 -0
  92. data/lib/decidim/acts_as_tree.rb +14 -1
  93. data/lib/decidim/asset_router/storage.rb +4 -0
  94. data/lib/decidim/attribute_encryptor.rb +6 -4
  95. data/lib/decidim/core/engine.rb +7 -3
  96. data/lib/decidim/core/test/factories.rb +314 -95
  97. data/lib/decidim/core/test/shared_examples/amendable/amendment_created_event_examples.rb +6 -26
  98. data/lib/decidim/core/test/shared_examples/amendable/amendment_promoted_event_examples.rb +8 -26
  99. data/lib/decidim/core/test/shared_examples/comments_examples.rb +32 -0
  100. data/lib/decidim/core/test/shared_examples/embed_resource_examples.rb +187 -11
  101. data/lib/decidim/core/test/shared_examples/has_attachment_collections.rb +8 -6
  102. data/lib/decidim/core/test/shared_examples/has_attachments.rb +4 -4
  103. data/lib/decidim/core/test/shared_examples/has_category.rb +27 -0
  104. data/lib/decidim/core/test/shared_examples/has_reference.rb +1 -1
  105. data/lib/decidim/core/test/shared_examples/has_space_in_mcell_examples.rb +1 -2
  106. data/lib/decidim/core/test/shared_examples/resource_endorsed_event_examples.rb +6 -3
  107. data/lib/decidim/core/test/shared_examples/resource_locator_presenter_examples.rb +134 -0
  108. data/lib/decidim/core/test/shared_examples/searchable_results_examples.rb +1 -1
  109. data/lib/decidim/core/test/shared_examples/simple_event.rb +50 -2
  110. data/lib/decidim/core/test.rb +1 -0
  111. data/lib/decidim/core/version.rb +1 -1
  112. data/lib/decidim/core.rb +1 -0
  113. data/lib/decidim/engine_router.rb +17 -4
  114. data/lib/decidim/events/base_event.rb +9 -2
  115. data/lib/decidim/events/simple_event.rb +3 -17
  116. data/lib/decidim/form_builder.rb +13 -1
  117. data/lib/decidim/has_category.rb +1 -1
  118. data/lib/decidim/has_conversations.rb +91 -0
  119. data/lib/decidim/organization_settings.rb +10 -2
  120. data/lib/decidim/participable.rb +17 -0
  121. data/lib/decidim/view_model.rb +1 -0
  122. data/lib/decidim/webpacker/webpack/.modernizrrc +9 -0
  123. data/lib/premailer/adapter/decidim.rb +5 -4
  124. data/lib/tasks/decidim_reminders_tasks.rake +1 -0
  125. data/lib/tasks/upgrade/decidim_fix_categorization.rake +15 -0
  126. metadata +29 -27
  127. data/config/environment.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9b09dc6caf48cd1318acfd39efaef6e9aa95ed3527ff40c3974a56f409eeff7
4
- data.tar.gz: bb6ca8ea6d9b57a37d6793757cd8a12bbcffdf63afd27924216f67af1d8acb14
3
+ metadata.gz: 71d8e583cc9dcd7338a03de09eb24a8a06a913f5e411b74bf16e5c42d9802639
4
+ data.tar.gz: c7028f8e3b04ab7c04afb94e6c74cbc114af7f734b5e65ff53d6a31bc0b6683e
5
5
  SHA512:
6
- metadata.gz: 4003252d8eeae3f86cb4056380f4fb7aefe8a8e97f3935e97a970513ccdd05e9d6b9ca8658b56147b7bff0e306608472e656db871eae5bbb50ab9cfc1e629d4b
7
- data.tar.gz: ce5aec4bd84ee34b87b5b7cc3f1afb08a1e6bcc3c34ff5cf1afa87d3ded13fe012f29a6a882fead250413f64db4a9b643770ca2c0e643e186ea51bb57a2e6c87
6
+ metadata.gz: f7df735fa2c671d915577eee036cb64b06c3ef85f4ecd1c95a65e22affb625f2a136c1596e55a7250f473397165f2364db867bad3287ecc3ffe5231e234cdc44
7
+ data.tar.gz: fd264b6e1db71ec2e9b1fb0cc31a73ddadf7c3e2a3dbe7209001f87f52a3840bae8f4bd406f5c84767460fea73fbcbf18a9f950b82d91d5b23c47b1bd02f60fa
@@ -38,9 +38,9 @@ module Decidim
38
38
 
39
39
  case resource_title
40
40
  when String
41
- resource_title
41
+ decidim_html_escape(resource_title)
42
42
  when Hash
43
- translated_attribute(resource_title)
43
+ decidim_escape_translated(resource_title)
44
44
  end
45
45
  end
46
46
 
@@ -28,11 +28,11 @@ module Decidim
28
28
  end
29
29
 
30
30
  def title
31
- model.try(:title) || model.try(:name) || ""
31
+ decidim_escape_translated(model.try(:title) || model.try(:name) || "")
32
32
  end
33
33
 
34
34
  def body
35
- model.try(:body) || model.try(:about) || ""
35
+ decidim_escape_translated(model.try(:body) || model.try(:about) || "")
36
36
  end
37
37
 
38
38
  def resource_manifest
@@ -1,7 +1,7 @@
1
1
  <div class="card__top">
2
2
  <% if render_space? %>
3
3
  <div class="card__content text-small">
4
- <span class="muted"><%= searchable_resource_human_name(model.participatory_space.class, count: 1) %>:</span>&nbsp;<%= link_to translated_attribute(model.participatory_space.title), Decidim::ResourceLocatorPresenter.new(model.participatory_space).path, class: "card__link text-ellipsis" %>
4
+ <span class="muted"><%= searchable_resource_human_name(model.participatory_space.class, count: 1) %>:</span>&nbsp;<%= link_to decidim_escape_translated(model.participatory_space.title), Decidim::ResourceLocatorPresenter.new(model.participatory_space).path, class: "card__link text-ellipsis" %>
5
5
  </div>
6
6
  <% end %>
7
7
  </div>
@@ -57,7 +57,7 @@ module Decidim
57
57
  end
58
58
 
59
59
  def title
60
- decidim_html_escape(translated_attribute(model.title))
60
+ decidim_escape_translated(model.title)
61
61
  end
62
62
 
63
63
  def description
@@ -46,7 +46,7 @@ module Decidim
46
46
 
47
47
  def render_screen_reader_title_for(resource)
48
48
  content_tag :span, class: "show-for-sr" do
49
- decidim_html_escape(resource_title(resource))
49
+ decidim_sanitize_translated(resource_title(resource))
50
50
  end
51
51
  end
52
52
 
@@ -0,0 +1,24 @@
1
+ <div class="card card--widget">
2
+ <ul class="card-data">
3
+ <li class="card-data__item">
4
+ <div class="card__link text-center">
5
+ <%= resource_icon notification.resource, class: "icon--large" %>
6
+ <span class="text-medium mt-xs" title="<%= l(notification.created_at) %>" data-tooltip="true" data-disable-hover="false">
7
+ <%= notification.created_at_in_words %>
8
+ </span>
9
+ </div>
10
+ </li>
11
+ <li class="card-data__item card-data__item--expand absolutes">
12
+ <div class="mr-s">
13
+ <span class="text-small"><%= notification.event_class.constantize.model_name.human %></span>
14
+ <br>
15
+ <%= t("decidim.notifications.show.moderated") %>
16
+ </div>
17
+ <div class="right center mr-s">
18
+ <%= link_to model, remote: true, method: :delete, class: "mark-as-read-button" do %>
19
+ <%= icon "circle-x", class: "card__link", aria_label: t("mark_as_read", scope: "layouts.decidim.notifications_dashboard"), role: "img" %>
20
+ <% end %>
21
+ </div>
22
+ </li>
23
+ </ul>
24
+ </div>
@@ -8,7 +8,11 @@ module Decidim
8
8
  include Decidim::Core::Engine.routes.url_helpers
9
9
 
10
10
  def show
11
- render :show
11
+ if notification.event_class_instance.try(:hidden_resource?)
12
+ render :moderated
13
+ else
14
+ render :show
15
+ end
12
16
  end
13
17
 
14
18
  def notification_title
@@ -1,5 +1,5 @@
1
1
  <div class="picker-values">
2
2
  <%- scopes.each do |scope, params| %>
3
- <div><%= link_to params[:text], params[:url], data: { picker_value: scope.id } %></div>
3
+ <div><%= link_to decidim_html_escape(params[:text]), params[:url], data: { picker_value: scope.id } %></div>
4
4
  <% end %>
5
5
  </div>
@@ -9,6 +9,8 @@ module Decidim
9
9
  # <%= cell("decidim/category", model.category, context: {resource: model}) %>
10
10
  #
11
11
  class TagsCell < Decidim::ViewModel
12
+ include Decidim::SanitizeHelper
13
+
12
14
  def show
13
15
  render if category? || scope?
14
16
  end
@@ -51,7 +53,7 @@ module Decidim
51
53
  end
52
54
 
53
55
  def category_name
54
- model.category.translated_name
56
+ decidim_html_escape model.category.translated_name
55
57
  end
56
58
 
57
59
  def category_path
@@ -29,7 +29,7 @@ module Decidim
29
29
  delegate :badge, to: :presented_resource
30
30
 
31
31
  def description
32
- html_truncate(decidim_html_escape(user.about.to_s), length: 100)
32
+ html_truncate(decidim_escape_translated(user.about), length: 100)
33
33
  end
34
34
 
35
35
  def avatar
@@ -57,14 +57,12 @@ module Decidim
57
57
  # to be marked confirmed.
58
58
  @user.skip_confirmation! if !@user.confirmed? && @user.email == verified_email
59
59
  else
60
- generated_password = SecureRandom.hex
61
-
62
60
  @user.email = (verified_email || form.email)
63
61
  @user.name = form.name
64
62
  @user.nickname = form.normalized_nickname
65
63
  @user.newsletter_notifications_at = nil
66
- @user.password = generated_password
67
- @user.password_confirmation = generated_password
64
+ @user.password = SecureRandom.hex
65
+ @user.password_confirmation = @user.password
68
66
  if form.avatar_url.present?
69
67
  url = URI.parse(form.avatar_url)
70
68
  filename = File.basename(url.path)
@@ -54,11 +54,13 @@ module Decidim
54
54
  notify(manager) do
55
55
  ConversationMailer.new_group_message(sender, manager, conversation, message, recipient).deliver_later
56
56
  end
57
+ Decidim::PushNotificationMessageSender.new.new_group_message(sender, manager, conversation, message, recipient).deliver
57
58
  end
58
59
  else
59
60
  notify(recipient) do
60
61
  ConversationMailer.new_message(sender, recipient, conversation, message).deliver_later
61
62
  end
63
+ Decidim::PushNotificationMessageSender.new.new_message(sender, recipient, conversation, message).deliver
62
64
  end
63
65
  end
64
66
  end
@@ -68,6 +70,7 @@ module Decidim
68
70
  notify(recipient) do
69
71
  ConversationMailer.comanagers_new_message(sender, recipient, conversation, message, form.context.current_user).deliver_later
70
72
  end
73
+ Decidim::PushNotificationMessageSender.new.comanagers_new_message(sender, recipient, conversation, message, form.context.current_user).deliver
71
74
  end
72
75
  end
73
76
 
@@ -54,11 +54,13 @@ module Decidim
54
54
  notify(manager) do
55
55
  ConversationMailer.new_group_conversation(originator, manager, conversation, recipient).deliver_later
56
56
  end
57
+ Decidim::PushNotificationMessageSender.new.new_group_conversation(originator, manager, conversation, recipient).deliver
57
58
  end
58
59
  else
59
60
  notify(recipient) do
60
61
  ConversationMailer.new_conversation(originator, recipient, conversation).deliver_later
61
62
  end
63
+ Decidim::PushNotificationMessageSender.new.new_conversation(originator, recipient, conversation).deliver
62
64
  end
63
65
  end
64
66
  end
@@ -68,6 +70,7 @@ module Decidim
68
70
  notify(recipient) do
69
71
  ConversationMailer.comanagers_new_conversation(originator, recipient, conversation, form.context.current_user).deliver_later
70
72
  end
73
+ Decidim::PushNotificationMessageSender.new.comanagers_new_conversation(originator, recipient, conversation, form.context.current_user).deliver
71
74
  end
72
75
  end
73
76
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module DeviseAuthenticationMethods
7
+ extend ActiveSupport::Concern
8
+ include Decidim::UserBlockedChecker
9
+
10
+ included do
11
+ def after_sign_in_path_for(user)
12
+ if user.present? && user.blocked?
13
+ check_user_block_status(user)
14
+ elsif user.needs_password_update?
15
+ change_password_path
16
+ elsif first_login_and_not_authorized?(user) && !user.admin? && !pending_redirect?(user)
17
+ decidim_verifications.first_login_authorizations_path
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ # Calling the `stored_location_for` method removes the key, so in order
24
+ # to check if there is any pending redirect after login I need to call
25
+ # this method and use the value to set a pending redirect. This is the
26
+ # only way to do this without checking the session directly.
27
+ def pending_redirect?(user)
28
+ store_location_for(user, stored_location_for(user))
29
+ end
30
+
31
+ def first_login_and_not_authorized?(user)
32
+ user.is_a?(User) && user.sign_in_count == 1 && current_organization.available_authorizations.any? && user.verifiable?
33
+ end
34
+ end
35
+ end
36
+ end
@@ -17,7 +17,7 @@ module Decidim
17
17
  # Breaks the request lifecycle, if user is not authenticated.
18
18
  # Otherwise returns.
19
19
  def ensure_authenticated!
20
- return true unless current_organization.force_users_to_authenticate_before_access_organization
20
+ return true unless current_organization&.force_users_to_authenticate_before_access_organization
21
21
 
22
22
  # Next stop: Let's check whether auth is ok
23
23
  unless user_signed_in?
@@ -19,7 +19,7 @@ module Decidim
19
19
 
20
20
  def per_page
21
21
  if OPTIONS.include?(params[:per_page])
22
- params[:per_page]
22
+ params[:per_page].to_i
23
23
  elsif params[:per_page]
24
24
  sorted = OPTIONS.sort
25
25
  params[:per_page].to_i.clamp(sorted.first, sorted.last)
@@ -25,7 +25,7 @@ module Decidim
25
25
  #
26
26
  # Returns a String.
27
27
  def organization_time_zone
28
- @organization_time_zone ||= current_organization.time_zone
28
+ @organization_time_zone ||= current_organization&.time_zone
29
29
  end
30
30
  end
31
31
  end
@@ -6,6 +6,7 @@ module Decidim
6
6
  class OmniauthRegistrationsController < ::Devise::OmniauthCallbacksController
7
7
  include FormFactory
8
8
  include Decidim::DeviseControllers
9
+ include Decidim::DeviseAuthenticationMethods
9
10
 
10
11
  def new
11
12
  @form = form(OmniauthRegistrationForm).from_params(params[:user])
@@ -45,28 +46,6 @@ module Decidim
45
46
  end
46
47
  end
47
48
 
48
- def after_sign_in_path_for(user)
49
- if user.present? && user.blocked?
50
- check_user_block_status(user)
51
- elsif !pending_redirect?(user) && first_login_and_not_authorized?(user)
52
- decidim_verifications.authorizations_path
53
- else
54
- super
55
- end
56
- end
57
-
58
- # Calling the `stored_location_for` method removes the key, so in order
59
- # to check if there's any pending redirect after login I need to call
60
- # this method and use the value to set a pending redirect. This is the
61
- # only way to do this without checking the session directly.
62
- def pending_redirect?(user)
63
- store_location_for(user, stored_location_for(user))
64
- end
65
-
66
- def first_login_and_not_authorized?(user)
67
- user.is_a?(User) && user.sign_in_count == 1 && Decidim::Verifications.workflows.any? && user.verifiable?
68
- end
69
-
70
49
  def action_missing(action_name)
71
50
  return send(:create) if devise_mapping.omniauthable? && current_organization.enabled_omniauth_providers.keys.include?(action_name.to_sym)
72
51
 
@@ -5,6 +5,7 @@ module Decidim
5
5
  # Custom Devise SessionsController to avoid namespace problems.
6
6
  class SessionsController < ::Devise::SessionsController
7
7
  include Decidim::DeviseControllers
8
+ include Decidim::DeviseAuthenticationMethods
8
9
 
9
10
  before_action :check_sign_in_enabled, only: :create
10
11
 
@@ -35,30 +36,6 @@ module Decidim
35
36
  end
36
37
  end
37
38
 
38
- def after_sign_in_path_for(user)
39
- if user.present? && user.blocked?
40
- check_user_block_status(user)
41
- elsif user.needs_password_update?
42
- change_password_path
43
- elsif first_login_and_not_authorized?(user) && !user.admin? && !pending_redirect?(user)
44
- decidim_verifications.first_login_authorizations_path
45
- else
46
- super
47
- end
48
- end
49
-
50
- # Calling the `stored_location_for` method removes the key, so in order
51
- # to check if there's any pending redirect after login I need to call
52
- # this method and use the value to set a pending redirect. This is the
53
- # only way to do this without checking the session directly.
54
- def pending_redirect?(user)
55
- store_location_for(user, stored_location_for(user))
56
- end
57
-
58
- def first_login_and_not_authorized?(user)
59
- user.is_a?(User) && user.sign_in_count == 1 && current_organization.available_authorizations.any? && user.verifiable?
60
- end
61
-
62
39
  def after_sign_out_path_for(user)
63
40
  request.referer || super
64
41
  end
@@ -35,7 +35,19 @@ module Decidim
35
35
  end
36
36
 
37
37
  def external_url
38
- @external_url ||= URI.parse(URI::Parser.new.escape(params[:external_url]))
38
+ @external_url ||= URI.parse(escape_url(params[:external_url]))
39
+ end
40
+
41
+ def escape_url(external_url)
42
+ before_fragment, fragment = external_url.split("#", 2)
43
+ escaped_before_fragment = URI::Parser.new.escape(before_fragment)
44
+
45
+ if fragment
46
+ escaped_fragment = URI::Parser.new.escape(fragment)
47
+ "#{escaped_before_fragment}##{escaped_fragment}"
48
+ else
49
+ escaped_before_fragment
50
+ end
39
51
  end
40
52
  end
41
53
  end
@@ -11,6 +11,8 @@ module Decidim
11
11
  helper_method :model, :iframe_url, :current_participatory_space
12
12
 
13
13
  def show
14
+ raise ActionController::RoutingError, "Not Found" if model.nil?
15
+
14
16
  respond_to do |format|
15
17
  format.js { render "decidim/widgets/show" }
16
18
  format.html
@@ -19,6 +21,10 @@ module Decidim
19
21
 
20
22
  private
21
23
 
24
+ def current_component
25
+ @current_component ||= request.env["decidim.current_component"]
26
+ end
27
+
22
28
  def current_participatory_space
23
29
  @current_participatory_space ||= model.component.participatory_space
24
30
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "mustache"
4
-
5
3
  module Decidim
6
4
  class WelcomeNotificationEvent < Decidim::Events::BaseEvent
7
5
  include Decidim::Events::EmailEvent
@@ -46,13 +44,12 @@ module Decidim
46
44
  private
47
45
 
48
46
  def interpolate(template)
49
- Mustache.render(
50
- template.to_s,
51
- organization: organization.name,
52
- name: user.name,
53
- help_url: url_helpers.pages_url(host: organization.host),
54
- badges_url: url_helpers.gamification_badges_url(host: organization.host)
55
- ).html_safe
47
+ template
48
+ .gsub("{{name}}", user.name)
49
+ .gsub("{{organization}}", organization.name)
50
+ .gsub("{{help_url}}", url_helpers.pages_url(host: organization.host))
51
+ .gsub("{{badges_url}}", url_helpers.gamification_badges_url(host: organization.host))
52
+ .html_safe
56
53
  end
57
54
  end
58
55
  end
@@ -14,7 +14,7 @@ module Decidim
14
14
  end
15
15
 
16
16
  def per_page
17
- params[:per_page] || Decidim::Paginable::OPTIONS.first
17
+ params[:per_page].to_i || Decidim::Paginable::OPTIONS.first
18
18
  end
19
19
  end
20
20
  end
@@ -50,20 +50,20 @@ module Decidim
50
50
  organization = current_participatory_space.organization
51
51
 
52
52
  sorted_main_categories = current_participatory_space.categories.first_class.includes(:subcategories).sort_by do |category|
53
- [category.weight, translated_attribute(category.name, organization)]
53
+ [category.weight, decidim_html_escape(translated_attribute(category.name, organization))]
54
54
  end
55
55
 
56
56
  categories_values = sorted_main_categories.flat_map do |category|
57
57
  sorted_descendant_categories = category.descendants.includes(:subcategories).sort_by do |subcategory|
58
- [subcategory.weight, translated_attribute(subcategory.name, organization)]
58
+ [subcategory.weight, decidim_html_escape(translated_attribute(subcategory.name, organization))]
59
59
  end
60
60
 
61
61
  subcategories = sorted_descendant_categories.flat_map do |subcategory|
62
- TreePoint.new(subcategory.id.to_s, translated_attribute(subcategory.name, organization))
62
+ TreePoint.new(subcategory.id.to_s, decidim_html_escape(translated_attribute(subcategory.name, organization)))
63
63
  end
64
64
 
65
65
  TreeNode.new(
66
- TreePoint.new(category.id.to_s, translated_attribute(category.name, organization)),
66
+ TreePoint.new(category.id.to_s, decidim_html_escape(translated_attribute(category.name, organization))),
67
67
  subcategories
68
68
  )
69
69
  end
@@ -8,28 +8,28 @@ module Decidim
8
8
  # for example transform "https://es.lipsum.com/" to "https://es.lipsum.com/?utm_source=localhost&utm_campaign=newsletter_11"
9
9
  # And replace "%{name}" on the subject or content of newsletter to the user Name
10
10
  # for example transform "%{name}" to "User Name"
11
+ #
12
+ # @param content [String] - the string to convert
13
+ # @param user [Decidim::User] - the user to replace
14
+ # @param id [Integer] - the id of the newsletter to change
15
+ #
16
+ # @return [String] - the content converted
11
17
  def parse_interpolations(content, user = nil, id = nil)
12
- if Decidim.config.track_newsletter_links && id.present? && user.present?
13
- host = user.organization.host.to_s
14
- campaign = "newsletter_#{id}"
18
+ host = user&.organization&.host&.to_s
15
19
 
16
- links = content.scan(/href\s*=\s*"([^"]*)"/)
17
-
18
- links.each do |link|
19
- link_replaced = link.first + utm_codes(host, campaign)
20
- content = content.gsub(/href\s*=\s*"([^"]*#{link.first})"/, %(href="#{link_replaced}"))
21
- end
22
- end
23
-
24
- if user.present?
25
- content.gsub("%{name}", user.name)
26
- else
27
- content.gsub("%{name}", "")
28
- end
20
+ content = interpret_name(content, user)
21
+ content = track_newsletter_links(content, id, host)
22
+ transform_image_urls(content, host)
29
23
  end
30
24
 
31
25
  # this method is used to generate the root link on mail with the utm_codes
32
26
  # If the newsletter_id is nil, it returns the root_url
27
+ #
28
+ # @param organization [Decidim::Organization] - the Organization of this newsletter
29
+ # @param newsletter_id [Integer] - the id of the newsletter
30
+ #
31
+ # @return [String] - the root_url converted
32
+ #
33
33
  def custom_url_for_mail_root(organization, newsletter_id = nil)
34
34
  decidim = EngineRouter.new("decidim", {})
35
35
  if newsletter_id.present?
@@ -39,10 +39,77 @@ module Decidim
39
39
  end
40
40
  end
41
41
 
42
+ private
43
+
42
44
  # Method to specify the utm_codes.
43
45
  # You can change or add utm_codes for track
46
+ #
47
+ # @param host [String] - the Decidim::Organization host add to the URL
48
+ # @param newsletter_id [String] - the ID of the newsletter
49
+ #
50
+ # @return [String] - the UTM codes to be added
51
+ #
44
52
  def utm_codes(host, newsletter_id)
45
53
  "?utm_source=#{host}&utm_campaign=#{newsletter_id}"
46
54
  end
55
+
56
+ # Interpret placeholder '%{name}' and replace by the user name
57
+ # If user is not define, it returns content with blank instead of the placeholder
58
+ #
59
+ # @param content [String] - the string to convert
60
+ # @param user [Decidim::User] - the user to replace
61
+ #
62
+ # @return [String] - the content converted
63
+ #
64
+ def interpret_name(content, user)
65
+ return content.gsub("%{name}", "") if user.blank?
66
+
67
+ content.gsub("%{name}", user.name)
68
+ end
69
+
70
+ # Find each img HTML tag with relative path in src attribute
71
+ # For each URL, prepends the decidim.root_url
72
+ # If host is not defined it returns full content
73
+ #
74
+ # @param content [String] - the string to convert
75
+ # @param host [String] - the Decidim::Organization host to replace
76
+ #
77
+ # @return [String] - the content converted
78
+ #
79
+ def transform_image_urls(content, host)
80
+ return content if host.blank?
81
+
82
+ content.scan(/src\s*=\s*"([^"]*)"/).each do |src|
83
+ root_url = decidim.root_url(host: host)[0..-2]
84
+ src_replaced = "#{root_url}#{src.first}"
85
+ content = content.gsub(/src\s*=\s*"([^"]*#{src.first})"/, %(src="#{src_replaced}"))
86
+ end
87
+
88
+ content
89
+ end
90
+
91
+ # Add tracking query params to each links
92
+ #
93
+ # @param content [String] - the string to convert
94
+ # @param id [Integer] - the id of the newsletter
95
+ # @param host [String] - the Decidim::Organization host
96
+ #
97
+ # @return [String] - the content converted
98
+ #
99
+ def track_newsletter_links(content, id, host)
100
+ return content unless Decidim.config.track_newsletter_links
101
+ return content if id.blank?
102
+ return content if host.blank?
103
+
104
+ campaign = "newsletter_#{id}"
105
+ links = content.scan(/href\s*=\s*"([^"]*)"/)
106
+
107
+ links.each do |link|
108
+ link_replaced = link.first + utm_codes(host, campaign)
109
+ content = content.gsub(/href\s*=\s*"([^"]*#{link.first})"/, %(href="#{link_replaced}"))
110
+ end
111
+
112
+ content
113
+ end
47
114
  end
48
115
  end
@@ -75,7 +75,7 @@ module Decidim
75
75
  # Returns a descriptive title for the resource
76
76
  def resource_title(resource)
77
77
  title = resource.try(:title) || resource.try(:name) || resource.try(:subject) || "#{resource.model_name.human} ##{resource.id}"
78
- title = translated_attribute(title) if title.is_a?(Hash)
78
+ title = decidim_escape_translated(title) if title.is_a?(Hash)
79
79
  title
80
80
  end
81
81
  end
@@ -6,6 +6,7 @@ module Decidim
6
6
  def self.included(base)
7
7
  base.include ActionView::Helpers::SanitizeHelper
8
8
  base.include ActionView::Helpers::TagHelper
9
+ base.include Decidim::TranslatableAttributes
9
10
  end
10
11
 
11
12
  # Public: It sanitizes a user-inputted string with the
@@ -53,6 +54,14 @@ module Decidim
53
54
  decidim_html_escape(text).sub(/^javascript:/, "")
54
55
  end
55
56
 
57
+ def decidim_sanitize_translated(text)
58
+ decidim_sanitize(translated_attribute(text))
59
+ end
60
+
61
+ def decidim_escape_translated(text)
62
+ decidim_html_escape(translated_attribute(text))
63
+ end
64
+
56
65
  private
57
66
 
58
67
  # Maintains the paragraphs and lists separations with their bullet points and
@@ -14,9 +14,14 @@ module Decidim
14
14
  #
15
15
  # Returns a String with the menu tab.
16
16
  def user_profile_tab(text, link, options = {})
17
- active = is_active_link?(link, (options[:aria_link_type] || :inclusive))
17
+ aria = {}
18
+ cls = %w(tabs-title)
19
+ if is_active_link?(link, (options[:aria_link_type] || :inclusive))
20
+ cls << "is-active"
21
+ aria[:current] = "page"
22
+ end
18
23
 
19
- content_tag(:li, class: "tabs-title#{active ? " is-active" : nil}") do
24
+ content_tag(:li, class: cls.join(" "), aria: aria) do
20
25
  link_to(text, link, options)
21
26
  end
22
27
  end