<!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>[492] sites/trunk/wordpress.org/public_html/wp-content/plugins: Add WordPress Events plugin from @iandunn.</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 { 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">
<dt>Revision</dt> <dd><a href="http://meta.trac.wordpress.org/changeset/492">492</a></dd>
<dt>Author</dt> <dd>Otto42</dd>
<dt>Date</dt> <dd>2014-04-01 21:39:48 +0000 (Tue, 01 Apr 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Add WordPress Events plugin from @iandunn. See <a href="http://meta.trac.wordpress.org/ticket/407">#407</a></pre>
<h3>Added Paths</h3>
<ul>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpresseventsofficialwordpresseventphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-event.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpresseventsofficialwordpresseventsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-events.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpresseventstemplateeventsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/template-events.php</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpresseventsofficialwordpresseventphp"></a>
<div class="addfile"><h4>Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-event.php (0 => 492)</h4>
<pre class="diff"><span>
<span class="info">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-event.php (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-event.php 2014-04-01 21:39:48 UTC (rev 492)
</span><span class="lines">@@ -0,0 +1,26 @@
</span><ins>+<?php
+
+/*
+ * An official WordPress event
+ *
+ * This doesn't have any real functionality, but it exists to provide a standard data structure
+ * for events across various event types.
+ */
+class Official_WordPress_Event {
+ public $type, $title, $url, $start_timestamp, $end_timestamp, $location;
+
+ /**
+ * Constructor
+ *
+ * @param array $members
+ */
+ public function __construct( $members ) {
+ $valid_members = get_object_vars( $this );
+
+ foreach ( $members as $member => $value ) {
+ if ( array_key_exists( $member, $valid_members ) ) {
+ $this->$member = $value;
+ }
+ }
+ }
+}
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpresseventsofficialwordpresseventsphp"></a>
<div class="addfile"><h4>Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-events.php (0 => 492)</h4>
<pre class="diff"><span>
<span class="info">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-events.php (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/official-wordpress-events.php 2014-04-01 21:39:48 UTC (rev 492)
</span><span class="lines">@@ -0,0 +1,248 @@
</span><ins>+<?php
+/*
+Plugin Name: Official WordPress Events
+Description: Retrieves data on official WordPress events
+Version: 0.1
+Author: WordPress.org Meta Team
+*/
+
+class Official_WordPress_Events {
+ const WORDCAMP_API_BASE_URL = 'https://central.wordcamp.org/wp-json.php';
+ const MEETUP_API_BASE_URL = 'https://api.meetup.com/';
+ const MEETUP_MEMBER_ID = 72560962;
+ const POSTS_PER_PAGE = 50;
+
+
+ /*
+ * @todo
+ *
+ * Setup `owe_error_email_addresses` callback and MEETUP_API_KEY, etc when deploy to production
+ * Ability to feature a camp in a hero area
+ * Add a "load more" button that retrieves more events via AJAX and updates the DOM. Have each click load the next month of events?
+ */
+
+
+ /**
+ * Constructor
+ */
+ public function __construct() {
+ add_shortcode( 'official_wordpress_events', array( $this, 'render_events' ) );
+ }
+
+ /**
+ * Gather the events data and render the events template with it
+ */
+ public function render_events() {
+ $events = $this->get_all_events();
+
+ if ( $events ) {
+ require_once( __DIR__ . '/template-events.php' );
+ }
+ }
+
+ /**
+ * Get all official events
+ *
+ * @return array
+ */
+ protected function get_all_events() {
+ $events = array_merge( $this->get_wordcamp_events(), $this->get_meetup_events() );
+ usort( $events, array( $this, 'sort_events' ) );
+
+ return $events;
+ }
+
+ /**
+ * Sort events based on start timestamp
+ *
+ * This is a callback for usort()
+ *
+ * @param $a
+ * @param $b
+ * @return int
+ */
+ protected function sort_events( $a, $b ) {
+ if ( $a->start_timestamp == $b->start_timestamp ) {
+ return 0;
+ } else {
+ return $a->start_timestamp > $b->start_timestamp ? 1 : -1;
+ }
+ }
+
+ /**
+ * Retrieve events fromm the WordCamp.org API
+ *
+ * @return array
+ */
+ protected function get_wordcamp_events() {
+ $events = array();
+ $response = $this->remote_get( self::WORDCAMP_API_BASE_URL . '/posts/?type=wordcamp' );
+ $wordcamps = json_decode( wp_remote_retrieve_body( $response ) );
+
+ if ( $wordcamps ) {
+ foreach ( $wordcamps as $wordcamp ) {
+ if ( isset( $wordcamp->post_meta->{'Start Date (YYYY-mm-dd)'}[0] ) && $wordcamp->post_meta->{'Start Date (YYYY-mm-dd)'}[0] < time() ) {
+ continue;
+
+ // todo if https://github.com/WP-API/WP-API/pull/118 is merged, then finish register_json_query_vars() in WordCamp_Loader and filter this via the url
+ // restrict it to just the upcoming month?
+ }
+
+ $events[] = new Official_WordPress_Event( array(
+ 'type' => 'wordcamp',
+ 'title' => $wordcamp->title,
+ 'url' => $wordcamp->post_meta->URL[0],
+ 'start_timestamp' => $wordcamp->post_meta->{'Start Date (YYYY-mm-dd)'}[0],
+ 'end_timestamp' => $wordcamp->post_meta->{'End Date (YYYY-mm-dd)'}[0],
+ 'localtion' => $wordcamp->post_meta->Location[0]
+ ) );
+ }
+ }
+
+ return $events;
+ }
+
+ /**
+ * Get WordPress meetups from the Meetup.com API
+ *
+ * @return array
+ */
+ protected function get_meetup_events() {
+ $events = array();
+
+ if ( ! defined( 'MEETUP_API_KEY' ) || ! MEETUP_API_KEY || ! $groups = $this->get_meetup_group_ids() ) {
+ return $events;
+ }
+
+ $response = $this->remote_get( sprintf(
+ '%s2/events?group_id=%s&time=0,1m&page=%d&key=%s',
+ self::MEETUP_API_BASE_URL,
+ implode( ',', $groups ),
+ self::POSTS_PER_PAGE,
+ MEETUP_API_KEY
+ ) );
+
+ $meetups = json_decode( wp_remote_retrieve_body( $response ) );
+ if ( ! empty ( $meetups->results ) ) {
+ $meetups = $meetups->results;
+ } else {
+ $meetups = array();
+ }
+
+ if ( $meetups ) {
+ foreach ( $meetups as $meetup ) {
+ $location = array();
+ $start_timestamp = ( $meetup->time / 1000 ) + ( $meetup->utc_offset / 1000 ); // convert to seconds
+
+ foreach ( array( 'city', 'state', 'country' ) as $part ) {
+ if ( ! empty( $meetup->venue->$part ) ) {
+ if ( in_array( $part, array( 'state', 'country' ) ) ) {
+ $location[] = strtoupper( $meetup->venue->$part );
+ } else {
+ $location[] = $meetup->venue->$part;
+ }
+ }
+ }
+
+ $events[] = new Official_WordPress_Event( array(
+ 'type' => 'meetup',
+ 'title' => $meetup->name,
+ 'url' => $meetup->event_url,
+ 'start_timestamp' => $start_timestamp,
+ 'end_timestamp' => ( empty ( $meetup->duration ) ? $start_timestamp : $start_timestamp + ( $meetup->duration / 1000 ) ), // convert to seconds
+ 'location' => empty( $location ) ? '' : implode( ' ', $location )
+ ) );
+ }
+ }
+
+ return $events;
+ }
+
+ /*
+ * Gets the IDs of all of the meetup groups associated
+ *
+ * @return array
+ */
+ protected function get_meetup_group_ids() {
+ if ( ! defined( 'MEETUP_API_KEY' ) || ! MEETUP_API_KEY ) {
+ return array();
+ }
+
+ $response = $this->remote_get( sprintf(
+ '%s2/profiles?&member_id=%d&key=%s',
+ self::MEETUP_API_BASE_URL,
+ self::MEETUP_MEMBER_ID,
+ MEETUP_API_KEY
+ ) );
+
+ $group_ids = json_decode( wp_remote_retrieve_body( $response ) );
+
+ if ( ! empty ( $group_ids->results ) ) {
+ $group_ids = wp_list_pluck( $group_ids->results, 'group' );
+ $group_ids = wp_list_pluck( $group_ids, 'id' );
+ }
+
+ if ( ! isset( $group_ids ) || ! is_array( $group_ids ) ) {
+ $group_ids = array();
+ }
+
+ return $group_ids;
+ }
+
+ /**
+ * Wrapper for wp_remote_get()
+ *
+ * This adds caching and error logging/notification
+ *
+ * @param string $url
+ * @param array $args
+ * @return false|array|WP_Error False if a valid $url was not passed; otherwise the results from wp_remote_get()
+ */
+ protected function remote_get( $url, $args = array() ) {
+ $response = $error = false;
+
+ if ( $url ) {
+ $transient_key = 'owe_' . wp_hash( $url . print_r( $args, true ) );
+
+ if ( ! $response = get_transient( $transient_key ) ) {
+ $response = wp_remote_get( $url, $args );
+
+ if ( is_wp_error( $response ) ) {
+ $error = sprintf(
+ 'Recieved WP_Error message: %s; Request was to %s; Arguments were: %s',
+ implode( ', ', $response->get_error_messages() ),
+ $url,
+ print_r( $args, true )
+ );
+ } elseif ( 200 != $response['response']['code'] ) {
+ // trigger_error() has a message limit of 1024 bytes, so we truncate $response['body'] to make sure that $body doesn't get truncated.
+
+ $error = sprintf(
+ 'Recieved HTTP code: %s and body: %s. Request was to: %s; Arguments were: %s',
+ $response['response']['code'],
+ substr( sanitize_text_field( $response['body'] ), 0, 500 ),
+ $url,
+ print_r( $args, true )
+ );
+
+ $response = new WP_Error( 'woe_invalid_http_response', 'Invalid HTTP response code', $response );
+ }
+
+ if ( $error ) {
+ trigger_error( sprintf( '%s error for %s: %s', __METHOD__, parse_url( site_url(), PHP_URL_HOST ), sanitize_text_field( $error ) ), E_USER_WARNING );
+
+ if ( $to = apply_filters( 'owe_error_email_addresses', array() ) ) {
+ wp_mail( $to, sprintf( '%s error for %s', __METHOD__, parse_url( site_url(), PHP_URL_HOST ) ), sanitize_text_field( $error ) );
+ }
+ } else {
+ set_transient( $transient_key, $response, HOUR_IN_SECONDS );
+ }
+ }
+ }
+
+ return $response;
+ }
+}
+
+require_once( __DIR__ . DIRECTORY_SEPARATOR . 'official-wordpress-event.php' );
+$GLOBALS['Official_WordPress_Events'] = new Official_WordPress_Events();
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsofficialwordpresseventstemplateeventsphp"></a>
<div class="addfile"><h4>Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/template-events.php (0 => 492)</h4>
<pre class="diff"><span>
<span class="info">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/template-events.php (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/official-wordpress-events/template-events.php 2014-04-01 21:39:48 UTC (rev 492)
</span><span class="lines">@@ -0,0 +1,17 @@
</span><ins>+<div id="ofe_events">
+ <ul>
+ <?php foreach ( $events as $event ) : ?>
+
+ <li>
+ <a href="<?php echo esc_attr( esc_url( $event->url ) ); ?>">
+ <?php echo esc_html( $event->title ); ?>
+ </a><br />
+
+ <?php echo esc_html( date( 'l, F jS | g:i a', (int) $event->start_timestamp ) ); ?><br />
+
+ <?php echo esc_html( $event->location ); ?>
+ </li>
+
+ <?php endforeach; ?>
+ </ul>
+</div> <!-- end #ofe_events -->
</ins></span></pre>
</div>
</div>
</body>
</html>