decidim-core 0.26.3 → 0.26.5

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 (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);