decidim-department_admin 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +132 -0
  4. data/Rakefile +9 -0
  5. data/app/controllers/decidim/department_admin/admin/application_controller.rb +15 -0
  6. data/app/controllers/decidim/department_admin/application_controller.rb +13 -0
  7. data/app/decorators/decidim/admin/newsletters_controller_decorator.rb +30 -0
  8. data/app/decorators/decidim/admin/users_controller_decorator.rb +128 -0
  9. data/app/decorators/decidim/area_decorator.rb +44 -0
  10. data/app/decorators/decidim/assemblies/admin/assemblies_controller_decorator.rb +25 -0
  11. data/app/decorators/decidim/assemblies/admin/assemblies_helper_decorator.rb +24 -0
  12. data/app/decorators/decidim/assemblies/create_assembly_decorator.rb +19 -0
  13. data/app/decorators/decidim/assemblies/parent_assemblies_for_select_decorator.rb +38 -0
  14. data/app/decorators/decidim/assemblies/permissions_decorator.rb +22 -0
  15. data/app/decorators/decidim/assemblies/update_assembly_decorator.rb +19 -0
  16. data/app/decorators/decidim/assemblies_decorator.rb +21 -0
  17. data/app/decorators/decidim/assemblies_with_user_role_decorator.rb +27 -0
  18. data/app/decorators/decidim/conference_form_decorator.rb +23 -0
  19. data/app/decorators/decidim/conferences/admin/conferences_controller_decorator.rb +27 -0
  20. data/app/decorators/decidim/conferences/create_conference_decorator.rb +51 -0
  21. data/app/decorators/decidim/conferences/permissions_decorator.rb +24 -0
  22. data/app/decorators/decidim/conferences/update_conference_decorator.rb +21 -0
  23. data/app/decorators/decidim/conferences_decorator.rb +27 -0
  24. data/app/decorators/decidim/conferences_with_user_role_decorator.rb +29 -0
  25. data/app/decorators/decidim/decidim_form_helper_decorator.rb +23 -0
  26. data/app/decorators/decidim/invite_user_decorator.rb +56 -0
  27. data/app/decorators/decidim/invite_user_form_decorator.rb +45 -0
  28. data/app/decorators/decidim/newsletter_decorator.rb +20 -0
  29. data/app/decorators/decidim/newsletters_helper_decorator.rb +44 -0
  30. data/app/decorators/decidim/participatory_process_decorator.rb +21 -0
  31. data/app/decorators/decidim/participatory_process_group_decorator.rb +17 -0
  32. data/app/decorators/decidim/participatory_processes/admin/participatory_processes_controller_decorator.rb +25 -0
  33. data/app/decorators/decidim/participatory_processes/create_participatory_process_decorator.rb +19 -0
  34. data/app/decorators/decidim/participatory_processes/permissions_decorator.rb +22 -0
  35. data/app/decorators/decidim/participatory_processes/update_participatory_process_decorator.rb +19 -0
  36. data/app/decorators/decidim/participatory_processes_with_user_role_decorator.rb +27 -0
  37. data/app/decorators/decidim/user_decorator.rb +59 -0
  38. data/app/decorators/lib/decidim/participatory_space_resourceable_decorator.rb +36 -0
  39. data/app/helpers/decidim/admin/user_roles_helper.rb +25 -0
  40. data/app/helpers/decidim/department_admin/application_helper.rb +54 -0
  41. data/app/models/decidim/participatory_space_role_config/department_admin.rb +8 -0
  42. data/app/overrides/decidim/admin/shared/add_radio_buttons_to_filters.rb +6 -0
  43. data/app/packs/entrypoints/decidim_department_admin.js +4 -0
  44. data/app/packs/entrypoints/decidim_department_admin.scss +1 -0
  45. data/app/packs/images/decidim/department_admin/icon.svg +1 -0
  46. data/app/packs/stylesheets/decidim/admin/department_admin.scss +5 -0
  47. data/app/permissions/decidim/assemblies/participatory_space_permissions.rb +14 -0
  48. data/app/permissions/decidim/conferences/participatory_space_permissions.rb +17 -0
  49. data/app/permissions/decidim/department_admin/permissions.rb +240 -0
  50. data/app/permissions/decidim/participatory_processes/participatory_space_permissions.rb +14 -0
  51. data/app/queries/decidim/admin/user_admin_by_space_name_filter.rb +67 -0
  52. data/app/queries/decidim/admin/user_admin_filter.rb +50 -0
  53. data/app/views/decidim/admin/users/_filters.html.erb +28 -0
  54. data/app/views/decidim/admin/users/_form.html.erb +30 -0
  55. data/app/views/decidim/admin/users/index.html.erb +93 -0
  56. data/app/views/decidim/admin/users/show.html.erb +99 -0
  57. data/app/views/decidim/assemblies/admin/assemblies/index.html.erb +147 -0
  58. data/app/views/decidim/conferences/admin/conferences/_form.html.erb +127 -0
  59. data/app/views/decidim/conferences/admin/conferences/index.html.erb +85 -0
  60. data/app/views/decidim/participatory_processes/admin/participatory_processes/index.html.erb +160 -0
  61. data/config/assets.rb +27 -0
  62. data/config/i18n-tasks.yml +148 -0
  63. data/config/initializers/user_roles.rb +4 -0
  64. data/config/locales/ca.yml +61 -0
  65. data/config/locales/cs.yml +46 -0
  66. data/config/locales/en.yml +61 -0
  67. data/config/locales/es.yml +61 -0
  68. data/db/migrate/20190328130102_create_department_admin_areas.rb +10 -0
  69. data/db/migrate/20210420143021_add_area_to_conferences.rb +7 -0
  70. data/lib/decidim/department_admin/admin.rb +10 -0
  71. data/lib/decidim/department_admin/admin_engine.rb +27 -0
  72. data/lib/decidim/department_admin/engine.rb +137 -0
  73. data/lib/decidim/department_admin/test/factories.rb +17 -0
  74. data/lib/decidim/department_admin/version.rb +11 -0
  75. data/lib/decidim/department_admin.rb +15 -0
  76. data/spec/commands/decidim/admin/deliver_newsletter_spec.rb +175 -0
  77. data/spec/commands/decidim/invite_user_spec.rb +42 -0
  78. data/spec/controllers/decidim/admin/users_controller_spec.rb +119 -0
  79. data/spec/factories.rb +4 -0
  80. data/spec/features/check_overrides_spec.rb +11 -0
  81. data/spec/i18n_spec.rb +29 -0
  82. data/spec/models/decidim/area_spec.rb +23 -0
  83. data/spec/permissions/decidim/department_admin/department_admin_permissions_spec.rb +120 -0
  84. data/spec/queries/parent_assemblies_for_select_spec.rb +52 -0
  85. data/spec/spec_helper.rb +47 -0
  86. data/spec/system/admin_explores_processes_spec.rb +53 -0
  87. data/spec/system/admin_invite_department_admin_spec.rb +118 -0
  88. data/spec/system/department_admin_manages_newsletters_spec.rb +237 -0
  89. data/spec/system/department_admin_should_be_able_to_access_admin_dashboard_spec.rb +75 -0
  90. data/spec/system/department_admin_should_be_able_to_manage_assemblies_spec.rb +99 -0
  91. data/spec/system/department_admin_should_be_able_to_manage_conferences_spec.rb +74 -0
  92. data/spec/system/department_admin_should_be_able_to_manage_processes_spec.rb +117 -0
  93. data/spec/system/department_admin_should_be_able_to_see_only_assemblies_from_her_area_spec.rb +46 -0
  94. data/spec/system/department_admin_should_be_able_to_see_only_newsletters_from_her_area_spec.rb +53 -0
  95. data/spec/system/department_admin_should_be_able_to_see_only_processes_from_her_area_spec.rb +50 -0
  96. metadata +229 -0
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DepartmentAdmin
5
+ # Custom helpers, scoped to the department_admin engine.
6
+ #
7
+ module ApplicationHelper
8
+ include ::Decidim::TranslatableAttributes
9
+
10
+ # rubocop: disable Rails/HelperInstanceVariable
11
+ def roles_with_title(user)
12
+ roles_with_title = user.roles.collect { |role| [role, ""] }
13
+ # if user had particiatory processes then add role of process admin
14
+ user_participatory_processes_filtered(user, current_locale, @search_text).each do |participatory_process|
15
+ roles_with_title << ["process_admin", translated_attribute(participatory_process.title)]
16
+ end
17
+ # if user is admin then add admin role
18
+ roles_with_title << ["admin", ""] if user.admin?
19
+ # if user had assemblies then add role of assembly admin
20
+ user_assemblies_filtered(user, current_locale, @search_text).each do |assembly|
21
+ roles_with_title << ["assembly_admin", translated_attribute(assembly.title)]
22
+ end
23
+ # if user had conferences then add role of conference admin
24
+ user_conferences_filtered(user, current_locale, @search_text).each do |conference|
25
+ roles_with_title << ["conference_admin", translated_attribute(conference.title)]
26
+ end
27
+ roles_with_title
28
+ end
29
+
30
+ private
31
+
32
+ def user_participatory_processes_filtered(user, _locale, search_text)
33
+ query = user.participatory_processes
34
+ query = query.where("lower(title->>?) like lower(?)", current_locale, "%#{search_text}%") if @by_process_name && search_text.present?
35
+ query
36
+ end
37
+
38
+ def user_assemblies_filtered(user, _locale, search_text)
39
+ query = user.assemblies
40
+ query = query.where("lower(title->>?) like lower(?)", current_locale, "%#{search_text}%") if @by_process_name && search_text.present?
41
+ query
42
+ end
43
+
44
+ def user_conferences_filtered(user, _locale, search_text)
45
+ return [] unless Decidim::DepartmentAdmin.conferences_defined?
46
+
47
+ query = user.conferences
48
+ query = query.where("lower(title->>?) like lower(?)", current_locale, "%#{search_text}%") if @by_process_name && search_text.present?
49
+ query
50
+ end
51
+ # rubocop: enable Rails/HelperInstanceVariable
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ParticipatorySpaceRoleConfig
5
+ class DepartmentAdmin < Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ Deface::Override.new(virtual_path: "decidim/admin/shared/_filters",
4
+ name: "add_radio_buttons_to_filters",
5
+ insert_after: "div.input-group",
6
+ partial: "decidim/admin/users/filters")
@@ -0,0 +1,4 @@
1
+ // Images
2
+ require.context("../images", true)
3
+
4
+ import "entrypoints/decidim_department_admin.scss";
@@ -0,0 +1 @@
1
+ @import "stylesheets/decidim/admin/department_admin";
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 35"><path d="M17.5 35A17.5 17.5 0 1 1 35 17.5 17.52 17.52 0 0 1 17.5 35zm0-33.06A15.56 15.56 0 1 0 33.06 17.5 15.57 15.57 0 0 0 17.5 1.94zm9.5 13.7H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zm0 3.68H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zM22.26 23H8a1 1 0 0 1 0-1.94h14.26a1 1 0 0 1 0 1.94z"/></svg>
@@ -0,0 +1,5 @@
1
+ .department-admin-filter {
2
+ .dropdown {
3
+ z-index: 0 !important;
4
+ }
5
+ }
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ class ParticipatorySpacePermissions < Decidim::DepartmentAdmin::Permissions
6
+ def initialize(*)
7
+ # This are the same permissions as Decidim's assemblies space.
8
+ # Right now are the same for admin and public views
9
+ self.class.delegate_chain = [Decidim::Assemblies::Permissions]
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Conferences
5
+ parent_class = Decidim::DepartmentAdmin.conferences_defined? ? Decidim::DepartmentAdmin::Permissions : Object
6
+ class ParticipatorySpacePermissions < parent_class
7
+ def initialize(*)
8
+ if Decidim::DepartmentAdmin.conferences_defined?
9
+ # This are the same permissions as Decidim's conferences space.
10
+ # Right now are the same for admin and public views
11
+ self.class.delegate_chain = [Decidim::Conferences::Permissions]
12
+ end
13
+ super
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DepartmentAdmin
5
+ class Permissions < Decidim::DefaultPermissions
6
+ class << self
7
+ attr_writer :delegate_chain, :configurable_checks
8
+ end
9
+
10
+ class << self
11
+ attr_reader :delegate_chain, :configurable_checks
12
+ end
13
+
14
+ def delegate_chain
15
+ self.class.delegate_chain
16
+ end
17
+
18
+ # Applications with custom modules can configure their checks in an initializer.
19
+ # The checks will be executed in `has_permissions?`, and the syntax should be like:
20
+ #
21
+ # <code>
22
+ # Decidim::DepartmentAdmin::Permissions.configurable_checks= [
23
+ # {permission_for?: [:admin, :enter, :space_area, space_name: :courses]}
24
+ # ]
25
+ # </code>
26
+ def configurable_checks
27
+ ::Decidim::DepartmentAdmin::Permissions.configurable_checks || []
28
+ end
29
+
30
+ def permissions
31
+ # byebug if same_action?(permission_action, :admin, :read, :global_moderation)
32
+
33
+ current_permission_action = permission_action
34
+ if permission_action.scope == :admin && user&.department_admin?
35
+ current_permission_action = apply_department_admin_permissions!
36
+ elsif delegate_chain.present?
37
+ # not admin or not a department_admin, use the standard permissions
38
+ delegate_chain.inject(permission_action) do |injected_permission_action, permission_class|
39
+ permission_class.new(user, injected_permission_action, context).permissions
40
+ end
41
+ else
42
+ super
43
+ end
44
+
45
+ current_permission_action
46
+ end
47
+
48
+ def apply_department_admin_permissions!
49
+ # avoid having PermissionCannotBeDisallowedError if permission was already disallowed in the chain
50
+ new_permission_action = permission_action.dup
51
+ if has_permission?(new_permission_action)
52
+ new_permission_action.allow!
53
+ new_permission_action
54
+ elsif delegate_chain.present?
55
+ # if department_admin has no permissions let's apply the default permissions
56
+ delegate_chain.inject(permission_action) do |injected_permission_action, permission_class|
57
+ permission_class.new(user, injected_permission_action, context).permissions
58
+ end
59
+ else
60
+ permission_action
61
+ end
62
+ end
63
+
64
+ def has_permission?(requested_action)
65
+ default_checks = [
66
+ -> { permission_for?(requested_action, :admin, :read, :admin_dashboard) },
67
+ -> { permission_for?(requested_action, :public, :read, :admin_dashboard) },
68
+
69
+ -> { permission_for_current_space?(requested_action) },
70
+
71
+ # PARTICIPATORY PROCESSES
72
+ -> { permission_for?(requested_action, :admin, :enter, :space_area, space_name: :processes) },
73
+ -> { permission_for?(requested_action, :admin, :read, :process_list) },
74
+ -> { permission_for?(requested_action, :admin, :create, :process) },
75
+ -> { same_area_permission_for?(requested_action, :admin, :preview, :process, restricted_rsrc: context[:process]) },
76
+ -> { same_area_permission_for?(requested_action, :admin, :update, :process, restricted_rsrc: context[:process]) },
77
+ -> { same_area_permission_for?(requested_action, :admin, :publish, :process, restricted_rsrc: context[:process]) },
78
+ -> { same_area_permission_for?(requested_action, :admin, :unpublish, :process, restricted_rsrc: context[:process]) },
79
+ -> { permission_for?(requested_action, :admin, :import, :process) },
80
+
81
+ # STEPS
82
+ -> { permission_for?(requested_action, :admin, :read, :process_step) },
83
+ -> { permission_for?(requested_action, :admin, :create, :process_step) },
84
+ -> { same_area_permission_for?(requested_action, :admin, :update, :process_step, restricted_rsrc: context[:process_step]&.participatory_process) },
85
+ -> { same_area_permission_for?(requested_action, :admin, :activate, :process_step, restricted_rsrc: context[:process_step]&.participatory_process) },
86
+ -> { same_area_permission_for?(requested_action, :admin, :destroy, :process_step, restricted_rsrc: context[:process_step]&.participatory_process) },
87
+ # COMPONENTS
88
+ -> { permission_for?(requested_action, :admin, :read, :component) },
89
+ -> { permission_for?(requested_action, :admin, :create, :component) },
90
+ -> { same_area_permission_for?(requested_action, :admin, :update, :component, restricted_rsrc: context[:component]&.participatory_space) },
91
+ -> { same_area_permission_for?(requested_action, :admin, :manage, :component, restricted_rsrc: context[:component]&.participatory_space) },
92
+ -> { same_area_permission_for?(requested_action, :admin, :destroy, :component, restricted_rsrc: context[:component]&.participatory_space) },
93
+ -> { same_area_permission_for?(requested_action, :admin, :publish, :component, restricted_rsrc: context[:component]&.participatory_space) },
94
+ -> { same_area_permission_for?(requested_action, :admin, :unpublish, :component, restricted_rsrc: context[:component]&.participatory_space) },
95
+ -> { same_area_permission_for?(requested_action, :admin, :export, :component_data, restricted_rsrc: context[:component]&.participatory_space) },
96
+ # CATEGORIES
97
+ -> { permission_for?(requested_action, :admin, :read, :category) },
98
+ -> { permission_for?(requested_action, :admin, :create, :category) },
99
+ -> { same_area_permission_for?(requested_action, :admin, :update, :category, restricted_rsrc: context[:category]&.participatory_space) },
100
+ -> { same_area_permission_for?(requested_action, :admin, :destroy, :category, restricted_rsrc: context[:category]&.participatory_space) },
101
+ # ATTACHMENT COLLECTIONS
102
+ -> { permission_for?(requested_action, :admin, :read, :attachment_collection) },
103
+ -> { permission_for?(requested_action, :admin, :create, :attachment_collection) },
104
+ -> { same_area_permission_for?(requested_action, :admin, :read, :attachment_collection, restricted_rsrc: context[:attachment_collection]&.collection_for) },
105
+ -> { same_area_permission_for?(requested_action, :admin, :update, :attachment_collection, restricted_rsrc: context[:attachment_collection]&.collection_for) },
106
+ -> { same_area_permission_for?(requested_action, :admin, :destroy, :attachment_collection, restricted_rsrc: context[:attachment_collection]&.collection_for) },
107
+ # ATTACHMENTS
108
+ -> { permission_for?(requested_action, :admin, :read, :attachment) },
109
+ -> { permission_for?(requested_action, :admin, :create, :attachment) },
110
+ -> { same_area_permission_for?(requested_action, :admin, :update, :attachment, restricted_rsrc: context[:attachment]&.attached_to) },
111
+ -> { same_area_permission_for?(requested_action, :admin, :destroy, :attachment, restricted_rsrc: context[:attachment]&.attached_to) },
112
+ # INVITE PROCESS ADMIN: USER ROLES
113
+ -> { permission_for?(requested_action, :admin, :read, :process_user_role) },
114
+ -> { permission_for?(requested_action, :admin, :create, :process_user_role) },
115
+ -> { permission_for?(requested_action, :admin, :update, :process_user_role) },
116
+ -> { permission_for?(requested_action, :admin, :destroy, :process_user_role) },
117
+ # SPACE PRIVATE USERS
118
+ -> { permission_for?(requested_action, :admin, :read, :space_private_user) },
119
+ -> { permission_for?(requested_action, :admin, :create, :space_private_user) },
120
+ -> { same_area_permission_for?(requested_action, :admin, :update, :space_private_user, restricted_rsrc: context[:private_user]&.privatable_to) },
121
+ -> { same_area_permission_for?(requested_action, :admin, :destroy, :space_private_user, restricted_rsrc: context[:private_user]&.privatable_to) },
122
+ -> { same_area_permission_for?(requested_action, :admin, :invite, :space_private_user, restricted_rsrc: context[:private_user]&.privatable_to) },
123
+ # MODERATIONS
124
+ -> { permission_for?(requested_action, :admin, :read, :moderation) },
125
+ -> { permission_for?(requested_action, :admin, :unreport, :moderation) },
126
+ -> { permission_for?(requested_action, :admin, :hide, :moderation) },
127
+ -> { permission_for?(requested_action, :admin, :unhide, :moderation) },
128
+
129
+ # ASSEMBLIES
130
+ -> { permission_for?(requested_action, :admin, :enter, :space_area, space_name: :assemblies) },
131
+ -> { permission_for?(requested_action, :admin, :read, :assembly_list) },
132
+ -> { permission_for?(requested_action, :admin, :create, :assembly) },
133
+ -> { same_area_permission_for?(requested_action, :admin, :update, :assembly, restricted_rsrc: context[:assembly]) },
134
+ -> { same_area_permission_for?(requested_action, :admin, :publish, :assembly, restricted_rsrc: context[:assembly]) },
135
+ -> { same_area_permission_for?(requested_action, :admin, :unpublish, :assembly, restricted_rsrc: context[:assembly]) },
136
+ -> { permission_for?(requested_action, :admin, :import, :assembly) },
137
+ # Assemly Admin: USER ROLES
138
+ -> { permission_for?(requested_action, :admin, :index, :assembly_user_role) },
139
+ -> { permission_for?(requested_action, :admin, :read, :assembly_user_role) },
140
+ -> { permission_for?(requested_action, :admin, :create, :assembly_user_role) },
141
+ -> { permission_for?(requested_action, :admin, :update, :assembly_user_role) },
142
+ -> { permission_for?(requested_action, :admin, :invite, :assembly_user_role) },
143
+ -> { permission_for?(requested_action, :admin, :destroy, :assembly_user_role) },
144
+
145
+ # Assembly Members
146
+ -> { same_area_permission_for?(requested_action, :admin, :read, :assembly_member, restricted_rsrc: context[:assembly]) },
147
+ -> { permission_for?(requested_action, :admin, :index, :assembly_member) },
148
+ -> { permission_for?(requested_action, :admin, :create, :assembly_member) },
149
+ -> { permission_for?(requested_action, :admin, :update, :assembly_member) },
150
+ -> { permission_for?(requested_action, :admin, :destroy, :assembly_member) },
151
+ # other assembly_member permissions are setted via Decidim::Assemblies::AssembliesWithUserRole decorator
152
+
153
+ # NEWSLETTER
154
+ -> { permission_for?(requested_action, :admin, :index, :newsletter) },
155
+ -> { permission_for?(requested_action, :admin, :read, :newsletter) },
156
+ -> { permission_for?(requested_action, :admin, :create, :newsletter) },
157
+ -> { same_area_permission_for?(requested_action, :admin, :update, :newsletter, restricted_rsrc: context[:newsletter]) },
158
+ -> { same_area_permission_for?(requested_action, :admin, :destroy, :newsletter, restricted_rsrc: context[:newsletter]) },
159
+
160
+ # CONFERENCES
161
+ -> { permission_for?(requested_action, :admin, :enter, :space_area, space_name: :conferences) },
162
+ -> { permission_for?(requested_action, :admin, :read, :conference_list) },
163
+ -> { permission_for?(requested_action, :admin, :create, :conference) },
164
+ -> { permission_for?(requested_action, :admin, :preview, :conference) },
165
+ -> { same_area_permission_for?(requested_action, :admin, :update, :conference, restricted_rsrc: context[:conference]) },
166
+ # Conference Admin: USER ROLES
167
+ -> { permission_for?(requested_action, :admin, :index, :conference_user_role) },
168
+ -> { permission_for?(requested_action, :admin, :read, :conference_user_role) },
169
+ -> { permission_for?(requested_action, :admin, :create, :conference_user_role) },
170
+ -> { permission_for?(requested_action, :admin, :update, :conference_user_role) },
171
+ -> { permission_for?(requested_action, :admin, :invite, :conference_user_role) },
172
+ -> { permission_for?(requested_action, :admin, :destroy, :conference_user_role) },
173
+
174
+ # USERS ADMINISTRATORS
175
+ -> { permission_for?(requested_action, :admin, :enter, :space_area, space_name: :users) },
176
+ -> { permission_for?(requested_action, :admin, :read, :admin_user) },
177
+ -> { permission_for?(requested_action, :admin, :create, :admin_user) },
178
+ -> { permission_for?(requested_action, :admin, :read, :managed_user) },
179
+ # USERS PARTICIPANTS
180
+ -> { permission_for?(requested_action, :admin, :index, :officialization) },
181
+ -> { permission_for?(requested_action, :admin, :read, :officialization) },
182
+ -> { permission_for?(requested_action, :admin, :create, :officialization) },
183
+ ]
184
+ default_checks.any?(&:call) || any_configurable_check?(requested_action)
185
+ end
186
+
187
+ def any_configurable_check?(requested_action)
188
+ configurable_checks.any? do |check|
189
+ check = check.entries.first
190
+ method = check.first
191
+ args = check.last
192
+ next unless [:permission_for?, :same_area_permission_for].include?(method)
193
+
194
+ send(method, requested_action, *args)
195
+ end
196
+ end
197
+
198
+ ALLOWED_SPACES = ["Decidim::ParticipatoryProcess", "Decidim::Assembly", "Decidim::Conference"].freeze
199
+ def permission_for_current_space?(permission_action)
200
+ has = permission_for?(permission_action, :admin, :read, :participatory_space)
201
+ has ||= permission_for?(permission_action, :public, :read, :participatory_space)
202
+ has &&= ALLOWED_SPACES.include?(context[:current_participatory_space].class.name)
203
+ has
204
+ end
205
+
206
+ # Does user have permission for the specified scope/action/subject?
207
+ def permission_for?(requested_action, scope, action, subject, expected_context = {})
208
+ same_action?(requested_action, scope, action, subject, expected_context)
209
+ end
210
+
211
+ # Does user have permission for the specified scope/action/subject?
212
+ # Also check if the resource in the context for with the key defined by `area_restricted_rsrc`
213
+ # has the same area as current user.
214
+ def same_area_permission_for?(requested_action, scope, action, subject, restricted_rsrc:)
215
+ if restricted_rsrc.respond_to?(:area) || restricted_rsrc.nil?
216
+ is = same_action?(requested_action, scope, action, subject)
217
+ is &&= in_same_area?(restricted_rsrc)
218
+ is
219
+ elsif restricted_rsrc.try(:participatory_space).try(:area).present?
220
+ same_area_permission_for?(requested_action, scope, action, subject, restricted_rsrc: restricted_rsrc.try(:participatory_space))
221
+ else
222
+ permission_for?(requested_action, scope, action, subject)
223
+ end
224
+ end
225
+
226
+ # Is current action requesting permissions for the specified scope/action/subject?
227
+ def same_action?(requested_action, scope, action, subject, expected_context = {})
228
+ is = requested_action.matches?(scope, action, subject)
229
+ expected_context.each_pair do |key, expected_value|
230
+ is &&= (context.try(:[], key) == expected_value)
231
+ end
232
+ is
233
+ end
234
+
235
+ def in_same_area?(resource)
236
+ user.areas.include? resource&.area
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ParticipatoryProcesses
5
+ class ParticipatorySpacePermissions < Decidim::DepartmentAdmin::Permissions
6
+ def initialize(*)
7
+ # This are the same permissions as Decidim's processes space.
8
+ # Right now are the same for admin and public views
9
+ self.class.delegate_chain = [Decidim::ParticipatoryProcesses::Permissions]
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # A class used to filter users by processes that hey administer
6
+ class UserAdminBySpaceNameFilter < Decidim::Query
7
+ # termsc - text to filter users by
8
+ def self.for(term = nil, organization = nil, current_locale = :ca)
9
+ new(term, organization, current_locale).query
10
+ end
11
+
12
+ # Initializes the class.
13
+ #
14
+ # term - text to filter users by name or email
15
+ def initialize(term = nil, organization = nil, current_locale = :ca)
16
+ @term = term
17
+ @organization = organization
18
+ @current_locale = current_locale
19
+ super(Decidim::User.space_admins(@organization))
20
+ end
21
+
22
+ def query
23
+ filter_by_processes_term
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :term, :current_locale
29
+
30
+ def filter_by_processes_term
31
+ return @scope if term.blank?
32
+
33
+ containing_space_name = "%#{term}%"
34
+
35
+ query = <<-EOSQL
36
+ (id in (select decidim_user_id
37
+ from decidim_participatory_process_user_roles
38
+ where decidim_participatory_process_id in (
39
+ select id
40
+ from decidim_participatory_processes
41
+ where lower(title->>?) like lower(?)))
42
+ or id in ( select decidim_user_id
43
+ from decidim_assembly_user_roles
44
+ where decidim_assembly_id in (
45
+ select id
46
+ from decidim_assemblies
47
+ where lower(title->>?) like lower(?)))
48
+ #{if Decidim::DepartmentAdmin.conferences_defined?
49
+ "or id in ( select decidim_user_id
50
+ from decidim_conference_user_roles
51
+ where decidim_conference_id in (
52
+ select id
53
+ from decidim_conferences
54
+ where lower(title->>?) like lower(?))))" else
55
+ ")"
56
+ end}
57
+ EOSQL
58
+
59
+ if Decidim::DepartmentAdmin.conferences_defined?
60
+ @scope.where(query, current_locale, containing_space_name, current_locale, containing_space_name, current_locale, containing_space_name)
61
+ else
62
+ @scope.where(query, current_locale, containing_space_name, current_locale, containing_space_name)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # A class used to filter users by whitelisted scope or searches on their
6
+ # name
7
+ class UserAdminFilter < Decidim::Query
8
+ # scope - the ActiveRecord::Relation of users to be filtered
9
+ # termsc - text to filter users by
10
+ # role - evaluation role to be used as a filter
11
+ def self.for(scope, term = nil, role = nil, organization = nil)
12
+ new(scope, term, role, organization).query
13
+ end
14
+
15
+ # Initializes the class.
16
+ #
17
+ # scope - the ActiveRecord::Relation of users to be filtered
18
+ # term - text to filter users by name or email
19
+ # role - users role, must be defined as a scope in the user model
20
+ def initialize(scope, term = nil, role = nil, organization = nil)
21
+ @scope = scope
22
+ @term = term
23
+ @role = role
24
+ @organization = organization
25
+ super(scope)
26
+ end
27
+
28
+ def query
29
+ filter_by_role(scope)
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :term, :role, :scope
35
+
36
+ def filter_by_role(users)
37
+ case role
38
+ when "space_admin"
39
+ Decidim::User.space_admins(@organization)
40
+ when "admin"
41
+ users.where('"decidim_users"."admin" = ?', true)
42
+ when "department_admin", "user_manager"
43
+ users.where("? = any(roles)", role)
44
+ else
45
+ users.or(Decidim::User.space_admins(@organization))
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,28 @@
1
+ <div class='department-admin-filter filter row'>
2
+ <div class='column medium-3'>
3
+ <span class='dropdown-menu-inverted_label'><%= t('.filter_by') %> :</span>
4
+ <ul class='dropdown menu dropdown-inverted' data-dropdown-menu data-close-on-click-inside='false'>
5
+ <li class='is-dropdown-submenu-parent'>
6
+ <a href='#'>
7
+ <% if @role.present? %>
8
+ <%= t("models.user.fields.roles.#{@role}", scope: 'decidim.admin') %>
9
+ <% else %>
10
+ <%= t('.filter.all') %>
11
+ <% end %>
12
+ </a>
13
+ <ul class='menu is-dropdown-submenu'>
14
+ <% Decidim::User::Roles.all.map do |role| %>
15
+ <li><%= link_to t("models.user.fields.roles.#{role}", scope: 'decidim.admin'), url_for(role: role, q: ransack_params) %></li>
16
+ <% end %>
17
+ <li><%= link_to t('models.user.fields.roles.space_admin', scope: 'decidim.admin'), url_for(role: 'space_admin', q: ransack_params) %></li>
18
+ <li><%= link_to t('.filter.all'), url_for(q: ransack_params) %></li>
19
+ </ul>
20
+ </li>
21
+ </ul>
22
+ <%= t('models.user.fields.search_question', scope: 'decidim.admin') %>
23
+ <input type='radio' id='processes_filter' name='filter_search' value='by_process_name' <%= @by_process_name ? "checked" : "" %> style='margin-left: 20px'>
24
+ <label for='processes_filter'><%= t('models.user.fields.search_field_processes', scope: 'decidim.admin') %></label>
25
+ <input type='radio' id='query_filter' name='filter_search' value='query' <%= params[:filter_search].blank? || params[:filter_search] == "query" ? "checked" : "" %> >
26
+ <label for='query_filter'><%= t('models.user.fields.search_field_admins', scope: 'decidim.admin') %></label>
27
+ </div>
28
+ </div>
@@ -0,0 +1,30 @@
1
+ <div class="row column">
2
+ <%= form.text_field :name, label: t(".name") %>
3
+ </div>
4
+
5
+ <div class="row column">
6
+ <%= form.email_field :email, label: t(".email") %>
7
+ </div>
8
+
9
+ <div class="row column">
10
+ <%= form.select :role, @form.available_roles_for_select, label: t(".role") %>
11
+ </div>
12
+
13
+ <div id="area-block" class="row column">
14
+ <%= form.select :area_id,
15
+ @form.available_areas_for_select,
16
+ { label: t(".area") },
17
+ { disabled: current_user.roles.include?('department_admin') }
18
+ %>
19
+ </div>
20
+
21
+ <%= javascript_tag do %>
22
+ $('#user_role').on('change', function(ev) {
23
+ var opt= ev.target
24
+ if(opt.value == 'department_admin') {
25
+ $('#area-block').show()
26
+ } else {
27
+ $('#area-block').hide()
28
+ }
29
+ })
30
+ <% end -%>
@@ -0,0 +1,93 @@
1
+ <% add_decidim_page_title(t("decidim.admin.titles.users")) %>
2
+ <div class="card">
3
+ <div class="card-divider">
4
+ <h2 class="card-title">
5
+ <%= t "decidim.admin.titles.users" %>
6
+ <% if allowed_to? :create, :admin_user %>
7
+ <%= link_to t("actions.user.new", scope: "decidim.admin"), [:new, :user], class: "button tiny button--title" %>
8
+ <% end %>
9
+ </h2>
10
+ </div>
11
+ <%= admin_filter_selector %>
12
+ <div class="card-section">
13
+ <div class="table-scroll">
14
+ <table class="table-list">
15
+ <thead>
16
+ <tr>
17
+ <th><%= sort_link(query, :role, t("models.user.fields.role", scope: "decidim.admin"), default_order: :desc) %></th>
18
+ <th><%= sort_link(query, :name, t("models.user.fields.name", scope: "decidim.admin"), default_order: :desc) %></th>
19
+ <th><%= sort_link(query, :email, t("models.user.fields.email", scope: "decidim.admin"), default_order: :desc) %></th>
20
+ <th><%= sort_link(query, :department, t("models.user.fields.department", scope: "decidim.admin"), default_order: :desc) %></th>
21
+ <th><%= sort_link(query, :spaces, t("models.user.fields.spaces", scope: "decidim.admin"), default_order: :desc) %></th>
22
+ <th><%= sort_link(query, :invitation_sent_at, t("models.user.fields.invitation_sent_at", scope: "decidim.admin"), default_order: :desc) %></th>
23
+ <th><%= sort_link(query, :invitation_accepted_at, t("models.user.fields.invitation_accepted_at", scope: "decidim.admin"), default_order: :desc) %></th>
24
+ <th><%= sort_link(query, :last_sign_in_at, t("models.user.fields.last_sign_in_at", scope: "decidim.admin"), default_order: :desc) %></th>
25
+ <th><%= sort_link(query, :created_at, t("models.user.fields.created_at", scope: "decidim.admin"), default_order: :desc) %></th>
26
+ <th></th>
27
+ </tr>
28
+ </thead>
29
+ <tbody>
30
+ <% filtered_collection.each do |user| %>
31
+ <% roles_with_title(user).each do |role_and_title| %>
32
+ <% if @role.blank? || (@role == role_and_title[0] || @role == 'space_admin' && (role_and_title[0] == 'process_admin' || role_and_title[0] == 'assembly_admin' || role_and_title[0] == 'conference_admin')) %>
33
+ <tr data-user-id="<%= user.id %>">
34
+ <td>
35
+ <% if role_and_title.nil? || role_and_title[0].empty? %>
36
+ <%= t("models.user.fields.roles.#{user.active_role}", scope: "decidim.admin") %>
37
+ <% else %>
38
+ <%= t("models.user.fields.roles.#{role_and_title[0] }", scope: "decidim.admin") %>
39
+ <% end %>
40
+ </td>
41
+ <td><%= user.name %></td>
42
+ <td><%= user.email %></td>
43
+ <td>
44
+ <% if user.department_admin? %>
45
+ <%= translated_attribute(user.areas.first&.name) %>
46
+ <% end %>
47
+ </td>
48
+ <td>
49
+ <% if !role_and_title.nil? && !role_and_title[1].empty? %>
50
+ <%= role_and_title[1] %>
51
+ <% end %>
52
+ </td>
53
+ <td>
54
+ <% if user.invitation_sent_at %>
55
+ <%= l user.invitation_sent_at, format: :short %>
56
+ <% end %>
57
+ </td>
58
+ <td>
59
+ <% if user.invitation_accepted_at %>
60
+ <%= l user.invitation_accepted_at, format: :short %>
61
+ <% end %>
62
+ </td>
63
+ <td>
64
+ <% if user.last_sign_in_at %>
65
+ <%= l user.last_sign_in_at, format: :short %>
66
+ <% end %>
67
+ </td>
68
+ <td><%= l user.created_at, format: :short %></td>
69
+ <td class="table-list__actions">
70
+ <% if user.participatory_processes.size > 0 || user.assemblies.size > 0 || (Decidim::DepartmentAdmin.conferences_defined? && user.conferences.size > 0) %>
71
+ <% if allowed_to? :preview, :user, user: user %>
72
+ <%= icon_link_to "eye", user_path(user.id), t("actions.preview", scope: "decidim.admin"), class: "action-icon--preview" %>
73
+ <% end %>
74
+ <% end %>
75
+
76
+ <% if allowed_to?(:invite, :admin_user, user: user) && user.invited_to_sign_up? %>
77
+ <%= icon_link_to "reload", [:resend_invitation, user], t("actions.resend_invitation", scope: "decidim.admin"), class: "resend-invitation", method: :post %>
78
+ <% end %>
79
+
80
+ <% if allowed_to? :destroy, :admin_user, user: user %>
81
+ <%= icon_link_to "circle-x", user, t("actions.destroy", scope: "decidim.admin"), class: "action-icon--remove", method: :delete, data: { confirm: t("actions.confirm_destroy", scope: "decidim.admin") } %>
82
+ <% end %>
83
+ </td>
84
+ </tr>
85
+ <% end %>
86
+ <% end %>
87
+ <% end %>
88
+ </tbody>
89
+ </table>
90
+ <%= paginate @users, theme: "decidim" %>
91
+ </div>
92
+ </div>
93
+ </div>