decidim-core 0.27.5 → 0.27.6

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 (106) 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/scopes_picker/scope_picker_values.erb +1 -1
  7. data/app/cells/decidim/tags_cell.rb +3 -1
  8. data/app/cells/decidim/user_profile_cell.rb +1 -1
  9. data/app/commands/decidim/create_omniauth_registration.rb +2 -4
  10. data/app/commands/decidim/messaging/reply_to_conversation.rb +3 -0
  11. data/app/commands/decidim/messaging/start_conversation.rb +3 -0
  12. data/app/controllers/concerns/decidim/devise_authentication_methods.rb +36 -0
  13. data/app/controllers/concerns/decidim/paginable.rb +1 -1
  14. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +1 -22
  15. data/app/controllers/decidim/devise/sessions_controller.rb +1 -24
  16. data/app/controllers/decidim/widgets_controller.rb +6 -0
  17. data/app/events/decidim/welcome_notification_event.rb +6 -9
  18. data/app/helpers/decidim/cells_paginate_helper.rb +1 -1
  19. data/app/helpers/decidim/check_boxes_tree_helper.rb +4 -4
  20. data/app/helpers/decidim/newsletters_helper.rb +83 -16
  21. data/app/helpers/decidim/resource_helper.rb +1 -1
  22. data/app/helpers/decidim/sanitize_helper.rb +9 -0
  23. data/app/helpers/decidim/user_profile_helper.rb +7 -2
  24. data/app/mailers/decidim/messaging/conversation_mailer.rb +3 -72
  25. data/app/models/decidim/push_notification_message.rb +39 -0
  26. data/app/packs/images/decidim/.keep +0 -0
  27. data/app/packs/src/decidim/input_hashtags.js +1 -1
  28. data/app/packs/src/decidim/input_mentions.js +1 -1
  29. data/app/packs/src/decidim/input_multiple_mentions.js +1 -1
  30. data/app/packs/src/decidim/vizzs/index.js +1 -1
  31. data/app/packs/stylesheets/decidim/plugins/leaflet.scss +118 -114
  32. data/app/presenters/decidim/admin_log/oauth_application_resource_presenter.rb +1 -1
  33. data/app/presenters/decidim/notification_to_mailer_presenter.rb +9 -0
  34. data/app/services/decidim/events_manager.rb +6 -0
  35. data/app/services/decidim/push_notification_message_sender.rb +36 -0
  36. data/app/services/decidim/send_push_notification.rb +22 -8
  37. data/app/views/decidim/devise/registrations/new.html.erb +2 -2
  38. data/app/views/decidim/notifications_digest_mailer/_email_content.html.erb +7 -0
  39. data/app/views/decidim/shared/_address_details.html.erb +2 -2
  40. data/app/views/layouts/decidim/_js_configuration.html.erb +1 -0
  41. data/config/locales/ar.yml +4 -2
  42. data/config/locales/bg.yml +74 -0
  43. data/config/locales/ca.yml +22 -22
  44. data/config/locales/de.yml +25 -25
  45. data/config/locales/el.yml +1 -3
  46. data/config/locales/es-MX.yml +1 -1
  47. data/config/locales/es-PY.yml +1 -1
  48. data/config/locales/es.yml +20 -20
  49. data/config/locales/eu.yml +11 -2
  50. data/config/locales/fi.yml +3 -3
  51. data/config/locales/fr-CA.yml +2 -2
  52. data/config/locales/fr.yml +2 -2
  53. data/config/locales/gl.yml +3 -0
  54. data/config/locales/he-IL.yml +1 -0
  55. data/config/locales/hu.yml +41 -8
  56. data/config/locales/it.yml +1 -3
  57. data/config/locales/ja.yml +3 -2
  58. data/config/locales/lb.yml +1 -3
  59. data/config/locales/lt.yml +0 -3
  60. data/config/locales/no.yml +1 -3
  61. data/config/locales/pl.yml +137 -1
  62. data/config/locales/pt-BR.yml +113 -21
  63. data/config/locales/pt.yml +1 -3
  64. data/config/locales/ro-RO.yml +0 -3
  65. data/config/locales/ru.yml +9 -0
  66. data/config/locales/sk.yml +2 -0
  67. data/config/locales/sv.yml +22 -2
  68. data/config/locales/tr-TR.yml +1 -3
  69. data/config/locales/uk.yml +14 -0
  70. data/config/locales/zh-CN.yml +0 -3
  71. data/config/locales/zh-TW.yml +0 -3
  72. data/decidim-core.gemspec +78 -0
  73. data/lib/decidim/acts_as_tree.rb +14 -1
  74. data/lib/decidim/asset_router/storage.rb +4 -0
  75. data/lib/decidim/attribute_encryptor.rb +6 -4
  76. data/lib/decidim/core/engine.rb +7 -3
  77. data/lib/decidim/core/test/factories.rb +308 -95
  78. data/lib/decidim/core/test/shared_examples/amendable/amendment_created_event_examples.rb +6 -26
  79. data/lib/decidim/core/test/shared_examples/amendable/amendment_promoted_event_examples.rb +8 -26
  80. data/lib/decidim/core/test/shared_examples/comments_examples.rb +32 -0
  81. data/lib/decidim/core/test/shared_examples/embed_resource_examples.rb +187 -11
  82. data/lib/decidim/core/test/shared_examples/has_attachment_collections.rb +8 -6
  83. data/lib/decidim/core/test/shared_examples/has_attachments.rb +4 -4
  84. data/lib/decidim/core/test/shared_examples/has_category.rb +27 -0
  85. data/lib/decidim/core/test/shared_examples/has_reference.rb +1 -1
  86. data/lib/decidim/core/test/shared_examples/has_space_in_mcell_examples.rb +1 -2
  87. data/lib/decidim/core/test/shared_examples/resource_endorsed_event_examples.rb +6 -3
  88. data/lib/decidim/core/test/shared_examples/resource_locator_presenter_examples.rb +134 -0
  89. data/lib/decidim/core/test/shared_examples/searchable_results_examples.rb +1 -1
  90. data/lib/decidim/core/test/shared_examples/simple_event.rb +50 -2
  91. data/lib/decidim/core/test.rb +1 -0
  92. data/lib/decidim/core/version.rb +1 -1
  93. data/lib/decidim/core.rb +1 -0
  94. data/lib/decidim/engine_router.rb +17 -4
  95. data/lib/decidim/events/base_event.rb +5 -2
  96. data/lib/decidim/events/simple_event.rb +3 -17
  97. data/lib/decidim/has_category.rb +1 -1
  98. data/lib/decidim/has_conversations.rb +91 -0
  99. data/lib/decidim/participable.rb +17 -0
  100. data/lib/decidim/view_model.rb +1 -0
  101. data/lib/decidim/webpacker/webpack/.modernizrrc +9 -0
  102. data/lib/premailer/adapter/decidim.rb +5 -4
  103. data/lib/tasks/decidim_reminders_tasks.rake +1 -0
  104. data/lib/tasks/upgrade/decidim_fix_categorization.rake +15 -0
  105. metadata +31 -30
  106. 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: 99119ce695e1aa34d4a744deca10b822209b52253058e77548a6399cd78f0069
4
+ data.tar.gz: '052746495077e884e4cb8e5bc33c8bcb6af257efbee57b385b39f62404530fd5'
5
5
  SHA512:
6
- metadata.gz: 4003252d8eeae3f86cb4056380f4fb7aefe8a8e97f3935e97a970513ccdd05e9d6b9ca8658b56147b7bff0e306608472e656db871eae5bbb50ab9cfc1e629d4b
7
- data.tar.gz: ce5aec4bd84ee34b87b5b7cc3f1afb08a1e6bcc3c34ff5cf1afa87d3ded13fe012f29a6a882fead250413f64db4a9b643770ca2c0e643e186ea51bb57a2e6c87
6
+ metadata.gz: e5b7a3d7779697415e7ec9badd7d4d00bd408a1f42e7c667f01b797558ba22524231273b94f3f6beeb644f7aee8692bd5d2d580a7493c84dfaa5eb563241bc58
7
+ data.tar.gz: 193b200909cc1fa784ccad5583b3c9593b8e6a9be31c10ab03c90ee9153075ac0d168566e7fe275bb9140c1bc53a8a42e58f84ffc9b852fe91269deef496d5a3
@@ -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
@@ -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
@@ -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)
@@ -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
@@ -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
@@ -5,74 +5,12 @@ module Decidim
5
5
  # A custom mailer for sending notifications to users when they receive
6
6
  # private messages
7
7
  class ConversationMailer < Decidim::ApplicationMailer
8
- def new_conversation(originator, user, conversation)
9
- notification_mail(
10
- from: originator,
11
- to: user,
12
- conversation: conversation,
13
- message: conversation.messages.first.body,
14
- action: "new_conversation"
15
- )
16
- end
17
-
18
- def new_group_conversation(originator, manager, conversation, group)
19
- notification_mail(
20
- from: originator,
21
- to: manager,
22
- conversation: conversation,
23
- message: conversation.messages.first.body,
24
- action: "new_group_conversation",
25
- third_party: group
26
- )
27
- end
28
-
29
- def comanagers_new_conversation(group, user, conversation, manager)
30
- notification_mail(
31
- from: group,
32
- to: user,
33
- conversation: conversation,
34
- message: conversation.messages.first.body,
35
- action: "comanagers_new_conversation",
36
- third_party: manager
37
- )
38
- end
39
-
40
- def new_message(sender, user, conversation, message)
41
- notification_mail(
42
- from: sender,
43
- to: user,
44
- conversation: conversation,
45
- message: message.body,
46
- action: "new_message"
47
- )
48
- end
49
-
50
- def new_group_message(sender, user, conversation, message, group)
51
- notification_mail(
52
- from: sender,
53
- to: user,
54
- conversation: conversation,
55
- message: message.body,
56
- action: "new_group_message",
57
- third_party: group
58
- )
59
- end
60
-
61
- def comanagers_new_message(sender, user, conversation, message, manager)
62
- notification_mail(
63
- from: sender,
64
- to: user,
65
- conversation: conversation,
66
- message: message.body,
67
- action: "comanagers_new_message",
68
- third_party: manager
69
- )
70
- end
8
+ include HasConversations
71
9
 
72
10
  private
73
11
 
74
12
  # rubocop:disable Metrics/ParameterLists
75
- def notification_mail(from:, to:, conversation:, action:, message: nil, third_party: nil)
13
+ def send_notification(from:, to:, conversation:, action:, message: nil, third_party: nil)
76
14
  with_user(to) do
77
15
  @organization = to.organization
78
16
  @conversation = conversation
@@ -81,14 +19,7 @@ module Decidim
81
19
  @third_party = third_party
82
20
  @message = message
83
21
  @host = @organization.host
84
-
85
- subject = I18n.t(
86
- "conversation_mailer.#{action}.subject",
87
- scope: "decidim.messaging",
88
- sender: @sender.name,
89
- manager: @third_party&.name,
90
- group: @third_party&.name
91
- )
22
+ subject = get_subject(action: action, sender: @sender, third_party: @third_party)
92
23
 
93
24
  mail(to: to.email, subject: subject)
94
25
  end