PageRenderTime 55ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/woocommerce/includes/api/v2/class-wc-api-webhooks.php

https://gitlab.com/webkod3r/tripolis
PHP | 462 lines | 237 code | 107 blank | 118 comment | 32 complexity | d3d22592e9e87c7ade641589a3b6238f MD5 | raw file
  1. <?php
  2. /**
  3. * WooCommerce API Webhooks class
  4. *
  5. * Handles requests to the /webhooks endpoint
  6. *
  7. * @author WooThemes
  8. * @category API
  9. * @package WooCommerce/API
  10. * @since 2.2
  11. */
  12. if ( ! defined( 'ABSPATH' ) ) {
  13. exit; // Exit if accessed directly
  14. }
  15. class WC_API_Webhooks extends WC_API_Resource {
  16. /** @var string $base the route base */
  17. protected $base = '/webhooks';
  18. /**
  19. * Register the routes for this class
  20. *
  21. * @since 2.2
  22. * @param array $routes
  23. * @return array
  24. */
  25. public function register_routes( $routes ) {
  26. # GET|POST /webhooks
  27. $routes[ $this->base ] = array(
  28. array( array( $this, 'get_webhooks' ), WC_API_Server::READABLE ),
  29. array( array( $this, 'create_webhook' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
  30. );
  31. # GET /webhooks/count
  32. $routes[ $this->base . '/count'] = array(
  33. array( array( $this, 'get_webhooks_count' ), WC_API_Server::READABLE ),
  34. );
  35. # GET|PUT|DELETE /webhooks/<id>
  36. $routes[ $this->base . '/(?P<id>\d+)' ] = array(
  37. array( array( $this, 'get_webhook' ), WC_API_Server::READABLE ),
  38. array( array( $this, 'edit_webhook' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
  39. array( array( $this, 'delete_webhook' ), WC_API_Server::DELETABLE ),
  40. );
  41. # GET /webhooks/<id>/deliveries
  42. $routes[ $this->base . '/(?P<webhook_id>\d+)/deliveries' ] = array(
  43. array( array( $this, 'get_webhook_deliveries' ), WC_API_Server::READABLE ),
  44. );
  45. # GET /webhooks/<webhook_id>/deliveries/<id>
  46. $routes[ $this->base . '/(?P<webhook_id>\d+)/deliveries/(?P<id>\d+)' ] = array(
  47. array( array( $this, 'get_webhook_delivery' ), WC_API_Server::READABLE ),
  48. );
  49. return $routes;
  50. }
  51. /**
  52. * Get all webhooks
  53. *
  54. * @since 2.2
  55. * @param array $fields
  56. * @param array $filter
  57. * @param int $page
  58. * @return array
  59. */
  60. public function get_webhooks( $fields = null, $filter = array(), $status = null, $page = 1 ) {
  61. if ( ! empty( $status ) ) {
  62. $filter['status'] = $status;
  63. }
  64. $filter['page'] = $page;
  65. $query = $this->query_webhooks( $filter );
  66. $webhooks = array();
  67. foreach ( $query->posts as $webhook_id ) {
  68. if ( ! $this->is_readable( $webhook_id ) ) {
  69. continue;
  70. }
  71. $webhooks[] = current( $this->get_webhook( $webhook_id, $fields ) );
  72. }
  73. $this->server->add_pagination_headers( $query );
  74. return array( 'webhooks' => $webhooks );
  75. }
  76. /**
  77. * Get the webhook for the given ID
  78. *
  79. * @since 2.2
  80. * @param int $id webhook ID
  81. * @param array $fields
  82. * @return array
  83. */
  84. public function get_webhook( $id, $fields = null ) {
  85. // ensure webhook ID is valid & user has permission to read
  86. $id = $this->validate_request( $id, 'shop_webhook', 'read' );
  87. if ( is_wp_error( $id ) ) {
  88. return $id;
  89. }
  90. $webhook = new WC_Webhook( $id );
  91. $webhook_data = array(
  92. 'id' => $webhook->id,
  93. 'name' => $webhook->get_name(),
  94. 'status' => $webhook->get_status(),
  95. 'topic' => $webhook->get_topic(),
  96. 'resource' => $webhook->get_resource(),
  97. 'event' => $webhook->get_event(),
  98. 'hooks' => $webhook->get_hooks(),
  99. 'delivery_url' => $webhook->get_delivery_url(),
  100. 'created_at' => $this->server->format_datetime( $webhook->get_post_data()->post_date_gmt ),
  101. 'updated_at' => $this->server->format_datetime( $webhook->get_post_data()->post_modified_gmt ),
  102. );
  103. return array( 'webhook' => apply_filters( 'woocommerce_api_webhook_response', $webhook_data, $webhook, $fields, $this ) );
  104. }
  105. /**
  106. * Get the total number of webhooks
  107. *
  108. * @since 2.2
  109. * @param string $status
  110. * @param array $filter
  111. * @return array
  112. */
  113. public function get_webhooks_count( $status = null, $filter = array() ) {
  114. try {
  115. if ( ! current_user_can( 'read_private_shop_webhooks' ) ) {
  116. throw new WC_API_Exception( 'woocommerce_api_user_cannot_read_webhooks_count', __( 'You do not have permission to read the webhooks count', 'woocommerce' ), 401 );
  117. }
  118. if ( ! empty( $status ) ) {
  119. $filter['status'] = $status;
  120. }
  121. $query = $this->query_webhooks( $filter );
  122. return array( 'count' => (int) $query->found_posts );
  123. } catch ( WC_API_Exception $e ) {
  124. return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
  125. }
  126. }
  127. /**
  128. * Create an webhook
  129. *
  130. * @since 2.2
  131. * @param array $data parsed webhook data
  132. * @return array
  133. */
  134. public function create_webhook( $data ) {
  135. try {
  136. if ( ! isset( $data['webhook'] ) ) {
  137. throw new WC_API_Exception( 'woocommerce_api_missing_webhook_data', sprintf( __( 'No %1$s data specified to create %1$s', 'woocommerce' ), 'webhook' ), 400 );
  138. }
  139. $data = $data['webhook'];
  140. // permission check
  141. if ( ! current_user_can( 'publish_shop_webhooks' ) ) {
  142. throw new WC_API_Exception( 'woocommerce_api_user_cannot_create_webhooks', __( 'You do not have permission to create webhooks', 'woocommerce' ), 401 );
  143. }
  144. $data = apply_filters( 'woocommerce_api_create_webhook_data', $data, $this );
  145. // validate topic
  146. if ( empty( $data['topic'] ) || ! wc_is_webhook_valid_topic( strtolower( $data['topic'] ) ) ) {
  147. throw new WC_API_Exception( 'woocommerce_api_invalid_webhook_topic', __( 'Webhook topic is required and must be valid', 'woocommerce' ), 400 );
  148. }
  149. // validate delivery URL
  150. if ( empty( $data['delivery_url'] ) || ! wc_is_valid_url( $data['delivery_url'] ) ) {
  151. throw new WC_API_Exception( 'woocommerce_api_invalid_webhook_delivery_url', __( 'Webhook delivery URL must be a valid URL starting with http:// or https://', 'woocommerce' ), 400 );
  152. }
  153. $webhook_data = apply_filters( 'woocommerce_new_webhook_data', array(
  154. 'post_type' => 'shop_webhook',
  155. 'post_status' => 'publish',
  156. 'ping_status' => 'closed',
  157. 'post_author' => get_current_user_id(),
  158. 'post_password' => strlen( ( $password = uniqid( 'webhook_' ) ) ) > 20 ? substr( $password, 0, 20 ) : $password,
  159. 'post_title' => ! empty( $data['name'] ) ? $data['name'] : sprintf( __( 'Webhook created on %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Webhook created on date parsed by strftime', 'woocommerce' ) ) ),
  160. ), $data, $this );
  161. $webhook_id = wp_insert_post( $webhook_data );
  162. if ( is_wp_error( $webhook_id ) || ! $webhook_id ) {
  163. throw new WC_API_Exception( 'woocommerce_api_cannot_create_webhook', sprintf( __( 'Cannot create webhook: %s', 'woocommerce' ), is_wp_error( $webhook_id ) ? implode( ', ', $webhook_id->get_error_messages() ) : '0' ), 500 );
  164. }
  165. $webhook = new WC_Webhook( $webhook_id );
  166. // set topic, delivery URL, and optional secret
  167. $webhook->set_topic( $data['topic'] );
  168. $webhook->set_delivery_url( $data['delivery_url'] );
  169. // set secret if provided, defaults to API users consumer secret
  170. $webhook->set_secret( ! empty( $data['secret'] ) ? $data['secret'] : get_user_meta( get_current_user_id(), 'woocommerce_api_consumer_secret', true ) );
  171. // send ping
  172. $webhook->deliver_ping();
  173. // HTTP 201 Created
  174. $this->server->send_status( 201 );
  175. do_action( 'woocommerce_api_create_webhook', $webhook->id, $this );
  176. delete_transient( 'woocommerce_webhook_ids' );
  177. return $this->get_webhook( $webhook->id );
  178. } catch ( WC_API_Exception $e ) {
  179. return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
  180. }
  181. }
  182. /**
  183. * Edit a webhook
  184. *
  185. * @since 2.2
  186. * @param int $id webhook ID
  187. * @param array $data parsed webhook data
  188. * @return array
  189. */
  190. public function edit_webhook( $id, $data ) {
  191. try {
  192. if ( ! isset( $data['webhook'] ) ) {
  193. throw new WC_API_Exception( 'woocommerce_api_missing_webhook_data', sprintf( __( 'No %1$s data specified to edit %1$s', 'woocommerce' ), 'webhook' ), 400 );
  194. }
  195. $data = $data['webhook'];
  196. $id = $this->validate_request( $id, 'shop_webhook', 'edit' );
  197. if ( is_wp_error( $id ) ) {
  198. return $id;
  199. }
  200. $data = apply_filters( 'woocommerce_api_edit_webhook_data', $data, $id, $this );
  201. $webhook = new WC_Webhook( $id );
  202. // update topic
  203. if ( ! empty( $data['topic'] ) ) {
  204. if ( wc_is_webhook_valid_topic( strtolower( $data['topic'] ) ) ) {
  205. $webhook->set_topic( $data['topic'] );
  206. } else {
  207. throw new WC_API_Exception( 'woocommerce_api_invalid_webhook_topic', __( 'Webhook topic must be valid', 'woocommerce' ), 400 );
  208. }
  209. }
  210. // update delivery URL
  211. if ( ! empty( $data['delivery_url'] ) ) {
  212. if ( wc_is_valid_url( $data['delivery_url'] ) ) {
  213. $webhook->set_delivery_url( $data['delivery_url'] );
  214. } else {
  215. throw new WC_API_Exception( 'woocommerce_api_invalid_webhook_delivery_url', __( 'Webhook delivery URL must be a valid URL starting with http:// or https://', 'woocommerce' ), 400 );
  216. }
  217. }
  218. // update secret
  219. if ( ! empty( $data['secret'] ) ) {
  220. $webhook->set_secret( $data['secret'] );
  221. }
  222. // update status
  223. if ( ! empty( $data['status'] ) ) {
  224. $webhook->update_status( $data['status'] );
  225. }
  226. // update user ID
  227. $webhook_data = array(
  228. 'ID' => $webhook->id,
  229. 'post_author' => get_current_user_id()
  230. );
  231. // update name
  232. if ( ! empty( $data['name'] ) ) {
  233. $webhook_data['post_title'] = $data['name'];
  234. }
  235. // update post
  236. wp_update_post( $webhook_data );
  237. do_action( 'woocommerce_api_edit_webhook', $webhook->id, $this );
  238. delete_transient( 'woocommerce_webhook_ids' );
  239. return $this->get_webhook( $id );
  240. } catch ( WC_API_Exception $e ) {
  241. return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
  242. }
  243. }
  244. /**
  245. * Delete a webhook
  246. *
  247. * @since 2.2
  248. * @param int $id webhook ID
  249. * @return array
  250. */
  251. public function delete_webhook( $id ) {
  252. $id = $this->validate_request( $id, 'shop_webhook', 'delete' );
  253. if ( is_wp_error( $id ) ) {
  254. return $id;
  255. }
  256. do_action( 'woocommerce_api_delete_webhook', $id, $this );
  257. delete_transient( 'woocommerce_webhook_ids' );
  258. // no way to manage trashed webhooks at the moment, so force delete
  259. return $this->delete( $id, 'webhook', true );
  260. }
  261. /**
  262. * Helper method to get webhook post objects
  263. *
  264. * @since 2.2
  265. * @param array $args request arguments for filtering query
  266. * @return WP_Query
  267. */
  268. private function query_webhooks( $args ) {
  269. // Set base query arguments
  270. $query_args = array(
  271. 'fields' => 'ids',
  272. 'post_type' => 'shop_webhook',
  273. );
  274. // Add status argument
  275. if ( ! empty( $args['status'] ) ) {
  276. switch ( $args['status'] ) {
  277. case 'active':
  278. $query_args['post_status'] = 'publish';
  279. break;
  280. case 'paused':
  281. $query_args['post_status'] = 'draft';
  282. break;
  283. case 'disabled':
  284. $query_args['post_status'] = 'pending';
  285. break;
  286. default:
  287. $query_args['post_status'] = 'publish';
  288. }
  289. unset( $args['status'] );
  290. }
  291. $query_args = $this->merge_query_args( $query_args, $args );
  292. return new WP_Query( $query_args );
  293. }
  294. /**
  295. * Get deliveries for a webhook
  296. *
  297. * @since 2.2
  298. * @param string $webhook_id webhook ID
  299. * @param string|null $fields fields to include in response
  300. * @return array
  301. */
  302. public function get_webhook_deliveries( $webhook_id, $fields = null ) {
  303. // Ensure ID is valid webhook ID
  304. $webhook_id = $this->validate_request( $webhook_id, 'shop_webhook', 'read' );
  305. if ( is_wp_error( $webhook_id ) ) {
  306. return $webhook_id;
  307. }
  308. $webhook = new WC_Webhook( $webhook_id );
  309. $logs = $webhook->get_delivery_logs();
  310. $delivery_logs = array();
  311. foreach ( $logs as $log ) {
  312. // Add timestamp
  313. $log['created_at'] = $this->server->format_datetime( $log['comment']->comment_date_gmt );
  314. // Remove comment object
  315. unset( $log['comment'] );
  316. $delivery_logs[] = $log;
  317. }
  318. return array( 'webhook_deliveries' => $delivery_logs );
  319. }
  320. /**
  321. * Get the delivery log for the given webhook ID and delivery ID
  322. *
  323. * @since 2.2
  324. * @param string $webhook_id webhook ID
  325. * @param string $id delivery log ID
  326. * @param string|null $fields fields to limit response to
  327. * @return array
  328. */
  329. public function get_webhook_delivery( $webhook_id, $id, $fields = null ) {
  330. try {
  331. // Validate webhook ID
  332. $webhook_id = $this->validate_request( $webhook_id, 'shop_webhook', 'read' );
  333. if ( is_wp_error( $webhook_id ) ) {
  334. return $webhook_id;
  335. }
  336. $id = absint( $id );
  337. if ( empty( $id ) ) {
  338. throw new WC_API_Exception( 'woocommerce_api_invalid_webhook_delivery_id', __( 'Invalid webhook delivery ID', 'woocommerce' ), 404 );
  339. }
  340. $webhook = new WC_Webhook( $webhook_id );
  341. $log = $webhook->get_delivery_log( $id );
  342. if ( ! $log ) {
  343. throw new WC_API_Exception( 'woocommerce_api_invalid_webhook_delivery_id', __( 'Invalid webhook delivery', 'woocommerce' ), 400 );
  344. }
  345. $delivery_log = $log;
  346. // Add timestamp
  347. $delivery_log['created_at'] = $this->server->format_datetime( $log['comment']->comment_date_gmt );
  348. // Remove comment object
  349. unset( $delivery_log['comment'] );
  350. return array( 'webhook_delivery' => apply_filters( 'woocommerce_api_webhook_delivery_response', $delivery_log, $id, $fields, $log, $webhook_id, $this ) );
  351. } catch ( WC_API_Exception $e ) {
  352. return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
  353. }
  354. }
  355. }