decidim-department_admin 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE-AGPLv3.txt +661 -0
- data/README.md +132 -0
- data/Rakefile +9 -0
- data/app/controllers/decidim/department_admin/admin/application_controller.rb +15 -0
- data/app/controllers/decidim/department_admin/application_controller.rb +13 -0
- data/app/decorators/decidim/admin/newsletters_controller_decorator.rb +30 -0
- data/app/decorators/decidim/admin/users_controller_decorator.rb +128 -0
- data/app/decorators/decidim/area_decorator.rb +44 -0
- data/app/decorators/decidim/assemblies/admin/assemblies_controller_decorator.rb +25 -0
- data/app/decorators/decidim/assemblies/admin/assemblies_helper_decorator.rb +24 -0
- data/app/decorators/decidim/assemblies/create_assembly_decorator.rb +19 -0
- data/app/decorators/decidim/assemblies/parent_assemblies_for_select_decorator.rb +38 -0
- data/app/decorators/decidim/assemblies/permissions_decorator.rb +22 -0
- data/app/decorators/decidim/assemblies/update_assembly_decorator.rb +19 -0
- data/app/decorators/decidim/assemblies_decorator.rb +21 -0
- data/app/decorators/decidim/assemblies_with_user_role_decorator.rb +27 -0
- data/app/decorators/decidim/conference_form_decorator.rb +23 -0
- data/app/decorators/decidim/conferences/admin/conferences_controller_decorator.rb +27 -0
- data/app/decorators/decidim/conferences/create_conference_decorator.rb +51 -0
- data/app/decorators/decidim/conferences/permissions_decorator.rb +24 -0
- data/app/decorators/decidim/conferences/update_conference_decorator.rb +21 -0
- data/app/decorators/decidim/conferences_decorator.rb +27 -0
- data/app/decorators/decidim/conferences_with_user_role_decorator.rb +29 -0
- data/app/decorators/decidim/decidim_form_helper_decorator.rb +23 -0
- data/app/decorators/decidim/invite_user_decorator.rb +56 -0
- data/app/decorators/decidim/invite_user_form_decorator.rb +45 -0
- data/app/decorators/decidim/newsletter_decorator.rb +20 -0
- data/app/decorators/decidim/newsletters_helper_decorator.rb +44 -0
- data/app/decorators/decidim/participatory_process_decorator.rb +21 -0
- data/app/decorators/decidim/participatory_process_group_decorator.rb +17 -0
- data/app/decorators/decidim/participatory_processes/admin/participatory_processes_controller_decorator.rb +25 -0
- data/app/decorators/decidim/participatory_processes/create_participatory_process_decorator.rb +19 -0
- data/app/decorators/decidim/participatory_processes/permissions_decorator.rb +22 -0
- data/app/decorators/decidim/participatory_processes/update_participatory_process_decorator.rb +19 -0
- data/app/decorators/decidim/participatory_processes_with_user_role_decorator.rb +27 -0
- data/app/decorators/decidim/user_decorator.rb +59 -0
- data/app/decorators/lib/decidim/participatory_space_resourceable_decorator.rb +36 -0
- data/app/helpers/decidim/admin/user_roles_helper.rb +25 -0
- data/app/helpers/decidim/department_admin/application_helper.rb +54 -0
- data/app/models/decidim/participatory_space_role_config/department_admin.rb +8 -0
- data/app/overrides/decidim/admin/shared/add_radio_buttons_to_filters.rb +6 -0
- data/app/packs/entrypoints/decidim_department_admin.js +4 -0
- data/app/packs/entrypoints/decidim_department_admin.scss +1 -0
- data/app/packs/images/decidim/department_admin/icon.svg +1 -0
- data/app/packs/stylesheets/decidim/admin/department_admin.scss +5 -0
- data/app/permissions/decidim/assemblies/participatory_space_permissions.rb +14 -0
- data/app/permissions/decidim/conferences/participatory_space_permissions.rb +17 -0
- data/app/permissions/decidim/department_admin/permissions.rb +240 -0
- data/app/permissions/decidim/participatory_processes/participatory_space_permissions.rb +14 -0
- data/app/queries/decidim/admin/user_admin_by_space_name_filter.rb +67 -0
- data/app/queries/decidim/admin/user_admin_filter.rb +50 -0
- data/app/views/decidim/admin/users/_filters.html.erb +28 -0
- data/app/views/decidim/admin/users/_form.html.erb +30 -0
- data/app/views/decidim/admin/users/index.html.erb +93 -0
- data/app/views/decidim/admin/users/show.html.erb +99 -0
- data/app/views/decidim/assemblies/admin/assemblies/index.html.erb +147 -0
- data/app/views/decidim/conferences/admin/conferences/_form.html.erb +127 -0
- data/app/views/decidim/conferences/admin/conferences/index.html.erb +85 -0
- data/app/views/decidim/participatory_processes/admin/participatory_processes/index.html.erb +160 -0
- data/config/assets.rb +27 -0
- data/config/i18n-tasks.yml +148 -0
- data/config/initializers/user_roles.rb +4 -0
- data/config/locales/ca.yml +61 -0
- data/config/locales/cs.yml +46 -0
- data/config/locales/en.yml +61 -0
- data/config/locales/es.yml +61 -0
- data/db/migrate/20190328130102_create_department_admin_areas.rb +10 -0
- data/db/migrate/20210420143021_add_area_to_conferences.rb +7 -0
- data/lib/decidim/department_admin/admin.rb +10 -0
- data/lib/decidim/department_admin/admin_engine.rb +27 -0
- data/lib/decidim/department_admin/engine.rb +137 -0
- data/lib/decidim/department_admin/test/factories.rb +17 -0
- data/lib/decidim/department_admin/version.rb +11 -0
- data/lib/decidim/department_admin.rb +15 -0
- data/spec/commands/decidim/admin/deliver_newsletter_spec.rb +175 -0
- data/spec/commands/decidim/invite_user_spec.rb +42 -0
- data/spec/controllers/decidim/admin/users_controller_spec.rb +119 -0
- data/spec/factories.rb +4 -0
- data/spec/features/check_overrides_spec.rb +11 -0
- data/spec/i18n_spec.rb +29 -0
- data/spec/models/decidim/area_spec.rb +23 -0
- data/spec/permissions/decidim/department_admin/department_admin_permissions_spec.rb +120 -0
- data/spec/queries/parent_assemblies_for_select_spec.rb +52 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/system/admin_explores_processes_spec.rb +53 -0
- data/spec/system/admin_invite_department_admin_spec.rb +118 -0
- data/spec/system/department_admin_manages_newsletters_spec.rb +237 -0
- data/spec/system/department_admin_should_be_able_to_access_admin_dashboard_spec.rb +75 -0
- data/spec/system/department_admin_should_be_able_to_manage_assemblies_spec.rb +99 -0
- data/spec/system/department_admin_should_be_able_to_manage_conferences_spec.rb +74 -0
- data/spec/system/department_admin_should_be_able_to_manage_processes_spec.rb +117 -0
- data/spec/system/department_admin_should_be_able_to_see_only_assemblies_from_her_area_spec.rb +46 -0
- data/spec/system/department_admin_should_be_able_to_see_only_newsletters_from_her_area_spec.rb +53 -0
- data/spec/system/department_admin_should_be_able_to_see_only_processes_from_her_area_spec.rb +50 -0
- 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 @@
|
|
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,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>
|