decidim-admin 0.31.6 → 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 (232) 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/content_blocks/landing_page.rb +1 -0
  19. data/app/controllers/concerns/decidim/admin/content_blocks/landing_page_content_blocks.rb +3 -2
  20. data/app/controllers/concerns/decidim/admin/filterable.rb +4 -4
  21. data/app/controllers/concerns/decidim/admin/participatory_space_admin_context.rb +1 -0
  22. data/app/controllers/concerns/decidim/admin/participatory_space_export.rb +1 -1
  23. data/app/controllers/decidim/admin/area_types_controller.rb +2 -2
  24. data/app/controllers/decidim/admin/areas_controller.rb +3 -2
  25. data/app/controllers/decidim/admin/block_user_controller.rb +2 -4
  26. data/app/controllers/decidim/admin/component_permissions_controller.rb +1 -1
  27. data/app/controllers/decidim/admin/components/base_controller.rb +13 -9
  28. data/app/controllers/decidim/admin/components_controller.rb +3 -2
  29. data/app/controllers/decidim/admin/concerns/has_attachment_collections.rb +2 -2
  30. data/app/controllers/decidim/admin/concerns/has_attachments.rb +2 -2
  31. data/app/controllers/decidim/admin/conflicts_controller.rb +1 -1
  32. data/app/controllers/decidim/admin/exports_controller.rb +1 -1
  33. data/app/controllers/decidim/admin/impersonations_controller.rb +1 -1
  34. data/app/controllers/decidim/admin/imports_controller.rb +2 -1
  35. data/app/controllers/decidim/admin/managed_users/promotions_controller.rb +1 -1
  36. data/app/controllers/decidim/admin/newsletters_controller.rb +9 -9
  37. data/app/controllers/decidim/admin/officializations_controller.rb +0 -2
  38. data/app/controllers/decidim/admin/organization_appearance_controller.rb +1 -1
  39. data/app/controllers/decidim/admin/organization_controller.rb +30 -1
  40. data/app/controllers/decidim/admin/organization_external_domain_allowlist_controller.rb +1 -1
  41. data/app/controllers/decidim/admin/participatory_space/concerns/has_members.rb +166 -0
  42. data/app/controllers/decidim/admin/participatory_space/concerns/has_members_csv_import.rb +67 -0
  43. data/app/controllers/decidim/admin/participatory_space/concerns/members_filterable.rb +36 -0
  44. data/app/controllers/decidim/admin/participatory_space/user_role_controller.rb +2 -2
  45. data/app/controllers/decidim/admin/reminders_controller.rb +1 -1
  46. data/app/controllers/decidim/admin/resource_permissions_controller.rb +1 -1
  47. data/app/controllers/decidim/admin/scope_types_controller.rb +2 -2
  48. data/app/controllers/decidim/admin/scopes_controller.rb +3 -2
  49. data/app/controllers/decidim/admin/share_tokens_controller.rb +2 -2
  50. data/app/controllers/decidim/admin/space_publications_controller.rb +2 -2
  51. data/app/controllers/decidim/admin/static_page_topics_controller.rb +2 -2
  52. data/app/controllers/decidim/admin/static_pages_controller.rb +2 -2
  53. data/app/controllers/decidim/admin/taxonomies_controller.rb +3 -3
  54. data/app/controllers/decidim/admin/taxonomy_filters_controller.rb +3 -3
  55. data/app/controllers/decidim/admin/taxonomy_items_controller.rb +3 -3
  56. data/app/controllers/decidim/admin/users_controller.rb +1 -1
  57. data/app/forms/decidim/admin/content_block_form.rb +28 -1
  58. data/app/forms/decidim/admin/organization_form.rb +0 -1
  59. data/app/forms/decidim/admin/participatory_space/member_csv_import_form.rb +33 -0
  60. data/app/forms/decidim/admin/participatory_space/member_form.rb +26 -0
  61. data/app/forms/decidim/admin/participatory_space_admin_user_form.rb +1 -1
  62. data/app/forms/decidim/admin/selective_newsletter_form.rb +7 -7
  63. data/app/forms/decidim/admin/taxonomy_filter_form.rb +10 -11
  64. data/app/helpers/decidim/admin/newsletters_helper.rb +8 -5
  65. data/app/helpers/decidim/admin/search_form_helper.rb +17 -0
  66. data/app/helpers/decidim/admin/settings_helper.rb +2 -0
  67. data/app/jobs/decidim/admin/newsletter_job.rb +2 -2
  68. data/app/jobs/decidim/admin/participatory_space/destroy_members_follows_job.rb +36 -0
  69. data/app/jobs/decidim/admin/participatory_space/import_member_csv_job.rb +29 -0
  70. data/app/packs/src/decidim/admin/admin_autocomplete.js +10 -16
  71. data/app/packs/src/decidim/admin/controllers/access_mode/access_mode.test.js +68 -0
  72. data/app/packs/src/decidim/admin/controllers/access_mode/controller.js +52 -0
  73. data/app/packs/src/decidim/admin/newsletters.js +2 -2
  74. data/app/packs/stylesheets/decidim/admin/_datepicker.scss +0 -4
  75. data/app/packs/stylesheets/decidim/admin/application.scss +1 -1
  76. data/app/queries/decidim/admin/newsletter_recipients.rb +8 -8
  77. data/app/views/decidim/admin/admin_terms/show.html.erb +3 -1
  78. data/app/views/decidim/admin/area_types/index.html.erb +3 -2
  79. data/app/views/decidim/admin/areas/index.html.erb +3 -2
  80. data/app/views/decidim/admin/attachment_collections/edit.html.erb +2 -2
  81. data/app/views/decidim/admin/attachment_collections/index.html.erb +3 -3
  82. data/app/views/decidim/admin/attachment_collections/new.html.erb +2 -2
  83. data/app/views/decidim/admin/attachments/index.html.erb +3 -2
  84. data/app/views/decidim/admin/authorization_workflows/index.html.erb +2 -2
  85. data/app/views/decidim/admin/block_user/new.html.erb +1 -0
  86. data/app/views/decidim/admin/components/_actions.html.erb +1 -1
  87. data/app/views/decidim/admin/components/index.html.erb +2 -2
  88. data/app/views/decidim/admin/components/manage_trash.html.erb +2 -2
  89. data/app/views/decidim/admin/conflicts/edit.html.erb +3 -1
  90. data/app/views/decidim/admin/conflicts/index.html.erb +3 -2
  91. data/app/views/decidim/admin/dashboard/show.html.erb +4 -4
  92. data/app/views/decidim/admin/help_sections/update.html.erb +2 -0
  93. data/app/views/decidim/admin/impersonatable_users/index.html.erb +3 -2
  94. data/app/views/decidim/admin/impersonations/_form.html.erb +2 -2
  95. data/app/views/decidim/admin/impersonations/new.html.erb +3 -2
  96. data/app/views/decidim/admin/imports/new.html.erb +1 -0
  97. data/app/views/decidim/admin/logs/_logs_list.html.erb +1 -1
  98. data/app/views/decidim/admin/logs/index.html.erb +1 -1
  99. data/app/views/decidim/admin/managed_users/impersonation_logs/index.html.erb +6 -5
  100. data/app/views/decidim/admin/managed_users/promotions/new.html.erb +3 -2
  101. data/app/views/decidim/admin/{participatory_space_private_users → members}/edit.html.erb +2 -2
  102. data/app/views/decidim/admin/{participatory_space_private_users → members}/index.html.erb +25 -25
  103. data/app/views/decidim/admin/{participatory_space_private_users → members}/new.html.erb +2 -2
  104. data/app/views/decidim/admin/{participatory_space_private_users_csv_imports → members_csv_imports}/new.html.erb +2 -2
  105. data/app/views/decidim/admin/moderations/_moderation-tr.html.erb +3 -7
  106. data/app/views/decidim/admin/moderations/_moderations-thead.html.erb +0 -1
  107. data/app/views/decidim/admin/moderations/reports/index.html.erb +1 -1
  108. data/app/views/decidim/admin/newsletter_templates/show.html.erb +3 -2
  109. data/app/views/decidim/admin/newsletters/confirm_recipients.html.erb +4 -6
  110. data/app/views/decidim/admin/newsletters/index.html.erb +2 -2
  111. data/app/views/decidim/admin/newsletters/select_recipients_to_deliver.html.erb +2 -2
  112. data/app/views/decidim/admin/newsletters/show.html.erb +3 -2
  113. data/app/views/decidim/admin/officializations/index.html.erb +6 -4
  114. data/app/views/decidim/admin/officializations/show_email.html.erb +3 -7
  115. data/app/views/decidim/admin/organization/form/_extra_features.html.erb +0 -4
  116. data/app/views/decidim/admin/organization_external_domain_allowlist/_form.html.erb +2 -2
  117. data/app/views/decidim/admin/organization_external_domain_allowlist/edit.html.erb +2 -2
  118. data/app/views/decidim/admin/reminders/new.html.erb +8 -1
  119. data/app/views/decidim/admin/resource_permissions/edit.html.erb +2 -2
  120. data/app/views/decidim/admin/scope_types/index.html.erb +3 -2
  121. data/app/views/decidim/admin/scopes/index.html.erb +2 -2
  122. data/app/views/decidim/admin/share_tokens/index.html.erb +2 -0
  123. data/app/views/decidim/admin/shared/_gallery.html.erb +15 -0
  124. data/app/views/decidim/admin/shared/landing_page/_content_blocks.html.erb +0 -2
  125. data/app/views/decidim/admin/shared/landing_page/edit.html.erb +2 -0
  126. data/app/views/decidim/admin/shared/landing_page_content_blocks/edit.html.erb +1 -0
  127. data/app/views/decidim/admin/static_page_topics/index.html.erb +2 -2
  128. data/app/views/decidim/admin/static_pages/edit.html.erb +2 -1
  129. data/app/views/decidim/admin/static_pages/index.html.erb +2 -2
  130. data/app/views/decidim/admin/statistics/_statistics.html.erb +1 -1
  131. data/app/views/decidim/admin/statistics/index.html.erb +2 -0
  132. data/app/views/decidim/admin/taxonomies/_row.html.erb +1 -1
  133. data/app/views/decidim/admin/taxonomies/_row_children.html.erb +1 -1
  134. data/app/views/decidim/admin/taxonomies/_table.html.erb +1 -1
  135. data/app/views/decidim/admin/taxonomies/edit.html.erb +2 -2
  136. data/app/views/decidim/admin/taxonomies/index.html.erb +4 -4
  137. data/app/views/decidim/admin/taxonomy_filters/_table.html.erb +2 -2
  138. data/app/views/decidim/admin/taxonomy_filters/index.html.erb +2 -2
  139. data/app/views/decidim/admin/taxonomy_filters_selector/index.html.erb +2 -0
  140. data/app/views/decidim/admin/taxonomy_filters_selector/new.html.erb +2 -0
  141. data/app/views/decidim/admin/taxonomy_filters_selector/show.html.erb +2 -0
  142. data/app/views/decidim/admin/taxonomy_items/edit.html.erb +2 -0
  143. data/app/views/decidim/admin/taxonomy_items/new.html.erb +2 -0
  144. data/app/views/decidim/admin/users/index.html.erb +3 -2
  145. data/app/views/layouts/decidim/admin/_application.html.erb +1 -1
  146. data/config/locales/ar.yml +9 -49
  147. data/config/locales/bg.yml +9 -55
  148. data/config/locales/bs-BA.yml +4 -24
  149. data/config/locales/ca-IT.yml +130 -113
  150. data/config/locales/ca.yml +130 -113
  151. data/config/locales/cs.yml +126 -107
  152. data/config/locales/de.yml +114 -98
  153. data/config/locales/el.yml +17 -45
  154. data/config/locales/en.yml +130 -112
  155. data/config/locales/eo.yml +0 -7
  156. data/config/locales/es-MX.yml +130 -113
  157. data/config/locales/es-PY.yml +130 -113
  158. data/config/locales/es.yml +130 -113
  159. data/config/locales/eu.yml +159 -142
  160. data/config/locales/fi-plain.yml +129 -112
  161. data/config/locales/fi.yml +129 -112
  162. data/config/locales/fr-CA.yml +91 -142
  163. data/config/locales/fr.yml +91 -142
  164. data/config/locales/ga-IE.yml +0 -13
  165. data/config/locales/gl.yml +10 -41
  166. data/config/locales/he-IL.yml +1 -5
  167. data/config/locales/hu.yml +10 -52
  168. data/config/locales/id-ID.yml +4 -34
  169. data/config/locales/is-IS.yml +1 -16
  170. data/config/locales/it.yml +8 -69
  171. data/config/locales/ja.yml +129 -114
  172. data/config/locales/kaa.yml +4 -16
  173. data/config/locales/ko.yml +6 -40
  174. data/config/locales/lb.yml +9 -41
  175. data/config/locales/lt.yml +10 -52
  176. data/config/locales/lv.yml +8 -38
  177. data/config/locales/nl.yml +10 -46
  178. data/config/locales/no.yml +10 -46
  179. data/config/locales/pl.yml +9 -54
  180. data/config/locales/pt-BR.yml +121 -104
  181. data/config/locales/pt.yml +9 -42
  182. data/config/locales/ro-RO.yml +15 -51
  183. data/config/locales/ru.yml +4 -35
  184. data/config/locales/sk.yml +8 -814
  185. data/config/locales/sl.yml +1 -7
  186. data/config/locales/sq-AL.yml +1 -21
  187. data/config/locales/sr-CS.yml +4 -24
  188. data/config/locales/sv.yml +79 -89
  189. data/config/locales/th-TH.yml +0 -8
  190. data/config/locales/tr-TR.yml +33 -65
  191. data/config/locales/uk.yml +0 -26
  192. data/config/locales/val-ES.yml +0 -1
  193. data/config/locales/zh-CN.yml +9 -38
  194. data/config/locales/zh-TW.yml +10 -52
  195. data/config/routes.rb +4 -0
  196. data/decidim-admin.gemspec +7 -10
  197. data/lib/decidim/admin/engine.rb +5 -2
  198. data/lib/decidim/admin/form_builder.rb +3 -1
  199. data/lib/decidim/admin/menu.rb +1 -1
  200. data/lib/decidim/admin/test/admin_members_shared_examples.rb +119 -0
  201. data/lib/decidim/admin/test/admin_participatory_space_access_examples.rb +3 -3
  202. data/lib/decidim/admin/test/filterable_examples.rb +20 -11
  203. data/lib/decidim/admin/test/invite_participatory_space_admins_shared_examples.rb +17 -17
  204. data/lib/decidim/admin/test/invite_participatory_space_collaborators_shared_examples.rb +1 -1
  205. data/lib/decidim/admin/test/invite_participatory_space_moderators_shared_examples.rb +1 -1
  206. data/lib/decidim/admin/test/invite_participatory_space_users_shared_context.rb +1 -1
  207. data/lib/decidim/admin/test/manage_attachment_collections_examples.rb +3 -3
  208. data/lib/decidim/admin/test/manage_attachments_examples.rb +6 -6
  209. data/lib/decidim/admin/test/manage_component_permissions_examples.rb +5 -5
  210. data/lib/decidim/admin/test/manage_moderations_examples.rb +6 -6
  211. data/lib/decidim/admin/test/manage_participatory_space_publications_examples.rb +2 -2
  212. data/lib/decidim/admin/test/manage_resource_soft_deletion_examples.rb +3 -3
  213. data/lib/decidim/admin/version.rb +1 -1
  214. data/lib/decidim/admin.rb +0 -2
  215. metadata +40 -36
  216. data/app/commands/decidim/admin/create_participatory_space_private_user.rb +0 -98
  217. data/app/commands/decidim/admin/destroy_participatory_space_private_user.rb +0 -28
  218. data/app/commands/decidim/admin/process_participatory_space_private_user_import_csv.rb +0 -44
  219. data/app/commands/decidim/admin/publish_all_participatory_space_private_users.rb +0 -50
  220. data/app/commands/decidim/admin/unpublish_all_participatory_space_private_users.rb +0 -50
  221. data/app/commands/decidim/admin/update_participatory_space_private_user.rb +0 -11
  222. data/app/controllers/concerns/decidim/participatory_space_private_users/admin/filterable.rb +0 -34
  223. data/app/controllers/decidim/admin/concerns/has_private_users.rb +0 -169
  224. data/app/controllers/decidim/admin/concerns/has_private_users_csv_import.rb +0 -65
  225. data/app/forms/decidim/admin/participatory_space_private_user_csv_import_form.rb +0 -31
  226. data/app/forms/decidim/admin/participatory_space_private_user_form.rb +0 -24
  227. data/app/jobs/decidim/admin/destroy_private_users_follows_job.rb +0 -37
  228. data/app/jobs/decidim/admin/import_participatory_space_private_user_csv_job.rb +0 -27
  229. data/app/views/decidim/admin/devise/mailers/password_change.html.erb +0 -3
  230. data/app/views/decidim/admin/devise/mailers/reset_password_instructions.html.erb +0 -8
  231. data/app/views/decidim/admin/shared/_attachments.html.erb +0 -15
  232. /data/app/views/decidim/admin/{participatory_space_private_users → members}/_form.html.erb +0 -0
@@ -31,7 +31,7 @@ module Decidim
31
31
 
32
32
  on(:invalid) do
33
33
  flash.now[:alert] = I18n.t("share_tokens.create.invalid", scope: "decidim.admin")
34
- render action: "new", status: :unprocessable_entity
34
+ render action: "new", status: :unprocessable_content
35
35
  end
36
36
  end
37
37
  end
@@ -53,7 +53,7 @@ module Decidim
53
53
 
54
54
  on(:invalid) do
55
55
  flash.now[:alert] = I18n.t("share_tokens.update.error", scope: "decidim.admin")
56
- render :edit, status: :unprocessable_entity
56
+ render :edit, status: :unprocessable_content
57
57
  end
58
58
  end
59
59
  end
@@ -17,7 +17,7 @@ module Decidim
17
17
  flash.now[:alert] = I18n.t("create.error", scope: i18n_scope)
18
18
  end
19
19
 
20
- redirect_back(fallback_location:)
20
+ redirect_back_or_to(fallback_location)
21
21
  end
22
22
  end
23
23
 
@@ -33,7 +33,7 @@ module Decidim
33
33
  flash.now[:alert] = I18n.t("destroy.error", scope: i18n_scope)
34
34
  end
35
35
 
36
- redirect_back(fallback_location:)
36
+ redirect_back_or_to(fallback_location)
37
37
  end
38
38
  end
39
39
 
@@ -30,7 +30,7 @@ module Decidim
30
30
 
31
31
  on(:invalid) do
32
32
  flash.now[:alert] = I18n.t("static_page_topics.create.error", scope: "decidim.admin")
33
- render :new, status: :unprocessable_entity
33
+ render :new, status: :unprocessable_content
34
34
  end
35
35
  end
36
36
  end
@@ -52,7 +52,7 @@ module Decidim
52
52
 
53
53
  on(:invalid) do
54
54
  flash.now[:alert] = I18n.t("static_page_topics.update.error", scope: "decidim.admin")
55
- render :edit, status: :unprocessable_entity
55
+ render :edit, status: :unprocessable_content
56
56
  end
57
57
  end
58
58
  end
@@ -62,7 +62,7 @@ module Decidim
62
62
 
63
63
  on(:invalid) do
64
64
  flash.now[:alert] = I18n.t("static_pages.create.error", scope: "decidim.admin")
65
- render :new, status: :unprocessable_entity
65
+ render :new, status: :unprocessable_content
66
66
  end
67
67
  end
68
68
  end
@@ -85,7 +85,7 @@ module Decidim
85
85
 
86
86
  on(:invalid) do
87
87
  flash.now[:alert] = I18n.t("static_pages.update.error", scope: "decidim.admin")
88
- render :edit, status: :unprocessable_entity
88
+ render :edit, status: :unprocessable_content
89
89
  end
90
90
  end
91
91
  end
@@ -39,7 +39,7 @@ module Decidim
39
39
 
40
40
  on(:invalid) do
41
41
  flash.now[:alert] = I18n.t("create.invalid", scope: "decidim.admin.taxonomies")
42
- render action: "new", status: :unprocessable_entity
42
+ render action: "new", status: :unprocessable_content
43
43
  end
44
44
  end
45
45
  end
@@ -66,7 +66,7 @@ module Decidim
66
66
 
67
67
  on(:invalid) do
68
68
  flash.now[:alert] = I18n.t("update.invalid", scope: "decidim.admin.taxonomies")
69
- render action: "edit", status: :unprocessable_entity
69
+ render action: "edit", status: :unprocessable_content
70
70
  end
71
71
  end
72
72
  end
@@ -83,7 +83,7 @@ module Decidim
83
83
  flash.now[:alert] = I18n.t("destroy.invalid", scope: "decidim.admin.taxonomies")
84
84
  end
85
85
  end
86
- redirect_back(fallback_location: taxonomies_path)
86
+ redirect_back_or_to(taxonomies_path)
87
87
  end
88
88
 
89
89
  def reorder
@@ -41,7 +41,7 @@ module Decidim
41
41
 
42
42
  on(:invalid) do
43
43
  flash.now[:alert] = I18n.t("create.error", scope: "decidim.admin.taxonomy_filters")
44
- render :new, status: :unprocessable_entity
44
+ render :new, status: :unprocessable_content
45
45
  end
46
46
  end
47
47
  end
@@ -63,7 +63,7 @@ module Decidim
63
63
  end
64
64
  on(:invalid) do
65
65
  flash.now[:alert] = I18n.t("update.error", scope: "decidim.admin.taxonomy_filters")
66
- render :edit, status: :unprocessable_entity
66
+ render :edit, status: :unprocessable_content
67
67
  end
68
68
  end
69
69
  end
@@ -78,7 +78,7 @@ module Decidim
78
78
  flash[:alert] = I18n.t("destroy.error", scope: "decidim.admin.taxonomy_filters")
79
79
  end
80
80
  end
81
- redirect_back(fallback_location: decidim_admin.taxonomy_filters_path(root_taxonomy))
81
+ redirect_back_or_to(decidim_admin.taxonomy_filters_path(root_taxonomy))
82
82
  end
83
83
 
84
84
  private
@@ -9,7 +9,7 @@ module Decidim
9
9
  before_action do
10
10
  if taxonomy_item && taxonomy_item.parent_ids.exclude?(taxonomy.id)
11
11
  flash[:alert] = I18n.t("update.invalid", scope: "decidim.admin.taxonomies")
12
- render plain: I18n.t("update.invalid", scope: "decidim.admin.taxonomies"), status: :unprocessable_entity
12
+ render plain: I18n.t("update.invalid", scope: "decidim.admin.taxonomies"), status: :unprocessable_content
13
13
  end
14
14
  end
15
15
 
@@ -29,7 +29,7 @@ module Decidim
29
29
 
30
30
  on(:invalid) do
31
31
  flash.now[:alert] = I18n.t("create.invalid", scope: "decidim.admin.taxonomies")
32
- render action: "new", status: :unprocessable_entity
32
+ render action: "new", status: :unprocessable_content
33
33
  end
34
34
  end
35
35
  end
@@ -50,7 +50,7 @@ module Decidim
50
50
 
51
51
  on(:invalid) do
52
52
  flash.now[:alert] = I18n.t("update.invalid", scope: "decidim.admin.taxonomies")
53
- render action: "edit", status: :unprocessable_entity
53
+ render action: "edit", status: :unprocessable_content
54
54
  end
55
55
  end
56
56
  end
@@ -38,7 +38,7 @@ module Decidim
38
38
 
39
39
  on(:invalid) do
40
40
  flash.now[:alert] = I18n.t("users.create.error", scope: "decidim.admin")
41
- render :new, status: :unprocessable_entity
41
+ render :new, status: :unprocessable_content
42
42
  end
43
43
  end
44
44
  end
@@ -12,12 +12,39 @@ module Decidim
12
12
  attribute :settings, Object
13
13
  attribute :images, Hash
14
14
 
15
+ validate :validate_settings
16
+
15
17
  def map_model(model)
16
18
  self.images = model.images_container
17
19
  end
18
20
 
19
21
  def settings?
20
- settings.manifest.settings.any?
22
+ return false unless settings.respond_to?(:manifest)
23
+
24
+ settings.manifest.attributes.any?
25
+ end
26
+
27
+ private
28
+
29
+ def validate_settings
30
+ coerce_settings if settings.respond_to?(:to_h) && !settings.respond_to?(:valid?)
31
+
32
+ return unless settings.respond_to?(:valid?)
33
+ return if settings.valid?
34
+
35
+ settings.errors.each do |error|
36
+ errors.add(:settings, error.message)
37
+ end
38
+ end
39
+
40
+ def coerce_settings
41
+ content_block = context[:content_block]
42
+ return unless content_block
43
+
44
+ self.settings = content_block.manifest.settings.schema.new(
45
+ settings.to_h,
46
+ content_block.organization.default_locale
47
+ )
21
48
  end
22
49
  end
23
50
  end
@@ -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,7 +6,7 @@ module Decidim
6
6
  #
7
7
  class NewsletterJob < ApplicationJob
8
8
  queue_as :newsletter
9
- self.enqueue_after_transaction_commit = :never
9
+ self.enqueue_after_transaction_commit = false
10
10
 
11
11
  def perform(newsletter, form, recipients_ids)
12
12
  @newsletter = newsletter
@@ -37,7 +37,7 @@ module Decidim
37
37
  send_to_verified_users: @form["send_to_verified_users"],
38
38
  send_to_followers: @form["send_to_followers"],
39
39
  send_to_participants: @form["send_to_participants"],
40
- send_to_private_members: @form["send_to_private_members"],
40
+ send_to_members: @form["send_to_members"],
41
41
  participatory_space_types: @form["participatory_space_types"],
42
42
  verification_types: @form["verification_types"]
43
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
+ })