Source: classes/controller/rest.php

  1. <?php
  2. namespace wpbuddy\rich_snippets;
  3. if ( ! defined( 'ABSPATH' ) ) {
  4. exit;
  5. } // Exit if accessed directly
  6. /**
  7. * Class Rest.
  8. *
  9. * Here for any REST things.
  10. *
  11. * @package wpbuddy\rich_snippets
  12. *
  13. * @since 2.0.0
  14. */
  15. class Rest_Controller {
  16. const RETURN_TYPES_ALLOWED = array( 'exact', 'all', 'required', 'parents' );
  17. /**
  18. * The instance.
  19. *
  20. * @var \wpbuddy\rich_snippets\Rest_Controller
  21. *
  22. * @since 2.0.0
  23. */
  24. protected static $_instance = null;
  25. /**
  26. * Get the singleton instance.
  27. *
  28. * Creates a new instance of the class if it does not exists.
  29. *
  30. * @return \wpbuddy\rich_snippets\Rest_Controller
  31. *
  32. * @since 2.0.0
  33. */
  34. public static function instance() {
  35. if ( null === self::$_instance ) {
  36. self::$_instance = new self;
  37. }
  38. return self::$_instance;
  39. }
  40. /**
  41. * Magic function for cloning.
  42. *
  43. * Disallow cloning as this is a singleton class.
  44. *
  45. * @since 2.0.0
  46. */
  47. protected function __clone() {
  48. }
  49. /**
  50. * Magic method for setting upt the class.
  51. *
  52. * Disallow external instances.
  53. *
  54. * @since 2.0.0
  55. */
  56. protected function __construct() {
  57. }
  58. /**
  59. * Initializes admin stuff
  60. *
  61. * @since 2.0.0
  62. */
  63. public static function init() {
  64. $instance = self::instance();
  65. # register routes
  66. $instance->register_routes();
  67. # load translations
  68. Admin_Controller::instance()->load_translations();
  69. }
  70. /**
  71. * Registers the REST routes.
  72. *
  73. * @since 2.0.0
  74. */
  75. protected function register_routes() {
  76. register_rest_route( 'wpbuddy/rich_snippets/v1', 'positions/value-select', array(
  77. 'methods' => \WP_REST_Server::READABLE,
  78. 'callback' => array( self::instance(), 'load_position_value_select' ),
  79. 'permission_callback' => function ( $request ) {
  80. return apply_filters(
  81. 'wpbuddy/rich_snippets/rest/permission',
  82. current_user_can( 'manage_options' ),
  83. $request
  84. );
  85. },
  86. 'args' => array(
  87. 'param' => array(
  88. 'validate_callback' => function ( $param, $request, $key ) {
  89. $param_groups = Admin_Position_Controller::instance()->get_params();
  90. foreach ( $param_groups as $param_list ) {
  91. if ( isset( $param_list['params'][ $param ] ) ) {
  92. return true;
  93. }
  94. }
  95. return false;
  96. },
  97. 'sanitize_callback' => function ( $param ) {
  98. return sanitize_text_field( $param );
  99. },
  100. ),
  101. ),
  102. ) );
  103. register_rest_route( 'wpbuddy/rich_snippets/v2', 'positions/value-select', array(
  104. 'methods' => \WP_REST_Server::READABLE,
  105. 'callback' => array( self::instance(), 'load_position_value_select_options' ),
  106. 'permission_callback' => function ( $request ) {
  107. return apply_filters(
  108. 'wpbuddy/rich_snippets/rest/permission',
  109. current_user_can( 'manage_options' ),
  110. $request
  111. );
  112. },
  113. 'args' => array(
  114. 'param' => array(
  115. 'validate_callback' => function ( $param, $request, $key ) {
  116. $param_groups = Admin_Position_Controller::instance()->get_params();
  117. foreach ( $param_groups as $param_list ) {
  118. if ( isset( $param_list['params'][ $param ] ) ) {
  119. return true;
  120. }
  121. }
  122. return false;
  123. },
  124. 'sanitize_callback' => function ( $param ) {
  125. return sanitize_text_field( $param );
  126. },
  127. ),
  128. ),
  129. ) );
  130. register_rest_route( 'wpbuddy/rich_snippets/v1', 'positions/value-possibilities', array(
  131. 'methods' => \WP_REST_Server::READABLE,
  132. 'callback' => array( self::instance(), 'load_position_value_possibilities' ),
  133. 'permission_callback' => function ( $request ) {
  134. return apply_filters(
  135. 'wpbuddy/rich_snippets/rest/permission',
  136. current_user_can( 'manage_options' ),
  137. $request
  138. );
  139. },
  140. 'args' => array(
  141. 'q' => array(
  142. 'required' => true,
  143. 'sanitize_callback' => function ( $param ) {
  144. return sanitize_text_field( $param );
  145. },
  146. ),
  147. 'page' => array(
  148. 'sanitize_callback' => function ( $param ) {
  149. return absint( $param );
  150. },
  151. ),
  152. 'param' => array(
  153. 'validate_callback' => function ( $param, $request, $key ) {
  154. $param_groups = Admin_Position_Controller::instance()->get_params();
  155. foreach ( $param_groups as $param_list ) {
  156. if ( isset( $param_list['params'][ $param ] ) ) {
  157. return true;
  158. }
  159. }
  160. return false;
  161. },
  162. 'sanitize_callback' => function ( $param ) {
  163. return sanitize_text_field( $param );
  164. },
  165. ),
  166. ),
  167. ) );
  168. register_rest_route( 'wpbuddy/rich_snippets/v1', '/admin/verify', array(
  169. 'methods' => \WP_REST_Server::READABLE,
  170. 'callback' => array( self::instance(), 'activate_plugin' ),
  171. 'permission_callback' => function ( $request ) {
  172. /**
  173. * REST Permission filter.
  174. *
  175. * Allows to modify the capability for the REST access.
  176. *
  177. * @hook wpbuddy/rich_snippets/rest/permission
  178. *
  179. * @param {string} $capability The capability for the REST access. Default: manage_options.
  180. * @param {WP_Rest_Request} $request
  181. *
  182. * @returns {string} The capability for the REST persmission.
  183. *
  184. * @since 2.0.0
  185. */
  186. return apply_filters(
  187. 'wpbuddy/rich_snippets/rest/permission',
  188. current_user_can( 'manage_options' ),
  189. $request
  190. );
  191. },
  192. 'args' => array(
  193. 'purchase_code' => array(
  194. 'required' => false,
  195. 'sanitize_callback' => function ( $param ) {
  196. return sanitize_text_field( $param );
  197. },
  198. ),
  199. ),
  200. ) );
  201. register_rest_route( 'wpbuddy/rich_snippets/v1', '/schemas/types', array(
  202. 'methods' => \WP_REST_Server::READABLE,
  203. 'callback' => array( self::instance(), 'get_schema_types' ),
  204. 'permission_callback' => function ( $request ) {
  205. return apply_filters(
  206. 'wpbuddy/rich_snippets/rest/permission',
  207. current_user_can( 'manage_options' ),
  208. $request
  209. );
  210. },
  211. 'args' => array(
  212. 'q' => array(
  213. 'required' => true,
  214. 'sanitize_callback' => function ( $param ) {
  215. return sanitize_text_field( $param );
  216. },
  217. ),
  218. 'page' => array(
  219. 'sanitize_callback' => function ( $param ) {
  220. return absint( $param );
  221. },
  222. ),
  223. ),
  224. ) );
  225. register_rest_route( 'wpbuddy/rich_snippets/v1', '/schema', array(
  226. 'methods' => \WP_REST_Server::READABLE,
  227. 'callback' => array( self::instance(), 'get_schema_type' ),
  228. 'permission_callback' => function ( $request ) {
  229. return apply_filters(
  230. 'wpbuddy/rich_snippets/rest/permission',
  231. current_user_can( 'manage_options' ),
  232. $request
  233. );
  234. },
  235. 'args' => array(
  236. 'type' => array(
  237. 'required' => true,
  238. 'sanitize_callback' => function ( $param ) {
  239. return sanitize_text_field( $param );
  240. },
  241. ),
  242. ),
  243. ) );
  244. register_rest_route( 'wpbuddy/rich_snippets/v1', '/schema/examples', array(
  245. 'methods' => \WP_REST_Server::READABLE,
  246. 'callback' => array( self::instance(), 'get_schema_type_examples' ),
  247. 'permission_callback' => function ( $request ) {
  248. return apply_filters(
  249. 'wpbuddy/rich_snippets/rest/permission',
  250. current_user_can( 'manage_options' ),
  251. $request
  252. );
  253. },
  254. 'args' => array(
  255. 'type' => array(
  256. 'required' => false,
  257. 'sanitize_callback' => function ( $param ) {
  258. $v = sanitize_text_field( $param );
  259. if ( 1 !== preg_match( "#http(s)?:\/\/#", $v ) ) {
  260. $v = 'http://' . $v;
  261. }
  262. return $v;
  263. },
  264. ),
  265. 'ids' => array(
  266. 'required' => false,
  267. 'type' => 'array',
  268. 'items' => [
  269. 'type' => 'integer'
  270. ]
  271. ),
  272. ),
  273. ) );
  274. register_rest_route( 'wpbuddy/rich_snippets/v1', '/schemas/recommended', array(
  275. 'methods' => \WP_REST_Server::READABLE,
  276. 'callback' => array( self::instance(), 'get_recommended_schemas' ),
  277. 'permission_callback' => function ( $request ) {
  278. return apply_filters(
  279. 'wpbuddy/rich_snippets/rest/permission',
  280. current_user_can( 'manage_options' ),
  281. $request
  282. );
  283. },
  284. ) );
  285. register_rest_route( 'wpbuddy/rich_snippets/v1', '/schemas/properties', array(
  286. 'methods' => \WP_REST_Server::READABLE,
  287. 'callback' => array( self::instance(), 'get_properties' ),
  288. 'permission_callback' => function ( $request ) {
  289. return apply_filters(
  290. 'wpbuddy/rich_snippets/rest/permission',
  291. current_user_can( 'manage_options' ),
  292. $request
  293. );
  294. },
  295. 'args' => array(
  296. 'schema_type' => array(
  297. 'required' => true,
  298. 'sanitize_callback' => function ( $param ) {
  299. $v = sanitize_text_field( $param );
  300. if ( 1 !== preg_match( "#http(s)?:\/\/#", $v ) ) {
  301. $v = 'http://' . $v;
  302. }
  303. return $v;
  304. },
  305. ),
  306. 'return_type' => array(
  307. 'required' => true,
  308. 'validate_callback' => function ( $param, $request, $key ) {
  309. return in_array( strtolower( $param ), self::RETURN_TYPES_ALLOWED );
  310. },
  311. 'sanitize_callback' => function ( $param ) {
  312. return sanitize_text_field( strtolower( $param ) );
  313. },
  314. ),
  315. 'q' => array(
  316. 'sanitize_callback' => function ( $param ) {
  317. return sanitize_text_field( $param );
  318. },
  319. ),
  320. ),
  321. ) );
  322. register_rest_route( 'wpbuddy/rich_snippets/v2', '/schemas/properties', array(
  323. 'methods' => \WP_REST_Server::READABLE,
  324. 'callback' => array( self::instance(), 'get_properties_v2' ),
  325. 'permission_callback' => function ( $request ) {
  326. return apply_filters(
  327. 'wpbuddy/rich_snippets/rest/permission',
  328. current_user_can( 'manage_options' ),
  329. $request
  330. );
  331. },
  332. 'args' => array(
  333. 'schema_type' => array(
  334. 'required' => true,
  335. 'sanitize_callback' => function ( $param ) {
  336. $v = sanitize_text_field( $param );
  337. if ( 1 !== preg_match( "#http(s)?:\/\/#", $v ) ) {
  338. $v = 'http://' . $v;
  339. }
  340. return $v;
  341. },
  342. ),
  343. 'return_type' => array(
  344. 'required' => true,
  345. 'validate_callback' => function ( $param, $request, $key ) {
  346. return in_array( strtolower( $param ), self::RETURN_TYPES_ALLOWED );
  347. },
  348. 'sanitize_callback' => function ( $param ) {
  349. return sanitize_text_field( strtolower( $param ) );
  350. },
  351. ),
  352. 'q' => array(
  353. 'sanitize_callback' => function ( $param ) {
  354. return sanitize_text_field( $param );
  355. },
  356. ),
  357. 'preconfigured' => array(
  358. 'required' => true,
  359. 'default' => false,
  360. 'sanitize_callback' => function ( $param ) {
  361. return Helper_Model::instance()->string_to_bool( $param );
  362. },
  363. ),
  364. ),
  365. ) );
  366. register_rest_route( 'wpbuddy/rich_snippets/v1', '/schemas/property', array(
  367. 'methods' => \WP_REST_Server::READABLE,
  368. 'callback' => array( self::instance(), 'get_property' ),
  369. 'permission_callback' => function ( $request ) {
  370. return apply_filters(
  371. 'wpbuddy/rich_snippets/rest/permission',
  372. current_user_can( 'manage_options' ),
  373. $request
  374. );
  375. },
  376. 'args' => array(
  377. 'property' => array(
  378. 'required' => true,
  379. 'sanitize_callback' => function ( $param ) {
  380. $v = sanitize_text_field( $param );
  381. if ( 1 !== preg_match( "#http(s)?:\/\/#", $v ) ) {
  382. $v = 'http://' . $v;
  383. }
  384. return $v;
  385. },
  386. ),
  387. ),
  388. ) );
  389. register_rest_route( 'wpbuddy/rich_snippets/v1', '/schemas/property/field-types', array(
  390. 'methods' => \WP_REST_Server::READABLE,
  391. 'callback' => array( self::instance(), 'get_property_field_types' ),
  392. 'permission_callback' => function ( $request ) {
  393. return apply_filters(
  394. 'wpbuddy/rich_snippets/rest/permission',
  395. current_user_can( 'manage_options' ),
  396. $request
  397. );
  398. },
  399. 'args' => array(
  400. 'property' => array(
  401. 'required' => true,
  402. 'sanitize_callback' => function ( $param ) {
  403. $v = sanitize_text_field( $param );
  404. if ( 1 !== preg_match( "#http(s)?:\/\/#", $v ) ) {
  405. $v = 'http://' . $v;
  406. }
  407. return $v;
  408. },
  409. ),
  410. ),
  411. ) );
  412. register_rest_route( 'wpbuddy/rich_snippets/v1', '/schemas/properties/html', array(
  413. 'methods' => \WP_REST_Server::CREATABLE,
  414. 'callback' => array( self::instance(), 'get_properties_html' ),
  415. 'permission_callback' => function ( $request ) {
  416. return apply_filters(
  417. 'wpbuddy/rich_snippets/rest/permission',
  418. current_user_can( 'manage_options' ),
  419. $request
  420. );
  421. },
  422. 'args' => array(
  423. 'properties' => array(
  424. 'validate_callback' => function ( $param, $request, $key ) {
  425. if ( ! is_array( $param ) ) {
  426. return new \WP_Error(
  427. 'wpbuddy/rich_snippets/rest/param',
  428. _x( 'Please provide a list of properties.',
  429. 'Thrown error on rest api when there was no list of properties found.',
  430. 'rich-snippets-schema' )
  431. );
  432. }
  433. return $param;
  434. },
  435. 'sanitize_callback' => function ( $param ) {
  436. return array_map( function ( $v ) {
  437. $v = sanitize_text_field( $v );
  438. if ( 1 !== preg_match( "#http(s)?:\/\/#", $v ) ) {
  439. $v = 'http://' . $v;
  440. }
  441. return $v;
  442. }, $param );
  443. },
  444. ),
  445. 'include_table' => array(
  446. 'sanitize_callback' => function ( $param ) {
  447. return filter_var( $param, FILTER_VALIDATE_BOOLEAN );
  448. },
  449. ),
  450. 'schema_type' => array(
  451. 'sanitize_callback' => function ( $param ) {
  452. $v = sanitize_text_field( $param );
  453. if ( 1 !== preg_match( "#http(s)?:\/\/#", $v ) ) {
  454. $v = 'http://' . $v;
  455. }
  456. return $v;
  457. },
  458. ),
  459. 'parent_type_id' => array(
  460. 'sanitize_callback' => function ( $param ) {
  461. return sanitize_text_field( $param );
  462. },
  463. ),
  464. 'post_id' => array(
  465. 'validate_callback' => function ( $param, $request, $key ) {
  466. # check if post exists
  467. return is_string( get_post_status( absint( $param ) ) );
  468. },
  469. 'sanitize_callback' => function ( $param ) {
  470. return absint( $param );
  471. },
  472. ),
  473. 'snippet_id' => array(
  474. 'sanitize_callback' => function ( $param ) {
  475. return sanitize_text_field( $param );
  476. },
  477. ),
  478. 'is_main_schema' => array(
  479. 'sanitize_callback' => function ( $param ) {
  480. return Helper_Model::instance()->string_to_bool( $param );
  481. },
  482. ),
  483. ),
  484. ) );
  485. register_rest_route( 'wpbuddy/rich_snippets/v1', 'form_new', array(
  486. 'methods' => \WP_REST_Server::READABLE,
  487. 'callback' => array( self::instance(), 'get_rich_snippets_form_new' ),
  488. 'permission_callback' => function ( $request ) {
  489. return apply_filters(
  490. 'wpbuddy/rich_snippets/rest/permission',
  491. current_user_can( 'manage_options' ),
  492. $request
  493. );
  494. },
  495. 'args' => array(
  496. 'post_id' => array(
  497. 'validate_callback' => function ( $param, $request, $key ) {
  498. # check if post exists
  499. return is_string( get_post_status( absint( $param ) ) );
  500. },
  501. 'sanitize_callback' => function ( $param ) {
  502. return absint( $param );
  503. },
  504. ),
  505. ),
  506. ) );
  507. register_rest_route( 'wpbuddy/rich_snippets/v1', 'snippets_forms', array(
  508. 'methods' => [ \WP_REST_Server::READABLE, \WP_REST_Server::CREATABLE ],
  509. 'callback' => array( self::instance(), 'get_rich_snippets_forms' ),
  510. 'permission_callback' => function ( $request ) {
  511. return apply_filters(
  512. 'wpbuddy/rich_snippets/rest/permission',
  513. current_user_can( 'manage_options' ),
  514. $request
  515. );
  516. },
  517. 'args' => array(
  518. 'post_id' => array(
  519. 'required' => true,
  520. 'validate_callback' => function ( $param, $request, $key ) {
  521. # check if post exists
  522. return is_string( get_post_status( absint( $param ) ) );
  523. },
  524. 'sanitize_callback' => function ( $param ) {
  525. return absint( $param );
  526. },
  527. ),
  528. 'form_data' => array(
  529. 'required' => false,
  530. 'default' => [],
  531. 'type' => 'array',
  532. 'validate_callback' => function ( $param, $request, $key ) {
  533. return is_array( $param );
  534. },
  535. 'sanitize_callback' => function ( $param ) {
  536. $data = [];
  537. foreach ( $param as $key => $value ) {
  538. if ( ! ( isset( $value['id'], $value['properties'] ) ) ) {
  539. continue;
  540. }
  541. $data[ $key ] = [
  542. 'id' => sanitize_text_field( $value['id'] ),
  543. 'loop' => isset( $value['loop'] ) ? sanitize_text_field( $value['loop'] ) : '',
  544. ];
  545. foreach ( $value['properties'] as $prop ) {
  546. $data[ $key ]['properties'][] = [
  547. 'id' => isset( $prop['id'] ) ? sanitize_text_field( $prop['id'] ) : '',
  548. 'overridable' => isset( $prop['overridable'] ) && $prop['overridable'],
  549. 'overridable_multiple' => isset( $prop['overridable_multiple'] ) && $prop['overridable_multiple'],
  550. 'ref' => isset( $prop['ref'] ) ? sanitize_text_field( $prop['ref'] ) : '',
  551. 'subfield_select' => isset( $prop['subfield_select'] ) ? sanitize_text_field( $prop['subfield_select'] ) : '',
  552. 'textfield' => isset( $prop['textfield'] ) ? sanitize_text_field( $prop['textfield'] ) : '',
  553. ];
  554. }
  555. }
  556. return $data;
  557. },
  558. ),
  559. ),
  560. ) );
  561. register_rest_route( 'wpbuddy/rich_snippets/v1', 'snippets_delete', array(
  562. 'methods' => \WP_REST_Server::CREATABLE,
  563. 'callback' => array( self::instance(), 'delete_snippets' ),
  564. 'permission_callback' => function ( $request ) {
  565. return apply_filters(
  566. 'wpbuddy/rich_snippets/rest/permission',
  567. current_user_can( 'manage_options' ),
  568. $request
  569. );
  570. },
  571. 'args' => array(
  572. 'post_id' => array(
  573. 'validate_callback' => function ( $param, $request, $key ) {
  574. # check if post exists
  575. return is_string( get_post_status( absint( $param ) ) );
  576. },
  577. 'sanitize_callback' => function ( $param ) {
  578. return absint( $param );
  579. },
  580. ),
  581. 'snippet_ids' => array(
  582. 'validate_callback' => function ( $param, $request, $key ) {
  583. return is_array( $param );
  584. },
  585. 'sanitize_callback' => function ( $param ) {
  586. if ( ! is_array( $param ) ) {
  587. return array();
  588. }
  589. return array_map( function ( $v ) {
  590. if ( ! is_scalar( $v ) ) {
  591. return '';
  592. }
  593. return sanitize_text_field( $v );
  594. }, $param );
  595. },
  596. ),
  597. ),
  598. ) );
  599. register_rest_route( 'wpbuddy/rich_snippets/v1', 'clear_cache', array(
  600. 'methods' => \WP_REST_Server::READABLE,
  601. 'callback' => array( self::instance(), 'clear_cache' ),
  602. 'permission_callback' => function ( $request ) {
  603. return apply_filters(
  604. 'wpbuddy/rich_snippets/rest/permission',
  605. current_user_can( 'manage_options' ),
  606. $request
  607. );
  608. },
  609. ) );
  610. register_rest_route( 'wpbuddy/rich_snippets/v1', 'faq', array(
  611. 'methods' => \WP_REST_Server::READABLE,
  612. 'callback' => array( self::instance(), 'support_faq_search' ),
  613. 'permission_callback' => function ( $request ) {
  614. return apply_filters(
  615. 'wpbuddy/rich_snippets/rest/permission',
  616. current_user_can( 'manage_options' ),
  617. $request
  618. );
  619. },
  620. 'args' => array(
  621. 'q' => array(
  622. 'required' => true,
  623. 'validate_callback' => function ( $param, $request, $key ) {
  624. $str = sanitize_text_field( $param );
  625. return ! empty( $str );
  626. },
  627. 'sanitize_callback' => function ( $param ) {
  628. return sanitize_text_field( $param );
  629. },
  630. ),
  631. ),
  632. ) );
  633. register_rest_route( 'wpbuddy/rich_snippets/v1', 'snips/import', array(
  634. 'methods' => \WP_REST_Server::CREATABLE,
  635. 'callback' => array( self::instance(), 'snips_import' ),
  636. 'permission_callback' => function ( $request ) {
  637. return apply_filters(
  638. 'wpbuddy/rich_snippets/rest/permission',
  639. current_user_can( 'manage_options' ),
  640. $request
  641. );
  642. },
  643. 'args' => array(
  644. 'snips' => array(
  645. 'required' => true,
  646. 'type' => 'array',
  647. 'items' => [
  648. 'type' => 'integer',
  649. 'minimum' => '1',
  650. 'require' => true,
  651. ],
  652. ),
  653. 'rulesets' => array(
  654. 'required' => false,
  655. 'type' => 'array',
  656. 'sanitize_callback' => function ( $param ) {
  657. if ( ! is_array( $param ) ) {
  658. return new \WP_Error(
  659. 'wpbuddy/rich_snippets/rest/snips/import',
  660. __( 'Rulesets is not an array.', 'rich-snippets-schema' )
  661. );
  662. }
  663. $p = [];
  664. foreach ( $param as $snip_id => $ruleset ) {
  665. $snip_id = (int) $snip_id;
  666. if ( ! array_key_exists( 'ruleGroups', $ruleset ) ) {
  667. continue;
  668. }
  669. $p[ $snip_id ] = new Position_Ruleset();
  670. foreach ( $ruleset['ruleGroups'] as $rulegroup ) {
  671. if ( ! array_key_exists( 'rules', $rulegroup ) ) {
  672. continue;
  673. }
  674. $rp = new Position_Rule_Group();
  675. foreach ( $rulegroup['rules'] as $rule ) {
  676. $r = new Position_Rule();
  677. $r->operator = sanitize_text_field( $rule['operator'] );
  678. $r->param = sanitize_text_field( $rule['param'] );
  679. $r->value = sanitize_text_field( $rule['value'] );
  680. $rp->add_rule( $r );
  681. }
  682. $p[ $snip_id ]->add_rulegroup( $rp );
  683. }
  684. }
  685. return $p;
  686. },
  687. ),
  688. 'properties' => array(
  689. 'required' => false,
  690. 'type' => 'array',
  691. 'sanitize_callback' => function ( $param ) {
  692. if ( ! is_array( $param ) ) {
  693. return new \WP_Error(
  694. 'wpbuddy/rich_snippets/rest/snips/import',
  695. __( 'Property is not an array.', 'rich-snippets-schema' )
  696. );
  697. }
  698. $test = [ 'label', 'selection', 'textValue' ];
  699. $p = [];
  700. foreach ( $param as $snip_id => $properties ) {
  701. $snip_id = (int) $snip_id;
  702. $p[ $snip_id ] = [];
  703. foreach ( $properties as $prop_id => $property ) {
  704. foreach ( $test as $t ) {
  705. if ( ! array_key_exists( $t, $property ) ) {
  706. return new \WP_Error(
  707. 'wpbuddy/rich_snippets/rest/snips/import',
  708. sprintf( __( 'Property value for %s is missing.', 'rich-snippets-schema' ), $t )
  709. );
  710. }
  711. $p[ $snip_id ][ $prop_id ][ $t ] = sanitize_text_field( $property[ $t ] );
  712. }
  713. }
  714. }
  715. return $p;
  716. },
  717. )
  718. ),
  719. ) );
  720. }
  721. /**
  722. * Performs a search on schema types
  723. *
  724. * @param \WP_REST_Request $request
  725. *
  726. * @return mixed|\WP_REST_Response
  727. * @since 2.0.0
  728. *
  729. */
  730. public function get_schema_types( $request ) {
  731. $q = $request->get_param( 'q' );
  732. $types = Schemas_Model::get_types( $q );
  733. if ( is_wp_error( $types ) ) {
  734. return $types;
  735. }
  736. return rest_ensure_response( array( 'schema_types' => $types ) );
  737. }
  738. /**
  739. * Activates the plugin.
  740. *
  741. * @param \WP_REST_Request $request
  742. *
  743. * @return mixed|\WP_REST_Response
  744. * @since 2.0.0
  745. */
  746. public function activate_plugin( $request ) {
  747. $purchase_code = $request->get_param( 'purchase_code' );
  748. update_option( 'wpb_rs/purchase_code', $purchase_code, false );
  749. update_option( 'wpb_rs/active', true, true );
  750. $verified = apply_filters( 'wpbuddy/rich_snippets/rest/activate_plugin', true, $purchase_code, $request );
  751. if ( is_wp_error( $verified ) ) {
  752. return $verified;
  753. }
  754. return rest_ensure_response( array( 'verified' => $verified ) );
  755. }
  756. /**
  757. * Fetches details about a single schema type.
  758. *
  759. * @param \WP_REST_Request $request
  760. *
  761. * @return mixed|\WP_REST_Response
  762. * @since 2.14.0
  763. *
  764. */
  765. public function get_schema_type( $request ) {
  766. $type = $request->get_param( 'type' );
  767. $type_details = Schemas_Model::get_type_details( $type );
  768. if ( is_wp_error( $type_details ) ) {
  769. return $type_details;
  770. }
  771. return rest_ensure_response( $type_details );
  772. }
  773. /**
  774. * Performs a search on schema properties.
  775. *
  776. * @param \WP_REST_Request $request
  777. *
  778. * @return mixed|\WP_REST_Response
  779. * @since 2.0.0
  780. *
  781. */
  782. public function get_properties( $request ) {
  783. $properties = Schemas_Model::get_properties( array(
  784. 'schema_type' => $request->get_param( 'schema_type' ),
  785. 'return_type' => $request->get_param( 'return_type' ),
  786. 'q' => $request->get_param( 'q' ),
  787. ) );
  788. if ( is_wp_error( $properties ) ) {
  789. return $properties;
  790. }
  791. $properties = wp_list_pluck( $properties, 'id' );
  792. return rest_ensure_response( array( 'properties' => $properties ) );
  793. }
  794. /**
  795. * Performs a search on schema properties.
  796. *
  797. * @param \WP_REST_Request $request
  798. *
  799. * @return mixed|\WP_REST_Response
  800. * @since 2.14.0
  801. *
  802. */
  803. public function get_properties_v2( $request ) {
  804. $properties = Schemas_Model::get_properties( array(
  805. 'schema_type' => $request->get_param( 'schema_type' ),
  806. 'return_type' => $request->get_param( 'return_type' ),
  807. 'q' => $request->get_param( 'q' ),
  808. 'preconfigured' => $request->get_param( 'preconfigured' )
  809. ) );
  810. if ( is_wp_error( $properties ) ) {
  811. return $properties;
  812. }
  813. $properties = wp_list_pluck( $properties, 'suggested_value', 'id' );
  814. $properties = array_map( function ( $suggested_value ) {
  815. if ( false !== stripos( $suggested_value, '://schema.org/' ) ) {
  816. $label = Helper_Model::instance()->remove_schema_url( $suggested_value );
  817. } else {
  818. $label = Fields_Model::get_label( $suggested_value );
  819. }
  820. return [
  821. 'value' => $suggested_value,
  822. 'label' => $label
  823. ];
  824. }, $properties );
  825. return rest_ensure_response( array( 'properties' => $properties ) );
  826. }
  827. /**
  828. * Gets information about a single property.
  829. *
  830. * @param \WP_REST_Request $request
  831. *
  832. * @return mixed|\WP_REST_Response
  833. * @since 2.14.0
  834. *
  835. */
  836. public function get_property( $request ) {
  837. $property = $request->get_param( 'property' );
  838. $prop = Schemas_Model::get_property_by_id( $property );
  839. if ( ! $prop instanceof Schema_Property ) {
  840. return new \WP_Error(
  841. 'wpbuddy/rich_snippets/rest/schema/property',
  842. __( 'This schema property does not exist.', 'rich-snippets-schema' )
  843. );
  844. }
  845. return rest_ensure_response( $prop );
  846. }
  847. /**
  848. * Builds a HTML form out of properties.
  849. *
  850. * @param \WP_REST_Request $request
  851. *
  852. * @return mixed|\WP_REST_Response
  853. * @since 2.0.0
  854. *
  855. */
  856. public function get_properties_html( $request ) {
  857. $prop_ids = (array) $request->get_param( 'properties' );
  858. $include_table = $request->get_param( 'include_table' );
  859. $schema_type = $request->get_param( 'schema_type' );
  860. $post_id = $request->get_param( 'post_id' );
  861. $snippet_id = $request->get_param( 'snippet_id' );
  862. $is_main_schema = $request->get_param( 'is_main_schema' );
  863. $snippet = Snippets_Model::get_snippet( $snippet_id, (int) $post_id );
  864. if ( ! $snippet instanceof Rich_Snippet ) {
  865. $snippet = new Rich_Snippet( [
  866. '_is_main_snippet' => $is_main_schema,
  867. ] );
  868. if ( ! empty( $snippet_id ) ) {
  869. $snippet->id = $snippet_id;
  870. }
  871. $snippet->type = Helper_Model::instance()->remove_schema_url( $schema_type );
  872. }
  873. $controller = rich_snippets() instanceof \wpbuddy\rich_snippets\pro\Admin_Snippets_Controller ? \wpbuddy\rich_snippets\pro\Admin_Snippets_Controller::instance() : Admin_Snippets_Controller::instance();
  874. if ( $include_table ) {
  875. $result = $controller->get_property_table( $snippet, $prop_ids,
  876. get_post( $post_id ) );
  877. } else {
  878. $result = $controller->get_property_table_elements( $snippet, $prop_ids,
  879. get_post( $post_id ) );
  880. }
  881. return rest_ensure_response( $result );
  882. }
  883. /**
  884. * Returns the HTML form to create a rich snippet.
  885. *
  886. * @param \WP_REST_Request $request
  887. *
  888. * @return mixed|\WP_REST_Response
  889. * @since 2.0.0
  890. *
  891. */
  892. public function get_rich_snippets_form_new( $request ) {
  893. $post_id = $request->get_param( 'post_id' );
  894. return rest_ensure_response( array(
  895. 'form' => $this->get_rich_snippets_form( $post_id ),
  896. ) );
  897. }
  898. /**
  899. * Get the HTML code for a snippets form.
  900. *
  901. * @param int $post_id
  902. * @param string $snippet_id
  903. *
  904. * @return string
  905. * @since 2.0.0
  906. *
  907. */
  908. private function get_rich_snippets_form( $post_id, $snippet_id = null ) {
  909. if ( is_null( $snippet_id ) ) {
  910. $snippet = new Rich_Snippet();
  911. } else {
  912. $snippet = Snippets_Model::get_snippet( $snippet_id, $post_id );
  913. if ( ! $snippet instanceof Rich_Snippet ) {
  914. $snippet = new Rich_Snippet();
  915. }
  916. }
  917. ob_start();
  918. View::admin_posts_snippet( get_post( $post_id ), $snippet );
  919. return ob_get_clean();
  920. }
  921. /**
  922. * Returns the HTML forms from existing snippets.
  923. *
  924. * @param \WP_REST_Request $request
  925. *
  926. * @return mixed|\WP_REST_Response
  927. * @since 2.0.0
  928. *
  929. */
  930. public function get_rich_snippets_forms( $request ) {
  931. $post_id = $request->get_param( 'post_id' );
  932. $form_data = $request->get_param( 'form_data' );
  933. $snippet_export = $request->get_json_params();
  934. $forms = array();
  935. if ( count( $form_data ) > 0 ) {
  936. $snippets = Snippets_Model::sanitize_and_generate_snippets( $form_data );
  937. Snippets_Model::update_snippets( $post_id, $snippets );
  938. } elseif ( is_array( $snippet_export ) ) {
  939. $snippet = Snippets_Model::convert_from_json( $snippet_export );
  940. $snippets = [ $snippet ];
  941. Snippets_Model::update_snippets( $post_id, $snippets );
  942. } else {
  943. $snippets = Snippets_Model::get_snippets( $post_id );
  944. }
  945. if ( isset( $snippets ) ) {
  946. foreach ( $snippets as $snippet_id => $rich_snippet ) {
  947. $forms[] = $this->get_rich_snippets_form( $post_id, $snippet_id );
  948. }
  949. }
  950. return rest_ensure_response( array(
  951. 'forms' => $forms,
  952. ) );
  953. }
  954. /**
  955. * Clears internal caches.
  956. *
  957. * @param \WP_REST_Request $request
  958. *
  959. * @return mixed|\WP_REST_Response
  960. * @since 2.0.0
  961. *
  962. */
  963. public function clear_cache( $request ) {
  964. $deleted = Cache_Model::clear_all_caches();
  965. return rest_ensure_response( array(
  966. 'cache_cleared' => true,
  967. 'cleared_items' => absint( $deleted ),
  968. ) );
  969. }
  970. /**
  971. * Deletes snippets from a post.
  972. *
  973. * @param \WP_REST_Request $request
  974. *
  975. * @return mixed|\WP_REST_Response
  976. * @since 2.0.0
  977. *
  978. */
  979. public function delete_snippets( $request ) {
  980. foreach ( $request->get_param( 'snippet_ids' ) as $snippet_id ) {
  981. $deleted = Snippets_Model::delete_snippet(
  982. $snippet_id,
  983. $request->get_param( 'post_id' )
  984. );
  985. if ( is_wp_error( $deleted ) ) {
  986. return $deleted;
  987. }
  988. }
  989. return rest_ensure_response( array(
  990. 'deleted' => true,
  991. ) );
  992. }
  993. /**
  994. * Search rich-snippets.io for FAQ results.
  995. *
  996. * @param \WP_REST_Request $request
  997. *
  998. * @return mixed|\WP_REST_Response
  999. *
  1000. * @since 2.3.0
  1001. */
  1002. public function support_faq_search( $request ) {
  1003. $faq_posts = WPBuddy_Model::request(
  1004. '/wp/v2/posts/?categories=10&per_page=20&search=' . urlencode( $request->get_param( 'q' ) )
  1005. );
  1006. if ( is_wp_error( $faq_posts ) ) {
  1007. return $faq_posts;
  1008. }
  1009. ob_start();
  1010. if ( count( $faq_posts ) <= 0 ) {
  1011. printf( '<li>%s</li>',
  1012. _x( 'Sorry, nothing matched your search query.', 'No FAQ entries found.', 'rich-snippets-schema' ) );
  1013. } else {
  1014. foreach ( $faq_posts as $faq_post ) {
  1015. printf(
  1016. '<li><a href="%s" target="_blank">%s</a><p class="description">%s</p></li>',
  1017. esc_url( $faq_post->link ),
  1018. strip_tags( $faq_post->title->rendered ),
  1019. wp_trim_words( strip_tags( $faq_post->excerpt->rendered ) )
  1020. );
  1021. }
  1022. }
  1023. return rest_ensure_response( [ 'html' => ob_get_clean() ] );
  1024. }
  1025. /**
  1026. * Returns field type options.
  1027. *
  1028. * @param \WP_REST_Request $request
  1029. *
  1030. * @return \WP_REST_Response|\WP_Error
  1031. * @since 2.14.0
  1032. *
  1033. */
  1034. public function get_property_field_types( $request ) {
  1035. $property_id = $request->get_param( 'property' );
  1036. $property = Schemas_Model::get_property_by_id( $property_id );
  1037. if ( ! $property instanceof Schema_Property ) {
  1038. return new \WP_Error(
  1039. 'wpbuddy/rich_snippets/rest/schema/property',
  1040. __( 'This schema property does not exist.', 'rich-snippets-schema' )
  1041. );
  1042. }
  1043. new Fields_Model();
  1044. $internal_values = call_user_func( function ( $range ) {
  1045. $options = [];
  1046. $values = Fields_Model::get_internal_values();
  1047. foreach ( $values as $schema => $fields ) {
  1048. if ( ! in_array( $schema, $range ) ) {
  1049. continue;
  1050. }
  1051. foreach ( $fields as $field ) {
  1052. $options[ $field['label'] ] = [
  1053. 'value' => $field['id'],
  1054. 'label' => $field['label'],
  1055. 'description' => isset( $field['description'] ) ? $field['description'] : ''
  1056. ];
  1057. }
  1058. }
  1059. ksort( $options, SORT_NATURAL );
  1060. return array_values( $options );
  1061. }, $property->range_includes );
  1062. $related_values = call_user_func( function ( $prop ) {
  1063. $options = [];
  1064. foreach ( Fields_Model::get_related_values( $prop ) as $schema ) {
  1065. # filter primitive types
  1066. if ( in_array( $schema, Schemas_Model::PRIMITIVE_TYPES, true ) ) {
  1067. continue;
  1068. }
  1069. $options[] = [
  1070. 'value' => $schema,
  1071. 'label' => Helper_Model::instance()->remove_schema_url( $schema ),
  1072. 'description' => '',
  1073. ];
  1074. }
  1075. return $options;
  1076. }, $property );
  1077. $type_descendants_values = call_user_func( function ( $range ) {
  1078. $options = [];
  1079. $descendants = Schemas_Model::get_type_descendants( $range );
  1080. if ( is_wp_error( $descendants ) ) {
  1081. return $options;
  1082. }
  1083. foreach ( $descendants as $type ) {
  1084. $options[] = [
  1085. 'value' => 'descendant-' . $type,
  1086. 'label' => Helper_Model::instance()->remove_schema_url( $type ),
  1087. 'description' => '',
  1088. ];
  1089. }
  1090. return $options;
  1091. }, $property->range_includes );
  1092. # Filter duplicates
  1093. return rest_ensure_response( [
  1094. 'internal' => $internal_values,
  1095. 'related' => $related_values,
  1096. 'descendants' => $type_descendants_values
  1097. ] );
  1098. }
  1099. /**
  1100. * Sanitizes schemas.
  1101. *
  1102. * @param array $data
  1103. *
  1104. * @return array|\WP_Error
  1105. * @since 2.14.0
  1106. */
  1107. public function sanitize_schema_field( $data ) {
  1108. if ( ! is_array( $data ) ) {
  1109. return new \WP_Error(
  1110. 'wpbuddy/rich_snippets/rest/sanitize_field_position',
  1111. __( 'An array is needed.', 'rich-snippets-schema' )
  1112. );
  1113. }
  1114. if ( ! isset( $data['schemas'] ) ) {
  1115. return new \WP_Error(
  1116. 'wpbuddy/rich_snippets/rest/sanitize_field_position',
  1117. __( 'An array with a key "schemas" is needed.', 'rich-snippets-schema' )
  1118. );
  1119. }
  1120. $new_data = [];
  1121. foreach ( $data['schemas'] as $schema_url => $properties ) {
  1122. $s = Helper_Model::instance()->sanitize_schema_props( $properties );
  1123. if ( count( $s[0] ) <= 0 ) {
  1124. continue;
  1125. }
  1126. $uid = uniqid( 'snip-' );
  1127. $new_data[ $uid ] = [
  1128. 'id' => $schema_url,
  1129. 'properties' => $s[0]
  1130. ];
  1131. if ( isset( $s[1] ) ) {
  1132. $new_data = array_merge( $new_data, $s[1] );
  1133. }
  1134. }
  1135. return $new_data;
  1136. }
  1137. /**
  1138. * Returns the schemas saved to a post.
  1139. *
  1140. * @param array $object
  1141. * @param string $field_name
  1142. * @param \WP_REST_Request $request
  1143. * @param string $object_type
  1144. *
  1145. * @return array
  1146. * @since 2.14.0
  1147. *
  1148. */
  1149. public function get_schema_field( $object, $field_name, $request, $object_type ) {
  1150. $meta = get_post_meta( $object['id'], '_wpb_rs_' . $field_name, true );
  1151. return $meta;
  1152. }
  1153. /**
  1154. * Updates the schema field.
  1155. *
  1156. * @param $field_value
  1157. * @param \WP_Post $object
  1158. * @param string $field_name
  1159. * @param \WP_REST_Request $request
  1160. * @param string $object_type
  1161. *
  1162. * @return bool|\WP_Error
  1163. * @since 2.14.0
  1164. */
  1165. public function update_schema_field( $field_value, $object, $field_name, $request, $object_type ) {
  1166. $snippets = Snippets_Model::sanitize_and_generate_snippets( $field_value );
  1167. $updated = Snippets_Model::update_snippets( $object->ID, $snippets );
  1168. if ( false === $updated ) {
  1169. return new \WP_Error(
  1170. 'wpbuddy/rich_snippets/rest/get_field',
  1171. __( 'An error occurred during the update.', 'rich-snippets-schema' ),
  1172. [
  1173. 'value' => $field_value,
  1174. ]
  1175. );
  1176. }
  1177. return true;
  1178. }
  1179. /**
  1180. * Sanitizes data before its processed.
  1181. *
  1182. * @param array $data
  1183. *
  1184. * @return array|\WP_Error
  1185. * @since 2.14.0
  1186. */
  1187. public function sanitize_position_field( $data ) {
  1188. $sanitized_data = [];
  1189. if ( ! isset( $data['ruleGroups'] ) ) {
  1190. return new \WP_Error(
  1191. 'wpbuddy/rich_snippets/rest/sanitize_field_position',
  1192. __( 'An array with a "ruleGroups" key is needed.', 'rich-snippets-schema' )
  1193. );
  1194. }
  1195. $i = - 1;
  1196. foreach ( $data['ruleGroups'] as $rule_group ) {
  1197. if ( ! isset( $rule_group['rules'] ) ) {
  1198. continue;
  1199. }
  1200. $i ++;
  1201. foreach ( $rule_group['rules'] as $rule ) {
  1202. if ( ! isset( $rule['param'] ) ) {
  1203. continue;
  1204. }
  1205. if ( ! isset( $rule['operator'] ) ) {
  1206. continue;
  1207. }
  1208. if ( ! isset( $rule['value'] ) ) {
  1209. continue;
  1210. }
  1211. $sanitized_data[ $i ][] = [
  1212. 'param' => sanitize_text_field( $rule['param'] ),
  1213. 'operator' => sanitize_text_field( $rule['operator'] ),
  1214. 'value' => sanitize_text_field( $rule['value'] ),
  1215. ];
  1216. }
  1217. }
  1218. return $sanitized_data;
  1219. }
  1220. /**
  1221. * Updates the position field.
  1222. *
  1223. * @param $field_value
  1224. * @param \WP_Post $object
  1225. * @param string $field_name
  1226. * @param \WP_REST_Request $request
  1227. * @param string $object_type
  1228. *
  1229. * @return bool|\WP_Error
  1230. * @since 2.14.0
  1231. */
  1232. public function update_position_field( $field_value, $object, $field_name, $request, $object_type ) {
  1233. $ruleset = new Position_Ruleset();
  1234. foreach ( $field_value as $rule_group ) {
  1235. $new_rule_group = new Position_Rule_Group();
  1236. foreach ( $rule_group['rules'] as $rule ) {
  1237. if ( ! isset( $rule['param'] ) ) {
  1238. continue;
  1239. }
  1240. if ( ! isset( $rule['operator'] ) ) {
  1241. continue;
  1242. }
  1243. if ( ! isset( $rule['value'] ) ) {
  1244. continue;
  1245. }
  1246. $new_rule = new Position_Rule();
  1247. $new_rule->param = sanitize_text_field( $rule['param'] );
  1248. $new_rule->operator = sanitize_text_field( $rule['operator'] );
  1249. $new_rule->value = sanitize_text_field( $rule['value'] );
  1250. $new_rule_group->add_rule( $new_rule );
  1251. }
  1252. $ruleset->add_rulegroup( $new_rule_group );
  1253. }
  1254. $updated = Rules_Model::update_ruleset( $object->ID, $ruleset );
  1255. if ( false === $updated ) {
  1256. return new \WP_Error(
  1257. 'wpbuddy/rich_snippets/rest/get_field',
  1258. __( 'An error occurred during the update.', 'rich-snippets-schema' ),
  1259. [
  1260. 'value' => $field_value,
  1261. ]
  1262. );
  1263. }
  1264. return true;
  1265. }
  1266. /**
  1267. * Get recommended schemas.
  1268. *
  1269. * @param \WP_REST_Request $request
  1270. *
  1271. * @return \WP_REST_Response|\WP_Error
  1272. * @since 2.14.0
  1273. *
  1274. */
  1275. public function get_recommended_schemas() {
  1276. $recommendations = [];
  1277. /**
  1278. * Check for WooCommerce
  1279. */
  1280. if ( function_exists( 'WC' ) ) {
  1281. $recommendations['product'] = [
  1282. 'detectedPlugin' => 'WooCommerce'
  1283. ];
  1284. }
  1285. /**
  1286. * Check if blog has blog posts
  1287. */
  1288. $post_count = wp_count_posts( 'post' );
  1289. if ( $post_count->publish > 0 ) {
  1290. $recommendations['article'] = [
  1291. 'detectedPlugin' => 'WordPress Core'
  1292. ];
  1293. }
  1294. /**
  1295. * Check if blog has pages
  1296. */
  1297. if ( ! isset( $recommendations['article'] ) ) {
  1298. $page_count = wp_count_posts( 'page' );
  1299. if ( $page_count->publish > 0 ) {
  1300. $recommendations['article'] = [
  1301. 'detectedPlugin' => 'WordPress Core'
  1302. ];
  1303. }
  1304. }
  1305. /**
  1306. * Check for WP Event Manager
  1307. * @see https://de.wordpress.org/plugins/wp-event-manager/
  1308. */
  1309. if ( function_exists( '\WPEM' ) ) {
  1310. $recommendations['event'] = [
  1311. 'detectedPlugin' => 'WP Event Manager'
  1312. ];
  1313. }
  1314. /**
  1315. * Check for The Events Calendar
  1316. * @see https://de.wordpress.org/plugins/the-events-calendar/
  1317. */
  1318. if ( ! isset( $recommendations['event'] ) && defined( 'TRIBE_EVENTS_FILE' ) ) {
  1319. $recommendations['event'] = [
  1320. 'detectedPlugin' => 'The Events Calendar'
  1321. ];
  1322. }
  1323. /**
  1324. * Check for WP Ultimate Recipe
  1325. * @see https://de.wordpress.org/plugins/wp-ultimate-recipe/
  1326. */
  1327. if ( ! isset( $recommendations['event'] ) && ( class_exists( '\WPUltimateRecipe' ) || class_exists( '\WPUltimateRecipePremium' ) ) ) {
  1328. $recommendations['event'] = [
  1329. 'detectedPlugin' => 'WP Ultimate Recipe'
  1330. ];
  1331. }
  1332. return rest_ensure_response( $recommendations );
  1333. }
  1334. /**
  1335. * Searches for SNIP examples.
  1336. *
  1337. * @param \WP_REST_Request $request
  1338. *
  1339. * @return mixed|\WP_REST_Response
  1340. * @since 2.14.0
  1341. */
  1342. public function get_schema_type_examples( $request ) {
  1343. $schema = $request->get_param( 'type' );
  1344. $ids = $request->get_param( 'ids' );
  1345. /**
  1346. * Get the TERM ID
  1347. */
  1348. if ( $schema ) {
  1349. $response = WPBuddy_Model::request(
  1350. '/wp/v2/schema?per_page=1&search=' . Helper_Model::instance()->remove_schema_url( $schema )
  1351. );
  1352. if ( is_wp_error( $response ) ) {
  1353. return $response;
  1354. }
  1355. if ( ! is_array( $response ) ) {
  1356. return new \WP_Error(
  1357. 'wpbuddy/rich_snippets/rest/schema_examples',
  1358. __( 'The WP-Buddy API returned an error while SNIP was searching for examples.', 'rich-snippets-schema' )
  1359. );
  1360. }
  1361. if ( ! is_array( $response ) || ( is_array( $response ) && count( $response ) <= 0 ) ) {
  1362. return new \WP_Error(
  1363. 'wpbuddy/rich_snippets/rest/schema_examples',
  1364. __( 'The WP-Buddy API did not return any Term-IDs to request for examples.', 'rich-snippets-schema' )
  1365. );
  1366. }
  1367. $tax_schema = reset( $response );
  1368. $response = WPBuddy_Model::request(
  1369. '/wp/v2/wpb-rs-sync?schema=' . $tax_schema->id
  1370. );
  1371. } elseif ( $ids ) {
  1372. $ids = array_map( function ( $id ) {
  1373. return sprintf( 'include[]=%d', $id );
  1374. }, $ids );
  1375. $response = WPBuddy_Model::request(
  1376. '/wp/v2/wpb-rs-sync?per_page=100&' . implode( '&', $ids )
  1377. );
  1378. } else {
  1379. return new \WP_Error(
  1380. 'wpbuddy/rich_snippets/rest/schema_examples',
  1381. _x( 'Either type or ids must be entered.', 'rich-snippets-schema' )
  1382. );
  1383. }
  1384. if ( is_wp_error( $response ) ) {
  1385. return $response;
  1386. }
  1387. if ( ! is_array( $response ) ) {
  1388. return new \WP_Error(
  1389. 'wpbuddy/rich_snippets/rest/schema_examples',
  1390. _x( 'The WP-Buddy API did not return any SNIPs.', 'Thrown error on rest api when there were no snip examples found.', 'rich-snippets-schema' )
  1391. );
  1392. }
  1393. $ret = array_map( function ( $obj ) {
  1394. $o = new \stdClass();
  1395. $o->id = intval( $obj->id );
  1396. $o->title = sanitize_text_field( $obj->title->rendered );
  1397. $o->link = esc_url( $obj->link );
  1398. $o->snip_code = $obj->snip_code;
  1399. $o->excerpt = sanitize_text_field( $obj->excerpt->rendered );
  1400. $o->dependencies = array_map( 'intval', (array) $obj->snip_dependencies );
  1401. return $o;
  1402. }, $response );
  1403. $ret = array_filter( $ret );
  1404. return rest_ensure_response( $ret );
  1405. }
  1406. /**
  1407. * Loads a position value select box (HTML code).
  1408. *
  1409. * @param \WP_REST_Request $request
  1410. *
  1411. * @return mixed|\WP_REST_Response
  1412. * @since 2.0.0
  1413. */
  1414. public function load_position_value_select( $request ) {
  1415. $rule = new Position_Rule();
  1416. $rule->param = $request->get_param( 'param' );
  1417. ob_start();
  1418. Admin_Position_Controller::instance()->print_value_select( $rule );
  1419. return rest_ensure_response( array(
  1420. 'select_html' => ob_get_clean(),
  1421. ) );
  1422. }
  1423. /**
  1424. * Returns the positions value select options.
  1425. *
  1426. * @param \WP_REST_Request $request
  1427. *
  1428. * @return mixed|\WP_REST_Response
  1429. * @since 2.14.0
  1430. *
  1431. */
  1432. public function load_position_value_select_options( $request ) {
  1433. $rule = new Position_Rule();
  1434. $rule->param = $request->get_param( 'param' );
  1435. $options = Admin_Position_Controller::instance()->get_value_select_options( $rule );
  1436. return rest_ensure_response( $options );
  1437. }
  1438. /**
  1439. * Loads values for a position value select2 box.
  1440. *
  1441. * @param \WP_REST_Request $request
  1442. *
  1443. * @return mixed|\WP_REST_Response
  1444. * @since 2.0.0
  1445. *
  1446. */
  1447. public function load_position_value_possibilities( $request ) {
  1448. $q = $request->get_param( 'q' );
  1449. $page = $request->get_param( 'page' );
  1450. $param = $request->get_param( 'param' );
  1451. $values = array();
  1452. $i18n = _x(
  1453. '%1$s (%2$s, %3$d)',
  1454. 'value possibilities: %1$s is the post title, %2$s is the post type, %3$d is the post ID',
  1455. 'rich-snippets-schema'
  1456. );
  1457. switch ( $param ) {
  1458. case 'user':
  1459. global $wpdb;
  1460. /**
  1461. * @var \WP_User[] $users
  1462. */
  1463. $users = get_users( [
  1464. 'search' => '*' . $q . '*',
  1465. 'search_columns' => [
  1466. 'ID',
  1467. 'user_login',
  1468. 'user_email',
  1469. 'user_nicename',
  1470. 'display_name'
  1471. ]
  1472. ] );
  1473. foreach ( $users as $user ) {
  1474. $values[ $user->ID ] = sprintf(
  1475. $i18n,
  1476. esc_attr( $user->user_login ),
  1477. 'user',
  1478. $user->ID
  1479. );
  1480. }
  1481. break;
  1482. case 'post':
  1483. case 'page_parent':
  1484. default:
  1485. global $wpdb;
  1486. $sql = "SELECT ID, post_title, post_type FROM {$wpdb->posts} WHERE ";
  1487. if ( false !== stripos( $q, 'id:' ) ) {
  1488. $q = (int) str_replace( 'id:', '', $q );
  1489. $sql .= 'ID = %d';
  1490. $sql = $wpdb->prepare( $sql, $q );
  1491. } else {
  1492. $like = sprintf( '%%%s%%', $wpdb->esc_like( $q ) );
  1493. $sql .= "(post_title LIKE '%s' OR ID = %d) AND post_status = 'publish'";
  1494. $sql = $wpdb->prepare( $sql, esc_sql( $like ), $q );
  1495. }
  1496. if ( 'page_parent' === $param ) {
  1497. $sql .= ' AND post_type = "page"';
  1498. }
  1499. $posts = $wpdb->get_results( $sql );
  1500. if ( ! is_array( $posts ) ) {
  1501. return rest_ensure_response( array(
  1502. 'values' => array(),
  1503. ) );
  1504. }
  1505. foreach ( $posts as $post ) {
  1506. $post_title = empty( $post->post_title ) ? __( '(No post title)', 'rich-snippets-schema' ) : $post->post_title;
  1507. $values[ $post->ID ] = sprintf(
  1508. $i18n,
  1509. esc_attr( $post_title ),
  1510. esc_attr( $post->post_type ),
  1511. $post->ID
  1512. );
  1513. }
  1514. }
  1515. /**
  1516. * Position value possibilities filter.
  1517. *
  1518. * Allows to modify the list of possible values for positions.
  1519. *
  1520. * @hook wpbuddy/rich_snippets/position/value_possibilities
  1521. *
  1522. * @param {array} $values The possible values.
  1523. * @param {string} $q The search term.
  1524. * @param {int} $page The page number.
  1525. * @param {string} $param
  1526. *
  1527. * @returns {array} A list of possible position values.
  1528. *
  1529. * @since 2.0.0
  1530. */
  1531. $values = apply_filters( 'wpbuddy/rich_snippets/position/value_possibilities', $values, $q, $page, $param );
  1532. return rest_ensure_response( array(
  1533. 'values' => $values,
  1534. ) );
  1535. }
  1536. /**
  1537. * Imports SNIPs that were found on rich-snippets.io
  1538. *
  1539. * @param \WP_REST_Request $request
  1540. *
  1541. * @return mixed|\WP_REST_Response
  1542. * @since 2.14.0
  1543. */
  1544. public function snips_import( $request ) {
  1545. global $wpdb;
  1546. $snipIds = $request->get_param( 'snips' );
  1547. $properties = $request->get_param( 'properties' );
  1548. $rulesets = $request->get_param( 'rulesets' );
  1549. $get_schemas = function ( $snip_ids ) {
  1550. $snip_ids = array_unique( $snip_ids );
  1551. $response = WPBuddy_Model::request(
  1552. '/wp/v2/wpb-rs-sync?include=' . implode( ',', $snip_ids )
  1553. );
  1554. if ( is_wp_error( $response ) ) {
  1555. return $response;
  1556. }
  1557. if ( ! is_array( $response ) ) {
  1558. return new \WP_Error(
  1559. 'wpbuddy/rich_snippets/rest/snips_import',
  1560. _x( 'The WP-Buddy API did not return any SNIPs.', 'Thrown error on rest api when there were no snip examples found.', 'rich-snippets-schema' )
  1561. );
  1562. }
  1563. return $response;
  1564. };
  1565. $response = $get_schemas( $snipIds );
  1566. if ( is_wp_error( $response ) ) {
  1567. return $response;
  1568. }
  1569. $messages = [];
  1570. $dependencies = [];
  1571. foreach ( $response as $snip ) {
  1572. if ( ! isset( $snip->snip_dependencies ) ) {
  1573. continue;
  1574. }
  1575. if ( ! is_array( $snip->snip_dependencies ) ) {
  1576. continue;
  1577. }
  1578. if ( count( $snip->snip_dependencies ) <= 0 ) {
  1579. continue;
  1580. }
  1581. $dependencies = array_merge_recursive( $dependencies, array_map( 'intval', $snip->snip_dependencies ) );
  1582. }
  1583. if ( count( $dependencies ) > 0 ) {
  1584. $response2 = $get_schemas( $dependencies );
  1585. if ( is_wp_error( $response2 ) ) {
  1586. return $response2;
  1587. }
  1588. $response = array_merge_recursive( $response, $response2 );
  1589. }
  1590. foreach ( $response as $snip ) {
  1591. $snip_id = intval( $snip->id );
  1592. $query = $wpdb->prepare( "SELECT post_id FROM {$wpdb->postmeta} as pm LEFT JOIN {$wpdb->posts} as p ON (pm.post_id = p.ID) WHERE pm.meta_key = '_wpb_rs_sync_id' AND pm.meta_value = %d AND p.post_status = 'publish' LIMIT 1", $snip_id );
  1593. $post_id = $wpdb->get_var( $query );
  1594. $is_new_post = false;
  1595. # Create a new Global Snippet if it does not exist
  1596. if ( is_null( $post_id ) ) {
  1597. $post_id = wp_insert_post( [
  1598. 'post_type' => 'wpb-rs-global',
  1599. 'post_title' => sanitize_text_field( $snip->title->rendered ),
  1600. 'post_status' => 'publish',
  1601. 'meta_input' => [
  1602. '_wpb_rs_sync_id' => intval( $snip_id )
  1603. ]
  1604. ] );
  1605. if ( is_wp_error( $post_id ) ) {
  1606. $messages[ $snip_id ] = new \WP_Error(
  1607. 'wpbuddy/rich_snippets/rest/snips_import',
  1608. sprintf(
  1609. __( 'Could not create global snippet during SNIP import. Got error: %s', 'rich-snippets-schema' ),
  1610. $post_id->get_error_message()
  1611. )
  1612. );
  1613. continue;
  1614. }
  1615. $is_new_post = true;
  1616. }
  1617. $post_id = intval( $post_id );
  1618. if ( is_null( $snip->snip_code ) ) {
  1619. if ( $is_new_post ) {
  1620. # delete previously created post
  1621. wp_delete_post( $post_id, true );
  1622. }
  1623. $messages[ $snip_id ] = new \WP_Error(
  1624. 'wpbuddy/rich_snippets/rest/snips_import',
  1625. __( 'Could not decode SNIP. No Global Snippet was created.', 'rich-snippets-schema' )
  1626. );
  1627. continue;
  1628. }
  1629. /**
  1630. * Now write all the post meta
  1631. */
  1632. $snippet = json_decode( json_encode( $snip->snip_code ), true );
  1633. if ( isset( $snippet['@ruleset'] ) ) {
  1634. $rules_array = $snippet['@ruleset'];
  1635. unset( $snippet['@ruleset'] );
  1636. } else {
  1637. $rules_array = [];
  1638. }
  1639. # the snippet
  1640. $snippet = Snippets_Model::convert_from_json( $snippet );
  1641. # merge properties
  1642. if ( array_key_exists( $snip_id, $properties ) ) {
  1643. $snippet = Snippets_Model::merge_props_into_schema( $snippet, $properties[ $snip_id ] );
  1644. }
  1645. Snippets_Model::update_snippets( $post_id, [ $snippet ] );
  1646. if ( ! is_array( $rules_array ) ) {
  1647. continue;
  1648. }
  1649. $rules_array = array_filter( $rules_array );
  1650. if ( count( $rules_array ) <= 0 ) {
  1651. continue;
  1652. }
  1653. $ruleset = Rules_Model::sanitize_and_convert_to_ruleset( $rules_array );
  1654. Rules_Model::update_ruleset( $post_id, $ruleset );
  1655. if ( $is_new_post ) {
  1656. $messages[ $snip_id ] = sprintf(
  1657. __( 'Created Global Snippet "%s".', 'rich-snippets-schema' ),
  1658. sanitize_text_field( $snip->title->rendered )
  1659. );
  1660. } else {
  1661. $messages[ $snip_id ] = sprintf(
  1662. __( 'Updated Global Snippet "%s".', 'rich-snippets-schema' ),
  1663. get_the_title( $post_id )
  1664. );
  1665. }
  1666. }
  1667. if ( isset( $response2 ) ) {
  1668. $messages[999999] = __( 'Some snippets were installed because they are dependent on others.', 'rich-snippets-schema' );
  1669. }
  1670. return rest_ensure_response( $messages );
  1671. }
  1672. }