PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.17.0
GiveWP – Donation Plugin and Fundraising Platform v4.17.0
4.17.0 4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 All 256 releases
give / src / API / REST / V3 / Routes / Donors / DonorNotesController.php

DonorNotesController.php in GiveWP – Donation Plugin and Fundraising Platform 4.17.0, at src/API/REST/V3/Routes/Donors/DonorNotesController.php

489 lines 16.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Give\API\REST\V3\Routes\Donors;
4
5 use Exception;
6 use Give\API\REST\V3\Routes\Donors\ValueObjects\DonorRoute;
7 use Give\API\REST\V3\Support\Headers;
8 use Give\API\REST\V3\Support\Item;
9 use Give\Donors\Models\Donor;
10 use Give\Donors\Models\DonorNote;
11 use Give\Donors\ValueObjects\DonorNoteType;
12 use Give\Framework\Permissions\Facades\UserPermissions;
13 use WP_Error;
14 use WP_REST_Controller;
15 use WP_REST_Request;
16 use WP_REST_Response;
17 use WP_REST_Server;
18
19 /**
20 * @since 4.4.0
21 */
22 class DonorNotesController extends WP_REST_Controller
23 {
24 /**
25 * @since 4.4.0
26 */
27 public function __construct()
28 {
29 $this->namespace = DonorRoute::NAMESPACE;
30 $this->rest_base = DonorRoute::BASE;
31 }
32
33 /**
34 * @since 4.9.0 Move schema key to the route level instead of defining it for each endpoint (which is incorrect)
35 * @since 4.4.0
36 */
37 public function register_routes()
38 {
39 register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<donorId>[\d]+)/notes', [
40 [
41 'methods' => WP_REST_Server::READABLE,
42 'callback' => [$this, 'get_items'],
43 'permission_callback' => [$this, 'get_items_permissions_check'],
44 'args' => array_merge([
45 'donorId' => [
46 'description' => __('The ID of the donor this note belongs to.', 'give'),
47 'type' => 'integer',
48 'required' => true,
49 ]
50 ], $this->get_collection_params()),
51 ],
52 [
53 'methods' => WP_REST_Server::CREATABLE,
54 'callback' => [$this, 'create_item'],
55 'permission_callback' => [$this, 'create_item_permissions_check'],
56 'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
57 ],
58 'schema' => [$this, 'get_public_item_schema'],
59 ]);
60
61 register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<donorId>[\d]+)/notes/(?P<id>[\d]+)', [
62 [
63 'methods' => WP_REST_Server::READABLE,
64 'callback' => [$this, 'get_item'],
65 'permission_callback' => [$this, 'get_item_permissions_check'],
66 'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::READABLE),
67 ],
68 [
69 'methods' => WP_REST_Server::EDITABLE,
70 'callback' => [$this, 'update_item'],
71 'permission_callback' => [$this, 'update_item_permissions_check'],
72 'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::EDITABLE),
73 ],
74 [
75 'methods' => WP_REST_Server::DELETABLE,
76 'callback' => [$this, 'delete_item'],
77 'permission_callback' => [$this, 'delete_item_permissions_check'],
78 'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::DELETABLE),
79 ],
80 'schema' => [$this, 'get_public_item_schema'],
81 ]);
82 }
83
84 /**
85 * Get a collection of donor notes.
86 *
87 * @since 4.14.0 Use Headers::addPagination() helper for pagination headers
88 * @since 4.4.0
89 *
90 * @param WP_REST_Request $request Full data about the request.
91 *
92 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
93 */
94 public function get_items($request)
95 {
96 $donor = Donor::find($request->get_param('donorId'));
97 if (!$donor) {
98 return new WP_Error('donor_not_found', __('Donor not found', 'give'), ['status' => 404]);
99 }
100
101 $page = $request->get_param('page');
102 $perPage = $request->get_param('per_page');
103
104 $query = DonorNote::query()
105 ->where('comment_parent', $donor->id)
106 ->limit($perPage)
107 ->offset(($page - 1) * $perPage)
108 ->orderBy('createdAt', 'DESC');
109
110 $notes = $query->getAll() ?? [];
111 $notes = array_map(function ($note) use ($request) {
112 $item = $this->prepare_item_for_response($note, $request);
113 return $this->prepare_response_for_collection($item);
114 }, $notes);
115
116 $totalNotes = DonorNote::query()->where('comment_parent', $donor->id)->count();
117 $routeBase = sprintf('%s/%s/%d/notes', $this->namespace, $this->rest_base, $donor->id);
118
119 $response = rest_ensure_response($notes);
120 $response = Headers::addPagination($response, $request, $totalNotes, $perPage, $routeBase);
121
122 return $response;
123 }
124
125 /**
126 * Create a donor note.
127 *
128 * @since 4.7.0 Add support updating custom fields
129 * @since 4.4.0
130 *
131 * @param WP_REST_Request $request Full data about the request.
132 *
133 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
134 *
135 * @throws Exception
136 */
137 public function create_item($request)
138 {
139 $donor = Donor::find($request->get_param('donorId'));
140 if (!$donor) {
141 return new WP_Error('donor_not_found', __('Donor not found', 'give'), ['status' => 404]);
142 }
143
144 $note = DonorNote::create([
145 'donorId' => $donor->id,
146 'content' => $request->get_param('content'),
147 'type' => new DonorNoteType($request->get_param('type')),
148 ]);
149
150 $response = $this->prepare_item_for_response($note, $request);
151 $response->set_status(201);
152
153 return rest_ensure_response($response);
154 }
155
156 /**
157 * Get a single donor note.
158 *
159 * @since 4.4.0
160 *
161 * @param WP_REST_Request $request Full data about the request.
162 *
163 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
164 */
165 public function get_item($request)
166 {
167 $donor = Donor::find($request->get_param('donorId'));
168 if (!$donor) {
169 return new WP_Error('donor_not_found', __('Donor not found', 'give'), ['status' => 404]);
170 }
171
172 $note = DonorNote::find($request->get_param('id'));
173 if (!$note || $note->donorId !== $donor->id) {
174 return new WP_Error('note_not_found', __('Note not found', 'give'), ['status' => 404]);
175 }
176
177 $response = $this->prepare_item_for_response($note, $request);
178 return rest_ensure_response($response);
179 }
180
181 /**
182 * Update a donor note.
183 *
184 * @since 4.7.0 Add support for updating custom fields
185 * @since 4.4.0
186 *
187 * @param WP_REST_Request $request Full data about the request.
188 *
189 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
190 *
191 * @throws Exception
192 */
193 public function update_item($request)
194 {
195 $donor = Donor::find($request->get_param('donorId'));
196 if (!$donor) {
197 return new WP_Error('donor_not_found', __('Donor not found', 'give'), ['status' => 404]);
198 }
199
200 $note = DonorNote::find($request->get_param('id'));
201 if (!$note || $note->donorId !== $donor->id) {
202 return new WP_Error('note_not_found', __('Note not found', 'give'), ['status' => 404]);
203 }
204
205 if ($request->has_param('content')) {
206 $note->content = $request->get_param('content');
207 }
208
209 if ($request->has_param('type')) {
210 $note->type = new DonorNoteType($request->get_param('type'));
211 }
212
213 if ($note->isDirty()) {
214 $note->save();
215 }
216
217 $fieldsUpdate = $this->update_additional_fields_for_object($note, $request);
218 if (is_wp_error($fieldsUpdate)) {
219 return $fieldsUpdate;
220 }
221
222 $response = $this->prepare_item_for_response($note, $request);
223 return rest_ensure_response($response);
224 }
225
226 /**
227 * Delete a donor note.
228 *
229 * @since 4.4.0
230 *
231 * @param WP_REST_Request $request Full data about the request.
232 *
233 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
234 *
235 * @throws Exception
236 */
237 public function delete_item($request)
238 {
239 $donor = Donor::find($request->get_param('donorId'));
240 if (!$donor) {
241 return new WP_Error('donor_not_found', __('Donor not found', 'give'), ['status' => 404]);
242 }
243
244 $note = DonorNote::find($request->get_param('id'));
245 if (!$note || $note->donorId !== $donor->id) {
246 return new WP_Error('note_not_found', __('Note not found', 'give'), ['status' => 404]);
247 }
248
249 // Store the note data before deletion for the response
250 $noteData = $note->toArray();
251
252 $note->delete();
253
254 $response = new WP_REST_Response($noteData);
255
256 return $response;
257 }
258
259 /**
260 * @since 4.14.0 update permission capability to use facade
261 * @since 4.4.0
262 */
263 public function get_items_permissions_check($request): bool
264 {
265 return UserPermissions::donors()->canView();
266 }
267
268 /**
269 * @since 4.14.0 replace logic with UserPermissions facade
270 * @since 4.4.0
271 */
272 public function create_item_permissions_check($request): bool
273 {
274 return UserPermissions::donors()->canCreate();
275 }
276
277 /**
278 * @since 4.14.0 replace logic with UserPermissions facade
279 * @since 4.4.0
280 */
281 public function get_item_permissions_check($request): bool
282 {
283 return UserPermissions::donors()->canView();
284 }
285
286 /**
287 * @since 4.14.0 replace logic with UserPermissions facade
288 * @since 4.4.0
289 */
290 public function update_item_permissions_check($request): bool
291 {
292 return UserPermissions::donors()->canEdit();
293 }
294
295 /**
296 * @since 4.14.0 replace logic with UserPermissions facade
297 * @since 4.4.0
298 */
299 public function delete_item_permissions_check($request): bool
300 {
301 return UserPermissions::donors()->canDelete();
302 }
303
304 /**
305 * @since 4.14.0 Format dates as strings using Item::formatDatesForResponse
306 * @since 4.7.0 Add support for adding custom fields to the response
307 * @since 4.4.0
308 */
309 public function prepare_item_for_response($note, $request): WP_REST_Response
310 {
311 $self_url = rest_url(sprintf(
312 '%s/%s/%d/notes/%d',
313 $this->namespace,
314 $this->rest_base,
315 $note->donorId,
316 $note->id
317 ));
318
319 $links = [
320 'self' => ['href' => $self_url],
321 ];
322
323 $item = $note->toArray();
324 $response = new WP_REST_Response(Item::formatDatesForResponse($item, ['createdAt', 'updatedAt']));
325 $response->add_links($links);
326 $response->data = $this->add_additional_fields_to_object($response->data, $request);
327
328 return $response;
329 }
330
331 /**
332 * @since 4.4.0
333 */
334 public function get_collection_params(): array
335 {
336 $params = parent::get_collection_params();
337
338 $params['page']['default'] = 1;
339 $params['per_page']['default'] = 30;
340
341 // Remove default parameters not being used
342 unset($params['context']);
343 unset($params['search']);
344
345 return $params;
346 }
347
348 /**
349 * Get the donor note schema, conforming to JSON Schema.
350 *
351 * @since 4.14.0 Add date format examples
352 * @since 4.13.0 add schema description
353 * @since 4.9.0 Set proper JSON Schema version
354 * @since 4.7.0 Change title to givewp/donor-note and add custom fields schema
355 * @since 4.4.0
356 *
357 * @return array
358 */
359 public function get_item_schema(): array
360 {
361 $schema = [
362 '$schema' => 'http://json-schema.org/draft-04/schema#',
363 'title' => 'givewp/donor-note',
364 'description' => esc_html__('Donor Note routes for CRUD operations', 'give'),
365 'type' => 'object',
366 'properties' => [
367 'id' => [
368 'description' => __('Unique identifier for the note.', 'give'),
369 'type' => 'integer',
370 'readonly' => true,
371 ],
372 'donorId' => [
373 'description' => __('The ID of the donor this note belongs to.', 'give'),
374 'type' => 'integer',
375 'required' => true,
376 ],
377 'content' => [
378 'description' => __('The content of the note.', 'give'),
379 'type' => 'string',
380 'required' => true,
381 'minLength' => 1,
382 ],
383 'type' => [
384 'description' => __('The type of the note.', 'give'),
385 'type' => 'string',
386 'enum' => ['admin', 'donor'],
387 'default' => 'admin',
388 ],
389 'createdAt' => [
390 'description' => sprintf(
391 /* translators: %s: WordPress documentation URL */
392 esc_html__('The date the note was created in ISO 8601 format. Follows WordPress REST API date format standards. See %s for more information.', 'give'),
393 '<a href="https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#format" target="_blank">WordPress REST API Date and Time</a>'
394 ),
395 'type' => ['string', 'null'],
396 'format' => 'date-time',
397 'example' => '2025-09-02T20:27:02',
398 'readonly' => true,
399 ],
400 'updatedAt' => [
401 'description' => sprintf(
402 /* translators: %s: WordPress documentation URL */
403 esc_html__('The date the note was last updated in ISO 8601 format. Follows WordPress REST API date format standards. See %s for more information.', 'give'),
404 '<a href="https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#format" target="_blank">WordPress REST API Date and Time</a>'
405 ),
406 'type' => ['string', 'null'],
407 'format' => 'date-time',
408 'example' => '2025-09-02T20:27:02',
409 'readonly' => true,
410 ],
411 ],
412 ];
413
414 return $this->add_additional_fields_schema($schema);
415 }
416
417 /**
418 * Get the donor note schema for public display.
419 *
420 * @since 4.4.0
421 *
422 * @return array
423 */
424 public function get_public_item_schema(): array
425 {
426 $schema = $this->get_item_schema();
427
428 // Add additional properties for public display
429 $schema['properties']['_links'] = [
430 'description' => __('HATEOAS links for the note.', 'give'),
431 'type' => 'object',
432 'readonly' => true,
433 ];
434
435 return $schema;
436 }
437
438 /**
439 * @since 4.6.0 Add format to content argument
440 * @since 4.4.0
441 */
442 public function get_endpoint_args_for_item_schema($method = WP_REST_Server::CREATABLE): array
443 {
444 $args = parent::get_endpoint_args_for_item_schema($method);
445 $schema = $this->get_item_schema();
446
447 // Common argument for all endpoints
448 $args['donorId'] = $schema['properties']['donorId'];
449 $args['donorId']['in'] = 'path';
450
451 // Arguments for single item endpoints (not for POST)
452 if (in_array($method, [WP_REST_Server::READABLE, WP_REST_Server::EDITABLE, WP_REST_Server::DELETABLE], true)) {
453 $args['id'] = [
454 'description' => __('The note ID.', 'give'),
455 'type' => 'integer',
456 'required' => true,
457 'in' => 'path',
458 ];
459 } else {
460 // Remove id if present (for POST)
461 unset($args['id']);
462 }
463
464 // Arguments for create/update endpoints
465 if (in_array($method, [WP_REST_Server::CREATABLE, WP_REST_Server::EDITABLE], true)) {
466 $args['content'] = [
467 'description' => __('The content of the note.', 'give'),
468 'type' => 'string',
469 'required' => $method === WP_REST_Server::CREATABLE,
470 'minLength' => 1,
471 'format' => 'text-field',
472 ];
473
474 $args['type'] = [
475 'description' => __('The type of the note.', 'give'),
476 'type' => 'string',
477 'required' => $method === WP_REST_Server::CREATABLE,
478 'enum' => ['admin', 'donor'],
479 'default' => 'admin',
480 ];
481 } else {
482 unset($args['content']);
483 unset($args['type']);
484 }
485
486 return $args;
487 }
488 }
489