PageRenderTime 59ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/shop quần áo starloveshop.com/wp-content/plugins/woocommerce/includes/api/class-wc-api-webhooks.php

https://gitlab.com/phamngsinh/baitaplon_sinhvien
PHP | 503 lines | 244 code | 120 blank | 139 comment | 39 complexity | 388c03d6197f30393d18dd6f6315f1b7 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 string $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. if ( ! empty( $status ) ) {
  115. $filter['status'] = $status;
  116. }
  117. $query = $this->query_webhooks( $filter );
  118. if ( ! current_user_can( 'read_private_shop_webhooks' ) ) {
  119. return new WP_Error( 'woocommerce_api_user_cannot_read_webhooks_count', __( 'You do not have permission to read the webhooks count', 'woocommerce' ), array( 'status' => 401 ) );
  120. }
  121. return array( 'count' => (int) $query->found_posts );
  122. }
  123. /**
  124. * Create an webhook
  125. *
  126. * @since 2.2
  127. * @param array $data parsed webhook data
  128. * @return array
  129. */
  130. public function create_webhook( $data ) {
  131. $data = isset( $data['webhook'] ) ? $data['webhook'] : array();
  132. try {
  133. // permission check
  134. if ( ! current_user_can( 'publish_shop_webhooks' ) ) {
  135. throw new WC_API_Exception( 'woocommerce_api_user_cannot_create_webhooks', __( 'You do not have permission to create webhooks', 'woocommerce' ), 401 );
  136. }
  137. $data = apply_filters( 'woocommerce_api_create_webhook_data', $data, $this );
  138. // validate topic
  139. if ( empty( $data['topic'] ) || ! $this->is_valid_topic( strtolower( $data['topic'] ) ) ) {
  140. throw new WC_API_Exception( 'woocommerce_api_invalid_webhook_topic', __( 'Webhook topic is required and must be valid', 'woocommerce' ), 400 );
  141. }
  142. // validate delivery URL
  143. if ( empty( $data['delivery_url'] ) || ! $this->is_valid_url( $data['delivery_url'] ) ) {
  144. 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 );
  145. }
  146. $webhook_data = apply_filters( 'woocommerce_new_webhook_data', array(
  147. 'post_type' => 'shop_webhook',
  148. 'post_status' => 'publish',
  149. 'ping_status' => 'closed',
  150. 'post_author' => get_current_user_id(),
  151. 'post_password' => uniqid( 'webhook_' ),
  152. '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' ) ) ),
  153. ), $data, $this );
  154. $webhook_id = wp_insert_post( $webhook_data );
  155. if ( is_wp_error( $webhook_id ) || ! $webhook_id ) {
  156. 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 );
  157. }
  158. $webhook = new WC_Webhook( $webhook_id );
  159. // set topic, delivery URL, and optional secret
  160. $webhook->set_topic( $data['topic'] );
  161. $webhook->set_delivery_url( $data['delivery_url'] );
  162. // set secret if provided, defaults to API users consumer secret
  163. $webhook->set_secret( ! empty( $data['secret'] ) ? $data['secret'] : get_user_meta( get_current_user_id(), 'woocommerce_api_consumer_secret', true ) );
  164. // send ping
  165. $webhook->deliver_ping();
  166. // HTTP 201 Created
  167. $this->server->send_status( 201 );
  168. do_action( 'woocommerce_api_create_webhook', $webhook->id, $this );
  169. return $this->get_webhook( $webhook->id );
  170. } catch ( WC_API_Exception $e ) {
  171. return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
  172. }
  173. }
  174. /**
  175. * Edit a webhook
  176. *
  177. * @since 2.2
  178. * @param int $id webhook ID
  179. * @param array $data parsed webhook data
  180. * @return array
  181. */
  182. public function edit_webhook( $id, $data ) {
  183. $data = isset( $data['webhook'] ) ? $data['webhook'] : array();
  184. try {
  185. $id = $this->validate_request( $id, 'shop_webhook', 'edit' );
  186. if ( is_wp_error( $id ) ) {
  187. return $id;
  188. }
  189. $data = apply_filters( 'woocommerce_api_edit_webhook_data', $data, $id, $this );
  190. $webhook = new WC_Webhook( $id );
  191. // update topic
  192. if ( ! empty( $data['topic'] ) ) {
  193. if ( $this->is_valid_topic( strtolower( $data['topic'] ) ) ) {
  194. $webhook->set_topic( $data['topic'] );
  195. } else {
  196. throw new WC_API_Exception( 'woocommerce_api_invalid_webhook_topic', __( 'Webhook topic must be valid', 'woocommerce' ), 400 );
  197. }
  198. }
  199. // update delivery URL
  200. if ( ! empty( $data['delivery_url'] ) ) {
  201. if ( $this->is_valid_url( $data['delivery_url'] ) ) {
  202. $webhook->set_delivery_url( $data['delivery_url'] );
  203. } else {
  204. 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 );
  205. }
  206. }
  207. // update secret
  208. if ( ! empty( $data['secret'] ) ) {
  209. $webhook->set_secret( $data['secret'] );
  210. }
  211. // update status
  212. if ( ! empty( $data['status'] ) ) {
  213. $webhook->update_status( $data['status'] );
  214. }
  215. // update user ID
  216. $webhook_data = array(
  217. 'ID' => $webhook->id,
  218. 'post_author' => get_current_user_id()
  219. );
  220. // update name
  221. if ( ! empty( $data['name'] ) ) {
  222. $webhook_data['post_title'] = $data['name'];
  223. }
  224. // update post
  225. wp_update_post( $webhook_data );
  226. do_action( 'woocommerce_api_edit_webhook', $webhook->id, $this );
  227. return $this->get_webhook( $id );
  228. } catch ( WC_API_Exception $e ) {
  229. return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
  230. }
  231. }
  232. /**
  233. * Check if the given topic is a valid webhook topic, a topic is valid if:
  234. *
  235. * + starts with `action.woocommerce_` or `action.wc_`
  236. * + it has a valid resource & event
  237. *
  238. * @since 2.2
  239. * @param string $topic webhook topic
  240. * @return bool true if valid, false otherwise
  241. */
  242. private function is_valid_topic( $topic ) {
  243. // custom topics are prefixed with woocommerce_ or wc_ are valid
  244. if ( 0 === strpos( $topic, 'action.woocommerce_' ) || 0 === strpos( $topic, 'action.wc_' ) ) {
  245. return true;
  246. }
  247. @list( $resource, $event ) = explode( '.', $topic );
  248. if ( ! isset( $resource ) || ! isset( $event ) ) {
  249. return false;
  250. }
  251. $valid_resources = apply_filters( 'woocommerce_valid_webhook_resources', array( 'coupon', 'customer', 'order', 'product' ) );
  252. $valid_events = apply_filters( 'woocommerce_valid_webhook_events', array( 'created', 'updated', 'deleted' ) );
  253. if ( in_array( $resource, $valid_resources ) && in_array( $event, $valid_events ) ) {
  254. return true;
  255. }
  256. return false;
  257. }
  258. /**
  259. * Simple check for validating a URL, it must start with http:// or https://
  260. * and pass FILTER_VALIDATE_URL validation
  261. *
  262. * @since 2.2
  263. * @param string $url delivery URL for the webhook
  264. * @return bool true if valid, false otherwise
  265. */
  266. private function is_valid_url( $url ) {
  267. // must start with http:// or https://
  268. if ( 0 !== strpos( $url, 'http://' ) && 0 !== strpos( $url, 'https://' ) ) {
  269. return false;
  270. }
  271. // must pass validation
  272. if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
  273. return false;
  274. }
  275. return true;
  276. }
  277. /**
  278. * Delete a webhook
  279. *
  280. * @since 2.2
  281. * @param int $id webhook ID
  282. * @return array
  283. */
  284. public function delete_webhook( $id ) {
  285. $id = $this->validate_request( $id, 'shop_webhook', 'delete' );
  286. if ( is_wp_error( $id ) ) {
  287. return $id;
  288. }
  289. do_action( 'woocommerce_api_delete_webhook', $id, $this );
  290. // no way to manage trashed webhooks at the moment, so force delete
  291. return $this->delete( $id, 'webhook', true );
  292. }
  293. /**
  294. * Helper method to get webhook post objects
  295. *
  296. * @since 2.2
  297. * @param array $args request arguments for filtering query
  298. * @return WP_Query
  299. */
  300. private function query_webhooks( $args ) {
  301. // set base query arguments
  302. $query_args = array(
  303. 'fields' => 'ids',
  304. 'post_type' => 'shop_webhook',
  305. );
  306. // add status argument
  307. if ( ! empty( $args['status'] ) ) {
  308. switch ( $args['status'] ) {
  309. case 'active':
  310. $query_args['post_status'] = 'publish';
  311. break;
  312. case 'paused':
  313. $query_args['post_status'] = 'draft';
  314. break;
  315. case 'disabled':
  316. $query_args['post_status'] = 'pending';
  317. break;
  318. default:
  319. $query_args['post_status'] = 'publish';
  320. }
  321. unset( $args['status'] );
  322. }
  323. $query_args = $this->merge_query_args( $query_args, $args );
  324. return new WP_Query( $query_args );
  325. }
  326. /**
  327. * Get deliveries for a webhook
  328. *
  329. * @since 2.2
  330. * @param string $webhook_id webhook ID
  331. * @param string|null $fields fields to include in response
  332. * @return array
  333. */
  334. public function get_webhook_deliveries( $webhook_id, $fields = null ) {
  335. // ensure ID is valid webhook ID
  336. $webhook_id = $this->validate_request( $webhook_id, 'shop_webhook', 'read' );
  337. if ( is_wp_error( $webhook_id ) ) {
  338. return $webhook_id;
  339. }
  340. $webhook = new WC_Webhook( $webhook_id );
  341. $logs = $webhook->get_delivery_logs();
  342. $delivery_logs = array();
  343. foreach ( $logs as $log ) {
  344. // add timestamp
  345. $log['created_at'] = $this->server->format_datetime( $log['comment']->comment_date_gmt );
  346. // remove comment object
  347. unset( $log['comment'] );
  348. $delivery_logs[] = $log;
  349. }
  350. return array( 'webhook_deliveries' => $delivery_logs );
  351. }
  352. /**
  353. * Get the delivery log for the given webhook ID and delivery ID
  354. *
  355. * @since 2.2
  356. * @param string $webhook_id webhook ID
  357. * @param string $id delivery log ID
  358. * @param string|null $fields fields to limit response to
  359. * @return array
  360. */
  361. public function get_webhook_delivery( $webhook_id, $id, $fields = null ) {
  362. // validate webhook ID
  363. $webhook_id = $this->validate_request( $webhook_id, 'shop_webhook', 'read' );
  364. if ( is_wp_error( $webhook_id ) ) {
  365. return $webhook_id;
  366. }
  367. $id = absint( $id );
  368. if ( empty( $id ) ) {
  369. return new WP_Error( 'woocommerce_api_invalid_webhook_delivery_id', __( 'Invalid webhook delivery ID', 'woocommerce' ), array( 'status' => 404 ) );
  370. }
  371. $webhook = new WC_Webhook( $webhook_id );
  372. $log = $webhook->get_delivery_log( $id );
  373. if ( ! $log ) {
  374. return new WP_Error( 'woocommerce_api_invalid_webhook_delivery_id', __( 'Invalid webhook delivery', 'woocommerce' ), array( 'status' => 400 ) );
  375. }
  376. $delivery_log = $log;
  377. // add timestamp
  378. $delivery_log['created_at'] = $this->server->format_datetime( $log['comment']->comment_date_gmt );
  379. // remove comment object
  380. unset( $delivery_log['comment'] );
  381. return array( 'webhook_delivery' => apply_filters( 'woocommerce_api_webhook_delivery_response', $delivery_log, $id, $fields, $log, $webhook_id, $this ) );
  382. }
  383. }