decidim-core 0.26.0.rc1 → 0.26.1

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.

Potentially problematic release.


This version of decidim-core might be problematic. Click here for more details.

Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity_cell.rb +3 -0
  3. data/app/cells/decidim/author_cell.rb +1 -0
  4. data/app/cells/decidim/card_m_cell.rb +1 -1
  5. data/app/cells/decidim/diff/diff_mode_dropdown.erb +13 -8
  6. data/app/cells/decidim/diff/diff_mode_html.erb +13 -8
  7. data/app/cells/decidim/diff/show.erb +5 -3
  8. data/app/cells/decidim/endorsement_buttons_cell.rb.2 +211 -0
  9. data/app/cells/decidim/endorsers_list/show.erb +1 -1
  10. data/app/cells/decidim/fingerprint/show.erb +1 -1
  11. data/app/cells/decidim/followers/show.erb +1 -1
  12. data/app/cells/decidim/following/show.erb +2 -2
  13. data/app/cells/decidim/groups/show.erb +1 -1
  14. data/app/cells/decidim/members/show.erb +1 -1
  15. data/app/cells/decidim/profile_sidebar/show.erb +1 -1
  16. data/app/cells/decidim/user_conversation/messages.erb +1 -1
  17. data/app/cells/decidim/user_conversation_cell.rb +4 -0
  18. data/app/cells/decidim/user_conversations/add_conversation_users.erb +1 -1
  19. data/app/cells/decidim/version_cell.rb +1 -1
  20. data/app/cells/decidim/versions_list_cell.rb +1 -1
  21. data/app/cells/decidim/versions_list_item/show.erb +2 -2
  22. data/app/commands/decidim/messaging/reply_to_conversation.rb +4 -1
  23. data/app/commands/decidim/unendorse_resource.rb +5 -4
  24. data/app/controllers/decidim/application_controller.rb +1 -0
  25. data/app/controllers/decidim/components/base_controller.rb +0 -1
  26. data/app/events/decidim/amendable/amendment_base_event.rb +1 -1
  27. data/app/forms/decidim/messaging/message_form.rb +1 -1
  28. data/app/helpers/decidim/endorsable_helper.rb +7 -6
  29. data/app/helpers/decidim/social_share_button_helper.rb +26 -0
  30. data/app/helpers/decidim/twitter_search_helper.rb +14 -0
  31. data/app/models/decidim/moderation.rb +3 -0
  32. data/app/models/decidim/user.rb +0 -9
  33. data/app/models/decidim/user_base_entity.rb +6 -0
  34. data/app/models/decidim/user_group.rb +0 -3
  35. data/app/packs/entrypoints/decidim_core.js +3 -0
  36. data/app/packs/src/decidim/back_to_list.js +26 -0
  37. data/app/packs/src/decidim/dialog_mode.js +11 -99
  38. data/app/packs/src/decidim/dialog_mode.test.js +17 -4
  39. data/app/packs/src/decidim/diff_mode_dropdown.js +3 -3
  40. data/app/packs/src/decidim/dropdowns_menus.js +1 -0
  41. data/app/packs/src/decidim/focus_guard.js +142 -0
  42. data/app/packs/src/decidim/form_filter.js +17 -1
  43. data/app/packs/src/decidim/form_remote.js +38 -0
  44. data/app/packs/src/decidim/index.js +15 -0
  45. data/app/packs/src/decidim/input_character_counter.js +4 -1
  46. data/app/packs/src/decidim/input_emoji.js +38 -6
  47. data/app/packs/src/decidim/input_multiple_mentions.js +19 -0
  48. data/app/packs/src/decidim/vendor/social-share-button.js +174 -0
  49. data/app/packs/stylesheets/decidim/extras/_quill.scss +1 -2
  50. data/app/packs/stylesheets/decidim/modules/_buttons.scss +2 -1
  51. data/app/packs/stylesheets/decidim/modules/_comments.scss +1 -0
  52. data/app/packs/stylesheets/decidim/modules/_forms.scss +6 -1
  53. data/app/packs/stylesheets/decidim/modules/_typography.scss +2 -0
  54. data/app/packs/stylesheets/decidim/utils/_settings.scss +1 -0
  55. data/app/packs/stylesheets/decidim/vendor/_social_share_button.scss +7 -1
  56. data/app/permissions/decidim/permissions.rb +9 -0
  57. data/app/presenters/decidim/menu_item_presenter.rb +9 -1
  58. data/app/views/decidim/account/show.html.erb +1 -1
  59. data/app/views/decidim/application/_collection.html.erb +2 -2
  60. data/app/views/decidim/endorsements/identities.html.erb +1 -1
  61. data/app/views/decidim/groups/new.html.erb +2 -0
  62. data/app/views/decidim/messaging/conversations/_add_conversation_users.html.erb +1 -1
  63. data/app/views/decidim/messaging/conversations/_conversation.html.erb +8 -2
  64. data/app/views/decidim/messaging/conversations/_reply.html.erb +1 -1
  65. data/app/views/decidim/messaging/conversations/_start.html.erb +1 -1
  66. data/app/views/decidim/messaging/conversations/create.js.erb +1 -0
  67. data/app/views/layouts/decidim/_language_chooser.html.erb +9 -2
  68. data/app/views/layouts/decidim/_logo.html.erb +1 -1
  69. data/config/initializers/devise.rb +7 -19
  70. data/config/locales/ar.yml +63 -0
  71. data/config/locales/ca.yml +46 -2
  72. data/config/locales/cs.yml +10 -2
  73. data/config/locales/de.yml +12 -1
  74. data/config/locales/en.yml +9 -0
  75. data/config/locales/es-MX.yml +47 -0
  76. data/config/locales/es-PY.yml +47 -0
  77. data/config/locales/es.yml +2 -0
  78. data/config/locales/eu.yml +6 -0
  79. data/config/locales/fi-plain.yml +48 -0
  80. data/config/locales/fi.yml +10 -0
  81. data/config/locales/fr-CA.yml +16 -0
  82. data/config/locales/fr.yml +41 -25
  83. data/config/locales/gl.yml +51 -0
  84. data/config/locales/hu.yml +111 -0
  85. data/config/locales/it.yml +1 -0
  86. data/config/locales/ja.yml +16 -5
  87. data/config/locales/nl.yml +0 -3
  88. data/config/locales/no.yml +225 -0
  89. data/config/locales/ro-RO.yml +14 -0
  90. data/config/locales/sv.yml +45 -3
  91. data/db/seeds.rb +2 -2
  92. data/lib/decidim/api/functions/user_entity_finder.rb +2 -1
  93. data/lib/decidim/api/functions/user_entity_list.rb +2 -1
  94. data/lib/decidim/content_renderers/link_renderer.rb +1 -1
  95. data/lib/decidim/core/engine.rb +43 -0
  96. data/lib/decidim/core/test/shared_examples/amendable/amendment_accepted_event_examples.rb +0 -1
  97. data/lib/decidim/core/test/shared_examples/amendable/amendment_created_event_examples.rb +0 -1
  98. data/lib/decidim/core/test/shared_examples/amendable/amendment_promoted_event_examples.rb +0 -1
  99. data/lib/decidim/core/test/shared_examples/amendable/amendment_rejected_event_examples.rb +0 -1
  100. data/lib/decidim/core/test/shared_examples/comments_examples.rb +27 -0
  101. data/lib/decidim/core/test/shared_examples/conversations_examples.rb +19 -0
  102. data/lib/decidim/core/test/shared_examples/endorsable.rb +69 -0
  103. data/lib/decidim/core/test/shared_examples/searchable_results_examples.rb +34 -0
  104. data/lib/decidim/core/test.rb +2 -0
  105. data/lib/decidim/core/version.rb +1 -1
  106. data/lib/decidim/endorsable.rb +5 -1
  107. data/lib/decidim/map/autocomplete.rb +12 -5
  108. data/lib/decidim/middleware/rails_cookies.rb +23 -0
  109. data/lib/decidim/resourceable.rb +1 -0
  110. data/lib/decidim/searchable.rb +10 -4
  111. data/lib/decidim/social_share/service.rb +33 -0
  112. data/lib/decidim/social_share/service_registry.rb +63 -0
  113. data/lib/decidim/social_share.rb +45 -0
  114. data/lib/decidim/view_model.rb +0 -1
  115. data/lib/tasks/decidim_webpacker_tasks.rake +4 -10
  116. data/lib/tasks/upgrade/decidim_moderation_tasks.rake +32 -0
  117. metadata +22 -10
  118. data/app/helpers/decidim/filter_params_helper.rb +0 -30
  119. data/config/initializers/mail_previews.rb +0 -5
@@ -76,6 +76,34 @@ sv:
76
76
  decidim_with_day_and_month_name: "%A %d %b %Y"
77
77
  decidim_with_month_name: "%d %B %Y"
78
78
  decidim_with_month_name_short: "%d %b"
79
+ datetime:
80
+ distance_in_words:
81
+ about_x_hours:
82
+ one: ungefär 1 timme
83
+ other: ungefär %{count} timmar
84
+ about_x_months:
85
+ one: ungefär 1 månad
86
+ other: ungefär %{count} månader
87
+ half_a_minute: en halv minut
88
+ less_than_x_minutes:
89
+ one: mindre än en minut
90
+ other: mindre än %{count} minut
91
+ less_than_x_seconds:
92
+ one: just nu
93
+ other: mindre än %{count} sekunder
94
+ x_days:
95
+ one: 1 dag sedan
96
+ other: "%{count} dagar sedan"
97
+ x_hours:
98
+ one: 1 timme sedan
99
+ other: "%{count} timmar sedan"
100
+ x_minutes:
101
+ one: 1 min sedan
102
+ other: "%{count} min sedan"
103
+ x_seconds:
104
+ one: 1 sek. sedan
105
+ other: "%{count} sek. sedan"
106
+ zero: just nu
79
107
  decidim:
80
108
  accessibility:
81
109
  external_link: Extern länk
@@ -153,6 +181,7 @@ sv:
153
181
  block: "%{user_name} blockerade användaren %{resource_name}"
154
182
  invite: "%{user_name} bjöd in användaren %{resource_name} med rollen: %{role}"
155
183
  officialize: "%{user_name} gjorde deltagaren %{resource_name} officiell"
184
+ promote: "%{user_name} befordrade %{resource_name}"
156
185
  remove_from_admin: "%{user_name} tog bort deltagaren %{resource_name} med rollen: %{role}"
157
186
  show_email: "%{user_name} hämtade e-postadressen för deltagaren %{resource_name}"
158
187
  transfer: "%{user_name} överförde deltagaren %{resource_name}"
@@ -967,6 +996,7 @@ sv:
967
996
  next: Nästa
968
997
  no_conversations: Du har inga konversationer än
969
998
  title: Samtal
999
+ to: Till
970
1000
  reply:
971
1001
  placeholder: Ditt svar...
972
1002
  send: Skicka
@@ -974,6 +1004,7 @@ sv:
974
1004
  show:
975
1005
  back: Tillbaka till alla konversationer
976
1006
  chat_with: Konversation med
1007
+ deleted_accounts: Du kan inte meddela raderade konton.
977
1008
  not_allowed: Deltagaren tar inte emot direktmeddelanden.
978
1009
  title: Samtal med %{usernames}
979
1010
  start:
@@ -1041,6 +1072,12 @@ sv:
1041
1072
  greetings: Hälsningar,<br/>%{organization_name}<br/><a href="%{organization_url}">%{organization_url}</a>
1042
1073
  hello: Hallå,
1043
1074
  subject: Vill du fortsätta att få relevant information om %{organization_name}?
1075
+ notification_mailer:
1076
+ event_received:
1077
+ no_translation_available: Tyvärr kunde maskinöversättningen inte hämtas när e-postmeddelandet skickades. Du kan kontrollera översättningen av originaltexten på följande länk %{link}.
1078
+ original_text: 'Originaltext:'
1079
+ same_language: Innehållet har skrevs i ditt valda språk (%{language}) så ingen automatisk översättning visas i detta e-post.
1080
+ translated_text: 'Maskinöversatt text:'
1044
1081
  notifications:
1045
1082
  no_notifications: Inga meddelanden ännu.
1046
1083
  notifications_settings:
@@ -1048,13 +1085,13 @@ sv:
1048
1085
  administrators: Administratörer
1049
1086
  allow_public_contact: Tillåt alla att skicka direktmeddelanden till mig, även de som jag inte följer.
1050
1087
  direct_messages: Ta emot direktmeddelanden från alla
1051
- email_on_moderations: Jag vill få ett e-brev varje gång något anmäls för moderering.
1052
- email_on_notification: Jag vill få ett e-brev varje gång jag får ett meddelande.
1088
+ email_on_moderations: Jag vill få ett e-post varje gång något anmäls för moderering.
1089
+ email_on_notification: Jag vill få ett e-post varje gång jag får en notis.
1053
1090
  everything_followed: Allt jag följer
1054
1091
  newsletter_notifications: Jag vill få nyhetsbrev
1055
1092
  newsletters: Nyhetsbrev
1056
1093
  own_activity: Min egen verksamhet, som när någon kommenterar mitt förslag eller nämner mig
1057
- receive_notifications_about: Jag vill få meddelanden om
1094
+ receive_notifications_about: Jag vill få notiser om
1058
1095
  send_notifications_by_email: Skicka meddelanden via e-post
1059
1096
  update_notifications_settings: Spara ändringar
1060
1097
  update:
@@ -1338,6 +1375,7 @@ sv:
1338
1375
  title_reply: Svara
1339
1376
  show:
1340
1377
  back: Visa alla samtal
1378
+ deleted_accounts: Du kan inte meddela raderade konton.
1341
1379
  not_allowed: Den här användaren accepterar inte fler direkta meddelanden.
1342
1380
  title: Konversation med %{usernames}
1343
1381
  update:
@@ -1635,10 +1673,12 @@ sv:
1635
1673
  name: Svenska
1636
1674
  name_with_error: Svenska (fel!)
1637
1675
  password_validator:
1676
+ blacklisted: är svartlistad
1638
1677
  domain_included_in_password: är för likt detta domännamn
1639
1678
  email_included_in_password: är för likt din e-post
1640
1679
  fallback: är inte giltigt
1641
1680
  name_included_in_password: är för likt ditt namn
1681
+ nickname_included_in_password: är för likt ditt användarnamn
1642
1682
  not_enough_unique_characters: har inte tillräckligt med unika tecken
1643
1683
  password_not_allowed: är ej tillåtet
1644
1684
  password_too_common: är för vanligt
@@ -1673,6 +1713,8 @@ sv:
1673
1713
  day_of_week: "%a"
1674
1714
  day_of_week_long: "%a %e"
1675
1715
  day_of_year: "%d.%m.%y"
1716
+ ddmm: "%d/%m"
1717
+ ddmmyyyy: "%d/%m/%Y"
1676
1718
  decidim_day_of_year: "%d %B %Y"
1677
1719
  decidim_short: "%d/%m/%Y %H:%M"
1678
1720
  default: "%d %B %Y kl.%H:%M:%S"
data/db/seeds.rb CHANGED
@@ -66,10 +66,10 @@ if !Rails.env.production? || ENV["SEED"]
66
66
  organization: organization
67
67
  )
68
68
 
69
- 3.times do
69
+ 3.times do |time|
70
70
  parent = Decidim::Scope.create!(
71
71
  name: Decidim::Faker::Localized.literal(Faker::Address.unique.state),
72
- code: Faker::Address.unique.country_code,
72
+ code: "#{Faker::Address.country_code}_#{time}",
73
73
  scope_type: province,
74
74
  organization: organization
75
75
  )
@@ -22,7 +22,8 @@ module Decidim
22
22
  filters[argument.to_sym] = v
23
23
  end
24
24
  Decidim::UserBaseEntity
25
- .where.not(confirmed_at: nil)
25
+ .confirmed
26
+ .not_blocked
26
27
  .find_by(filters)
27
28
  end
28
29
  end
@@ -19,7 +19,8 @@ module Decidim
19
19
  def call(_obj, args, ctx)
20
20
  @query = Decidim::UserBaseEntity
21
21
  .where(organization: ctx[:current_organization])
22
- .where.not(confirmed_at: nil)
22
+ .confirmed
23
+ .not_blocked
23
24
  .includes(avatar_attachment: :blob)
24
25
  add_filter_keys(args[:filter])
25
26
  add_order_keys(args[:order].to_h)
@@ -20,7 +20,7 @@ module Decidim
20
20
  def render(options = {})
21
21
  return content unless content.is_a?(String)
22
22
 
23
- options = { target: "_blank", rel: "nofollow noopener" }.merge(options)
23
+ options = { target: "_blank", rel: "nofollow noopener noreferrer ugc" }.merge(options)
24
24
  auto_link(content, options)
25
25
  end
26
26
 
@@ -22,6 +22,7 @@ require "omniauth"
22
22
  require "omniauth-facebook"
23
23
  require "omniauth-twitter"
24
24
  require "omniauth-google-oauth2"
25
+ require "omniauth/rails_csrf_protection"
25
26
  require "invisible_captcha"
26
27
  require "premailer/rails"
27
28
  require "premailer/adapter/decidim"
@@ -48,6 +49,9 @@ require "decidim/api"
48
49
  require "decidim/middleware/strip_x_forwarded_host"
49
50
  require "decidim/middleware/current_organization"
50
51
 
52
+ # Backport cookie handling extensions for Rails 6.0
53
+ require "decidim/middleware/rails_cookies"
54
+
51
55
  module Decidim
52
56
  module Core
53
57
  # Decidim's core Rails Engine.
@@ -311,6 +315,7 @@ module Decidim
311
315
 
312
316
  initializer "Expire sessions" do
313
317
  Rails.application.config.session_store :cookie_store, secure: Decidim.config.force_ssl, expire_after: Decidim.config.expire_session_after
318
+ Rails.application.config.action_dispatch.cookies_same_site_protection = :lax
314
319
  end
315
320
 
316
321
  initializer "decidim.core.register_resources" do
@@ -545,6 +550,44 @@ module Decidim
545
550
  Decidim.register_assets_path File.expand_path("app/packs", root)
546
551
  end
547
552
 
553
+ initializer "decidim_core.preview_mailer" do
554
+ # Load in mailer previews for apps to use in development.
555
+ # We need to make sure we call `Preview.all` before requiring our
556
+ # previews, otherwise any previews the app attempts to add need to be
557
+ # manually required.
558
+ if Rails.env.development? || Rails.env.test?
559
+ ActionMailer::Preview.all
560
+
561
+ Dir[root.join("spec/mailers/previews/**/*_preview.rb")].each do |file|
562
+ require_dependency file
563
+ end
564
+ end
565
+ end
566
+
567
+ # These are moved from initializers/devise.rb because we need to run initializers folder before
568
+ # setting these or Decidim.config variables have default values.
569
+ initializer "decidim_core.after_initializers_folder", after: "load_config_initializers" do
570
+ Devise.setup do |config|
571
+ # ==> Mailer Configuration
572
+ # Configure the e-mail address which will be shown in Devise::Mailer,
573
+ # note that it will be overwritten if you use your own mailer class
574
+ # with default "from" parameter.
575
+ config.mailer_sender = Decidim.config.mailer_sender
576
+
577
+ # A period that the user is allowed to access the website even without
578
+ # confirming their account. For instance, if set to 2.days, the user will be
579
+ # able to access the website for two days without confirming their account,
580
+ # access will be blocked just in the third day. Default is 0.days, meaning
581
+ # the user cannot access the website without confirming their account.
582
+ config.allow_unconfirmed_access_for = Decidim.unconfirmed_access_for
583
+
584
+ # ==> Configuration for :timeoutable
585
+ # The time you want to timeout the user session without activity. After this
586
+ # time the user will be asked for credentials again. Default is 30 minutes.
587
+ config.timeout_in = Decidim.config.expire_session_after
588
+ end
589
+ end
590
+
548
591
  config.to_prepare do
549
592
  FoundationRailsHelper::FlashHelper.include Decidim::FlashHelperExtensions
550
593
  end
@@ -8,7 +8,6 @@ shared_examples "amendment accepted event" do
8
8
 
9
9
  it_behaves_like "a simple event"
10
10
 
11
- let(:amendable_title) { amendable.title }
12
11
  let(:emendation_author_nickname) { "@#{emendation.creator_author.nickname}" }
13
12
  let(:emendation_path) { Decidim::ResourceLocatorPresenter.new(emendation).path }
14
13
  let(:emendation_author_path) { Decidim::UserPresenter.new(emendation.creator_author).profile_path }
@@ -13,7 +13,6 @@ shared_examples "amendment created event" do
13
13
 
14
14
  it_behaves_like "a simple event"
15
15
 
16
- let(:amendable_title) { amendable.title }
17
16
  let(:emendation_author_nickname) { "@#{emendation.creator_author.nickname}" }
18
17
  let(:emendation_path) { Decidim::ResourceLocatorPresenter.new(emendation).path }
19
18
  let(:emendation_author_path) { Decidim::UserPresenter.new(emendation.creator_author).profile_path }
@@ -8,7 +8,6 @@ shared_examples "amendment promoted event" do
8
8
 
9
9
  it_behaves_like "a simple event"
10
10
 
11
- let(:amendable_title) { amendable.title }
12
11
  let(:emendation_author_nickname) { "@#{emendation.creator_author.nickname}" }
13
12
  let(:emendation_path) { Decidim::ResourceLocatorPresenter.new(emendation).path }
14
13
  let(:emendation_author_path) { Decidim::UserPresenter.new(emendation.creator_author).profile_path }
@@ -8,7 +8,6 @@ shared_examples "amendment rejected event" do
8
8
 
9
9
  it_behaves_like "a simple event"
10
10
 
11
- let(:amendable_title) { amendable.title }
12
11
  let(:emendation_author_nickname) { "@#{emendation.creator_author.nickname}" }
13
12
  let(:emendation_path) { Decidim::ResourceLocatorPresenter.new(emendation).path }
14
13
  let(:emendation_author_path) { Decidim::UserPresenter.new(emendation.creator_author).profile_path }
@@ -132,6 +132,33 @@ shared_examples "comments" do
132
132
  end
133
133
  end
134
134
  end
135
+
136
+ it "let the emoji button works properly when there are not too much characters" do
137
+ if component.present?
138
+ component.update!(settings: { comments_max_length: 100 })
139
+ visit current_path
140
+
141
+ within ".add-comment form" do
142
+ find(:css, "textarea:enabled").set("toto")
143
+ expect(page).not_to have_selector(".emoji-picker__wrapper")
144
+ find("svg").click
145
+ end
146
+ expect(page).to have_selector(".emoji-picker__wrapper")
147
+ end
148
+ end
149
+
150
+ it "deactivate the emoji button when there are less than 4 characters left" do
151
+ if component.present?
152
+ component.update!(settings: { comments_max_length: 30 })
153
+ visit current_path
154
+
155
+ within ".add-comment form" do
156
+ find(:css, "textarea:enabled").set("0123456789012345678901234567")
157
+ find("svg").click
158
+ expect(page).not_to have_selector(".emoji-picker__wrapper")
159
+ end
160
+ end
161
+ end
135
162
  end
136
163
  end
137
164
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ shared_examples_for "conversation field with maximum length" do |field|
4
+ describe "character counter" do
5
+ let(:message) { "#{::Faker::Lorem.paragraph}\n#{::Faker::Lorem.paragraph}" }
6
+ let(:max_length) { Decidim.config.maximum_conversation_message_length }
7
+
8
+ before do
9
+ allow(Decidim.config).to receive(
10
+ :maximum_conversation_message_length
11
+ ).and_return(max_length)
12
+ end
13
+
14
+ it "shows character counter" do
15
+ fill_in field, with: message
16
+ expect(page).to have_content("#{max_length - message.length} characters left")
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ shared_examples_for "endorsable" do
6
+ context "when endorsable" do
7
+ let(:user) { create(:user, organization: subject.organization) }
8
+
9
+ describe "#endorsed_by?" do
10
+ context "with User endorsement" do
11
+ it "returns false if the resource is not endorsed by the given user" do
12
+ expect(subject).not_to be_endorsed_by(user)
13
+ end
14
+
15
+ it "returns true if the resource is endorsed by the given user" do
16
+ create(:endorsement, resource: subject, author: user)
17
+ expect(subject).to be_endorsed_by(user)
18
+ end
19
+ end
20
+
21
+ context "with Organization endorsement" do
22
+ let!(:user_group) { create(:user_group, verified_at: Time.current, organization: user.organization) }
23
+ let!(:membership) { create(:user_group_membership, user: user, user_group: user_group) }
24
+
25
+ before { user_group.reload }
26
+
27
+ it "returns false if the resource is not endorsed by the given organization" do
28
+ expect(subject).not_to be_endorsed_by(user, user_group)
29
+ end
30
+
31
+ context "when there's an endorsement" do
32
+ let!(:endorsement) { create(:endorsement, resource: subject, author: user, user_group: user_group) }
33
+
34
+ before { user_group.reload }
35
+
36
+ it "returns false if the resource is not endorsed by the given user" do
37
+ expect(subject).not_to be_endorsed_by(user)
38
+ end
39
+
40
+ it "returns true if the resource is endorsed by the given organization" do
41
+ expect(subject).to be_endorsed_by(user, user_group)
42
+ end
43
+
44
+ context "with another user" do
45
+ let!(:another_user) { create(:user, :confirmed, organization: user.organization) }
46
+ let!(:another_membership) { create(:user_group_membership, user: another_user, user_group: user_group, role: "admin") }
47
+
48
+ before { user_group.reload }
49
+
50
+ it "returns true if the resource is endorsed by other user of the same organization" do
51
+ expect(subject).to be_endorsed_by(another_user, user_group)
52
+ end
53
+ end
54
+
55
+ context "with another organization" do
56
+ let!(:another_user_group) { create(:user_group, verified_at: Time.current, organization: user.organization) }
57
+ let!(:another_membership) { create(:user_group_membership, user: user, user_group: another_user_group) }
58
+
59
+ before { another_user_group.reload }
60
+
61
+ it "returns false if the resource is not endorsed by another organization of the same user" do
62
+ expect(subject).not_to be_endorsed_by(user)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -37,6 +37,40 @@ shared_examples "searchable results" do
37
37
  end
38
38
  end
39
39
 
40
+ context "when moderation is involved" do
41
+ it "not contains these searchables" do
42
+ expect(searchables).not_to be_empty
43
+ expect(term).not_to be_empty
44
+
45
+ fill_in "term", with: term
46
+ find("input#term").native.send_keys :enter
47
+
48
+ expect(page).to have_current_path decidim.search_path, ignore_query: true
49
+ expect(page).to have_content(/results for the search: "#{term}"/i)
50
+ expect(page).to have_selector(".filters__section")
51
+ expect(page.find("#search-count .section-heading").text.to_i).to be_positive
52
+
53
+ searchables.each do |searchable|
54
+ next unless searchable.is_a?(Decidim::Reportable)
55
+
56
+ create(:moderation, reportable: searchable, hidden_at: Time.current)
57
+ # rubocop:disable Rails/SkipsModelValidations
58
+ searchable.reload.touch
59
+ # rubocop:enable Rails/SkipsModelValidations
60
+ end
61
+
62
+ visit decidim.root_path
63
+
64
+ fill_in "term", with: term
65
+ find("input#term").native.send_keys :enter
66
+
67
+ expect(page).to have_current_path decidim.search_path, ignore_query: true
68
+ expect(page).to have_content(/results for the search: "#{term}"/i)
69
+ expect(page).to have_selector(".filters__section")
70
+ expect(page.find("#search-count .section-heading").text.to_i).not_to be_positive
71
+ end
72
+ end
73
+
40
74
  context "when participatory space is not visible" do
41
75
  shared_examples_for "no searchs found" do
42
76
  it "not contains these searchables" do
@@ -4,6 +4,7 @@ require "decidim/core/test/shared_examples/acts_as_author_examples"
4
4
  require "decidim/core/test/shared_examples/admin_log_presenter_examples"
5
5
  require "decidim/core/test/shared_examples/authorable"
6
6
  require "decidim/core/test/shared_examples/coauthorable"
7
+ require "decidim/core/test/shared_examples/endorsable"
7
8
  require "decidim/core/test/shared_examples/publicable"
8
9
  require "decidim/core/test/shared_examples/localised_email"
9
10
  require "decidim/core/test/shared_examples/logo_email"
@@ -69,3 +70,4 @@ require "decidim/core/test/shared_examples/share_link_examples"
69
70
  require "decidim/core/test/shared_examples/categories_container_examples"
70
71
  require "decidim/core/test/shared_examples/assembly_announcements_examples"
71
72
  require "decidim/core/test/shared_examples/translated_event_examples"
73
+ require "decidim/core/test/shared_examples/conversations_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.26.0.rc1"
7
+ "0.26.1"
8
8
  end
9
9
  end
10
10
  end
@@ -18,7 +18,11 @@ module Decidim
18
18
  #
19
19
  # Returns Boolean.
20
20
  def endorsed_by?(user, user_group = nil)
21
- endorsements.where(author: user, user_group: user_group).any?
21
+ if user_group
22
+ endorsements.where(user_group: user_group).any?
23
+ else
24
+ endorsements.where(author: user, user_group: nil).any?
25
+ end
22
26
  end
23
27
  end
24
28
  end
@@ -57,11 +57,7 @@ module Decidim
57
57
  @template.snippets.add(:head, @template.snippets.for(:geocoding))
58
58
  end
59
59
 
60
- options[:value] ||= object.send(attribute) if object.respond_to?(attribute)
61
- if object.respond_to?(:latitude) && object.respond_to?(:longitude)
62
- point = [object.latitude, object.longitude]
63
- options["data-coordinates"] ||= point.join(",")
64
- end
60
+ options = merge_geocoding_options(attribute, options)
65
61
 
66
62
  field(attribute, options) do |opts|
67
63
  builder.geocoding_field(
@@ -71,6 +67,17 @@ module Decidim
71
67
  )
72
68
  end
73
69
  end
70
+
71
+ private
72
+
73
+ def merge_geocoding_options(attribute, options)
74
+ options[:value] ||= object.send(attribute) if object.respond_to?(attribute)
75
+ if object.respond_to?(:latitude) && object.respond_to?(:longitude) && object.latitude.present? && object.longitude.present?
76
+ point = [object.latitude, object.longitude]
77
+ options["data-coordinates"] ||= point.join(",")
78
+ end
79
+ options
80
+ end
74
81
  end
75
82
  end
76
83
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This is a backport for Rails 6.0 for the Same-Site option for the cookies
4
+ # because this option was added in Rails 6.1.
5
+ #
6
+ # See:
7
+ # https://github.com/decidim/decidim/pull/9051
8
+ # https://github.com/rails/rails/issues/35822
9
+ # https://github.com/rails/rails/commit/7ccaa125ba396d418aad1b217b63653d06044680
10
+ module ActionDispatch
11
+ class Cookies
12
+ class CookieJar #:nodoc:
13
+ alias handle_options_original handle_options
14
+
15
+ def handle_options(options)
16
+ handle_options_original(options)
17
+
18
+ options[:same_site] ||= Rails.application.config.action_dispatch.cookies_same_site_protection || :lax
19
+ options[:same_site] = false if options[:same_site] == :none
20
+ end
21
+ end
22
+ end
23
+ end
@@ -51,6 +51,7 @@ module Decidim
51
51
 
52
52
  scope = manifest.resource_scope(component)
53
53
  scope = scope.where("#{self.class.table_name}.id != ?", id) if manifest.model_class == self.class
54
+ scope = scope.not_hidden if manifest.model_class.respond_to?(:not_hidden)
54
55
  scope.includes(:component).where.not(decidim_components: { published_at: nil })
55
56
  end
56
57
 
@@ -54,11 +54,12 @@ module Decidim
54
54
  end
55
55
  end
56
56
 
57
+ after_touch do |searchable|
58
+ remove_from_index(searchable) if searchable.respond_to?(:hidden?) && searchable.hidden?
59
+ end
60
+
57
61
  after_destroy do |searchable|
58
- if self.class.search_resource_fields_mapper
59
- org = self.class.search_resource_fields_mapper.retrieve_organization(searchable)
60
- searchable.searchable_resources.by_organization(org.id).destroy_all
61
- end
62
+ remove_from_index(searchable) if self.class.search_resource_fields_mapper
62
63
  end
63
64
  # after_create and after_update callbacks are dynamically setted in `searchable_fields` method.
64
65
 
@@ -70,6 +71,11 @@ module Decidim
70
71
  add_to_index_as_search_resource
71
72
  end
72
73
 
74
+ def remove_from_index(searchable)
75
+ org = self.class.search_resource_fields_mapper.retrieve_organization(searchable)
76
+ searchable.searchable_resources.by_organization(org.id).destroy_all
77
+ end
78
+
73
79
  # Forces the model to be indexed for the first time.
74
80
  def add_to_index_as_search_resource
75
81
  fields = self.class.search_resource_fields_mapper.mapped(self)
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module SocialShare
5
+ # This class represents an abstract social network.
6
+ class Service
7
+ include Decidim::AttributeObject::Model
8
+ include ActiveModel::Validations
9
+
10
+ # The name of the social networking service.
11
+ attribute :name, String
12
+
13
+ # The icon of the social networking service.
14
+ attribute :icon, String
15
+
16
+ # The share_url of the social networking service.
17
+ attribute :share_uri, String
18
+
19
+ # TODO validate #{url} in share_url
20
+
21
+ def formatted_share_uri(title, **args)
22
+ format(share_uri, title: title, **args)
23
+ end
24
+
25
+ # Public: Returns an image of the icon for this social network.
26
+ #
27
+ # Returns a String with the icon.
28
+ def icon_path
29
+ ActionController::Base.helpers.asset_pack_path("media/images/#{icon}")
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module SocialShare
5
+ # This class represents a repository of social networking services. New services can be
6
+ # registered thanks to its DSL and will be validated prior to being
7
+ # inserted.
8
+ #
9
+ class ServiceRegistry
10
+ # Public: Initializes the social networking service.
11
+ def initialize
12
+ @services = {}
13
+ end
14
+
15
+ # Public: Returns all the registered social networking services.
16
+ #
17
+ # Returns Array<Service>.
18
+ def all
19
+ @services.values
20
+ end
21
+
22
+ # Public: Finds a social networking services given its name.
23
+ #
24
+ # name - The name of the service to find.
25
+ #
26
+ # Returns a Service if found or nil otherwise.
27
+ def find(name)
28
+ @services[name.to_s]
29
+ end
30
+
31
+ # Public: Registers a new social networking service.
32
+ #
33
+ # name - The name of the social network to register.
34
+ # &block - A block that gets the new social network as argument.
35
+ #
36
+ # Example:
37
+ # register(:fake){ |service| service.image = "twitter.svg" }
38
+ #
39
+ # Returns a Service when registered successfully, raises an exception
40
+ # otherwise.
41
+ def register(name, &block)
42
+ name = name.to_s
43
+
44
+ service = Service.new(name: name).tap do |object|
45
+ object.instance_eval(&block)
46
+ end
47
+
48
+ service.validate!
49
+
50
+ @services[name] = service
51
+ end
52
+
53
+ # Public: Unregisters a previously registered social network.
54
+ #
55
+ # name - The name of the social network to unregister.
56
+ #
57
+ # Returns the deleted Service if found, nil otherwise.
58
+ def unregister(name)
59
+ @services.delete(name.to_s)
60
+ end
61
+ end
62
+ end
63
+ end