<!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>[8083] sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt: WCPT: Send notifications to community-events or community-team slack channel on status changes in WordCamp/Meetup applications</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/8083">8083</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/8083","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>vedjain</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2019-01-15 09:06:15 +0000 (Tue, 15 Jan 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'>WCPT: Send notifications to community-events or community-team slack channel on status changes in WordCamp/Meetup applications

Whenever a WordCamp/Meetup application is received, accepted or declined, we will send out notifications. This will help in surfacing interesting changes happening in the community.
This is also discussed in https://make.wordpress.org/community/2018/12/24/slack-notifications-for-wordcamp-and-meetup-application-updates/</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcpteventclasseventadminphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/class-event-admin.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcpteventclasseventapplicationphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/class-event-application.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptloaderphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-loader.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptmeetupclassmeetupadminphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/class-meetup-admin.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptmeetupclassmeetupapplicationphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/class-meetup-application.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptmeetupmeetupphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/meetup.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptwordcampclasswordcampapplicationphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-wordcamp/class-wordcamp-application.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptwordcampwordcampadminphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-wordcamp/wordcamp-admin.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcpteventnotificationphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/notification.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcpteventclasseventadminphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/class-event-admin.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/class-event-admin.php     2019-01-15 03:54:51 UTC (rev 8082)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/class-event-admin.php       2019-01-15 09:06:15 UTC (rev 8083)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -55,10 +55,15 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'transition_post_status', array( $this, 'log_status_changes' ), 10, 3 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                add_action( 'transition_post_status', array( $this, 'notify_application_status_in_slack' ), 10, 3 );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 add_filter( 'redirect_post_location', array( $this, 'add_admin_notices_to_redirect_url' ), 10, 2 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Admin notices
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'admin_notices', array( $this, 'print_admin_notices' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               add_action( 'send_decline_notification_action',  'Event_Admin::send_decline_notification', 10, 3 );
+
</ins><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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -277,6 +282,46 @@
</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">+         * Hooked to `transition_post_status`, will send notifications to community slack channels based whenever an application status changes to something that we are interested in. Most likely would be when an application is declined or accepted.
+        *
+        * @param string  $new_status New status
+        * @param string  $old_status Old Status
+        * @param WP_Post $event
+        */
+       abstract public function notify_application_status_in_slack( $new_status, $old_status, WP_Post $event );
+
+       /**
+        * Schedule notificaiton for declined application. Currently supports WordCamp and Meetup
+        *
+        * @param WP_Post $event Event object
+        * @param string  $label Could be WordCamp or Meetup
+        * @param string  $location
+        *
+        * @return bool|string
+        */
+       public static function schedule_decline_notification( $event, $label, $location ) {
+               wp_schedule_single_event( time() + DAY_IN_SECONDS, 'send_decline_notification_action', array( $event->ID, $label, $location ) );
+       }
+
+       /**
+        * Send declined notification to community team slack channel.
+        *
+        * @param int    $event_id
+        * @param string $label
+        * @param string $location
+        */
+       public static function send_decline_notification( $event_id, $label, $location ) {
+               $message = sprintf(
+                       "A %s application for %s has been declined, and the applicant has been informed via email.",
+                       $label,
+                       $location
+               );
+
+               $attachment = create_event_status_attachment( $message, $event_id, '' );
+               wcpt_slack_notify( COMMUNITY_TEAM_SLACK, $attachment );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Load common admin side scripts
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function common_admin_scripts() {
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcpteventclasseventapplicationphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/class-event-application.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/class-event-application.php       2019-01-15 03:54:51 UTC (rev 8082)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/class-event-application.php 2019-01-15 09:06:15 UTC (rev 8083)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6,6 +6,7 @@
</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"> namespace WordPress_Community\Applications;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once WCPT_DIR . 'wcpt-event/notification.php';
</ins><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">  * Class Event_Application
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -84,6 +85,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function submit_application() {
</span><span class="cx" style="display: block; padding: 0 10px">                $application_data = $this->validate_data( $_POST );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( $this->is_rate_limited() ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $message        = __( 'You have submitted too many applications recently. Please wait and try again in a few hours.', 'wordcamporg' );
</span><span class="cx" style="display: block; padding: 0 10px">                        $notice_classes = 'notice-error';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -92,7 +94,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $notice_classes = 'notice-error';
</span><span class="cx" style="display: block; padding: 0 10px">                } else {
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->create_post( $application_data );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $this->notify_applicant_application_received( $this->get_organizer_email(), $this->get_event_location() );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $this->notify_applicant_application_received(
+                                       $this->get_organizer_email(),
+                                       $this->get_event_location()
+                       );
+
+                       $this->notify_new_application_in_slack();
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $message        = __( "Thank you for your application! We've received it, and we'll contact you once we've had a chance to review it.", 'wordcamporg' );
</span><span class="cx" style="display: block; padding: 0 10px">                        $notice_classes = 'notice-success';
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -174,6 +182,20 @@
</span><span class="cx" style="display: block; padding: 0 10px">        abstract public function get_event_location();
</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 default status for a new application.
+        *
+        * @return string|null
+        */
+       abstract public static function get_default_status();
+
+       /**
+        * Get publicly accessible report url for a event. Should return null if such report is published.
+        *
+        * @return string|null
+        */
+       abstract public static function get_application_report_url();
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Display a notice to applicant while submitting the form.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param string $message Message to be displayed.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -191,9 +213,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * Notify the applicant that we've received their application
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param string $email_address
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param string $meetup_city
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param string $event_city
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public function notify_applicant_application_received( $email_address, $meetup_city ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function notify_applicant_application_received( $email_address, $event_city ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 //translators: Name of the event. Egs WordCamp.
</span><span class="cx" style="display: block; padding: 0 10px">                $subject = sprintf( __( "We've received your %s application", 'wpct' ), $this->get_event_label() );
</span><span class="cx" style="display: block; padding: 0 10px">                $headers = array( 'Reply-To: support@wordcamp.org' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -203,9 +225,38 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                "Thank you for applying to organize a %s in %s! We'll send you a follow-up e-mail once we've had a chance to review your application.",
</span><span class="cx" style="display: block; padding: 0 10px">                                'wpct'
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $this->get_event_label(), sanitize_text_field( $meetup_city )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $this->get_event_label(), sanitize_text_field( $event_city )
</ins><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">                wp_mail( $email_address, $subject, $message, $headers );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * Notify in community slack channel that we've received an application
+        */
+       public function notify_new_application_in_slack() {
+
+               // Not translating because this will be sent to community events slack channel.
+               $message = sprintf( "A %s application for %s has been received.", $this->get_event_label(), $this->get_event_location() );
+
+               $public_report_url = $this->get_application_report_url();
+               if ( isset( $public_report_url ) ) {
+                       // `<%s|here> is syntax for slack message to hyperlink text `here` with url provided in `%s`
+                       $message = sprintf( "%s Public status can be followed on <%s|%s application report page>.", $message, $public_report_url, $this->get_event_label() );
+               }
+
+               $default_status = $this->get_default_status();
+               $queue_size = wp_count_posts( $post_type=$this->get_event_type() )->$default_status;
+               if ( isset( $queue_size ) ) {
+                       $singular = "is $queue_size application";
+                       $plural   = "are $queue_size applications";
+                       $message = sprintf(
+                                       "%s\n _There %s in vetting queue._",
+                                       $message,
+                                       1 === $queue_size ? $singular : $plural );
+               }
+
+               $attachment = create_event_attachment( $message,  sprintf( "New %s application ", $this->get_event_label() ) );
+               return wcpt_slack_notify( COMMUNITY_TEAM_SLACK, $attachment );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcpteventnotificationphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/notification.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/notification.php                          (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-event/notification.php    2019-01-15 09:06:15 UTC (rev 8083)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,121 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Provides helper methods for sending slack notifications in response to status changes in application.
+ */
+
+if ( defined( 'WPORG_SANDBOXED' ) && WPORG_SANDBOXED ) {
+       // If this is sandbox and then send notification of owner of sandbox (as long as sandbox username and slack username matches)
+       if ( defined( 'SANDBOX_SLACK_USERNAME' ) ) {
+               $slack_username = SANDBOX_SLACK_USERNAME;
+       } else {
+               $slack_username = "@" . str_replace( array( '.dev.ord', '.dev' ), '', WPORG_SANDBOXED );
+       }
+       define( 'COMMUNITY_TEAM_SLACK', $slack_username );
+       define( 'COMMUNITY_EVENTS_SLACK', $slack_username );
+} else {
+       define( 'COMMUNITY_TEAM_SLACK', '#community-team' );
+       define( 'COMMUNITY_EVENTS_SLACK', '#community-events' );
+}
+
+/**
+ * Send attachment to Make WordPress slack. Will be used to send event notifications to community channels
+ *
+ * @param string $channel Name of the channel we want to send the notification to.
+ * @param array  $attachment Attachment object
+ *
+ * @return bool|string
+ */
+function wcpt_slack_notify ( $channel, $attachment ) {
+       if ( ! class_exists( 'Dotorg\Slack\Send' ) ) {
+               return false;
+       }
+
+       if ( ! defined( 'SLACK_ERROR_REPORT_URL' ) ) {
+               return false;
+       }
+
+       $send = new \Dotorg\Slack\Send( SLACK_ERROR_REPORT_URL );
+       $send->add_attachment( $attachment );
+       return $send->send( $channel );
+}
+
+/**
+ * Creates attachment that can be sent using slack notification.
+ * See the structure of attachment here: https://api.slack.com/docs/message-attachments
+ *
+ * @param string $message Main text to send in the notification
+ * @param string $event_label Label for the event. Would probably be one of `WordCamp` or `Meetup`.
+ *
+ * @return array
+ */
+function create_event_attachment ( $message, $title ) {
+       // Not translating because this will be send to Slack.
+       return array(
+               "title" => $title,
+               "text" => $message,
+       );
+}
+
+function create_event_status_attachment( $message, $event_id, $title ) {
+       $props = get_props_for_event( $event_id );
+
+       $props_string = implode( ", ", $props );
+       return array(
+               "title" => $title,
+               "text" => $message,
+               "fields" => array(
+                       array(
+                               "title" => "Application processed by",
+                               "value" => $props_string,
+                       ),
+               ),
+       );
+}
+
+/**
+ * Get and array of usernames for everyone contributed to vetting an event application.
+ * This includes people who have changed statuses, added notes etc.
+ * Currently supports WordCamp and Meetups
+ *
+ * @param int $event_id Id of the event to fetch props for.
+ *
+ * @return array Array of usernames of people who have participated in vetting this application
+ */
+function get_props_for_event ( $event_id ) {
+       $user_ids = array();
+
+       $status_change_logs = get_post_meta( $event_id, '_status_change' ) ?? array();
+
+       $user_ids = array_merge( $user_ids, wp_list_pluck( $status_change_logs, 'user_id' ) );
+
+       $notes = get_post_meta( $event_id, '_note' ) ?? array();
+
+       $user_ids = array_merge( $user_ids, wp_list_pluck( $notes, 'user_id' ) );
+
+       $user_ids = array_unique( $user_ids );
+
+       $user_nicenames = get_user_nicenames_from_ids( $user_ids );
+
+       // remove bot user `wordcamp`
+       $user_nicenames = array_diff( $user_nicenames, array( 'wordcamp' ) );
+       return $user_nicenames;
+}
+
+/**
+ * Return user names for list of user ids provided in the function
+ *
+ * @param array $user_ids List of user_ids
+ *
+ * @return array List of user nicenames
+ */
+function get_user_nicenames_from_ids( $user_ids ) {
+       $user_query = new WP_User_Query( array( 'include' => $user_ids, 'fields'  => array( 'user_nicename' ), ) );
+
+       $users = $user_query->get_results();
+       $user_display_names = array();
+       foreach ( $users as $user ) {
+               $user_display_names[] = $user->user_nicename;
+       }
+
+       return $user_display_names;
+}
</ins></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptloaderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-loader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-loader.php      2019-01-15 03:54:51 UTC (rev 8082)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-loader.php        2019-01-15 09:06:15 UTC (rev 8083)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -50,6 +50,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                require_once ( WCPT_DIR . 'wcpt-wordcamp/wordcamp.php' );
</span><span class="cx" style="display: block; padding: 0 10px">                require_once ( WCPT_DIR . 'wcpt-meetup/meetup.php' );
</span><span class="cx" style="display: block; padding: 0 10px">                require_once ( WCPT_DIR . 'wcpt-meetup/class-meetup-admin.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                require_once ( WCPT_DIR . 'wcpt-event/class-event-admin.php' ); // required for declined application cron to work.
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Require admin files.
</span><span class="cx" style="display: block; padding: 0 10px">                if ( is_admin() || ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptmeetupclassmeetupadminphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/class-meetup-admin.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/class-meetup-admin.php   2019-01-15 03:54:51 UTC (rev 8082)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/class-meetup-admin.php     2019-01-15 09:06:15 UTC (rev 8083)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8,9 +8,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> use \WordPress_Community\Applications\Meetup_Application;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> require_once WCPT_DIR . 'wcpt-event/class-event-admin.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once WCPT_DIR . 'wcpt-event/notification.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-if ( ! class_exists( 'MeetupAdmin' ) ) :
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+if ( ! class_exists( 'Meetup_Admin' ) ) :
+
</ins><span class="cx" style="display: block; padding: 0 10px">         /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Implements Meetup Admin class
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -479,6 +481,58 @@
</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">+                 * Send notification to slack when a Meetup becomes active in the chapter or is declined.
+                *
+                * @param string  $new_status
+                * @param string  $old_status
+                * @param WP_Post $meetup
+                *
+                * @return null|bool Will be null if notification was not enabled, or false if notifcation was attempted but failed. true if notification was successful
+                */
+               public function notify_application_status_in_slack( $new_status, $old_status, WP_Post $meetup ) {
+
+                       $notification_enabled = apply_filters( 'meetup_application_notification_enabled', true );
+
+                       if ( ! $notification_enabled ) {
+                               return null;
+                       }
+
+                       if ( 'wcpt-mtp-active' === $new_status ) {
+                               return $this->notify_new_meetup_group_in_slack( $meetup );
+                       } elseif ( 'wcpt-mtp-rejected' === $new_status ) {
+                               $location = get_post_meta( $meetup->ID, 'Meetup Location', true );
+                               return $this->schedule_decline_notification( $meetup, $this->get_event_label(), $location );
+                       }
+               }
+
+               /**
+                * Send notification when a new Meetup groups is added to the chapter.
+                *
+                * @param WP_Post $meetup Meetup post object
+                *
+                * @return bool|string
+                */
+               public static function notify_new_meetup_group_in_slack( $meetup ) {
+
+                       // Not translating strings here because these will be sent to Slack.
+                       $city            = get_post_meta( $meetup->ID, 'Meetup Location', true );
+                       $organizer_slack = get_post_meta( $meetup->ID, 'Slack', true );
+                       $meetup_link     = get_post_meta( $meetup->ID, 'Meetup URL', true );
+                       $title           = "New meetup group added";
+
+                       $message = sprintf(
+                               "Let's welcome the new WordPress meetup group%s%s, to the chapter! :tada: :community: :wordpress:\n%s",
+                               isset( $city ) ? " in $city," : "",
+                               isset( $organizer_slack ) ? " organized by @$organizer_slack" : "",
+                               $meetup_link
+                       );
+
+                       $attachment = create_event_status_attachment( $message, $meetup->ID, $title );
+
+                       return wcpt_slack_notify( COMMUNITY_EVENTS_SLACK, $attachment );
+               }
+
+               /**
</ins><span class="cx" style="display: block; padding: 0 10px">                  * Helper function for getting list of organizers.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param string $main_organizer
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptmeetupclassmeetupapplicationphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/class-meetup-application.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/class-meetup-application.php     2019-01-15 03:54:51 UTC (rev 8082)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/class-meetup-application.php       2019-01-15 09:06:15 UTC (rev 8083)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -270,8 +270,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function get_event_location() {
</span><span class="cx" style="display: block; padding: 0 10px">                if ( isset( $this->post->ID ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        return get_post_meta( $this->post->ID, 'q_city', true );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 return get_post_meta( $this->post->ID, 'Meetup Location', true );
</ins><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">+        /**
+        * Public report URL for Meetup Applications
+        */
+       public static function get_application_report_url() {
+               return "https://central.wordcamp.org/reports/meetup-applications/";
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptmeetupmeetupphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/meetup.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/meetup.php       2019-01-15 03:54:51 UTC (rev 8082)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-meetup/meetup.php 2019-01-15 09:06:15 UTC (rev 8083)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2,7 +2,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> namespace WordPress_Community\Applications\Meetup;
</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 'class-meetup-application.php';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once WCPT_DIR . 'wcpt-meetup/class-meetup-application.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> use WordPress_Community\Applications\Meetup_Application;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> defined( 'WPINC' ) or die();
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptwordcampclasswordcampapplicationphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-wordcamp/class-wordcamp-application.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-wordcamp/class-wordcamp-application.php 2019-01-15 03:54:51 UTC (rev 8082)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-wordcamp/class-wordcamp-application.php   2019-01-15 09:06:15 UTC (rev 8083)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -14,7 +14,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        const SHORTCODE_SLUG = 'wordcamp-organizer-application';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        static function get_event_label() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                __( 'WordCamp', 'wordcamporg' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return __( 'WordCamp', 'wordcamporg' );
</ins><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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -157,6 +157,24 @@
</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">+         * Gets default status of new WordCamp application
+        *
+        * @return string
+        */
+       public static function get_default_status() {
+               return WCPT_DEFAULT_STATUS;
+       }
+
+       /**
+        * Public report URL for WordCamp applications.
+        *
+        * @return string
+        */
+       public static function get_application_report_url() {
+               return "https://central.wordcamp.org/reports/application-status/";
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Create a WordCamp post from an application
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array $data
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -186,6 +204,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                add_post_meta( $post_id, '_application_data', $data );
</span><span class="cx" style="display: block; padding: 0 10px">                add_post_meta( $post_id, '_application_submitter_ip_address', $_SERVER['REMOTE_ADDR'] );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 add_post_meta(
</span><span class="cx" style="display: block; padding: 0 10px">                        $post_id, 'Organizer Name', sprintf(
</span><span class="cx" style="display: block; padding: 0 10px">                                '%s %s',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -230,7 +249,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        function get_organizer_email() {
</span><span class="cx" style="display: block; padding: 0 10px">                if ( isset( $this->post ) && isset( $this->post->ID ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        return get_post_meta( $this->post->ID, 'q_1079059_email' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 return get_post_meta( $this->post->ID, 'Email Address', true );
</ins><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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -241,7 +260,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        function get_event_location() {
</span><span class="cx" style="display: block; padding: 0 10px">                if ( isset( $this->post ) && isset( $this->post->ID ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        return get_post_meta( $this->post->ID, 'q_1079103_wordcamp_location' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 return get_post_meta( $this->post->ID, 'Location', true );
</ins><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></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswcptwcptwordcampwordcampadminphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-wordcamp/wordcamp-admin.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-wordcamp/wordcamp-admin.php     2019-01-15 03:54:51 UTC (rev 8082)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wcpt/wcpt-wordcamp/wordcamp-admin.php       2019-01-15 09:06:15 UTC (rev 8083)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5,6 +5,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> use WordPress_Community\Applications\WordCamp_Application;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> require_once WCPT_DIR . 'wcpt-event/class-event-admin.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once WCPT_DIR . 'wcpt-event/notification.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> if ( ! class_exists( 'WordCamp_Admin' ) ) :
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -707,6 +708,57 @@
</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">+                 * Send notification to slack when a WordCamp is scheduled or declined. Runs whenever status of an applications changes
+                *
+                * @param string  $new_status
+                * @param string  $old_status
+                * @param WP_Post $wordcamp
+                *
+                * @return null|bool
+                */
+               public function notify_application_status_in_slack( $new_status, $old_status, WP_Post $wordcamp ) {
+
+                       $notification_enabled = apply_filters( 'wordcamp_application_notification_enabled', true );
+
+                       if ( ! $notification_enabled ) {
+                               return null;
+                       }
+
+                       if ( 'wcpt-scheduled' === $new_status ) {
+                               return $this->notify_new_wordcamp_in_slack( $wordcamp );
+                       } elseif ( 'wcpt-rejected' === $new_status ) {
+                               $location = get_post_meta( $wordcamp->ID, 'Location', true );
+                               return $this->schedule_decline_notification( $wordcamp, 'WordCamp', $location );
+                       }
+               }
+
+               /**
+                * Send notification when a new WordCamp comes in scheduled status.
+                *
+                * @param WP_Post $wordcamp
+                *
+                * @return null|bool
+                */
+               public static function notify_new_wordcamp_in_slack( $wordcamp ) {
+                       // Not translating any string because they will be sent to slack.
+                       $city             = get_post_meta( $wordcamp->ID, 'Location', true );
+                       $start_date       = get_post_meta( $wordcamp->ID, 'Start Date (YYYY-mm-dd)', true );
+                       $wordcamp_url     = get_post_meta( $wordcamp->ID, 'URL', true );
+                       $title            = 'New WordCamp scheduled!!!';
+
+                       $message = sprintf(
+                               "<%s|WordCamp $city> has been scheduled for a start date of %s. :tada: :community: :wordpress:\n\n%s",
+                               $wordcamp_url,
+                               date( 'F j, Y', $start_date ),
+                               $wordcamp_url
+                       );
+
+                       $attachment = create_event_status_attachment( $message, $wordcamp->ID, $title );
+
+                       return wcpt_slack_notify( COMMUNITY_EVENTS_SLACK, $attachment );
+               }
+
+               /**
</ins><span class="cx" style="display: block; padding: 0 10px">                  * Enforce a valid post status for WordCamps.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param array $post_data
</span></span></pre>
</div>
</div>

</body>
</html>