decidim-admin 0.31.8 → 0.32.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -12
  3. data/app/cells/decidim/admin/attachments_privacy_warning/show.erb +1 -1
  4. data/app/cells/decidim/admin/attachments_privacy_warning_cell.rb +2 -6
  5. data/app/commands/decidim/admin/content_blocks/reorder_content_blocks.rb +1 -1
  6. data/app/commands/decidim/admin/create_newsletter.rb +1 -1
  7. data/app/commands/decidim/admin/create_taxonomy_filter.rb +2 -16
  8. data/app/commands/decidim/admin/participatory_space/create_member.rb +99 -0
  9. data/app/commands/decidim/admin/participatory_space/destroy_member.rb +30 -0
  10. data/app/commands/decidim/admin/participatory_space/import_member_csv.rb +46 -0
  11. data/app/commands/decidim/admin/participatory_space/publish_all_members.rb +52 -0
  12. data/app/commands/decidim/admin/participatory_space/unpublish_all_members.rb +52 -0
  13. data/app/commands/decidim/admin/participatory_space/update_member.rb +13 -0
  14. data/app/commands/decidim/admin/reorder_components.rb +1 -1
  15. data/app/commands/decidim/admin/reorder_taxonomies.rb +1 -1
  16. data/app/commands/decidim/admin/update_organization.rb +1 -1
  17. data/app/commands/decidim/admin/update_taxonomy_filter.rb +6 -24
  18. data/app/controllers/concerns/decidim/admin/component_taxonomies_helper.rb +1 -1
  19. data/app/controllers/concerns/decidim/admin/content_blocks/landing_page.rb +6 -5
  20. data/app/controllers/concerns/decidim/admin/content_blocks/landing_page_content_blocks.rb +3 -2
  21. data/app/controllers/concerns/decidim/admin/filterable.rb +4 -4
  22. data/app/controllers/concerns/decidim/admin/global_moderation_context.rb +0 -2
  23. data/app/controllers/concerns/decidim/admin/participatory_space_admin_context.rb +1 -2
  24. data/app/controllers/concerns/decidim/admin/participatory_space_export.rb +1 -3
  25. data/app/controllers/decidim/admin/admin_terms_controller.rb +0 -1
  26. data/app/controllers/decidim/admin/application_controller.rb +0 -2
  27. data/app/controllers/decidim/admin/area_types_controller.rb +2 -2
  28. data/app/controllers/decidim/admin/areas_controller.rb +3 -2
  29. data/app/controllers/decidim/admin/block_user_controller.rb +2 -6
  30. data/app/controllers/decidim/admin/component_permissions_controller.rb +1 -1
  31. data/app/controllers/decidim/admin/components/base_controller.rb +3 -4
  32. data/app/controllers/decidim/admin/components_controller.rb +5 -4
  33. data/app/controllers/decidim/admin/concerns/has_attachment_collections.rb +2 -4
  34. data/app/controllers/decidim/admin/concerns/has_attachments.rb +2 -4
  35. data/app/controllers/decidim/admin/concerns/has_breadcrumb_items.rb +0 -4
  36. data/app/controllers/decidim/admin/conflicts_controller.rb +6 -6
  37. data/app/controllers/decidim/admin/exports_controller.rb +1 -1
  38. data/app/controllers/decidim/admin/global_moderations/reports_controller.rb +0 -2
  39. data/app/controllers/decidim/admin/global_moderations_controller.rb +0 -2
  40. data/app/controllers/decidim/admin/help_sections_controller.rb +3 -3
  41. data/app/controllers/decidim/admin/impersonations_controller.rb +2 -3
  42. data/app/controllers/decidim/admin/imports_controller.rb +2 -1
  43. data/app/controllers/decidim/admin/managed_users/impersonation_logs_controller.rb +1 -1
  44. data/app/controllers/decidim/admin/managed_users/promotions_controller.rb +1 -1
  45. data/app/controllers/decidim/admin/moderated_users_controller.rb +0 -3
  46. data/app/controllers/decidim/admin/moderations_controller.rb +0 -4
  47. data/app/controllers/decidim/admin/newsletter_templates_controller.rb +1 -4
  48. data/app/controllers/decidim/admin/newsletters_controller.rb +9 -12
  49. data/app/controllers/decidim/admin/officializations_controller.rb +0 -2
  50. data/app/controllers/decidim/admin/organization_appearance_controller.rb +1 -1
  51. data/app/controllers/decidim/admin/organization_controller.rb +30 -1
  52. data/app/controllers/decidim/admin/organization_external_domain_allowlist_controller.rb +1 -1
  53. data/app/controllers/decidim/admin/organization_homepage_controller.rb +3 -7
  54. data/app/controllers/decidim/admin/participatory_space/concerns/has_members.rb +166 -0
  55. data/app/controllers/decidim/admin/participatory_space/concerns/has_members_csv_import.rb +67 -0
  56. data/app/controllers/decidim/admin/participatory_space/concerns/members_filterable.rb +36 -0
  57. data/app/controllers/decidim/admin/participatory_space/user_role_controller.rb +2 -2
  58. data/app/controllers/decidim/admin/reminders_controller.rb +1 -1
  59. data/app/controllers/decidim/admin/resource_permissions_controller.rb +1 -5
  60. data/app/controllers/decidim/admin/scope_types_controller.rb +2 -2
  61. data/app/controllers/decidim/admin/scopes_controller.rb +3 -2
  62. data/app/controllers/decidim/admin/share_tokens_controller.rb +2 -2
  63. data/app/controllers/decidim/admin/space_publications_controller.rb +2 -2
  64. data/app/controllers/decidim/admin/static_page_topics_controller.rb +2 -2
  65. data/app/controllers/decidim/admin/static_pages_controller.rb +27 -29
  66. data/app/controllers/decidim/admin/taxonomies_controller.rb +3 -3
  67. data/app/controllers/decidim/admin/taxonomy_filters_controller.rb +5 -5
  68. data/app/controllers/decidim/admin/taxonomy_filters_selector_controller.rb +1 -1
  69. data/app/controllers/decidim/admin/taxonomy_items_controller.rb +3 -3
  70. data/app/controllers/decidim/admin/users_controller.rb +1 -1
  71. data/app/forms/decidim/admin/content_block_form.rb +28 -1
  72. data/app/forms/decidim/admin/organization_form.rb +0 -1
  73. data/app/forms/decidim/admin/participatory_space/member_csv_import_form.rb +33 -0
  74. data/app/forms/decidim/admin/participatory_space/member_form.rb +26 -0
  75. data/app/forms/decidim/admin/participatory_space_admin_user_form.rb +1 -1
  76. data/app/forms/decidim/admin/selective_newsletter_form.rb +7 -7
  77. data/app/forms/decidim/admin/taxonomy_filter_form.rb +10 -11
  78. data/app/helpers/decidim/admin/newsletters_helper.rb +8 -5
  79. data/app/helpers/decidim/admin/search_form_helper.rb +17 -0
  80. data/app/helpers/decidim/admin/settings_helper.rb +2 -0
  81. data/app/jobs/decidim/admin/newsletter_job.rb +2 -1
  82. data/app/jobs/decidim/admin/participatory_space/destroy_members_follows_job.rb +36 -0
  83. data/app/jobs/decidim/admin/participatory_space/import_member_csv_job.rb +29 -0
  84. data/app/packs/src/decidim/admin/admin_autocomplete.js +10 -16
  85. data/app/packs/src/decidim/admin/controllers/access_mode/access_mode.test.js +68 -0
  86. data/app/packs/src/decidim/admin/controllers/access_mode/controller.js +52 -0
  87. data/app/packs/src/decidim/admin/newsletters.js +2 -2
  88. data/app/packs/stylesheets/decidim/admin/_datepicker.scss +0 -4
  89. data/app/packs/stylesheets/decidim/admin/_sidebar-menu.scss +1 -3
  90. data/app/packs/stylesheets/decidim/admin/application.scss +1 -1
  91. data/app/permissions/decidim/admin/permissions.rb +0 -16
  92. data/app/queries/decidim/admin/newsletter_recipients.rb +8 -8
  93. data/app/views/decidim/admin/admin_terms/show.html.erb +3 -1
  94. data/app/views/decidim/admin/area_types/index.html.erb +3 -2
  95. data/app/views/decidim/admin/areas/index.html.erb +3 -2
  96. data/app/views/decidim/admin/attachment_collections/edit.html.erb +2 -2
  97. data/app/views/decidim/admin/attachment_collections/index.html.erb +3 -3
  98. data/app/views/decidim/admin/attachment_collections/new.html.erb +2 -2
  99. data/app/views/decidim/admin/attachments/index.html.erb +3 -2
  100. data/app/views/decidim/admin/authorization_workflows/index.html.erb +2 -2
  101. data/app/views/decidim/admin/block_user/new.html.erb +1 -0
  102. data/app/views/decidim/admin/components/_actions.html.erb +1 -1
  103. data/app/views/decidim/admin/components/index.html.erb +2 -2
  104. data/app/views/decidim/admin/components/manage_trash.html.erb +2 -2
  105. data/app/views/decidim/admin/conflicts/edit.html.erb +3 -1
  106. data/app/views/decidim/admin/conflicts/index.html.erb +3 -2
  107. data/app/views/decidim/admin/dashboard/show.html.erb +4 -4
  108. data/app/views/decidim/admin/help_sections/update.html.erb +2 -0
  109. data/app/views/decidim/admin/impersonatable_users/index.html.erb +3 -2
  110. data/app/views/decidim/admin/impersonations/_form.html.erb +2 -2
  111. data/app/views/decidim/admin/impersonations/new.html.erb +3 -2
  112. data/app/views/decidim/admin/imports/new.html.erb +1 -0
  113. data/app/views/decidim/admin/logs/_logs_list.html.erb +1 -1
  114. data/app/views/decidim/admin/logs/index.html.erb +1 -1
  115. data/app/views/decidim/admin/managed_users/impersonation_logs/index.html.erb +6 -5
  116. data/app/views/decidim/admin/managed_users/promotions/new.html.erb +3 -2
  117. data/app/views/decidim/admin/{participatory_space_private_users → members}/edit.html.erb +2 -2
  118. data/app/views/decidim/admin/{participatory_space_private_users → members}/index.html.erb +25 -25
  119. data/app/views/decidim/admin/{participatory_space_private_users → members}/new.html.erb +2 -2
  120. data/app/views/decidim/admin/{participatory_space_private_users_csv_imports → members_csv_imports}/new.html.erb +2 -2
  121. data/app/views/decidim/admin/moderations/_moderation-tr.html.erb +3 -7
  122. data/app/views/decidim/admin/moderations/_moderations-thead.html.erb +0 -1
  123. data/app/views/decidim/admin/moderations/reports/index.html.erb +1 -1
  124. data/app/views/decidim/admin/newsletter_templates/show.html.erb +3 -2
  125. data/app/views/decidim/admin/newsletters/confirm_recipients.html.erb +4 -6
  126. data/app/views/decidim/admin/newsletters/index.html.erb +2 -2
  127. data/app/views/decidim/admin/newsletters/select_recipients_to_deliver.html.erb +2 -2
  128. data/app/views/decidim/admin/newsletters/show.html.erb +3 -2
  129. data/app/views/decidim/admin/officializations/index.html.erb +6 -4
  130. data/app/views/decidim/admin/officializations/show_email.html.erb +3 -7
  131. data/app/views/decidim/admin/organization/form/_extra_features.html.erb +2 -22
  132. data/app/views/decidim/admin/organization_external_domain_allowlist/_form.html.erb +2 -2
  133. data/app/views/decidim/admin/organization_external_domain_allowlist/edit.html.erb +2 -2
  134. data/app/views/decidim/admin/reminders/new.html.erb +8 -1
  135. data/app/views/decidim/admin/resource_permissions/edit.html.erb +2 -2
  136. data/app/views/decidim/admin/scope_types/index.html.erb +3 -2
  137. data/app/views/decidim/admin/scopes/index.html.erb +2 -2
  138. data/app/views/decidim/admin/share_tokens/index.html.erb +2 -0
  139. data/app/views/decidim/admin/shared/_gallery.html.erb +15 -0
  140. data/app/views/decidim/admin/shared/landing_page/_content_blocks.html.erb +0 -2
  141. data/app/views/decidim/admin/shared/landing_page/edit.html.erb +2 -0
  142. data/app/views/decidim/admin/shared/landing_page_content_blocks/edit.html.erb +1 -0
  143. data/app/views/decidim/admin/static_page_topics/index.html.erb +2 -2
  144. data/app/views/decidim/admin/static_pages/edit.html.erb +2 -1
  145. data/app/views/decidim/admin/static_pages/index.html.erb +2 -2
  146. data/app/views/decidim/admin/statistics/_statistics.html.erb +1 -1
  147. data/app/views/decidim/admin/statistics/index.html.erb +2 -0
  148. data/app/views/decidim/admin/taxonomies/_row.html.erb +1 -1
  149. data/app/views/decidim/admin/taxonomies/_row_children.html.erb +1 -1
  150. data/app/views/decidim/admin/taxonomies/_table.html.erb +1 -1
  151. data/app/views/decidim/admin/taxonomies/edit.html.erb +2 -2
  152. data/app/views/decidim/admin/taxonomies/index.html.erb +4 -4
  153. data/app/views/decidim/admin/taxonomy_filters/_table.html.erb +2 -2
  154. data/app/views/decidim/admin/taxonomy_filters/index.html.erb +2 -2
  155. data/app/views/decidim/admin/taxonomy_filters_selector/index.html.erb +2 -0
  156. data/app/views/decidim/admin/taxonomy_filters_selector/new.html.erb +2 -0
  157. data/app/views/decidim/admin/taxonomy_filters_selector/show.html.erb +2 -0
  158. data/app/views/decidim/admin/taxonomy_items/edit.html.erb +2 -0
  159. data/app/views/decidim/admin/taxonomy_items/new.html.erb +2 -0
  160. data/app/views/decidim/admin/users/index.html.erb +3 -2
  161. data/app/views/layouts/decidim/admin/_application.html.erb +1 -1
  162. data/config/locales/ar.yml +9 -49
  163. data/config/locales/bg.yml +9 -57
  164. data/config/locales/bs-BA.yml +4 -24
  165. data/config/locales/ca-IT.yml +130 -117
  166. data/config/locales/ca.yml +130 -117
  167. data/config/locales/cs.yml +126 -116
  168. data/config/locales/de.yml +114 -123
  169. data/config/locales/el.yml +17 -45
  170. data/config/locales/en.yml +130 -116
  171. data/config/locales/eo.yml +0 -7
  172. data/config/locales/es-MX.yml +130 -117
  173. data/config/locales/es-PY.yml +130 -117
  174. data/config/locales/es.yml +130 -117
  175. data/config/locales/eu.yml +189 -176
  176. data/config/locales/fi-plain.yml +129 -116
  177. data/config/locales/fi.yml +129 -116
  178. data/config/locales/fr-CA.yml +91 -149
  179. data/config/locales/fr.yml +91 -149
  180. data/config/locales/ga-IE.yml +0 -13
  181. data/config/locales/gl.yml +57 -649
  182. data/config/locales/he-IL.yml +1 -5
  183. data/config/locales/hu.yml +10 -52
  184. data/config/locales/id-ID.yml +4 -34
  185. data/config/locales/is-IS.yml +1 -16
  186. data/config/locales/it.yml +8 -69
  187. data/config/locales/ja.yml +129 -118
  188. data/config/locales/kaa.yml +4 -16
  189. data/config/locales/ko.yml +6 -40
  190. data/config/locales/lb.yml +9 -41
  191. data/config/locales/lt.yml +10 -52
  192. data/config/locales/lv.yml +8 -38
  193. data/config/locales/nl.yml +10 -46
  194. data/config/locales/no.yml +10 -46
  195. data/config/locales/pl.yml +9 -54
  196. data/config/locales/pt-BR.yml +121 -104
  197. data/config/locales/pt.yml +9 -42
  198. data/config/locales/ro-RO.yml +15 -51
  199. data/config/locales/ru.yml +4 -35
  200. data/config/locales/sk.yml +8 -814
  201. data/config/locales/sl.yml +1 -7
  202. data/config/locales/sq-AL.yml +1 -21
  203. data/config/locales/sr-CS.yml +4 -24
  204. data/config/locales/sv.yml +80 -121
  205. data/config/locales/th-TH.yml +0 -8
  206. data/config/locales/tr-TR.yml +33 -93
  207. data/config/locales/uk.yml +0 -26
  208. data/config/locales/val-ES.yml +0 -1
  209. data/config/locales/zh-CN.yml +9 -38
  210. data/config/locales/zh-TW.yml +10 -52
  211. data/config/routes.rb +4 -0
  212. data/decidim-admin.gemspec +7 -10
  213. data/lib/decidim/admin/engine.rb +5 -2
  214. data/lib/decidim/admin/form_builder.rb +3 -1
  215. data/lib/decidim/admin/menu.rb +1 -1
  216. data/lib/decidim/admin/test/admin_members_shared_examples.rb +119 -0
  217. data/lib/decidim/admin/test/admin_participatory_space_access_examples.rb +3 -3
  218. data/lib/decidim/admin/test/filterable_examples.rb +20 -11
  219. data/lib/decidim/admin/test/invite_participatory_space_admins_shared_examples.rb +17 -17
  220. data/lib/decidim/admin/test/invite_participatory_space_collaborators_shared_examples.rb +1 -1
  221. data/lib/decidim/admin/test/invite_participatory_space_moderators_shared_examples.rb +1 -1
  222. data/lib/decidim/admin/test/invite_participatory_space_users_shared_context.rb +1 -1
  223. data/lib/decidim/admin/test/manage_attachment_collections_examples.rb +3 -3
  224. data/lib/decidim/admin/test/manage_attachments_examples.rb +6 -6
  225. data/lib/decidim/admin/test/manage_component_permissions_examples.rb +5 -5
  226. data/lib/decidim/admin/test/manage_moderations_examples.rb +6 -6
  227. data/lib/decidim/admin/test/manage_participatory_space_publications_examples.rb +2 -2
  228. data/lib/decidim/admin/test/manage_resource_soft_deletion_examples.rb +3 -3
  229. data/lib/decidim/admin/version.rb +1 -1
  230. data/lib/decidim/admin.rb +0 -2
  231. metadata +40 -36
  232. data/app/commands/decidim/admin/create_participatory_space_private_user.rb +0 -98
  233. data/app/commands/decidim/admin/destroy_participatory_space_private_user.rb +0 -28
  234. data/app/commands/decidim/admin/process_participatory_space_private_user_import_csv.rb +0 -44
  235. data/app/commands/decidim/admin/publish_all_participatory_space_private_users.rb +0 -50
  236. data/app/commands/decidim/admin/unpublish_all_participatory_space_private_users.rb +0 -50
  237. data/app/commands/decidim/admin/update_participatory_space_private_user.rb +0 -11
  238. data/app/controllers/concerns/decidim/participatory_space_private_users/admin/filterable.rb +0 -34
  239. data/app/controllers/decidim/admin/concerns/has_private_users.rb +0 -173
  240. data/app/controllers/decidim/admin/concerns/has_private_users_csv_import.rb +0 -67
  241. data/app/forms/decidim/admin/participatory_space_private_user_csv_import_form.rb +0 -31
  242. data/app/forms/decidim/admin/participatory_space_private_user_form.rb +0 -24
  243. data/app/jobs/decidim/admin/destroy_private_users_follows_job.rb +0 -37
  244. data/app/jobs/decidim/admin/import_participatory_space_private_user_csv_job.rb +0 -27
  245. data/app/views/decidim/admin/devise/mailers/password_change.html.erb +0 -3
  246. data/app/views/decidim/admin/devise/mailers/reset_password_instructions.html.erb +0 -8
  247. data/app/views/decidim/admin/shared/_attachments.html.erb +0 -15
  248. /data/app/views/decidim/admin/{participatory_space_private_users → members}/_form.html.erb +0 -0
@@ -20,7 +20,6 @@ module Decidim
20
20
  attribute :rich_text_editor_in_public_views, Boolean
21
21
  attribute :enable_machine_translations, Boolean
22
22
  attribute :machine_translation_display_priority, String
23
- attribute :enable_participatory_space_filters, Boolean
24
23
 
25
24
  attribute :twitter_handler, String
26
25
  attribute :facebook_handler, String
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "csv"
4
+
5
+ module Decidim
6
+ module Admin
7
+ module ParticipatorySpace
8
+ # A form object used to upload CSV to batch members.
9
+ #
10
+ class MemberCsvImportForm < Form
11
+ include Decidim::HasUploadValidations
12
+ include Decidim::Admin::CustomImport
13
+
14
+ attribute :file, Decidim::Attributes::Blob
15
+ attribute :user_name, String
16
+ attribute :email, String
17
+
18
+ validates :file, presence: true, file_content_type: { allow: ["text/csv"] }
19
+ validate :validate_csv
20
+
21
+ def validate_csv
22
+ return if file.blank?
23
+
24
+ process_import_file(file) do |(_email, user_name)|
25
+ errors.add(:user_name, :invalid) if user_name.blank? || !user_name.match?(UserBaseEntity::REGEXP_NAME)
26
+ end
27
+ rescue CSV::MalformedCSVError
28
+ errors.add(:file, :malformed)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ module ParticipatorySpace
6
+ # A form object used to create members from the
7
+ # admin dashboard.
8
+ #
9
+ class MemberForm < Form
10
+ include TranslatableAttributes
11
+
12
+ mimic :member
13
+
14
+ attribute :name, String
15
+ attribute :email, String
16
+ attribute :published, Boolean
17
+
18
+ translatable_attribute :role, String
19
+
20
+ validates :name, :email, presence: true
21
+
22
+ validates :name, format: { with: UserBaseEntity::REGEXP_NAME }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module Admin
5
- class ParticipatorySpaceAdminUserForm < ParticipatorySpacePrivateUserForm
5
+ class ParticipatorySpaceAdminUserForm < Decidim::Admin::ParticipatorySpace::MemberForm
6
6
  attribute :role, String
7
7
 
8
8
  validates :role, presence: true
@@ -12,13 +12,13 @@ module Decidim
12
12
  attribute :send_to_verified_users, Boolean
13
13
  attribute :send_to_participants, Boolean
14
14
  attribute :send_to_followers, Boolean
15
- attribute :send_to_private_members, Boolean
15
+ attribute :send_to_members, Boolean
16
16
 
17
17
  validates :send_to_all_users, presence: true, unless: :other_groups_selected_for_all_users?
18
18
  validates :send_to_verified_users, presence: true, unless: :other_groups_selected_for_verified_users?
19
19
  validates :send_to_followers, presence: true, if: :only_followers_selected?
20
20
  validates :send_to_participants, presence: true, if: :only_participants_selected?
21
- validates :send_to_private_members, presence: true, if: :only_private_members_selected?
21
+ validates :send_to_members, presence: true, if: :only_members_selected?
22
22
 
23
23
  validate :at_least_one_participatory_space_selected
24
24
 
@@ -49,31 +49,31 @@ module Decidim
49
49
  send_to_verified_users.present? ||
50
50
  send_to_participants.present? ||
51
51
  send_to_followers.present? ||
52
- send_to_private_members.present?
52
+ send_to_members.present?
53
53
  end
54
54
 
55
55
  def other_groups_selected_for_verified_users?
56
56
  send_to_all_users.present? ||
57
57
  send_to_participants.present? ||
58
58
  send_to_followers.present? ||
59
- send_to_private_members.present?
59
+ send_to_members.present?
60
60
  end
61
61
 
62
62
  def only_followers_selected?
63
63
  send_to_all_users.blank? &&
64
64
  send_to_participants.blank? &&
65
- send_to_private_members.blank? &&
65
+ send_to_members.blank? &&
66
66
  send_to_verified_users.blank?
67
67
  end
68
68
 
69
69
  def only_participants_selected?
70
70
  send_to_all_users.blank? &&
71
71
  send_to_followers.blank? &&
72
- send_to_private_members.blank? &&
72
+ send_to_members.blank? &&
73
73
  send_to_verified_users.blank?
74
74
  end
75
75
 
76
- def only_private_members_selected?
76
+ def only_members_selected?
77
77
  send_to_all_users.blank? &&
78
78
  send_to_followers.blank? &&
79
79
  send_to_participants.blank? &&
@@ -5,6 +5,7 @@ module Decidim
5
5
  # A form object to create or update areas.
6
6
  class TaxonomyFilterForm < Form
7
7
  include TranslatableAttributes
8
+
8
9
  Item = Struct.new(:name, :value, :children)
9
10
  Manifest = Struct.new(:id, :name)
10
11
 
@@ -43,7 +44,7 @@ module Decidim
43
44
  def items_collection
44
45
  return [] unless root_taxonomy
45
46
 
46
- @items_collection ||= build_items_collection
47
+ @items_collection ||= map_items_collection(root_taxonomy)
47
48
  end
48
49
 
49
50
  def root_taxonomy
@@ -61,24 +62,22 @@ module Decidim
61
62
 
62
63
  private
63
64
 
64
- def build_items_collection
65
- children_by_parent_id = root_taxonomy.all_children.group_by(&:parent_id)
66
- build_subtree(root_taxonomy.id, children_by_parent_id)
67
- end
68
-
69
- def build_subtree(parent_id, children_by_parent_id)
70
- Array(children_by_parent_id[parent_id]).map do |item|
65
+ def map_items_collection(taxonomy)
66
+ taxonomy.children.map do |item|
71
67
  Item.new(
72
68
  name: translated_attribute(item.name),
73
69
  value: item.id,
74
- children: build_subtree(item.id, children_by_parent_id)
70
+ children: map_items_collection(item)
75
71
  )
76
72
  end
77
73
  end
78
74
 
79
75
  def valid_taxonomy_items
80
- valid_ids = root_taxonomy ? root_taxonomy.all_children.pluck(:id).to_set : Set.new
81
- return if taxonomy_items.all? { |item| valid_ids.include?(item.to_i) }
76
+ return if taxonomy_items.all? do |item|
77
+ next unless root_taxonomy
78
+
79
+ root_taxonomy.all_children.map(&:id).include?(item.to_i)
80
+ end
82
81
 
83
82
  errors.add(:taxonomy_items, :invalid)
84
83
  end
@@ -23,10 +23,13 @@ module Decidim
23
23
  return if spaces_user_can_admin[space_type.manifest_name.to_sym].blank?
24
24
 
25
25
  html = ""
26
- form_object.fields_for "participatory_space_types[#{space_type.manifest_name}]", space_type do |ff|
27
- html += participatory_space_title(space_type)
28
- html += ff.hidden_field :manifest_name, value: space_type.manifest_name
29
- html += select_tag_participatory_spaces(space_type.manifest_name, spaces_for_select(space_type.manifest_name.to_sym), ff)
26
+
27
+ form_object.fields_for :participatory_space_types do |f|
28
+ f.fields_for space_type.manifest_name, space_type do |ff|
29
+ html += participatory_space_title(space_type)
30
+ html += ff.hidden_field :manifest_name, value: space_type.manifest_name
31
+ html += select_tag_participatory_spaces(space_type.manifest_name, spaces_for_select(space_type.manifest_name.to_sym), ff)
32
+ end
30
33
  end
31
34
  html.html_safe
32
35
  end
@@ -80,7 +83,7 @@ module Decidim
80
83
  recipients << content_tag(:strong, t("index.verified_users", scope: "decidim.admin.newsletters")) if newsletter.sent_to_verified_users?
81
84
  recipients << content_tag(:strong, t("index.followers", scope: "decidim.admin.newsletters")) if newsletter.sent_to_followers?
82
85
  recipients << content_tag(:strong, t("index.participants", scope: "decidim.admin.newsletters")) if newsletter.sent_to_participants?
83
- recipients << content_tag(:strong, t("index.private_members", scope: "decidim.admin.newsletters")) if newsletter.sent_to_private_members?
86
+ recipients << content_tag(:strong, t("index.members", scope: "decidim.admin.newsletters")) if newsletter.sent_to_members?
84
87
 
85
88
  concat recipients.join(t("index.and", scope: "decidim.admin.newsletters")).html_safe
86
89
  end
@@ -11,9 +11,26 @@ module Decidim
11
11
  # as datetime pickers.
12
12
  def search_form_for(record, options = {}, &)
13
13
  options[:builder] ||= SearchFormBuilder
14
+ options[:url] = url_for(params.to_unsafe_h.except("locale", :locale).merge(locale: nil)) if options[:url].blank?
14
15
 
15
16
  super
16
17
  end
18
+
19
+ def sort_link(search_object, attribute, *args, &)
20
+ options = args.extract_options!
21
+ options = options.merge(params: sort_link_params)
22
+ args << options
23
+
24
+ super
25
+ end
26
+
27
+ private
28
+
29
+ def sort_link_params
30
+ request_params = params.to_unsafe_h.except("locale", :locale).merge(locale: nil)
31
+ request_params["q"] = request_params["q"].except("s", :s) if request_params["q"].is_a?(Hash)
32
+ request_params
33
+ end
17
34
  end
18
35
  end
19
36
  end
@@ -161,6 +161,7 @@ module Decidim
161
161
  #
162
162
  # @param attribute [Decidim::SettingsManifest::Attribute]
163
163
  # @return [Symbol] The FormBuilder's method used to render
164
+ # @param [Object] options
164
165
  def form_method_for_attribute(attribute, options)
165
166
  return :editor if attribute.type.to_sym == :text && options[:editor]
166
167
 
@@ -225,6 +226,7 @@ module Decidim
225
226
  #
226
227
  # @param name (see #settings_attribute_input)
227
228
  # @param i18n_scope (see #settings_attribute_input)
229
+ # @param [Object] form
228
230
  def taxonomy_filters(form, name, i18n_scope)
229
231
  return disabled_taxonomy_filters(name, i18n_scope) if @component&.new_record?
230
232
 
@@ -6,6 +6,7 @@ module Decidim
6
6
  #
7
7
  class NewsletterJob < ApplicationJob
8
8
  queue_as :newsletter
9
+ self.enqueue_after_transaction_commit = false
9
10
 
10
11
  def perform(newsletter, form, recipients_ids)
11
12
  @newsletter = newsletter
@@ -36,7 +37,7 @@ module Decidim
36
37
  send_to_verified_users: @form["send_to_verified_users"],
37
38
  send_to_followers: @form["send_to_followers"],
38
39
  send_to_participants: @form["send_to_participants"],
39
- send_to_private_members: @form["send_to_private_members"],
40
+ send_to_members: @form["send_to_members"],
40
41
  participatory_space_types: @form["participatory_space_types"],
41
42
  verification_types: @form["verification_types"]
42
43
  }
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ module ParticipatorySpace
6
+ class DestroyMembersFollowsJob < ApplicationJob
7
+ queue_as :default
8
+
9
+ def perform(decidim_user_id, space)
10
+ return unless space.respond_to?(:restricted?) && space.restricted?
11
+ return if space.respond_to?(:transparent?) && space.transparent?
12
+
13
+ user = Decidim::User.find_by(id: decidim_user_id)
14
+
15
+ return if user.blank?
16
+
17
+ return if space.respond_to?(:can_participate?) && space.can_participate?(user)
18
+
19
+ follows = Decidim::Follow.where(user:)
20
+ follows.where(followable: space).destroy_all
21
+
22
+ destroy_children_follows(follows, space)
23
+ end
24
+
25
+ def destroy_children_follows(follows, space)
26
+ follows.map do |follow|
27
+ object = follow.followable.presence
28
+ next unless object.respond_to?(:decidim_component_id)
29
+
30
+ follow.destroy if space.component_ids.include?(object.decidim_component_id)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ module ParticipatorySpace
6
+ # Custom ApplicationJob scoped to the admin panel.
7
+ #
8
+ class ImportMemberCsvJob < ApplicationJob
9
+ queue_as :exports
10
+
11
+ def perform(email, user_name, participatory_space, current_user)
12
+ return if email.blank? || user_name.blank?
13
+
14
+ params = {
15
+ name: user_name,
16
+ email: email.downcase.strip
17
+ }
18
+ member_form = MemberForm.from_params(params, participatory_space:)
19
+ .with_context(
20
+ current_user:,
21
+ current_participatory_space: participatory_space
22
+ )
23
+
24
+ CreateMember.call(member_form, participatory_space, via_csv: true)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -10,6 +10,7 @@ import AutoComplete from "src/decidim/refactor/moved/autocomplete";
10
10
  * - name: assembly_member[user_id],
11
11
  * - options: [],
12
12
  * - placeholder: "Select a participant",
13
+ * - searchURL: "http://..."
13
14
  * - selected: "",
14
15
  *
15
16
  * @param {HTMLElement} el The element to generate the autocomplete for.
@@ -17,6 +18,7 @@ import AutoComplete from "src/decidim/refactor/moved/autocomplete";
17
18
  */
18
19
  const autoConfigure = (el) => {
19
20
  const config = JSON.parse(el.dataset.autocomplete);
21
+ const searchUrl = new URL(config.searchURL);
20
22
  const textInput = document.createElement("input");
21
23
  textInput.type = "text";
22
24
  textInput.className = "autocomplete-input";
@@ -44,24 +46,16 @@ const autoConfigure = (el) => {
44
46
  }
45
47
  }
46
48
 
47
- const graphqlEscapedQuery = (query) => query.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
48
-
49
49
  const dataSource = (query, callback) => {
50
- const apiPath = window.Decidim.config.get("api_path");
51
- fetch(apiPath, {
52
- method: "POST",
53
- headers: { "Content-Type": "application/json" },
54
- body: JSON.stringify({
55
- query: `{users(filter:{wildcard:"${graphqlEscapedQuery(query)}"}){id,nickname,name,__typename}}`
56
- })
50
+ const params = new URLSearchParams({
51
+ ...Object.fromEntries(searchUrl.searchParams),
52
+ term: query
53
+ });
54
+ fetch(`${searchUrl.origin}${searchUrl.pathname}?${params.toString()}`, {
55
+ method: "GET",
56
+ headers: { "Content-Type": "application/json" }
57
57
  }).then((response) => response.json()).then((data) => {
58
- const users = data?.data?.users || [];
59
- callback(users.map((user) => ({
60
- value: user.id,
61
- label: `${user.name} (${user.nickname})`
62
- })))
63
- }).catch(() => {
64
- callback([])
58
+ callback(data)
65
59
  });
66
60
  };
67
61
 
@@ -0,0 +1,68 @@
1
+ /* global jest */
2
+
3
+ import { Application } from "@hotwired/stimulus"
4
+ import AccessModeController from "src/decidim/admin/controllers/access_mode/controller"
5
+
6
+ describe("AccessModeController", () => {
7
+ let application = null
8
+ let controller = null
9
+ let element = null
10
+ let checkbox = null
11
+ let accessModeFieldset = null
12
+
13
+ beforeEach(async () => {
14
+ document.body.innerHTML = `
15
+ <div data-controller="access-mode">
16
+ <div id="has_members">
17
+ <input type="hidden" name="has_members" value="0">
18
+ <input type="checkbox" id="has_members_checkbox" name="has_members" value="1">
19
+ </div>
20
+ <fieldset id="access_mode">
21
+ <legend>Access mode</legend>
22
+ <label>Open<input type="radio" name="access_mode" value="open"></label>
23
+ </fieldset>
24
+ </div>
25
+ `
26
+
27
+ application = Application.start()
28
+ application.register("access-mode", AccessModeController)
29
+
30
+ element = document.querySelector("[data-controller='access-mode']")
31
+ checkbox = element.querySelector("#has_members input[type='checkbox']")
32
+ accessModeFieldset = element.querySelector("#access_mode")
33
+
34
+ await new Promise((resolve) => {
35
+ setTimeout(() => {
36
+ controller = application.getControllerForElementAndIdentifier(element, "access-mode")
37
+ resolve()
38
+ }, 0)
39
+ })
40
+ })
41
+
42
+ afterEach(() => {
43
+ controller.disconnect()
44
+ application.stop()
45
+ document.body.innerHTML = ""
46
+ })
47
+
48
+ it("hides the access_mode fieldset when members checkbox is unchecked", () => {
49
+ expect(accessModeFieldset.hidden).toBe(true)
50
+ expect(accessModeFieldset.style.display).toBe("none")
51
+ })
52
+
53
+ it("shows the fieldset when the members checkbox becomes checked", () => {
54
+ checkbox.checked = true
55
+ checkbox.dispatchEvent(new Event("change"))
56
+
57
+ expect(accessModeFieldset.hidden).toBe(false)
58
+ expect(accessModeFieldset.style.display).toBe("")
59
+ })
60
+
61
+ it("removes the event listener on disconnect", () => {
62
+ const removeSpy = jest.spyOn(checkbox, "removeEventListener")
63
+
64
+ controller.disconnect()
65
+
66
+ expect(removeSpy).toHaveBeenCalledWith("change", controller.toggleAccessMode)
67
+ })
68
+ })
@@ -0,0 +1,52 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ /**
4
+ * Toggles the visibility of the access mode options according to the
5
+ * "This space has members" checkbox.
6
+ *
7
+ * The controller expects to sit on the access accordion so it can
8
+ * query the checkbox and the `#access_mode` fieldset that lives inside.
9
+ */
10
+ export default class extends Controller {
11
+
12
+ /**
13
+ * Find the relevant inputs and attach the change listener.
14
+ * @returns {void}
15
+ */
16
+ connect() {
17
+ this.hasMembersCheckbox = this.element.querySelector("#has_members input[type='checkbox']")
18
+ this.accessModeFieldset = this.element.querySelector("#access_mode")
19
+
20
+ if (!this.hasMembersCheckbox || !this.accessModeFieldset) {
21
+ return
22
+ }
23
+
24
+ this.toggleAccessMode = this.toggleAccessMode.bind(this)
25
+ this.hasMembersCheckbox.addEventListener("change", this.toggleAccessMode)
26
+ this.toggleAccessMode()
27
+ }
28
+
29
+ /**
30
+ * Remove the listener bound during `connect`.
31
+ * @returns {void}
32
+ */
33
+ disconnect() {
34
+ if (this.hasMembersCheckbox && this.toggleAccessMode) {
35
+ this.hasMembersCheckbox.removeEventListener("change", this.toggleAccessMode)
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Show or hide the access mode fieldset depending on the checkbox state.
41
+ * @returns {void}
42
+ */
43
+ toggleAccessMode() {
44
+ const showAccessMode = this.hasMembersCheckbox.checked
45
+
46
+ this.accessModeFieldset.disabled = !showAccessMode
47
+ this.accessModeFieldset.hidden = !showAccessMode
48
+ this.accessModeFieldset.style.display = showAccessMode
49
+ ? ""
50
+ : "none"
51
+ }
52
+ }
@@ -9,7 +9,7 @@ document.addEventListener("turbo:load", () => {
9
9
  sendToVerifiedUsers: document.querySelector("#newsletter_send_to_verified_users"),
10
10
  sendToParticipants: document.querySelector("#newsletter_send_to_participants"),
11
11
  sendToFollowers: document.querySelector("#newsletter_send_to_followers"),
12
- sendToPrivateMembers: document.querySelector("#newsletter_send_to_private_members"),
12
+ sendToMembers: document.querySelector("#newsletter_send_to_members"),
13
13
  verificationTypesSelect: document.querySelector("#verification_types_for_select"),
14
14
  participatorySpacesForSelect: document.querySelector("#participatory_spaces_for_select"),
15
15
  deliverButton: document.querySelector("#deliver-button"),
@@ -24,7 +24,7 @@ document.addEventListener("turbo:load", () => {
24
24
  checkboxes: [
25
25
  selectors.sendToParticipants,
26
26
  selectors.sendToFollowers,
27
- selectors.sendToPrivateMembers
27
+ selectors.sendToMembers
28
28
  ].filter(Boolean)
29
29
  };
30
30
 
@@ -22,10 +22,6 @@
22
22
  @apply top-2;
23
23
  }
24
24
 
25
- &__pick-calendar {
26
- @apply z-[3];
27
- }
28
-
29
25
  &__close-calendar {
30
26
  @apply z-[3];
31
27
  }
@@ -37,9 +37,7 @@
37
37
 
38
38
  li {
39
39
  a {
40
- @apply px-2 py-1 overflow-hidden;
41
-
42
- overflow-wrap: anywhere;
40
+ @apply px-2 py-1;
43
41
  }
44
42
 
45
43
  &.is-active a {
@@ -24,7 +24,7 @@
24
24
  @use "stylesheets/decidim/admin/datepicker";
25
25
  @use "stylesheets/decidim/admin/minimap";
26
26
  @use "stylesheets/decidim/admin/proposal_status";
27
- @use "stylesheets/decidim/editor_suggestions";
27
+ @use "stylesheets/decidim/tribute";
28
28
 
29
29
  :root {
30
30
  --primary: #e02d2d;
@@ -26,7 +26,6 @@ module Decidim
26
26
 
27
27
  allow! if user_can_enter_space_area?(require_admin_terms_accepted: true)
28
28
 
29
- accept_admin_terms_action?
30
29
  read_admin_dashboard_action?
31
30
  apply_newsletter_permissions_for_admin!
32
31
 
@@ -44,7 +43,6 @@ module Decidim
44
43
  allow! if user_action?
45
44
  allow! if admin_user_action?
46
45
  allow! if moderate_user_action?
47
- allow! if resource_permission_action?
48
46
 
49
47
  allow! if permission_action.subject == :component
50
48
  allow! if permission_action.subject == :attachment
@@ -92,13 +90,6 @@ module Decidim
92
90
  user && !user.admin? && user.role?("user_manager")
93
91
  end
94
92
 
95
- def accept_admin_terms_action?
96
- return unless permission_action.subject == :admin_terms &&
97
- permission_action.action == :accept
98
-
99
- allow! if user.admin? || user_has_any_role?(user, nil, broad_check: true)
100
- end
101
-
102
93
  def read_admin_dashboard_action?
103
94
  return unless permission_action.subject == :admin_dashboard &&
104
95
  permission_action.action == :read
@@ -256,13 +247,6 @@ module Decidim
256
247
  end
257
248
  end
258
249
 
259
- def resource_permission_action?
260
- return unless permission_action.subject == :resource_permission &&
261
- permission_action.action == :update
262
-
263
- true
264
- end
265
-
266
250
  def organization
267
251
  @organization ||= context.fetch(:organization, nil) || context.fetch(:current_organization, nil)
268
252
  end
@@ -46,14 +46,14 @@ module Decidim
46
46
  end
47
47
 
48
48
  def filters_present?
49
- @form.send_to_followers || @form.send_to_participants || @form.send_to_private_members
49
+ @form.send_to_followers || @form.send_to_participants || @form.send_to_members
50
50
  end
51
51
 
52
52
  def apply_filters(recipients)
53
53
  filters = [
54
54
  user_id_of_followers,
55
55
  participant_ids,
56
- private_member_ids
56
+ member_ids
57
57
  ].compact.flatten.uniq
58
58
 
59
59
  filters.empty? ? recipients.none : recipients.where(id: filters)
@@ -119,17 +119,17 @@ module Decidim
119
119
  participant_ids.flatten.compact.uniq
120
120
  end
121
121
 
122
- def private_spaces
122
+ def spaces_with_members
123
123
  return [] if spaces.blank?
124
124
 
125
- spaces.select { |space| space.try(:private_space?) }
125
+ spaces.select { |space| space.respond_to?(:members) && space.members.exists? }
126
126
  end
127
127
 
128
- def private_member_ids
129
- return unless @form.send_to_private_members
130
- return [] if private_spaces.blank?
128
+ def member_ids
129
+ return unless @form.send_to_members
130
+ return [] if spaces_with_members.blank?
131
131
 
132
- Decidim::ParticipatorySpacePrivateUser.private_user_ids_for_participatory_spaces(private_spaces)
132
+ Decidim::ParticipatorySpace::Member.member_ids_for_participatory_spaces(spaces_with_members)
133
133
  end
134
134
  end
135
135
  end
@@ -1,8 +1,10 @@
1
+ <% add_decidim_page_title(t(".title")) %>
2
+
1
3
  <%= cell("decidim/announcement", announcement_body, callout_class: current_user.admin_terms_accepted? ? "success" : "warning" ) %>
2
4
 
3
5
  <div class="item_show__header">
4
6
  <h1 class="item_show__header-title">
5
- <%= t("title", scope: "decidim.admin.admin_terms_of_service") %>
7
+ <%= t(".title") %>
6
8
  </h1>
7
9
  </div>
8
10