decidim-admin 0.31.5 → 0.32.0.rc2

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 (218) 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/participatory_space/create_member.rb +99 -0
  7. data/app/commands/decidim/admin/participatory_space/destroy_member.rb +30 -0
  8. data/app/commands/decidim/admin/participatory_space/import_member_csv.rb +46 -0
  9. data/app/commands/decidim/admin/participatory_space/publish_all_members.rb +52 -0
  10. data/app/commands/decidim/admin/participatory_space/unpublish_all_members.rb +52 -0
  11. data/app/commands/decidim/admin/participatory_space/update_member.rb +13 -0
  12. data/app/commands/decidim/admin/reorder_components.rb +1 -1
  13. data/app/commands/decidim/admin/reorder_taxonomies.rb +1 -1
  14. data/app/commands/decidim/admin/update_organization.rb +1 -1
  15. data/app/controllers/concerns/decidim/admin/content_blocks/landing_page.rb +1 -0
  16. data/app/controllers/concerns/decidim/admin/content_blocks/landing_page_content_blocks.rb +3 -2
  17. data/app/controllers/concerns/decidim/admin/filterable.rb +4 -4
  18. data/app/controllers/concerns/decidim/admin/participatory_space_admin_context.rb +1 -0
  19. data/app/controllers/concerns/decidim/admin/participatory_space_export.rb +1 -1
  20. data/app/controllers/decidim/admin/area_types_controller.rb +2 -2
  21. data/app/controllers/decidim/admin/areas_controller.rb +3 -2
  22. data/app/controllers/decidim/admin/block_user_controller.rb +2 -2
  23. data/app/controllers/decidim/admin/component_permissions_controller.rb +1 -1
  24. data/app/controllers/decidim/admin/components/base_controller.rb +13 -9
  25. data/app/controllers/decidim/admin/components_controller.rb +3 -2
  26. data/app/controllers/decidim/admin/concerns/has_attachment_collections.rb +2 -2
  27. data/app/controllers/decidim/admin/concerns/has_attachments.rb +2 -2
  28. data/app/controllers/decidim/admin/conflicts_controller.rb +1 -1
  29. data/app/controllers/decidim/admin/exports_controller.rb +1 -1
  30. data/app/controllers/decidim/admin/impersonations_controller.rb +1 -1
  31. data/app/controllers/decidim/admin/imports_controller.rb +2 -1
  32. data/app/controllers/decidim/admin/managed_users/promotions_controller.rb +1 -1
  33. data/app/controllers/decidim/admin/newsletters_controller.rb +9 -8
  34. data/app/controllers/decidim/admin/organization_appearance_controller.rb +1 -1
  35. data/app/controllers/decidim/admin/organization_controller.rb +1 -1
  36. data/app/controllers/decidim/admin/organization_external_domain_allowlist_controller.rb +1 -1
  37. data/app/controllers/decidim/admin/participatory_space/concerns/has_members.rb +166 -0
  38. data/app/controllers/decidim/admin/participatory_space/concerns/has_members_csv_import.rb +67 -0
  39. data/app/controllers/decidim/admin/participatory_space/concerns/members_filterable.rb +36 -0
  40. data/app/controllers/decidim/admin/participatory_space/user_role_controller.rb +2 -2
  41. data/app/controllers/decidim/admin/reminders_controller.rb +1 -1
  42. data/app/controllers/decidim/admin/resource_permissions_controller.rb +1 -1
  43. data/app/controllers/decidim/admin/scope_types_controller.rb +2 -2
  44. data/app/controllers/decidim/admin/scopes_controller.rb +3 -2
  45. data/app/controllers/decidim/admin/share_tokens_controller.rb +2 -2
  46. data/app/controllers/decidim/admin/space_publications_controller.rb +2 -2
  47. data/app/controllers/decidim/admin/static_page_topics_controller.rb +2 -2
  48. data/app/controllers/decidim/admin/static_pages_controller.rb +2 -2
  49. data/app/controllers/decidim/admin/taxonomies_controller.rb +3 -3
  50. data/app/controllers/decidim/admin/taxonomy_filters_controller.rb +3 -3
  51. data/app/controllers/decidim/admin/taxonomy_items_controller.rb +3 -3
  52. data/app/controllers/decidim/admin/users_controller.rb +1 -1
  53. data/app/forms/decidim/admin/content_block_form.rb +28 -1
  54. data/app/forms/decidim/admin/organization_form.rb +0 -1
  55. data/app/forms/decidim/admin/participatory_space/member_csv_import_form.rb +33 -0
  56. data/app/forms/decidim/admin/participatory_space/member_form.rb +26 -0
  57. data/app/forms/decidim/admin/participatory_space_admin_user_form.rb +1 -1
  58. data/app/forms/decidim/admin/selective_newsletter_form.rb +7 -7
  59. data/app/forms/decidim/admin/taxonomy_filter_form.rb +1 -0
  60. data/app/helpers/decidim/admin/newsletters_helper.rb +8 -5
  61. data/app/helpers/decidim/admin/search_form_helper.rb +17 -0
  62. data/app/helpers/decidim/admin/settings_helper.rb +2 -0
  63. data/app/jobs/decidim/admin/newsletter_job.rb +2 -2
  64. data/app/jobs/decidim/admin/participatory_space/destroy_members_follows_job.rb +36 -0
  65. data/app/jobs/decidim/admin/participatory_space/import_member_csv_job.rb +29 -0
  66. data/app/packs/src/decidim/admin/controllers/access_mode/access_mode.test.js +68 -0
  67. data/app/packs/src/decidim/admin/controllers/access_mode/controller.js +52 -0
  68. data/app/packs/src/decidim/admin/newsletters.js +2 -2
  69. data/app/packs/stylesheets/decidim/admin/_datepicker.scss +0 -4
  70. data/app/queries/decidim/admin/newsletter_recipients.rb +8 -8
  71. data/app/views/decidim/admin/admin_terms/show.html.erb +3 -1
  72. data/app/views/decidim/admin/area_types/index.html.erb +3 -2
  73. data/app/views/decidim/admin/areas/index.html.erb +3 -2
  74. data/app/views/decidim/admin/attachment_collections/edit.html.erb +2 -2
  75. data/app/views/decidim/admin/attachment_collections/index.html.erb +2 -2
  76. data/app/views/decidim/admin/attachment_collections/new.html.erb +2 -2
  77. data/app/views/decidim/admin/attachments/index.html.erb +3 -2
  78. data/app/views/decidim/admin/authorization_workflows/index.html.erb +2 -2
  79. data/app/views/decidim/admin/block_user/new.html.erb +1 -0
  80. data/app/views/decidim/admin/components/index.html.erb +2 -2
  81. data/app/views/decidim/admin/components/manage_trash.html.erb +2 -2
  82. data/app/views/decidim/admin/conflicts/edit.html.erb +3 -1
  83. data/app/views/decidim/admin/conflicts/index.html.erb +3 -2
  84. data/app/views/decidim/admin/dashboard/show.html.erb +4 -4
  85. data/app/views/decidim/admin/help_sections/update.html.erb +2 -0
  86. data/app/views/decidim/admin/impersonatable_users/index.html.erb +3 -2
  87. data/app/views/decidim/admin/impersonations/_form.html.erb +2 -2
  88. data/app/views/decidim/admin/impersonations/new.html.erb +3 -2
  89. data/app/views/decidim/admin/imports/new.html.erb +1 -0
  90. data/app/views/decidim/admin/logs/_logs_list.html.erb +1 -1
  91. data/app/views/decidim/admin/logs/index.html.erb +1 -1
  92. data/app/views/decidim/admin/managed_users/impersonation_logs/index.html.erb +6 -5
  93. data/app/views/decidim/admin/managed_users/promotions/new.html.erb +3 -2
  94. data/app/views/decidim/admin/{participatory_space_private_users → members}/edit.html.erb +2 -2
  95. data/app/views/decidim/admin/{participatory_space_private_users → members}/index.html.erb +25 -25
  96. data/app/views/decidim/admin/{participatory_space_private_users → members}/new.html.erb +2 -2
  97. data/app/views/decidim/admin/{participatory_space_private_users_csv_imports → members_csv_imports}/new.html.erb +2 -2
  98. data/app/views/decidim/admin/moderations/_moderation-tr.html.erb +2 -6
  99. data/app/views/decidim/admin/moderations/_moderations-thead.html.erb +0 -1
  100. data/app/views/decidim/admin/moderations/reports/index.html.erb +1 -1
  101. data/app/views/decidim/admin/newsletter_templates/show.html.erb +3 -2
  102. data/app/views/decidim/admin/newsletters/confirm_recipients.html.erb +4 -6
  103. data/app/views/decidim/admin/newsletters/index.html.erb +2 -2
  104. data/app/views/decidim/admin/newsletters/select_recipients_to_deliver.html.erb +2 -2
  105. data/app/views/decidim/admin/newsletters/show.html.erb +3 -2
  106. data/app/views/decidim/admin/officializations/index.html.erb +6 -4
  107. data/app/views/decidim/admin/officializations/show_email.html.erb +2 -0
  108. data/app/views/decidim/admin/organization/form/_extra_features.html.erb +0 -4
  109. data/app/views/decidim/admin/organization_external_domain_allowlist/_form.html.erb +2 -2
  110. data/app/views/decidim/admin/organization_external_domain_allowlist/edit.html.erb +2 -2
  111. data/app/views/decidim/admin/reminders/new.html.erb +8 -1
  112. data/app/views/decidim/admin/resource_permissions/edit.html.erb +2 -2
  113. data/app/views/decidim/admin/scope_types/index.html.erb +3 -2
  114. data/app/views/decidim/admin/scopes/index.html.erb +2 -2
  115. data/app/views/decidim/admin/share_tokens/index.html.erb +2 -0
  116. data/app/views/decidim/admin/shared/landing_page/_content_blocks.html.erb +0 -2
  117. data/app/views/decidim/admin/shared/landing_page/edit.html.erb +2 -0
  118. data/app/views/decidim/admin/shared/landing_page_content_blocks/edit.html.erb +1 -0
  119. data/app/views/decidim/admin/static_page_topics/index.html.erb +2 -2
  120. data/app/views/decidim/admin/static_pages/edit.html.erb +2 -1
  121. data/app/views/decidim/admin/static_pages/index.html.erb +2 -2
  122. data/app/views/decidim/admin/statistics/_statistics.html.erb +1 -1
  123. data/app/views/decidim/admin/statistics/index.html.erb +2 -0
  124. data/app/views/decidim/admin/taxonomies/_row.html.erb +1 -1
  125. data/app/views/decidim/admin/taxonomies/_row_children.html.erb +1 -1
  126. data/app/views/decidim/admin/taxonomies/_table.html.erb +1 -1
  127. data/app/views/decidim/admin/taxonomies/edit.html.erb +2 -2
  128. data/app/views/decidim/admin/taxonomies/index.html.erb +4 -4
  129. data/app/views/decidim/admin/taxonomy_filters/index.html.erb +2 -2
  130. data/app/views/decidim/admin/taxonomy_filters_selector/index.html.erb +2 -0
  131. data/app/views/decidim/admin/taxonomy_filters_selector/new.html.erb +2 -0
  132. data/app/views/decidim/admin/taxonomy_filters_selector/show.html.erb +2 -0
  133. data/app/views/decidim/admin/taxonomy_items/edit.html.erb +2 -0
  134. data/app/views/decidim/admin/taxonomy_items/new.html.erb +2 -0
  135. data/app/views/decidim/admin/users/index.html.erb +3 -2
  136. data/config/locales/ar.yml +4 -48
  137. data/config/locales/bg.yml +4 -54
  138. data/config/locales/bs-BA.yml +0 -23
  139. data/config/locales/ca-IT.yml +126 -98
  140. data/config/locales/ca.yml +126 -98
  141. data/config/locales/cs.yml +122 -95
  142. data/config/locales/de.yml +109 -93
  143. data/config/locales/el.yml +11 -44
  144. data/config/locales/en.yml +126 -98
  145. data/config/locales/eo.yml +0 -6
  146. data/config/locales/es-MX.yml +126 -98
  147. data/config/locales/es-PY.yml +126 -98
  148. data/config/locales/es.yml +126 -98
  149. data/config/locales/eu.yml +126 -98
  150. data/config/locales/fi-plain.yml +126 -98
  151. data/config/locales/fi.yml +126 -98
  152. data/config/locales/fr-CA.yml +91 -90
  153. data/config/locales/fr.yml +91 -90
  154. data/config/locales/ga-IE.yml +0 -12
  155. data/config/locales/gl.yml +4 -40
  156. data/config/locales/he-IL.yml +0 -4
  157. data/config/locales/hu.yml +4 -51
  158. data/config/locales/id-ID.yml +4 -33
  159. data/config/locales/is-IS.yml +0 -14
  160. data/config/locales/it.yml +4 -67
  161. data/config/locales/ja.yml +124 -98
  162. data/config/locales/kaa.yml +0 -15
  163. data/config/locales/ko.yml +0 -39
  164. data/config/locales/lb.yml +4 -40
  165. data/config/locales/lt.yml +4 -51
  166. data/config/locales/lv.yml +4 -37
  167. data/config/locales/nl.yml +4 -45
  168. data/config/locales/no.yml +4 -45
  169. data/config/locales/pl.yml +4 -53
  170. data/config/locales/pt-BR.yml +109 -96
  171. data/config/locales/pt.yml +4 -41
  172. data/config/locales/ro-RO.yml +9 -50
  173. data/config/locales/ru.yml +4 -34
  174. data/config/locales/sk.yml +126 -98
  175. data/config/locales/sl.yml +0 -6
  176. data/config/locales/sq-AL.yml +0 -20
  177. data/config/locales/sr-CS.yml +0 -23
  178. data/config/locales/sv.yml +74 -86
  179. data/config/locales/th-TH.yml +0 -7
  180. data/config/locales/tr-TR.yml +21 -57
  181. data/config/locales/uk.yml +0 -25
  182. data/config/locales/zh-CN.yml +4 -37
  183. data/config/locales/zh-TW.yml +4 -51
  184. data/decidim-admin.gemspec +7 -10
  185. data/lib/decidim/admin/engine.rb +5 -2
  186. data/lib/decidim/admin/menu.rb +1 -1
  187. data/lib/decidim/admin/test/admin_members_shared_examples.rb +119 -0
  188. data/lib/decidim/admin/test/admin_participatory_space_access_examples.rb +3 -3
  189. data/lib/decidim/admin/test/filterable_examples.rb +20 -11
  190. data/lib/decidim/admin/test/invite_participatory_space_admins_shared_examples.rb +17 -17
  191. data/lib/decidim/admin/test/invite_participatory_space_collaborators_shared_examples.rb +1 -1
  192. data/lib/decidim/admin/test/invite_participatory_space_moderators_shared_examples.rb +1 -1
  193. data/lib/decidim/admin/test/invite_participatory_space_users_shared_context.rb +1 -1
  194. data/lib/decidim/admin/test/manage_attachment_collections_examples.rb +3 -3
  195. data/lib/decidim/admin/test/manage_attachments_examples.rb +6 -6
  196. data/lib/decidim/admin/test/manage_component_permissions_examples.rb +5 -5
  197. data/lib/decidim/admin/test/manage_moderations_examples.rb +6 -6
  198. data/lib/decidim/admin/test/manage_participatory_space_publications_examples.rb +2 -2
  199. data/lib/decidim/admin/test/manage_resource_soft_deletion_examples.rb +3 -3
  200. data/lib/decidim/admin/version.rb +1 -1
  201. data/lib/decidim/admin.rb +0 -2
  202. metadata +39 -35
  203. data/app/commands/decidim/admin/create_participatory_space_private_user.rb +0 -98
  204. data/app/commands/decidim/admin/destroy_participatory_space_private_user.rb +0 -28
  205. data/app/commands/decidim/admin/process_participatory_space_private_user_import_csv.rb +0 -44
  206. data/app/commands/decidim/admin/publish_all_participatory_space_private_users.rb +0 -50
  207. data/app/commands/decidim/admin/unpublish_all_participatory_space_private_users.rb +0 -50
  208. data/app/commands/decidim/admin/update_participatory_space_private_user.rb +0 -11
  209. data/app/controllers/concerns/decidim/participatory_space_private_users/admin/filterable.rb +0 -34
  210. data/app/controllers/decidim/admin/concerns/has_private_users.rb +0 -169
  211. data/app/controllers/decidim/admin/concerns/has_private_users_csv_import.rb +0 -65
  212. data/app/forms/decidim/admin/participatory_space_private_user_csv_import_form.rb +0 -31
  213. data/app/forms/decidim/admin/participatory_space_private_user_form.rb +0 -24
  214. data/app/jobs/decidim/admin/destroy_private_users_follows_job.rb +0 -37
  215. data/app/jobs/decidim/admin/import_participatory_space_private_user_csv_job.rb +0 -27
  216. data/app/views/decidim/admin/devise/mailers/password_change.html.erb +0 -3
  217. data/app/views/decidim/admin/devise/mailers/reset_password_instructions.html.erb +0 -8
  218. /data/app/views/decidim/admin/{participatory_space_private_users → members}/_form.html.erb +0 -0
@@ -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
 
@@ -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
@@ -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
  }
@@ -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
 
@@ -1,8 +1,9 @@
1
- <% add_decidim_page_title(t("decidim.admin.titles.area_types")) %>
1
+ <% add_decidim_page_title(t(".title")) %>
2
+
2
3
  <div class="card" id="area-types">
3
4
  <div class="item_show__header">
4
5
  <h1 class="item_show__header-title">
5
- <%= t "decidim.admin.titles.area_types" %>
6
+ <%= t(".title") %>
6
7
  <% if allowed_to? :create, :area_type %>
7
8
  <%= link_to t("actions.add", scope: "decidim.admin"), [:new, :area_type], class: "button button__sm button__secondary new" %>
8
9
  <% end %>
@@ -1,8 +1,9 @@
1
- <% add_decidim_page_title(t("areas", scope: "decidim.admin.titles")) %>
1
+ <% add_decidim_page_title(t(".title")) %>
2
+
2
3
  <div class="card" id="areas">
3
4
  <div class="item_show__header">
4
5
  <h1 class="item_show__header-title">
5
- <%= t "decidim.admin.titles.areas" %>
6
+ <%= t ".title" %>
6
7
  <% if allowed_to? :create, :area %>
7
8
  <%= link_to t("actions.add", scope: "decidim.admin"), new_area_path, class: "button button__sm button__secondary new" %>
8
9
  <% end %>
@@ -1,8 +1,8 @@
1
- <% add_decidim_page_title(t("attachment_collections.edit.title", scope: "decidim.admin")) %>
1
+ <% add_decidim_page_title(t(".title")) %>
2
2
 
3
3
  <div class="item_show__header">
4
4
  <h1 class="item_show__header-title">
5
- <%= t("attachment_collections.edit.title", scope: "decidim.admin") %>
5
+ <%= t(".title") %>
6
6
  </h1>
7
7
  </div>
8
8
 
@@ -1,9 +1,9 @@
1
- <% add_decidim_page_title(t("attachment_collections.index.attachment_collections_title", scope: "decidim.admin")) %>
1
+ <% add_decidim_page_title(t(".title")) %>
2
2
 
3
3
  <div class="card" id="attachment_collections">
4
4
  <div class="item_show__header">
5
5
  <h1 class="item_show__header-title">
6
- <%= t("attachment_collections.index.attachment_collections_title", scope: "decidim.admin") %>
6
+ <%= t(".title") %>
7
7
  <% if allowed_to? :create, :attachment_collection %>
8
8
  <%= link_to t("actions.attachment_collection.new", scope: "decidim.admin"), url_for(action: :new), class: "button button__sm button__secondary new" %>
9
9
  <% end %>
@@ -1,7 +1,7 @@
1
- <% add_decidim_page_title(t("attachment_collections.new.title", scope: "decidim.admin")) %>
1
+ <% add_decidim_page_title(t(".title")) %>
2
2
  <div class="item_show__header">
3
3
  <h1 class="item_show__header-title">
4
- <%= t("attachment_collections.new.title", scope: "decidim.admin") %>
4
+ <%= t(".title") %>
5
5
  </h1>
6
6
  </div>
7
7
 
@@ -1,8 +1,9 @@
1
- <% add_decidim_page_title(t(".attachments_title")) %>
1
+ <% add_decidim_page_title(t(".title")) %>
2
+
2
3
  <div class="card" id="attachments">
3
4
  <div class="item_show__header">
4
5
  <h1 class="item_show__header-title">
5
- <%= t(".attachments_title") %>
6
+ <%= t(".title") %>
6
7
  <% if allowed_to? :create, :attachment %>
7
8
  <%= link_to t("actions.attachment.new", scope: "decidim.admin"), url_for(action: :new), class: "button button__sm button__secondary new" %>
8
9
  <% end %>