PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.14
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.14
6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 All 196 releases
fluentform / app / Services / Manager / ManagerService.php

ManagerService.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.14, at app/Services/Manager/ManagerService.php

248 lines 8.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentForm\App\Services\Manager;
4
5 use FluentForm\App\Helpers\Helper;
6 use FluentForm\App\Modules\Acl\Acl;
7 use FluentForm\Framework\Support\Arr;
8 use FluentForm\Framework\Validator\ValidationException;
9 use FluentForm\Framework\Validator\Validator;
10
11 class ManagerService
12 {
13 public function getManagers($attributes = [])
14 {
15 $limit = Arr::get($attributes, 'per_page', 10);
16 $page = Arr::get($attributes, 'page', 1);
17 $offset = 1 == $page ? 0 : ($page - 1) * $limit;
18
19 $query = new \WP_User_Query([
20 'meta_key' => '_fluent_forms_has_role',
21 'meta_value' => 1,
22 'meta_compare' => '=',
23 'number' => $limit,
24 'offset' => $offset,
25 ]);
26
27 $managers = [];
28
29 foreach ($query->get_results() as $user) {
30 $allowedForms = FormManagerService::getUserAllowedFormsScope($user->ID);
31 $hasSpecificFormsPermission = false !== $allowedForms;
32 $managers[] = [
33 'id' => $user->ID,
34 'first_name' => $user->first_name,
35 'last_name' => $user->last_name,
36 'email' => $user->user_email,
37 'permissions' => Acl::getUserPermissions($user),
38 'forms' => false !== $allowedForms ? array_values($allowedForms) : false,
39 'roles' => $this->getUserRoles($user->roles),
40 'has_specific_forms_permission'=> $hasSpecificFormsPermission ? 'yes' : 'no',
41 ];
42 }
43
44 $total = $query->get_total();
45
46 $forms = Helper::getForms();
47 if ($forms) {
48 Arr::forget($forms, 0);
49 }
50
51 return ([
52 'managers' => $managers,
53 'total' => $total,
54 'permissions' => Acl::getReadablePermissions(),
55 'forms' => $forms,
56 ]);
57 }
58
59 public function addManager($attributes = [])
60 {
61 $manager = Arr::get($attributes, 'manager');
62
63 $this->validate($manager);
64
65 $permissions = Arr::get($manager, 'permissions', []);
66
67 // Defense in depth: only a full WordPress administrator may grant the
68 // crown-jewel `fluentform_full_access` permission. A holder of that ACL
69 // permission is not necessarily a WP admin (managers can be delegated),
70 // so without this a compromised full-access session — e.g. a script
71 // running via XSS in a privileged session — could mint new full-access
72 // managers and escalate. Lesser permission grants are unaffected.
73 if (in_array('fluentform_full_access', (array) $permissions, true) && !current_user_can('manage_options')) {
74 $permissions = array_values(array_filter((array) $permissions, function ($permission) {
75 return 'fluentform_full_access' !== $permission;
76 }));
77 }
78
79 $user = get_user_by('email', $manager['email']);
80
81 if (!$user) {
82 throw new ValidationException('', 0, null, ['message' => 'Please Provide Valid Email']);
83 }
84
85 Acl::attachPermissions($user, $permissions);
86
87 update_user_meta($user->ID, '_fluent_forms_has_role', 1);
88
89 $hasSpecificFormsPermission = 'yes' === Arr::get($manager, 'has_specific_forms_permission');
90 $allowedForms = array_values(array_filter(array_map('intval', (array) Arr::get($manager, 'forms', []))));
91
92 // The UI always sends this flag, so an absent one is a partial payload, not a request to clear.
93 $restrictionWasSubmitted = null !== Arr::get($manager, 'has_specific_forms_permission');
94
95 // Keep an empty selection unrestricted so the manager UI's
96 // "leave blank for all forms" behavior matches the saved ACL state.
97 if ($hasSpecificFormsPermission && $allowedForms) {
98 FormManagerService::updateHasSpecificFormsPermission($user->ID, 'yes');
99 FormManagerService::addUserAllowedForms($allowedForms, $user->ID);
100 } elseif ($restrictionWasSubmitted) {
101 FormManagerService::updateHasSpecificFormsPermission($user->ID, 'no');
102 FormManagerService::deleteUserAllowedForms($user->ID);
103 }
104
105 $updatedUser = [
106 'id' => $user->ID,
107 'first_name' => $user->first_name,
108 'last_name' => $user->last_name,
109 'email' => $user->user_email,
110 'permissions' => Acl::getUserPermissions($user),
111 ];
112
113 return ([
114 'message' => __('Manager has been saved.', 'fluentform'),
115 'manager' => $updatedUser,
116 ]);
117 }
118
119 public function removeManager($attributes = [])
120 {
121 $userID = intval(Arr::get($attributes, 'id'));
122 $user = get_user_by('ID', $userID);
123
124 if (!$user) {
125 return ([
126 'message' => __('Associate user could not be found', 'fluentform'),
127 ]);
128 }
129
130 // Removal deletes _fluent_forms_has_role, which is what suppresses the role fallback.
131 if (get_current_user_id() === $user->ID) {
132 return ([
133 'message' => __('You cannot remove yourself as a manager.', 'fluentform'),
134 ]);
135 }
136
137 // Mirrors the grant-side guard at :73.
138 if (in_array('fluentform_full_access', (array) Acl::getUserPermissions($user), true) && !current_user_can('manage_options')) {
139 return ([
140 'message' => __('You do not have permission to remove a full access manager.', 'fluentform'),
141 ]);
142 }
143
144 Acl::attachPermissions($user, []);
145
146 delete_user_meta($user->ID, '_fluent_forms_has_role');
147
148 $deletedUser = [
149 'id' => $user->ID,
150 'first_name' => $user->first_name,
151 'last_name' => $user->last_name,
152 'email' => $user->user_email,
153 'permissions' => Acl::getUserPermissions($user),
154 ];
155
156 return ([
157 'message' => __('Manager has been removed.', 'fluentform'),
158 'manager' => $deletedUser,
159 ]);
160 }
161
162 private function validate($manager)
163 {
164 $rules = [
165 'permissions' => 'required',
166 'email' => 'required|email',
167 ];
168
169 $validatorInstance = new Validator();
170 $validator = $validatorInstance->make($manager, $rules);
171
172 $errors = null;
173
174 if ($validator->validate()->fails()) {
175 $errors = $validator->errors();
176 }
177
178 if (!isset($errors['email'])) {
179 $user = get_user_by('email', $manager['email']);
180
181 if (!$user) {
182 $errors['email'] = [
183 'no_user' => __('We could not found any user with this email.', 'fluentform'),
184 ];
185 }
186 }
187
188 if (!isset($errors['permissions'])) {
189 $message = $this->dependencyValidate($manager['permissions']);
190
191 if ($message) {
192 $errors['permissions'] = [
193 'dependency' => $message,
194 ];
195 }
196 }
197
198 if ($errors) {
199 // Escape all error messages before throwing
200 $escapedErrors = array_map(function ($errorMessages) {
201 if (is_array($errorMessages)) {
202 return array_map('esc_html', $errorMessages);
203 }
204 return esc_html($errorMessages);
205 }, $errors);
206
207 throw new ValidationException('', 0, null, [
208 // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Error messages are escaped above
209 'errors' => $escapedErrors,
210 ]);
211 }
212 }
213
214 private function dependencyValidate($permissions)
215 {
216 $allPermissions = Acl::getReadablePermissions();
217
218 foreach ($permissions as $permission) {
219 $depends = Arr::get($allPermissions, $permission . '.depends', []);
220
221 if ($depends && $more = array_values(array_diff($depends, $permissions))) {
222 $message = $allPermissions[$permission]['title'] . ' requires permission: ';
223
224 foreach ($more as $i => $p) {
225 $joiner = $i ? ', ' : '';
226 $message = $message . $joiner . $allPermissions[$p]['title'];
227 }
228
229 return $message;
230 }
231 }
232 }
233
234 private function getUserRoles($roles)
235 {
236 $roleStr = '';
237 if (count($roles) > 1) {
238 foreach ($roles as $role) {
239 $roleStr .= $role . ', ';
240 }
241 } else {
242 $roleStr = Arr::get($roles, '0');
243 }
244
245 return $roleStr;
246 }
247 }
248