<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[9091] sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events: Official WordPress Events: Update source of Meetup_Client class</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://meta.trac.wordpress.org/changeset/9091">9091</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://meta.trac.wordpress.org/changeset/9091","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>coreymckrill</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2019-08-05 19:10:11 +0000 (Mon, 05 Aug 2019)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Official WordPress Events: Update source of Meetup_Client class

The WordCamp codebase has moved to GitHub, and the files in meta.svn are no
longer maintained (and will eventually be removed). There are some potential
pitfalls during deployment with using GitHub's svn bridge for svn externals,
so it has been decided to maintain a separate version of the Meetup_Client
instead. This also adds a helper method for loading required files and
instantiating the client class, since it needs to happen in multiple places
in this plugin.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpresseventsofficialwordpresseventsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-events.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/meetup/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpresseventsmeetupclassmeetupclientphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/meetup/class-meetup-client.php</a></li>
</ul>

<h3>Property Changed</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpressevents">sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<span class="cx" style="display: block; padding: 0 10px">Index: sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events
</span><span class="cx" style="display: block; padding: 0 10px">===================================================================
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events   2019-08-05 02:42:57 UTC (rev 9090)
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events    2019-08-05 19:10:11 UTC (rev 9091)
</ins><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpressevents"></a>
<div class="propset"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Property changes: sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnexternals"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: svn:externals</h4></div>
<del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-class-meetup-client.php              https://meta.svn.wordpress.org/sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/utilities/class-meetup-client.php
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpresseventsmeetupclassmeetupclientphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/meetup/class-meetup-client.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/meetup/class-meetup-client.php                         (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/meetup/class-meetup-client.php   2019-08-05 19:10:11 UTC (rev 9091)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,492 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace WordCamp\Utilities;
+use WP_Error;
+
+defined( 'WPINC' ) || die();
+
+/**
+ * Class Meetup_Client
+ *
+ * TODO Refactor this to use the API_Client base class.
+ */
+class Meetup_Client {
+       /**
+        * @var string The base URL for the API endpoints.
+        */
+       protected $api_base = 'https://api.meetup.com/';
+
+       /**
+        * @var string The API key.
+        */
+       protected $api_key = '';
+
+       /**
+        * @var bool If true, the client will fetch fewer results, for faster debugging.
+        */
+       protected $debug_mode;
+
+       /**
+        * @var WP_Error|null Container for errors.
+        */
+       public $error = null;
+
+       /**
+        * Meetup_Client constructor.
+        */
+       public function __construct() {
+               $this->error = new WP_Error();
+
+               if ( defined( 'MEETUP_API_KEY' ) ) {
+                       $this->api_key = MEETUP_API_KEY;
+               } else {
+                       $this->error->add(
+                               'api_key_undefined',
+                               'The Meetup.com API Key is undefined.'
+                       );
+               }
+
+               $this->debug_mode = apply_filters( 'wcmc_debug_mode', false );
+       }
+
+       /**
+        * Send a paginated request to the Meetup API and return the aggregated response.
+        *
+        * This automatically paginates requests and will repeat requests to ensure all results are retrieved.
+        * It also tries to account for API request limits and throttles to avoid getting a limit error.
+        *
+        * @param string $request_url The API endpoint URL to send the request to.
+        *
+        * @return array|WP_Error The results of the request.
+        */
+       protected function send_paginated_request( $request_url ) {
+               $data = array();
+
+               $request_url = add_query_arg( array(
+                       'page' => 200,
+               ), $request_url );
+
+               while ( $request_url ) {
+                       $request_url = $this->sign_request_url( $request_url );
+
+                       $response = $this->tenacious_remote_get( $request_url );
+
+                       if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
+                               $body = json_decode( wp_remote_retrieve_body( $response ), true );
+
+                               if ( isset( $body['results'] ) ) {
+                                       $new_data = $body['results'];
+                               } else {
+                                       $new_data = $body;
+                               }
+
+                               if ( is_array( $new_data ) ) {
+                                       $data = array_merge( $data, $new_data );
+                               } else {
+                                       $this->error->add(
+                                               'unexpected_response_data',
+                                               'The API response did not provide the expected data format.',
+                                               $response
+                                       );
+                                       break;
+                               }
+
+                               $request_url = $this->get_next_url( $response );
+                       } else {
+                               $this->handle_error_response( $response );
+                               break;
+                       }
+
+                       if ( $this->debug_mode ) {
+                               break;
+                       }
+               }
+
+               if ( ! empty( $this->error->get_error_messages() ) ) {
+                       return $this->error;
+               }
+
+               return $data;
+       }
+
+       /**
+        * Send a single request to the Meetup API and return the total number of results available.
+        *
+        * @param string $request_url The API endpoint URL to send the request to.
+        *
+        * @return int|WP_Error
+        */
+       protected function send_total_count_request( $request_url ) {
+               $count = 0;
+
+               $request_url = add_query_arg( array(
+                       // We're only interested in the headers, so we don't need to receive more than one result.
+                       'page' => 1,
+               ), $request_url );
+
+               $request_url = $this->sign_request_url( $request_url );
+
+               $response = $this->tenacious_remote_get( $request_url );
+
+               if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
+                       $count_header = wp_remote_retrieve_header( $response, 'X-Total-Count' );
+
+                       if ( $count_header ) {
+                               $count = absint( $count_header );
+                       } else {
+                               $this->error->add(
+                                       'unexpected_response_data',
+                                       'The API response did not provide a total count value.'
+                               );
+                       }
+               } else {
+                       $this->handle_error_response( $response );
+               }
+
+               if ( ! empty( $this->error->get_error_messages() ) ) {
+                       return $this->error;
+               }
+
+               return $count;
+       }
+
+       /**
+        * Wrapper for `wp_remote_get` to retry requests that fail temporarily for various reasons.
+        *
+        * One common example of a reason a request would fail, but later succeed, is when the first request times out.
+        *
+        * Based on `wcorg_redundant_remote_get`.
+        *
+        * @param string $url
+        * @param array  $args
+        *
+        * @return array|WP_Error
+        */
+       protected function tenacious_remote_get( $url, $args = array() ) {
+               $attempt_count = 0;
+               $max_attempts  = 3;
+
+               /*
+                * Response codes that should break the loop.
+                *
+                * See https://www.meetup.com/meetup_api/docs/#errors.
+                *
+                * `200` (ok) is not in the list, because it needs to be handled conditionally. See below.
+                *
+                * `400` (bad request) is not in the list, even though it seems like it _should_ indicate an unrecoverable
+                * error. In practice we've observed that it's common for a seemingly valid request to be rejected with
+                * a `400` response, but then get a `200` response if that exact same request is retried.
+                */
+               $breaking_codes = array(
+                       401, // Unauthorized (invalid key).
+                       429, // Too many requests (rate-limited).
+                       404, // Unable to find group
+               );
+
+               // The default of 5 seconds often results in frequent timeouts.
+               if ( empty( $args['timeout'] ) ) {
+                       $args['timeout'] = 15;
+               }
+
+               while ( $attempt_count < $max_attempts ) {
+                       $response      = wp_remote_get( $url, $args );
+                       $response_code = wp_remote_retrieve_response_code( $response );
+
+                       $this->maybe_throttle( wp_remote_retrieve_headers( $response ) );
+
+                       /*
+                        * Sometimes their API inexplicably returns a success code with an empty body, but will return a valid
+                        * response if the exact request is retried.
+                        */
+                       if ( 200 === $response_code && ! empty( wp_remote_retrieve_body( $response ) ) ) {
+                               break;
+                       }
+
+                       if ( in_array( $response_code, $breaking_codes, true ) ) {
+                               break;
+                       }
+
+                       $attempt_count++;
+
+                       /**
+                        * Action: Fires when tenacious_remote_get fails a request attempt.
+                        *
+                        * Note that the request parameter includes the request URL that contains a query string for the API key.
+                        * This should be redacted before outputting anywhere public.
+                        *
+                        * @param array $response
+                        * @param array $request
+                        * @param int   $attempt_count
+                        * @param int   $max_attempts
+                        */
+                       do_action( 'meetup_client_tenacious_remote_get_attempt', $response, compact( 'url', 'args' ), $attempt_count, $max_attempts );
+
+                       if ( $attempt_count < $max_attempts ) {
+                               $retry_after = wp_remote_retrieve_header( $response, 'retry-after' ) ?: 5;
+                               $wait        = min( $retry_after * $attempt_count, 30 );
+
+                               if ( 'cli' === php_sapi_name() ) {
+                                       echo "\nRequest failed $attempt_count times. Pausing for $wait seconds before retrying.";
+                               }
+
+                               sleep( $wait );
+                       }
+               }
+
+               if ( $attempt_count === $max_attempts && 'cli' === php_sapi_name() ) {
+                       if ( 200 !== $response_code || is_wp_error( $response ) ) {
+                               echo "\nRequest failed $attempt_count times. Giving up.";
+                       }
+               }
+
+               return $response;
+       }
+
+       /**
+        * Sign a request URL with our API key.
+        *
+        * @param string $request_url
+        *
+        * @return string
+        */
+       protected function sign_request_url( $request_url ) {
+               return add_query_arg( array(
+                       'sign' => true,
+                       'key'  => $this->api_key,
+               ), $request_url );
+       }
+
+       /**
+        * Get the URL for the next page of results from a paginated API response.
+        *
+        * @param array $response
+        *
+        * @return string
+        */
+       protected function get_next_url( $response ) {
+               $url = '';
+
+               // First try v3.
+               $links = wp_remote_retrieve_header( $response, 'link' );
+               if ( $links ) {
+                       // Meetup.com is now returning combined link headers
+                       if ( is_string( $links ) ) {
+                               $links = preg_split( '!,\s+!', $links );
+                       }
+                       foreach ( (array) $links as $link ) {
+                               if ( false !== strpos( $link, 'rel="next"' ) && preg_match( '/^<([^>]+)>/', $link, $matches ) ) {
+                                       $url = $matches[1];
+                                       break;
+                               }
+                       }
+               }
+
+               // Then try v2.
+               if ( ! $url ) {
+                       $body = json_decode( wp_remote_retrieve_body( $response ), true );
+
+                       if ( isset( $body['meta']['next'] ) ) {
+                               $url = $body['meta']['next'];
+                       }
+               }
+
+               return esc_url_raw( $url );
+       }
+
+       /**
+        * Check the rate limit status in an API response and delay further execution if necessary.
+        *
+        * @param array $headers
+        */
+       protected function maybe_throttle( $headers ) {
+               if ( ! isset( $headers['x-ratelimit-remaining'], $headers['x-ratelimit-reset'] ) ) {
+                       return;
+               }
+
+               $remaining = absint( $headers['x-ratelimit-remaining'] );
+               $period    = absint( $headers['x-ratelimit-reset'    ] );
+
+               // Pause more frequently than we need to, and for longer, just to be safe.
+               if ( $remaining > 2 ) {
+                       return;
+               }
+
+               if ( $period < 2 ) {
+                       $period = 2;
+               }
+
+               if ( 'cli' === php_sapi_name() ) {
+                       echo "\nPausing for $period seconds to avoid rate-limiting.";
+               }
+
+               sleep( $period );
+       }
+
+       /**
+        * Extract error information from an API response and add it to our error handler.
+        *
+        * @param array|WP_Error $response
+        *
+        * @return void
+        */
+       protected function handle_error_response( $response ) {
+               if ( is_wp_error( $response ) ) {
+                       $codes = $response->get_error_codes();
+
+                       foreach ( $codes as $code ) {
+                               $messages = $response->get_error_messages( $code );
+
+                               foreach ( $messages as $message ) {
+                                       $this->error->add( $code, $message );
+                               }
+                       }
+
+                       return;
+               }
+
+               $response_code = wp_remote_retrieve_response_code( $response );
+               $data          = json_decode( wp_remote_retrieve_body( $response ), true );
+
+               if ( isset( $data['errors'] ) ) {
+                       foreach ( $data['errors'] as $error ) {
+                               $this->error->add( $error['code'], $error['message'] );
+                       }
+               } elseif ( isset( $data['code'] ) && isset( $data['details'] ) ) {
+                       $this->error->add( $data['code'], $data['details'] );
+               } elseif ( $response_code ) {
+                       $this->error->add(
+                               'http_response_code',
+                               sprintf( 'HTTP Status: %d', absint( $response_code ) )
+                       );
+               } else {
+                       $this->error->add( 'unknown_error', 'There was an unknown error.' );
+               }
+       }
+
+       /**
+        * Retrieve data about groups in the Chapter program.
+        *
+        * @param array $args Optional. Additional request parameters.
+        *                    See https://www.meetup.com/meetup_api/docs/pro/:urlname/groups/.
+        *
+        * @return array|WP_Error
+        */
+       public function get_groups( array $args = array() ) {
+               $request_url = $this->api_base . 'pro/wordpress/groups';
+
+               if ( ! empty( $args ) ) {
+                       $request_url = add_query_arg( $args, $request_url );
+               }
+
+               return $this->send_paginated_request( $request_url );
+       }
+
+       /**
+        * Retrieve data about events associated with a set of groups.
+        *
+        * This automatically breaks up requests into chunks of 50 groups to avoid overloading the API.
+        *
+        * @param array $group_ids The IDs of the groups to get events for.
+        * @param array $args      Optional. Additional request parameters.
+        *                         See https://www.meetup.com/meetup_api/docs/2/events/.
+        *
+        * @return array|WP_Error
+        */
+       public function get_events( array $group_ids, array $args = array() ) {
+               $url_base     = $this->api_base . '2/events';
+               $group_chunks = array_chunk( $group_ids, 50, true ); // Meetup API sometimes throws an error with chunk size larger than 50.
+               $events       = array();
+
+               foreach ( $group_chunks as $chunk ) {
+                       $query_args = array_merge( array(
+                               'group_id' => implode( ',', $chunk ),
+                       ), $args );
+
+                       $request_url = add_query_arg( $query_args, $url_base );
+
+                       $data = $this->send_paginated_request( $request_url );
+
+                       if ( is_wp_error( $data ) ) {
+                               return $data;
+                       }
+
+                       $events = array_merge( $events, $data );
+               }
+
+               return $events;
+       }
+
+       /**
+        * Retrieve data about the group. Calls https://www.meetup.com/meetup_api/docs/:urlname/#get
+        *
+        * @param string $group_slug The slug/urlname of a group.
+        * @param array $args Optional. Additional request parameters.
+        *
+        * @return array|WP_Error
+        */
+       public function get_group_details ( $group_slug, $args = array() ) {
+               $request_url = $this->api_base . "$group_slug";
+
+               if ( ! empty( $args ) ) {
+                       $request_url = add_query_arg( $args, $request_url );
+               }
+
+               return $this->send_paginated_request( $request_url );
+       }
+
+       /**
+        * Retrieve group members. Calls https://www.meetup.com/meetup_api/docs/:urlname/members/#list
+        *
+        * @param string $group_slug The slug/urlname of a group.
+        * @param array $args Optional. Additional request parameters.
+        *
+        * @return array|WP_Error
+        */
+       public function get_group_members ( $group_slug, $args = array() ) {
+               $request_url = $this->api_base . "$group_slug/members";
+
+               if ( ! empty( $args ) ) {
+                       $request_url = add_query_arg( $args, $request_url );
+               }
+
+               return $this->send_paginated_request( $request_url );
+       }
+
+       /**
+        * Retrieve data about events associated with one particular group.
+        *
+        * @param string $group_slug The slug/urlname of a group.
+        * @param array  $args       Optional. Additional request parameters.
+        *                           See https://www.meetup.com/meetup_api/docs/:urlname/events/.
+        *
+        * @return array|WP_Error
+        */
+       public function get_group_events( $group_slug, array $args = array() ) {
+               $request_url = $this->api_base . "$group_slug/events";
+
+               if ( ! empty( $args ) ) {
+                       $request_url = add_query_arg( $args, $request_url );
+               }
+
+               return $this->send_paginated_request( $request_url );
+       }
+
+       /**
+        * Find out how many results are available for a particular request.
+        *
+        * @param string $route The Meetup.com API route to send a request to.
+        * @param array  $args  Optional. Additional request parameters.
+        *                      See https://www.meetup.com/meetup_api/docs/.
+        *
+        * @return int|WP_Error
+        */
+       public function get_result_count( $route, array $args = array() ) {
+               $request_url = $this->api_base . $route;
+
+               if ( ! empty( $args ) ) {
+                       $request_url = add_query_arg( $args, $request_url );
+               }
+
+               return $this->send_total_count_request( $request_url );
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpresseventsofficialwordpresseventsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-events.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-events.php  2019-08-05 02:42:57 UTC (rev 9090)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-events.php    2019-08-05 19:10:11 UTC (rev 9091)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -52,6 +52,25 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Get an instance of the Meetup Client, loading files first as necessary.
+        *
+        * @return Meetup_Client
+        */
+       protected function get_meetup_client() {
+               if ( ! class_exists( '\WordCamp\Utilities\Meetup_Client' ) ) {
+                       $files = array(
+                               'class-meetup-client.php',
+                       );
+
+                       foreach ( $files as $file ) {
+                               require_once trailingslashit( __DIR__ ) . "meetup/$file";
+                       }
+               }
+
+               return new Meetup_Client();
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Prime the cache of WordPress events
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * WARNING: The database table is used by api.wordpress.org/events/1.0 (and possibly by future versions), so
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -319,9 +338,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        protected function get_meetup_events() {
</span><span class="cx" style="display: block; padding: 0 10px">                $events = array();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                require_once( __DIR__ . '/class-meetup-client.php' );
-
-               $client = new Meetup_Client();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $client = $this->get_meetup_client();
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( ! empty( $client->error->errors ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->log( 'Failed to instantiate meetup client: ' . wp_json_encode( $client->error ), true );
</span><span class="cx" style="display: block; padding: 0 10px">                        return $events;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -520,8 +537,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $chunked_db_events[ $event->meetup_url ][] = $event;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                require_once( __DIR__ . '/class-meetup-client.php' );
-               $meetup_client = new Meetup_Client();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $meetup_client = $this->get_meetup_client();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! empty( $meetup_client->error->errors ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->log( 'Failed to instantiate meetup client: ' . wp_json_encode( $meetup_client->error ), true );
</span></span></pre>
</div>
</div>

</body>
</html>