Completed
Push — master ( ed32be...1a3fa2 )
by Joro
14:27 queued 04:32
created

Router::get_association_options()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15

Duplication

Lines 15
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 0
dl 15
loc 15
rs 9.7666
c 0
b 0
f 0
1
<?php
2
3
namespace Carbon_Fields\REST_API;
4
5
use Carbon_Fields\Helper\Helper;
6
use Carbon_Fields\Container\Repository as ContainerRepository;
7
8
/**
9
* Register custom routes for REST API
10
*/
11
class Router {
12
13
	/**
14
	 * Carbon Fields routes
15
	 *
16
	 * @var array
17
	 */
18
	protected $routes = array(
19
		'post_meta' => array(
20
			'path'                => '/posts/(?P<id>\d+)',
21
			'callback'            => 'get_post_meta',
22
			'permission_callback' => 'allow_access',
23
			'methods'             => 'GET',
24
		),
25
		'term_meta' => array(
26
			'path'                => '/terms/(?P<id>\d+)',
27
			'callback'            => 'get_term_meta',
28
			'permission_callback' => 'allow_access',
29
			'methods'             => 'GET',
30
		),
31
		'user_meta' => array(
32
			'path'                => '/users/(?P<id>\d+)',
33
			'callback'            => 'get_user_meta',
34
			'permission_callback' => 'allow_access',
35
			'methods'             => 'GET',
36
		),
37
		'comment_meta' => array(
38
			'path'                => '/comments/(?P<id>\d+)',
39
			'callback'            => 'get_comment_meta',
40
			'permission_callback' => 'allow_access',
41
			'methods'             => 'GET',
42
		),
43
		'theme_options' => array(
44
			'path'                => '/options/',
45
			'callback'            => 'options_accessor',
46
			'permission_callback' => 'options_permission',
47
			'methods'             => array( 'GET', 'POST' ),
48
		),
49
		'association_data' => array(
50
			'path'                => '/association',
51
			'callback'            => 'get_association_data',
52
			'permission_callback' => 'allow_access',
53
			'methods'             => 'GET',
54
		),
55
		'association_options' => array(
56
			'path'                => '/association/options',
57
			'callback'            => 'get_association_options',
58
			'permission_callback' => 'allow_access',
59
			'methods'             => 'GET',
60
		),
61
		'attachment_data' => array(
62
			'path'                => '/attachment',
63
			'callback'            => 'get_attachment_data',
64
			'permission_callback' => 'allow_access',
65
			'methods'             => 'GET',
66
			'args'                => 'attachment_data_args_schema',
67
		),
68
		'block_renderer' => array(
69
			'path'                => '/block-renderer',
70
			'callback'            => 'block_renderer',
71
			'permission_callback' => 'block_renderer_permission',
72
			'methods'             => 'POST',
73
			'args'                => 'block_renderer_args_schema',
74
		)
75
	);
76
77
	/**
78
	 * Version of the API
79
	 *
80
	 * @see set_version()
81
	 * @see get_version()
82
	 * @var string
83
	 */
84
	protected $version = '1';
85
86
	/**
87
	 * Vendor slug for the API
88
	 *
89
	 * @see set_vendor()
90
	 * @see get_vendor()
91
	 * @var string
92
	 */
93
	protected $vendor = 'carbon-fields';
94
95
	/**
96
	 * ContainerRepository instance
97
	 *
98
	 * @var ContainerRepository
99
	 */
100
	protected $container_repository;
101
102
	/**
103
	 * @param ContainerRepository $container_repository
104
	 */
105
	public function __construct( ContainerRepository $container_repository ) {
106
		$this->container_repository = $container_repository;
107
	}
108
109
	/**
110
	 * Boot up functionality
111
	 */
112
	public function boot() {
113
		add_action( 'rest_api_init', array( $this, 'register_routes' ), 15 );
114
	}
115
116
	/**
117
	 * Set routes
118
	 *
119
	 * @param array $routes
120
	 */
121
	public function set_routes( $routes ) {
122
		$this->routes = $routes;
123
	}
124
125
	/**
126
	 * Return routes
127
	 *
128
	 * @return array
129
	 */
130
	public function get_routes() {
131
		return $this->routes;
132
	}
133
134
	/**
135
	 * Set version
136
	 *
137
	 * @param string $version
138
	 */
139
	public function set_version( $version ) {
140
		$this->version = $version;
141
	}
142
143
	/**
144
	 * Return version
145
	 *
146
	 * @return string
147
	 */
148
	public function get_version() {
149
		return $this->version;
150
	}
151
152
	/**
153
	 * Set vendor
154
	 *
155
	 * @param string $vendor
156
	 */
157
	public function set_vendor( $vendor ) {
158
		$this->vendor = $vendor;
159
	}
160
161
	/**
162
	 * Return vendor
163
	 *
164
	 * @return string
165
	 */
166
	public function get_vendor() {
167
		return $this->vendor;
168
	}
169
170
	/**
171
	 * Allow access to an endpoint
172
	 *
173
	 * @return bool
174
	 */
175
	public function allow_access() {
176
		return true;
177
	}
178
179
	/**
180
	 * Register custom routes
181
	 *
182
	 * @see  register_route()
183
	 */
184
	public function register_routes() {
185
		foreach ( $this->routes as $route ) {
186
			$this->register_route( $route );
187
		}
188
	}
189
190
	/**
191
	 * Register a custom REST route
192
	 *
193
	 * @param  array $route
194
	 */
195
	protected function register_route( $route ) {
196
		register_rest_route( $this->get_vendor() . '/v' . $this->get_version(), $route['path'], array(
197
			'methods'             => $route['methods'],
198
			'permission_callback' => array( $this, $route['permission_callback'] ),
199
			'callback'            => array( $this, $route['callback'] ),
200
			'args'                => isset( $route['args'] ) ? call_user_func( array( $this, $route['args'] ) ) : array(),
201
		) );
202
	}
203
204
	/**
205
	 * Proxy method for handling get/set for theme options
206
	 *
207
	 * @param  \WP_REST_Request $request
208
	 * @return array|\WP_REST_Response
209
	 */
210
	public function options_accessor( $request ) {
211
		$request_type = $request->get_method();
212
213
		if ( $request_type === 'POST' ) {
214
			return $this->set_options( $request );
215
		}
216
217
		return $this->get_options();
218
	}
219
220
	/**
221
	 * Proxy method for handling theme options permissions
222
	 *
223
	 * @param  \WP_REST_Request $request
224
	 * @return bool
225
	 */
226
	public function options_permission( $request ) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
227
		return current_user_can( 'manage_options' );
228
	}
229
230
	/**
231
	 * Wrapper method used for retrieving data from Data_Manager
232
	 *
233
	 * @param  string $container_type
234
	 * @param  string $object_id
235
	 * @return array
236
	 */
237
	protected function get_all_field_values( $container_type, $object_id = null ) {
238
		$object_id = ( $object_id !== '' ) ? $object_id : null;
239
240
		$containers = $this->container_repository->get_containers( $container_type );
241
		$fields = array();
242
		foreach ( $containers as $container ) {
243
			$fields = array_merge( $fields, $container->get_fields() );
244
		}
245
246
		$values = array();
247
		foreach ( $fields as $field ) {
248
			if ( ! $field->get_visible_in_rest_api() ) {
249
				continue;
250
			}
251
			$values[ $field->get_base_name() ] = Helper::get_value( $object_id, $container_type, '', $field->get_base_name() );
252
		}
253
		return $values;
254
	}
255
256
	/**
257
	 * Get Carbon Fields post meta values
258
	 *
259
	 * @param  array $data
260
	 * @return array
261
	 */
262
	public function get_post_meta( $data ) {
263
		$carbon_data = $this->get_all_field_values( 'post_meta', $data['id'] );
264
		return array( 'carbon_fields' => $carbon_data );
265
	}
266
267
	/**
268
	 * Get Carbon Fields user meta values
269
	 *
270
	 * @param  array $data
271
	 * @return array
272
	 */
273
	public function get_user_meta( $data ) {
274
		$carbon_data = $this->get_all_field_values( 'user_meta', $data['id'] );
275
		return array( 'carbon_fields' => $carbon_data );
276
	}
277
278
	/**
279
	 * Get Carbon Fields term meta values
280
	 *
281
	 * @param  array $data
282
	 * @return array
283
	 */
284
	public function get_term_meta( $data ) {
285
		$carbon_data = $this->get_all_field_values( 'term_meta', $data['id'] );
286
		return array( 'carbon_fields' => $carbon_data );
287
	}
288
289
	/**
290
	 * Get Carbon Fields comment meta values
291
	 *
292
	 * @param  array $data
293
	 * @return array
294
	 */
295
	public function get_comment_meta( $data ) {
296
		$carbon_data = $this->get_all_field_values( 'comment_meta', $data['id'] );
297
		return array( 'carbon_fields' => $carbon_data );
298
	}
299
300
	/**
301
	 * Get Carbon Fields association selected options.
302
	 *
303
	 * @access public
304
	 *
305
	 * @return array
306
	 */
307
	public function get_association_data() {
308
		$container_id = $_GET['container_id'];
309
		$field_id     = $_GET['field_id'];
310
		$options      = isset( $_GET['options'] ) ? explode( ';', $_GET['options'] ) : array();
311
		$return_value = array();
312
313
		/** @var \Carbon_Fields\Field\Association_Field $field */
314
		$field = Helper::get_field( null, $container_id, $field_id );
315
316
		$options = array_map( function ( $option ) {
317
			$option = explode( ':', $option );
318
319
			return [
320
				'id'      => $option[0],
321
				'type'    => $option[1],
322
				'subtype' => $option[2],
323
			];
324
		}, $options );
325
326
		foreach ( $options as $option ) {
327
			$item = array(
328
				'type'       => $option['type'],
329
				'subtype'    => $option['subtype'],
330
				'thumbnail'  => $field->get_thumbnail_by_type( $option['id'], $option['type'], $option['subtype'] ),
331
				'id'         => intval( $option['id'] ),
332
				'title'      => $field->get_title_by_type( $option['id'], $option['type'], $option['subtype'] ),
333
				'label'      => $field->get_item_label( $option['id'], $option['type'], $option['subtype'] ),
334
				'is_trashed' => ( $option['type'] == 'post' && get_post_status( $option['id'] ) === 'trash' ),
335
			);
336
337
			$return_value[] = $item;
338
		}
339
340
		return $return_value;
341
	}
342
343
	/**
344
	 * Get Carbon Fields association options data.
345
	 *
346
	 * @access public
347
	 *
348
	 * @return array
349
	 */
350 View Code Duplication
	public function get_association_options() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
351
		$page = isset( $_GET['page'] ) ? absint( $_GET['page'] )              : 1;
352
		$term = isset( $_GET['term'] ) ? sanitize_text_field( $_GET['term'] ) : '';
353
354
		$container_id = $_GET['container_id'];
355
		$field_id     = $_GET['field_id'];
356
357
		/** @var \Carbon_Fields\Field\Association_Field $field */
358
		$field = Helper::get_field( null, $container_id, $field_id );
359
360
		return $field->get_options( array(
361
			'page' => $page,
362
			'term' => $term,
363
		) );
364
	}
365
366
	/**
367
	 * Get attachment data by given ID or URL.
368
	 *
369
	 * @return array
370
	 */
371
	public function get_attachment_data() {
372
		$type  = sanitize_text_field( $_GET['type'] );
373
		$value = sanitize_text_field( $_GET['value'] );
374
375
		return Helper::get_attachment_metadata( $value, $type );
376
	}
377
378
	/**
379
	 * Retrieve Carbon theme options
380
	 *
381
	 * @return array
382
	 */
383
	protected function get_options() {
384
		$carbon_data = $this->get_all_field_values( 'theme_options' );
385
		return array( 'carbon_fields' => $carbon_data );
386
	}
387
388
	/**
389
	 * Set Carbon theme options
390
	 *
391
	 * @param \WP_REST_Request $request Full data about the request.
392
	 * @return \WP_Error|\WP_REST_Response
393
	 */
394
	protected function set_options( $request ) {
395
		$options = $request->get_params();
396
397
		if ( empty( $options ) ) {
398
			return new \WP_REST_Response( __( 'No option names provided', 'carbon-fields' ) );
399
		}
400
401
		foreach ( $options as $key => $value ) {
402
			try {
403
				Helper::set_value( null, 'Theme_Options', '', $key, $value );
404
			} catch ( \Exception $e ) {
405
				return new \WP_REST_Response( wp_strip_all_tags( $e->getMessage() ) );
406
			}
407
		}
408
409
		return new \WP_REST_Response( __( 'Theme Options updated.', 'carbon-fields' ), 200 );
410
	}
411
412
	/**
413
	 * Checks if a given request has access to read blocks.
414
	 *
415
	 * @see https://github.com/WordPress/WordPress/blob/master/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php#L78-L116
416
	 *
417
	 * @param  \WP_REST_Request
418
	 * @return true|\WP_Error
419
	 */
420
	public function block_renderer_permission( $request ) {
421
		global $post;
422
423
		$post_id = isset( $request['post_id'] ) ? intval( $request['post_id'] ) : 0;
424
425
		if ( 0 < $post_id ) {
426
			$post = get_post( $post_id );
427
428 View Code Duplication
			if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
429
				return new \WP_Error(
430
					'block_cannot_read',
431
					__( 'Sorry, you are not allowed to read blocks of this post.', 'carbon-fields' ),
432
					array(
433
						'status' => rest_authorization_required_code(),
434
					)
435
				);
436
			}
437 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
438
			if ( ! current_user_can( 'edit_posts' ) ) {
439
				return new \WP_Error(
440
					'block_cannot_read',
441
					__( 'Sorry, you are not allowed to read blocks as this user.', 'carbon-fields' ),
442
					array(
443
						'status' => rest_authorization_required_code(),
444
					)
445
				);
446
			}
447
		}
448
449
		return true;
450
	}
451
452
	/**
453
	 * Returns the schema of the accepted arguments.
454
	 *
455
	 * @return array
456
	 */
457
	public function attachment_data_args_schema() {
458
		return array(
459
			'type'       => array(
460
				'type'        => 'string',
461
				'required'    => true,
462
				'description' => __( 'The requested type: ID or URL.', 'carbon-fields' ),
463
			),
464
			'value'    => array(
465
				'type'        => 'string',
466
				'required'    => true,
467
				'description' => __( 'The ID / URL of the attachment', 'carbon-fields' ),
468
			),
469
		);
470
	}
471
472
	/**
473
	 * Returns the schema of the accepted arguments.
474
	 *
475
	 * @see https://github.com/WordPress/WordPress/blob/master/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php#L56-L71
476
	 *
477
	 * @return array
478
	 */
479
	public function block_renderer_args_schema() {
480
		return array(
481
			'name'       => array(
482
				'type'        => 'string',
483
				'required'    => true,
484
				'description' => __( 'The name of the block.', 'carbon-fields' ),
485
			),
486
			'content'    => array(
487
				'type'        => 'string',
488
				'required'    => true,
489
				'description' => __( 'The content of the block.', 'carbon-fields' ),
490
			),
491
			'post_id'    => array(
492
				'type'        => 'integer',
493
				'description' => __( 'ID of the post context.', 'carbon-fields' ),
494
			),
495
		);
496
	}
497
498
	/**
499
	 * Returns block output from block's registered render_callback.
500
	 *
501
	 * @see https://github.com/WordPress/WordPress/blob/master/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php#L118-L154
502
	 *
503
	 * @param  \WP_REST_Request $request
504
	 * @return \WP_REST_Response|\WP_Error
505
	 */
506
	public function block_renderer( $request ) {
507
		global $post;
508
509
		$post_id = isset( $request['post_id'] ) ? intval( $request['post_id'] ) : 0;
510
511
		if ( 0 < $post_id ) {
512
			$post = get_post( $post_id );
513
514
			// Set up postdata since this will be needed if post_id was set.
515
			setup_postdata( $post );
516
		}
517
518
		$registry = \WP_Block_Type_Registry::get_instance();
519
		$block    = $registry->get_registered( $request['name'] );
520
521
		if ( null === $block ) {
522
			return new \WP_Error(
523
				'block_invalid',
524
				__( 'Invalid block.' ),
525
				array(
526
					'status' => 404,
527
				)
528
			);
529
		}
530
531
		$data = array(
532
			'rendered' => do_blocks( $request['content'] ),
533
		);
534
535
		return rest_ensure_response( $data );
536
	}
537
}
538