allowed_setting_keys()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 2
rs 10
1
<?php
2
/**
3
 * REST API Setting Options controller
4
 *
5
 * Handles requests to the /settings and /settings/$setting_id endpoints.
6
 *
7
 * @package GetPaid
8
 * @subpackage REST API
9
 * @since   2.0.0
10
 */
11
12
defined( 'ABSPATH' ) || exit;
13
14
/**
15
 * GetPaid REST Setting controller class.
16
 *
17
 * @package Invoicing
18
 */
19
class GetPaid_REST_Settings_Controller extends GetPaid_REST_Controller {
20
21
	/**
22
	 * An array of available settings.
23
	 *
24
	 * @var string
25
	 */
26
	protected $settings;
27
28
	/**
29
	 * Route base.
30
	 *
31
	 * @var string
32
	 */
33
	protected $rest_base = 'settings';
34
35
	/**
36
	 * Registers the routes for the objects of the controller.
37
	 *
38
	 * @since 2.0.0
39
	 *
40
	 * @see register_rest_route()
41
	 */
42
	public function register_namespace_routes( $namespace ) {
43
44
		// List all registered tabs.
45
		register_rest_route(
46
			$namespace,
47
			$this->rest_base,
48
			array(
49
				array(
50
					'methods'             => WP_REST_Server::READABLE,
51
					'callback'            => array( $this, 'get_tabs' ),
52
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
53
				),
54
				'schema' => '__return_empty_array',
55
			)
56
		);
57
58
		// View/Update a single setting.
59
		register_rest_route(
60
			$namespace,
61
			$this->rest_base . '/setting/(?P<id>[\w-]+)',
62
			array(
63
				'args'   => array(
64
					'id' => array(
65
						'description' => __( 'Unique identifier for the setting.', 'invoicing' ),
66
						'type'        => 'string',
67
						'required'    => true,
68
					),
69
				),
70
				array(
71
					'methods'             => WP_REST_Server::READABLE,
72
					'callback'            => array( $this, 'get_item' ),
73
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
74
				),
75
				array(
76
					'methods'             => WP_REST_Server::EDITABLE,
77
					'callback'            => array( $this, 'update_item' ),
78
					'permission_callback' => array( $this, 'update_items_permissions_check' ),
79
					'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
80
				),
81
				'schema' => array( $this, 'get_public_item_schema' ),
82
			)
83
		);
84
85
		// List registered sections for a given tab.
86
		register_rest_route(
87
			$namespace,
88
			$this->rest_base . '/(?P<tab>[\w-]+)',
89
			array(
90
				'args'   => array(
91
					'tab' => array(
92
						'description' => __( 'Unique identifier for the tab whose sections should be retrieved.', 'invoicing' ),
93
						'type'        => 'string',
94
						'required'    => true,
95
						'enum'        => array_keys( wpinv_get_settings_tabs() ),
96
					),
97
				),
98
				array(
99
					'methods'             => WP_REST_Server::READABLE,
100
					'callback'            => array( $this, 'get_sections' ),
101
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
102
				),
103
				'schema' => '__return_empty_array',
104
			)
105
		);
106
107
		// List all registered settings for a given tab.
108
		register_rest_route(
109
			$namespace,
110
			$this->rest_base . '/(?P<tab>[\w-]+)/(?P<section>[\w-]+)',
111
			array(
112
				'args'   => array(
113
					'tab'     => array(
114
						'description' => __( 'Unique identifier for the tab whose settings should be retrieved.', 'invoicing' ),
115
						'type'        => 'string',
116
						'required'    => true,
117
						'enum'        => array_keys( wpinv_get_settings_tabs() ),
118
					),
119
					'section' => array(
120
						'description' => __( 'The section in the tab whose settings should be retrieved.', 'invoicing' ),
121
						'type'        => 'string',
122
						'required'    => true,
123
					),
124
				),
125
				array(
126
					'methods'             => WP_REST_Server::READABLE,
127
					'callback'            => array( $this, 'get_items' ),
128
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
129
				),
130
				'schema' => array( $this, 'get_public_item_schema' ),
131
			)
132
		); 
133
134
		register_rest_route(
135
			$namespace,
136
			'/' . $this->rest_base . '/batch',
137
			array(
138
				'args'   => array(
139
					'id' => array(
140
						'description' => __( 'Setting ID.', 'invoicing' ),
141
						'type'        => 'string',
142
					),
143
				),
144
				array(
145
					'methods'             => WP_REST_Server::EDITABLE,
146
					'callback'            => array( $this, 'batch_items' ),
147
					'permission_callback' => array( $this, 'batch_items_permissions_check' ),
148
					'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
149
				),
150
				'schema' => array( $this, 'get_public_batch_schema' ),
151
			)
152
		);
153
154
	}
155
156
	/**
157
	 * Return all settings.
158
	 *
159
	 * @since  2.0.0
160
	 * @param  WP_REST_Request $request Request data.
161
	 * @return WP_Error|WP_REST_Response
162
	 */
163
	public function get_items( $request ) {
164
165
		$settings = $this->get_settings();
166
167
		if ( ! isset( $settings[ $request['tab'] ] ) ) {
168
			return new WP_Error( 'rest_invalid_tab', __( 'Invalid tab.', 'invoicing' ), array( 'status' => 400 ) );
169
		}
170
171
		if ( ! isset( $settings[ $request['tab'] ][ $request['section'] ] ) ) {
172
			return new WP_Error( 'rest_invalid_section', __( 'Invalid section.', 'invoicing' ), array( 'status' => 400 ) );
173
		}
174
175
		$settings = $settings[ $request['tab'] ][ $request['section'] ];
176
		$prepared = array();
177
178
		foreach ( $settings as $setting ) {
179
180
			$setting      = $this->sanitize_setting( $setting );
181
			$setting_data = $this->prepare_item_for_response( $setting, $request );
182
			$setting_data = $this->prepare_response_for_collection( $setting_data );
183
184
			if ( $this->is_setting_type_valid( $setting['type'] ) ) {
185
				$prepared[]   = $setting_data;
186
			}
187
}
188
189
		return rest_ensure_response( $prepared );
190
	}
191
192
	/**
193
	 * Return a single setting.
194
	 *
195
	 * @since  2.0.0
196
	 * @param  WP_REST_Request $request Request data.
197
	 * @return WP_Error|WP_REST_Response
198
	 */
199
	public function get_item( $request ) {
200
		$setting  = $this->get_setting( $request['id'] );
201
202
		if ( is_wp_error( $setting ) ) {
203
			return $setting;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $setting also could return the type array which is incompatible with the documented return type WP_Error|WP_REST_Response.
Loading history...
204
		}
205
206
		$setting  = $this->sanitize_setting( $setting );
0 ignored issues
show
Bug introduced by
It seems like $setting can also be of type WP_Error; however, parameter $setting of GetPaid_REST_Settings_Co...ler::sanitize_setting() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

206
		$setting  = $this->sanitize_setting( /** @scrutinizer ignore-type */ $setting );
Loading history...
207
		$response = $this->prepare_item_for_response( $setting, $request );
208
		return rest_ensure_response( $response );
209
	}
210
211
	/**
212
	 * Update a single setting.
213
	 *
214
	 * @since  2.0.0
215
	 * @param  WP_REST_Request $request Request data.
216
	 * @return WP_Error|WP_REST_Response
217
	 */
218
	public function update_item( $request ) {
219
		$setting = $this->get_setting( $request['id'] );
220
221
		if ( is_wp_error( $setting ) ) {
222
			return $setting;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $setting also could return the type array which is incompatible with the documented return type WP_Error|WP_REST_Response.
Loading history...
223
		}
224
225
		if ( is_callable( array( $this, 'validate_setting_' . $setting['type'] . '_field' ) ) ) {
226
			$value = $this->{'validate_setting_' . $setting['type'] . '_field'}( $request['value'], $setting );
227
		} else {
228
			$value = $this->validate_setting_text_field( $request['value'], $setting );
0 ignored issues
show
Unused Code introduced by
The call to GetPaid_REST_Settings_Co...te_setting_text_field() has too many arguments starting with $setting. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

228
			/** @scrutinizer ignore-call */ 
229
   $value = $this->validate_setting_text_field( $request['value'], $setting );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
229
		}
230
231
		if ( is_wp_error( $value ) ) {
232
			return $value;
233
		}
234
235
		wpinv_update_option( $request['id'], $value );
236
		$setting['value'] = $value;
237
		$setting          = $this->sanitize_setting( $setting );
0 ignored issues
show
Bug introduced by
It seems like $setting can also be of type WP_Error; however, parameter $setting of GetPaid_REST_Settings_Co...ler::sanitize_setting() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

237
		$setting          = $this->sanitize_setting( /** @scrutinizer ignore-type */ $setting );
Loading history...
238
		$response         = $this->prepare_item_for_response( $setting, $request );
239
240
		return rest_ensure_response( $response );
241
	}
242
243
	/**
244
	 * Makes sure the current user has access to READ the settings APIs.
245
	 *
246
	 * @since  2.0.0
247
	 * @param WP_REST_Request $request Full data about the request.
248
	 * @return WP_Error|boolean
249
	 */
250
	public function get_items_permissions_check( $request ) {
251
		if ( ! wpinv_current_user_can_manage_invoicing() ) {
252
			return new WP_Error( 'rest_cannot_view', __( 'Sorry, you cannot list resources.', 'invoicing' ), array( 'status' => rest_authorization_required_code() ) );
253
		}
254
255
		return true;
256
	}
257
258
	/**
259
	 * Makes sure the current user has access to WRITE the settings APIs.
260
	 *
261
	 * @since  2.0.0
262
	 * @param WP_REST_Request $request Full data about the request.
263
	 * @return WP_Error|boolean
264
	 */
265
	public function update_items_permissions_check( $request ) {
266
		if ( ! wpinv_current_user_can_manage_invoicing() ) {
267
			return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you cannot edit this resource.', 'invoicing' ), array( 'status' => rest_authorization_required_code() ) );
268
		}
269
270
		return true;
271
	}
272
273
	/**
274
	 * Check if a given request has access batch create, update and delete items.
275
	 *
276
	 * @param  WP_REST_Request $request Full details about the request.
277
	 *
278
	 * @return boolean|WP_Error
279
	 */
280
	public function batch_items_permissions_check( $request ) {
281
		return wpinv_current_user_can_manage_invoicing() ? true : new WP_Error( 'rest_cannot_batch', __( 'Sorry, you are not allowed to batch manipulate this resource.', 'invoicing' ), array( 'status' => rest_authorization_required_code() ) );
282
	}
283
284
	/**
285
	 * Prepare links for the request.
286
	 *
287
	 * @param string $setting_id Setting ID.
288
	 * @return array Links for the given setting.
289
	 */
290
	protected function prepare_links( $setting_id ) {
291
292
		$links = array(
293
			'self'       => array(
294
				'href' => rest_url( sprintf( '/%s/%s/setting/%s', $this->namespace, $this->rest_base, $setting_id ) ),
295
			),
296
			'collection' => array(
297
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
298
			),
299
		);
300
301
		return $links;
302
	}
303
304
	/**
305
	 * Prepare a settings object for serialization.
306
	 *
307
	 * @since  2.0.0
308
	 * @param array           $item Setting object.
309
	 * @param WP_REST_Request $request Request object.
310
	 * @return WP_REST_Response $response Response data.
311
	 */
312
	public function prepare_item_for_response( $item, $request ) {
313
		$context = empty( $request['context'] ) ? 'view' : $request['context'];
314
		$data    = $this->add_additional_fields_to_object( $item, $request );
315
		$data    = $this->filter_response_by_context( $data, $context );
316
317
		$response = rest_ensure_response( $data );
318
319
		$response->add_links( $this->prepare_links( $item['id'] ) );
320
321
		return $response;
322
	}
323
324
	/**
325
	 * Filters out bad values from the settings array/filter so we
326
	 * only return known values via the API.
327
	 *
328
	 * @since 2.0.0
329
	 * @param  array $setting Setting.
330
	 * @return array
331
	 */
332
	public function filter_setting( $setting ) {
333
		return array_intersect_key(
334
			$setting,
335
			array_flip( array_filter( array_keys( $setting ), array( $this, 'allowed_setting_keys' ) ) )
336
		);
337
	}
338
339
	/**
340
	 * Callback for allowed keys for each setting response.
341
	 *
342
	 * @param  string $key Key to check.
343
	 * @return boolean
344
	 */
345
	public function allowed_setting_keys( $key ) {
346
		return in_array( $key, array_keys( $this->setting_defaults() ), true );
347
	}
348
349
	/**
350
	 * Returns default options for a setting. null means the field is required.
351
	 *
352
	 * @since  2.0.0
353
	 * @return array
354
	 */
355
	protected function setting_defaults() {
356
		return array(
357
			'id'          => null,
358
			'name'        => null,
359
			'desc'        => '',
360
			'options'     => array(),
361
			'std'         => false,
362
			'value'       => false,
363
			'placeholder' => '',
364
			'readonly'    => false,
365
			'faux'        => false,
366
			'section'     => 'main',
367
			'tab'         => 'general',
368
			'type'        => 'text',
369
		);
370
	}
371
372
	/**
373
	 * Sanitizes a setting's field.
374
	 *
375
	 * @param  array $setting The setting to sanitize.
376
	 * @return array
377
	 */
378
	public function sanitize_setting( $setting ) {
379
380
		$setting          = wp_parse_args( $setting, $this->setting_defaults() );
381
		$setting['value'] = wpinv_get_option( $setting['id'], $setting['std'] );
382
		return $this->filter_setting( $setting );
383
384
	}
385
386
	/**
387
	 * Get setting data.
388
	 *
389
	 * @since  2.0.0
390
	 * @param string $setting_id Setting ID.
391
	 * @return array|WP_Error
392
	 */
393
	public function get_setting( $setting_id ) {
394
395
		if ( empty( $setting_id ) ) {
396
			return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'invoicing' ), array( 'status' => 404 ) );
397
		}
398
399
		$settings  = $this->get_settings();
400
401
		foreach ( $settings as $tabs ) {
402
403
			foreach ( $tabs as $sections ) {
404
405
				if ( isset( $sections[ $setting_id ] ) ) {
406
					if ( ! $this->is_setting_type_valid( $sections[ $setting_id ]['type'] ) ) {
407
						return new WP_Error( 'rest_setting_setting_type_invalid', __( 'Invalid setting type.', 'invoicing' ), array( 'status' => 404 ) );
408
					}
409
410
					return $sections[ $setting_id ];
411
				}
412
}
413
}
414
415
		return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'invoicing' ), array( 'status' => 404 ) );
416
	}
417
418
	/**
419
	 * Get all tabs.
420
	 *
421
	 * @param  WP_REST_Request $request Request data.
422
	 * @return array
423
	 */
424
	public function get_tabs( $request ) {
425
		$tabs     = wpinv_get_settings_tabs();
426
		$prepared = array();
427
428
		foreach ( $tabs as $id => $tab ) {
429
430
			$_request        = $request;
431
			$_request['tab'] = sanitize_title( $id );
432
			$data            = array(
433
				'id'       => sanitize_title( $id ),
434
				'label'    => sanitize_text_field( $tab ),
435
				'sections' => $this->get_sections( $_request ),
436
			);
437
438
			$data     = $this->add_additional_fields_to_object( $data, $request );
439
			$response = rest_ensure_response( $data );
440
441
			if ( ! is_wp_error( $response ) ) {
442
				$links = array(
443
					'sections'   => array(
444
						'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $id ) ),
445
					),
446
					'collection' => array(
447
						'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
448
					),
449
				);
450
				$response->add_links( $links );
451
				$response = $this->prepare_response_for_collection( $response );
452
			}
453
454
			$prepared[] = $response;
455
456
		}
457
458
		return rest_ensure_response( $prepared );
0 ignored issues
show
Bug Best Practice introduced by
The expression return rest_ensure_response($prepared) returns the type WP_REST_Response which is incompatible with the documented return type array.
Loading history...
459
	}
460
461
	/**
462
	 * Get all sections.
463
	 *
464
	 * @param  WP_REST_Request $request Request data.
465
	 * @return array
466
	 */
467
	public function get_sections( $request ) {
468
469
		$tab      = sanitize_title( $request['tab'] );
470
		$sections = wpinv_get_settings_tab_sections( $tab );
471
		$prepared = array();
472
473
		foreach ( $sections as $id => $section ) {
474
475
			$data            = array(
476
				'id'    => sanitize_title( $id ),
477
				'label' => sanitize_text_field( $section ),
478
			);
479
480
			$data     = $this->add_additional_fields_to_object( $data, $request );
481
			$response = rest_ensure_response( $data );
482
483
			if ( ! is_wp_error( $response ) ) {
484
				$links = array(
485
					'settings'   => array(
486
						'href' => rest_url( sprintf( '/%s/%s/%s/%s', $this->namespace, $this->rest_base, $tab, $id ) ),
487
					),
488
					'collection' => array(
489
						'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $tab ) ),
490
					),
491
					'tabs'       => array(
492
						'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
493
					),
494
				);
495
				$response->add_links( $links );
496
				$response = $this->prepare_response_for_collection( $response );
497
			}
498
499
			$prepared[] = $response;
500
501
		}
502
503
		return rest_ensure_response( $prepared );
0 ignored issues
show
Bug Best Practice introduced by
The expression return rest_ensure_response($prepared) returns the type WP_REST_Response which is incompatible with the documented return type array.
Loading history...
504
	}
505
506
	/**
507
	 * Get all settings.
508
	 *
509
	 * @return array
510
	 */
511
	public function get_settings() {
512
513
		if ( empty( $this->settings ) ) {
514
			$this->settings = wpinv_get_registered_settings();
0 ignored issues
show
Documentation Bug introduced by
It seems like wpinv_get_registered_settings() of type array is incompatible with the declared type string of property $settings.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
515
		}
516
517
		return $this->settings;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->settings also could return the type string which is incompatible with the documented return type array.
Loading history...
518
519
	}
520
521
	/**
522
	 * Boolean for if a setting type is a valid supported setting type.
523
	 *
524
	 * @since  2.0.0
525
	 * @param  string $type Type.
526
	 * @return bool
527
	 */
528
	public function is_setting_type_valid( $type ) {
529
530
		return in_array(
531
			$type,
532
            array(
533
				'text',         // Validates with validate_setting_text_field.
534
				'email',        // Validates with validate_setting_text_field.
535
				'number',       // Validates with validate_setting_text_field.
536
				'color',        // Validates with validate_setting_text_field.
537
				'password',     // Validates with validate_setting_text_field.
538
				'textarea',     // Validates with validate_setting_textarea_field.
539
				'select',       // Validates with validate_setting_select_field.
540
				'multiselect',  // Validates with validate_setting_multiselect_field.
541
				'radio',        // Validates with validate_setting_radio_field (-> validate_setting_select_field).
542
				'checkbox',     // Validates with validate_setting_checkbox_field.
543
				'header',       // Validates with validate_setting_text_field.
544
			)
545
		);
546
547
	}
548
549
	/**
550
	 * Get the settings schema, conforming to JSON Schema.
551
	 *
552
	 * @return array
553
	 */
554
	public function get_item_schema() {
555
556
		// Maybe retrieve the schema from cache.
557
		if ( ! empty( $this->schema ) ) {
558
			return $this->add_additional_fields_schema( $this->schema );
559
		}
560
561
		$schema = array(
562
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
563
			'title'      => 'setting',
564
			'type'       => 'object',
565
			'properties' => array(
566
				'id'          => array(
567
					'description' => __( 'A unique identifier for the setting.', 'invoicing' ),
568
					'type'        => 'string',
569
					'arg_options' => array(
570
						'sanitize_callback' => 'sanitize_title',
571
					),
572
					'context'     => array( 'view', 'edit' ),
573
					'readonly'    => true,
574
				),
575
				'tab'         => array(
576
					'description' => __( 'An identifier for the tab this setting belongs to.', 'invoicing' ),
577
					'type'        => 'string',
578
					'arg_options' => array(
579
						'sanitize_callback' => 'sanitize_title',
580
					),
581
					'context'     => array( 'view', 'edit' ),
582
					'readonly'    => true,
583
				),
584
				'section'     => array(
585
					'description' => __( 'An identifier for the section this setting belongs to.', 'invoicing' ),
586
					'type'        => 'string',
587
					'arg_options' => array(
588
						'sanitize_callback' => 'sanitize_title',
589
					),
590
					'context'     => array( 'view', 'edit' ),
591
					'readonly'    => true,
592
				),
593
				'name'        => array(
594
					'description' => __( 'A human readable label for the setting used in interfaces.', 'invoicing' ),
595
					'type'        => 'string',
596
					'arg_options' => array(
597
						'sanitize_callback' => 'sanitize_text_field',
598
					),
599
					'context'     => array( 'view', 'edit' ),
600
					'readonly'    => true,
601
				),
602
				'desc'        => array(
603
					'description' => __( 'A human readable description for the setting used in interfaces.', 'invoicing' ),
604
					'type'        => 'string',
605
					'context'     => array( 'view', 'edit' ),
606
					'readonly'    => true,
607
				),
608
				'value'       => array(
609
					'description' => __( 'The current value of this setting.', 'invoicing' ),
610
					'type'        => 'mixed',
611
					'context'     => array( 'view', 'edit' ),
612
				),
613
				'default'     => array(
614
					'description' => __( 'Default value for the setting.', 'invoicing' ),
615
					'type'        => 'mixed',
616
					'context'     => array( 'view', 'edit' ),
617
					'readonly'    => true,
618
				),
619
				'placeholder' => array(
620
					'description' => __( 'Placeholder text to be displayed in text inputs.', 'invoicing' ),
621
					'type'        => 'string',
622
					'arg_options' => array(
623
						'sanitize_callback' => 'sanitize_text_field',
624
					),
625
					'context'     => array( 'view', 'edit' ),
626
					'readonly'    => true,
627
				),
628
				'type'        => array(
629
					'description' => __( 'Type of setting.', 'invoicing' ),
630
					'type'        => 'string',
631
					'arg_options' => array(
632
						'sanitize_callback' => 'sanitize_text_field',
633
					),
634
					'context'     => array( 'view', 'edit' ),
635
					'enum'        => array( 'text', 'email', 'number', 'color', 'password', 'textarea', 'select', 'multiselect', 'radio', 'image_width', 'checkbox', 'raw_html' ),
636
					'readonly'    => true,
637
				),
638
				'options'     => array(
639
					'description' => __( 'Array of options (key value pairs) for inputs such as select, multiselect, and radio buttons.', 'invoicing' ),
640
					'type'        => 'object',
641
					'context'     => array( 'view', 'edit' ),
642
					'readonly'    => true,
643
				),
644
				'readonly'    => array(
645
					'description' => __( 'Whether or not this setting is readonly', 'invoicing' ),
646
					'type'        => 'string',
647
					'context'     => array( 'view' ),
648
					'readonly'    => true,
649
				),
650
				'faux'        => array(
651
					'description' => __( 'Whether or not this setting is readonly/faux', 'invoicing' ),
652
					'type'        => 'string',
653
					'context'     => array( 'view' ),
654
					'readonly'    => true,
655
				),
656
			),
657
		);
658
659
		// Filters the settings schema for the REST API.
660
        $schema = apply_filters( 'getpaid_rest_settings_schema', $schema );
661
662
		// Cache the settings schema.
663
		$this->schema = $schema;
664
665
		return $this->add_additional_fields_schema( $this->schema );
666
667
	}
668
669
	/**
670
	 * Validate a text value for a text based setting.
671
	 *
672
	 * @since 2.0.0
673
	 * @param string $value Value.
674
	 * @param array  $setting Setting.
675
	 * @return string
676
	 */
677
	public function validate_setting_text_field( $value ) {
678
		$value = is_null( $value ) ? '' : $value;
0 ignored issues
show
introduced by
The condition is_null($value) is always false.
Loading history...
679
		return wp_kses_post( trim( stripslashes( $value ) ) );
680
	}
681
682
	/**
683
	 * Validate select based settings.
684
	 *
685
	 * @since 2.0.0
686
	 * @param string $value Value.
687
	 * @param array  $setting Setting.
688
	 * @return string|WP_Error
689
	 */
690
	public function validate_setting_select_field( $value, $setting ) {
691
		if ( array_key_exists( $value, $setting['options'] ) ) {
692
			return $value;
693
		} else {
694
			return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'invoicing' ), array( 'status' => 400 ) );
695
		}
696
	}
697
698
	/**
699
	 * Validate multiselect based settings.
700
	 *
701
	 * @since 2.0.0
702
	 * @param array $values Values.
703
	 * @param array $setting Setting.
704
	 * @return array|WP_Error
705
	 */
706
	public function validate_setting_multiselect_field( $values, $setting ) {
707
		if ( empty( $values ) ) {
708
			return array();
709
		}
710
711
		if ( ! is_array( $values ) ) {
0 ignored issues
show
introduced by
The condition is_array($values) is always true.
Loading history...
712
			return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'invoicing' ), array( 'status' => 400 ) );
713
		}
714
715
		$final_values = array();
716
		foreach ( $values as $value ) {
717
			if ( array_key_exists( $value, $setting['options'] ) ) {
718
				$final_values[] = $value;
719
			}
720
		}
721
722
		return $final_values;
723
	}
724
725
	/**
726
	 * Validate radio based settings.
727
	 *
728
	 * @since 2.0.0
729
	 * @param string $value Value.
730
	 * @param array  $setting Setting.
731
	 * @return string|WP_Error
732
	 */
733
	public function validate_setting_radio_field( $value, $setting ) {
734
		return $this->validate_setting_select_field( $value, $setting );
735
	}
736
737
	/**
738
	 * Validate checkbox based settings.
739
	 *
740
	 * @since 2.0.0
741
	 * @param string $value Value.
742
	 * @return int
743
	 */
744
	public function validate_setting_checkbox_field( $value ) {
745
		return (int) ! empty( $value );
746
	}
747
748
	/**
749
	 * Validate textarea based settings.
750
	 *
751
	 * @since 2.0.0
752
	 * @param string $value Value.
753
	 * @return string
754
	 */
755
	public function validate_setting_textarea_field( $value ) {
756
		$value = is_null( $value ) ? '' : $value;
0 ignored issues
show
introduced by
The condition is_null($value) is always false.
Loading history...
757
		return wp_kses(
758
			trim( stripslashes( $value ) ),
759
			array_merge(
760
				array(
761
					'iframe' => array(
762
						'src'   => true,
763
						'style' => true,
764
						'id'    => true,
765
						'class' => true,
766
					),
767
				),
768
				wp_kses_allowed_html( 'post' )
769
			)
770
		);
771
	}
772
773
}
774