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 / Http / Controllers / SubmissionController.php

SubmissionController.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.14, at app/Http/Controllers/SubmissionController.php

302 lines 10.4 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\Http\Controllers;
4
5 use Exception;
6 use FluentForm\App\Models\Submission;
7 use FluentForm\App\Modules\Acl\Acl;
8 use FluentForm\App\Services\Manager\FormManagerService;
9 use FluentForm\App\Services\Submission\SubmissionService;
10 use FluentForm\Framework\Support\Arr;
11
12 class SubmissionController extends Controller
13 {
14 public function index(SubmissionService $submissionService)
15 {
16 try {
17 $attributes = $this->sanitizeSubmissionAttributes($this->request->all());
18
19 return $this->sendSuccess(
20 $submissionService->get($attributes)
21 );
22 } catch (Exception $e) {
23 return $this->sendError([
24 'message' => $e->getMessage(),
25 ]);
26 }
27 }
28
29 public function find(SubmissionService $submissionService, $submissionId)
30 {
31 try {
32 return $this->sendSuccess(
33 $submissionService->find($submissionId)
34 );
35 } catch (Exception $e) {
36 return $this->sendError([
37 'message' => $e->getMessage(),
38 ]);
39 }
40 }
41
42 public function resources(SubmissionService $submissionService)
43 {
44 try {
45 $attributes = $this->request->all();
46
47 $sanitizeMap = [
48 'form_id' => 'intval',
49 ];
50 $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
51
52 // SECURITY (FINDING-02): this route has no {entry_id} placeholder, so SubmissionPolicy
53 // authorizes the form owning the *request* entry_id, while resources() then reads a
54 // separate form_id — letting a form-scoped user read another form's counts/labels/
55 // fields and (via next/previous) submission rows. Re-verify the caller may view
56 // entries of the form actually being queried.
57 $formId = (int) Arr::get($attributes, 'form_id');
58 if (!$formId || !Acl::hasPermission('fluentform_entries_viewer', $formId)) {
59 return $this->sendError([
60 'message' => __('You do not have permission to view this form\'s entries.', 'fluentform'),
61 ], 403);
62 }
63
64 return $this->sendSuccess(
65 $submissionService->resources($attributes)
66 );
67 } catch (Exception $e) {
68 return $this->sendError([
69 'message' => $e->getMessage(),
70 ]);
71 }
72 }
73
74 public function updateStatus(SubmissionService $submissionService, $submissionId)
75 {
76 try {
77 $attributes = $this->request->all();
78 $attributes['entry_id'] = intval($submissionId);
79 $status = $submissionService->updateStatus($attributes);
80
81 /* translators: %s is the submission status */
82 $message = sprintf(__('The submission has been marked as %s', 'fluentform'), $status);
83
84 return $this->sendSuccess([
85 'message' => $message,
86 'status' => $status,
87 ]);
88 } catch (Exception $e) {
89 return $this->sendError([
90 'message' => $e->getMessage(),
91 ]);
92 }
93 }
94
95 public function toggleIsFavorite(SubmissionService $submissionService, $submissionId)
96 {
97 try {
98 [$message, $isFavourite] = $submissionService->toggleIsFavorite(
99 intval($submissionId)
100 );
101
102 return $this->sendSuccess([
103 'message' => $message,
104 'is_favourite' => $isFavourite,
105 ]);
106 } catch (Exception $e) {
107 return $this->sendError([
108 'message' => $e->getMessage(),
109 ]);
110 }
111 }
112
113 public function handleBulkActions(SubmissionService $submissionService)
114 {
115 try {
116 $message = $submissionService->handleBulkActions($this->request->all());
117
118 return $this->sendSuccess(['message' => $message]);
119 } catch (Exception $e) {
120 return $this->sendError([
121 'message' => $e->getMessage(),
122 ]);
123 }
124 }
125
126 public function remove(SubmissionService $submissionService, $submissionId)
127 {
128 try {
129 $submission = Submission::findOrFail($submissionId);
130 $submissionService->deleteEntries([$submissionId], $submission->form_id);
131 do_action('fluentform/submission_deleted', $submissionId);
132
133 return $this->sendSuccess([
134 'message' => __('Selected submission successfully deleted Permanently', 'fluentform'),
135 ]);
136
137 } catch (Exception $e) {
138 return $this->sendError([
139 'message' => $e->getMessage(),
140 ]);
141 }
142 }
143
144 /**
145 * Get user list for submission page
146 *
147 * @return \WP_REST_Response
148 */
149 public function submissionUsers()
150 {
151 // SECURITY (FINDING-21): don't let a lower-tier user enumerate the whole WP roster here.
152 // Require WP's list_users OR the FF entries-manager permission this feature is built for —
153 // a delegated non-admin manager holds fluentform_manage_entries (and the assign-user UI is
154 // shown only to them) but NOT core list_users, so gating on list_users alone broke them.
155 if (!current_user_can('list_users') && !current_user_can('fluentform_manage_entries')) {
156 return $this->sendError(['message' => __('You do not have permission to list users.', 'fluentform')], 403);
157 }
158 $search = sanitize_text_field($this->request->get('search'));
159 if (current_user_can('list_users')) {
160 $users = get_users([
161 'search' => "*{$search}*",
162 'number' => 50,
163 ]);
164 } else {
165 // A delegated entries manager may confirm an address they already know,
166 // but must not browse the site's user roster (FF-SEC-45).
167 $user = is_email($search) ? get_user_by('email', $search) : false;
168 $users = $user ? [$user] : [];
169 }
170
171 $formattedUsers = [];
172 foreach ($users as $user) {
173 $formattedUsers[] = [
174 'ID' => $user->ID,
175 'label' => $user->display_name . ' - ' . $user->user_email,
176 ];
177 }
178
179 return $this->sendSuccess([
180 'users' => $formattedUsers,
181 ]);
182 }
183
184 /**
185 * Update User of a submission
186 *
187 * @param SubmissionService $submissionService
188 * @param int $submissionId
189 * @return \WP_REST_Response
190 */
191 public function updateSubmissionUser(SubmissionService $submissionService, $submissionId)
192 {
193 try {
194 $userId = intval($this->request->get('user_id'));
195 $submissionId = intval($submissionId);
196 $response = $submissionService->updateSubmissionUser($userId, $submissionId);
197 return $this->sendSuccess($response);
198 } catch (Exception $e) {
199 return $this->sendError([
200 'message' => $e->getMessage(),
201 ]);
202 }
203 }
204
205 /**
206 * Get All Submissions
207 *
208 * @param Submission $submission
209 * @return \WP_REST_Response
210 */
211 public function all(Submission $submission)
212 {
213 try {
214 $attributes = $this->sanitizeSubmissionAttributes($this->request->all());
215
216 return $this->sendSuccess(
217 $submission->allSubmissions($attributes)
218 );
219 } catch (Exception $e) {
220 return $this->sendError([
221 'message' => $e->getMessage(),
222 ]);
223 }
224 }
225 /**
226 * Get printable content
227 *
228 * @param SubmissionService $submissionService
229 * @return \WP_REST_Response
230 */
231 public function print(SubmissionService $submissionService)
232 {
233 try {
234 $attributes = $this->request->all();
235
236 $sanitizeMap = [
237 'submission_ids' => function ($value) {
238 if (is_array($value)) {
239 return array_map('intval', $value);
240 }
241 return [];
242 },
243 'form_id' => 'intval',
244 ];
245 $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
246
247 // Preserve backward compatibility with any legacy callers that still
248 // send entry_ids, while normalizing to the current submission_ids key.
249 if (empty($attributes['submission_ids']) && isset($attributes['entry_ids'])) {
250 $entryIds = $attributes['entry_ids'];
251 $attributes['submission_ids'] = is_array($entryIds)
252 ? array_map('intval', $entryIds)
253 : [];
254 }
255
256 // Re-verify against the form actually printed; the policy scopes on a request entry_id.
257 if (!FormManagerService::hasFormPermission((int) Arr::get($attributes, 'form_id'))) {
258 return $this->sendError([
259 'message' => __('You do not have permission to view this form\'s entries.', 'fluentform'),
260 ], 403);
261 }
262
263 return $this->sendSuccess(
264 $submissionService->getPrintContent($attributes)
265 );
266 } catch (Exception $e) {
267 return $this->sendError([
268 'message' => $e->getMessage(),
269 ]);
270 }
271 }
272
273 private function sanitizeSubmissionAttributes($attributes)
274 {
275 $sanitizeMap = [
276 'search' => 'sanitize_text_field',
277 'status' => 'sanitize_text_field',
278 'entry_type' => 'sanitize_text_field',
279 'form_id' => 'intval',
280 'per_page' => 'intval',
281 'page' => 'intval',
282 'is_favourite' => 'rest_sanitize_boolean',
283 ];
284
285 $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
286
287 if (isset($attributes['entry_type']) && !isset($attributes['status'])) {
288 $attributes['status'] = $attributes['entry_type'];
289 }
290
291 if (isset($attributes['date_range']) && is_array($attributes['date_range'])) {
292 $attributes['date_range'] = array_map('sanitize_text_field', $attributes['date_range']);
293 }
294
295 if (isset($attributes['payment_statuses']) && is_array($attributes['payment_statuses'])) {
296 $attributes['payment_statuses'] = array_map('sanitize_text_field', $attributes['payment_statuses']);
297 }
298
299 return $attributes;
300 }
301 }
302