decidim-core 0.25.2 → 0.26.0.rc1

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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity_cell.rb +2 -1
  3. data/app/cells/decidim/author/flag_user.erb +1 -1
  4. data/app/cells/decidim/author/profile_inline.erb +1 -1
  5. data/app/cells/decidim/author/withdraw.erb +2 -2
  6. data/app/cells/decidim/author_cell.rb +32 -0
  7. data/app/cells/decidim/card_m_cell.rb +1 -1
  8. data/app/cells/decidim/content_blocks/cta_cell.rb +1 -1
  9. data/app/cells/decidim/content_blocks/hero_cell.rb +1 -1
  10. data/app/cells/decidim/content_blocks/highlighted_content_banner/show.erb +1 -1
  11. data/app/cells/decidim/content_blocks/last_activity_cell.rb +1 -1
  12. data/app/cells/decidim/content_blocks/stats_cell.rb +12 -0
  13. data/app/cells/decidim/endorsers_list_cell.rb +3 -1
  14. data/app/cells/decidim/flag_modal/flag_user.erb +2 -2
  15. data/app/cells/decidim/flag_modal/show.erb +2 -2
  16. data/app/cells/decidim/flag_modal_cell.rb +10 -0
  17. data/app/cells/decidim/notification/show.erb +31 -0
  18. data/app/cells/decidim/notification_cell.rb +20 -0
  19. data/app/cells/decidim/notifications/show.erb +1 -24
  20. data/app/cells/decidim/notifications_cell.rb +0 -1
  21. data/app/cells/decidim/user_conversation/conversation_header.erb +1 -1
  22. data/app/cells/decidim/user_conversation/show.erb +4 -2
  23. data/app/cells/decidim/user_conversations/conversation_item.erb +1 -1
  24. data/app/commands/decidim/create_editor_image.rb +41 -0
  25. data/app/controllers/decidim/cookie_policy_controller.rb +2 -0
  26. data/app/controllers/decidim/editor_images_controller.rb +47 -0
  27. data/app/controllers/decidim/user_activities_controller.rb +2 -1
  28. data/app/forms/decidim/editor_image_form.rb +16 -0
  29. data/app/helpers/decidim/amendments_helper.rb +1 -1
  30. data/app/helpers/decidim/application_helper.rb +2 -2
  31. data/app/helpers/decidim/messaging/conversation_helper.rb +32 -3
  32. data/app/helpers/decidim/resource_versions_helper.rb +1 -1
  33. data/app/helpers/decidim/sanitize_helper.rb +65 -0
  34. data/app/models/decidim/editor_image.rb +14 -0
  35. data/app/models/decidim/messaging/conversation.rb +9 -0
  36. data/app/models/decidim/participatory_space_private_user.rb +16 -0
  37. data/app/models/decidim/user.rb +3 -3
  38. data/app/models/decidim/user_group.rb +40 -0
  39. data/app/packs/entrypoints/decidim_core.js +1 -0
  40. data/app/packs/src/decidim/dialog_mode.js +143 -0
  41. data/app/packs/src/decidim/dialog_mode.test.js +168 -0
  42. data/app/packs/src/decidim/editor.js +56 -14
  43. data/app/packs/src/decidim/form_attachments.js +5 -0
  44. data/app/packs/src/decidim/index.js +4 -0
  45. data/app/packs/src/decidim/vendor/image-resize.min.js +3 -0
  46. data/app/packs/src/decidim/vendor/image-upload.min.js +8 -0
  47. data/app/packs/stylesheets/decidim/extras/_extras.scss +0 -1
  48. data/app/packs/stylesheets/decidim/extras/_quill.scss +7 -0
  49. data/app/packs/stylesheets/decidim/modules/_buttons.scss +11 -4
  50. data/app/packs/stylesheets/decidim/modules/_cards.scss +4 -0
  51. data/app/packs/stylesheets/decidim/modules/_layout.scss +1 -1
  52. data/app/presenters/decidim/nil_presenter.rb +2 -2
  53. data/app/presenters/decidim/notification_presenter.rb +25 -0
  54. data/app/presenters/decidim/official_author_presenter.rb +1 -1
  55. data/app/presenters/decidim/validation_errors_presenter.rb +27 -0
  56. data/app/queries/decidim/similar_emendations.rb +1 -1
  57. data/app/resolvers/decidim/core/metric_resolver.rb +1 -1
  58. data/app/services/decidim/activity_search.rb +2 -2
  59. data/app/services/decidim/email_notification_generator.rb +4 -1
  60. data/app/services/decidim/html_truncation.rb +130 -0
  61. data/app/services/decidim/open_data_exporter.rb +29 -5
  62. data/app/services/decidim/resource_search.rb +1 -1
  63. data/app/uploaders/decidim/editor_image_uploader.rb +6 -0
  64. data/app/validators/password_validator.rb +123 -0
  65. data/app/views/decidim/account/_password_fields.html.erb +1 -1
  66. data/app/views/decidim/devise/passwords/edit.html.erb +1 -1
  67. data/app/views/decidim/devise/registrations/new.html.erb +1 -1
  68. data/app/views/decidim/devise/shared/_omniauth_buttons_mini.html.erb +6 -4
  69. data/app/views/decidim/messaging/conversations/_conversation.html.erb +3 -3
  70. data/app/views/decidim/messaging/conversations/_messages.html.erb +8 -2
  71. data/app/views/decidim/messaging/conversations/_show.html.erb +10 -12
  72. data/app/views/decidim/messaging/conversations/show.html.erb +4 -2
  73. data/app/views/decidim/newsletters/show.html.erb +1 -1
  74. data/app/views/decidim/notification_mailer/event_received.html.erb +17 -0
  75. data/app/views/decidim/pages/_tabbed.html.erb +1 -1
  76. data/app/views/decidim/searches/_filters_small_view.html.erb +3 -3
  77. data/app/views/decidim/shared/_login_modal.html.erb +5 -5
  78. data/app/views/decidim/shared/_orders.html.erb +1 -1
  79. data/app/views/decidim/shared/_results_per_page.html.erb +1 -1
  80. data/app/views/decidim/shared/participatory_space_filters/_filters_small_view.html.erb +3 -3
  81. data/app/views/layouts/decidim/_application.html.erb +1 -12
  82. data/app/views/layouts/decidim/_head.html.erb +4 -0
  83. data/app/views/layouts/decidim/_language_chooser.html.erb +1 -1
  84. data/app/views/layouts/decidim/_meta_tags_config.html.erb +11 -0
  85. data/app/views/layouts/decidim/_wrapper.html.erb +1 -1
  86. data/config/brakeman.ignore +149 -0
  87. data/config/initializers/devise.rb +1 -1
  88. data/config/initializers/rack_attack.rb +23 -21
  89. data/config/locales/ca.yml +2 -0
  90. data/config/locales/cs.yml +60 -0
  91. data/config/locales/en.yml +45 -0
  92. data/config/locales/es.yml +45 -0
  93. data/config/locales/eu.yml +5 -0
  94. data/config/locales/fi-plain.yml +6 -0
  95. data/config/locales/fi.yml +45 -0
  96. data/config/locales/fr-CA.yml +38 -0
  97. data/config/locales/fr.yml +44 -6
  98. data/config/locales/gl.yml +5 -0
  99. data/config/locales/it.yml +11 -0
  100. data/config/locales/ja.yml +72 -36
  101. data/config/locales/lb-LU.yml +1354 -0
  102. data/config/locales/lb.yml +1 -1
  103. data/config/locales/nl.yml +54 -0
  104. data/config/locales/pl.yml +5 -5
  105. data/config/locales/pt-BR.yml +1 -1
  106. data/config/locales/ro-RO.yml +8 -0
  107. data/config/locales/sv.yml +5 -0
  108. data/config/locales/val-ES.yml +1 -0
  109. data/config/routes.rb +2 -0
  110. data/db/migrate/20210730112319_create_decidim_editor_images.rb +12 -0
  111. data/db/migrate/20211126183540_add_timestamps_to_content_blocks.rb +14 -0
  112. data/db/seeds.rb +16 -14
  113. data/lib/decidim/api/functions/user_entity_list.rb +1 -0
  114. data/lib/decidim/api/input_sorts/component_input_sort.rb +1 -1
  115. data/lib/decidim/common_passwords.rb +56 -0
  116. data/lib/decidim/content_parsers/inline_images_parser.rb +68 -0
  117. data/lib/decidim/content_parsers.rb +1 -0
  118. data/lib/decidim/content_renderers/link_renderer.rb +85 -1
  119. data/lib/decidim/content_renderers/user_group_renderer.rb +1 -1
  120. data/lib/decidim/content_renderers/user_renderer.rb +1 -1
  121. data/lib/decidim/core/engine.rb +3 -12
  122. data/lib/decidim/core/test/factories.rb +7 -1
  123. data/lib/decidim/core/test/shared_examples/translated_event_examples.rb +131 -0
  124. data/lib/decidim/core/test.rb +1 -0
  125. data/lib/decidim/core/version.rb +1 -1
  126. data/lib/decidim/core.rb +22 -5
  127. data/lib/decidim/db/common-passwords.txt +128420 -0
  128. data/lib/decidim/etherpad/pad.rb +48 -0
  129. data/lib/decidim/etherpad.rb +7 -0
  130. data/lib/decidim/events/base_event.rb +18 -0
  131. data/lib/decidim/events/machine_translated_event.rb +36 -0
  132. data/lib/decidim/events/user_group_event.rb +1 -3
  133. data/lib/decidim/events.rb +1 -0
  134. data/lib/decidim/exporters/csv.rb +7 -7
  135. data/lib/decidim/faker/localized.rb +15 -6
  136. data/lib/decidim/form_builder.rb +14 -4
  137. data/lib/decidim/has_attachments.rb +11 -4
  138. data/lib/decidim/importers/import_manifest.rb +103 -3
  139. data/lib/decidim/paddable.rb +1 -9
  140. data/lib/decidim/searchable.rb +2 -2
  141. data/lib/decidim/settings_manifest.rb +2 -0
  142. data/lib/decidim/translatable_attributes.rb +6 -6
  143. data/lib/decidim/view_model.rb +10 -0
  144. data/lib/tasks/decidim_active_storage_migration_tasks.rake +68 -0
  145. metadata +56 -65
  146. data/app/packs/stylesheets/decidim/extras/_social_icons_mini.scss +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 874945a9b1099eec0e81862510812650bdaef2233da2ab8717150841dc242885
4
- data.tar.gz: 4f951811ef9152312c943efc46c1da849f3686f6af331ebc5b2dfb959b275916
3
+ metadata.gz: dac5187d40c4a753efdf1ca3e005b5a0d6561fcde905fb50bbf054fac2eca159
4
+ data.tar.gz: 6ed72398e69f803c6ab3b328ff9b26ec217769a2878349c00fbadf76b65e120e
5
5
  SHA512:
6
- metadata.gz: 472a3e9b094066455486eb1d43b504009e8903772fce927950f091b433ff83bc8471847fcb919a6996a2ffb688159f5b4cd5c2611763511fedfc96db045505f3
7
- data.tar.gz: 27d8a8201a9add23103bdc8c6b4173108cb16873cc6b1727be0a487fd88735c871bf9fce41771e1086e40a99df013b305ff04d165b3651d7896fa0e0154df09b
6
+ metadata.gz: 532bcf1725b00692cf50880c865cd74b5e81f13870f65db9d9b61c227fb78b997ad5b1eff984f041a900b9e117a29d3c62d72097506ddd15bd40320c7c839dc2
7
+ data.tar.gz: dfe236abe5ba215792e259e1e93d54a82c8ac60120b2f4af166e1e86343aefc9cf3ba7eb743987ec65b96202accd6c8cf53d8aebc0f4fd9ac3e5d028f4f12ee0
@@ -92,10 +92,11 @@ module Decidim
92
92
 
93
93
  def cache_hash
94
94
  hash = []
95
+ hash << I18n.locale.to_s
95
96
  hash << model.class.name.underscore
96
97
  hash << model.cache_key_with_version
97
98
 
98
- hash.join("/")
99
+ hash.join(Decidim.cache_key_separator)
99
100
  end
100
101
 
101
102
  private
@@ -1,5 +1,5 @@
1
1
  <% if user_flaggable? && model.try(:id) != current_user.try(:id) %>
2
- <button type="button" class="link-alt" data-open="<%= current_user.present? ? "flagUserModal" : "loginModal" %>" title="<%= t("report", scope: "decidim.proposals.proposals.show") %>" aria-controls="<%= current_user.present? ? "flagUserModal" : "loginModal" %>" aria-haspopup="true" tabindex="0">
2
+ <button type="button" class="link-alt" data-open="<%= current_user.present? ? "flagUserModal" : "loginModal" %>" title="<%= t("report", scope: "decidim.proposals.proposals.show") %>" aria-controls="<%= current_user.present? ? "flagUserModal" : "loginModal" %>" aria-haspopup="dialog" tabindex="0">
3
3
  <%= icon "flag", aria_hidden: true, class: "icon--small", role: "img", "aria-hidden": true %>
4
4
  <span class="show-for-sr">
5
5
  <%= t("report", scope: "decidim.proposals.proposals.show") %>
@@ -1,5 +1,5 @@
1
1
  <span class="author__avatar">
2
- <%= image_tag model.avatar_url, alt: t("decidim.author.avatar", name: decidim_sanitize(author_name)) %>
2
+ <%= image_tag model.avatar_url(:thumb), alt: t("decidim.author.avatar", name: decidim_sanitize(author_name)) %>
3
3
  </span>
4
4
 
5
5
  <% if model.deleted? %>
@@ -1,6 +1,6 @@
1
1
  <% if withdrawable? %>
2
- <%= action_authorized_link_to :withdraw, withdraw_path, method: :put, class: "title-action__action button small hollow", title: t("withdraw_btn_hint", scope: "decidim.proposals.proposals.show"), data: { confirm: t("withdraw_confirmation_html", scope: "decidim.proposals.proposals.show") } do %>
3
- <%= t("withdraw_proposal", scope: "decidim.proposals.proposals.show") %>
2
+ <%= action_authorized_link_to :withdraw, withdraw_path, method: :put, class: "title-action__action button small hollow", title: t("withdraw_btn_hint", scope: resource_i18n_scope ), data: { confirm: t("withdraw_confirmation_html", scope: resource_i18n_scope ) } do %>
3
+ <%= t("withdraw_#{resource_name}", scope: resource_i18n_scope) %>
4
4
  <%= icon "x", role: "img", "aria-hidden": true %>
5
5
  <% end %>
6
6
  <% end %>
@@ -55,8 +55,28 @@ module Decidim
55
55
  render
56
56
  end
57
57
 
58
+ def perform_caching?
59
+ true
60
+ end
61
+
58
62
  private
59
63
 
64
+ def cache_hash
65
+ hash = []
66
+
67
+ hash.push(I18n.locale)
68
+ hash.push(model.cache_key_with_version) if model.respond_to?(:cache_key_with_version)
69
+ hash.push(current_user.try(:id))
70
+ hash.push(current_user.present?)
71
+ hash.push(commentable?)
72
+ hash.push(endorsable?)
73
+ hash.push(actionable?)
74
+ hash.push(withdrawable?)
75
+ hash.push(flaggable?)
76
+ hash.push(profile_path?)
77
+ hash.join(Decidim.cache_key_separator)
78
+ end
79
+
60
80
  def from_context_path
61
81
  resource_locator(from_context).path
62
82
  end
@@ -111,5 +131,17 @@ module Decidim
111
131
  def raw_model
112
132
  model.try(:__getobj__) || model
113
133
  end
134
+
135
+ def resource_i18n_scope
136
+ @resource_i18n_scope ||= [
137
+ from_context.class.name.deconstantize.underscore.gsub("/", "."),
138
+ resource_name.pluralize,
139
+ :show
140
+ ].join(".")
141
+ end
142
+
143
+ def resource_name
144
+ @resource_name ||= from_context.class.name.demodulize.underscore
145
+ end
114
146
  end
115
147
  end
@@ -64,7 +64,7 @@ module Decidim
64
64
  attribute = model.try(:short_description) || model.try(:body) || model.description
65
65
  text = translated_attribute(attribute)
66
66
 
67
- decidim_sanitize(html_truncate(text, length: 100))
67
+ decidim_sanitize_editor(html_truncate(text, length: 100))
68
68
  end
69
69
 
70
70
  def has_authors?
@@ -16,7 +16,7 @@ module Decidim
16
16
  end
17
17
 
18
18
  def translated_description
19
- @translated_description ||= decidim_sanitize(translated_attribute(model.settings.description))
19
+ @translated_description ||= decidim_sanitize_editor(translated_attribute(model.settings.description))
20
20
  end
21
21
 
22
22
  def button_url
@@ -30,7 +30,7 @@ module Decidim
30
30
  hash << current_organization.cache_key_with_version
31
31
  hash << I18n.locale.to_s
32
32
 
33
- hash.join("/")
33
+ hash.join(Decidim.cache_key_separator)
34
34
  end
35
35
  end
36
36
  end
@@ -7,7 +7,7 @@
7
7
  <%= translated_attribute current_organization.highlighted_content_banner_title %>
8
8
  </h1>
9
9
  <span class="text-highlight">
10
- <%= decidim_sanitize translated_attribute current_organization.highlighted_content_banner_short_description %>
10
+ <%= decidim_sanitize_editor translated_attribute current_organization.highlighted_content_banner_short_description %>
11
11
  </span>
12
12
  </div>
13
13
  <div class="columns large-2">
@@ -52,7 +52,7 @@ module Decidim
52
52
  hash << Digest::MD5.hexdigest(valid_activities.map(&:cache_key_with_version).to_s)
53
53
  hash << I18n.locale.to_s
54
54
 
55
- hash.join("/")
55
+ hash.join(Decidim.cache_key_separator)
56
56
  end
57
57
 
58
58
  def activities
@@ -3,9 +3,21 @@
3
3
  module Decidim
4
4
  module ContentBlocks
5
5
  class StatsCell < Decidim::ViewModel
6
+ cache :show, expires_in: 10.minutes, if: :perform_caching? do
7
+ cache_hash
8
+ end
9
+
6
10
  def stats
7
11
  @stats ||= HomeStatsPresenter.new(organization: current_organization)
8
12
  end
13
+
14
+ private
15
+
16
+ def cache_hash
17
+ hash = []
18
+ hash.push(I18n.locale)
19
+ hash.join(Decidim.cache_key_separator)
20
+ end
9
21
  end
10
22
  end
11
23
  end
@@ -23,7 +23,9 @@ module Decidim
23
23
  #
24
24
  # Returns an Array of presented Users/UserGroups
25
25
  def endorsers
26
- @endorsers ||= model.endorsements.for_listing.map { |identity| present(identity.normalized_author) }
26
+ @endorsers ||= model.endorsements.for_listing
27
+ .includes(:author, :user_group)
28
+ .map { |identity| present(identity.normalized_author) }
27
29
  end
28
30
  end
29
31
  end
@@ -1,6 +1,6 @@
1
- <div class="reveal flag-modal" id="flagUserModal" data-reveal>
1
+ <div class="reveal flag-modal" id="flagUserModal" data-reveal role="dialog" aria-modal="true" aria-labelledby="flagUserModal-label">
2
2
  <div class="reveal__header">
3
- <h3 class="reveal__title"><%= t("decidim.shared.flag_user_modal.title") %></h3>
3
+ <h3 id="flagUserModal-label" class="reveal__title"><%= t("decidim.shared.flag_user_modal.title") %></h3>
4
4
  <button class="close-button" data-close aria-label="<%= t("decidim.shared.flag_user_modal.close") %>" type="button">
5
5
  <span aria-hidden="true">&times;</span>
6
6
  </button>
@@ -1,6 +1,6 @@
1
- <div class="reveal flag-modal" id="<%= modal_id %>" data-reveal>
1
+ <div class="reveal flag-modal" id="<%= modal_id %>" data-reveal role="dialog" aria-modal="true" aria-labelledby="<%= modal_id %>-label">
2
2
  <div class="reveal__header">
3
- <h3 class="reveal__title"><%= t("decidim.shared.flag_modal.title") %></h3>
3
+ <h3 id="<%= modal_id %>-label" class="reveal__title"><%= t("decidim.shared.flag_modal.title") %></h3>
4
4
  <button class="close-button" data-close aria-label="<%= t("decidim.shared.flag_modal.close") %>" type="button">
5
5
  <span aria-hidden="true">&times;</span>
6
6
  </button>
@@ -8,6 +8,16 @@ module Decidim
8
8
  render
9
9
  end
10
10
 
11
+ def cache_hash
12
+ hash = []
13
+ hash.push(I18n.locale)
14
+ hash.push(current_user.try(:id))
15
+ hash.push(model.reported_by?(current_user) ? 1 : 0)
16
+ hash.push(model.class.name.gsub("::", ":"))
17
+ hash.push(model.id)
18
+ hash.join(Decidim.cache_key_separator)
19
+ end
20
+
11
21
  private
12
22
 
13
23
  def user_report_form
@@ -0,0 +1,31 @@
1
+ <div class="card card--widget">
2
+ <ul class="card-data">
3
+ <li class="card-data__item">
4
+ <div class="card__link text-center">
5
+ <%= resource_icon notification.resource, class: "icon--large" %>
6
+ <span class="text-medium mt-xs" title="<%= l(notification.created_at) %>" data-tooltip="true" data-disable-hover="false">
7
+ <%= notification.created_at_in_words %>
8
+ </span>
9
+ </div>
10
+ </li>
11
+ <li class="card-data__item card-data__item--expand absolutes">
12
+ <div class="mr-s">
13
+ <span class="text-small"><%= notification.event_class.constantize.model_name.human %></span>
14
+ <br>
15
+ <span>
16
+ <%= notification.event_class_instance.notification_title %>
17
+ </span>
18
+ <% if notification.display_resource_text? %>
19
+ <p>
20
+ <em><%= notification.resource_text %></em>
21
+ </p>
22
+ <% end %>
23
+ </div>
24
+ <div class="right center mr-s">
25
+ <%= link_to model, remote: true, method: :delete, class: "mark-as-read-button" do %>
26
+ <%= icon "circle-x", class: "card__link", aria_label: t("mark_as_read", scope: "layouts.decidim.notifications_dashboard"), role: "img" %>
27
+ <% end %>
28
+ </div>
29
+ </li>
30
+ </ul>
31
+ </div>
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This cell renders a notification from a notifications collection
5
+
6
+ class NotificationCell < Decidim::ViewModel
7
+ include Decidim::IconHelper
8
+ include Decidim::Core::Engine.routes.url_helpers
9
+
10
+ def show
11
+ render :show
12
+ end
13
+
14
+ private
15
+
16
+ def notification
17
+ @notification ||= Decidim::NotificationPresenter.new(model)
18
+ end
19
+ end
20
+ end
@@ -17,30 +17,7 @@
17
17
  </div>
18
18
 
19
19
  <% notifications.select(&:resource).each do |notification| %>
20
- <div class="card card--widget">
21
- <ul class="card-data">
22
- <li class="card-data__item">
23
- <div class="card__link text-center">
24
- <%= resource_icon notification.resource, class: "icon--large" %>
25
- <span class="text-medium mt-xs"><%= l notification.created_at, format: :day_of_week_long %></span>
26
- </div>
27
- </li>
28
- <li class="card-data__item card-data__item--expand absolutes">
29
- <div class="mr-s">
30
- <span class="text-small"><%= notification.event_class.constantize.model_name.human %></span>
31
- <br>
32
- <span>
33
- <%= notification.event_class_instance.notification_title %>
34
- </span>
35
- </div>
36
- <div class="right center mr-s">
37
- <%= link_to notification, remote: true, method: :delete, class: "mark-as-read-button" do %>
38
- <%= icon "circle-x", class: "card__link", aria_label: t("mark_as_read", scope: "layouts.decidim.notifications_dashboard"), role: "img" %>
39
- <% end %>
40
- </div>
41
- </li>
42
- </ul>
43
- </div>
20
+ <%= cell("decidim/notification", notification) %>
44
21
  <% end %>
45
22
  </div>
46
23
 
@@ -3,7 +3,6 @@
3
3
  module Decidim
4
4
  class NotificationsCell < Decidim::ViewModel
5
5
  include Decidim::CellsPaginateHelper
6
- include Decidim::IconHelper
7
6
  include Decidim::Core::Engine.routes.url_helpers
8
7
 
9
8
  helper_method :notifications
@@ -9,6 +9,6 @@
9
9
  <% end %>
10
10
 
11
11
  <div class="ml-s">
12
- <%= t("decidim.user_conversations.show.title", usernames: interlocutors_names) %>
12
+ <%= t("decidim.user_conversations.show.title", usernames: interlocutors_names).html_safe %>
13
13
  </div>
14
14
  </div>
@@ -10,10 +10,12 @@
10
10
  <% end %>
11
11
  </div>
12
12
 
13
- <% if conversation.accept_user?(user) %>
13
+ <% if conversation.with_deleted_users?(user) %>
14
+ <div class="callout warning margin-top-2"><%= t "decidim.user_conversations.show.deleted_accounts" %></div>
15
+ <% elsif conversation.accept_user?(user) %>
14
16
  <%= render view: :reply, locals: { title: t("decidim.user_conversations.reply.title_reply") } %>
15
17
  <% else %>
16
- <em><%= t "decidim.user_conversations.show.not_allowed" %></em>
18
+ <div class="callout warning margin-top-2"><%= t "decidim.user_conversations.show.not_allowed" %></div>
17
19
  <% end %>
18
20
  </div>
19
21
  </div>
@@ -10,7 +10,7 @@
10
10
  </li>
11
11
  <li class="card-data__item card--list__item card-data__item--expand absolutes">
12
12
  <div class="mr-s">
13
- <%= t("decidim.user_conversations.index.from") %>: <strong><%= conversation_interlocutors(conversation) %></strong>
13
+ <%= t("decidim.user_conversations.index.from") %>: <strong><%= conversation_interlocutors(conversation).html_safe %></strong>
14
14
  <br>
15
15
  <span class="muted">
16
16
  <%= truncate conversation.last_message.body, length: 150 %>
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # A command with all the business logic to create an editor image.
5
+ class CreateEditorImage < Rectify::Command
6
+ # Public: Initializes the command.
7
+ #
8
+ # form - A form object with the params.
9
+ def initialize(form)
10
+ @form = form
11
+ end
12
+
13
+ # Executes the command. Broadcasts these events:
14
+ #
15
+ # - :ok when everything is valid.
16
+ # - :invalid if the form wasn't valid and we couldn't proceed.
17
+ #
18
+ # Returns nothing.
19
+ def call
20
+ return broadcast(:invalid) if form.invalid?
21
+
22
+ transaction do
23
+ create_editor_image
24
+ end
25
+
26
+ broadcast(:ok, @editor_image)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :form
32
+
33
+ def create_editor_image
34
+ @editor_image = EditorImage.create!(
35
+ decidim_author_id: form.current_user.id,
36
+ organization: form.organization,
37
+ file: form.file
38
+ )
39
+ end
40
+ end
41
+ end
@@ -10,6 +10,8 @@ module Decidim
10
10
  Decidim.config.consent_cookie_name,
11
11
  value: "true",
12
12
  path: "/",
13
+ httponly: true,
14
+ secure: request.session_options[:secure],
13
15
  expires: 1.year.from_now.utc
14
16
  )
15
17
 
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ class EditorImagesController < Decidim::ApplicationController
5
+ include FormFactory
6
+
7
+ # overwrite original rescue_from to ensure we print messages from ajax methods (update)
8
+ rescue_from Decidim::ActionForbidden, with: :ajax_user_has_no_permission
9
+
10
+ def create
11
+ enforce_permission_to :create, :editor_image
12
+
13
+ @form = form(EditorImageForm).from_params(form_values)
14
+
15
+ CreateEditorImage.call(@form) do
16
+ on(:ok) do |image|
17
+ render json: { url: image.attached_uploader(:file).path, message: I18n.t("success", scope: "decidim.editor_images.create") }
18
+ end
19
+
20
+ on(:invalid) do |_message|
21
+ render json: { message: I18n.t("error", scope: "decidim.editor_images.create") }, status: :unprocessable_entity
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ # Rescue ajax calls and print the update.js view which prints the info on the message ajax form
29
+ # Only if the request is AJAX, otherwise behave as Decidim standards
30
+ def ajax_user_has_no_permission
31
+ return user_has_no_permission unless request.xhr?
32
+
33
+ render json: { message: I18n.t("actions.unauthorized", scope: "decidim.core") }, status: :unprocessable_entity
34
+ end
35
+
36
+ def form_values
37
+ {
38
+ file: params[:image],
39
+ author_id: current_user.id
40
+ }
41
+ end
42
+
43
+ def permission_scope
44
+ :admin
45
+ end
46
+ end
47
+ end
@@ -12,7 +12,8 @@ module Decidim
12
12
  helper_method :activities, :resource_types, :user
13
13
 
14
14
  def index
15
- raise ActionController::RoutingError, "Blocked User" if user&.blocked? && !current_user&.admin?
15
+ raise ActionController::RoutingError, "Missing user: #{params[:nickname]}" unless user
16
+ raise ActionController::RoutingError, "Blocked User" if user.blocked? && !current_user&.admin?
16
17
  end
17
18
 
18
19
  private
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ class EditorImageForm < Decidim::Form
5
+ mimic :editor_image
6
+
7
+ attribute :file
8
+ attribute :author_id, Integer
9
+
10
+ validates :author_id, presence: true
11
+ validates :file, presence: true
12
+ validates :file, passthru: { to: Decidim::EditorImage }
13
+
14
+ alias organization current_organization
15
+ end
16
+ end
@@ -136,7 +136,7 @@ module Decidim
136
136
 
137
137
  return body unless rich_text_editor_in_public_views?
138
138
 
139
- decidim_sanitize(body)
139
+ decidim_sanitize_editor(body)
140
140
  end
141
141
 
142
142
  # Return the edited field value or presents the original attribute value in a form.
@@ -15,7 +15,7 @@ module Decidim
15
15
  # options - A Hash with the options to truncate the text (default: {}):
16
16
  # :length - An Integer number with the max length of the text.
17
17
  # :separator - A String to append to the text when it's being
18
- # truncated. See `truncato` gem for more options.
18
+ # truncated.
19
19
  #
20
20
  # Returns a String.
21
21
  def html_truncate(text, options = {})
@@ -25,7 +25,7 @@ module Decidim
25
25
  options[:count_tail] ||= false
26
26
  options[:tail_before_final_tag] = true unless options.has_key?(:tail_before_final_tag)
27
27
 
28
- Truncato.truncate(text, options)
28
+ Decidim::HtmlTruncation.new(text, options).perform
29
29
  end
30
30
 
31
31
  def present(object, presenter_class: nil)
@@ -3,14 +3,43 @@
3
3
  module Decidim
4
4
  module Messaging
5
5
  module ConversationHelper
6
+ def conversation_name_for(users)
7
+ return content_tag(:span, t("decidim.profile.deleted"), class: "label label--small label--basic") if users.first.deleted?
8
+
9
+ content_tag = content_tag(:strong, users.first.name)
10
+ content_tag << tag.br
11
+ content_tag << content_tag(:span, "@#{users.first.nickname}", class: "muted")
12
+ content_tag
13
+ end
14
+
15
+ def conversation_label_for(participants)
16
+ return t("title", scope: "decidim.messaging.conversations.show", usernames: username_list(participants)) unless participants.count == 1
17
+
18
+ chat_with_user = if participants.first.deleted?
19
+ t("decidim.profile.deleted")
20
+ else
21
+ "#{participants.first.name} (@#{participants.first.nickname})"
22
+ end
23
+
24
+ "#{t("chat_with", scope: "decidim.messaging.conversations.show")} #{chat_with_user}"
25
+ end
26
+
6
27
  #
7
28
  # Generates a visualization of users for listing conversations threads
8
29
  #
9
30
  def username_list(users, shorten: false)
10
- return users.pluck(:name).join(", ") unless shorten
11
- return users.pluck(:name).join(", ") unless users.count > 3
31
+ content_tags = []
32
+ first_users = shorten ? users.first(3) : users
33
+ deleted_user_tag = content_tag(:span, t("decidim.profile.deleted"), class: "label label--small label--basic")
34
+ first_users.each do |u|
35
+ content_tags.push(u.deleted? ? deleted_user_tag : content_tag(:strong, u.name))
36
+ end
37
+
38
+ return content_tags.join(", ") unless shorten
39
+ return content_tags.join(", ") unless users.count > 3
12
40
 
13
- "#{users.first(3).pluck(:name).join(", ")} + #{users.count - 3}"
41
+ content_tags.push(content_tag(:strong, " + #{users.count - 3}"))
42
+ content_tags.join(", ")
14
43
  end
15
44
 
16
45
  #
@@ -13,7 +13,7 @@ module Decidim
13
13
  #
14
14
  # Returns a String.
15
15
  def resource_version(resource, options = {})
16
- return unless resource.respond_to?(:versions) && resource.versions.present?
16
+ return unless resource.respond_to?(:versions) && resource.versions_count.positive?
17
17
 
18
18
  html = []
19
19
  html << resource_version_number(resource.versions_count)
@@ -5,6 +5,7 @@ module Decidim
5
5
  module SanitizeHelper
6
6
  def self.included(base)
7
7
  base.include ActionView::Helpers::SanitizeHelper
8
+ base.include ActionView::Helpers::TagHelper
8
9
  end
9
10
 
10
11
  # Public: It sanitizes a user-inputted string with the
@@ -30,6 +31,10 @@ module Decidim
30
31
  end
31
32
  end
32
33
 
34
+ def decidim_sanitize_editor(html, options = {})
35
+ content_tag(:div, decidim_sanitize(html, options), class: %w(ql-editor ql-reset-decidim))
36
+ end
37
+
33
38
  def decidim_html_escape(text)
34
39
  ERB::Util.unwrapped_html_escape(text.to_str)
35
40
  end
@@ -37,5 +42,65 @@ module Decidim
37
42
  def decidim_url_escape(text)
38
43
  decidim_html_escape(text).sub(/^javascript:/, "")
39
44
  end
45
+
46
+ private
47
+
48
+ # Maintains the paragraphs and lists separations with their bullet points and
49
+ # list numberings where appropriate.
50
+ #
51
+ # Returns a String.
52
+ def sanitize_text(text)
53
+ add_line_feeds(sanitize_ordered_lists(sanitize_unordered_lists(text)))
54
+ end
55
+
56
+ def sanitize_unordered_lists(text)
57
+ text.gsub(%r{(?=.*</ul>)(?!.*?<li>.*?</ol>.*?</ul>)<li>}) { |li| "#{li}• " }
58
+ end
59
+
60
+ def sanitize_ordered_lists(text)
61
+ i = 0
62
+
63
+ text.gsub(%r{(?=.*</ol>)(?!.*?<li>.*?</ul>.*?</ol>)<li>}) do |li|
64
+ i += 1
65
+
66
+ li + "#{i}. "
67
+ end
68
+ end
69
+
70
+ def add_line_feeds_to_paragraphs(text)
71
+ text.gsub("</p>") { |p| "#{p}\n\n" }
72
+ end
73
+
74
+ def add_line_feeds_to_list_items(text)
75
+ text.gsub("</li>") { |li| "#{li}\n" }
76
+ end
77
+
78
+ # Adds line feeds after the paragraph and list item closing tags.
79
+ #
80
+ # Returns a String.
81
+ def add_line_feeds(text)
82
+ add_line_feeds_to_paragraphs(add_line_feeds_to_list_items(text))
83
+ end
84
+
85
+ def content_handle_locale(body, all_locales, extras, links, strip_tags)
86
+ handle_locales(body, all_locales) do |content|
87
+ content = strip_tags(sanitize_text(content)) if strip_tags
88
+
89
+ renderer = Decidim::ContentRenderers::HashtagRenderer.new(content)
90
+ content = renderer.render(links: links, extras: extras).html_safe
91
+
92
+ content = Decidim::ContentRenderers::LinkRenderer.new(content).render if links
93
+ content
94
+ end
95
+ end
96
+
97
+ def render_sanitized_content(resource, method)
98
+ content = present(resource).send(method, links: true, strip_tags: !safe_content?)
99
+ content = simple_format(content, {}, sanitize: false)
100
+
101
+ return content unless safe_content?
102
+
103
+ decidim_sanitize_editor(content)
104
+ end
40
105
  end
41
106
  end