WC_REST_Setting_Options_Controller

REST API Setting Options controller class.

Defined (1)

The class is defined in the following location(s).

/includes/api/class-wc-rest-setting-options-controller.php  
  1. class WC_REST_Setting_Options_Controller extends WC_REST_Controller { 
  2.  
  3. /** 
  4. * WP REST API namespace/version. 
  5. */ 
  6. protected $namespace = 'wc/v2'; 
  7.  
  8. /** 
  9. * Route base. 
  10. * @var string 
  11. */ 
  12. protected $rest_base = 'settings/(?P<group_id>[\w-]+)'; 
  13.  
  14. /** 
  15. * Register routes. 
  16. * @since 3.0.0 
  17. */ 
  18. public function register_routes() { 
  19. register_rest_route( $this->namespace, '/' . $this->rest_base, array( 
  20. 'args' => array( 
  21. 'group' => array( 
  22. 'description' => __( 'Settings group ID.', 'woocommerce' ),  
  23. 'type' => 'string',  
  24. ),  
  25. ),  
  26. array( 
  27. 'methods' => WP_REST_Server::READABLE,  
  28. 'callback' => array( $this, 'get_items' ),  
  29. 'permission_callback' => array( $this, 'get_items_permissions_check' ),  
  30. ),  
  31. 'schema' => array( $this, 'get_public_item_schema' ),  
  32. ) ); 
  33.  
  34. register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array( 
  35. 'args' => array( 
  36. 'group' => array( 
  37. 'description' => __( 'Settings group ID.', 'woocommerce' ),  
  38. 'type' => 'string',  
  39. ),  
  40. ),  
  41. array( 
  42. 'methods' => WP_REST_Server::EDITABLE,  
  43. 'callback' => array( $this, 'batch_items' ),  
  44. 'permission_callback' => array( $this, 'update_items_permissions_check' ),  
  45. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),  
  46. ),  
  47. 'schema' => array( $this, 'get_public_batch_schema' ),  
  48. ) ); 
  49.  
  50. register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\w-]+)', array( 
  51. 'args' => array( 
  52. 'group' => array( 
  53. 'description' => __( 'Settings group ID.', 'woocommerce' ),  
  54. 'type' => 'string',  
  55. ),  
  56. 'id' => array( 
  57. 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),  
  58. 'type' => 'string',  
  59. ),  
  60. ),  
  61. array( 
  62. 'methods' => WP_REST_Server::READABLE,  
  63. 'callback' => array( $this, 'get_item' ),  
  64. 'permission_callback' => array( $this, 'get_items_permissions_check' ),  
  65. ),  
  66. array( 
  67. 'methods' => WP_REST_Server::EDITABLE,  
  68. 'callback' => array( $this, 'update_item' ),  
  69. 'permission_callback' => array( $this, 'update_items_permissions_check' ),  
  70. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),  
  71. ),  
  72. 'schema' => array( $this, 'get_public_item_schema' ),  
  73. ) ); 
  74.  
  75. /** 
  76. * Return a single setting. 
  77. * @since 3.0.0 
  78. * @param WP_REST_Request $request 
  79. * @return WP_Error|WP_REST_Response 
  80. */ 
  81. public function get_item( $request ) { 
  82. $setting = $this->get_setting( $request['group_id'], $request['id'] ); 
  83.  
  84. if ( is_wp_error( $setting ) ) { 
  85. return $setting; 
  86.  
  87. $response = $this->prepare_item_for_response( $setting, $request ); 
  88.  
  89. return rest_ensure_response( $response ); 
  90.  
  91. /** 
  92. * Return all settings in a group. 
  93. * @since 3.0.0 
  94. * @param WP_REST_Request $request 
  95. * @return WP_Error|WP_REST_Response 
  96. */ 
  97. public function get_items( $request ) { 
  98. $settings = $this->get_group_settings( $request['group_id'] ); 
  99.  
  100. if ( is_wp_error( $settings ) ) { 
  101. return $settings; 
  102.  
  103. $data = array(); 
  104.  
  105. foreach ( $settings as $setting_obj ) { 
  106. $setting = $this->prepare_item_for_response( $setting_obj, $request ); 
  107. $setting = $this->prepare_response_for_collection( $setting ); 
  108. if ( $this->is_setting_type_valid( $setting['type'] ) ) { 
  109. $data[] = $setting; 
  110.  
  111. return rest_ensure_response( $data ); 
  112.  
  113. /** 
  114. * Get all settings in a group. 
  115. * @since 3.0.0 
  116. * @param string $group_id Group ID. 
  117. * @return array|WP_Error 
  118. */ 
  119. public function get_group_settings( $group_id ) { 
  120. if ( empty( $group_id ) ) { 
  121. return new WP_Error( 'rest_setting_setting_group_invalid', __( 'Invalid setting group.', 'woocommerce' ), array( 'status' => 404 ) ); 
  122.  
  123. $settings = apply_filters( 'woocommerce_settings-' . $group_id, array() ); 
  124.  
  125. if ( empty( $settings ) ) { 
  126. return new WP_Error( 'rest_setting_setting_group_invalid', __( 'Invalid setting group.', 'woocommerce' ), array( 'status' => 404 ) ); 
  127.  
  128. $filtered_settings = array(); 
  129. foreach ( $settings as $setting ) { 
  130. $option_key = $setting['option_key']; 
  131. $setting = $this->filter_setting( $setting ); 
  132. $default = isset( $setting['default'] ) ? $setting['default'] : ''; 
  133. // Get the option value 
  134. if ( is_array( $option_key ) ) { 
  135. $option = get_option( $option_key[0] ); 
  136. $setting['value'] = isset( $option[ $option_key[1] ] ) ? $option[ $option_key[1] ] : $default; 
  137. } else { 
  138. $admin_setting_value = WC_Admin_Settings::get_option( $option_key ); 
  139. $setting['value'] = empty( $admin_setting_value ) ? $default : $admin_setting_value; 
  140.  
  141. if ( 'multi_select_countries' === $setting['type'] ) { 
  142. $setting['options'] = WC()->countries->get_countries(); 
  143. $setting['type'] = 'multiselect'; 
  144.  
  145. $filtered_settings[] = $setting; 
  146.  
  147. return $filtered_settings; 
  148.  
  149. /** 
  150. * Get setting data. 
  151. * @since 3.0.0 
  152. * @param string $group_id Group ID. 
  153. * @param string $setting_id Setting ID. 
  154. * @return stdClass|WP_Error 
  155. */ 
  156. public function get_setting( $group_id, $setting_id ) { 
  157. if ( empty( $setting_id ) ) { 
  158. return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'woocommerce' ), array( 'status' => 404 ) ); 
  159.  
  160. $settings = $this->get_group_settings( $group_id ); 
  161.  
  162. if ( is_wp_error( $settings ) ) { 
  163. return $settings; 
  164.  
  165. $array_key = array_keys( wp_list_pluck( $settings, 'id' ), $setting_id ); 
  166.  
  167. if ( empty( $array_key ) ) { 
  168. return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'woocommerce' ), array( 'status' => 404 ) ); 
  169.  
  170. $setting = $settings[ $array_key[0] ]; 
  171.  
  172. if ( ! $this->is_setting_type_valid( $setting['type'] ) ) { 
  173. return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'woocommerce' ), array( 'status' => 404 ) ); 
  174.  
  175. return $setting; 
  176.  
  177. /** 
  178. * Bulk create, update and delete items. 
  179. * @since 3.0.0 
  180. * @param WP_REST_Request $request Full details about the request. 
  181. * @return array Of WP_Error or WP_REST_Response. 
  182. */ 
  183. public function batch_items( $request ) { 
  184. // Get the request params. 
  185. $items = array_filter( $request->get_params() ); 
  186.  
  187. /** 
  188. * Since our batch settings update is group-specific and matches based on the route,  
  189. * we inject the URL parameters (containing group) into the batch items 
  190. */ 
  191. if ( ! empty( $items['update'] ) ) { 
  192. $to_update = array(); 
  193. foreach ( $items['update'] as $item ) { 
  194. $to_update[] = array_merge( $request->get_url_params(), $item ); 
  195. $request = new WP_REST_Request( $request->get_method() ); 
  196. $request->set_body_params( array( 'update' => $to_update ) ); 
  197.  
  198. return parent::batch_items( $request ); 
  199.  
  200. /** 
  201. * Update a single setting in a group. 
  202. * @since 3.0.0 
  203. * @param WP_REST_Request $request 
  204. * @return WP_Error|WP_REST_Response 
  205. */ 
  206. public function update_item( $request ) { 
  207. $setting = $this->get_setting( $request['group_id'], $request['id'] ); 
  208.  
  209. if ( is_wp_error( $setting ) ) { 
  210. return $setting; 
  211.  
  212. if ( is_callable( array( $this, 'validate_setting_' . $setting['type'] . '_field' ) ) ) { 
  213. $value = $this->{'validate_setting_' . $setting['type'] . '_field'}( $request['value'], $setting ); 
  214. } else { 
  215. $value = $this->validate_setting_text_field( $request['value'], $setting ); 
  216.  
  217. if ( is_wp_error( $value ) ) { 
  218. return $value; 
  219.  
  220. if ( is_array( $setting['option_key'] ) ) { 
  221. $setting['value'] = $value; 
  222. $option_key = $setting['option_key']; 
  223. $prev = get_option( $option_key[0] ); 
  224. $prev[ $option_key[1] ] = $request['value']; 
  225. update_option( $option_key[0], $prev ); 
  226. } else { 
  227. $update_data = array(); 
  228. $update_data[ $setting['option_key'] ] = $value; 
  229. $setting['value'] = $value; 
  230. WC_Admin_Settings::save_fields( array( $setting ), $update_data ); 
  231.  
  232. $response = $this->prepare_item_for_response( $setting, $request ); 
  233.  
  234. return rest_ensure_response( $response ); 
  235.  
  236. /** 
  237. * Prepare a single setting object for response. 
  238. * @since 3.0.0 
  239. * @param object $item Setting object. 
  240. * @param WP_REST_Request $request Request object. 
  241. * @return WP_REST_Response $response Response data. 
  242. */ 
  243. public function prepare_item_for_response( $item, $request ) { 
  244. unset( $item['option_key'] ); 
  245. $data = $this->filter_setting( $item ); 
  246. $data = $this->add_additional_fields_to_object( $data, $request ); 
  247. $data = $this->filter_response_by_context( $data, empty( $request['context'] ) ? 'view' : $request['context'] ); 
  248. $response = rest_ensure_response( $data ); 
  249. $response->add_links( $this->prepare_links( $data['id'], $request['group_id'] ) ); 
  250. return $response; 
  251.  
  252. /** 
  253. * Prepare links for the request. 
  254. * @since 3.0.0 
  255. * @param string $setting_id Setting ID. 
  256. * @param string $group_id Group ID. 
  257. * @return array Links for the given setting. 
  258. */ 
  259. protected function prepare_links( $setting_id, $group_id ) { 
  260. $base = str_replace( '(?P<group_id>[\w-]+)', $group_id, $this->rest_base ); 
  261. $links = array( 
  262. 'self' => array( 
  263. 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $base, $setting_id ) ),  
  264. ),  
  265. 'collection' => array( 
  266. 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),  
  267. ),  
  268. ); 
  269.  
  270. return $links; 
  271.  
  272. /** 
  273. * Makes sure the current user has access to READ the settings APIs. 
  274. * @since 3.0.0 
  275. * @param WP_REST_Request $request Full data about the request. 
  276. * @return WP_Error|boolean 
  277. */ 
  278. public function get_items_permissions_check( $request ) { 
  279. if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) { 
  280. return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); 
  281.  
  282. return true; 
  283.  
  284. /** 
  285. * Makes sure the current user has access to WRITE the settings APIs. 
  286. * @since 3.0.0 
  287. * @param WP_REST_Request $request Full data about the request. 
  288. * @return WP_Error|boolean 
  289. */ 
  290. public function update_items_permissions_check( $request ) { 
  291. if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) { 
  292. return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you cannot edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); 
  293.  
  294. return true; 
  295.  
  296. /** 
  297. * Filters out bad values from the settings array/filter so we 
  298. * only return known values via the API. 
  299. * @since 3.0.0 
  300. * @param array $setting 
  301. * @return array 
  302. */ 
  303. public function filter_setting( $setting ) { 
  304. $setting = array_intersect_key( 
  305. $setting,  
  306. array_flip( array_filter( array_keys( $setting ), array( $this, 'allowed_setting_keys' ) ) ) 
  307. ); 
  308.  
  309. if ( empty( $setting['options'] ) ) { 
  310. unset( $setting['options'] ); 
  311.  
  312. if ( 'image_width' === $setting['type'] ) { 
  313. $setting = $this->cast_image_width( $setting ); 
  314.  
  315. return $setting; 
  316.  
  317. /** 
  318. * For image_width, Crop can return "0" instead of false -- so we want 
  319. * to make sure we return these consistently the same we accept them. 
  320. * @since 3.0.0 
  321. * @param array $setting 
  322. * @return array 
  323. */ 
  324. public function cast_image_width( $setting ) { 
  325. foreach ( array( 'default', 'value' ) as $key ) { 
  326. if ( isset( $setting[ $key ] ) ) { 
  327. $setting[ $key ]['width'] = intval( $setting[ $key ]['width'] ); 
  328. $setting[ $key ]['height'] = intval( $setting[ $key ]['height'] ); 
  329. $setting[ $key ]['crop'] = (bool) $setting[ $key ]['crop']; 
  330. return $setting; 
  331.  
  332. /** 
  333. * Callback for allowed keys for each setting response. 
  334. * @since 3.0.0 
  335. * @param string $key Key to check 
  336. * @return boolean 
  337. */ 
  338. public function allowed_setting_keys( $key ) { 
  339. return in_array( $key, array( 
  340. 'id',  
  341. 'label',  
  342. 'description',  
  343. 'default',  
  344. 'tip',  
  345. 'placeholder',  
  346. 'type',  
  347. 'options',  
  348. 'value',  
  349. 'option_key',  
  350. ) ); 
  351.  
  352. /** 
  353. * Boolean for if a setting type is a valid supported setting type. 
  354. * @since 3.0.0 
  355. * @param string $type 
  356. * @return bool 
  357. */ 
  358. public function is_setting_type_valid( $type ) { 
  359. return in_array( $type, array( 
  360. 'text', // Validates with validate_setting_text_field. 
  361. 'email', // Validates with validate_setting_text_field. 
  362. 'number', // Validates with validate_setting_text_field. 
  363. 'color', // Validates with validate_setting_text_field. 
  364. 'password', // Validates with validate_setting_text_field. 
  365. 'textarea', // Validates with validate_setting_textarea_field. 
  366. 'select', // Validates with validate_setting_select_field. 
  367. 'multiselect', // Validates with validate_setting_multiselect_field. 
  368. 'radio', // Validates with validate_setting_radio_field (-> validate_setting_select_field). 
  369. 'checkbox', // Validates with validate_setting_checkbox_field. 
  370. 'image_width', // Validates with validate_setting_image_width_field. 
  371. ) ); 
  372.  
  373. /** 
  374. * Get the settings schema, conforming to JSON Schema. 
  375. * @since 3.0.0 
  376. * @return array 
  377. */ 
  378. public function get_item_schema() { 
  379. $schema = array( 
  380. '$schema' => 'http://json-schema.org/draft-04/schema#',  
  381. 'title' => 'setting',  
  382. 'type' => 'object',  
  383. 'properties' => array( 
  384. 'id' => array( 
  385. 'description' => __( 'A unique identifier for the setting.', 'woocommerce' ),  
  386. 'type' => 'string',  
  387. 'arg_options' => array( 
  388. 'sanitize_callback' => 'sanitize_title',  
  389. ),  
  390. 'context' => array( 'view', 'edit' ),  
  391. 'readonly' => true,  
  392. ),  
  393. 'label' => array( 
  394. 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ),  
  395. 'type' => 'string',  
  396. 'arg_options' => array( 
  397. 'sanitize_callback' => 'sanitize_text_field',  
  398. ),  
  399. 'context' => array( 'view', 'edit' ),  
  400. 'readonly' => true,  
  401. ),  
  402. 'description' => array( 
  403. 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ),  
  404. 'type' => 'string',  
  405. 'arg_options' => array( 
  406. 'sanitize_callback' => 'sanitize_text_field',  
  407. ),  
  408. 'context' => array( 'view', 'edit' ),  
  409. 'readonly' => true,  
  410. ),  
  411. 'value' => array( 
  412. 'description' => __( 'Setting value.', 'woocommerce' ),  
  413. 'type' => 'mixed',  
  414. 'context' => array( 'view', 'edit' ),  
  415. ),  
  416. 'default' => array( 
  417. 'description' => __( 'Default value for the setting.', 'woocommerce' ),  
  418. 'type' => 'mixed',  
  419. 'context' => array( 'view', 'edit' ),  
  420. 'readonly' => true,  
  421. ),  
  422. 'tip' => array( 
  423. 'description' => __( 'Additional help text shown to the user about the setting.', 'woocommerce' ),  
  424. 'type' => 'string',  
  425. 'arg_options' => array( 
  426. 'sanitize_callback' => 'sanitize_text_field',  
  427. ),  
  428. 'context' => array( 'view', 'edit' ),  
  429. 'readonly' => true,  
  430. ),  
  431. 'placeholder' => array( 
  432. 'description' => __( 'Placeholder text to be displayed in text inputs.', 'woocommerce' ),  
  433. 'type' => 'string',  
  434. 'arg_options' => array( 
  435. 'sanitize_callback' => 'sanitize_text_field',  
  436. ),  
  437. 'context' => array( 'view', 'edit' ),  
  438. 'readonly' => true,  
  439. ),  
  440. 'type' => array( 
  441. 'description' => __( 'Type of setting.', 'woocommerce' ),  
  442. 'type' => 'string',  
  443. 'arg_options' => array( 
  444. 'sanitize_callback' => 'sanitize_text_field',  
  445. ),  
  446. 'context' => array( 'view', 'edit' ),  
  447. 'enum' => array( 'text', 'email', 'number', 'color', 'password', 'textarea', 'select', 'multiselect', 'radio', 'image_width', 'checkbox' ),  
  448. 'readonly' => true,  
  449. ),  
  450. 'options' => array( 
  451. 'description' => __( 'Array of options (key value pairs) for inputs such as select, multiselect, and radio buttons.', 'woocommerce' ),  
  452. 'type' => 'object',  
  453. 'context' => array( 'view', 'edit' ),  
  454. 'readonly' => true,  
  455. ),  
  456. ),  
  457. ); 
  458.  
  459. return $this->add_additional_fields_schema( $schema );