decidim-core 0.27.2 → 0.27.3

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/upload_modal/files.erb +1 -0
  3. data/app/cells/decidim/upload_modal_cell.rb +14 -4
  4. data/app/commands/decidim/attachment_methods.rb +20 -2
  5. data/app/commands/decidim/create_registration.rb +1 -0
  6. data/app/commands/decidim/gallery_methods.rb +1 -1
  7. data/app/commands/decidim/update_account.rb +1 -0
  8. data/app/commands/decidim/update_password.rb +2 -0
  9. data/app/controllers/decidim/devise/sessions_controller.rb +18 -2
  10. data/app/controllers/decidim/links_controller.rb +8 -11
  11. data/app/helpers/decidim/cells_helper.rb +1 -0
  12. data/app/helpers/decidim/external_domain_helper.rb +14 -3
  13. data/app/helpers/decidim/sanitize_helper.rb +3 -2
  14. data/app/models/decidim/scope_type.rb +24 -0
  15. data/app/packs/src/decidim/direct_uploads/upload_modal.js +0 -1
  16. data/app/packs/src/decidim/editor/clipboard_override.js +6 -2
  17. data/app/packs/src/decidim/editor.js +63 -33
  18. data/app/packs/stylesheets/decidim/modules/_buttons.scss +10 -6
  19. data/app/packs/stylesheets/decidim/modules/_cards.scss +1 -1
  20. data/app/packs/stylesheets/decidim/modules/_comments.scss +24 -0
  21. data/app/packs/stylesheets/decidim/modules/_input-gallery.scss +2 -1
  22. data/app/packs/stylesheets/decidim/modules/_upload_modal.scss +0 -4
  23. data/app/packs/stylesheets/decidim/vizzs/_linechart.scss +2 -2
  24. data/app/packs/stylesheets/decidim/vizzs/_rowchart.scss +2 -2
  25. data/app/presenters/decidim/notification_presenter.rb +1 -1
  26. data/app/presenters/decidim/notification_to_mailer_presenter.rb +1 -0
  27. data/app/presenters/decidim/user_group_presenter.rb +1 -1
  28. data/app/presenters/decidim/user_presenter.rb +1 -1
  29. data/app/scrubbers/decidim/admin_input_scrubber.rb +3 -1
  30. data/app/scrubbers/decidim/user_input_scrubber.rb +30 -1
  31. data/app/services/decidim/traceability.rb +1 -0
  32. data/app/validators/uploader_image_dimensions_validator.rb +22 -2
  33. data/app/views/decidim/links/_invalid_url_modal.html.erb +17 -0
  34. data/app/views/decidim/links/_modal.html.erb +1 -1
  35. data/app/views/decidim/links/invalid_url.js.erb +24 -0
  36. data/app/views/decidim/links/new.html.erb +1 -1
  37. data/app/views/decidim/messaging/conversations/_conversation.html.erb +1 -5
  38. data/config/locales/ar.yml +566 -3
  39. data/config/locales/bg.yml +1 -4
  40. data/config/locales/ca.yml +21 -17
  41. data/config/locales/cs.yml +22 -30
  42. data/config/locales/da.yml +4 -0
  43. data/config/locales/de.yml +4 -22
  44. data/config/locales/el.yml +2 -4
  45. data/config/locales/en.yml +16 -13
  46. data/config/locales/eo.yml +2 -1
  47. data/config/locales/es-MX.yml +20 -16
  48. data/config/locales/es-PY.yml +20 -16
  49. data/config/locales/es.yml +21 -17
  50. data/config/locales/et.yml +4 -0
  51. data/config/locales/eu.yml +149 -58
  52. data/config/locales/fa-IR.yml +1 -0
  53. data/config/locales/fi-plain.yml +1 -18
  54. data/config/locales/fi.yml +19 -15
  55. data/config/locales/fr-CA.yml +23 -16
  56. data/config/locales/fr.yml +21 -14
  57. data/config/locales/ga-IE.yml +1 -0
  58. data/config/locales/gl.yml +0 -21
  59. data/config/locales/gn-PY.yml +4 -0
  60. data/config/locales/hr.yml +4 -0
  61. data/config/locales/hu.yml +64 -23
  62. data/config/locales/id-ID.yml +2 -4
  63. data/config/locales/is-IS.yml +2 -1
  64. data/config/locales/it.yml +1 -5
  65. data/config/locales/ja.yml +10 -21
  66. data/config/locales/ka-GE.yml +4 -0
  67. data/config/locales/kaa.yml +1 -0
  68. data/config/locales/lb.yml +0 -4
  69. data/config/locales/lt.yml +0 -34
  70. data/config/locales/lv.yml +0 -3
  71. data/config/locales/nl.yml +1 -23
  72. data/config/locales/no.yml +1 -23
  73. data/config/locales/oc-FR.yml +3 -0
  74. data/config/locales/pl.yml +0 -34
  75. data/config/locales/pt-BR.yml +2 -6
  76. data/config/locales/pt.yml +0 -4
  77. data/config/locales/ro-RO.yml +36 -4
  78. data/config/locales/ru.yml +1 -3
  79. data/config/locales/sk.yml +3 -5
  80. data/config/locales/sl.yml +1 -0
  81. data/config/locales/sr-CS.yml +2 -0
  82. data/config/locales/sv.yml +1 -23
  83. data/config/locales/tr-TR.yml +3 -7
  84. data/config/locales/uk.yml +1 -3
  85. data/config/locales/zh-CN.yml +0 -4
  86. data/config/locales/zh-TW.yml +1872 -0
  87. data/lib/decidim/asset_router/pipeline.rb +2 -0
  88. data/lib/decidim/core/test/shared_examples/comments_examples.rb +36 -0
  89. data/lib/decidim/core/test/shared_examples/digest_mail_examples.rb +33 -0
  90. data/lib/decidim/core/test/shared_examples/editor_shared_examples.rb +5 -4
  91. data/lib/decidim/core/test/shared_examples/rich_text_editor_examples.rb +7 -3
  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 +4 -3
  95. data/lib/decidim/publicable.rb +4 -0
  96. metadata +12 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 391ba34f55c860208f7644dd10b1b3e2b6465ed45b9e9f9fd194ce1036516995
4
- data.tar.gz: 93636f8d556d73547fcdff45986fcc85ba4a22e9c399fdeca0c550ee6c241363
3
+ metadata.gz: d6577740d87b562e24f541c4723480ec774239f69a389d4434536ab72be3b116
4
+ data.tar.gz: 8366df277375b9fadc5c96b4a9e647a0300057d7148eafcb672f3a57d3abd55f
5
5
  SHA512:
6
- metadata.gz: 157c005fbea98fe374586f91f15f17bc54450d6e4b6f7136d51582d5cae7af9442d250edbbf4fe2a4308aac902ba0eb28c49681153dcda6b6948f8589414e930
7
- data.tar.gz: 8ebc9089ad86980956ded3024d490f419bf8015ae0dc4b3fe6e003d415010de8ed28751bd0c168cf526c8a17f5db50467c9223242c2150cf600304990e1eda04
6
+ metadata.gz: f6f4e9e7bcf14ccc5943ec41d4468338d1cf62aec7d37b296d0fa37a3c1c7bc97711b06579d10b32167ce291b8d664989f90eecfcba9a94f228ff60e7e8698e0
7
+ data.tar.gz: 4fcd0dacd12572c3a4b404709ec632dcc6c5cc964dfd9dc29d92a4dd03da2b10ddc3e5df57a6ed91cb3f90e0dac70c4668ba89040f8cf5788e34d1f8e857d3b8
@@ -30,6 +30,7 @@
30
30
  add_attribute: add_attribute,
31
31
  resource_name: resource_name,
32
32
  resource_class: resource_class,
33
+ required: required?,
33
34
  optional: optional,
34
35
  max_file_size: max_file_size,
35
36
  multiple: multiple,
@@ -5,6 +5,7 @@ module Decidim
5
5
  class UploadModalCell < Decidim::ViewModel
6
6
  include Cell::ViewModel::Partial
7
7
  include ERB::Util
8
+ include Decidim::SanitizeHelper
8
9
 
9
10
  alias form model
10
11
 
@@ -29,7 +30,7 @@ module Decidim
29
30
  end
30
31
 
31
32
  def label
32
- options[:label]
33
+ form.send(:custom_label, attribute, options[:label], { required: required?, for: nil })
33
34
  end
34
35
 
35
36
  def button_label
@@ -72,8 +73,17 @@ module Decidim
72
73
  options[:multiple] || false
73
74
  end
74
75
 
76
+ # @deprecated Please use `required?` instead.
77
+ #
78
+ # NOTE: When this is removed, also the `optional` option should be removed.
75
79
  def optional
76
- options[:optional]
80
+ !required?
81
+ end
82
+
83
+ def required?
84
+ return !options[:optional] if options.has_key?(:optional)
85
+
86
+ options[:required] == true
77
87
  end
78
88
 
79
89
  # By default Foundation adds form errors next to input, but since input is in the modal
@@ -81,7 +91,7 @@ module Decidim
81
91
  # This should only be necessary when file is required by the form.
82
92
  def input_validation_field
83
93
  object_name = form.object.present? ? "#{form.object.model_name.param_key}[#{add_attribute}_validation]" : "#{add_attribute}_validation"
84
- input = check_box_tag object_name, 1, attachments.present?, class: "hide", label: false, required: !optional
94
+ input = check_box_tag object_name, 1, attachments.present?, class: "hide", label: false, required: required?
85
95
  message = form.send(:abide_error_element, add_attribute) + form.send(:error_and_help_text, add_attribute)
86
96
  input + message
87
97
  end
@@ -142,7 +152,7 @@ module Decidim
142
152
  def title_for(attachment)
143
153
  return unless has_title?
144
154
 
145
- translated_attribute(attachment.title)
155
+ decidim_html_escape(decidim_sanitize(translated_attribute(attachment.title)))
146
156
  end
147
157
 
148
158
  def truncated_file_name_for(attachment, max_length = 31)
@@ -12,8 +12,8 @@ module Decidim
12
12
  @attachment = Attachment.new(
13
13
  title: { I18n.locale => @form.attachment.title },
14
14
  attached_to: attached_to,
15
- file: @form.attachment.file, # Define attached_to before this
16
- content_type: @form.attachment.file.content_type
15
+ file: signed_id_for(@form.attachment.file),
16
+ content_type: content_type_for(@form.attachment.file)
17
17
  )
18
18
  end
19
19
 
@@ -53,5 +53,23 @@ module Decidim
53
53
  def delete_attachment?
54
54
  @form.attachment&.delete_file.present?
55
55
  end
56
+
57
+ protected
58
+
59
+ def signed_id_for(attachment)
60
+ return attachment[:file] if attachment.is_a?(Hash)
61
+
62
+ attachment
63
+ end
64
+
65
+ def content_type_for(attachment)
66
+ return attachment.content_type if attachment.instance_of?(ActionDispatch::Http::UploadedFile)
67
+
68
+ blob(signed_id_for(attachment)).content_type
69
+ end
70
+
71
+ def blob(signed_id)
72
+ ActiveStorage::Blob.find_signed(signed_id)
73
+ end
56
74
  end
57
75
  end
@@ -41,6 +41,7 @@ module Decidim
41
41
  nickname: form.nickname,
42
42
  password: form.password,
43
43
  password_confirmation: form.password_confirmation,
44
+ password_updated_at: Time.current,
44
45
  organization: form.current_organization,
45
46
  tos_agreement: form.tos_agreement,
46
47
  newsletter_notifications_at: form.newsletter_at,
@@ -24,7 +24,7 @@ module Decidim
24
24
  end
25
25
 
26
26
  def update_attachment_title_for(photo)
27
- Decidim::Attachment.find(photo[:id]).update(title: title_for(photo))
27
+ Decidim::Attachment.find(photo[:id]).update(title: photos_title(photo))
28
28
  end
29
29
 
30
30
  def image?(signed_id)
@@ -55,6 +55,7 @@ module Decidim
55
55
 
56
56
  @user.password = @form.password
57
57
  @user.password_confirmation = @form.password_confirmation
58
+ @user.password_updated_at = Time.current
58
59
  end
59
60
 
60
61
  def notify_followers
@@ -16,6 +16,8 @@ module Decidim
16
16
  return broadcast(:invalid) if form.invalid?
17
17
 
18
18
  user.password = form.password
19
+ user.password_confirmation = form.password
20
+ user.password_updated_at = Time.current
19
21
 
20
22
  if user.save
21
23
  broadcast(:ok)
@@ -6,9 +6,25 @@ module Decidim
6
6
  class SessionsController < ::Devise::SessionsController
7
7
  include Decidim::DeviseControllers
8
8
 
9
- # rubocop: disable Rails/LexicallyScopedActionFilter
10
9
  before_action :check_sign_in_enabled, only: :create
11
- # rubocop: enable Rails/LexicallyScopedActionFilter
10
+
11
+ def create
12
+ super do |user|
13
+ if user.admin?
14
+ # Check that the admin password passes the validation and clear the
15
+ # `password_updated_at` field when the password is weak to force a
16
+ # password update on the user.
17
+ #
18
+ # Handles a case when the user registers through the registration
19
+ # form and they are promoted to an admin after that. In this case,
20
+ # the newly promoted admin user would otherwise have to change their
21
+ # password straight away even if they originally registered with a
22
+ # strong password.
23
+ validator = PasswordValidator.new({ attributes: :password })
24
+ user.update!(password_updated_at: nil) unless validator.validate_each(user, :password, sign_in_params[:password])
25
+ end
26
+ end
27
+ end
12
28
 
13
29
  def destroy
14
30
  current_user.invalidate_all_sessions!
@@ -21,24 +21,21 @@ module Decidim
21
21
 
22
22
  def invalid_url
23
23
  flash[:alert] = I18n.t("decidim.links.invalid_url")
24
- redirect_to decidim.root_path
24
+ if request.xhr?
25
+ render "invalid_url"
26
+ else
27
+ redirect_to decidim.root_path
28
+ end
25
29
  end
26
30
 
27
31
  def parse_url
32
+ raise Decidim::InvalidUrlError if params[:external_url].blank?
28
33
  raise Decidim::InvalidUrlError unless external_url
29
-
30
- parts = external_url.match %r{\A(([a-z]+):)?//([^/]+)(/.*)?\z}
31
- raise Decidim::InvalidUrlError unless parts
32
-
33
- @url_parts = {
34
- protocol: parts[1],
35
- domain: parts[3],
36
- path: parts[4]
37
- }
34
+ raise Decidim::InvalidUrlError unless %w(http https).include?(external_url.scheme)
38
35
  end
39
36
 
40
37
  def external_url
41
- @external_url ||= URI.parse(params[:external_url]).to_s
38
+ @external_url ||= URI.parse(params[:external_url])
42
39
  end
43
40
  end
44
41
  end
@@ -35,6 +35,7 @@ module Decidim
35
35
  end
36
36
 
37
37
  def user_flaggable?
38
+ return if (try(:profile_holder) || try(:profile_user) || try(:model)).try(:blocked)
38
39
  return unless context[:controller].try(:flaggable_controller?)
39
40
 
40
41
  true
@@ -3,10 +3,21 @@
3
3
  module Decidim
4
4
  module ExternalDomainHelper
5
5
  def highlight_domain
6
+ highlighted_domain = [
7
+ external_url.host,
8
+ (external_url.port && [80, 443].include?(external_url.port) ? "" : ":#{external_url.port}")
9
+ ].join
10
+
11
+ path = [
12
+ external_url.path,
13
+ (external_url.query ? "?#{external_url.query}" : ""),
14
+ (external_url.fragment ? "##{external_url.fragment}" : "")
15
+ ].join
16
+
6
17
  tag.div do
7
- content_tag(:span, "#{@url_parts[:protocol]}//") +
8
- content_tag(:span, @url_parts[:domain], class: "alert") +
9
- content_tag(:span, @url_parts[:path])
18
+ content_tag(:span, "#{external_url.scheme}://") +
19
+ content_tag(:span, highlighted_domain, class: "text-alert") +
20
+ content_tag(:span, path)
10
21
  end
11
22
  end
12
23
  end
@@ -112,9 +112,10 @@ module Decidim
112
112
  #
113
113
  # @return ActiveSupport::SafeBuffer
114
114
  def render_sanitized_content(resource, method)
115
- content = present(resource).send(method, links: true, strip_tags: !safe_content?)
115
+ content = present(resource).send(method, links: true, strip_tags: !try(:safe_content?))
116
116
 
117
- return decidim_sanitize(content, {}) unless safe_content?
117
+ return decidim_sanitize(content, {}) unless try(:safe_content?)
118
+ return decidim_sanitize_editor_admin(content, {}) if try(:safe_content_admin?)
118
119
 
119
120
  decidim_sanitize_editor(content)
120
121
  end
@@ -17,8 +17,32 @@ module Decidim
17
17
 
18
18
  validates :name, presence: true
19
19
 
20
+ before_destroy :detach_dynamic_associations
21
+
20
22
  def self.log_presenter_class_for(_log)
21
23
  Decidim::AdminLog::ScopeTypePresenter
22
24
  end
25
+
26
+ private
27
+
28
+ # This method detaches all records that may have association with the scope
29
+ # type. This cannot be done directly using the `dependent` option in the
30
+ # `has_many` relation in order to avoid tight coupling between the modules.
31
+ #
32
+ # This logic does not have to be applied to any classes that have been
33
+ # defined as `has_many` associations within this model already as they are
34
+ # already handled by the `dependent` option.
35
+ def detach_dynamic_associations
36
+ ActiveRecord::Base.descendants.each do |cls|
37
+ next if cls.abstract_class? || !cls.name&.match?(/^Decidim::/)
38
+ next if cls == self.class || cls == Decidim::Scope
39
+
40
+ cls.reflect_on_all_associations(:belongs_to).each do |ref|
41
+ next unless ref.options[:class_name] == self.class.name
42
+
43
+ cls.where(ref.options[:foreign_key] => id).update_all(ref.options[:foreign_key] => nil) # rubocop:disable Rails/SkipsModelValidations
44
+ end
45
+ end
46
+ end
23
47
  end
24
48
  end
@@ -16,7 +16,6 @@ export default class UploadModal {
16
16
  // - addAttribute - Field name / attribute of resource (e.g. avatar)
17
17
  // - resourceName - The resource to which the attribute belongs (e.g. user)
18
18
  // - resourceClass - Ruby class of the resource (e.g. Decidim::User)
19
- // - optional - Defines if file is optional
20
19
  // - multiple - Defines if multiple files can be uploaded
21
20
  // - titled - Defines if file(s) can have titles
22
21
  // - maxFileSize - Defines maximum file size in bytes
@@ -70,7 +70,9 @@ export default class ClipboardOverride extends Clipboard {
70
70
  const text = ev.clipboardData.getData("text/plain");
71
71
  const files = Array.from(ev.clipboardData.files || []);
72
72
  if (!html && files.length > 0) {
73
- this.quill.uploader.upload(range, files);
73
+ if (typeof this.quill.uploader !== "undefined") {
74
+ this.quill.uploader.upload(range, files);
75
+ }
74
76
  return;
75
77
  }
76
78
  if (html && files.length > 0) {
@@ -79,7 +81,9 @@ export default class ClipboardOverride extends Clipboard {
79
81
  doc.body.childElementCount === 1 &&
80
82
  doc.body.firstElementChild.tagName === "IMG"
81
83
  ) {
82
- this.quill.uploader.upload(range, files);
84
+ if (typeof this.quill.uploader !== "undefined") {
85
+ this.quill.uploader.upload(range, files);
86
+ }
83
87
  return;
84
88
  }
85
89
  }
@@ -1,11 +1,25 @@
1
1
  /* eslint-disable require-jsdoc */
2
2
 
3
- import lineBreakButtonHandler from "src/decidim/editor/linebreak_module"
4
- import "src/decidim/editor/clipboard_override"
5
- import "src/decidim/vendor/image-resize.min"
6
- import "src/decidim/vendor/image-upload.min"
3
+ import lineBreakButtonHandler from "src/decidim/editor/linebreak_module";
4
+ import "src/decidim/editor/clipboard_override";
5
+ import "src/decidim/vendor/image-resize.min";
6
+ import "src/decidim/vendor/image-upload.min";
7
7
 
8
- const quillFormats = ["bold", "italic", "link", "underline", "header", "list", "video", "image", "alt", "break", "width", "style", "code", "blockquote", "indent"];
8
+ const quillFormats = [
9
+ "bold",
10
+ "italic",
11
+ "link",
12
+ "underline",
13
+ "header",
14
+ "list",
15
+ "alt",
16
+ "break",
17
+ "width",
18
+ "style",
19
+ "code",
20
+ "blockquote",
21
+ "indent"
22
+ ];
9
23
 
10
24
  export default function createQuillEditor(container) {
11
25
  const toolbar = $(container).data("toolbar");
@@ -17,26 +31,28 @@ export default function createQuillEditor(container) {
17
31
  [{ list: "ordered" }, { list: "bullet" }],
18
32
  ["link", "clean"],
19
33
  ["code", "blockquote"],
20
- [{ "indent": "-1"}, { "indent": "+1" }]
34
+ [{ indent: "-1" }, { indent: "+1" }]
21
35
  ];
22
36
 
23
- let addImage = $(container).data("editorImages");
37
+ let addImage = false;
38
+ let addVideo = false;
24
39
 
25
- if (toolbar === "full") {
40
+ /**
41
+ * - basic = only basic controls without titles
42
+ * - content = basic + headings
43
+ * - full = basic + headings + image + video
44
+ */
45
+ if (toolbar === "content") {
46
+ quillToolbar = [[{ header: [2, 3, 4, 5, 6, false] }], ...quillToolbar];
47
+ } else if (toolbar === "full") {
48
+ addImage = true;
49
+ addVideo = true;
26
50
  quillToolbar = [
27
51
  [{ header: [2, 3, 4, 5, 6, false] }],
28
52
  ...quillToolbar,
29
- ["video"]
53
+ ["video"],
54
+ ["image"]
30
55
  ];
31
- } else if (toolbar === "basic") {
32
- quillToolbar = [
33
- ...quillToolbar,
34
- ["video"]
35
- ];
36
- }
37
-
38
- if (addImage) {
39
- quillToolbar.push(["image"]);
40
56
  }
41
57
 
42
58
  let modules = {
@@ -44,17 +60,26 @@ export default function createQuillEditor(container) {
44
60
  toolbar: {
45
61
  container: quillToolbar,
46
62
  handlers: {
47
- "linebreak": lineBreakButtonHandler
63
+ linebreak: lineBreakButtonHandler
48
64
  }
49
65
  }
50
66
  };
51
67
  const $input = $(container).siblings('input[type="hidden"]');
52
68
  container.innerHTML = $input.val() || "";
53
69
  const token = $('meta[name="csrf-token"]').attr("content");
70
+
71
+ if (addVideo) {
72
+ quillFormats.push("video");
73
+ }
74
+
54
75
  if (addImage) {
76
+ // Attempt to allow images only if the image support is enabled at editor support.
77
+ // see: https://github.com/quilljs/quill/issues/1108
78
+ quillFormats.push("image");
79
+
55
80
  modules.imageResize = {
56
81
  modules: ["Resize", "DisplaySize"]
57
- }
82
+ };
58
83
  modules.imageUpload = {
59
84
  url: $(container).data("uploadImagesPath"),
60
85
  method: "POST",
@@ -62,18 +87,23 @@ export default function createQuillEditor(container) {
62
87
  withCredentials: false,
63
88
  headers: { "X-CSRF-Token": token },
64
89
  callbackOK: (serverResponse, next) => {
65
- $("div.ql-toolbar").last().removeClass("editor-loading")
90
+ $("div.ql-toolbar").last().removeClass("editor-loading");
66
91
  next(serverResponse.url);
67
92
  },
68
93
  callbackKO: (serverError) => {
69
- $("div.ql-toolbar").last().removeClass("editor-loading")
94
+ $("div.ql-toolbar").last().removeClass("editor-loading");
70
95
  console.log(`Image upload error: ${serverError.message}`);
71
96
  },
72
97
  checkBeforeSend: (file, next) => {
73
- $("div.ql-toolbar").last().addClass("editor-loading")
98
+ $("div.ql-toolbar").last().addClass("editor-loading");
74
99
  next(file);
75
100
  }
76
- }
101
+ };
102
+
103
+ const text = $(container).data("dragAndDropHelpText");
104
+ $(container).after(
105
+ `<p class="help-text" style="margin-top:-1.5rem;">${text}</p>`
106
+ );
77
107
  }
78
108
  const quill = new Quill(container, {
79
109
  modules: modules,
@@ -81,6 +111,11 @@ export default function createQuillEditor(container) {
81
111
  theme: "snow"
82
112
  });
83
113
 
114
+ if (addImage === false) {
115
+ // Firefox natively implements image drop in contenteditable which is why we need to disable that
116
+ quill.root.addEventListener("drop", (ev) => ev.preventDefault());
117
+ }
118
+
84
119
  if (disabled) {
85
120
  quill.disable();
86
121
  }
@@ -95,7 +130,10 @@ export default function createQuillEditor(container) {
95
130
  });
96
131
  container.dispatchEvent(event);
97
132
 
98
- if ((text === "\n" || text === "\n\n") && quill.root.querySelectorAll(allowedEmptyContentSelector).length === 0) {
133
+ if (
134
+ (text === "\n" || text === "\n\n") &&
135
+ quill.root.querySelectorAll(allowedEmptyContentSelector).length === 0
136
+ ) {
99
137
  $input.val("");
100
138
  } else {
101
139
  const emptyParagraph = "<p><br></p>";
@@ -109,13 +147,5 @@ export default function createQuillEditor(container) {
109
147
  // After editor is ready, linebreak_module deletes two extraneous new lines
110
148
  quill.emitter.emit("editor-ready");
111
149
 
112
- if (addImage) {
113
- const text = $(container).data("dragAndDropHelpText");
114
- $(container).after(`<p class="help-text" style="margin-top:-1.5rem;">${text}</p>`);
115
- }
116
-
117
- // After editor is ready, linebreak_module deletes two extraneous new lines
118
- quill.emitter.emit("editor-ready");
119
-
120
150
  return quill;
121
151
  }
@@ -123,6 +123,10 @@
123
123
  }
124
124
  }
125
125
 
126
+ &.primary{
127
+ @include button-hollow-variant(var(--primary));
128
+ }
129
+
126
130
  &.secondary{
127
131
  @include button-hollow-variant(var(--secondary));
128
132
  }
@@ -257,12 +261,12 @@
257
261
 
258
262
  .button--shadow{
259
263
  $shadows: (
260
- primary: shade($primary, 50),
261
- secondary: shade($secondary, 50),
262
- success: shade($success, 50),
263
- warning: shade($warning, 50),
264
- alert: shade($alert, 50),
265
- muted: shade($muted, 50),
264
+ primary: shade($primary, 50%),
265
+ secondary: shade($secondary, 50%),
266
+ success: shade($success, 50%),
267
+ warning: shade($warning, 50%),
268
+ alert: shade($alert, 50%),
269
+ muted: shade($muted, 50%),
266
270
  );
267
271
 
268
272
  @include modifiers(background-color, $shadows){
@@ -934,7 +934,7 @@ a .card__title{
934
934
  @include modifiers(
935
935
  color,
936
936
  (
937
- muted: tint($muted, 50),
937
+ muted: tint($muted, 50%),
938
938
  )
939
939
  ){
940
940
  margin-top: -$global-margin * .95;
@@ -17,6 +17,30 @@ $comment-form-bg: $light-gray;
17
17
 
18
18
  .comment-thread{
19
19
  @extend .card;
20
+
21
+ .show-comment-replies{
22
+ display: none;
23
+ }
24
+
25
+ .hide-comment-replies{
26
+ display: inline;
27
+ }
28
+
29
+ .comment__hide{
30
+ float: left;
31
+ margin-bottom: 0;
32
+ }
33
+
34
+ .no-comments{
35
+ .show-comment-replies{
36
+ display: inline;
37
+ }
38
+
39
+ .hide-comment-replies,
40
+ .replies{
41
+ display: none;
42
+ }
43
+ }
20
44
  }
21
45
 
22
46
  .comment-thread__title{
@@ -26,7 +26,8 @@
26
26
  top: 2px;
27
27
  width: 2rem;
28
28
  height: 2rem;
29
- background: rgba(var(--primary-rgb), .8);
29
+ background: var(--primary);
30
+ opacity: .8;
30
31
  color: $white;
31
32
  border-radius: 50%;
32
33
  }
@@ -32,10 +32,6 @@
32
32
  border-width: 2px;
33
33
  border-radius: 4px;
34
34
 
35
- * >{
36
- display: none;
37
- }
38
-
39
35
  .form-error{
40
36
  margin: 0;
41
37
  }
@@ -36,8 +36,8 @@
36
36
  @mixin loop-colors-types($color, $max: 12){
37
37
  @for $i from 0 through ($max - 1){
38
38
  $interval: ($i % 4) * 24 + 1;
39
- $tints: tint($color, $interval);
40
- $shades: shade($color, $interval);
39
+ $tints: tint($color, $interval * 1%);
40
+ $shades: shade($color, $interval * 1%);
41
41
  $adjusts: adjust-color($color, $lightness: $interval * 1%, $hue: -$interval);
42
42
 
43
43
  .type-#{$i}:not(.legend){
@@ -27,8 +27,8 @@
27
27
  @mixin loop-colors-types($color, $max: 12){
28
28
  @for $i from 0 through ($max - 1){
29
29
  $interval: ($i % 4) * 24 + 1;
30
- $tints: tint($color, $interval);
31
- $shades: shade($color, $interval);
30
+ $tints: tint($color, $interval * 1%);
31
+ $shades: shade($color, $interval * 1%);
32
32
  $adjusts: adjust-color($color, $lightness: $interval * 1%, $hue: -$interval);
33
33
 
34
34
  .type-#{$i}{
@@ -11,7 +11,7 @@ module Decidim
11
11
 
12
12
  def created_at_in_words
13
13
  if created_at.between?(1.month.ago, Time.current)
14
- time_ago_in_words(created_at)
14
+ I18n.t("decidim.user_conversations.index.time_ago", time: time_ago_in_words(created_at))
15
15
  else
16
16
  format = created_at.year == Time.current.year ? :ddmm : :ddmmyyyy
17
17
  I18n.l(created_at, format: format)
@@ -27,6 +27,7 @@ module Decidim
27
27
  @event ||= event_class.constantize.new(
28
28
  resource: resource,
29
29
  user: user,
30
+ user_role: user_role,
30
31
  event_name: event_name,
31
32
  extra: extra
32
33
  )
@@ -16,7 +16,7 @@ module Decidim
16
16
  end
17
17
 
18
18
  def can_be_contacted?
19
- true
19
+ true unless blocked?
20
20
  end
21
21
 
22
22
  def officialization_text
@@ -61,7 +61,7 @@ module Decidim
61
61
  end
62
62
 
63
63
  def can_be_contacted?
64
- true
64
+ true unless blocked?
65
65
  end
66
66
 
67
67
  def officialization_text
@@ -14,12 +14,14 @@ module Decidim
14
14
  class AdminInputScrubber < UserInputScrubber
15
15
  private
16
16
 
17
+ DECIDIM_ALLOWED_TAGS = %w(img video audio source comment iframe).freeze
18
+
17
19
  def custom_allowed_attributes
18
20
  super + %w(frameborder allowfullscreen) - %w(onerror)
19
21
  end
20
22
 
21
23
  def custom_allowed_tags
22
- super + %w(comment iframe)
24
+ super + DECIDIM_ALLOWED_TAGS
23
25
  end
24
26
  end
25
27
  end