decidim-core 0.28.0 → 0.28.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (196) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity_cell.rb +2 -2
  3. data/app/cells/decidim/address/online.erb +27 -9
  4. data/app/cells/decidim/address/show.erb +27 -12
  5. data/app/cells/decidim/address_cell.rb +29 -0
  6. data/app/cells/decidim/authorization_modal/show.erb +8 -4
  7. data/app/cells/decidim/authorization_modal_cell.rb +1 -0
  8. data/app/cells/decidim/card_metadata_cell.rb +3 -3
  9. data/app/cells/decidim/coauthorships_cell.rb +1 -1
  10. data/app/cells/decidim/content_blocks/menu_breadcrumb_last_activity_cell.rb +6 -0
  11. data/app/cells/decidim/content_blocks/participatory_space_hero_cell.rb +20 -4
  12. data/app/cells/decidim/content_blocks/participatory_space_hero_settings_form/show.erb +8 -0
  13. data/app/cells/decidim/content_blocks/participatory_space_hero_settings_form_cell.rb +13 -0
  14. data/app/cells/decidim/content_blocks/participatory_space_metadata_cell.rb +1 -1
  15. data/app/cells/decidim/footer_pages_cell.rb +3 -3
  16. data/app/cells/decidim/profile/tabs.erb +3 -2
  17. data/app/cells/decidim/tags_cell.rb +3 -1
  18. data/app/cells/decidim/upload_modal/modal.erb +2 -2
  19. data/app/commands/decidim/create_omniauth_registration.rb +1 -3
  20. data/app/commands/decidim/messaging/reply_to_conversation.rb +3 -0
  21. data/app/commands/decidim/messaging/start_conversation.rb +3 -0
  22. data/app/controllers/concerns/decidim/devise_authentication_methods.rb +36 -0
  23. data/app/controllers/concerns/decidim/paginable.rb +1 -1
  24. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +1 -22
  25. data/app/controllers/decidim/devise/sessions_controller.rb +1 -24
  26. data/app/events/decidim/welcome_notification_event.rb +6 -9
  27. data/app/helpers/decidim/application_helper.rb +0 -18
  28. data/app/helpers/decidim/cells_paginate_helper.rb +1 -1
  29. data/app/helpers/decidim/check_boxes_tree_helper.rb +6 -6
  30. data/app/helpers/decidim/layout_helper.rb +1 -1
  31. data/app/helpers/decidim/map_helper.rb +1 -1
  32. data/app/helpers/decidim/menu_helper.rb +2 -0
  33. data/app/helpers/decidim/newsletters_helper.rb +83 -16
  34. data/app/helpers/decidim/paginate_helper.rb +1 -1
  35. data/app/helpers/decidim/sanitize_helper.rb +9 -0
  36. data/app/helpers/decidim/social_share_button_helper.rb +1 -1
  37. data/app/helpers/decidim/user_profile_helper.rb +7 -2
  38. data/app/mailers/decidim/messaging/conversation_mailer.rb +3 -72
  39. data/app/models/decidim/push_notification_message.rb +38 -0
  40. data/app/packs/entrypoints/decidim_overrides.scss +2 -0
  41. data/app/packs/images/decidim/.keep +0 -0
  42. data/app/packs/src/decidim/a11y.js +1 -1
  43. data/app/packs/src/decidim/account_form.js +1 -1
  44. data/app/packs/src/decidim/data_consent/consent_manager.test.js +1 -1
  45. data/app/packs/src/decidim/data_consent/index.js +1 -1
  46. data/app/packs/src/decidim/direct_uploads/upload_field.js +1 -1
  47. data/app/packs/src/decidim/direct_uploads/upload_modal.js +10 -6
  48. data/app/packs/src/decidim/editor/extensions/hashtag/index.js +1 -1
  49. data/app/packs/src/decidim/editor/extensions/mention/index.js +1 -1
  50. data/app/packs/src/decidim/editor/extensions/video_embed/index.js +3 -0
  51. data/app/packs/src/decidim/editor/test/editor/create.test.js +1 -1
  52. data/app/packs/src/decidim/editor/test/extensions/bold.test.js +2 -3
  53. data/app/packs/src/decidim/editor/test/extensions/character_count.test.js +2 -2
  54. data/app/packs/src/decidim/editor/test/extensions/decidim_kit.test.js +2 -3
  55. data/app/packs/src/decidim/editor/test/extensions/dialog.test.js +2 -2
  56. data/app/packs/src/decidim/editor/test/extensions/emoji.test.js +2 -2
  57. data/app/packs/src/decidim/editor/test/extensions/hashtag.test.js +2 -2
  58. data/app/packs/src/decidim/editor/test/extensions/heading.test.js +2 -2
  59. data/app/packs/src/decidim/editor/test/extensions/image.test.js +4 -4
  60. data/app/packs/src/decidim/editor/test/extensions/indent.test.js +2 -2
  61. data/app/packs/src/decidim/editor/test/extensions/link.test.js +3 -3
  62. data/app/packs/src/decidim/editor/test/extensions/mention.test.js +2 -2
  63. data/app/packs/src/decidim/editor/test/extensions/ordered_list.test.js +2 -2
  64. data/app/packs/src/decidim/editor/test/extensions/video_embed.test.js +3 -3
  65. data/app/packs/src/decidim/editor/test/helpers.js +5 -4
  66. data/app/packs/src/decidim/editor/test/toolbar/basic.test.js +2 -2
  67. data/app/packs/src/decidim/editor/test/toolbar/content.test.js +2 -2
  68. data/app/packs/src/decidim/editor/test/toolbar/full.test.js +3 -3
  69. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic.js +6 -6
  70. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_block.js +2 -2
  71. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_formatting.js +1 -1
  72. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_indent.js +2 -2
  73. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_link.js +2 -2
  74. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_list.js +2 -2
  75. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_basic_styling.js +2 -2
  76. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_content.js +7 -7
  77. data/app/packs/src/decidim/editor/test/toolbar/shared/behaves_like_content_styling.js +2 -2
  78. data/app/packs/src/decidim/editor/test/toolbar/shared/context.js +1 -1
  79. data/app/packs/src/decidim/editor/test/utilities/paste_transform.test.js +2 -2
  80. data/app/packs/src/decidim/external_domain_warning.js +13 -0
  81. data/app/packs/src/decidim/external_domain_warning.test.js +1 -1
  82. data/app/packs/src/decidim/external_link.js +48 -9
  83. data/app/packs/src/decidim/external_link.test.js +1 -1
  84. data/app/packs/src/decidim/focus_guard.js +8 -20
  85. data/app/packs/src/decidim/form_filter.component_for_testing.js +1 -1
  86. data/app/packs/src/decidim/form_filter.js +3 -3
  87. data/app/packs/src/decidim/geocoding/attach_input.js +1 -1
  88. data/app/packs/src/decidim/i18n.test.js +1 -1
  89. data/app/packs/src/decidim/index.js +4 -1
  90. data/app/packs/src/decidim/input_hashtags.js +1 -1
  91. data/app/packs/src/decidim/input_mentions.js +1 -1
  92. data/app/packs/src/decidim/input_multiple_mentions.js +1 -1
  93. data/app/packs/src/decidim/sw/index.js +3 -3
  94. data/app/packs/src/decidim/user_registrations.js +1 -1
  95. data/app/packs/src/decidim/vizzs/index.js +1 -1
  96. data/app/packs/stylesheets/decidim/_cards.scss +2 -2
  97. data/app/packs/stylesheets/decidim/_dropdown.scss +2 -2
  98. data/app/packs/stylesheets/decidim/_layout.scss +4 -4
  99. data/app/packs/stylesheets/decidim/application.scss +0 -3
  100. data/app/packs/stylesheets/decidim/decidim_application.scss +4 -0
  101. data/app/packs/stylesheets/decidim/legacy/leaflet.scss +88 -107
  102. data/app/presenters/decidim/admin_log/oauth_application_resource_presenter.rb +1 -1
  103. data/app/presenters/decidim/log/diff_presenter.rb +1 -1
  104. data/app/presenters/decidim/notification_to_mailer_presenter.rb +9 -0
  105. data/app/services/decidim/events_manager.rb +6 -0
  106. data/app/services/decidim/iframe_disabler.rb +4 -0
  107. data/app/services/decidim/push_notification_message_sender.rb +40 -0
  108. data/app/services/decidim/send_push_notification.rb +22 -8
  109. data/app/uploaders/decidim/background_image_uploader.rb +11 -0
  110. data/app/views/decidim/application/_collection.html.erb +2 -2
  111. data/app/views/decidim/application/_document.html.erb +1 -1
  112. data/app/views/decidim/devise/registrations/new.html.erb +2 -2
  113. data/app/views/decidim/notifications_digest_mailer/_email_content.html.erb +7 -0
  114. data/app/views/decidim/offline/show.html.erb +15 -9
  115. data/app/views/layouts/decidim/_head.html.erb +1 -0
  116. data/app/views/layouts/decidim/_js_configuration.html.erb +3 -1
  117. data/app/views/layouts/decidim/footer/_main_social_media_links.html.erb +5 -5
  118. data/app/views/layouts/decidim/footer/_mini.html.erb +2 -2
  119. data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile_tablet.html.erb +1 -1
  120. data/config/assets.rb +1 -0
  121. data/config/locales/ar.yml +63 -7
  122. data/config/locales/bg.yml +32 -2
  123. data/config/locales/ca.yml +29 -24
  124. data/config/locales/cs.yml +6 -2
  125. data/config/locales/de.yml +34 -29
  126. data/config/locales/el.yml +3 -6
  127. data/config/locales/en.yml +7 -2
  128. data/config/locales/es-MX.yml +9 -4
  129. data/config/locales/es-PY.yml +9 -4
  130. data/config/locales/es.yml +27 -22
  131. data/config/locales/eu.yml +22 -8
  132. data/config/locales/fi-plain.yml +8 -3
  133. data/config/locales/fi.yml +10 -5
  134. data/config/locales/fr-CA.yml +12 -7
  135. data/config/locales/fr.yml +12 -7
  136. data/config/locales/ga-IE.yml +1 -0
  137. data/config/locales/gl.yml +4 -2
  138. data/config/locales/he-IL.yml +1 -0
  139. data/config/locales/hu.yml +264 -7
  140. data/config/locales/id-ID.yml +0 -2
  141. data/config/locales/it.yml +2 -5
  142. data/config/locales/ja.yml +11 -6
  143. data/config/locales/lb.yml +2 -5
  144. data/config/locales/lt.yml +0 -10
  145. data/config/locales/lv.yml +0 -2
  146. data/config/locales/nl.yml +1 -2
  147. data/config/locales/no.yml +2 -5
  148. data/config/locales/pl.yml +581 -2
  149. data/config/locales/pt-BR.yml +202 -18
  150. data/config/locales/pt.yml +2 -5
  151. data/config/locales/ro-RO.yml +2 -5
  152. data/config/locales/ru.yml +7 -2
  153. data/config/locales/sk.yml +0 -2
  154. data/config/locales/sv.yml +24 -5
  155. data/config/locales/tr-TR.yml +3 -5
  156. data/config/locales/uk.yml +12 -2
  157. data/config/locales/zh-CN.yml +0 -5
  158. data/config/locales/zh-TW.yml +2 -10
  159. data/decidim-core.gemspec +90 -0
  160. data/lib/decidim/asset_router/storage.rb +2 -0
  161. data/lib/decidim/attribute_encryptor.rb +6 -4
  162. data/lib/decidim/attributes/time_with_zone.rb +1 -1
  163. data/lib/decidim/core/engine.rb +7 -6
  164. data/lib/decidim/core/seeds.rb +36 -32
  165. data/lib/decidim/core/test/factories.rb +296 -89
  166. data/lib/decidim/core/test/shared_examples/amendable/amendment_created_event_examples.rb +6 -26
  167. data/lib/decidim/core/test/shared_examples/amendable/amendment_promoted_event_examples.rb +8 -26
  168. data/lib/decidim/core/test/shared_examples/has_attachment_collections.rb +8 -6
  169. data/lib/decidim/core/test/shared_examples/has_attachments.rb +8 -8
  170. data/lib/decidim/core/test/shared_examples/has_category.rb +27 -0
  171. data/lib/decidim/core/test/shared_examples/has_reference.rb +1 -1
  172. data/lib/decidim/core/test/shared_examples/has_space_in_mcell_examples.rb +1 -1
  173. data/lib/decidim/core/test/shared_examples/map_examples.rb +3 -0
  174. data/lib/decidim/core/test/shared_examples/resource_endorsed_event_examples.rb +5 -2
  175. data/lib/decidim/core/test/shared_examples/resource_locator_presenter_examples.rb +134 -0
  176. data/lib/decidim/core/test/shared_examples/simple_event.rb +18 -2
  177. data/lib/decidim/core/test.rb +1 -0
  178. data/lib/decidim/core/version.rb +1 -1
  179. data/lib/decidim/core.rb +1 -0
  180. data/lib/decidim/engine_router.rb +17 -4
  181. data/lib/decidim/events/base_event.rb +3 -3
  182. data/lib/decidim/events/simple_event.rb +3 -17
  183. data/lib/decidim/form_builder.rb +8 -2
  184. data/lib/decidim/has_category.rb +1 -1
  185. data/lib/decidim/has_conversations.rb +91 -0
  186. data/lib/decidim/participable.rb +17 -0
  187. data/lib/decidim/upgrade/wysiwyg_migrator.rb +7 -0
  188. data/lib/decidim/view_model.rb +1 -0
  189. data/lib/decidim/webpacker/webpack/.modernizrrc +9 -0
  190. data/lib/premailer/adapter/decidim.rb +5 -4
  191. data/lib/tasks/decidim_reminders_tasks.rake +1 -0
  192. data/lib/tasks/upgrade/decidim_fix_categorization.rake +15 -0
  193. metadata +27 -30
  194. data/app/views/decidim/searches/index.js.erb +0 -7
  195. data/config/brakeman.ignore +0 -37
  196. data/config/environment.rb +0 -3
@@ -12,36 +12,16 @@ shared_examples "amendment created event" do
12
12
  end
13
13
 
14
14
  it_behaves_like "a simple event"
15
+ it_behaves_like "a simple event email"
16
+ it_behaves_like "a simple event notification"
15
17
 
16
18
  let(:emendation_author_nickname) { "@#{emendation.creator_author.nickname}" }
17
19
  let(:emendation_path) { Decidim::ResourceLocatorPresenter.new(emendation).path }
18
20
  let(:emendation_author_path) { Decidim::UserPresenter.new(emendation.creator_author).profile_path }
19
21
  let(:amendable_path) { Decidim::ResourceLocatorPresenter.new(amendable).path }
20
22
 
21
- describe "email_subject" do
22
- it "is generated correctly" do
23
- expect(subject.email_subject).to eq("New amendment for #{amendable_title}")
24
- end
25
- end
26
-
27
- describe "email_intro" do
28
- it "is generated correctly" do
29
- expect(subject.email_intro)
30
- .to eq("A new amendment has been created for #{amendable_title}. You can see it from this page:")
31
- end
32
- end
33
-
34
- describe "email_outro" do
35
- it "is generated correctly" do
36
- expect(subject.email_outro)
37
- .to eq("You have received this notification because you are following #{amendable_title}. You can stop receiving notifications following the previous link.")
38
- end
39
- end
40
-
41
- describe "notification_title" do
42
- it "is generated correctly" do
43
- expect(subject.notification_title)
44
- .to eq("A new amendment has been created for <a href=\"#{amendable_path}\">#{amendable_title}</a>.")
45
- end
46
- end
23
+ let(:email_subject) { "New amendment for #{amendable_title}" }
24
+ let(:email_intro) { "A new amendment has been created for #{amendable_title}. You can see it from this page:" }
25
+ let(:email_outro) { "You have received this notification because you are following #{amendable_title}. You can stop receiving notifications following the previous link." }
26
+ let(:notification_title) { "A new amendment has been created for <a href=\"#{amendable_path}\">#{amendable_title}</a>." }
47
27
  end
@@ -7,36 +7,18 @@ shared_examples "amendment promoted event" do
7
7
  let(:event_name) { "decidim.events.amendments.amendment_promoted" }
8
8
 
9
9
  it_behaves_like "a simple event"
10
+ it_behaves_like "a simple event email"
11
+ it_behaves_like "a simple event notification"
10
12
 
11
13
  let(:emendation_author_nickname) { "@#{emendation.creator_author.nickname}" }
12
14
  let(:emendation_path) { Decidim::ResourceLocatorPresenter.new(emendation).path }
13
15
  let(:emendation_author_path) { Decidim::UserPresenter.new(emendation.creator_author).profile_path }
14
16
  let(:amendable_path) { Decidim::ResourceLocatorPresenter.new(amendable).path }
15
17
 
16
- describe "email_subject" do
17
- it "is generated correctly" do
18
- expect(subject.email_subject).to eq("An amendment from #{emendation_author_nickname} has been published as a new proposal")
19
- end
20
- end
21
-
22
- describe "email_intro" do
23
- it "is generated correctly" do
24
- expect(subject.email_intro)
25
- .to eq("A rejected amendment for #{amendable_title} has been published as a new #{amendable_type}. You can see it from this page:")
26
- end
27
- end
28
-
29
- describe "email_outro" do
30
- it "is generated correctly" do
31
- expect(subject.email_outro)
32
- .to eq("You have received this notification because you are following #{amendable_title}. You can stop receiving notifications following the previous link.")
33
- end
34
- end
35
-
36
- describe "notification_title" do
37
- it "is generated correctly" do
38
- expect(subject.notification_title)
39
- .to eq("A <a href=\"#{emendation_path}\">rejected amendment</a> for <a href=\"#{amendable_path}\">#{amendable_title}</a> has been published as a new #{amendable_type} by <a href=\"#{emendation_author_path}\">#{emendation_author_nickname}</a>.") # rubocop:disable Layout/LineLength
40
- end
41
- end
18
+ let(:email_subject) { "An amendment from #{emendation_author_nickname} has been published as a new proposal" }
19
+ let(:email_intro) { "A rejected amendment for #{amendable_title} has been published as a new #{amendable_type}. You can see it from this page:" }
20
+ let(:email_outro) { "You have received this notification because you are following #{amendable_title}. You can stop receiving notifications following the previous link." }
21
+ # rubocop:disable Layout/LineLength
22
+ let(:notification_title) { "A <a href=\"#{emendation_path}\">rejected amendment</a> for <a href=\"#{amendable_path}\">#{amendable_title}</a> has been published as a new #{amendable_type} by <a href=\"#{emendation_author_path}\">#{emendation_author_nickname}</a>." }
23
+ # rubocop:enable Layout/LineLength
42
24
  end
@@ -13,13 +13,13 @@ shared_examples_for "has attachment collections" do
13
13
  end
14
14
 
15
15
  it "shows them" do
16
- expect(page).to have_content(/#{translated(attachment_collection.name, locale: :en)}/i)
16
+ expect(page).to have_content(translated(attachment_collection.name))
17
17
  end
18
18
 
19
19
  it "show their documents" do
20
20
  within "[id*=documents-#{attachment_collection.id}]", visible: false do
21
- expect(page).to have_content(:all, /#{translated(document.title, locale: :en)}/i)
22
- expect(page).not_to have_content(:all, /#{translated(other_document.title, locale: :en)}/i)
21
+ expect(page).to have_content(:all, translated(document.title))
22
+ expect(page).not_to have_content(:all, translated(other_document.title))
23
23
  end
24
24
  end
25
25
  end
@@ -36,7 +36,9 @@ shared_examples_for "has attachment collections" do
36
36
  end
37
37
 
38
38
  it "shows them ordered" do
39
- expect(translated(first_attachment_collection.name, locale: :en)).to appear_before(translated(last_attachment_collection.name, locale: :en))
39
+ expect(decidim_escape_translated(first_attachment_collection.name).gsub("&quot;",
40
+ "\"")).to appear_before(decidim_escape_translated(last_attachment_collection.name).gsub("&quot;",
41
+ "\""))
40
42
  end
41
43
  end
42
44
 
@@ -50,8 +52,8 @@ shared_examples_for "has attachment collections" do
50
52
  end
51
53
 
52
54
  it "is not present" do
53
- expect(page).to have_content(/#{translated(attachment_collection.name, locale: :en)}/i)
54
- expect(page).not_to have_content(/#{translated(empty_attachment_collection.name, locale: :en)}/i)
55
+ expect(page).to have_content(translated(attachment_collection.name))
56
+ expect(page).not_to have_content(translated(empty_attachment_collection.name))
55
57
  end
56
58
  end
57
59
  end
@@ -14,7 +14,7 @@ shared_examples_for "has attachments content blocks" do
14
14
 
15
15
  it "shows them" do
16
16
  within "[data-content] .documents__container" do
17
- expect(page).to have_content(/#{translated(document.title, locale: :en)}/i)
17
+ expect(page).to have_content(translated(document.title))
18
18
  end
19
19
 
20
20
  within "[data-content] [data-gallery]" do
@@ -27,7 +27,7 @@ shared_examples_for "has attachments content blocks" do
27
27
  let!(:last_document) { create(:attachment, :with_pdf, attached_to:, weight: 2) }
28
28
  let!(:first_document) { create(:attachment, :with_pdf, attached_to:, weight: 1) }
29
29
  let!(:last_image) { create(:attachment, attached_to:, weight: 2) }
30
- let!(:fist_image) { create(:attachment, attached_to:, weight: 1) }
30
+ let!(:first_image) { create(:attachment, attached_to:, weight: 1) }
31
31
 
32
32
  before do
33
33
  visit current_path
@@ -35,11 +35,11 @@ shared_examples_for "has attachments content blocks" do
35
35
 
36
36
  it "shows them ordered" do
37
37
  within "[data-content] .documents__container" do
38
- expect(translated(first_document.title, locale: :en)).to appear_before(translated(last_document.title, locale: :en))
38
+ expect(decidim_escape_translated(first_document.title).gsub("&quot;", "\"")).to appear_before(decidim_escape_translated(last_document.title).gsub("&quot;", "\""))
39
39
  end
40
40
 
41
41
  within "[data-content] [data-gallery]" do
42
- expect(strip_tags(translated(fist_image.title, locale: :en))).to appear_before(strip_tags(translated(last_image.title, locale: :en)))
42
+ expect(strip_tags(translated(first_image.title, locale: :en))).to appear_before(strip_tags(translated(last_image.title, locale: :en)))
43
43
  end
44
44
  end
45
45
  end
@@ -58,7 +58,7 @@ shared_examples_for "has attachments tabs" do
58
58
  it "shows them" do
59
59
  find("li [data-controls='panel-documents']").click
60
60
  within "#panel-documents" do
61
- expect(page).to have_content(/#{translated(document.title, locale: :en)}/i)
61
+ expect(page).to have_content(translated(document.title))
62
62
  end
63
63
 
64
64
  find("li [data-controls='panel-images']").click
@@ -72,7 +72,7 @@ shared_examples_for "has attachments tabs" do
72
72
  let!(:last_document) { create(:attachment, :with_pdf, attached_to:, weight: 2) }
73
73
  let!(:first_document) { create(:attachment, :with_pdf, attached_to:, weight: 1) }
74
74
  let!(:last_image) { create(:attachment, attached_to:, weight: 2) }
75
- let!(:fist_image) { create(:attachment, attached_to:, weight: 1) }
75
+ let!(:first_image) { create(:attachment, attached_to:, weight: 1) }
76
76
 
77
77
  before do
78
78
  visit current_path
@@ -81,12 +81,12 @@ shared_examples_for "has attachments tabs" do
81
81
  it "shows them ordered" do
82
82
  find("li [data-controls='panel-documents']").click
83
83
  within "#panel-documents" do
84
- expect(translated(first_document.title, locale: :en)).to appear_before(translated(last_document.title, locale: :en))
84
+ expect(decidim_escape_translated(first_document.title).gsub("&quot;", "\"")).to appear_before(decidim_escape_translated(last_document.title).gsub("&quot;", "\""))
85
85
  end
86
86
 
87
87
  find("li [data-controls='panel-images']").click
88
88
  within "#panel-images" do
89
- expect(strip_tags(translated(fist_image.title, locale: :en))).to appear_before(strip_tags(translated(last_image.title, locale: :en)))
89
+ expect(strip_tags(translated(first_image.title, locale: :en))).to appear_before(strip_tags(translated(last_image.title, locale: :en)))
90
90
  end
91
91
  end
92
92
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  shared_examples_for "has category" do
4
+ let(:participatory_space) { subject.participatory_space }
5
+
4
6
  context "when the category is from another organization" do
5
7
  before do
6
8
  subject.category = create(:category)
@@ -8,4 +10,29 @@ shared_examples_for "has category" do
8
10
 
9
11
  it { is_expected.not_to be_valid }
10
12
  end
13
+
14
+ context "when the category is from the same organization" do
15
+ before do
16
+ subject.category = create(:category, participatory_space:)
17
+ end
18
+
19
+ it { is_expected.to be_valid }
20
+ end
21
+
22
+ context "when the resource is being deleted" do
23
+ before do
24
+ subject.category = create(:category, participatory_space:)
25
+ subject.save!
26
+ end
27
+
28
+ it "persists the categorization" do
29
+ expect(subject.categorization).to be_persisted
30
+ end
31
+
32
+ it "deletes the categorization" do
33
+ expect(Decidim::Categorization.count).to eq(1)
34
+ expect { subject.destroy }.to change(Decidim::Categorization, :count).by(-1)
35
+ expect(Decidim::Categorization.count).to eq(0)
36
+ end
37
+ end
11
38
  end
@@ -8,7 +8,7 @@ shared_examples_for "has reference" do
8
8
 
9
9
  context "when there is not a custom resource reference generator present" do
10
10
  it "generates a valid reference" do
11
- expect(subject.reference).to match(/[A-z]+/)
11
+ expect(subject.reference).to match(/[a-zA-Z]+/)
12
12
  end
13
13
  end
14
14
 
@@ -8,7 +8,7 @@ shared_examples_for "has space in m-cell" do
8
8
  let(:show_space) { true }
9
9
 
10
10
  it "renders the space where the model belongs to" do
11
- expect(cell_html).to have_content(translated(model.component.participatory_space.title, locale: :en))
11
+ expect(cell_html).to have_content(decidim_escape_translated(model.component.participatory_space.title))
12
12
  end
13
13
  end
14
14
  end
@@ -99,6 +99,8 @@ shared_context "with frontend map elements" do
99
99
  let(:html_body) { "" }
100
100
 
101
101
  before do
102
+ # Create a favicon so it does not fail when trying to fetch it
103
+ favicon = ""
102
104
  # Create a temporary route to display the generated HTML in a correct site
103
105
  # context.
104
106
  final_html = html_document
@@ -106,6 +108,7 @@ shared_context "with frontend map elements" do
106
108
  get "maptiles/:z/:x/:y.png", to: ->(_) { [200, {}, [final_html]] }
107
109
  get "test_dynamic_map", to: ->(_) { [200, {}, [final_html]] }
108
110
  get "offline", to: ->(_) { [200, {}, [""]] }
111
+ get "/favicon.ico", to: ->(_) { [200, {}, [favicon]] }
109
112
  end
110
113
 
111
114
  visit "/test_dynamic_map"
@@ -35,17 +35,20 @@ shared_examples_for "resource endorsed event" do
35
35
  end
36
36
 
37
37
  describe "email_intro" do
38
+ let(:resource_title) { decidim_sanitize_translated(resource.title) }
38
39
  it "is generated correctly" do
39
40
  expect(subject.email_intro)
40
41
  .to eq("#{author.name} #{author_presenter.nickname}, who you are following, " \
41
- "has just endorsed \"#{translated resource.title}\" and we think it may be interesting to you. Check it out and contribute:")
42
+ "has just endorsed \"#{resource_title}\" and we think it may be interesting to you. Check it out and contribute:")
42
43
  end
43
44
  end
44
45
 
45
46
  describe "notification_title" do
47
+ let(:resource_title) { decidim_sanitize_translated(resource.title) }
48
+
46
49
  it "is generated correctly" do
47
50
  expect(subject.notification_title)
48
- .to include("The <a href=\"#{resource_path}\">#{translated resource.title}</a> #{resource_type} has been endorsed by ")
51
+ .to include("The <a href=\"#{resource_path}\">#{resource_title}</a> #{resource_type} has been endorsed by ")
49
52
 
50
53
  expect(subject.notification_title)
51
54
  .to include("<a href=\"/profiles/#{author.nickname}\">#{author.name} #{author_presenter.nickname}</a>.")
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ shared_examples "generates routes without query strings on slug" do
6
+ let(:organization) { create(:organization, host: "1.lvh.me") }
7
+ let(:participatory_space) { create(factory_name, organization:) }
8
+ let(:component) { create(:component, id: 1, participatory_space:) }
9
+ let(:resource) { create(:dummy_resource, id: 1, component:) }
10
+
11
+ context "with a component resource" do
12
+ describe "#url" do
13
+ subject { described_class.new(resource).url }
14
+
15
+ it { is_expected.to eq("http://1.lvh.me:#{Capybara.server_port}/#{route_fragment}/f/1/dummy_resources/1") }
16
+
17
+ context "when specific port configured" do
18
+ before do
19
+ allow(ActionMailer::Base)
20
+ .to receive(:default_url_options)
21
+ .and_return(port: 3000)
22
+ end
23
+
24
+ it { is_expected.to eq("http://1.lvh.me:3000/#{route_fragment}/f/1/dummy_resources/1") }
25
+ end
26
+ end
27
+
28
+ describe "#path" do
29
+ subject { described_class.new(resource).path }
30
+
31
+ it { is_expected.to eq("/#{route_fragment}/f/1/dummy_resources/1") }
32
+ end
33
+
34
+ describe "#show" do
35
+ subject { described_class.new(participatory_space).show }
36
+
37
+ it { is_expected.to eq("/admin/#{admin_route_fragment}") }
38
+ end
39
+
40
+ describe "#edit" do
41
+ subject { described_class.new(participatory_space).edit }
42
+
43
+ it { is_expected.to eq("/admin/#{admin_route_fragment}/edit") }
44
+ end
45
+ end
46
+
47
+ context "with a polymorphic resource" do
48
+ let(:nested_resource) do
49
+ create(:nested_dummy_resource, id: 1, dummy_resource: resource)
50
+ end
51
+
52
+ describe "#url" do
53
+ subject { described_class.new([resource, nested_resource]).url }
54
+
55
+ it { is_expected.to eq("http://1.lvh.me:#{Capybara.server_port}/#{route_fragment}/f/1/dummy_resources/1/nested_dummy_resources/1") }
56
+
57
+ context "when specific port configured" do
58
+ before do
59
+ allow(ActionMailer::Base)
60
+ .to receive(:default_url_options)
61
+ .and_return(port: 3000)
62
+ end
63
+
64
+ it { is_expected.to eq("http://1.lvh.me:3000/#{route_fragment}/f/1/dummy_resources/1/nested_dummy_resources/1") }
65
+ end
66
+ end
67
+
68
+ describe "#path" do
69
+ subject { described_class.new([resource, nested_resource]).path }
70
+
71
+ it { is_expected.to eq("/#{route_fragment}/f/1/dummy_resources/1/nested_dummy_resources/1") }
72
+ end
73
+
74
+ describe "#index" do
75
+ subject { described_class.new([resource, nested_resource]).index }
76
+
77
+ it { is_expected.to eq("/#{route_fragment}/f/1/dummy_resources/1/nested_dummy_resources") }
78
+ end
79
+
80
+ describe "#admin_index" do
81
+ subject { described_class.new([resource, nested_resource]).admin_index }
82
+
83
+ it { is_expected.to eq("/admin/#{admin_route_fragment}/components/1/manage/dummy_resources/1/nested_dummy_resources") }
84
+ end
85
+
86
+ describe "#show" do
87
+ subject { described_class.new([resource, nested_resource]).show }
88
+
89
+ it { is_expected.to eq("/admin/#{admin_route_fragment}/components/1/manage/dummy_resources/1/nested_dummy_resources/1") }
90
+ end
91
+
92
+ describe "#edit" do
93
+ subject { described_class.new([resource, nested_resource]).edit }
94
+
95
+ it { is_expected.to eq("/admin/#{admin_route_fragment}/components/1/manage/dummy_resources/1/nested_dummy_resources/1/edit") }
96
+ end
97
+ end
98
+
99
+ context "with a participatory_space" do
100
+ describe "#url" do
101
+ subject { described_class.new(participatory_space).url }
102
+
103
+ it { is_expected.to eq("http://1.lvh.me:#{Capybara.server_port}/#{route_fragment}") }
104
+
105
+ context "when specific port configured" do
106
+ before do
107
+ allow(ActionMailer::Base)
108
+ .to receive(:default_url_options)
109
+ .and_return(port: 3000)
110
+ end
111
+
112
+ it { is_expected.to eq("http://1.lvh.me:3000/#{route_fragment}") }
113
+ end
114
+ end
115
+
116
+ describe "#path" do
117
+ subject { described_class.new(participatory_space).path }
118
+
119
+ it { is_expected.to eq("/#{route_fragment}") }
120
+ end
121
+
122
+ describe "#show" do
123
+ subject { described_class.new(participatory_space).show }
124
+
125
+ it { is_expected.to eq("/admin/#{admin_route_fragment}") }
126
+ end
127
+
128
+ describe "#edit" do
129
+ subject { described_class.new(participatory_space).edit }
130
+
131
+ it { is_expected.to eq("/admin/#{admin_route_fragment}/edit") }
132
+ end
133
+ end
134
+ end
@@ -28,10 +28,10 @@ shared_context "when a simple event" do
28
28
  let(:extra) { {} }
29
29
  let(:resource_path) { resource_locator(resource).path }
30
30
  let(:resource_url) { resource_locator(resource).url }
31
- let(:resource_title) { resource.title["en"] }
31
+ let(:resource_title) { decidim_sanitize_translated(resource.title) }
32
32
  # to be used when resource is a component resource, not a participatory space, in which case should be overriden
33
33
  let(:participatory_space) { resource.participatory_space }
34
- let(:participatory_space_title) { participatory_space.title["en"] }
34
+ let(:participatory_space_title) { decidim_sanitize_translated(participatory_space.title) }
35
35
  let(:participatory_space_path) { Decidim::ResourceLocatorPresenter.new(participatory_space).path }
36
36
  let(:participatory_space_url) { Decidim::ResourceLocatorPresenter.new(participatory_space).url }
37
37
  let(:author) do
@@ -65,6 +65,7 @@ shared_examples_for "a simple event" do |skip_space_checks|
65
65
  it "is generated correctly" do
66
66
  expect(subject.email_subject).to be_kind_of(String)
67
67
  expect(subject.email_subject).not_to include("translation missing")
68
+ expect(subject.email_subject).not_to include("script")
68
69
  end
69
70
  end
70
71
 
@@ -100,6 +101,7 @@ shared_examples_for "a simple event" do |skip_space_checks|
100
101
  it "is generated correctly" do
101
102
  expect(subject.notification_title).to be_kind_of(String)
102
103
  expect(subject.notification_title).not_to include("translation missing")
104
+ expect(subject.notification_title).not_to include("script")
103
105
  end
104
106
  end
105
107
 
@@ -129,6 +131,12 @@ shared_examples_for "a simple event" do |skip_space_checks|
129
131
  expect(subject.participatory_space_url).to start_with("http")
130
132
  end
131
133
  end
134
+
135
+ describe "participatory_space_title" do
136
+ it "is generated correctly" do
137
+ expect(translated(participatory_space.title)).to include("script")
138
+ end
139
+ end
132
140
  end
133
141
 
134
142
  describe "i18n_options" do
@@ -158,6 +166,10 @@ shared_examples_for "a simple event email" do
158
166
  it "is generated correctly" do
159
167
  expect(subject.email_subject).to eq(email_subject)
160
168
  end
169
+
170
+ it "is html safe" do
171
+ expect(subject.email_subject).not_to include("script")
172
+ end
161
173
  end
162
174
 
163
175
  describe "email_intro" do
@@ -179,5 +191,9 @@ shared_examples_for "a simple event notification" do
179
191
  expect(subject.notification_title)
180
192
  .to eq(notification_title)
181
193
  end
194
+
195
+ it "is html safe" do
196
+ expect(subject.notification_title).not_to include("script")
197
+ end
182
198
  end
183
199
  end
@@ -77,3 +77,4 @@ require "decidim/core/test/shared_examples/digest_mail_examples"
77
77
  require "decidim/core/test/shared_examples/hideable_resource_examples"
78
78
  require "decidim/core/test/shared_examples/active_support_examples"
79
79
  require "decidim/core/test/shared_examples/statistics_cell_examples"
80
+ require "decidim/core/test/shared_examples/resource_locator_presenter_examples"
@@ -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.28.0"
7
+ "0.28.1"
8
8
  end
9
9
  end
10
10
  end
data/lib/decidim/core.rb CHANGED
@@ -122,6 +122,7 @@ module Decidim
122
122
  autoload :ModerationTools, "decidim/moderation_tools"
123
123
  autoload :ContentSecurityPolicy, "decidim/content_security_policy"
124
124
  autoload :IconRegistry, "decidim/icon_registry"
125
+ autoload :HasConversations, "decidim/has_conversations"
125
126
 
126
127
  include ActiveSupport::Configurable
127
128
  # Loads seeds from all engines.
@@ -16,7 +16,7 @@ module Decidim
16
16
  #
17
17
  # @return [EngineRouter] The new engine router
18
18
  def self.main_proxy(target)
19
- new(target.mounted_engine, target.mounted_params)
19
+ new(target.mounted_engine, target.mounted_params, target)
20
20
  end
21
21
 
22
22
  # Instantiates a router to the backend engine for an object.
@@ -25,12 +25,13 @@ module Decidim
25
25
  #
26
26
  # @return [EngineRouter] The new engine router
27
27
  def self.admin_proxy(target)
28
- new(target.mounted_admin_engine, target.mounted_params)
28
+ new(target.mounted_admin_engine, target.mounted_params, target)
29
29
  end
30
30
 
31
- def initialize(engine, default_url_options)
31
+ def initialize(engine, default_url_options, target = nil)
32
32
  @engine = engine
33
33
  @default_url_options = default_url_options
34
+ @target = target
34
35
  end
35
36
 
36
37
  def default_url_options
@@ -44,11 +45,23 @@ module Decidim
44
45
  def method_missing(method_name, *args)
45
46
  return super unless route_helper?(method_name)
46
47
 
47
- send(@engine).send(method_name, *args)
48
+ filter_slug_params!(method_name)
49
+
50
+ send(engine).send(method_name, *args)
48
51
  end
49
52
 
50
53
  private
51
54
 
55
+ attr_reader :engine, :target
56
+
57
+ def filter_slug_params!(method_name)
58
+ return if target.nil?
59
+ return unless target.respond_to?(:mounted_params)
60
+
61
+ skip_space_slug = target.respond_to?(:slug_param_name) && target.respond_to?(:skip_space_slug?) && target.skip_space_slug?(method_name)
62
+ @default_url_options.except!(target.slug_param_name) if skip_space_slug == true
63
+ end
64
+
52
65
  def route_helper?(method_name)
53
66
  method_name.to_s.match?(/_(url|path)$/)
54
67
  end
@@ -7,7 +7,7 @@ module Decidim
7
7
  # notifications dashboard and to generate other notifications (emails, for example).
8
8
  class BaseEvent
9
9
  extend ActiveModel::Translation
10
- include Decidim::TranslatableAttributes
10
+ include Decidim::SanitizeHelper
11
11
 
12
12
  class_attribute :types
13
13
  self.types = []
@@ -103,9 +103,9 @@ module Decidim
103
103
  return unless resource
104
104
 
105
105
  title = if resource.respond_to?(:title)
106
- translated_attribute(resource.title)
106
+ decidim_sanitize_translated(resource.title)
107
107
  elsif resource.respond_to?(:name)
108
- translated_attribute(resource.name)
108
+ decidim_sanitize_translated(resource.name)
109
109
  end
110
110
 
111
111
  Decidim::ContentProcessor.render_without_format(title, links: false).html_safe
@@ -11,7 +11,6 @@ module Decidim
11
11
  include Decidim::Events::EmailEvent
12
12
  include Decidim::Events::NotificationEvent
13
13
  include Decidim::ComponentPathHelper
14
- include Decidim::SanitizeHelper
15
14
 
16
15
  delegate :created_at, to: :resource
17
16
 
@@ -34,14 +33,7 @@ module Decidim
34
33
  end
35
34
 
36
35
  def email_subject
37
- I18n.t("email_subject", **email_subject_i18n_options).html_safe
38
- end
39
-
40
- def email_subject_i18n_options
41
- sanitized_values = { resource_title: decidim_sanitize(resource_title) }
42
- sanitized_values[:mentioned_proposal_title] = decidim_sanitize(mentioned_proposal_title) if i18n_options.has_key?(:mentioned_proposal_title)
43
- sanitized_values[:participatory_space_title] = decidim_sanitize(participatory_space_title) if i18n_options.has_key?(:participatory_space_title)
44
- i18n_options.merge(sanitized_values)
36
+ I18n.t("email_subject", **i18n_options).html_safe
45
37
  end
46
38
 
47
39
  def email_intro
@@ -77,13 +69,7 @@ module Decidim
77
69
 
78
70
  # Public: The Hash of options to pass to the I18.t method.
79
71
  def i18n_options
80
- default_i18n_options.merge(event_interpolations).transform_values do |value|
81
- if value.is_a?(String)
82
- decidim_html_escape(value)
83
- else
84
- value
85
- end
86
- end
72
+ default_i18n_options.merge(event_interpolations)
87
73
  end
88
74
 
89
75
  # Caches the path for the given resource when it is a Decidim::Component.
@@ -136,7 +122,7 @@ module Decidim
136
122
  end
137
123
 
138
124
  def participatory_space_title
139
- translated_attribute(participatory_space.try(:title))
125
+ decidim_sanitize_translated(participatory_space.try(:title))
140
126
  end
141
127
  end
142
128
  end