PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/mailchimp-for-wp/includes/api/class-api.php

https://gitlab.com/ebrjose/comcebu
PHP | 578 lines | 294 code | 92 blank | 192 comment | 55 complexity | 2e1bc54c76038e5e8c20e701a175283d MD5 | raw file
  1. <?php
  2. /**
  3. * Takes care of requests to the Mailchimp API (version 2.0, deprecated)
  4. *
  5. * @access public
  6. * @uses WP_HTTP
  7. * @since 1.0
  8. * @deprecated 4.0
  9. */
  10. class MC4WP_API {
  11. /**
  12. * @var string The URL to the Mailchimp API
  13. */
  14. protected $api_url = 'https://api.mailchimp.com/2.0/';
  15. /**
  16. * @var string The API key to use
  17. */
  18. protected $api_key = '';
  19. /**
  20. * @var string The error message of the latest API request (if any)
  21. */
  22. protected $error_message = '';
  23. /**
  24. * @var int The error code of the last API request (if any)
  25. */
  26. protected $error_code = 0;
  27. /**
  28. * @var boolean Boolean indicating whether the user is connected with Mailchimp
  29. */
  30. protected $connected;
  31. /**
  32. * @var object The full response object of the latest API call
  33. */
  34. protected $last_response;
  35. /**
  36. * Constructor
  37. *
  38. * @param string $api_key
  39. */
  40. public function __construct( $api_key ) {
  41. $this->api_key = $api_key;
  42. $dash_position = strpos( $api_key, '-' );
  43. if ( $dash_position !== false ) {
  44. $this->api_url = 'https://' . substr( $api_key, $dash_position + 1 ) . '.api.mailchimp.com/2.0/';
  45. }
  46. }
  47. /**
  48. * Show an error message to administrators
  49. *
  50. * @param string $message
  51. *
  52. * @return bool
  53. */
  54. private function show_error( $message ) {
  55. if ( ! is_admin() || ! current_user_can( 'manage_options' ) ) {
  56. return false;
  57. }
  58. if ( ! function_exists( 'add_settings_error' ) ) {
  59. return false;
  60. }
  61. add_settings_error( 'mc4wp-api', 'mc4wp-api-error', $message, 'error' );
  62. return true;
  63. }
  64. /**
  65. * @param $message
  66. *
  67. * @return bool
  68. */
  69. private function show_connection_error( $message ) {
  70. $message .= '<br /><br />' . sprintf( '<a href="%s">' . __( 'Read more about common connectivity issues.', 'mailchimp-for-wp' ) . '</a>', 'https://www.mc4wp.com/kb/solving-connectivity-issues/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=settings-notice' );
  71. return $this->show_error( $message );
  72. }
  73. /**
  74. * Pings the Mailchimp API to see if we're connected
  75. *
  76. * The result is cached to ensure a maximum of 1 API call per page load
  77. *
  78. * @return boolean
  79. */
  80. public function is_connected() {
  81. if ( is_bool( $this->connected ) ) {
  82. return $this->connected;
  83. }
  84. $result = $this->call( 'helper/ping' );
  85. $this->connected = false;
  86. if ( is_object( $result ) ) {
  87. // Msg key set? All good then!
  88. if ( ! empty( $result->msg ) ) {
  89. $this->connected = true;
  90. return true;
  91. }
  92. // Uh oh. We got an error back.
  93. if ( isset( $result->error ) ) {
  94. $this->show_error( 'Mailchimp Error: ' . $result->error );
  95. }
  96. }
  97. return $this->connected;
  98. }
  99. /**
  100. * Sends a subscription request to the Mailchimp API
  101. *
  102. * @param string $list_id The list id to subscribe to
  103. * @param string $email The email address to subscribe
  104. * @param array $merge_vars Array of extra merge variables
  105. * @param string $email_type The email type to send to this email address. Possible values are `html` and `text`.
  106. * @param boolean $double_optin Should this email be confirmed via double opt-in?
  107. * @param boolean $update_existing Update information if this email is already on list?
  108. * @param boolean $replace_interests Replace interest groupings, only if update_existing is true.
  109. * @param boolean $send_welcome Send a welcome e-mail, only if double_optin is false.
  110. *
  111. * @return boolean|string True if success, 'error' if error
  112. */
  113. public function subscribe( $list_id, $email, array $merge_vars = array(), $email_type = 'html', $double_optin = true, $update_existing = false, $replace_interests = true, $send_welcome = false ) {
  114. $data = array(
  115. 'id' => $list_id,
  116. 'email' => array( 'email' => $email ),
  117. 'merge_vars' => $merge_vars,
  118. 'email_type' => $email_type,
  119. 'double_optin' => $double_optin,
  120. 'update_existing' => $update_existing,
  121. 'replace_interests' => $replace_interests,
  122. 'send_welcome' => $send_welcome,
  123. );
  124. $response = $this->call( 'lists/subscribe', $data );
  125. if ( is_object( $response ) && isset( $response->email ) ) {
  126. return true;
  127. }
  128. return false;
  129. }
  130. /**
  131. * Gets the Groupings for a given List
  132. * @param int $list_id
  133. * @return array|boolean
  134. */
  135. public function get_list_groupings( $list_id ) {
  136. $result = $this->call( 'lists/interest-groupings', array( 'id' => $list_id ) );
  137. if ( is_array( $result ) ) {
  138. return $result;
  139. }
  140. return false;
  141. }
  142. /**
  143. * @param array $list_ids Array of ID's of the lists to fetch. (optional)
  144. *
  145. * @return bool
  146. */
  147. public function get_lists( $list_ids = array() ) {
  148. $args = array(
  149. 'limit' => 100,
  150. 'sort_field' => 'web',
  151. 'sort_dir' => 'ASC',
  152. );
  153. // set filter if the $list_ids parameter was set
  154. if ( count( $list_ids ) > 0 ) {
  155. $args['filters'] = array(
  156. 'list_id' => implode( ',', $list_ids ),
  157. );
  158. }
  159. $result = $this->call( 'lists/list', $args );
  160. if ( is_object( $result ) && isset( $result->data ) ) {
  161. return $result->data;
  162. }
  163. return false;
  164. }
  165. /**
  166. * Get the lists an email address is subscribed to
  167. *
  168. * @param array|string $email
  169. *
  170. * @return array
  171. */
  172. public function get_lists_for_email( $email ) {
  173. if ( is_string( $email ) ) {
  174. $email = array(
  175. 'email' => $email,
  176. );
  177. }
  178. $result = $this->call( 'helper/lists-for-email', array( 'email' => $email ) );
  179. if ( ! is_array( $result ) ) {
  180. return array();
  181. }
  182. return $result;
  183. }
  184. /**
  185. * Get lists with their merge_vars for a given array of list id's
  186. * @param array $list_ids
  187. * @return array|bool
  188. */
  189. public function get_lists_with_merge_vars( $list_ids ) {
  190. $result = $this->call( 'lists/merge-vars', array( 'id' => $list_ids ) );
  191. if ( is_object( $result ) && isset( $result->data ) ) {
  192. return $result->data;
  193. }
  194. return false;
  195. }
  196. /**
  197. * Gets the member info for one or multiple emails on a list
  198. *
  199. * @param string $list_id
  200. * @param array $emails
  201. * @return array|bool
  202. */
  203. public function get_subscriber_info( $list_id, $emails ) {
  204. if ( is_string( $emails ) ) {
  205. $emails = array( $emails );
  206. }
  207. $result = $this->call(
  208. 'lists/member-info',
  209. array(
  210. 'id' => $list_id,
  211. 'emails' => $emails,
  212. )
  213. );
  214. if ( is_object( $result ) && isset( $result->data ) ) {
  215. return $result->data;
  216. }
  217. return false;
  218. }
  219. /**
  220. * Checks if an email address is on a given list
  221. *
  222. * @param string $list_id
  223. * @param string $email
  224. * @return bool
  225. */
  226. public function list_has_subscriber( $list_id, $email ) {
  227. $member_info = $this->get_subscriber_info( $list_id, array( array( 'email' => $email ) ) );
  228. if ( is_array( $member_info ) && isset( $member_info[0] ) ) {
  229. return ( $member_info[0]->status === 'subscribed' );
  230. }
  231. return false;
  232. }
  233. /**
  234. * @param string $list_id
  235. * @param array|string $email
  236. * @param array $merge_vars
  237. * @param string $email_type
  238. * @param bool $replace_interests
  239. *
  240. * @return bool
  241. */
  242. public function update_subscriber( $list_id, $email, $merge_vars = array(), $email_type = 'html', $replace_interests = false ) {
  243. // default to using email for updating
  244. if ( is_string( $email ) ) {
  245. $email = array(
  246. 'email' => $email,
  247. );
  248. }
  249. $result = $this->call(
  250. 'lists/update-member',
  251. array(
  252. 'id' => $list_id,
  253. 'email' => $email,
  254. 'merge_vars' => $merge_vars,
  255. 'email_type' => $email_type,
  256. 'replace_interests' => $replace_interests,
  257. )
  258. );
  259. if ( is_object( $result ) ) {
  260. if ( isset( $result->error ) ) {
  261. return false;
  262. } else {
  263. return true;
  264. }
  265. }
  266. return false;
  267. }
  268. /**
  269. * Unsubscribes the given email or luid from the given Mailchimp list
  270. *
  271. * @param string $list_id
  272. * @param array|string $struct
  273. * @param bool $delete_member
  274. * @param bool $send_goodbye
  275. * @param bool $send_notification
  276. *
  277. * @return bool
  278. */
  279. public function unsubscribe( $list_id, $struct, $send_goodbye = true, $send_notification = false, $delete_member = false ) {
  280. if ( ! is_array( $struct ) ) {
  281. // assume $struct is an email
  282. $struct = array(
  283. 'email' => $struct,
  284. );
  285. }
  286. $response = $this->call(
  287. 'lists/unsubscribe',
  288. array(
  289. 'id' => $list_id,
  290. 'email' => $struct,
  291. 'delete_member' => $delete_member,
  292. 'send_goodbye' => $send_goodbye,
  293. 'send_notify' => $send_notification,
  294. )
  295. );
  296. if ( is_object( $response ) ) {
  297. if ( isset( $response->complete ) && $response->complete ) {
  298. return true;
  299. }
  300. }
  301. return false;
  302. }
  303. /**
  304. * @see https://apidocs.mailchimp.com/api/2.0/ecomm/order-add.php
  305. *
  306. * @param array $order_data
  307. *
  308. * @return boolean
  309. */
  310. public function add_ecommerce_order( array $order_data ) {
  311. $response = $this->call( 'ecomm/order-add', array( 'order' => $order_data ) );
  312. if ( is_object( $response ) ) {
  313. // complete means success
  314. if ( isset( $response->complete ) && $response->complete ) {
  315. return true;
  316. }
  317. // if order was already added: great
  318. if ( isset( $response->code, $response->error ) && $response->code == 330 && strpos( $response->error, 'already been recorded' ) !== false ) {
  319. return true;
  320. }
  321. }
  322. return false;
  323. }
  324. /**
  325. * @see https://apidocs.mailchimp.com/api/2.0/ecomm/order-del.php
  326. *
  327. * @param string $store_id
  328. * @param string $order_id
  329. *
  330. * @return bool
  331. */
  332. public function delete_ecommerce_order( $store_id, $order_id ) {
  333. $data = array(
  334. 'store_id' => $store_id,
  335. 'order_id' => $order_id,
  336. );
  337. $response = $this->call( 'ecomm/order-del', $data );
  338. if ( is_object( $response ) ) {
  339. if ( isset( $response->complete ) && $response->complete ) {
  340. return true;
  341. }
  342. // Invalid order (order not existing). Good!
  343. if ( isset( $response->code ) && $response->code == 330 ) {
  344. return true;
  345. }
  346. }
  347. return false;
  348. }
  349. /**
  350. * Calls the Mailchimp API
  351. *
  352. * @uses WP_HTTP
  353. *
  354. * @param string $method
  355. * @param array $data
  356. *
  357. * @return object|bool
  358. */
  359. public function call( $method, array $data = array() ) {
  360. $this->empty_last_response();
  361. // do not make request when no api key was provided.
  362. if ( empty( $this->api_key ) ) {
  363. $this->error_message = 'Missing Mailchimp API key.';
  364. $this->error_code = 001;
  365. return false;
  366. }
  367. // do not make request if helper/ping failed already
  368. if ( $this->connected === false ) {
  369. return false;
  370. }
  371. $data['apikey'] = $this->api_key;
  372. $url = $this->api_url . $method . '.json';
  373. $request_args = array(
  374. 'body' => $data,
  375. 'timeout' => 10,
  376. 'headers' => $this->get_headers(),
  377. 'sslverify' => apply_filters( 'mc4wp_use_sslverify', true ),
  378. );
  379. $response = wp_remote_post( $url, $request_args );
  380. try {
  381. $response = $this->parse_response( $response );
  382. } catch ( Exception $e ) {
  383. $this->error_code = $e->getCode();
  384. $this->error_message = $e->getMessage();
  385. $this->show_connection_error( $e->getMessage() );
  386. return false;
  387. }
  388. // store response
  389. $this->last_response = $response;
  390. // store error (if any)
  391. if ( is_object( $response ) ) {
  392. if ( ! empty( $response->error ) ) {
  393. $this->error_message = $response->error;
  394. }
  395. // store error code (if any)
  396. if ( ! empty( $response->code ) ) {
  397. $this->error_code = (int) $response->code;
  398. }
  399. }
  400. return $response;
  401. }
  402. /**
  403. * Checks if an error occured in the most recent request
  404. * @return boolean
  405. */
  406. public function has_error() {
  407. return ( ! empty( $this->error_message ) );
  408. }
  409. /**
  410. * Gets the most recent error message
  411. * @return string
  412. */
  413. public function get_error_message() {
  414. return $this->error_message;
  415. }
  416. /**
  417. * Gets the most recent error code
  418. *
  419. * @return int
  420. */
  421. public function get_error_code() {
  422. return $this->error_code;
  423. }
  424. /**
  425. * Get the most recent response object
  426. *
  427. * @return object
  428. */
  429. public function get_last_response() {
  430. return $this->last_response;
  431. }
  432. /**
  433. * Empties all data from previous response
  434. */
  435. private function empty_last_response() {
  436. $this->last_response = null;
  437. $this->error_code = 0;
  438. $this->error_message = '';
  439. }
  440. /**
  441. * Get the request headers to send to the Mailchimp API
  442. *
  443. * @return array
  444. */
  445. private function get_headers() {
  446. global $wp_version;
  447. $headers = array(
  448. 'Accept' => 'application/json',
  449. 'User-Agent' => 'mc4wp/' . MC4WP_VERSION . '; WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ),
  450. );
  451. // Copy Accept-Language from browser headers
  452. if ( ! empty( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
  453. $headers['Accept-Language'] = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
  454. }
  455. return $headers;
  456. }
  457. /**
  458. * @param array|WP_Error $response
  459. * @return object
  460. * @throws Exception
  461. */
  462. private function parse_response( $response ) {
  463. if ( is_wp_error( $response ) ) {
  464. throw new Exception( 'Error connecting to Mailchimp. ' . $response->get_error_message(), (int) $response->get_error_code() );
  465. }
  466. // decode response body
  467. $body = wp_remote_retrieve_body( $response );
  468. $data = json_decode( $body );
  469. if ( ! is_null( $data ) ) {
  470. return $data;
  471. }
  472. $code = (int) wp_remote_retrieve_response_code( $response );
  473. $message = wp_remote_retrieve_response_message( $response );
  474. if ( $code !== 200 ) {
  475. $message = sprintf( 'The Mailchimp API server returned the following response: <em>%s %s</em>.', $code, $message );
  476. // check for Akamai firewall response
  477. if ( $code === 403 ) {
  478. preg_match( '/Reference (.*)/i', $body, $matches );
  479. if ( ! empty( $matches[1] ) ) {
  480. $message .= '</strong><br /><br />' . sprintf( 'This usually means that your server is blacklisted by Mailchimp\'s firewall. Please contact Mailchimp support with the following reference number: %s </strong>', $matches[1] );
  481. }
  482. }
  483. }
  484. throw new Exception( $message, $code );
  485. }
  486. }