PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.16.9
GiveWP – Donation Plugin and Fundraising Platform v4.16.9
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 2.30.0 All 255 releases
give / src / API / REST / V3 / Routes / Donations / DonationNotesController.php

DonationNotesController.php in GiveWP – Donation Plugin and Fundraising Platform 4.16.9, at src/API/REST/V3/Routes/Donations/DonationNotesController.php

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