decidim-core 0.29.1 → 0.29.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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity_cell.rb +0 -3
  3. data/app/cells/decidim/author/show.erb +5 -4
  4. data/app/cells/decidim/author_cell.rb +26 -0
  5. data/app/cells/decidim/card_s/show.erb +5 -3
  6. data/app/cells/decidim/diff_cell.rb +4 -0
  7. data/app/cells/decidim/newsletter_templates/image_text_cta_cell.rb +1 -1
  8. data/app/cells/decidim/participatory_space_dropdown_metadata/show.erb +5 -3
  9. data/app/cells/decidim/profile_actions/show.erb +1 -1
  10. data/app/cells/decidim/report_button/already_reported_modal.erb +2 -2
  11. data/app/cells/decidim/report_button/flag_modal.erb +13 -27
  12. data/app/cells/decidim/report_button_cell.rb +2 -8
  13. data/app/cells/decidim/report_user_button/already_reported_modal.erb +11 -0
  14. data/app/cells/decidim/report_user_button/flag_modal.erb +46 -0
  15. data/app/cells/decidim/report_user_button/show.erb +2 -0
  16. data/app/cells/decidim/report_user_button_cell.rb +59 -0
  17. data/app/cells/decidim/resource_types_filter/show.erb +1 -1
  18. data/app/cells/decidim/resource_types_filter_cell.rb +6 -6
  19. data/app/cells/decidim/translation_bar/show.erb +2 -2
  20. data/app/cells/decidim/translation_bar_cell.rb +1 -1
  21. data/app/cells/decidim/user_activity/show.erb +1 -1
  22. data/app/commands/decidim/create_omniauth_registration.rb +14 -8
  23. data/app/commands/decidim/create_report.rb +1 -6
  24. data/app/commands/decidim/destroy_account.rb +3 -0
  25. data/app/commands/decidim/search.rb +14 -0
  26. data/app/controllers/decidim/doorkeeper/credentials_controller.rb +1 -1
  27. data/app/controllers/decidim/links_controller.rb +1 -1
  28. data/app/controllers/decidim/profiles_controller.rb +6 -2
  29. data/app/controllers/decidim/reports_controller.rb +1 -1
  30. data/app/controllers/decidim/user_activities_controller.rb +1 -1
  31. data/app/forms/decidim/account_form.rb +5 -2
  32. data/app/helpers/concerns/decidim/user_role_checker.rb +46 -0
  33. data/app/helpers/decidim/cta_button_helper.rb +1 -1
  34. data/app/helpers/decidim/map_helper.rb +6 -1
  35. data/app/helpers/decidim/orders_helper.rb +2 -1
  36. data/app/helpers/decidim/participatory_space_helpers.rb +1 -1
  37. data/app/helpers/decidim/sanitize_helper.rb +11 -2
  38. data/app/jobs/decidim/hide_child_resources_job.rb +24 -0
  39. data/app/mailers/decidim/reported_mailer.rb +1 -0
  40. data/app/models/decidim/action_log.rb +1 -9
  41. data/app/models/decidim/attachment.rb +1 -1
  42. data/app/models/decidim/report.rb +1 -1
  43. data/app/models/decidim/user.rb +0 -4
  44. data/app/models/decidim/user_base_entity.rb +4 -0
  45. data/app/packs/src/decidim/append_redirect_url_to_modals.js +14 -6
  46. data/app/packs/src/decidim/datepicker/datepicker_functions.js +3 -3
  47. data/app/packs/src/decidim/direct_uploads/upload_field.js +21 -8
  48. data/app/packs/src/decidim/index.js +5 -0
  49. data/app/packs/src/decidim/map/provider/here.js +1 -1
  50. data/app/packs/src/decidim/remote_tooltips.js +38 -0
  51. data/app/packs/src/decidim/toggle.js +1 -1
  52. data/app/packs/src/decidim/tooltips.js +42 -22
  53. data/app/packs/stylesheets/decidim/_content_blocks.scss +4 -0
  54. data/app/packs/stylesheets/decidim/_hashtags.scss +5 -0
  55. data/app/packs/stylesheets/decidim/_header.scss +11 -5
  56. data/app/packs/stylesheets/decidim/_labels.scss +1 -1
  57. data/app/packs/stylesheets/decidim/_profile.scss +1 -1
  58. data/app/packs/stylesheets/decidim/_progress-bar.scss +1 -1
  59. data/app/packs/stylesheets/decidim/application.scss +1 -0
  60. data/app/packs/stylesheets/decidim/legacy/conference-diploma.scss +2 -1
  61. data/app/presenters/decidim/attachment_presenter.rb +1 -1
  62. data/app/presenters/decidim/log/user_presenter.rb +1 -0
  63. data/app/presenters/decidim/user_presenter.rb +1 -1
  64. data/app/services/decidim/base_diff_renderer.rb +28 -2
  65. data/app/services/decidim/email_notification_generator.rb +14 -5
  66. data/app/services/decidim/static_map_generator.rb +1 -1
  67. data/app/views/decidim/last_activities/index.html.erb +1 -1
  68. data/app/views/decidim/pages/_tabbed.html.erb +2 -2
  69. data/app/views/decidim/reported_mailer/hide.html.erb +17 -1
  70. data/app/views/decidim/reported_mailer/report.html.erb +1 -1
  71. data/app/views/decidim/searches/_count.html.erb +1 -1
  72. data/app/views/decidim/searches/_filters.html.erb +40 -38
  73. data/app/views/decidim/shared/_orders.html.erb +2 -2
  74. data/app/views/layouts/decidim/footer/_main_legal.html.erb +1 -1
  75. data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile_tablet.html.erb +1 -1
  76. data/config/locales/ar.yml +56 -27
  77. data/config/locales/bg.yml +10 -24
  78. data/config/locales/bn-BD.yml +1 -0
  79. data/config/locales/bs-BA.yml +100 -0
  80. data/config/locales/ca-IT.yml +2111 -0
  81. data/config/locales/ca.yml +70 -38
  82. data/config/locales/cs.yml +60 -32
  83. data/config/locales/de.yml +66 -38
  84. data/config/locales/el.yml +17 -15
  85. data/config/locales/en.yml +48 -16
  86. data/config/locales/eo.yml +2 -0
  87. data/config/locales/es-MX.yml +61 -29
  88. data/config/locales/es-PY.yml +66 -34
  89. data/config/locales/es.yml +71 -39
  90. data/config/locales/eu.yml +303 -261
  91. data/config/locales/fi-plain.yml +48 -28
  92. data/config/locales/fi.yml +85 -65
  93. data/config/locales/fr-CA.yml +64 -27
  94. data/config/locales/fr.yml +62 -25
  95. data/config/locales/ga-IE.yml +13 -4
  96. data/config/locales/gl.yml +33 -15
  97. data/config/locales/hu.yml +12 -26
  98. data/config/locales/id-ID.yml +32 -16
  99. data/config/locales/is-IS.yml +18 -2
  100. data/config/locales/it.yml +54 -27
  101. data/config/locales/ja.yml +70 -38
  102. data/config/locales/lb.yml +33 -22
  103. data/config/locales/lt.yml +10 -18
  104. data/config/locales/lv.yml +26 -15
  105. data/config/locales/nl.yml +33 -19
  106. data/config/locales/no.yml +27 -16
  107. data/config/locales/pl.yml +8 -22
  108. data/config/locales/pt-BR.yml +13 -25
  109. data/config/locales/pt.yml +32 -16
  110. data/config/locales/ro-RO.yml +500 -220
  111. data/config/locales/ru.yml +31 -8
  112. data/config/locales/sk.yml +38 -19
  113. data/config/locales/sl.yml +4 -0
  114. data/config/locales/sr-CS.yml +2 -0
  115. data/config/locales/sv.yml +29 -33
  116. data/config/locales/tr-TR.yml +34 -24
  117. data/config/locales/uk.yml +20 -3
  118. data/config/locales/zh-CN.yml +27 -15
  119. data/config/locales/zh-TW.yml +16 -16
  120. data/config/routes.rb +1 -0
  121. data/decidim-core.gemspec +4 -1
  122. data/lib/decidim/api/functions/component_list.rb +1 -1
  123. data/lib/decidim/api/functions/participatory_space_finder_base.rb +11 -1
  124. data/lib/decidim/api/interfaces/participatory_space_interface.rb +1 -1
  125. data/lib/decidim/api/types/component_type.rb +7 -0
  126. data/lib/decidim/api/types/user_group_type.rb +4 -0
  127. data/lib/decidim/api/types/user_type.rb +4 -0
  128. data/lib/decidim/asset_router/storage.rb +7 -2
  129. data/lib/decidim/attributes/rich_text.rb +38 -0
  130. data/lib/decidim/attributes/time_with_zone.rb +16 -2
  131. data/lib/decidim/attributes.rb +2 -0
  132. data/lib/decidim/content_parsers/blob_parser.rb +95 -0
  133. data/lib/decidim/content_parsers/user_parser.rb +1 -1
  134. data/lib/decidim/content_parsers.rb +1 -0
  135. data/lib/decidim/content_renderers/blob_renderer.rb +90 -0
  136. data/lib/decidim/content_renderers.rb +1 -0
  137. data/lib/decidim/core/engine.rb +29 -1
  138. data/lib/decidim/core/test/factories.rb +28 -0
  139. data/lib/decidim/core/test/shared_examples/authorable_interface_examples.rb +1 -1
  140. data/lib/decidim/core/test/shared_examples/comments_examples.rb +15 -2
  141. data/lib/decidim/core/test/shared_examples/reports_examples.rb +48 -6
  142. data/lib/decidim/core/test/shared_examples/social_share_examples.rb +32 -0
  143. data/lib/decidim/core/test/shared_examples/uncommentable_component_examples.rb +26 -0
  144. data/lib/decidim/core/test/shared_examples/versions_controller_examples.rb +26 -0
  145. data/lib/decidim/core/version.rb +1 -1
  146. data/lib/decidim/diffy_extension.rb +18 -0
  147. data/lib/decidim/form_builder.rb +1 -1
  148. data/lib/decidim/map/autocomplete.rb +1 -0
  149. data/lib/decidim/map/provider/dynamic_map/here.rb +1 -40
  150. data/lib/decidim/map/provider/static_map/here.rb +34 -0
  151. data/lib/decidim/moderation_tools.rb +16 -2
  152. data/lib/decidim/nicknamizable.rb +1 -1
  153. data/lib/decidim/participatory_space_user.rb +4 -0
  154. data/lib/decidim/query_extensions.rb +0 -26
  155. data/lib/decidim/reportable.rb +6 -2
  156. data/lib/decidim/settings_manifest.rb +2 -0
  157. data/lib/decidim/translatable_attributes.rb +10 -1
  158. data/lib/tasks/upgrade/clean_hidden_resources.rake +33 -0
  159. data/lib/tasks/upgrade/decidim_fix_categorization.rake +34 -8
  160. data/lib/tasks/upgrade/decidim_fix_nickname_uniqueness.rake +23 -20
  161. metadata +37 -15
  162. data/app/cells/decidim/author/flag.erb +0 -6
  163. data/app/cells/decidim/author/flag_user.erb +0 -14
  164. data/app/cells/decidim/flag_modal/flag_user.erb +0 -34
  165. data/app/cells/decidim/flag_modal/show.erb +0 -52
  166. data/app/cells/decidim/flag_modal_cell.rb +0 -56
  167. data/app/cells/decidim/profile_sidebar/show.erb +0 -167
  168. data/app/cells/decidim/profile_sidebar_cell.rb +0 -68
  169. data/app/packs/src/decidim/vendor/leaflet-tilelayer-here.js +0 -212
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ContentRenderers
5
+ # A renderer that searches Global IDs representing blobs in content and
6
+ # replaces it with a URL to these blobs.
7
+ #
8
+ # e.g. gid://<APP_NAME>/ActiveStorage::Blob/1
9
+ #
10
+ # OR for representations
11
+ #
12
+ # e.g. gid://<APP_NAME>/ActiveStorage::Blob/1/<encoded variant transformations>
13
+ #
14
+ # The `<encoded variant transformations>` part of the URL is a Base64
15
+ # encoded string that contains an unencrypted JSON-encoded value about the
16
+ # blob transformations. This way the specific representations can be stored
17
+ # in the database without having these values expiring.
18
+ #
19
+ # @see BaseRenderer Examples of how to use a content renderer
20
+ class BlobRenderer < BaseRenderer
21
+ # Matches a global id representing a Decidim::User
22
+ GLOBAL_ID_REGEX = %r{(gid://[\w-]+/ActiveStorage::Blob/\d+)(/([\w=-]+))?}
23
+
24
+ # Replaces found Global IDs matching an existing blob with a URL to
25
+ # that blob. The Global IDs representing an invalid ActiveStorage::Blob
26
+ # are replaced with an empty string.
27
+ #
28
+ # @return [String] the content ready to display (contains HTML)
29
+ def render(_options = nil)
30
+ replace_pattern(content, GLOBAL_ID_REGEX)
31
+ end
32
+
33
+ protected
34
+
35
+ def replace_pattern(text, pattern)
36
+ return text unless text.respond_to?(:gsub)
37
+
38
+ text.gsub(pattern) do
39
+ blob_gid = Regexp.last_match(1)
40
+ variation_key = Regexp.last_match(3)
41
+
42
+ blob = GlobalID::Locator.locate(blob_gid)
43
+ if variation_key
44
+ variation = begin
45
+ ActiveSupport::JSON.decode(Base64.strict_decode64(variation_key))
46
+ rescue JSON::ParseError
47
+ variation_key
48
+ end
49
+ blob_url(blob, variation)
50
+ else
51
+ blob_url(blob)
52
+ end
53
+ rescue ActiveRecord::RecordNotFound => _e
54
+ ""
55
+ end
56
+ end
57
+
58
+ def blob_url(blob, variation = nil)
59
+ url = begin
60
+ if variation
61
+ blob.variant(variation).url
62
+ else
63
+ blob.url
64
+ end
65
+ rescue ArgumentError
66
+ # ArgumentError is raised in case the blob's service is set to
67
+ # ActiveStorage::Service::DiskService and
68
+ # `ActiveStorage::Current.url_options` is not set.
69
+ end
70
+ raise URI::InvalidURIError if url.blank?
71
+
72
+ url
73
+ rescue URI::InvalidURIError
74
+ local_blob_url(blob, variation)
75
+ end
76
+
77
+ def local_blob_url(blob, variation = nil)
78
+ if variation
79
+ routes.rails_representation_url(blob.variant(variation), only_path: true)
80
+ else
81
+ routes.rails_blob_url(blob, only_path: true)
82
+ end
83
+ end
84
+
85
+ def routes
86
+ @routes ||= Rails.application.routes.url_helpers
87
+ end
88
+ end
89
+ end
90
+ end
@@ -3,6 +3,7 @@
3
3
  module Decidim
4
4
  module ContentRenderers
5
5
  autoload :BaseRenderer, "decidim/content_renderers/base_renderer"
6
+ autoload :BlobRenderer, "decidim/content_renderers/blob_renderer"
6
7
  autoload :UserRenderer, "decidim/content_renderers/user_renderer"
7
8
  autoload :UserGroupRenderer, "decidim/content_renderers/user_group_renderer"
8
9
  autoload :HashtagRenderer, "decidim/content_renderers/hashtag_renderer"
@@ -239,6 +239,34 @@ module Decidim
239
239
  app.config.action_mailer.deliver_later_queue_name = :mailers
240
240
  end
241
241
 
242
+ initializer "decidim_core.active_storage", before: "active_storage.configs" do |app|
243
+ next if app.config.active_storage.service_urls_expire_in.present?
244
+
245
+ # Ensure that the ActiveStorage URLs are valid long enough because with
246
+ # the default configuration they would expire in 5 minutes which is a
247
+ # problem:
248
+ # a) for the backend blob URL replacement
249
+ # and
250
+ # b) for caching
251
+ #
252
+ # Note the limitations for each storage service regarding the signed URL
253
+ # expiration times. This limitation has to be also considered when
254
+ # defining a caching strategy, otherwise e.g. images or files may not
255
+ # display correctly when caching is enabled.
256
+ #
257
+ # ActiveStorage disk service (default): no limitation
258
+ #
259
+ # S3: maximum is 7 days from the creation of the URL
260
+ # https://docs.aws.amazon.com/AmazonS3/latest/userguide/PresignedUrlUploadObject.html
261
+ #
262
+ # Google: maximum is 7 days (604800 seconds)
263
+ # https://cloud.google.com/storage/docs/access-control/signed-urls
264
+ #
265
+ # Azure: no limitation
266
+ # https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview#best-practices-when-using-sas
267
+ app.config.active_storage.service_urls_expire_in = 7.days
268
+ end
269
+
242
270
  initializer "decidim_core.signed_global_id", after: "global_id" do |app|
243
271
  next if app.config.global_id.fetch(:expires_in, nil).present?
244
272
 
@@ -414,7 +442,7 @@ module Decidim
414
442
 
415
443
  initializer "decidim_core.content_processors" do |_app|
416
444
  Decidim.configure do |config|
417
- config.content_processors += [:user, :user_group, :hashtag, :link]
445
+ config.content_processors += [:user, :user_group, :hashtag, :link, :blob]
418
446
  end
419
447
  end
420
448
 
@@ -218,8 +218,15 @@ FactoryBot.define do
218
218
  end
219
219
 
220
220
  trait :deleted do
221
+ name { "" }
222
+ nickname { "" }
221
223
  email { "" }
224
+ delete_reason { "I want to delete my account" }
225
+ admin { false }
222
226
  deleted_at { Time.current }
227
+ avatar { nil }
228
+ personal_url { "" }
229
+ about { "" }
223
230
  end
224
231
 
225
232
  trait :admin_terms_accepted do
@@ -1022,4 +1029,25 @@ FactoryBot.define do
1022
1029
  end
1023
1030
  end
1024
1031
  end
1032
+
1033
+ factory :blob, class: "ActiveStorage::Blob" do
1034
+ transient do
1035
+ filepath { Decidim::Dev.asset("city.jpeg") }
1036
+ end
1037
+
1038
+ filename { File.basename(filepath) }
1039
+ content_type { MiniMime.lookup_by_filename(filepath)&.content_type || "text/plain" }
1040
+
1041
+ before(:create) do |object, evaluator|
1042
+ object.upload(File.open(evaluator.filepath))
1043
+ end
1044
+
1045
+ trait :image do
1046
+ filepath { Decidim::Dev.asset("city.jpeg") }
1047
+ end
1048
+
1049
+ trait :document do
1050
+ filepath { Decidim::Dev.asset("Exampledocument.pdf") }
1051
+ end
1052
+ end
1025
1053
  end
@@ -18,7 +18,7 @@ shared_examples_for "authorable interface" do
18
18
  end
19
19
 
20
20
  describe "with a regular user" do
21
- let(:author) { create(:user, organization: model.participatory_space.organization) }
21
+ let(:author) { create(:user, :confirmed, organization: model.participatory_space.organization) }
22
22
  let(:query) { "{ author { name } }" }
23
23
 
24
24
  before do
@@ -619,7 +619,7 @@ shared_examples "comments" do
619
619
  end
620
620
 
621
621
  context "when the user has verified organizations" do
622
- let(:user_group) { create(:user_group, :verified) }
622
+ let(:user_group) { create(:user_group, :verified, organization:) }
623
623
  let(:content) { "This is a new comment" }
624
624
 
625
625
  before do
@@ -747,6 +747,20 @@ shared_examples "comments" do
747
747
  expect(page).to have_content("Edited")
748
748
  end
749
749
  end
750
+
751
+ it "has only one edit modal" do
752
+ expect(page).to have_css("#editCommentModal#{comment.id}", visible: :hidden, count: 1)
753
+ 3.times do |index|
754
+ sleep 2
755
+ within "#comment_#{comment.id}" do
756
+ page.find("[id^='dropdown-trigger']").click
757
+ click_on "Edit"
758
+ end
759
+ fill_in "edit_comment_#{comment.id}", with: " This comment has been edited #{1 + index} times"
760
+ click_on "Send"
761
+ end
762
+ expect(page).to have_css("#editCommentModal#{comment.id}", visible: :all, count: 1)
763
+ end
750
764
  end
751
765
  end
752
766
  end
@@ -1031,7 +1045,6 @@ shared_examples "comments blocked" do
1031
1045
  end
1032
1046
 
1033
1047
  context "when authenticated" do
1034
- let!(:organization) { create(:organization) }
1035
1048
  let!(:user) { create(:user, :confirmed, organization:) }
1036
1049
  let!(:comments) { create_list(:comment, 3, commentable:) }
1037
1050
 
@@ -33,17 +33,46 @@ shared_examples "higher user role hides" do
33
33
  before do
34
34
  login_as user, scope: :user
35
35
  end
36
- around do |example|
37
- previous = Capybara.raise_server_errors
38
36
 
39
- Capybara.raise_server_errors = false
40
- example.run
41
- Capybara.raise_server_errors = previous
37
+ it "reports the resource" do
38
+ visit reportable_path
39
+
40
+ expect(page).to have_css(%(button[data-dialog-open="flagModal"]))
41
+ find(%(button[data-dialog-open="flagModal"])).click
42
+ expect(page).to have_css(".flag-modal", visible: :visible)
43
+
44
+ within ".flag-modal" do
45
+ find(:css, "input[name='report[hide]']").set(true)
46
+ click_on "Hide"
47
+ end
48
+
49
+ sleep(1)
50
+
51
+ expect(page).to have_current_path(reportable_index_path, ignore_query: true)
52
+
53
+ expect(reportable.reload).to be_hidden
42
54
  end
55
+ end
56
+ end
57
+
58
+ shared_examples "higher user role hides resource with comments" do
59
+ context "and the admin hides a resource with comments" do
60
+ let!(:comments) { create_list(:comment, 2, body: "Dummy comment", commentable: reportable, author: user) }
61
+
62
+ before do
63
+ login_as user, scope: :user
64
+ end
65
+
66
+ it "hides the resource" do
67
+ visit decidim.search_path
68
+ expect(page).to have_content(translated(comments.first.body))
69
+ expect(page).to have_content(translated(comments.second.body))
43
70
 
44
- it "reports the resource" do
45
71
  visit reportable_path
46
72
 
73
+ expect(page).to have_content(translated(comments.first.body))
74
+ expect(page).to have_content(translated(comments.second.body))
75
+
47
76
  expect(page).to have_css(%(button[data-dialog-open="flagModal"]))
48
77
  find(%(button[data-dialog-open="flagModal"])).click
49
78
  expect(page).to have_css(".flag-modal", visible: :visible)
@@ -53,7 +82,19 @@ shared_examples "higher user role hides" do
53
82
  click_on "Hide"
54
83
  end
55
84
 
85
+ sleep(1)
86
+
87
+ expect(page).to have_current_path(reportable_index_path, ignore_query: true)
88
+
89
+ perform_enqueued_jobs
90
+
56
91
  expect(reportable.reload).to be_hidden
92
+ expect(comments.first.reload).to be_hidden
93
+ expect(comments.second.reload).to be_hidden
94
+
95
+ visit decidim.search_path
96
+ expect(page).to have_no_content(translated(comments.first.body))
97
+ expect(page).to have_no_content(translated(comments.second.body))
57
98
  end
58
99
  end
59
100
  end
@@ -120,6 +161,7 @@ shared_examples "reports by user type" do
120
161
  let!(:user) { create(:user, :admin, :confirmed, organization:) }
121
162
  include_examples "higher user role reports"
122
163
  include_examples "higher user role hides"
164
+ include_examples "higher user role hides resource with comments"
123
165
  end
124
166
  context "When reporting user is process admin" do
125
167
  let!(:user) { create :process_admin, :confirmed, participatory_process: }
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ def visit_resource
6
+ return visit resource if resource.is_a?(String)
7
+
8
+ return visit decidim.root_path if resource.is_a?(Decidim::Organization)
9
+
10
+ visit resource_locator(resource).path
11
+ end
12
+
13
+ shared_examples "a social share widget" do
14
+ it "has the social share button" do
15
+ visit_resource
16
+
17
+ expect(page).to have_css('button[data-dialog-open="socialShare"]')
18
+ end
19
+
20
+ it "lists all the expected social share providers" do
21
+ visit_resource
22
+ click_on "Share"
23
+
24
+ within "#socialShare" do
25
+ expect(page).to have_css('a[title="Share to X"]')
26
+ expect(page).to have_css('a[title="Share to Facebook"]')
27
+ expect(page).to have_css('a[title="Share to WhatsApp"]')
28
+ expect(page).to have_css('a[title="Share to Telegram"]')
29
+ expect(page).to have_css(".share-modal__input")
30
+ end
31
+ end
32
+ end
@@ -8,6 +8,7 @@ shared_examples "an uncommentable component" do
8
8
  manifest:,
9
9
  participatory_space:)
10
10
  end
11
+ let!(:comment) { create(:comment, commentable: resources.first) }
11
12
 
12
13
  it "does not displays comments count" do
13
14
  component.update!(settings: { comments_enabled: false })
@@ -18,4 +19,29 @@ shared_examples "an uncommentable component" do
18
19
  expect(page).to have_no_link(resource_locator(resource).path)
19
20
  end
20
21
  end
22
+
23
+ describe "when search a comment in the global search" do
24
+ it "does displays the comments" do
25
+ visit decidim.root_path
26
+
27
+ within ".main-bar__search" do
28
+ fill_in "term", with: comment.body["en"]
29
+ find("input#input-search").native.send_keys :enter
30
+ end
31
+
32
+ expect(page).to have_content("1 Results for the search")
33
+ end
34
+
35
+ it "does not displays the comment when comments are disable" do
36
+ component.update!(settings: { comments_enabled: false })
37
+ visit decidim.root_path
38
+
39
+ within ".main-bar__search" do
40
+ fill_in "term", with: comment.body["en"]
41
+ find("input#input-search").native.send_keys :enter
42
+ end
43
+
44
+ expect(page).to have_content("0 Results for the search")
45
+ end
46
+ end
21
47
  end
@@ -2,6 +2,32 @@
2
2
 
3
3
  require "spec_helper"
4
4
 
5
+ shared_examples "a version of a hidden object" do
6
+ before do
7
+ visit resource_path
8
+ click_on "see other versions"
9
+ click_on("Version 1 of #{hidden_object.reload.versions.size}")
10
+ end
11
+
12
+ around do |example|
13
+ previous = Capybara.raise_server_errors
14
+
15
+ Capybara.raise_server_errors = false
16
+ example.run
17
+ Capybara.raise_server_errors = previous
18
+ end
19
+
20
+ it "shows an error page" do
21
+ expect(page).to have_content("Changes at")
22
+
23
+ create(:moderation, reportable: hidden_object, hidden_at: 1.day.ago)
24
+
25
+ visit current_path
26
+
27
+ expect(page).to have_content(ActiveRecord::RecordNotFound)
28
+ end
29
+ end
30
+
5
31
  shared_examples "versions controller" do
6
32
  let(:base_params) do
7
33
  if resource.is_a?(Decidim::Participable)
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-core version.
5
5
  module Core
6
6
  def self.version
7
- "0.29.1"
7
+ "0.29.3"
8
8
  end
9
9
  end
10
10
  end
@@ -13,6 +13,24 @@ module Decidim
13
13
  str = wrap_lines(@diff.map { |line| wrap_line(line) })
14
14
  ActionView::Base.new(ActionView::LookupContext.new(nil), {}, nil).sanitize(str, tags: TAGS)
15
15
  end
16
+
17
+ private
18
+
19
+ def wrap_line(line)
20
+ cleaned = clean_line(line)
21
+ case line
22
+ when /^(---|\+\+\+|\\\\)/
23
+ " <li class=\"diff-comment\"><div>#{line.chomp}</div></li>"
24
+ when /^\+/
25
+ " <li class=\"ins\"><ins>#{cleaned}</ins></li>"
26
+ when /^-/
27
+ " <li class=\"del\"><del>#{cleaned}</del></li>"
28
+ when /^ /
29
+ " <li class=\"unchanged\"><div>#{cleaned}</div></li>"
30
+ when /^@@/
31
+ " <li class=\"diff-block-info\"><div>#{line.chomp}</div></li>"
32
+ end
33
+ end
16
34
  end
17
35
 
18
36
  # Adding a new method to Diffy::Format so we can pass the
@@ -390,7 +390,7 @@ module Decidim
390
390
  # options - A Hash with options to build the field. See upload method for
391
391
  # more detailed information.
392
392
  def attachment(attribute, options = {})
393
- object_attachment = object.attachment.present?
393
+ object_attachment = object.respond_to?(:attachment) && object.attachment.present?
394
394
  record = object_attachment ? object.attachment : object
395
395
  options = {
396
396
  titled: options[:multiple],
@@ -42,6 +42,7 @@ module Decidim
42
42
  @autocomplete_utility ||= Decidim::Map.autocomplete(
43
43
  organization: @template.current_organization
44
44
  )
45
+
45
46
  return text_field(attribute, options) unless @autocomplete_utility
46
47
 
47
48
  # Decidim::Map::Autocomplete::Builder
@@ -49,46 +49,7 @@ module Decidim
49
49
  private
50
50
 
51
51
  def language_code
52
- primary = I18n.locale.to_s
53
- secondary = primary.split("-")[0]
54
- available_language_codes[primary] || available_language_codes[secondary] || ""
55
- end
56
-
57
- def available_language_codes
58
- @available_language_codes ||= {
59
- "ar" => "ara", # Arabic
60
- "eu" => "baq", # Basque
61
- "ca" => "cat", # Catalan
62
- "zh" => "chi", # Chinese (simplified)
63
- # "" => "cht", # Chinese (traditional)
64
- "cs" => "cze", # Czech
65
- "da" => "dan", # Danish
66
- "nl" => "dut", # Dutch
67
- "en" => "eng", # English
68
- "fi" => "fin", # Finnish
69
- "fr" => "fre", # French
70
- "de" => "ger", # German
71
- "ga" => "gle", # Gaelic
72
- "el" => "gre", # Greek
73
- "he" => "heb", # Hebrew
74
- "hi" => "hin", # Hindi
75
- "id" => "ind", # Indonesian
76
- "it" => "ita", # Italian
77
- "no" => "nor", # Norwegian
78
- "fa" => "per", # Persian
79
- "pl" => "pol", # Polish
80
- "pt" => "por", # Portuguese
81
- "ru" => "rus", # Russian
82
- "si" => "sin", # Sinhalese
83
- "es" => "spa", # Spanish
84
- "sv" => "swe", # Swedish
85
- "th" => "tha", # Thai
86
- "tr" => "tur", # Turkish
87
- "uk" => "ukr", # Ukrainian
88
- "ur" => "urd", # Urdu
89
- "vi" => "vie", # Vietnamese
90
- "cy" => "wel" # Welsh
91
- }
52
+ I18n.locale.to_s
92
53
  end
93
54
  end
94
55
  end
@@ -6,8 +6,42 @@ module Decidim
6
6
  module StaticMap
7
7
  # The static map utility class for the HERE maps service
8
8
  class Here < ::Decidim::Map::StaticMap
9
+ def url(latitude:, longitude:, options: {})
10
+ map_url = configuration.fetch(:url, nil)
11
+ return super unless map_url
12
+
13
+ return super unless map_url.include?("mia/v3")
14
+
15
+ w = options[:width] || Decidim::Map::StaticMap::DEFAULT_SIZE
16
+ h = options[:height] || Decidim::Map::StaticMap::DEFAULT_SIZE
17
+
18
+ params = {
19
+ apiKey: configuration[:api_key],
20
+ overlay: "point:#{latitude},#{longitude};icon=cp;size=large|#{latitude},#{longitude};style=circle;width=50m;color=%231B9D2C60"
21
+ }
22
+
23
+ URI.parse("#{map_url}:radius=90/#{w}x#{h}/png8").tap do |uri|
24
+ uri.query = URI.encode_www_form(params)
25
+ end.to_s
26
+ end
27
+
9
28
  # @See Decidim::Map::StaticMap#url_params
10
29
  def url_params(latitude:, longitude:, options: {})
30
+ ActiveSupport::Deprecation.warn(
31
+ <<~DEPRECATION.strip
32
+ Please use a V3 version HERE maps.
33
+ For further information, see:
34
+ https://www.here.com/docs/bundle/map-image-migration-guide-v3/page/README.html
35
+ Also make sure your Decidim.maps configurations are using the
36
+ up to date format.
37
+ You need to change:
38
+ static_url = "https://image.maps.ls.hereapi.com/mia/1.6/mapview" if static_provider == "here" && static_url.blank?
39
+ to:
40
+ static_url = "https://image.maps.hereapi.com/mia/v3/base/mc/overlay" if static_provider == "here"
41
+ in your config/initializers/decidim.rb file.
42
+ DEPRECATION
43
+ )
44
+
11
45
  params = {
12
46
  c: "#{latitude}, #{longitude}",
13
47
  z: options[:zoom] || Decidim::Map::StaticMap::DEFAULT_ZOOM,
@@ -59,9 +59,11 @@ module Decidim
59
59
  event_class: Decidim::ResourceHiddenEvent,
60
60
  resource: @reportable,
61
61
  extra: {
62
- report_reasons:
62
+ report_reasons:,
63
+ force_email: true
63
64
  },
64
- affected_users: @reportable.try(:authors) || [@reportable.try(:normalized_author)]
65
+ affected_users:,
66
+ force_send: true
65
67
  }
66
68
 
67
69
  Decidim::EventsManager.publish(**data)
@@ -80,10 +82,22 @@ module Decidim
80
82
  @reportable.moderation.update!(hidden_at: Time.current)
81
83
  @reportable.try(:touch)
82
84
  end
85
+
86
+ if @reportable.is_a?(Decidim::Comments::Commentable)
87
+ @reportable.comment_threads.each do |comment|
88
+ Decidim::HideChildResourcesJob.perform_later(comment, @current_user.id)
89
+ end
90
+ end
91
+
92
+ send_notification_to_author
83
93
  end
84
94
 
85
95
  private
86
96
 
97
+ def affected_users
98
+ @affected_users ||= (@reportable.try(:authors) || [@reportable.try(:author)]).select { |author| author.is_a?(Decidim::User) }
99
+ end
100
+
87
101
  def report_reasons
88
102
  @reportable.moderation.reports.pluck(:reason).uniq
89
103
  end
@@ -52,7 +52,7 @@ module Decidim
52
52
  candidate = name
53
53
 
54
54
  2.step do |n|
55
- return candidate if Decidim::UserBaseEntity.where("nickname ILIKE ?", candidate.downcase).where(scope).empty?
55
+ return candidate if Decidim::UserBaseEntity.where(nickname: candidate.downcase).where(scope).empty?
56
56
 
57
57
  candidate = numbered_variation_of(name, n)
58
58
  end
@@ -37,6 +37,10 @@ module Decidim
37
37
  raise "Not implemented"
38
38
  end
39
39
 
40
+ def self.order_by_name
41
+ includes(:user).order("decidim_users.name ASC")
42
+ end
43
+
40
44
  private
41
45
 
42
46
  # Private: check if the process and the user have the same organization
@@ -10,22 +10,6 @@ module Decidim
10
10
  #
11
11
  # Returns nothing.
12
12
  def self.included(type)
13
- type.field :participatory_processes,
14
- [Decidim::ParticipatoryProcesses::ParticipatoryProcessType],
15
- null: true,
16
- description: "Lists all participatory_processes" do
17
- argument :filter, Decidim::ParticipatoryProcesses::ParticipatoryProcessInputFilter, "This argument lets you filter the results", required: false
18
- argument :order, Decidim::ParticipatoryProcesses::ParticipatoryProcessInputSort, "This argument lets you order the results", required: false
19
- end
20
-
21
- type.field :participatory_process,
22
- Decidim::ParticipatoryProcesses::ParticipatoryProcessType,
23
- null: true,
24
- description: "Finds a participatory_process" do
25
- argument :id, GraphQL::Types::ID, "The ID of the participatory space", required: false
26
- argument :slug, String, "The slug of the participatory process", required: false
27
- end
28
-
29
13
  type.field :component, Decidim::Core::ComponentInterface, null: true do
30
14
  description "Lists the components this space contains."
31
15
  argument :id, GraphQL::Types::ID, required: true, description: "The ID of the component to be found"
@@ -62,16 +46,6 @@ module Decidim
62
46
  end
63
47
  end
64
48
 
65
- def participatory_processes(filter: {}, order: {})
66
- manifest = Decidim.participatory_space_manifests.select { |m| m.name == :participatory_processes }.first
67
- Decidim::Core::ParticipatorySpaceListBase.new(manifest:).call(object, { filter:, order: }, context)
68
- end
69
-
70
- def participatory_process(id: nil, slug: nil)
71
- manifest = Decidim.participatory_space_manifests.select { |m| m.name == :participatory_processes }.first
72
- Decidim::Core::ParticipatorySpaceFinderBase.new(manifest:).call(object, { id:, slug: }, context)
73
- end
74
-
75
49
  def component(id: {})
76
50
  component = Decidim::Component.published.find_by(id:)
77
51
  component&.organization == context[:current_organization] ? component : nil