<!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>[7588] sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports: WordCamp Reports: Updates to WordCamp Status and WordCamp Details reports</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/7588">7588</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/7588","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>2018-08-02 19:12:19 +0000 (Thu, 02 Aug 2018)</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'>WordCamp Reports: Updates to WordCamp Status and WordCamp Details reports

* Simplify the Details report, making all criteria optional, and adding a
  parameter so specific WordCamp IDs can be submitted.
* Rework the UI of the Status report so that it utilizes the Details report
  for exporting data about WordCamps that appear in the results.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsassetsjswordcampstatusjs">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/assets/js/wordcamp-status.js</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsclassesreportclasswordcampdetailsphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/classes/report/class-wordcamp-details.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsclassesreportclasswordcampstatusphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/classes/report/class-wordcamp-status.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsincludesvalidationphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/includes/validation.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsindexphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/index.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsviewshtmlwordcampstatusphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/html/wordcamp-status.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsviewspublicwordcampstatusphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/public/wordcamp-status.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsviewsreportwordcampdetailsphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/report/wordcamp-details.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsviewsreportwordcampstatusphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/report/wordcamp-status.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsassetsjswordcampstatusjs"></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/wordcamp-reports/assets/js/wordcamp-status.js</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/wordcamp-reports/assets/js/wordcamp-status.js     2018-08-02 19:00:11 UTC (rev 7587)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/assets/js/wordcamp-status.js       2018-08-02 19:12:19 UTC (rev 7588)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -17,14 +17,34 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                toggles: []
</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">+                        this.setupFieldsToggle();
</ins><span class="cx" style="display: block; padding: 0 10px">                         this.setupSingleToggles();
</span><span class="cx" style="display: block; padding: 0 10px">                        this.setupBulkToggles();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        $( '#status' ).select2();
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $( document ).ready( function() {
</span><span class="cx" style="display: block; padding: 0 10px">                                self.cache.$hideAll.trigger( 'click' );
</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">+                setupFieldsToggle: function() {
+                       var self = this;
+
+                       self.cache.$fieldsToggle = $( '#fields-toggle' );
+                       self.cache.$fieldsContainer = $( '#fields-section' );
+
+                       self.cache.$fieldsToggle.on( 'click', function( event ) {
+                               event.preventDefault();
+
+                               if ( self.cache.$fieldsContainer.hasClass( 'hidden' ) ) {
+                                       self.cache.$fieldsContainer.removeClass( 'hidden' );
+                               } else {
+                                       self.cache.$fieldsContainer.addClass( 'hidden' );
+                               }
+                       } );
+               },
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * Set up status log toggles for individual camps.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsclassesreportclasswordcampdetailsphp"></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/wordcamp-reports/classes/report/class-wordcamp-details.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/wordcamp-reports/classes/report/class-wordcamp-details.php        2018-08-02 19:00:11 UTC (rev 7587)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/classes/report/class-wordcamp-details.php  2018-08-02 19:12:19 UTC (rev 7588)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8,10 +8,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> use Exception;
</span><span class="cx" style="display: block; padding: 0 10px"> use DateTime;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use WP_Post, WP_Query;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use WP_Post, WP_Query, WP_Error;
</ins><span class="cx" style="display: block; padding: 0 10px"> use function WordCamp\Reports\{get_assets_url, get_assets_dir_path, get_views_dir_path};
</span><span class="cx" style="display: block; padding: 0 10px"> use WordCamp\Reports\Utility\Date_Range;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use function WordCamp\Reports\Validation\{validate_date_range, validate_wordcamp_id, validate_wordcamp_status};
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use function WordCamp\Reports\Validation\{validate_date_range, validate_wordcamp_id};
</ins><span class="cx" style="display: block; padding: 0 10px"> use WordCamp_Admin, WordCamp_Loader;
</span><span class="cx" style="display: block; padding: 0 10px"> use WordCamp\Utilities\Export_CSV;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -20,6 +20,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * A report class for exporting a spreadsheet of WordCamps.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Note that this report does not use caching because it is only used in WP Admin and has a large number of
+ * optional parameters.
+ *
</ins><span class="cx" style="display: block; padding: 0 10px">  * @package WordCamp\Reports\Report
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> class WordCamp_Details extends Base {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -42,7 +45,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @var string
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public static $description = 'Details about WordCamps occurring within a specified date range.';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public static $description = 'Create a spreadsheet of details about WordCamps that match optional criteria.';
</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">         * Report methodology.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -51,9 +54,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public static $methodology = "
</span><span class="cx" style="display: block; padding: 0 10px">                <ol>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <li>Retrieve WordCamp posts that fit within the date range and other optional criteria.</li>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <li>Retrieve WordCamp posts that fit within the criteria.</li>
</ins><span class="cx" style="display: block; padding: 0 10px">                         <li>Extract the data for each post that match the fields requested.</li>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <li>Walk all of the extracted data and format it for display.</li>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <li>Walk through all of the extracted data and format it for display.</li>
</ins><span class="cx" style="display: block; padding: 0 10px">                 </ol>
</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">@@ -65,7 +68,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public static $group = 'wordcamp';
</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">-         * The date range that defines the scope of the report data.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * A date range that WordCamp events must fall within.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @var null|Date_Range
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -72,20 +75,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $range = null;
</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">-         * The status to filter for in the report.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * A list of WordCamp post IDs.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @var string
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @var array
</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 $status = '';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public $wordcamp_ids = [];
</ins><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">-         * Whether to include data for WordCamps that don't have a date set.
-        *
-        * @var bool
-        */
-       public $include_dateless = false;
-
-       /**
</del><span class="cx" style="display: block; padding: 0 10px">          * Whether to include counts of various post types for each WordCamp.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @var bool
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -109,12 +105,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * WordCamp_Details constructor.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param string $start_date       The start of the date range for the report.
-        * @param string $end_date         The end of the date range for the report.
-        * @param string $status           Optional. The status ID to filter for in the report.
-        * @param bool   $include_dateless Optional. True to include data for WordCamps that don't have a date set. Default false.
-        * @param bool   $include_counts   Optional. True to include counts of various post types for each WordCamp. Default false.
-        * @param array  $options          {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param Date_Range $date_range       Optional. A date range that WordCamp events must fall within.
+        * @param array      $wordcamp_ids     Optional. A list of WordCamp post IDs to include in the results.
+        * @param bool       $include_counts   Optional. True to include counts of various post types for each WordCamp.
+        *                                     Default false.
+        * @param array      $options          {
</ins><span class="cx" style="display: block; padding: 0 10px">          *     Optional. Additional report parameters.
</span><span class="cx" style="display: block; padding: 0 10px">         *     See Base::__construct and the functions in WordCamp\Reports\Validation for additional parameters.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -122,42 +117,38 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *     @type array $fields        Not implemented yet.
</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">-        public function __construct( $start_date, $end_date, $status = '', $include_dateless = false, $include_counts = false, array $options = [] ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function __construct( Date_Range $date_range = null, array $wordcamp_ids = [], $include_counts = false, array $options = [] ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Report-specific options.
</span><span class="cx" style="display: block; padding: 0 10px">                $options = wp_parse_args( $options, [
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'status_subset' => [],
-                       'fields'        => [],
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'fields' => [],
</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">                parent::__construct( $options );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                try {
-                       $this->range = validate_date_range( $start_date, $end_date, $options );
-               } catch ( Exception $e ) {
-                       $this->error->add(
-                               self::$slug . '-date-error',
-                               $e->getMessage()
-                       );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( $date_range instanceof Date_Range ) {
+                       $this->range = $date_range;
</ins><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">-                if ( $status && 'any' !== $status ) {
-                       try {
-                               $this->status = validate_wordcamp_status( $status, $options );
-                       } catch ( Exception $e ) {
-                               $this->error->add(
-                                       self::$slug . '-status-error',
-                                       $e->getMessage()
-                               );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ! empty( $wordcamp_ids ) ) {
+                       foreach ( $wordcamp_ids as $wordcamp_id ) {
+                               try {
+                                       $this->wordcamp_ids[] = validate_wordcamp_id( $wordcamp_id, [ 'require_site' => false ] )->post_id;
+                               } catch ( Exception $e ) {
+                                       $this->error->add(
+                                               self::$slug . '-wordcamp-id-error',
+                                               $e->getMessage()
+                                       );
+
+                                       break;
+                               }
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->include_dateless = wp_validate_boolean( $include_dateless );
-               $this->include_counts   = wp_validate_boolean( $include_counts );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->include_counts = wp_validate_boolean( $include_counts );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $public_data_field_keys = array_merge(
</span><span class="cx" style="display: block; padding: 0 10px">                        [
</span><span class="cx" style="display: block; padding: 0 10px">                                'Name',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'Status',
</del><span class="cx" style="display: block; padding: 0 10px">                         ],
</span><span class="cx" style="display: block; padding: 0 10px">                        WordCamp_Loader::get_public_meta_keys()
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -166,6 +157,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $private_data_field_keys = array_merge(
</span><span class="cx" style="display: block; padding: 0 10px">                        [
</span><span class="cx" style="display: block; padding: 0 10px">                                'ID',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                'Created',
+                               'Status',
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'Tickets',
</span><span class="cx" style="display: block; padding: 0 10px">                                'Speakers',
</span><span class="cx" style="display: block; padding: 0 10px">                                'Sponsors',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -210,41 +203,6 @@
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Generate a cache key.
-        *
-        * @return string
-        */
-       protected function get_cache_key() {
-               $cache_key_segments = [
-                       parent::get_cache_key(),
-                       $this->range->generate_cache_key_segment(),
-               ];
-
-               if ( $this->status ) {
-                       $cache_key_segments[] = $this->status;
-               }
-
-               if ( $this->include_dateless ) {
-                       $cache_key_segments[] = '+dateless';
-               }
-
-               if ( $this->include_counts ) {
-                       $cache_key_segments[] = '+counts';
-               }
-
-               return implode( '_', $cache_key_segments );
-       }
-
-       /**
-        * Generate a cache expiration interval.
-        *
-        * @return int A time interval in seconds.
-        */
-       protected function get_cache_expiration() {
-               return $this->range->generate_cache_duration( parent::get_cache_expiration() );
-       }
-
-       /**
</del><span class="cx" style="display: block; padding: 0 10px">          * Query and parse the data for the report.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @return array
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -255,12 +213,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return array();
</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">-                // Maybe use cached data.
-               $data = $this->maybe_get_cached_data();
-               if ( is_array( $data ) ) {
-                       return $data;
-               }
-
</del><span class="cx" style="display: block; padding: 0 10px">                 $data = [];
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $wordcamp_posts = $this->get_wordcamp_posts();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -277,8 +229,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $row = array_intersect_key( array_replace( $field_order, $row ), $row );
</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">-                $this->maybe_cache_data( $data );
-
</del><span class="cx" style="display: block; padding: 0 10px">                 return $data;
</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">@@ -311,6 +261,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        ],
</span><span class="cx" style="display: block; padding: 0 10px">                        array_keys( $wordcamp_admin->meta_keys( 'wordcamp' ) ),
</span><span class="cx" style="display: block; padding: 0 10px">                        [
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                'Created',
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'Status',
</span><span class="cx" style="display: block; padding: 0 10px">                                'Tickets',
</span><span class="cx" style="display: block; padding: 0 10px">                                'Speakers',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -338,7 +289,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @return 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">-        protected function prepare_data_for_display( array $data ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function prepare_data_for_display( array $data ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $all_statuses = WordCamp_Loader::get_post_statuses();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                array_walk( $data, function( &$row ) use ( $all_statuses ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -380,9 +331,9 @@
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Get all current WordCamp posts.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Get WordCamp posts that fit the report criteria.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @return array
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @return array An array of WP_Post objects.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        protected function get_wordcamp_posts() {
</span><span class="cx" style="display: block; padding: 0 10px">                $post_args = array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -392,9 +343,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'nopaging'            => true,
</span><span class="cx" style="display: block; padding: 0 10px">                        'no_found_rows'       => false,
</span><span class="cx" style="display: block; padding: 0 10px">                        'ignore_sticky_posts' => true,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'orderby'             => 'meta_value_num title',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'orderby'             => 'id',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'order'               => 'ASC',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'meta_query'          => [
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         );
+
+               if ( $this->range instanceof Date_Range ) {
+                       // This replaces the default meta query.
+                       $post_args['meta_query'] = [
</ins><span class="cx" style="display: block; padding: 0 10px">                                 [
</span><span class="cx" style="display: block; padding: 0 10px">                                        'key'      => 'Start Date (YYYY-mm-dd)',
</span><span class="cx" style="display: block; padding: 0 10px">                                        'value'    => array( $this->range->start->getTimestamp(), $this->range->end->getTimestamp() ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -401,53 +356,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        'compare'  => 'BETWEEN',
</span><span class="cx" style="display: block; padding: 0 10px">                                        'type'     => 'NUMERIC',
</span><span class="cx" style="display: block; padding: 0 10px">                                ],
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        ],
-               );
-
-               if ( $this->include_dateless ) {
-                       $post_args['meta_query'] = array_merge( $post_args['meta_query'], [
-                               'relation' => 'OR',
-                               [
-                                       'key'     => 'Start Date (YYYY-mm-dd)',
-                                       'compare' => 'NOT EXISTS',
-                               ],
-                               [
-                                       'key'     => 'Start Date (YYYY-mm-dd)',
-                                       'compare' => '=',
-                                       'value'   => '',
-                               ],
-                       ] );
-
-                       // Don't include really old camps with no date or ones that didn't exist during the date range.
-                       $post_args['date_query'] = [
-                               [
-                                       'before' => $this->range->end->format( 'Y-m-d' ),
-                                       'after'  => $this->range->start->format( 'Y-m-d' ) . ' - 1 year',
-                               ],
</del><span class="cx" style="display: block; padding: 0 10px">                         ];
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        $post_args['orderby'] = 'meta_value_num title';
</ins><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">+                if ( ! empty( $this->wordcamp_ids ) ) {
+                       $post_args['post__in'] = $this->wordcamp_ids;
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( $this->options['public'] ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $post_args['post_status'] = WordCamp_Loader::get_public_post_statuses();
</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">-                if ( $this->status ) {
-                       $status_report = new WordCamp_Status(
-                               $this->range->start->format( 'Y-m-d' ),
-                               $this->range->end->format( 'Y-m-d' ),
-                               $this->status,
-                               $this->options
-                       );
-
-                       $post_ids = array_keys( $status_report->get_data() );
-
-                       if ( empty( $post_ids ) ) {
-                               return [];
-                       }
-
-                       $post_args['post__in'] = $post_ids;
-               }
-
</del><span class="cx" style="display: block; padding: 0 10px">                 return get_posts( $post_args );
</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">@@ -462,9 +382,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $meta_keys   = $this->get_meta_keys();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $row = [
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'ID'     => $wordcamp->ID,
-                       'Name'   => $wordcamp->post_title,
-                       'Status' => $wordcamp->post_status,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'ID'      => $wordcamp->ID,
+                       'Name'    => $wordcamp->post_title,
+                       'Created' => get_the_date( 'Y-m-d', $wordcamp->ID ),
+                       'Status'  => $wordcamp->post_status,
</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">                if ( $this->include_counts ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -501,6 +422,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Count the number of various post types for a WordCamp.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * If the WordCamp doesn't have a site yet, the counts will all be zero.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @param WP_Post $wordcamp
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @return array
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -514,11 +437,15 @@
</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">                try {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $ids = validate_wordcamp_id( $wordcamp->ID, [ 'require_site' => true ] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $id = validate_wordcamp_id( $wordcamp->ID, [ 'require_site' => false ] );
</ins><span class="cx" style="display: block; padding: 0 10px">                 } catch ( Exception $e ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        return $counts;
</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">+                if ( ! $id->site_id ) {
+                       return $counts;
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $get_count = function( $post_type ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $posts = new WP_Query( [
</span><span class="cx" style="display: block; padding: 0 10px">                                'posts_per_page' => 1, // Only need to fetch 1 to populate total number in found_posts.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -529,7 +456,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return absint( $posts->found_posts );
</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">-                switch_to_blog( $ids['site_id'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         switch_to_blog( $id->site_id );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Tickets
</span><span class="cx" style="display: block; padding: 0 10px">                $stats = get_option( 'camptix_stats' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -548,11 +475,47 @@
</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">+         * Render HTML form inputs for the fields that are available for inclusion in the spreadsheet.
+        *
+        * @param string $context        'public' or 'private'. Default 'public'.
+        * @param array  $field_defaults Optional. An associative array where the keys are field keys and the values
+        *                               are extra attributes for those field inputs. Examples: checked or required.
+        */
+       public static function render_available_fields( $context = 'public', array $field_defaults = [] ) {
+               $field_order      = array_fill_keys( self::get_field_order(), '' );
+               $field_defaults   = array_replace( $field_order, $field_defaults );
+
+               $shadow_report = new self( null, [], false, [ 'public' => ( 'private' === $context ) ? false : true ] );
+
+               $available_fields = array_intersect_key( $field_defaults, $shadow_report->get_data_fields_safelist() );
+               ?>
+               <fieldset class="fields-container">
+                       <legend class="fields-label">Available Fields</legend>
+
+                       <?php foreach ( $available_fields as $field_name => $extra_props ) : ?>
+                               <div class="field-checkbox">
+                                       <input
+                                               type="checkbox"
+                                               id="fields-<?php echo esc_attr( $field_name ); ?>"
+                                               name="fields[]"
+                                               value="<?php echo esc_attr( $field_name ); ?>"
+                                               <?php if ( $extra_props && is_string( $extra_props ) ) echo esc_html( $extra_props ); ?>
+                                       />
+                                       <label for="fields-<?php echo esc_attr( $field_name ); ?>">
+                                               <?php echo esc_attr( $field_name ); ?>
+                                       </label>
+                               </div>
+                       <?php endforeach; ?>
+               </fieldset>
+               <?php
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Register all assets used by this report.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @return void
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        protected static function register_assets() {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public static function register_assets() {
</ins><span class="cx" style="display: block; padding: 0 10px">                 wp_register_script(
</span><span class="cx" style="display: block; padding: 0 10px">                        self::$slug,
</span><span class="cx" style="display: block; padding: 0 10px">                        get_assets_url() . 'js/' . self::$slug . '.js',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -588,17 +551,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return void
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public static function render_admin_page() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $start_date       = filter_input( INPUT_POST, 'start-date' );
-               $end_date         = filter_input( INPUT_POST, 'end-date' );
-               $include_dateless = filter_input( INPUT_POST, 'include_dateless', FILTER_VALIDATE_BOOLEAN );
-               $status           = filter_input( INPUT_POST, 'status' );
-               $refresh          = filter_input( INPUT_POST, 'refresh', FILTER_VALIDATE_BOOLEAN );
-               $action           = filter_input( INPUT_POST, 'action' );
-               $nonce            = filter_input( INPUT_POST, self::$slug . '-nonce' );
-               $statuses         = WordCamp_Loader::get_post_statuses();
-
-               $field_order      = array_fill_keys( self::get_field_order(), '' );
-               $field_defaults   = array_replace( $field_order, [
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $field_defaults = [
</ins><span class="cx" style="display: block; padding: 0 10px">                         'ID'                      => 'checked',
</span><span class="cx" style="display: block; padding: 0 10px">                        'Name'                    => 'checked disabled',
</span><span class="cx" style="display: block; padding: 0 10px">                        'Start Date (YYYY-mm-dd)' => 'checked',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -605,11 +558,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'End Date (YYYY-mm-dd)'   => 'checked',
</span><span class="cx" style="display: block; padding: 0 10px">                        'Location'                => 'checked',
</span><span class="cx" style="display: block; padding: 0 10px">                        'URL'                     => 'checked',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                ] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         ];
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $shadow_report    = new self( '', '', '', false, false, [ 'public' => false ] );
-               $available_fields = array_intersect_key( $field_defaults, $shadow_report->get_data_fields_safelist() );
-
</del><span class="cx" style="display: block; padding: 0 10px">                 include get_views_dir_path() . 'report/wordcamp-details.php';
</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">@@ -621,10 +571,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public static function export_to_file() {
</span><span class="cx" style="display: block; padding: 0 10px">                $start_date       = filter_input( INPUT_POST, 'start-date' );
</span><span class="cx" style="display: block; padding: 0 10px">                $end_date         = filter_input( INPUT_POST, 'end-date' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $include_dateless = filter_input( INPUT_POST, 'include_dateless', FILTER_VALIDATE_BOOLEAN );
-               $status           = filter_input( INPUT_POST, 'status' );
</del><span class="cx" style="display: block; padding: 0 10px">                 $fields           = filter_input( INPUT_POST, 'fields', FILTER_SANITIZE_STRING, [ 'flags' => FILTER_REQUIRE_ARRAY ] );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $refresh          = filter_input( INPUT_POST, 'refresh', FILTER_VALIDATE_BOOLEAN );
</del><span class="cx" style="display: block; padding: 0 10px">                 $action           = filter_input( INPUT_POST, 'action' );
</span><span class="cx" style="display: block; padding: 0 10px">                $nonce            = filter_input( INPUT_POST, self::$slug . '-nonce' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -635,6 +582,23 @@
</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">                if ( wp_verify_nonce( $nonce, 'run-report' ) && current_user_can( 'manage_network' ) ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        $error = null;
+                       $range = null;
+
+                       if ( $start_date || $end_date ) {
+                               try {
+                                       $range = validate_date_range( $start_date, $end_date, [
+                                               'allow_future_start' => true,
+                                               'earliest_start'     => new DateTime( '2006-01-01' ), // No WordCamp posts before 2006.,
+                                       ] );
+                               } catch ( Exception $e ) {
+                                       $error = new WP_Error(
+                                               self::$slug . '-date-range-error',
+                                               $e->getMessage()
+                                       );
+                               }
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $include_counts = false;
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! empty( array_intersect( $fields, [ 'Tickets', 'Speakers', 'Sponsors', 'Organizers' ] ) ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $include_counts = true;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -645,30 +609,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $fields[] = 'Name';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $options = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'fields'         => $fields,
-                               'public'         => false,
-                               'earliest_start' => new DateTime( '2006-01-01' ), // No WordCamp posts before 2006.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'fields' => $fields,
+                               'public' => false,
</ins><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">-                        if ( $status ) {
-                               $options['earliest_start'] = new DateTime( '2015-01-01' ); // No status log data before 2015.
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $report = new self( $range, [], $include_counts, $options );
</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 ( $refresh ) {
-                               $options['flush_cache'] = true;
-                       }
-
-                       $report = new self( $start_date, $end_date, $status, $include_dateless, $include_counts, $options );
-
</del><span class="cx" style="display: block; padding: 0 10px">                         $filename = [ $report::$name ];
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $filename[] = $report->range->start->format( 'Y-m-d' );
-                       $filename[] = $report->range->end->format( 'Y-m-d' );
-                       if ( $report->status ) {
-                               $filename[] = $report->status;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( $report->range instanceof Date_Range ) {
+                               $filename[] = $report->range->start->format( 'Y-m-d' );
+                               $filename[] = $report->range->end->format( 'Y-m-d' );
</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 ( $report->include_dateless ) {
-                               $filename[] = 'include-dateless';
-                       }
</del><span class="cx" style="display: block; padding: 0 10px">                         if ( $report->include_counts ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $filename[] = 'include-counts';
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -687,6 +638,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $exporter->error = $report->merge_errors( $report->error, $exporter->error );
</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">+                        if ( $error instanceof WP_Error ) {
+                               $exporter->error = $report->merge_errors( $error, $exporter->error );
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $exporter->emit_file();
</span><span class="cx" style="display: block; padding: 0 10px">                } // End if().
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsclassesreportclasswordcampstatusphp"></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/wordcamp-reports/classes/report/class-wordcamp-status.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/wordcamp-reports/classes/report/class-wordcamp-status.php 2018-08-02 19:00:11 UTC (rev 7587)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/classes/report/class-wordcamp-status.php   2018-08-02 19:12:19 UTC (rev 7588)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -7,9 +7,14 @@
</span><span class="cx" style="display: block; padding: 0 10px"> defined( 'WPINC' ) || die();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> use Exception;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use WordCamp\Reports;
-use function WordCamp\Reports\Validation\validate_wordcamp_status;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use DateTime;
+use WP_Error, WP_Post;
+use function WordCamp\Reports\{get_assets_url, get_assets_dir_path, get_views_dir_path};
+use WordCamp\Reports\Utility\Date_Range;
+use function WordCamp\Reports\Validation\{validate_date_range, validate_wordcamp_status};
+use function WordCamp\Reports\Time\{year_array, quarter_array, month_array, convert_time_period_to_date_range};
</ins><span class="cx" style="display: block; padding: 0 10px"> use WordCamp_Loader;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use WordCamp\Utilities\Export_CSV;
</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 WordCamp_Status
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -18,7 +23,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @package WordCamp\Reports\Report
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-class WordCamp_Status extends Date_Range {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+class WordCamp_Status extends Base {
</ins><span class="cx" style="display: block; padding: 0 10px">         /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Report name.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -68,6 +73,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public static $shortcode_tag = 'wordcamp_status_report';
</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">+         * The date range that defines the scope of the report data.
+        *
+        * @var null|Date_Range
+        */
+       public $range = null;
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * The status to filter for in the report.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @var string
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -105,8 +117,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'status_subset' => [],
</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">-                parent::__construct( $start_date, $end_date, $options );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         parent::__construct( $options );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                try {
+                       $this->range = validate_date_range( $start_date, $end_date, $options );
+               } catch ( Exception $e ) {
+                       $this->error->add(
+                               self::$slug . '-date-error',
+                               $e->getMessage()
+                       );
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( $status && 'any' !== $status ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        try {
</span><span class="cx" style="display: block; padding: 0 10px">                                $this->status = validate_wordcamp_status( $status, $options );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -138,16 +159,28 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return string
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        protected function get_cache_key() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $cache_key = parent::get_cache_key();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $cache_key_segments = [
+                       parent::get_cache_key(),
+                       $this->range->generate_cache_key_segment(),
+               ];
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $this->status ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $cache_key .= '_' . $this->status;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $cache_key_segments[] = $this->status;
</ins><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">-                return $cache_key;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return implode( '_', $cache_key_segments );
</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">+         * Generate a cache expiration interval.
+        *
+        * @return int A time interval in seconds.
+        */
+       protected function get_cache_expiration() {
+               return $this->range->generate_cache_duration( parent::get_cache_expiration() );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Query and parse the data for the report.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @return array
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -176,7 +209,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Trim log entries occurring after the date range.
</span><span class="cx" style="display: block; padding: 0 10px">                        $logs = array_filter( $logs, function( $entry ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( $entry['timestamp'] > $this->end_date->getTimestamp() ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( $entry['timestamp'] > $this->range->end->getTimestamp() ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         return false;
</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">@@ -194,7 +227,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Trim log entries occurring before the date range.
</span><span class="cx" style="display: block; padding: 0 10px">                        $logs = array_filter( $logs, function( $entry ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( $entry['timestamp'] < $this->start_date->getTimestamp() ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( $entry['timestamp'] < $this->range->start->getTimestamp() ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         return false;
</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">@@ -299,7 +332,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        // Don't include WordCamps that happened more than 3 months before the start date.
</span><span class="cx" style="display: block; padding: 0 10px">                                        'key'     => 'Start Date (YYYY-mm-dd)',
</span><span class="cx" style="display: block; padding: 0 10px">                                        'compare' => '>=',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'value'   => strtotime( '-3 months', $this->start_date->getTimestamp() ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'value'   => strtotime( '-3 months', $this->range->start->getTimestamp() ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                         'type'    => 'NUMERIC',
</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">@@ -311,11 +344,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Retrieve the log of status changes for a particular WordCamp.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param \WP_Post $wordcamp A WordCamp post.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param WP_Post $wordcamp A WordCamp post.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @return 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">-        protected function get_wordcamp_status_logs( \WP_Post $wordcamp ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ protected function get_wordcamp_status_logs( WP_Post $wordcamp ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $log_entries = get_post_meta( $wordcamp->ID, '_status_change' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! empty( $log_entries ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -397,8 +430,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function render_html() {
</span><span class="cx" style="display: block; padding: 0 10px">                $data       = $this->compile_report_data( $this->get_data() );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $start_date = $this->start_date;
-               $end_date   = $this->end_date;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $start_date = $this->range->start;
+               $end_date   = $this->range->end;
</ins><span class="cx" style="display: block; padding: 0 10px">                 $status     = $this->status;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $active_camps   = $data['active_camps'];
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -406,15 +439,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $statuses       = WordCamp_Loader::get_post_statuses();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! empty( $this->error->get_error_messages() ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        ?>
-                       <div class="notice notice-error">
-                               <?php foreach ( $this->error->get_error_messages() as $message ) : ?>
-                                       <?php echo wpautop( wp_kses_post( $message ) ); ?>
-                               <?php endforeach; ?>
-                       </div>
-               <?php
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $this->render_error_html();
</ins><span class="cx" style="display: block; padding: 0 10px">                 } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        include Reports\get_views_dir_path() . 'html/wordcamp-status.php';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 include get_views_dir_path() . 'html/wordcamp-status.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"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -426,17 +453,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">        protected static function register_assets() {
</span><span class="cx" style="display: block; padding: 0 10px">                wp_register_script(
</span><span class="cx" style="display: block; padding: 0 10px">                        self::$slug,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        Reports\get_assets_url() . 'js/' . self::$slug . '.js',
-                       array( 'jquery' ),
-                       Reports\JS_VERSION,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 get_assets_url() . 'js/' . self::$slug . '.js',
+                       array( 'jquery', 'select2' ),
+                       filemtime( get_assets_dir_path() . 'js/' . self::$slug . '.js' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                         true
</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">                wp_register_style(
</span><span class="cx" style="display: block; padding: 0 10px">                        self::$slug,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        Reports\get_assets_url() . 'css/' . self::$slug . '.css',
-                       array(),
-                       Reports\CSS_VERSION,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 get_assets_url() . 'css/' . self::$slug . '.css',
+                       array( 'select2' ),
+                       filemtime( get_assets_dir_path() . 'css/' . self::$slug . '.css' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                         'screen'
</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">@@ -448,7 +475,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public static function enqueue_admin_assets() {
</span><span class="cx" style="display: block; padding: 0 10px">                self::register_assets();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                WordCamp_Details::register_assets();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                wp_enqueue_style( WordCamp_Details::$slug );
</ins><span class="cx" style="display: block; padding: 0 10px">                 wp_enqueue_script( self::$slug );
</span><span class="cx" style="display: block; padding: 0 10px">                wp_enqueue_style( self::$slug );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -467,11 +496,26 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $nonce      = filter_input( INPUT_POST, self::$slug . '-nonce' );
</span><span class="cx" style="display: block; padding: 0 10px">                $statuses   = WordCamp_Loader::get_post_statuses();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $field_defaults = [
+                       'ID'                      => 'checked',
+                       'Name'                    => 'checked disabled',
+                       'Start Date (YYYY-mm-dd)' => 'checked',
+                       'End Date (YYYY-mm-dd)'   => 'checked',
+                       'Location'                => 'checked',
+                       'URL'                     => 'checked',
+                       'Created'                 => 'checked',
+                       'Status'                  => 'checked',
+               ];
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $report = null;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( 'run-report' === $action && wp_verify_nonce( $nonce, 'run-report' ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( 'Show results' === $action
+                    && wp_verify_nonce( $nonce, 'run-report' )
+                    && current_user_can( 'manage_network' )
+               ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         $options = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'earliest_start' => new \DateTime( '2015-01-01' ), // No status log data before 2015.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'public'         => false,
+                               'earliest_start' => new DateTime( '2015-01-01' ), // No status log data before 2015.
</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">                        if ( $refresh ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -479,14 +523,99 @@
</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">                        $report = new self( $start_date, $end_date, $status, $options );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // The report adjusts the end date in some circumstances.
-                       if ( empty( $report->error->get_error_messages() ) ) {
-                               $end_date = $report->end_date->format( 'Y-m-d' );
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         include get_views_dir_path() . 'report/wordcamp-status.php';
+       }
+
+       /**
+        * Export the report data to a file.
+        *
+        * @return void
+        */
+       public static function export_to_file() {
+               $start_date = filter_input( INPUT_POST, 'start-date' );
+               $end_date   = filter_input( INPUT_POST, 'end-date' );
+               $status     = filter_input( INPUT_POST, 'status' );
+               $fields     = filter_input( INPUT_POST, 'fields', FILTER_SANITIZE_STRING, [ 'flags' => FILTER_REQUIRE_ARRAY ] );
+               $refresh    = filter_input( INPUT_POST, 'refresh', FILTER_VALIDATE_BOOLEAN );
+               $action     = filter_input( INPUT_POST, 'action' );
+               $nonce      = filter_input( INPUT_POST, self::$slug . '-nonce' );
+
+               if ( 'Export CSV' !== $action ) {
+                       return;
</ins><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">-                include Reports\get_views_dir_path() . 'report/wordcamp-status.php';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( wp_verify_nonce( $nonce, 'run-report' ) && current_user_can( 'manage_network' ) ) {
+                       $error = null;
+
+                       $options = array(
+                               'public'         => false,
+                               'earliest_start' => new DateTime( '2015-01-01' ), // No status log data before 2015.
+                       );
+
+                       if ( $refresh ) {
+                               $options['flush_cache'] = true;
+                       }
+
+                       $status_report = new self( $start_date, $end_date, $status, $options );
+                       $wordcamp_ids  = array_keys( $status_report->get_data() );
+
+                       if ( ! empty( $status_report->error->get_error_messages() ) ) {
+                               $error = $status_report->error;
+                       } elseif ( empty( $wordcamp_ids ) ) {
+                               $error = new WP_Error(
+                                       self::$slug . '-export-error',
+                                       'No status data available for the given criteria.'
+                               );
+                       }
+
+                       $include_counts = false;
+                       if ( ! empty( array_intersect( $fields, [ 'Tickets', 'Speakers', 'Sponsors', 'Organizers' ] ) ) ) {
+                               $include_counts = true;
+                       }
+
+                       // The "Name" field should always be included, but does not get submitted because the input is disabled,
+                       // so add it in here.
+                       $fields[] = 'Name';
+
+                       $options = array(
+                               'fields' => $fields,
+                               'public' => false,
+                       );
+
+                       $details_report = new WordCamp_Details( null, $wordcamp_ids, $include_counts, $options );
+
+                       $filename = [ $status_report::$name ];
+                       $filename[] = $status_report->range->start->format( 'Y-m-d' );
+                       $filename[] = $status_report->range->end->format( 'Y-m-d' );
+                       if ( $status_report->status ) {
+                               $filename[] = $status_report->status;
+                       }
+                       if ( $details_report->include_counts ) {
+                               $filename[] = 'include-counts';
+                       }
+
+                       $data = $details_report->prepare_data_for_display( $details_report->get_data() );
+
+                       $headers = ( ! empty( $data ) ) ? array_keys( $data[0] ) : [];
+
+                       $exporter = new Export_CSV( array(
+                               'filename' => $filename,
+                               'headers'  => $headers,
+                               'data'     => $data,
+                       ) );
+
+                       if ( ! empty( $details_report->error->get_error_messages() ) ) {
+                               $exporter->error = $details_report->merge_errors( $details_report->error, $exporter->error );
+                       }
+
+                       if ( $error instanceof WP_Error ) {
+                               $exporter->error = $details_report->merge_errors( $error, $exporter->error );
+                       }
+
+                       $exporter->emit_file();
+               } // End if().
</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">@@ -502,6 +631,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                if ( 'page' === get_post_type() ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        self::register_assets();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        wp_enqueue_style( 'select2' );
</ins><span class="cx" style="display: block; padding: 0 10px">                         wp_enqueue_script( self::$slug );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        ob_start();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -524,8 +654,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $status = filter_input( INPUT_GET, 'status' );
</span><span class="cx" style="display: block; padding: 0 10px">                $action = filter_input( INPUT_GET, 'action' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $years    = self::year_array( absint( date( 'Y' ) ), 2015 );
-               $months   = self::month_array();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $years    = year_array( absint( date( 'Y' ) ), 2015 );
+               $quarters = quarter_array();
+               $months   = month_array();
</ins><span class="cx" style="display: block; padding: 0 10px">                 $statuses = WordCamp_Loader::get_post_statuses();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! $year ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -539,15 +670,22 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $report = null;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( 'Show results' === $action ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $range = self::convert_time_period_to_date_range( $year, $period );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 try {
+                               $range = convert_time_period_to_date_range( $year, $period );
+                       } catch ( Exception $e ) {
+                               $error = new WP_Error(
+                                       self::$slug . '-time-period-error',
+                                       $e->getMessage()
+                               );
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $options = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'earliest_start' => new \DateTime( '2015-01-01' ), // No status log data before 2015.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'earliest_start' => new DateTime( '2015-01-01' ), // No status log data before 2015.
</ins><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">-                        $report = new self( $range['start_date'], $range['end_date'], $status, $options );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $report = new self( $range->start, $range->end, $status, $options );
</ins><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">-                include Reports\get_views_dir_path() . 'public/wordcamp-status.php';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         include get_views_dir_path() . 'public/wordcamp-status.php';
</ins><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_htmlwpcontentpluginswordcampreportsincludesvalidationphp"></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/wordcamp-reports/includes/validation.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/wordcamp-reports/includes/validation.php  2018-08-02 19:00:11 UTC (rev 7587)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/includes/validation.php    2018-08-02 19:12:19 UTC (rev 7588)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4,6 +4,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> defined( 'WPINC' ) || die();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> use Exception;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use stdClass;
</ins><span class="cx" style="display: block; padding: 0 10px"> use DateTime, DateTimeImmutable, DateInterval;
</span><span class="cx" style="display: block; padding: 0 10px"> use WP_Post;
</span><span class="cx" style="display: block; padding: 0 10px"> use WordCamp\Reports\Utility\Date_Range;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -120,7 +121,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  *     @type bool $require_site True if the WordCamp post must have an associated site in the network.
</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">- * @return array An associative array containing valid post ID and site ID integers for the WordCamp.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @return object An object containing properties for valid post ID and site ID integers for the WordCamp.
</ins><span class="cx" style="display: block; padding: 0 10px">  * @throws Exception
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function validate_wordcamp_id( $post_id, array $config = [] ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -139,28 +140,28 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $wordcamp_post = get_post( $post_id );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        if ( ! $wordcamp_post instanceof WP_Post || WCPT_POST_TYPE_ID !== get_post_type( $wordcamp_post ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                throw new Exception( 'Please enter a valid WordCamp ID.' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         throw new Exception( sprintf(
+                       'Invalid WordCamp ID: %s',
+                       esc_html( $post_id )
+               ) );
</ins><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">-        $valid = [
-               'post_id' => $post_id,
-               'site_id' => 0,
-       ];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $valid = new stdClass();
</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 ( $config['require_site'] ) {
-               $site_id = get_wordcamp_site_id( $wordcamp_post );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $valid->post_id = $post_id;
+       $valid->site_id = get_wordcamp_site_id( $wordcamp_post );
</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 ( ! $site_id ) {
-                       throw new Exception( 'The specified WordCamp does not have a site yet.' );
-               }
-
-               $valid['site_id'] = $site_id;
-       }
-
</del><span class="cx" style="display: block; padding: 0 10px">         if ( $switched ) {
</span><span class="cx" style="display: block; padding: 0 10px">                restore_current_blog();
</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">+        if ( $config['require_site'] && ! $valid->site_id ) {
+               throw new Exception( sprintf(
+                       'The WordCamp with ID %d does not have a site.',
+                       absint( $post_id )
+               ) );
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         return $valid;
</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_htmlwpcontentpluginswordcampreportsindexphp"></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/wordcamp-reports/index.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/wordcamp-reports/index.php        2018-08-02 19:00:11 UTC (rev 7587)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/index.php  2018-08-02 19:12:19 UTC (rev 7588)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -133,8 +133,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                __NAMESPACE__ . '\Report\Sponsor_Invoices',
</span><span class="cx" style="display: block; padding: 0 10px">                __NAMESPACE__ . '\Report\Payment_Activity',
</span><span class="cx" style="display: block; padding: 0 10px">                __NAMESPACE__ . '\Report\Sponsorship_Grants',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                __NAMESPACE__ . '\Report\WordCamp_Status',
</ins><span class="cx" style="display: block; padding: 0 10px">                 __NAMESPACE__ . '\Report\WordCamp_Details',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                __NAMESPACE__ . '\Report\WordCamp_Status',
</del><span class="cx" style="display: block; padding: 0 10px">                 __NAMESPACE__ . '\Report\Meetup_Groups',
</span><span class="cx" style="display: block; padding: 0 10px">                __NAMESPACE__ . '\Report\Meetup_Events',
</span><span class="cx" style="display: block; padding: 0 10px">                __NAMESPACE__ . '\Report\WordCamp_Payment_Methods',
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsviewshtmlwordcampstatusphp"></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/wordcamp-reports/views/html/wordcamp-status.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/wordcamp-reports/views/html/wordcamp-status.php   2018-08-02 19:00:11 UTC (rev 7587)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/html/wordcamp-status.php     2018-08-02 19:12:19 UTC (rev 7588)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6,8 +6,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> namespace WordCamp\Reports\Views\HTML\WordCamp_Status;
</span><span class="cx" style="display: block; padding: 0 10px"> defined( 'WPINC' ) || die();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/** @var \DateTime $start_date */
-/** @var \DateTime $end_date */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use DateTime;
+
+/** @var DateTime $start_date */
+/** @var DateTime $end_date */
</ins><span class="cx" style="display: block; padding: 0 10px"> /** @var string $status */
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var array $active_camps */
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var array $inactive_camps */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -14,7 +16,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var array $statuses */
</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">-<?php if ( ! empty( $active_camps ) ) : ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php if ( count( $active_camps ) ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         <h3 id="active-heading">
</span><span class="cx" style="display: block; padding: 0 10px">                <?php if ( $status ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                        WordCamps set to &ldquo;<?php echo esc_html( $statuses[ $status ] ); ?>&rdquo;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -28,6 +30,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px">        </h3>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        <table class="striped widefat but-not-too-wide">
+               <tr>
+                       <td>Active WordCamps</td>
+                       <td class="number"><?php echo number_format_i18n( count( $active_camps ) ); ?></td>
+               </tr>
+       </table>
+
</ins><span class="cx" style="display: block; padding: 0 10px">         <?php foreach ( $active_camps as $active_camp ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                <p><strong class="active-camp"><?php echo esc_html( $active_camp['name'] ); ?></strong> &ndash; <?php echo esc_html( $statuses[ $active_camp['latest_status'] ] ); ?></p>
</span><span class="cx" style="display: block; padding: 0 10px">                <ul class="status-log ul-disc">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -42,7 +51,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        <?php endforeach; ?>
</span><span class="cx" style="display: block; padding: 0 10px"> <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php if ( ! empty( $inactive_camps ) ) : ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php if ( count( $inactive_camps ) ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         <h3 id="inactive-heading">
</span><span class="cx" style="display: block; padding: 0 10px">                WordCamps
</span><span class="cx" style="display: block; padding: 0 10px">                <?php if ( $status ) : ?>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -56,6 +65,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px">        </h3>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        <table class="striped widefat but-not-too-wide">
+               <tr>
+                       <td>Inactive WordCamps</td>
+                       <td class="number"><?php echo number_format_i18n( count( $inactive_camps ) ); ?></td>
+               </tr>
+       </table>
+
</ins><span class="cx" style="display: block; padding: 0 10px">         <ul>
</span><span class="cx" style="display: block; padding: 0 10px">        <?php foreach ( $inactive_camps as $inactive_camp ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                <li>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -68,7 +84,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> <?php if ( empty( $active_camps ) && empty( $inactive_camps ) ) : ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <h3 id="no-data-heading">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <p>
</ins><span class="cx" style="display: block; padding: 0 10px">                 No data
</span><span class="cx" style="display: block; padding: 0 10px">                <?php if ( $status ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                        involving &ldquo;<?php echo esc_html( $statuses[ $status ] ); ?>&rdquo;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -78,5 +94,5 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <?php else : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                        between <?php echo esc_html( $start_date->format( 'M jS, Y' ) ); ?> and <?php echo esc_html( $end_date->format( 'M jS, Y' ) ); ?>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php endif; ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        </h3>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ </p>
</ins><span class="cx" style="display: block; padding: 0 10px"> <?php endif; ?>
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsviewspublicwordcampstatusphp"></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/wordcamp-reports/views/public/wordcamp-status.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/wordcamp-reports/views/public/wordcamp-status.php 2018-08-02 19:00:11 UTC (rev 7587)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/public/wordcamp-status.php   2018-08-02 19:12:19 UTC (rev 7588)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -12,6 +12,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var string $period */
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var string $status */
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var array  $years */
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/** @var array  $quarters */
</ins><span class="cx" style="display: block; padding: 0 10px"> /** @var array  $months */
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var array  $statuses */
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var Report\WordCamp_Status|null $report */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -36,10 +37,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        <label for="period">Time Period</label>
</span><span class="cx" style="display: block; padding: 0 10px">                        <select id="period" name="period">
</span><span class="cx" style="display: block; padding: 0 10px">                                <option value="all"<?php selected( 'all' === $period ); ?>>Entire year</option>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <option value="q1"<?php selected( 'q1' === $period ); ?>>1st quarter</option>
-                               <option value="q2"<?php selected( 'q2' === $period ); ?>>2nd quarter</option>
-                               <option value="q3"<?php selected( 'q3' === $period ); ?>>3rd quarter</option>
-                               <option value="q4"<?php selected( 'q4' === $period ); ?>>4th quarter</option>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <?php foreach ( $quarters as $quarter_value => $quarter_label ) : ?>
+                                       <option value="<?php echo esc_attr( $quarter_value ); ?>"<?php selected( $quarter_value, $period ); ?>><?php echo esc_html( $quarter_label ); ?></option>
+                               <?php endforeach; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <?php foreach ( $months as $month_value => $month_label ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <option value="<?php echo esc_attr( $month_value ); ?>"<?php selected( $month_value, $period ); ?>><?php echo esc_html( $month_label ); ?></option>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php endforeach; ?>
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsviewsreportwordcampdetailsphp"></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/wordcamp-reports/views/report/wordcamp-details.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/wordcamp-reports/views/report/wordcamp-details.php        2018-08-02 19:00:11 UTC (rev 7587)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/report/wordcamp-details.php  2018-08-02 19:12:19 UTC (rev 7588)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -9,12 +9,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> use WordCamp\Reports;
</span><span class="cx" style="display: block; padding: 0 10px"> use WordCamp\Reports\Report;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/** @var string $start_date */
-/** @var string $end_date */
-/** @var bool   $include_dateless */
-/** @var string $status */
-/** @var array  $statuses */
-/** @var array  $available_fields */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/** @var array $field_defaults */
</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"> <div class="wrap">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -37,54 +32,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <table class="form-table">
</span><span class="cx" style="display: block; padding: 0 10px">                        <tbody>
</span><span class="cx" style="display: block; padding: 0 10px">                        <tr>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <th scope="row"><label for="start-date">Start Date</label></th>
-                               <td><input type="date" id="start-date" name="start-date" value="<?php echo esc_attr( $start_date ) ?>" required /></td>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <th scope="row"><label for="start-date">Start Date (optional)</label></th>
+                               <td><input type="date" id="start-date" name="start-date" value="" /></td>
</ins><span class="cx" style="display: block; padding: 0 10px">                         </tr>
</span><span class="cx" style="display: block; padding: 0 10px">                        <tr>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <th scope="row"><label for="end-date">End Date</label></th>
-                               <td><input type="date" id="end-date" name="end-date" value="<?php echo esc_attr( $end_date ) ?>" required /></td>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <th scope="row"><label for="end-date">End Date (optional)</label></th>
+                               <td><input type="date" id="end-date" name="end-date" value="" /></td>
</ins><span class="cx" style="display: block; padding: 0 10px">                         </tr>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <tr>
-                               <th scope="row"><label for="include_dateless">Include WordCamps without a date</label></th>
-                               <td><input type="checkbox" id="include_dateless" name="include_dateless"<?php checked( $include_dateless ); ?> /></td>
-                       </tr>
-                       <tr>
-                               <th scope="row"><label for="status">Status (optional)</label></th>
-                               <td>
-                                       <select id="status" name="status">
-                                               <option value="any"<?php selected( ( ! $status || 'any' === $status ) ); ?>>Any</option>
-                                               <?php foreach ( $statuses as $value => $label ) : ?>
-                                                       <option value="<?php echo esc_attr( $value ); ?>"<?php selected( $value, $status ); ?>><?php echo esc_attr( $label ); ?></option>
-                                               <?php endforeach; ?>
-                                       </select>
-                               </td>
-                       </tr>
-                       <tr>
-                               <th scope="row"><label for="refresh">Refresh results</label></th>
-                               <td><input type="checkbox" id="refresh" name="refresh" /></td>
-                       </tr>
</del><span class="cx" style="display: block; padding: 0 10px">                         </tbody>
</span><span class="cx" style="display: block; padding: 0 10px">                </table>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <fieldset class="fields-container">
-                       <legend class="fields-label">Available Fields</legend>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php Report\WordCamp_Details::render_available_fields( 'private', $field_defaults ) ?>
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <?php foreach ( $available_fields as $field_name => $extra_props ) : ?>
-                               <div class="field-checkbox">
-                                       <input
-                                               type="checkbox"
-                                               id="fields-<?php echo esc_attr( $field_name ); ?>"
-                                               name="fields[]"
-                                               value="<?php echo esc_attr( $field_name ); ?>"
-                                               <?php if ( $extra_props && is_string( $extra_props ) ) echo esc_html( $extra_props ); ?>
-                                       />
-                                       <label for="fields-<?php echo esc_attr( $field_name ); ?>">
-                                               <?php echo esc_attr( $field_name ); ?>
-                                       </label>
-                               </div>
-                       <?php endforeach; ?>
-               </fieldset>
-
</del><span class="cx" style="display: block; padding: 0 10px">                 <?php submit_button( 'Export CSV', 'primary', 'action', false ); ?>
</span><span class="cx" style="display: block; padding: 0 10px">        </form>
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampreportsviewsreportwordcampstatusphp"></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/wordcamp-reports/views/report/wordcamp-status.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/wordcamp-reports/views/report/wordcamp-status.php 2018-08-02 19:00:11 UTC (rev 7587)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/report/wordcamp-status.php   2018-08-02 19:12:19 UTC (rev 7588)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -13,6 +13,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var string $end_date */
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var string $status */
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var array  $statuses */
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/** @var array  $field_defaults */
</ins><span class="cx" style="display: block; padding: 0 10px"> /** @var Report\WordCamp_Status|null $report */
</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">@@ -37,21 +38,24 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        <tbody>
</span><span class="cx" style="display: block; padding: 0 10px">                                <tr>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <th scope="row"><label for="start-date">Start Date</label></th>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <td><input type="date" id="start-date" name="start-date" value="<?php echo esc_attr( $start_date ) ?>" /></td>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <td><input type="date" id="start-date" name="start-date" value="<?php echo esc_attr( $start_date ) ?>" required /></td>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 </tr>
</span><span class="cx" style="display: block; padding: 0 10px">                                <tr>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <th scope="row"><label for="end-date">End Date</label></th>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <td><input type="date" id="end-date" name="end-date" value="<?php echo esc_attr( $end_date ) ?>" /></td>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <td><input type="date" id="end-date" name="end-date" value="<?php echo esc_attr( $end_date ) ?>" required /></td>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 </tr>
</span><span class="cx" style="display: block; padding: 0 10px">                                <tr>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <th scope="row"><label for="status">Status (optional)</label></th>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <td>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                <select id="status" name="status">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         <select id="status" name="status" class="select2-container">
</ins><span class="cx" style="display: block; padding: 0 10px">                                                         <option value="any"<?php selected( ( ! $status || 'any' === $status ) ); ?>>Any</option>
</span><span class="cx" style="display: block; padding: 0 10px">                                                        <?php foreach ( $statuses as $value => $label ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                                                <option value="<?php echo esc_attr( $value ); ?>"<?php selected( $value, $status ); ?>><?php echo esc_attr( $label ); ?></option>
</span><span class="cx" style="display: block; padding: 0 10px">                                                        <?php endforeach; ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                                </select>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                <p class="description">
+                                                       This will select all WordCamps that had this status at any time during the date range.
+                                               </p>
</ins><span class="cx" style="display: block; padding: 0 10px">                                         </td>
</span><span class="cx" style="display: block; padding: 0 10px">                                </tr>
</span><span class="cx" style="display: block; padding: 0 10px">                                <tr>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -61,7 +65,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        </tbody>
</span><span class="cx" style="display: block; padding: 0 10px">                </table>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <?php submit_button( 'Show results', 'primary', '' ); ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php submit_button( 'Show results', 'primary', 'action', false ); ?>
+
+               <button id="fields-toggle" class="secondary button-secondary">Export resulting WordCamps as a CSV</button>
+
+               <section id="fields-section" class="hidden">
+                       <?php Report\WordCamp_Details::render_available_fields( 'private', $field_defaults ); ?>
+                       <?php submit_button( 'Export CSV', 'primary', 'action', false ); ?>
+               </section>
</ins><span class="cx" style="display: block; padding: 0 10px">         </form>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        <?php if ( $report instanceof Report\WordCamp_Status ) : ?>
</span></span></pre>
</div>
</div>

</body>
</html>