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 / Subscriptions / SubscriptionNotesController.php

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

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