decidim-core 0.26.3 → 0.26.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of decidim-core might be problematic. Click here for more details.

Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/amendable/announcement_cell.rb +1 -1
  3. data/app/cells/decidim/card_m_cell.rb +1 -1
  4. data/app/cells/decidim/newsletter_templates/base_cell.rb +8 -0
  5. data/app/cells/decidim/newsletter_templates/basic_only_text/show.erb +4 -4
  6. data/app/cells/decidim/newsletter_templates/image_text_cta/show.erb +4 -4
  7. data/app/commands/decidim/unendorse_resource.rb +1 -1
  8. data/app/controllers/decidim/devise/invitations_controller.rb +9 -2
  9. data/app/controllers/decidim/devise/registrations_controller.rb +4 -0
  10. data/app/controllers/decidim/groups_controller.rb +5 -0
  11. data/app/controllers/decidim/links_controller.rb +4 -2
  12. data/app/controllers/decidim/profiles_controller.rb +1 -1
  13. data/app/forms/decidim/account_form.rb +3 -3
  14. data/app/forms/decidim/amendable/form.rb +2 -1
  15. data/app/forms/decidim/registration_form.rb +3 -3
  16. data/app/helpers/decidim/icon_helper.rb +3 -3
  17. data/app/helpers/decidim/newsletters_helper.rb +1 -0
  18. data/app/mailers/decidim/newsletter_mailer.rb +10 -3
  19. data/app/mailers/decidim/notification_mailer.rb +1 -0
  20. data/app/models/decidim/newsletter.rb +28 -0
  21. data/app/models/decidim/user.rb +0 -2
  22. data/app/models/decidim/user_base_entity.rb +2 -0
  23. data/app/models/decidim/user_block.rb +2 -2
  24. data/app/models/decidim/user_group.rb +1 -1
  25. data/app/packs/src/decidim/editor/clipboard_override.js +143 -0
  26. data/app/packs/src/decidim/editor/clipboard_utilities.js +119 -0
  27. data/app/packs/src/decidim/editor/linebreak_module.js +0 -8
  28. data/app/packs/src/decidim/editor.js +9 -2
  29. data/app/packs/src/decidim/form_filter.component.test.js +148 -5
  30. data/app/packs/src/decidim/form_filter.js +26 -4
  31. data/app/packs/stylesheets/decidim/email.scss +7 -0
  32. data/app/presenters/decidim/admin_log/user_group_presenter.rb +1 -1
  33. data/app/presenters/decidim/admin_log/user_moderation_presenter.rb +1 -1
  34. data/app/presenters/decidim/home_stats_presenter.rb +11 -4
  35. data/app/presenters/decidim/stats_presenter.rb +7 -8
  36. data/app/presenters/decidim/user_presenter.rb +9 -4
  37. data/app/services/decidim/activity_search.rb +1 -0
  38. data/app/validators/etiquette_validator.rb +7 -3
  39. data/app/views/decidim/newsletter_mailer/newsletter.html.erb +3 -3
  40. data/app/views/decidim/newsletters/show.html.erb +1 -1
  41. data/app/views/decidim/notification_mailer/event_received.html.erb +1 -1
  42. data/app/views/layouts/decidim/_mailer_logo.html.erb +2 -2
  43. data/app/views/layouts/decidim/newsletter_base.html.erb +2 -2
  44. data/config/locales/ar.yml +5 -26
  45. data/config/locales/bg.yml +5 -26
  46. data/config/locales/ca.yml +17 -33
  47. data/config/locales/cs.yml +9 -26
  48. data/config/locales/de.yml +2 -27
  49. data/config/locales/el.yml +4 -27
  50. data/config/locales/en.yml +8 -24
  51. data/config/locales/es-MX.yml +10 -26
  52. data/config/locales/es-PY.yml +10 -26
  53. data/config/locales/es.yml +19 -35
  54. data/config/locales/eu.yml +22 -44
  55. data/config/locales/fi-plain.yml +8 -24
  56. data/config/locales/fi.yml +9 -25
  57. data/config/locales/fr-CA.yml +8 -27
  58. data/config/locales/fr.yml +8 -27
  59. data/config/locales/ga-IE.yml +0 -4
  60. data/config/locales/gl.yml +2 -26
  61. data/config/locales/gn-PY.yml +1 -0
  62. data/config/locales/hu.yml +4 -27
  63. data/config/locales/id-ID.yml +5 -26
  64. data/config/locales/is-IS.yml +0 -1
  65. data/config/locales/it.yml +1 -27
  66. data/config/locales/ja.yml +22 -38
  67. data/config/locales/ka-GE.yml +1 -0
  68. data/config/locales/lb.yml +0 -26
  69. data/config/locales/lo-LA.yml +1 -0
  70. data/config/locales/lt.yml +0 -26
  71. data/config/locales/lv.yml +5 -26
  72. data/config/locales/nl.yml +0 -26
  73. data/config/locales/no.yml +2 -28
  74. data/config/locales/pl.yml +2 -27
  75. data/config/locales/pt-BR.yml +0 -26
  76. data/config/locales/pt.yml +0 -26
  77. data/config/locales/ro-RO.yml +5 -25
  78. data/config/locales/ru.yml +5 -5
  79. data/config/locales/sk.yml +5 -26
  80. data/config/locales/sv.yml +2 -27
  81. data/config/locales/tr-TR.yml +4 -27
  82. data/config/locales/uk.yml +5 -1
  83. data/config/locales/zh-CN.yml +3 -26
  84. data/config/routes.rb +20 -2
  85. data/lib/decidim/api/types/localized_string_type.rb +9 -0
  86. data/lib/decidim/api/types/translated_field_type.rb +20 -5
  87. data/lib/decidim/attributes/localized_date.rb +9 -1
  88. data/lib/decidim/attributes/time_with_zone.rb +13 -1
  89. data/lib/decidim/core/engine.rb +0 -5
  90. data/lib/decidim/core/test/factories.rb +13 -6
  91. data/lib/decidim/core/test/shared_examples/mcell_examples.rb +17 -0
  92. data/lib/decidim/core/test.rb +1 -0
  93. data/lib/decidim/core/version.rb +1 -1
  94. data/lib/decidim/form_builder.rb +9 -2
  95. data/lib/decidim/participatory_space_resourceable.rb +7 -1
  96. data/lib/decidim/resourceable.rb +5 -4
  97. data/lib/decidim/settings_manifest.rb +1 -1
  98. metadata +12 -8
  99. data/app/packs/images/decidim/gamification/badges/decidim_gamification_badges_invitations.svg +0 -1
  100. data/app/views/decidim/devise/registrations/edit.html.erb +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3cbd72f4c386bc8730dfa1d3958a691c5ed719b3cf43b15f778d2011ae5a7f0d
4
- data.tar.gz: 32e72e8185fc2f28f0298ef2a9a0350cd8b436e1a39bda136fe82c9da5644433
3
+ metadata.gz: 8937550934aed6ed69fa84837edbbc2902c5fd6779654a5180a63abbfe25d700
4
+ data.tar.gz: ba86b8da585b54db8845cf1d5e476086db70d03920f99ddf3a32cbe023521992
5
5
  SHA512:
6
- metadata.gz: 3d7a4d41fa9aeb76c68e21e5a486b8a3149d117a956c4d0ec17020db1abad1c8a8ce1316ba014a158bbb2fe254ebc0db4b4923a756d0f97997c06bb128c3e440
7
- data.tar.gz: e282e746492e32a9e85edb4b72ede7804be3b5b7fd9567e49eac2ba6b9430f6d46da2596f044b87fa520fb4cde0e92259c40695837c87d6fb0943f2c244fa8e2
6
+ metadata.gz: de40caba191fb9ff0b88bbf9055fbb4592fcc12352c6e35d21d812e87399b1772254d669438ade3ada960ca994247ab87b3eb4743e80df9121cb3bb07c6a9f18
7
+ data.tar.gz: c115b3d2e531d0b0d8e43f7b41a863dc51eb2c076bc8c4f2c8731e642fb191115b2fdf6aebe6a3cf28c48fb746c3e9cec85ea9a5be630d382ee695cff238278f
@@ -39,7 +39,7 @@ module Decidim::Amendable
39
39
  end
40
40
 
41
41
  def proposal_link(resource = model.amendable, text = nil)
42
- text ||= %(<strong>#{present(model.amendable).title}</strong>)
42
+ text ||= %(<strong>#{decidim_sanitize(present(model.amendable).title, strip_tags: true)}</strong>)
43
43
  link_to resource_locator(resource).path do
44
44
  text
45
45
  end
@@ -57,7 +57,7 @@ module Decidim
57
57
  end
58
58
 
59
59
  def title
60
- translated_attribute model.title
60
+ decidim_html_escape(translated_attribute(model.title))
61
61
  end
62
62
 
63
63
  def description
@@ -25,6 +25,14 @@ module Decidim
25
25
  def recipient_user
26
26
  options[:recipient_user]
27
27
  end
28
+
29
+ def custom_url_for_mail_root
30
+ options[:custom_url_for_mail_root]
31
+ end
32
+
33
+ def decidim
34
+ @decidim ||= EngineRouter.new("decidim", {})
35
+ end
28
36
  end
29
37
  end
30
38
  end
@@ -16,7 +16,7 @@
16
16
  <tr>
17
17
  <th>
18
18
  <center>
19
- <%= render partial: "layouts/decidim/mailer_logo.html", locals: { organization: organization } %>
19
+ <%= render partial: "layouts/decidim/mailer_logo.html", locals: { organization: organization, custom_url_for_mail_root: custom_url_for_mail_root } %>
20
20
  </center>
21
21
  </th>
22
22
  </tr>
@@ -27,7 +27,7 @@
27
27
  <tr>
28
28
  <th>
29
29
  <% if organization.official_img_header.attached? %>
30
- <%= link_to organization.official_url do %>
30
+ <%= link_to newsletter.organization_official_url do %>
31
31
  <%= image_tag organization.attached_uploader(:official_img_header).path, alt: "", style: "max-height: 50px", class: "float-right" %>
32
32
  <% end %>
33
33
  <% end %>
@@ -72,8 +72,8 @@
72
72
  <th class="expander"></th>
73
73
  <th class="small-12 first columns cityhall-bar">
74
74
  <div class="decidim-logo" style="float: right; text-align: right; padding-right: 16px">
75
- <% if @custom_url_for_mail_root.present? %>
76
- <%= link_to organization.name.html_safe, @custom_url_for_mail_root %>
75
+ <% if custom_url_for_mail_root.present? %>
76
+ <%= link_to organization.name.html_safe, custom_url_for_mail_root %>
77
77
  <% else %>
78
78
  <%= link_to organization.name.html_safe, decidim.root_url(host: organization.host) %>
79
79
  <% end %>
@@ -24,7 +24,7 @@ table.button table td {
24
24
  <tr>
25
25
  <th>
26
26
  <center>
27
- <%= render partial: "layouts/decidim/mailer_logo.html", locals: { organization: organization } %>
27
+ <%= render partial: "layouts/decidim/mailer_logo.html", locals: { organization: organization, custom_url_for_mail_root: custom_url_for_mail_root } %>
28
28
  </center>
29
29
  </th>
30
30
  </tr>
@@ -35,7 +35,7 @@ table.button table td {
35
35
  <tr>
36
36
  <th>
37
37
  <% if organization.official_img_header.attached? %>
38
- <%= link_to organization.official_url do %>
38
+ <%= link_to newsletter.organization_official_url do %>
39
39
  <%= image_tag organization.attached_uploader(:official_img_header).url(host: organization.host), alt: "", style: "max-height: 50px", class: "float-right" %>
40
40
  <% end %>
41
41
  <% end %>
@@ -111,8 +111,8 @@ table.button table td {
111
111
  <th class="expander"></th>
112
112
  <th class="small-12 first columns cityhall-bar">
113
113
  <div class="decidim-logo" style="float: right; text-align: right; padding-right: 16px">
114
- <% if @custom_url_for_mail_root.present? %>
115
- <%= link_to organization.name.html_safe, @custom_url_for_mail_root %>
114
+ <% if custom_url_for_mail_root.present? %>
115
+ <%= link_to organization.name.html_safe, custom_url_for_mail_root %>
116
116
  <% else %>
117
117
  <%= link_to organization.name.html_safe, decidim.root_url(host: organization.host) %>
118
118
  <% end %>
@@ -31,7 +31,7 @@ module Decidim
31
31
  query = if @current_group.present?
32
32
  @resource.endorsements.where(decidim_user_group_id: @current_group&.id)
33
33
  else
34
- @resource.endorsements.where(author: @current_user)
34
+ @resource.endorsements.where(author: @current_user, decidim_user_group_id: nil)
35
35
  end
36
36
  query.destroy_all
37
37
  end
@@ -19,7 +19,7 @@ module Decidim
19
19
  # invitation. Using the param `invite_redirect` we can redirect the user
20
20
  # to a custom path after it has accepted the invitation.
21
21
  def after_accept_path_for(resource)
22
- params[:invite_redirect] || after_sign_in_path_for(resource)
22
+ invite_redirect_path || after_sign_in_path_for(resource)
23
23
  end
24
24
 
25
25
  # When a managed user accepts the invitation is promoted to non-managed user.
@@ -30,7 +30,6 @@ module Decidim
30
30
  resource.update!(newsletter_notifications_at: Time.current) if update_resource_params[:newsletter_notifications]
31
31
  resource.update!(managed: false) if resource.managed?
32
32
  resource.update!(accepted_tos_version: resource.organization.tos_version)
33
- Decidim::Gamification.increment_score(resource.invited_by, :invitations) if resource.invited_by
34
33
  end
35
34
 
36
35
  resource
@@ -38,6 +37,14 @@ module Decidim
38
37
 
39
38
  protected
40
39
 
40
+ def invite_redirect_path
41
+ path = params[:invite_redirect]
42
+ return unless path
43
+ return unless path.starts_with?(%r{^/[a-z0-9]+})
44
+
45
+ path
46
+ end
47
+
41
48
  def configure_permitted_parameters
42
49
  devise_parameter_sanitizer.permit(:accept_invitation, keys: [:nickname, :tos_agreement, :newsletter_notifications])
43
50
  end
@@ -58,6 +58,10 @@ module Decidim
58
58
  super(hash)
59
59
  resource.organization = current_organization
60
60
  end
61
+
62
+ def devise_mapping
63
+ ::Devise.mappings[:user]
64
+ end
61
65
  end
62
66
  end
63
67
  end
@@ -7,6 +7,7 @@ module Decidim
7
7
  include UserGroups
8
8
 
9
9
  before_action :enforce_user_groups_enabled
10
+ before_action :ensure_user_group_not_blocked
10
11
 
11
12
  def new
12
13
  enforce_permission_to :create, :user_group, current_user: current_user
@@ -73,6 +74,10 @@ module Decidim
73
74
 
74
75
  private
75
76
 
77
+ def ensure_user_group_not_blocked
78
+ raise ActionController::RoutingError, "Blocked User Group" if user_group&.blocked?
79
+ end
80
+
76
81
  def accepted_user_group
77
82
  @accepted_user_group ||= Decidim::UserGroups::AcceptedUserGroups.for(current_user).find_by(nickname: params[:id])
78
83
  end
@@ -7,9 +7,11 @@ module Decidim
7
7
  skip_before_action :store_current_location
8
8
 
9
9
  helper Decidim::ExternalDomainHelper
10
+ helper_method :external_url
10
11
 
11
12
  before_action :parse_url
12
13
  rescue_from Decidim::InvalidUrlError, with: :invalid_url
14
+ rescue_from URI::InvalidURIError, with: :invalid_url
13
15
 
14
16
  def new
15
17
  headers["X-Robots-Tag"] = "noindex"
@@ -25,7 +27,7 @@ module Decidim
25
27
  def parse_url
26
28
  raise Decidim::InvalidUrlError unless external_url
27
29
 
28
- parts = external_url.match %r{^(([a-z]+):)?//([^/]+)(/.*)?$}
30
+ parts = external_url.match %r{\A(([a-z]+):)?//([^/]+)(/.*)?\z}
29
31
  raise Decidim::InvalidUrlError unless parts
30
32
 
31
33
  @url_parts = {
@@ -36,7 +38,7 @@ module Decidim
36
38
  end
37
39
 
38
40
  def external_url
39
- @external_url ||= params[:external_url]
41
+ @external_url ||= URI.parse(params[:external_url]).to_s
40
42
  end
41
43
  end
42
44
  end
@@ -13,7 +13,7 @@ module Decidim
13
13
  before_action :ensure_profile_holder
14
14
  before_action :ensure_profile_holder_is_a_group, only: [:members]
15
15
  before_action :ensure_profile_holder_is_a_user, only: [:groups, :following]
16
- before_action :ensure_user_not_blocked, only: [:following, :followers, :badges]
16
+ before_action :ensure_user_not_blocked
17
17
 
18
18
  def show
19
19
  return redirect_to profile_timeline_path(nickname: params[:nickname]) if profile_holder == current_user
@@ -19,9 +19,9 @@ module Decidim
19
19
  attribute :personal_url
20
20
  attribute :about
21
21
 
22
- validates :name, presence: true
23
- validates :email, presence: true, 'valid_email_2/email': { disposable: true }
24
- validates :nickname, presence: true, format: Decidim::User::REGEXP_NICKNAME
22
+ validates :name, presence: true, format: { with: Decidim::User::REGEXP_NAME }
23
+ validates :email, presence: true, "valid_email_2/email": { disposable: true }
24
+ validates :nickname, presence: true, format: { with: Decidim::User::REGEXP_NICKNAME }
25
25
 
26
26
  validates :nickname, length: { maximum: Decidim::User.nickname_max_length, allow_blank: true }
27
27
  validates :password, confirmation: true
@@ -66,7 +66,8 @@ module Decidim
66
66
  errors = amendable_form_errors.details[key] - @original_form.errors.details[key]
67
67
 
68
68
  errors.map do |hash|
69
- @amendable_form.errors.add(key, hash[:error]) unless @amendable_form.errors.details[key].include? error: hash[:error]
69
+ error = hash.delete(:error)
70
+ @amendable_form.errors.add(key, error, **hash) unless @amendable_form.errors.details[key].include?(error: error)
70
71
  end
71
72
  end
72
73
  end
@@ -14,9 +14,9 @@ module Decidim
14
14
  attribute :tos_agreement, Boolean
15
15
  attribute :current_locale, String
16
16
 
17
- validates :name, presence: true
18
- validates :nickname, presence: true, format: /\A[\w\-]+\z/, length: { maximum: Decidim::User.nickname_max_length }
19
- validates :email, presence: true, 'valid_email_2/email': { disposable: true }
17
+ validates :name, presence: true, format: { with: Decidim::User::REGEXP_NAME }
18
+ validates :nickname, presence: true, format: { with: Decidim::User::REGEXP_NICKNAME }, length: { maximum: Decidim::User.nickname_max_length }
19
+ validates :email, presence: true, "valid_email_2/email": { disposable: true }
20
20
  validates :password, confirmation: true
21
21
  validates :password, password: { name: :name, email: :email, username: :nickname }
22
22
  validates :password_confirmation, presence: true
@@ -24,7 +24,7 @@ module Decidim
24
24
  #
25
25
  # Returns an HTML tag with the icon.
26
26
  def manifest_icon(manifest, options = {})
27
- if manifest.icon
27
+ if manifest.respond_to?(:icon) && manifest.icon.present?
28
28
  external_icon manifest.icon, options
29
29
  else
30
30
  icon "question-mark", options
@@ -42,9 +42,9 @@ module Decidim
42
42
  def resource_icon(resource, options = {})
43
43
  if resource.class.name == "Decidim::Comments::Comment"
44
44
  icon "comment-square", options
45
- elsif resource.respond_to?(:component)
45
+ elsif resource.respond_to?(:component) && resource.component.present?
46
46
  component_icon(resource.component, options)
47
- elsif resource.respond_to?(:manifest)
47
+ elsif resource.respond_to?(:manifest) && resource.manifest.present?
48
48
  manifest_icon(resource.manifest, options)
49
49
  elsif resource.is_a?(Decidim::User)
50
50
  icon "person", options
@@ -31,6 +31,7 @@ module Decidim
31
31
  # this method is used to generate the root link on mail with the utm_codes
32
32
  # If the newsletter_id is nil, it returns the root_url
33
33
  def custom_url_for_mail_root(organization, newsletter_id = nil)
34
+ decidim = EngineRouter.new("decidim", {})
34
35
  if newsletter_id.present?
35
36
  decidim.root_url(host: organization.host) + utm_codes(organization.host, newsletter_id.to_s)
36
37
  else
@@ -11,14 +11,20 @@ module Decidim
11
11
 
12
12
  helper_method :cell
13
13
 
14
- def newsletter(user, newsletter)
14
+ def newsletter(user, newsletter, preview: false)
15
15
  return if user.email.blank?
16
16
 
17
17
  @organization = user.organization
18
18
  @newsletter = newsletter
19
19
  @user = user
20
-
21
- @custom_url_for_mail_root = custom_url_for_mail_root(@organization, @newsletter.id) if Decidim.config.track_newsletter_links
20
+ @preview = preview
21
+
22
+ @custom_url_for_mail_root =
23
+ if @preview
24
+ "#"
25
+ elsif Decidim.config.track_newsletter_links
26
+ custom_url_for_mail_root(@organization, @newsletter.id)
27
+ end
22
28
  @encrypted_token = Decidim::NewsletterEncryptor.sent_at_encrypted(@user.id, @newsletter.sent_at)
23
29
 
24
30
  with_user(user) do
@@ -40,6 +46,7 @@ module Decidim
40
46
  organization: @organization,
41
47
  newsletter: @newsletter,
42
48
  recipient_user: @user,
49
+ custom_url_for_mail_root: @custom_url_for_mail_root,
43
50
  context: {
44
51
  controller: self
45
52
  }
@@ -5,6 +5,7 @@ module Decidim
5
5
  # a events are received.
6
6
  class NotificationMailer < Decidim::ApplicationMailer
7
7
  helper Decidim::ResourceHelper
8
+ helper Decidim::SanitizeHelper
8
9
 
9
10
  def event_received(event, event_class_name, resource, user, user_role, extra) # rubocop:disable Metrics/ParameterLists
10
11
  with_user(user) do
@@ -56,6 +56,24 @@ module Decidim
56
56
  .find_by(scoped_resource_id: id)
57
57
  end
58
58
 
59
+ def url(**kwargs)
60
+ proxy_url(:newsletter_url, id: id, **kwargs)
61
+ end
62
+
63
+ def notifications_settings_url(**kwargs)
64
+ proxy_url(__method__, **kwargs)
65
+ end
66
+
67
+ def unsubscribe_newsletters_url(**kwargs)
68
+ proxy_url(__method__, **kwargs)
69
+ end
70
+
71
+ def organization_official_url
72
+ return "#" unless sent?
73
+
74
+ organization.official_url || proxy_url(:root_url)
75
+ end
76
+
59
77
  private
60
78
 
61
79
  def author_belongs_to_organization
@@ -63,5 +81,15 @@ module Decidim
63
81
 
64
82
  errors.add(:author, :invalid) unless author.organization == organization
65
83
  end
84
+
85
+ def proxy_url(method, **kwargs)
86
+ return "#" unless sent?
87
+
88
+ router.public_send(method, host: organization.host, **kwargs)
89
+ end
90
+
91
+ def router
92
+ @router ||= EngineRouter.new("decidim", {})
93
+ end
66
94
  end
67
95
  end
@@ -35,8 +35,6 @@ module Decidim
35
35
  has_many :access_grants, class_name: "Doorkeeper::AccessGrant", foreign_key: :resource_owner_id, dependent: :destroy
36
36
  has_many :access_tokens, class_name: "Doorkeeper::AccessToken", foreign_key: :resource_owner_id, dependent: :destroy
37
37
 
38
- has_one :blocking, class_name: "Decidim::UserBlock", foreign_key: :id, primary_key: :block_id, dependent: :destroy
39
-
40
38
  validates :name, presence: true, unless: -> { deleted? }
41
39
  validates :nickname,
42
40
  presence: true,
@@ -17,6 +17,8 @@ module Decidim
17
17
  has_many :notifications, foreign_key: "decidim_user_id", class_name: "Decidim::Notification", dependent: :destroy
18
18
  has_many :following_follows, foreign_key: "decidim_user_id", class_name: "Decidim::Follow", dependent: :destroy
19
19
 
20
+ has_one :blocking, class_name: "Decidim::UserBlock", foreign_key: :id, primary_key: :block_id, dependent: :destroy
21
+
20
22
  # Regex for name & nickname format validations
21
23
  REGEXP_NAME = /\A(?!.*[<>?%&\^*#@()\[\]=+:;"{}\\|])/.freeze
22
24
 
@@ -4,7 +4,7 @@ module Decidim
4
4
  class UserBlock < ApplicationRecord
5
5
  MINIMUM_JUSTIFICATION_LENGTH = 15
6
6
 
7
- belongs_to :user, class_name: "Decidim::User", foreign_key: :decidim_user_id
8
- belongs_to :blocking_user, class_name: "Decidim::User"
7
+ belongs_to :user, class_name: "Decidim::UserBaseEntity", foreign_key: :decidim_user_id
8
+ belongs_to :blocking_user, class_name: "Decidim::UserBaseEntity"
9
9
  end
10
10
  end
@@ -21,7 +21,7 @@ module Decidim
21
21
  foreign_key: :decidim_user_id,
22
22
  source: :user
23
23
 
24
- validates :name, presence: true, uniqueness: { scope: :decidim_organization_id }
24
+ validates :name, presence: true, uniqueness: { scope: :decidim_organization_id }, unless: -> { blocked? }
25
25
 
26
26
  validate :correct_state
27
27
  validate :unique_document_number, if: :has_document_number?
@@ -0,0 +1,143 @@
1
+ /* eslint max-lines: ["error", 350] */
2
+
3
+ /**
4
+ * Quill clipboard utilities
5
+ *
6
+ * Copyright (c) 2017, Slab
7
+ * Copyright (c) 2014, Jason Chen
8
+ * Copyright (c) 2013, salesforce.com
9
+ * BSD 3-Clause "New" or "Revised" License
10
+ *
11
+ * Extends the original version from https://github.com/quilljs/quill
12
+ * Relevant parts converted from TypeScript to JavaScript
13
+ */
14
+
15
+ import CodeBlock from "quill/formats/code";
16
+ import { matchNewline, matchBreak, deltaEndsWith, traverse } from "src/decidim/editor/clipboard_utilities";
17
+
18
+ const Delta = Quill.import("delta");
19
+ const Clipboard = Quill.import("modules/clipboard");
20
+
21
+ /**
22
+ * Pasting bold text is broken in Quill as described at:
23
+ * https://github.com/quilljs/quill/issues/306
24
+ *
25
+ * The reason is that the `<strong>` nodes are not recognized as bold types.
26
+ * This override fixes the issue by introducing parts of the newer Quill code
27
+ * at GitHub and defining the `<strong>` tags as bold tags.
28
+ */
29
+ export default class ClipboardOverride extends Clipboard {
30
+ constructor(quill, options) {
31
+ super(quill, options);
32
+ this.overrideMatcher("b", "b, strong");
33
+ this.overrideMatcher("br", "br", matchBreak);
34
+
35
+ // Change the matchNewLine matchers to the newer version
36
+ this.matchers[1][1] = matchNewline;
37
+ this.matchers[3][1] = matchNewline;
38
+
39
+ // Remove `matchSpacing` as that is also removed in the newer versions.
40
+ this.removeMatcher(Node.ELEMENT_NODE, "matchSpacing");
41
+ }
42
+
43
+ overrideMatcher(originalSelector, newSelector, newMatcher = null) {
44
+ const idx = this.matchers.findIndex((item) => item[0] === originalSelector);
45
+ if (idx >= 0) {
46
+ this.matchers[idx][0] = newSelector;
47
+ if (newMatcher) {
48
+ this.matchers[idx][1] = newMatcher;
49
+ }
50
+ }
51
+ }
52
+
53
+ removeMatcher(selector, matcherName) {
54
+ const idx = this.matchers.findIndex((item) => item[0] === selector && item[1].name === matcherName);
55
+ if (idx >= 0) {
56
+ this.matchers.splice(idx, 1);
57
+ }
58
+ }
59
+
60
+ onPaste(ev) {
61
+ if (ev.defaultPrevented || !this.quill.isEnabled()) {
62
+ return;
63
+ }
64
+ ev.preventDefault();
65
+ const range = this.quill.getSelection(true);
66
+ if (range === null) {
67
+ return;
68
+ }
69
+ const html = ev.clipboardData.getData("text/html");
70
+ const text = ev.clipboardData.getData("text/plain");
71
+ const files = Array.from(ev.clipboardData.files || []);
72
+ if (!html && files.length > 0) {
73
+ this.quill.uploader.upload(range, files);
74
+ return;
75
+ }
76
+ if (html && files.length > 0) {
77
+ const doc = new DOMParser().parseFromString(html, "text/html");
78
+ if (
79
+ doc.body.childElementCount === 1 &&
80
+ doc.body.firstElementChild.tagName === "IMG"
81
+ ) {
82
+ this.quill.uploader.upload(range, files);
83
+ return;
84
+ }
85
+ }
86
+ this.onPasteRange(range, { html, text });
87
+ }
88
+
89
+ onPasteRange(range, { text, html }) {
90
+ const formats = this.quill.getFormat(range.index);
91
+ const pastedDelta = this.convertPaste({ text, html }, formats);
92
+ // debug.log('onPaste", pastedDelta, { text, html });
93
+ const delta = new Delta().retain(range.index).delete(range.length).concat(pastedDelta);
94
+ this.quill.updateContents(delta, Quill.sources.USER);
95
+ // range.length contributes to delta.length()
96
+ this.quill.setSelection(
97
+ delta.length() - range.length,
98
+ Quill.sources.SILENT,
99
+ );
100
+ this.quill.scrollIntoView();
101
+ }
102
+
103
+ convertPaste({ html, text }, formats = {}) {
104
+ if (formats[CodeBlock.blotName]) {
105
+ return new Delta().insert(text, {
106
+ [CodeBlock.blotName]: formats[CodeBlock.blotName]
107
+ });
108
+ }
109
+ if (!html) {
110
+ return new Delta().insert(text || "");
111
+ }
112
+ const delta = this.convertPasteHTML(html);
113
+ // Remove trailing newline
114
+ if (
115
+ deltaEndsWith(delta, "\n") &&
116
+ (delta.ops[delta.ops.length - 1].attributes === null || formats.table)
117
+ ) {
118
+ return delta.compose(new Delta().retain(delta.length() - 1).delete(1));
119
+ }
120
+ return delta;
121
+ }
122
+
123
+ convertPasteHTML(html) {
124
+ const doc = new DOMParser().parseFromString(html, "text/html");
125
+ const container = doc.body;
126
+ const nodeMatches = new WeakMap();
127
+ const [elementMatchers, textMatchers] = this.prepareMatching(
128
+ container,
129
+ nodeMatches
130
+ );
131
+ return traverse(
132
+ this.quill.scroll,
133
+ container,
134
+ elementMatchers,
135
+ textMatchers,
136
+ nodeMatches
137
+ );
138
+ }
139
+ }
140
+
141
+ // Disable warning messages from overwritting modules
142
+ Quill.debug("error");
143
+ Quill.register({"modules/clipboard": ClipboardOverride}, true);