<!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>[5336] sites/trunk/wordcamp.org/public_html/wp-content/plugins: WordCamp: Add new WordCamp Mentors plugin</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" 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/5336">5336</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/5336","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>2017-04-14 22:35:45 +0000 (Fri, 14 Apr 2017)</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: Add new WordCamp Mentors plugin

This plugin currently includes the Planning Checklist tool.</pre>

<h3>Added Paths</h3>
<ul>
<li>sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/</li>
<li>sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/css/</li>
<li>sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/css/tasks/</li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorscsstasksdashboardcss">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/css/tasks/dashboard.css</a></li>
<li>sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/</li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsincludestaskscontrollerphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks-controller.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsincludestasksdataphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks-data.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsincludestaskslistphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks-list.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsincludestasksphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks.php</a></li>
<li>sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/js/</li>
<li>sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/js/tasks/</li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsjstasksdashboardjs">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/js/tasks/dashboard.js</a></li>
<li>sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/views/</li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsviewstaskmorephp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/views/task-more.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsviewstasksphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/views/tasks.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorswordcampmentorsphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/wordcamp-mentors.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorscsstasksdashboardcss"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/css/tasks/dashboard.css</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-mentors/css/tasks/dashboard.css                          (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/css/tasks/dashboard.css    2017-04-14 22:35:45 UTC (rev 5336)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,86 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+.wp-list-table > tbody > .wcm-highlight,
+.wp-list-table > tbody > .wcm-expanded {
+    background-color: #f7fcfe;
+}
+
+.widefat td {
+       vertical-align: middle;
+}
+
+.wcm-task:hover {
+       box-shadow: inset 0 0 7px 7px #f7fcfe;
+       cursor: pointer;
+}
+
+.wcm-task td,
+.wcm-more td {
+       border-bottom: 1px solid #f9f9f9;
+}
+
+.wcm-task.wcm-expanded td {
+       border-bottom-color: #f7fcfe;
+}
+
+.column-task {
+       width: 50%;
+}
+
+.column-task_category ul {
+       margin: 0;
+}
+
+.column-task_category li {
+       color: #fff;
+       float: left;
+       margin: 2px;
+       padding: 3px 6px;
+}
+.rtl .column-task_category li {
+       float: right;
+       margin-right: 0;
+       margin-left: 2px;
+}
+
+.category-after-party {
+       background-color: #a7863d;
+}
+.category-audio-video {
+       background-color: #a4c2f4;
+}
+.category-budget {
+       background-color: #d64db8;
+}
+.category-committee {
+       background-color: #e69138;
+}
+.category-contributor-day {
+       background-color: #c67df4;
+}
+.category-design {
+       background-color: #76a5af;
+}
+.category-food {
+       background-color: #a64d79;
+}
+.category-lead {
+       background-color: #cc0000;
+}
+.category-registration {
+       background-color: #ffa6e7;
+}
+.category-speaker {
+       background-color: #3d85c6;
+}
+.category-sponsor {
+       background-color: #38761d;
+}
+.category-swag {
+       background-color: #93c47d;
+}
+.category-volunteer {
+       background-color: #674ea7;
+}
+.category-web {
+       background-color: #bf9000;
+}
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsincludestaskscontrollerphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks-controller.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-mentors/includes/tasks-controller.php                            (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks-controller.php      2017-04-14 22:35:45 UTC (rev 5336)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,180 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * REST Controller for Tasks.
+ *
+ * @package WordCamp\Mentors
+ */
+
+namespace WordCamp\Mentors\Tasks;
+defined( 'WPINC' ) || die();
+
+use WordCamp\Mentors;
+
+/**
+ * Class Controller.
+ *
+ * @package WordCamp\Mentors\Tasks
+ */
+class Controller extends \WP_REST_Posts_Controller {
+       /**
+        * Registers the routes for the objects of the controller.
+        *
+        * Based on the routes for the Posts controller, but more limited.
+        *
+        * @since 1.0.0
+        *
+        * @see register_rest_route()
+        */
+       public function register_routes() {
+               register_rest_route( $this->namespace, '/' . $this->rest_base, array(
+                       array(
+                               'methods'             => \WP_REST_Server::READABLE,
+                               'callback'            => array( $this, 'get_items' ),
+                               'permission_callback' => array( $this, 'get_permissions_check' ),
+                               'args'                => $this->get_collection_params(),
+                       ),
+                       'schema' => array( $this, 'get_public_item_schema' ),
+               ) );
+
+               $get_item_args = array(
+                       'context'  => $this->get_context_param( array( 'default' => 'view' ) ),
+               );
+
+               register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
+                       'args' => array(
+                               'id' => array(
+                                       'description' => __( 'Unique identifier for the object.' ),
+                                       'type'        => 'integer',
+                               ),
+                       ),
+                       array(
+                               'methods'             => \WP_REST_Server::READABLE,
+                               'callback'            => array( $this, 'get_item' ),
+                               'permission_callback' => array( $this, 'get_permissions_check' ),
+                               'args'                => $get_item_args,
+                       ),
+                       array(
+                               'methods'             => \WP_REST_Server::EDITABLE,
+                               'callback'            => array( $this, 'update_item' ),
+                               'permission_callback' => array( $this, 'get_permissions_check' ),
+                               'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
+                       ),
+                       'schema' => array( $this, 'get_public_item_schema' ),
+               ) );
+       }
+
+       /**
+        * Catchall permissions check for interacting with tasks via the REST API.
+        *
+        * @since 1.0.0
+        *
+        * @return bool
+        */
+       public function get_permissions_check() {
+               return current_user_can( Mentors\ORGANIZER_CAP ) || current_user_can( Mentors\MENTOR_CAP );
+       }
+
+       /**
+        * Retrieves the Task post's schema, conforming to JSON Schema.
+        *
+        * Task-specific modifications to the standard post schema.
+        *
+        * @since 1.0.0
+        *
+        * @return array Item schema data.
+        */
+       public function get_item_schema() {
+               $schema = parent::get_item_schema();
+
+               // Show the custom statuses in the REST response.
+               if ( false === array_search( 'view', $schema['properties']['status']['context'], true ) ) {
+                       $schema['properties']['status']['context'][] = 'view';
+               }
+
+               // Specify custom statuses.
+               $schema['properties']['status']['enum'] = array_keys( get_task_statuses() );
+
+               // Add a localized, relative date string.
+               $schema['properties']['modified']['type'] = 'object';
+               $schema['properties']['modified']['properties'] = array(
+                       'raw' => array(
+                               'description' => __( "The date the object was last modified, in the site's timezone." ),
+                               'type'        => 'string',
+                               'format'      => 'date-time',
+                               'context'     => array( 'view', 'edit' ),
+                               'readonly'    => true,
+                       ),
+                       'relative' => array(
+                               'description' => __( 'The relative time since the object was last modified.' ),
+                               'type'        => 'string',
+                               'format'      => 'date-time',
+                               'context'     => array( 'view', 'edit' ),
+                               'readonly'    => true,
+                       ),
+               );
+
+               return $this->add_additional_fields_schema( $schema );
+       }
+
+       /**
+        * Retrieves the query params for the posts collection.
+        *
+        * Task-specific modifications to the standard posts collection query params.
+        *
+        * @since 1.0.0
+        *
+        * @return array Collection parameters.
+        */
+       public function get_collection_params() {
+               $query_params = parent::get_collection_params();
+
+               // Allow posts with our custom statuses.
+               $query_params['status']['items']['enum'] = array_keys( get_task_statuses() );
+               $query_params['status']['default'] = $query_params['status']['items']['enum'];
+
+               // Allow a higher maximum for query results.
+               $query_params['per_page']['maximum'] = 300;
+
+               return $query_params;
+       }
+
+       /**
+        * Sanitizes and validates the list of post statuses, including whether the
+        * user can query private statuses.
+        *
+        * @since 1.0.0
+        *
+        * @param  string|array     $statuses  One or more post statuses.
+        * @param  \WP_REST_Request $request   Full details about the request.
+        * @param  string           $parameter Additional parameter to pass to validation.
+        * @return array|\WP_Error A list of valid statuses, otherwise WP_Error object.
+        */
+       public function sanitize_post_statuses( $statuses, $request, $parameter ) {
+               $statuses = wp_parse_slug_list( $statuses );
+
+               $task_statuses = array_keys( get_task_statuses() );
+
+               foreach ( $statuses as $status ) {
+                       if ( in_array( $status, $task_statuses, true ) ) {
+                               continue;
+                       }
+
+                       if ( current_user_can( Mentors\ORGANIZER_CAP ) ) {
+                               $result = rest_validate_request_arg( $status, $request, $parameter );
+                               if ( is_wp_error( $result ) ) {
+                                       return $result;
+                               }
+                       } else {
+                               return new \WP_Error(
+                                       'rest_forbidden_status',
+                                       __( 'Status is forbidden.' ),
+                                       array(
+                                               'status' => rest_authorization_required_code(),
+                                       )
+                               );
+                       }
+               }
+
+               return $statuses;
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsincludestasksdataphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks-data.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-mentors/includes/tasks-data.php                          (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks-data.php    2017-04-14 22:35:45 UTC (rev 5336)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,778 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Data for the Planning Checklist.
+ *
+ * @package WordCamp\Mentors
+ */
+
+namespace WordCamp\Mentors\Tasks;
+defined( 'WPINC' ) || die();
+
+use WordCamp\Mentors;
+
+/**
+ * Define the task categories for the Planning Checklist.
+ *
+ * @since 1.0.0
+ *
+ * @return array
+ */
+function get_task_category_data() {
+       return array(
+               'after-party'     => esc_html__( 'After Party', 'wordcamporg' ),
+               'audio-video'     => esc_html__( 'Audio/Video', 'wordcamporg' ),
+               'budget'          => esc_html__( 'Budget', 'wordcamporg' ),
+               'committee'       => esc_html__( 'Committee', 'wordcamporg' ),
+               'contributor-day' => esc_html__( 'Contributor Day', 'wordcamporg' ),
+               'design'          => esc_html__( 'Design', 'wordcamporg' ),
+               'food'            => esc_html__( 'Food', 'wordcamporg' ),
+               'lead'            => esc_html__( 'Lead', 'wordcamporg' ),
+               'registration'    => esc_html__( 'Registration', 'wordcamporg' ),
+               'speaker'         => esc_html__( 'Speaker', 'wordcamporg' ),
+               'sponsor'         => esc_html__( 'Sponsor', 'wordcamporg' ),
+               'swag'            => esc_html__( 'Swag', 'wordcamporg' ),
+               'volunteer'       => esc_html__( 'Volunteer', 'wordcamporg' ),
+               'web'             => esc_html__( 'Web', 'wordcamporg' ),
+       );
+}
+
+/**
+ * Define the tasks for the Planning Checklist.
+ *
+ * @since 1.0.0
+ *
+ * @return array
+ */
+function get_task_data() {
+       /**
+        * When adding or editing items, be sure to update the value of the DATA_VERSION constant in
+        * the wordcamp-mentors.php file with the current YYYYMMDD timestamp (include hours and
+        * minutes if necessary).
+        *
+        * The task data keys are randomized strings instead of sequential and/or contextual because:
+        * - The order of the tasks could change, in which case having out-of-order sequential numbers
+        *   could be confusing.
+        * - The wording, category, or other properties of a task could change, in which case a key
+        *   string based on these properties could be confusing.
+        *
+        * When adding new task items, randomized key strings can be created here:
+        * http://textmechanic.com/text-tools/randomization-tools/random-string-generator/
+        */
+       return array(
+               't5o8' => array(
+                       'title'   => __( 'Apply to organize your local WordCamp', 'wordcamporg' ),
+                       'excerpt' => __( 'You must fill out a new application each year.', 'wordcamporg' ),
+                       'cat'     => array( 'lead' ),
+                       'link'    => array(
+                               'text' => __( 'Apply', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/become-an-organizer/',
+                       ),
+               ),
+               '22ix' => array(
+                       'title'   => __( 'Recruit your organizing team', 'wordcamporg' ),
+                       'excerpt' => __( 'Recruit a full organizing team from within your community.', 'wordcamporg' ),
+                       'cat'     => array( 'lead' ),
+                       'link'    => array(
+                               'text' => __( 'Build your team', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/first-steps/the-organizing-team/',
+                       ),
+               ),
+               'v2cu' => array(
+                       'title'   => __( 'Explore venue options', 'wordcamporg' ),
+                       'excerpt' => __( 'Look into venues that may work for your event. Ask for suggestions from your team and meetup.', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+                       'link'    => array(
+                               'text' => __( 'Venue Information', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/first-steps/venue-and-date/',
+                       ),
+               ),
+               '8pb0' => array(
+                       'title'   => __( 'Update budget page', 'wordcamporg' ),
+                       'excerpt' => __( 'Add your camp\'s budget information to the budget tool located in your dashboard.', 'wordcamporg' ),
+                       'cat'     => array( 'budget' ),
+                       'link'    => array(
+                               'text' => __( 'Budget and Finances', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/first-steps/budget-and-finances/',
+                       ),
+               ),
+               'jv29' => array(
+                       'title'   => __( 'Start design and branding', 'wordcamporg' ),
+                       'excerpt' => __( 'Start thinking about and implementing Design process/branding.', 'wordcamporg' ),
+                       'cat'     => array( 'design' ),
+               ),
+               'o1rt' => array(
+                       'title'   => __( 'Brainstorming speaker topics and ideas', 'wordcamporg' ),
+                       'excerpt' => __( 'Brainstorm and discuss with your team topics/requested speaker topics.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+                       'link'    => array(
+                               'text' => __( 'Speakers', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/speakers/',
+                       ),
+               ),
+               '7j6f' => array(
+                       'title'   => __( 'Venue walk-through', 'wordcamporg' ),
+                       'excerpt' => __( 'Before you lock-in your venue have a walk through with your leads and wranglers to ensure this venue will be accessible and functional for all areas of the event.', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+                       'link'    => array(
+                               'text' => __( 'Venue Information', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/first-steps/venue-and-date/',
+                       ),
+               ),
+               'siq8' => array(
+                       'title'   => __( 'Choose a Date', 'wordcamporg' ),
+                       'excerpt' => __( 'Consider holidays, other events in your city, and other WordCamps in your region. Be sure to have your budget approved and have a contract or agreement for the venue signed by WordCamp Central before you announce your dates.', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+                       'link'    => array(
+                               'text' => __( 'Pick a date', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/first-steps/venue-and-date/',
+                       ),
+               ),
+               'lkcs' => array(
+                       'title'   => __( 'Get quotes from vendors', 'wordcamporg' ),
+                       'excerpt' => '',
+                       'cat'     => array( 'swag' ),
+               ),
+               '5j0m' => array(
+                       'title'   => __( 'Select caterer and menu for event', 'wordcamporg' ),
+                       'excerpt' => __( 'Ensure that you accomodate all dietary requirements.', 'wordcamporg' ),
+                       'cat'     => array( 'food' ),
+                       'link'    => array(
+                               'text' => __( 'Food and Beverage', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/food-and-beverage/',
+                       ),
+               ),
+               'n7rk' => array(
+                       'title'   => __( 'Budget approval', 'wordcamporg' ),
+                       'excerpt' => __( 'Build your budget and submit it for review and approval.', 'wordcamporg' ),
+                       'cat'     => array( 'budget' ),
+                       'link'    => array(
+                               'text' => __( 'Budget review and approval', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/first-steps/budget-and-finances/',
+                       ),
+               ),
+               'f087' => array(
+                       'title'   => __( 'Confirm your venue', 'wordcamporg' ),
+                       'excerpt' => __( 'Make sure you get a contract, agreement or confirmation email. In order to move your WordCamp to scheduled you need some sort of written confirmation that the venue is reserved for your dates. ', 'wordcamporg' ),
+                       'cat'     => array( 'lead' ),
+                       'link'    => array(
+                               'text' => __( 'Lock in your venue', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/first-steps/venue-and-date/',
+                       ),
+               ),
+               'asdf' => array(
+                       'title'   => __( 'Contributor day venue', 'wordcamporg' ),
+                       'excerpt' => __( 'If you\'re hosting a contributor day and it will not be held at your primary venue, find a location now.', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+               ),
+               '75sp' => array(
+                       'title'   => __( 'Find after party and speaker event venues', 'wordcamporg' ),
+                       'excerpt' => __( 'Please keep in mind that all venues should be open and welcoming to everyone. No age restricted venues. Keep in mind ease of access from your event venue.', 'wordcamporg' ),
+                       'cat'     => array( 'after-party' ),
+                       'link'    => array(
+                               'text' => __( 'Parties', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/parties/',
+                       ),
+               ),
+               'vbbj' => array(
+                       'title'   => __( 'Setup and design your site', 'wordcamporg' ),
+                       'excerpt' => '',
+                       'cat'     => array( 'web' ),
+                       'link'    => array(
+                               'text' => __( 'Site setup and design', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/first-steps/web-presence/#website',
+                       ),
+               ),
+               'krv3' => array(
+                       'title'   => __( 'Launch website design', 'wordcamporg' ),
+                       'excerpt' => __( 'Launch site design and take site out of "Coming Soon" mode.', 'wordcamporg' ),
+                       'cat'     => array( 'web' ),
+               ),
+               'inv6' => array(
+                       'title'   => __( 'Announce your event', 'wordcamporg' ),
+                       'excerpt' => __( 'Get the word out to the community!', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+                       'link'    => array(
+                               'text' => __( 'Publicity', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/publicity/',
+                       ),
+               ),
+               '3vf4' => array(
+                       'title'   => __( 'Set sponsorship levels', 'wordcamporg' ),
+                       'excerpt' => __( 'Identify internal sponsorship levels and what each level includes.', 'wordcamporg' ),
+                       'cat'     => array( 'sponsor' ),
+                       'link'    => array(
+                               'text' => __( 'Local sponsorshps', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/fundraising/local-wordcamp-sponsorship/',
+                       ),
+               ),
+               'erjt' => array(
+                       'title'   => __( 'Create documents and templates', 'wordcamporg' ),
+                       'excerpt' => __( 'Create/use email templates for sponsorship, volunteers, speakers, etc.', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+                       'link'    => array(
+                               'text' => __( 'Helpful documents and templates', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/first-steps/helpful-documents-and-templates/',
+                       ),
+               ),
+               '5uqp' => array(
+                       'title'   => __( 'Reach out to prior sponsors', 'wordcamporg' ),
+                       'excerpt' => __( 'Reach out to sponsors from previous years. If this is your event\'s first year reach out to sponsors from past WordCamps in your region.', 'wordcamporg' ),
+                       'cat'     => array( 'sponsor' ),
+                       'link'    => array(
+                               'text' => __( 'Local Sponsors', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/fundraising/local-wordcamp-sponsorship/',
+                       ),
+               ),
+               '9tsp' => array(
+                       'title'   => __( 'Open call for sponsors', 'wordcamporg' ),
+                       'excerpt' => __( 'Having already reached out to prior sponsors with your completed sponsor packet, it\'s now time to publically open your call for sponsors on your site. At this time you can also reach out to sponsors who have supported other WordCamps in your area.', 'wordcamporg' ),
+                       'cat'     => array( 'sponsor' ),
+                       'link'    => array(
+                               'text' => __( 'Fundraising', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/fundraising/',
+                       ),
+               ),
+               'u1rl' => array(
+                       'title'   => __( 'Vet sponsor applicants', 'wordcamporg' ),
+                       'excerpt' => __( 'Vet all sponsor applicants to ensure they meet expectations of the WordPress Open Source Project with GPL and Trademark.', 'wordcamporg' ),
+                       'cat'     => array( 'sponsor' ),
+                       'link'    => array(
+                               'text' => __( 'GPL primer', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/gpl-primer/',
+                       ),
+               ),
+               'ibzq' => array(
+                       'title'   => __( 'Open call for speakers', 'wordcamporg' ),
+                       'excerpt' => __( 'Have web wrangler add call for speakers on site.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+                       'link'    => array(
+                               'text' => __( 'Speakers', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/speakers/',
+                       ),
+               ),
+               'wccy' => array(
+                       'title'   => __( 'Speaker recruitment and Speaker Applicant outreach', 'wordcamporg' ),
+                       'excerpt' => __( 'Reach out to known/wanted community speakers and community groups. Encourage those from commonly-under represented groups to apply. Invite a keynote or featured speaker if applicable. ', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+                       'link'    => array(
+                               'text' => __( 'Speakers', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/speakers/',
+                       ),
+               ),
+               '3b1k' => array(
+                       'title'   => __( 'Determine what volunteers roles are needed', 'wordcamporg' ),
+                       'excerpt' => '',
+                       'cat'     => array( 'volunteer' ),
+                       'link'    => array(
+                               'text' => __( 'Volunteers', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/volunteers/',
+                       ),
+               ),
+               'rko5' => array(
+                       'title'   => __( 'Open call for volunteers', 'wordcamporg' ),
+                       'excerpt' => __( 'Have web wrangler add call for volunteers on website including descriptions.', 'wordcamporg' ),
+                       'cat'     => array( 'volunteer' ),
+                       'link'    => array(
+                               'text' => __( 'Volunteers', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/volunteers/',
+                       ),
+               ),
+               'uw8g' => array(
+                       'title'   => __( 'Open ticket sales', 'wordcamporg' ),
+                       'excerpt' => __( 'Set up your tickets -- make sure you ask for dietary preferences and t-shirt sizes (if providing t-shirts). Also ask in registration if attendees require any special acoomodation to attend.', 'wordcamporg' ),
+                       'cat'     => array( 'registration' ),
+                       'link'    => array(
+                               'text' => __( 'Selling Tickets', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/selling-tickets/',
+                       ),
+               ),
+               'qi9n' => array(
+                       'title'   => __( 'Anonymize speaker submissions (remove gender specific information, names)', 'wordcamporg' ),
+                       'excerpt' => __( 'Prepare the speaker submissions and prepare the speaker selection team.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+               ),
+               'co30' => array(
+                       'title'   => __( 'Review anonymized speaker submissions with your speaker panel', 'wordcamporg' ),
+                       'excerpt' => __( 'After the team reviews all submissions, speaker lead should review rejected talks to ensure you\'re not discarding a spectacular speaker who didn\'t present well in application.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+                       'link'    => array(
+                               'text' => __( 'Speakers', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/speakers/',
+                       ),
+               ),
+               'eu82' => array(
+                       'title'   => __( 'Send out update email to speaker applicants', 'wordcamporg' ),
+                       'excerpt' => __( 'Let speaker applicants know their submissions were received and give them an idea of when they will hear back from the team.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+               ),
+               'm2sc' => array(
+                       'title'   => __( 'Contact sponsors for details', 'wordcamporg' ),
+                       'excerpt' => __( 'Contact sponsors to gather information needed for site and signage. If they qualify for a table or to hand out swag, let them know where to ship merchandise and what their space will be like. Provide them with coupon codes and remind them how many free tickets they receive.', 'wordcamporg' ),
+                       'cat'     => array( 'sponsor' ),
+               ),
+               'k9k0' => array(
+                       'title'   => __( 'Start fulfilling sponsor level benefits', 'wordcamporg' ),
+                       'excerpt' => __( 'If you promised blog posts, tweets, or signage make sure that you\'re scheduling these in compliance with the offerings per level.', 'wordcamporg' ),
+                       'cat'     => array( 'sponsor' ),
+               ),
+               'fucx' => array(
+                       'title'   => __( 'Finalize swag decision and place order', 'wordcamporg' ),
+                       'excerpt' => __( 'By now you should have a swag vendor, place order and ensure it will ship in time for your event. Order custom stickers from StickerGiant.', 'wordcamporg' ),
+                       'cat'     => array( 'swag' ),
+                       'link'    => array(
+                               'text' => __( 'Custom Swag', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/swag/',
+                       ),
+               ),
+               'cxpj' => array(
+                       'title'   => __( 'Makes plans for volunteer orientation', 'wordcamporg' ),
+                       'excerpt' => __( 'Decide if you will do an in person, video, or text orientation for volunteers and plan accordingly.', 'wordcamporg' ),
+                       'cat'     => array( 'volunteer' ),
+                       'link'    => array(
+                               'text' => __( 'Volunteers', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/volunteers/',
+                       ),
+               ),
+               'uutz' => array(
+                       'title'   => __( 'Request video kit from WordCamp Central', 'wordcamporg' ),
+                       'excerpt' => __( 'If you haven\'t hired a videographer, please let WordCamp Central know how many tracks you have and where the camera kits should be received.', 'wordcamporg' ),
+                       'cat'     => array( 'audio-video' ),
+                       'link'    => array(
+                               'text' => __( 'Video', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/video/',
+                       ),
+               ),
+               'mkj1' => array(
+                       'title'   => __( 'Email selected speakers', 'wordcamporg' ),
+                       'excerpt' => __( 'Contact selected speakers. Ask them to sign the speaker agreement and AV release. Remind them you\'ll need to review their slides 2 weeks prior to WordCamp. Give them their coupon code for a free ticket. Determine if they have any special AV requirements and relay that info to your AV person.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+               ),
+               'c00l' => array(
+                       'title'   => __( 'Email remaining speaker applicants', 'wordcamporg' ),
+                       'excerpt' => __( 'Make sure that you notify applicants who were not selected BEFORE you announce your speakers publicly.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+               ),
+               '84y3' => array(
+                       'title'   => __( 'Publish/Announce speakers', 'wordcamporg' ),
+                       'excerpt' => __( 'Also ask speakers to promote on their social channels.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+               ),
+               'sfrb' => array(
+                       'title'   => __( 'Begin posting original content', 'wordcamporg' ),
+                       'excerpt' => __( 'In order to drive traffic to the site and keep your followers engaged it\'s a good idea to publish more than just announcements. Try WordCamp stories, speaker profiles, community involvement stories, etc.', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+               ),
+               'l931' => array(
+                       'title'   => __( 'Publish/Announce sessions and schedule', 'wordcamporg' ),
+                       'excerpt' => '',
+                       'cat'     => array( 'speaker' ),
+               ),
+               'p0ns' => array(
+                       'title'   => __( 'Order speaker gifts', 'wordcamporg' ),
+                       'excerpt' => __( 'Speaker gifts are not required but if it\'s in your budget and you plan to do something you should order at least 6-weeks in advance. Don\'t spend too much on this, remember we should always use funds on what will benefit attendees most.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+               ),
+               '03kv' => array(
+                       'title'   => __( 'Send a pre-Camp event invite (if having)', 'wordcamporg' ),
+                       'excerpt' => __( 'If you\'re hosting a pre-camp event (e.g., a speaker event) make sure you invite the appropriate folks (e.g., speakers, sponsors, volunteers).', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+                       'link'    => array(
+                               'text' => __( 'Parties', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/parties/',
+                       ),
+               ),
+               'srgu' => array(
+                       'title'   => __( 'Publish sponsor posts', 'wordcamporg' ),
+                       'excerpt' => '',
+                       'cat'     => array( 'sponsor' ),
+               ),
+               'bc1e' => array(
+                       'title'   => __( 'Design name badges', 'wordcamporg' ),
+                       'excerpt' => __( 'Review the guidlines for creating badges before finalizing your design.', 'wordcamporg' ),
+                       'cat'     => array( 'design' ),
+                       'link'    => array(
+                               'text' => __( 'Create WordCamp Badges', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/wordcamp-name-badge-templates/',
+                       ),
+               ),
+               'e0uc' => array(
+                       'title'   => __( 'Design signage', 'wordcamporg' ),
+                       'excerpt' => '',
+                       'cat'     => array( 'design' ),
+               ),
+               'ws2p' => array(
+                       'title'   => __( 'Confirm swag order from WordCamp Central', 'wordcamporg' ),
+                       'excerpt' => __( '6 weeks out - WordPress stickers, buttons, and lanyards will come from WordCamp Central. Confirm your shipping address and number of attendees when Central reaches out.', 'wordcamporg' ),
+                       'cat'     => array( 'swag' ),
+                       'link'    => array(
+                               'text' => __( 'Swag', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/swag/',
+                       ),
+               ),
+               '2u4x' => array(
+                       'title'   => __( 'Offer speaker mentorship', 'wordcamporg' ),
+                       'excerpt' => __( '6 weeks out - Especially with less experienced speakers offer them mentorship or the chance to share their talk in advance (via Google Hangouts, speaker training).', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+                       'link'    => array(
+                               'text' => __( 'Speaking at a WordCamp', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/speakers/speaking-at-a-wordcamp/',
+                       ),
+               ),
+               'tpvw' => array(
+                       'title'   => __( 'Create backup plans including backup speaker', 'wordcamporg' ),
+                       'excerpt' => __( 'If a speaker gets sick, do you have someone who can fill in on the same topic? For out of town speakers, what if they miss their flights? Have one backup speaker per track. Back up speakers should not be rejected applicants.', 'wordcamporg' ),
+                       'cat'     => array( 'lead' ),
+               ),
+               'r3ge' => array(
+                       'title'   => __( 'Remind speakers to send slides', 'wordcamporg' ),
+                       'excerpt' => __( 'Remind speakers that you will need their slides by two weeks before the event. If they haven\'t signed the speaker agreement and A/V release, request those.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+               ),
+               'ckzp' => array(
+                       'title'   => __( 'Complete volunteer schedule and confirm volunteers.', 'wordcamporg' ),
+                       'excerpt' => __( '4 weeks out - Email volunteers with details of their role, provide coupon code, and ask them to sign the volunteer agreement.', 'wordcamporg' ),
+                       'cat'     => array( 'volunteer' ),
+                       'link'    => array(
+                               'text' => __( 'Volunteers', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/volunteers/',
+                       ),
+               ),
+               'q97z' => array(
+                       'title'   => __( 'Review speaker slides', 'wordcamporg' ),
+                       'excerpt' => __( '2 weeks out - Collect slides from speakers and review as a team. This helps avoid any inappropriate content in the presentations (WordCamps should be family-friendly, with no swearing or discriminatory jokes/comments), and also helps you catch any misspellings, fauxgos, or other problems in the slides. Check to make sure they properly camel case WordPress and WordCamp.', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+               ),
+               'c46e' => array(
+                       'title'   => __( 'Confirm catering ', 'wordcamporg' ),
+                       'excerpt' => __( 'Confirm catering menu for any meals or snacks you have arranged based on attendee numbers. Ensure delivery/ pickup is scheudled. Please do not run out of coffee. At this time confirm menus for any ancillary events (ie: contributor day, speaker event, after party, etc.) you may be having.', 'wordcamporg' ),
+                       'cat'     => array( 'food' ),
+                       'link'    => array(
+                               'text' => __( 'Food and Beverage', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/food-and-beverage/',
+                       ),
+               ),
+               'nhws' => array(
+                       'title'   => __( 'Finalize name badge order', 'wordcamporg' ),
+                       'excerpt' => __( 'Send your badge files off to the printer. Make sure they\'ll have them ready a day or two before your event.', 'wordcamporg' ),
+                       'cat'     => array( 'design' ),
+               ),
+               'f1ln' => array(
+                       'title'   => __( 'Order signage', 'wordcamporg' ),
+                       'excerpt' => __( 'Make sure to include additional wayfinding signage and confirm with sponsor coordinator that you\'re meeting sponsor level requirements.', 'wordcamporg' ),
+                       'cat'     => array( 'design' ),
+               ),
+               'h641' => array(
+                       'title'   => __( 'Email Speakers', 'wordcamporg' ),
+                       'excerpt' => __( 'Email a final confirmation to your speakers. Include the date and time of their talk, when and with whom they should check in the day they\'re speaking, what the av setup is, and any other details you feel they should know. If you\'re hosting a speaker event make sure they have that information as well.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+               ),
+               'svit' => array(
+                       'title'   => __( 'Email Sponsors', 'wordcamporg' ),
+                       'excerpt' => __( 'Email a final confirmation to your sponsors. Include date and time of their load-in, when they can arrive day of the event, where they will setup and who their on site contact will be. If they\'re invited to your speaker event confirm those details with them as well.', 'wordcamporg' ),
+                       'cat'     => array( 'sponsor' ),
+               ),
+               '2iiq' => array(
+                       'title'   => __( 'Test and prepare equipment from camera kit (if using)', 'wordcamporg' ),
+                       'excerpt' => __( 'Check out your kits to make sure everything is there, the batteries are charged, and you or your video coordiantor understnad how to use them.', 'wordcamporg' ),
+                       'cat'     => array( 'audio-video' ),
+                       'link'    => array(
+                               'text' => __( 'Video Quick Start Guide', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/video/quick-start/',
+                       ),
+               ),
+               'w3l6' => array(
+                       'title'   => __( 'Send a pre-event attendee email', 'wordcamporg' ),
+                       'excerpt' => __( '2-days prior - Send an email to all attendees (including speakers, sponsors, and volunteers) with important information they will need for attendance. Include information about parking, wifi, menus, and registration. If there will be any known traffic disruptions due to events or construction let them know there may be traffic delays. Welcome them to your WordCamp.', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+               ),
+               'gmue' => array(
+                       'title'   => __( 'Close registration', 'wordcamporg' ),
+                       'excerpt' => __( '1-day prior - If you\'re not offering walk-in tickets, close registration.', 'wordcamporg' ),
+                       'cat'     => array( 'registration' ),
+               ),
+               'yzu9' => array(
+                       'title'   => __( 'Sort swag', 'wordcamporg' ),
+                       'excerpt' => __( 'e.g., fold and sort t-shirts, stuff bags, generally prepare swag and badges to be distributed.', 'wordcamporg' ),
+                       'cat'     => array( 'swag' ),
+               ),
+               'rhgc' => array(
+                       'title'   => __( 'Final venue walkthrough', 'wordcamporg' ),
+                       'excerpt' => __( 'Ensure you have contact info for technical and facilities. Count the signage, observe the flow, order additional signage as needed. Locate/setup volunteer stations in venue.', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+               ),
+               'hscb' => array(
+                       'title'   => __( 'Volunteer Training', 'wordcamporg' ),
+                       'excerpt' => __( '1-day prior or early day-of - Make sure your volunteers have all the information they will need to carry out their tasks. Let them know that if someone asks a question to which they do not know the answer it\'s best to say "I don\'t know, but let\'s find out" and ask an organizer.', 'wordcamporg' ),
+                       'cat'     => array( 'volunteer' ),
+                       'link'    => array(
+                               'text' => __( 'Volunteers', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/planning-details/volunteers/',
+                       ),
+               ),
+               '20g7' => array(
+                       'title'   => __( 'Hold pre-camp event (e.g., speaker/sponsor dinner)', 'wordcamporg' ),
+                       'excerpt' => '',
+                       'cat'     => array( 'committee' ),
+               ),
+               'hkv5' => array(
+                       'title'   => __( 'A/V setup', 'wordcamporg' ),
+                       'excerpt' => __( 'Whether it\'s volunteers or venue staff, make sure the A/V setup is complete prior to the start of your event day.', 'wordcamporg' ),
+                       'cat'     => array( 'audio-video' ),
+               ),
+               'gyc3' => array(
+                       'title'   => __( 'Have a WordCamp!', 'wordcamporg' ),
+                       'excerpt' => __( 'Be present. Breathe. Enjoy the event!', 'wordcamporg' ),
+                       'cat'     => array( 'volunteer' ),
+                       'link'    => array(
+                               'text' => __( 'During WordCamp', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/during-wordcamp/',
+                       ),
+               ),
+               'jccc' => array(
+                       'title'   => __( 'Upload videos to WordPress.tv', 'wordcamporg' ),
+                       'excerpt' => __( 'You\'ll need to do a small amount of post-production and upload your WordCamp videos to WordPress.tv.', 'wordcamporg' ),
+                       'cat'     => array( 'audio-video' ),
+                       'link'    => array(
+                               'text' => __( 'Post-Production', 'wordcamporg' ),
+                               'url'  => 'https://make.wordpress.org/community/handbook/wordcamp-organizer/video/after-the-event-post-production/',
+                       ),
+               ),
+               'gnws' => array(
+                       'title'   => __( 'Ship camera kits (if using WordCamp Central kits)', 'wordcamporg' ),
+                       'excerpt' => __( 'If you used WordCamp Central\'s camera kits it\'s time to ship them on to the next event or back to their base for service. Someone for WordCamp Central will reach out to you with instructions.', 'wordcamporg' ),
+                       'cat'     => array( 'audio-video' ),
+               ),
+               'f8k2' => array(
+                       'title'   => __( 'Close out your budget ', 'wordcamporg' ),
+                       'excerpt' => __( 'Make sure all payment requests are submitted and balance your budget within two weeks of your event. If you have any questions reach out to support@wordcamp.org.', 'wordcamporg' ),
+                       'cat'     => array( 'budget' ),
+               ),
+               'r95r' => array(
+                       'title'   => __( 'Send attendee survey', 'wordcamporg' ),
+                       'excerpt' => __( 'Send your attendees, speakers, sponsors, and volunteers a post-event email recapping the event, sharing any information they may need, thanking them, and asking them to fill out the WordCamp attendee survey.', 'wordcamporg' ),
+                       'cat'     => array( 'lead' ),
+                       'link'    => array(
+                               'text' => __( 'Attendee Survey', 'wordcamporg' ),
+                               'url'  => 'https://central.wordcamp.org/wordcamp-attendee-survey/',
+                       ),
+               ),
+               'uu96' => array(
+                       'title'   => __( 'Thank you emails and request feedback', 'wordcamporg' ),
+                       'excerpt' => __( 'Thank your sponsors for their generous support and ask them if they have any feedback for future events.', 'wordcamporg' ),
+                       'cat'     => array( 'sponsor' ),
+               ),
+               'uqrx' => array(
+                       'title'   => __( 'Thank you emails and request feedback', 'wordcamporg' ),
+                       'excerpt' => __( 'Thank your speakers for their participation and ask them if they have any feedback for future events.', 'wordcamporg' ),
+                       'cat'     => array( 'speaker' ),
+               ),
+               '1hc1' => array(
+                       'title'   => __( 'Thank you emails and request feedback', 'wordcamporg' ),
+                       'excerpt' => __( 'Thank your volunteers for helping to make the event possible and ask them if they have any feedback for future events.', 'wordcamporg' ),
+                       'cat'     => array( 'volunteer' ),
+               ),
+               'fl69' => array(
+                       'title'   => __( 'Fill out your WordCamp debrief', 'wordcamporg' ),
+                       'excerpt' => __( 'We\'d love to hear how you feel the event went - what were your proudest moments and your greatest disappointments? We\'ve created a WordCamp Debrief survey so we can get all the details of how things went with your illustrious event.', 'wordcamporg' ),
+                       'cat'     => array( 'committee' ),
+                       'link'    => array(
+                               'text' => __( 'Debrief', 'wordcamporg' ),
+                               'url'  => 'http://wordcampcentral.polldaddy.com/s/wordcamp-debrief',
+                       ),
+               ),
+       );
+}
+
+/**
+ * Handle a POST request to reset the task data.
+ *
+ * @since 1.0.0
+ *
+ * @return void
+ */
+function handle_tasks_reset() {
+       // The base redirect URL.
+       $redirect_url = add_query_arg( array(
+               'page' => Mentors\PREFIX . '-planning-checklist',
+       ), admin_url( 'index.php' ) );
+
+       if ( ! isset( $_POST[ Mentors\PREFIX . '-tasks-reset-nonce' ] ) ||
+            ! wp_verify_nonce( $_POST[ Mentors\PREFIX . '-tasks-reset-nonce' ], Mentors\PREFIX . '-tasks-reset' ) ) {
+               $status_code = 'invalid-nonce';
+       } elseif ( ! current_user_can( Mentors\MENTOR_CAP ) ) {
+               $status_code = 'insufficient-permissions';
+       } else {
+               $status_code = _reset_tasks();
+       }
+
+       $redirect_url = add_query_arg( 'status', $status_code, $redirect_url );
+
+       wp_safe_redirect( esc_url_raw( $redirect_url ) );
+}
+
+add_action( 'admin_post_' . Mentors\PREFIX . '-tasks-reset', __NAMESPACE__ . '\handle_tasks_reset' );
+
+/**
+ * Reset the list of task posts and their related taxonomy terms.
+ *
+ * @access private
+ *
+ * @since 1.0.0
+ *
+ * @return string Status code
+ */
+function _reset_tasks() {
+       $results = array();
+
+       // Delete existing tasks.
+       $existing_tasks = get_posts( array(
+               'post_type'      => Mentors\PREFIX . '_task',
+               'post_status'    => array_keys( get_task_statuses() ),
+               'posts_per_page' => 999,
+       ) );
+
+       foreach ( $existing_tasks as $existing_task ) {
+               $results[] = wp_delete_post( $existing_task->ID, true );
+       }
+
+       // Delete existing categories.
+       $existing_categories = get_terms( array(
+               'taxonomy'   => Mentors\PREFIX . '_task_category',
+               'hide_empty' => false,
+       ) );
+
+       foreach ( $existing_categories as $existing_category ) {
+               $results[] = wp_delete_term( $existing_category->term_id, Mentors\PREFIX . '_task_category' );
+       }
+
+       // Create new categories.
+       $new_category_data = get_task_category_data();
+
+       foreach ( $new_category_data as $slug => $label ) {
+               $results[] = wp_insert_term( $slug, Mentors\PREFIX . '_task_category' );
+       }
+
+       // Create new tasks.
+       $new_task_data = get_task_data();
+       $order = 0;
+
+       foreach ( $new_task_data as $l10n_id => $data ) {
+               $order += 10;
+
+               $args = array(
+                       'post_type'   => Mentors\PREFIX . '_task',
+                       'post_status' => Mentors\PREFIX . '_task_pending',
+                       'post_title'  => $l10n_id,
+                       'menu_order'  => $order,
+                       'meta_input'  => array(
+                               Mentors\PREFIX . '-data-version' => Mentors\DATA_VERSION,
+                       ),
+               );
+
+               $post_id = wp_insert_post( $args );
+
+               $results[] = wp_set_object_terms( $post_id, $data['cat'], Mentors\PREFIX . '_task_category' );
+       }
+
+       if ( in_array( false, $results, true ) ||
+            ! empty( array_filter( $results, function( $i ) { return $i instanceof \WP_Error; } ) ) ) {
+               return 'reset-errors';
+       }
+
+       return 'reset-success';
+}
+
+/**
+ * Insert translated strings into REST response for tasks.
+ *
+ * The strings are translated here instead of when the task posts are inserted so that
+ * they remain translatable if mentors and/or organizers who are viewing the Planning Checklist
+ * have a different locale than the one used when the task data was set up.
+ *
+ * @since 1.0.0
+ *
+ * @param \WP_REST_Response $response The response object to be sent.
+ * @param \WP_Post          $post     The post in the response object.
+ *
+ * @return \WP_REST_Response
+ */
+function localize_task( $response, $post ) {
+       $l10n_id = $post->post_title;
+       $task_data = get_task_data();
+
+       if ( isset( $task_data[ $l10n_id ] ) ) {
+               $parsed_data = wp_parse_args( $task_data[ $l10n_id ], array(
+                       'title'   => '',
+                       'excerpt' => '',
+                       'cat'     => array(),
+                       'link'    => array(
+                               'text' => '',
+                               'url'  => '',
+                       ),
+               ) );
+
+               $response->data['title']['rendered']   = apply_filters( 'the_title', $parsed_data['title'] );
+               $response->data['excerpt']['rendered'] = wp_kses( $parsed_data['excerpt'], array() );
+               $response->data['helpLink']['text']    = wp_kses( $parsed_data['link']['text'], array() );
+               $response->data['helpLink']['url']     = esc_url( $parsed_data['link']['url'] );
+       } else {
+               $response->data['title']['rendered'] = esc_html__( 'Unknown task.', 'wordcamporg' );
+       }
+
+       $raw_modified = $response->data['modified'];
+       $response->data['modified'] = array(
+               'raw'      => $raw_modified,
+               'relative' => sprintf(
+                       /* translators: Time since an event has occurred. */
+                       esc_html__( '%s ago', 'wordcamporg' ),
+                       human_time_diff( strtotime( $raw_modified ), current_time( 'timestamp' ) )
+               ),
+       );
+
+       return $response;
+}
+
+add_filter( 'rest_prepare_' . Mentors\PREFIX . '_task', __NAMESPACE__ . '\localize_task', 10, 2 );
+
+/**
+ * Insert translated strings into REST response for task categories.
+ *
+ * The strings are translated here instead of when the task posts are inserted so that
+ * they remain translatable if mentors and/or organizers who are viewing the Planning Checklist
+ * have a different locale than the one used when the task data was set up.
+ *
+ * @since 1.0.0
+ *
+ * @param \WP_REST_Response $response The response object to be sent.
+ * @param \WP_Term          $item     The term in the response object.
+ *
+ * @return \WP_REST_Response
+ */
+function localize_task_category( $response, $item ) {
+       $task_category_data = get_task_category_data();
+
+       if ( isset( $task_category_data[ $item->slug ] ) ) {
+               $response->data['name'] = $task_category_data[ $item->slug ];
+       }
+
+       return $response;
+}
+
+add_filter( 'rest_prepare_' . Mentors\PREFIX . '_task_category', __NAMESPACE__ . '\localize_task_category', 10, 2 );
+
+/**
+ * Record the username of the user updating the task post.
+ *
+ * @since 1.0.0
+ *
+ * @param \WP_Post $post The task post currently being updated.
+ */
+function update_last_modifier( $post ) {
+       $user = wp_get_current_user();
+
+       if ( $user instanceof \WP_User ) {
+               update_post_meta( $post->ID, Mentors\PREFIX . '-last-modifier', $user->user_login );
+       }
+}
+
+add_action( 'rest_insert_' . Mentors\PREFIX . '_task', __NAMESPACE__ . '\update_last_modifier' );
</ins></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsincludestaskslistphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks-list.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-mentors/includes/tasks-list.php                          (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks-list.php    2017-04-14 22:35:45 UTC (rev 5336)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,270 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * List Table for the tasks in the Planning Checklist.
+ *
+ * @package WordCamp\Mentors
+ */
+
+namespace WordCamp\Mentors\Tasks;
+defined( 'WPINC' ) || die();
+
+use WordCamp\Mentors;
+
+if ( ! class_exists( 'WP_List_Table' ) ) {
+       require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
+}
+
+/**
+ * Class List_Table.
+ *
+ * This class is used both to render the markup for the list table on the
+ * Planning Checklist page, and to generate the JS template for rendering
+ * each individual task row.
+ *
+ * When instantiating the class for the purpose of generating the JS template,
+ * the class should be passed an args array with a `js` key set to `true`.
+ *
+ * @package WordCamp\Mentors\Tasks
+ */
+class List_Table extends \WP_List_Table {
+       /**
+        * Switch the context between page load and JS template
+        *
+        * @since 1.0.0
+        *
+        * @var bool
+        */
+       protected $js = false;
+
+       /**
+        * List_Table constructor.
+        *
+        * @since 1.0.0
+        *
+        * @param array $args Class args.
+        */
+       public function __construct( $args = array() ) {
+               $defaults = array(
+                       'js' => false,
+               );
+               $args = wp_parse_args( $args, $defaults );
+
+               $this->js = $args['js'];
+
+               parent::__construct( $args );
+       }
+
+       /**
+        * Add controls above and below the list table
+        *
+        * @since 1.0.0
+        *
+        * @param string $which Location of the extra tablenav.
+        */
+       public function extra_tablenav( $which = 'top' ) {
+               if ( 'top' === $which ) : ?>
+               <div class="<?php echo ( is_rtl() ) ? 'alignright' : 'alignleft'; ?> actions">
+                       <form id="tasks-filter">
+                               <?php $this->task_category_dropdown(); ?>
+                               <?php $this->status_dropdown(); ?>
+                               <?php submit_button( __( 'Filter', 'wordcamporg' ), 'secondary', 'submit', false ); ?>
+                       </form>
+               </div>
+               <?php elseif ( 'bottom' === $which ) : ?>
+               <div class="<?php echo ( is_rtl() ) ? 'alignleft' : 'alignright'; ?> actions">
+                       <?php if ( current_user_can( Mentors\MENTOR_CAP ) ) : ?>
+                               <form id="tasks-reset" method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
+                                       <input type="hidden" name="action" value="<?php echo esc_attr( Mentors\PREFIX ); ?>-tasks-reset" />
+                                       <?php wp_nonce_field( Mentors\PREFIX . '-tasks-reset', Mentors\PREFIX . '-tasks-reset-nonce' ); ?>
+                                       <?php submit_button( __( 'Reset Task Data', 'wordcamporg' ), 'delete', 'submit', false ); ?>
+                               </form>
+                       <?php endif; ?>
+               </div>
+               <?php endif;
+       }
+
+       /**
+        * Dropdown for task categories
+        *
+        * @since 1.0.0
+        */
+       protected function task_category_dropdown() {
+               $task_categories = get_terms( array(
+                       'taxonomy'   => Mentors\PREFIX . '_task_category',
+                       'hide_empty' => false,
+               ) );
+               $task_category_data = get_task_category_data();
+
+               $pref = get_user_setting( Mentors\PREFIX . '-' . Mentors\PREFIX . '_task_category', 'any' );
+               ?>
+               <label for="filter-by-task-category" class="screen-reader-text"><?php esc_html_e( 'Filter by task category', 'wordcamporg' ); ?></label>
+               <select id="filter-by-task-category" data-attribute="<?php echo esc_attr( Mentors\PREFIX ); ?>_task_category">
+                       <option value="any" <?php selected( 'any', $pref ); ?>>
+                               <?php esc_html_e( 'All task categories', 'wordcamporg' ); ?>
+                       </option>
+                       <?php foreach ( $task_categories as $cat ) : ?>
+                               <option value="<?php echo esc_attr( $cat->term_id ); ?>" <?php selected( $cat->term_id, $pref ); ?>>
+                                       <?php echo esc_html( $task_category_data[ $cat->slug ] ); ?>
+                               </option>
+                       <?php endforeach; ?>
+               </select>
+               <?php
+       }
+
+       /**
+        * Dropdown for task statuses
+        *
+        * @since 1.0.0
+        */
+       protected function status_dropdown() {
+               $task_statuses = get_task_statuses();
+               $pref = get_user_setting( Mentors\PREFIX . '-status', Mentors\PREFIX . '_task_pending' );
+               ?>
+               <label for="filter-by-task-status" class="screen-reader-text"><?php esc_html_e( 'Filter by status', 'wordcamporg' ); ?></label>
+               <select id="filter-by-task-status" data-attribute="status">
+                       <option value="any" <?php selected( 'any', $pref ); ?>>
+                               <?php esc_html_e( 'All statuses', 'wordcamporg' ); ?>
+                       </option>
+                       <?php foreach ( $task_statuses as $status ) : ?>
+                               <option value="<?php echo esc_attr( $status->name ); ?>" <?php selected( $status->name, $pref ); ?>>
+                                       <?php echo esc_html( $status->label ); ?>
+                               </option>
+                       <?php endforeach; ?>
+               </select>
+               <?php
+       }
+
+       /**
+        * Get a list of CSS classes for the WP_List_Table table tag.
+        *
+        * @since 1.0.0
+        *
+        * @return array List of CSS classes for the table tag.
+        */
+       protected function get_table_classes() {
+               return array( 'widefat', 'fixed', $this->_args['plural'] );
+       }
+
+       /**
+        * Prepare the table items
+        *
+        * @since 1.0.0
+        */
+       public function prepare_items() {
+               $columns = $this->get_columns();
+               $hidden = array();
+               $sortable = array();
+               $this->_column_headers = array( $columns, $hidden, $sortable );
+
+               if ( $this->js ) {
+                       // For the JS template, only one row is needed.
+                       $this->items = array(
+                               (object) array(
+                                       'ID' => 'data.id',
+                               ),
+                       );
+               }
+       }
+
+       /**
+        * Message to be displayed when there are no items
+        *
+        * @since 1.0.0
+        */
+       public function no_items() {}
+
+       /**
+        * Specify the column names and order
+        *
+        * @since 1.0.0
+        *
+        * @return array
+        */
+       public function get_columns() {
+               $columns = array();
+
+               $columns['task']          = esc_html__( 'Task', 'wordcamporg' );
+               $columns['task_category'] = get_taxonomy( Mentors\PREFIX . '_task_category' )->labels->singular_name;
+               $columns['status']        = esc_html__( 'Status', 'wordcamporg' );
+               $columns['modified']      = esc_html__( 'Modified', 'wordcamporg' );
+
+               return $columns;
+       }
+
+       /**
+        * Render the Task column.
+        *
+        * @since 1.0.0
+        */
+       public function column_task() {
+               if ( $this->js ) : ?>
+                       {{ data.title.rendered }}
+               <?php endif;
+       }
+
+       /**
+        * Render the Task Category column
+        *
+        * @since 1.0.0
+        */
+       public function column_task_category() {
+               if ( $this->js ) : ?>
+                       <ul>
+                               <# if ( data.task_category.length ) { #>
+                                       <# _.each( data.task_category, function( category ) { #>
+                                               <li class="category-{{ category.get( 'slug' ) }}">{{ category.get( 'name' ) }}</li>
+                                       <# }); #>
+                               <# } else { #>
+                                       <li class="category-none"><?php esc_html_e( 'No category' , 'wordcamporg' ) ?></li>
+                               <# } #>
+                       </ul>
+               <?php endif;
+       }
+
+       /**
+        * Render the Status column
+        *
+        * @since 1.0.0
+        */
+       public function column_status() {
+               if ( $this->js ) : ?>
+                       <select>
+                               <# if ( 'object' !== typeof data.stati[ data.status ] ) { #>
+                                       <option value="{{ data.status }}" selected disabled>
+                                               {{ data.status }}
+                                       </option>
+                               <# } #>
+                               <# _.each( data.stati, function( status, slug ) {
+                                       var selected = ( slug === data.status ) ? 'selected' : '';
+                                       #>
+                                       <option value="{{ slug }}" {{ selected }}>
+                                               {{ status.label }}
+                                       </option>
+                               <# }); #>
+                       </select>
+               <?php endif;
+       }
+
+       /**
+        * Render the Modified column
+        *
+        * @since 1.0.0
+        */
+       public function column_modified() {
+               if ( $this->js ) : ?>
+                       <# if ( data.modified.raw !== data.date ) { #>
+                               {{ data.modified.relative }}
+                               <# if ( data.lastModifier ) { #>
+                                       <br />
+                                       <?php
+                                       printf(
+                                               /* translators: Attribution to a user, e.g. by wordcampadmin */
+                                               wp_kses( __( 'by <strong>%s</strong>', 'wordcamporg' ), array( 'strong' => true ) ),
+                                               '{{ data.lastModifier }}'
+                                       );
+                                       ?>
+                               <# } #>
+                       <# } #>
+               <?php endif;
+       }
+}
</ins></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsincludestasksphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks.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-mentors/includes/tasks.php                               (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/includes/tasks.php 2017-04-14 22:35:45 UTC (rev 5336)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,437 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Set up functionality for the Planning Checklist tool.
+ *
+ * @package WordCamp\Mentors
+ */
+
+namespace WordCamp\Mentors\Tasks;
+defined( 'WPINC' ) || die();
+
+use WordCamp\Mentors;
+
+/**
+ * Initialize the Tasks functionality
+ */
+function init() {
+       register_cpt();
+       register_tax();
+       register_status();
+
+       // Admin notices.
+       if ( isset( $_GET['page'] ) && Mentors\PREFIX . '-planning-checklist' === $_GET['page'] ) {
+               add_action( 'admin_notices', __NAMESPACE__ . '\admin_notices' );
+       }
+}
+
+add_action( 'init', __NAMESPACE__ . '\init', 0 );
+
+/**
+ * Register custom post types.
+ *
+ * @since 1.0.0
+ */
+function register_cpt() {
+       $labels = array(
+               'name'                  => _x( 'Tasks', 'Post Type General Name', 'wordcamporg' ),
+               'singular_name'         => _x( 'Task', 'Post Type Singular Name', 'wordcamporg' ),
+               'menu_name'             => __( 'Tasks', 'wordcamporg' ),
+               'name_admin_bar'        => __( 'Tasks', 'wordcamporg' ),
+               'archives'              => __( 'Task Archives', 'wordcamporg' ),
+               'attributes'            => __( 'Task Attributes', 'wordcamporg' ),
+               'parent_item_colon'     => __( 'Parent Task:', 'wordcamporg' ),
+               'all_items'             => __( 'All Tasks', 'wordcamporg' ),
+               'add_new_item'          => __( 'Add New Task', 'wordcamporg' ),
+               'add_new'               => __( 'Add New', 'wordcamporg' ),
+               'new_item'              => __( 'New Task', 'wordcamporg' ),
+               'edit_item'             => __( 'Edit Task', 'wordcamporg' ),
+               'update_item'           => __( 'Update Task', 'wordcamporg' ),
+               'view_item'             => __( 'View Task', 'wordcamporg' ),
+               'view_items'            => __( 'View Tasks', 'wordcamporg' ),
+               'search_items'          => __( 'Search Task', 'wordcamporg' ),
+               'not_found'             => __( 'Not found', 'wordcamporg' ),
+               'not_found_in_trash'    => __( 'Not found in Trash', 'wordcamporg' ),
+               'featured_image'        => __( 'Featured Image', 'wordcamporg' ),
+               'set_featured_image'    => __( 'Set featured image', 'wordcamporg' ),
+               'remove_featured_image' => __( 'Remove featured image', 'wordcamporg' ),
+               'use_featured_image'    => __( 'Use as featured image', 'wordcamporg' ),
+               'insert_into_item'      => __( 'Insert into task', 'wordcamporg' ),
+               'uploaded_to_this_item' => __( 'Uploaded to this task', 'wordcamporg' ),
+               'items_list'            => __( 'Tasks list', 'wordcamporg' ),
+               'items_list_navigation' => __( 'Tasks list navigation', 'wordcamporg' ),
+               'filter_items_list'     => __( 'Filter tasks list', 'wordcamporg' ),
+       );
+
+       $args = array(
+               'label'                 => __( 'Task', 'wordcamporg' ),
+               'description'           => __( 'Planning Checklist tasks', 'wordcamporg' ),
+               'labels'                => $labels,
+               'supports'              => array( 'title', 'excerpt', 'page-attributes', 'custom-fields' ),
+               'taxonomies'            => array( Mentors\PREFIX . '_task_category' ),
+               'hierarchical'          => false,
+               'public'                => false,
+               'show_ui'               => false,
+               'show_in_menu'          => false,
+               'menu_position'         => 5,
+               'show_in_admin_bar'     => false,
+               'show_in_nav_menus'     => false,
+               'can_export'            => true,
+               'has_archive'           => false,
+               'exclude_from_search'   => true,
+               'publicly_queryable'    => false,
+               'rewrite'               => false,
+               'capability_type'       => 'task',
+               'show_in_rest'          => true,
+               'rest_controller_class' => __NAMESPACE__ . '\Controller',
+       );
+
+       register_post_type( Mentors\PREFIX . '_task', $args );
+
+       add_filter( 'map_meta_cap', __NAMESPACE__ . '\map_task_caps', 10, 2 );
+}
+
+/**
+ * Map CPT capabilities.
+ *
+ * @since 1.0.0
+ *
+ * @param array  $caps The user's actual capabilities.
+ * @param string $cap  Capability name.
+ *
+ * @return array
+ */
+function map_task_caps( $caps, $cap ) {
+       switch ( $cap ) {
+               case 'edit_task' :
+               case 'edit_tasks' :
+               case 'edit_others_tasks' :
+               case 'read_task' :
+                       $caps[] = Mentors\ORGANIZER_CAP;
+                       break;
+
+               case 'read_private_tasks' :
+               case 'publish_tasks' :
+               case 'delete_task' :
+                       $caps[] = Mentors\MENTOR_CAP;
+                       break;
+       }
+
+       return $caps;
+}
+
+/**
+ * Register custom taxonomies.
+ *
+ * @since 1.0.0
+ */
+function register_tax() {
+       $labels = array(
+               'name'                       => _x( 'Task Categories', 'Taxonomy General Name', 'wordcamporg' ),
+               'singular_name'              => _x( 'Task Category', 'Taxonomy Singular Name', 'wordcamporg' ),
+               'menu_name'                  => __( 'Category', 'wordcamporg' ),
+               'all_items'                  => __( 'All Categories', 'wordcamporg' ),
+               'parent_item'                => __( 'Parent Category', 'wordcamporg' ),
+               'parent_item_colon'          => __( 'Parent Category:', 'wordcamporg' ),
+               'new_item_name'              => __( 'New Category Name', 'wordcamporg' ),
+               'add_new_item'               => __( 'Add New Category', 'wordcamporg' ),
+               'edit_item'                  => __( 'Edit Category', 'wordcamporg' ),
+               'update_item'                => __( 'Update Category', 'wordcamporg' ),
+               'view_item'                  => __( 'View Category', 'wordcamporg' ),
+               'separate_items_with_commas' => __( 'Separate categories with commas', 'wordcamporg' ),
+               'add_or_remove_items'        => __( 'Add or remove categories', 'wordcamporg' ),
+               'choose_from_most_used'      => __( 'Choose from the most used', 'wordcamporg' ),
+               'popular_items'              => __( 'Popular Categories', 'wordcamporg' ),
+               'search_items'               => __( 'Search Categories', 'wordcamporg' ),
+               'not_found'                  => __( 'Not Found', 'wordcamporg' ),
+               'no_terms'                   => __( 'No categories', 'wordcamporg' ),
+               'items_list'                 => __( 'Categories list', 'wordcamporg' ),
+               'items_list_navigation'      => __( 'Categories list navigation', 'wordcamporg' ),
+       );
+
+       $args = array(
+               'labels'                     => $labels,
+               'hierarchical'               => true,
+               'public'                     => false,
+               'show_ui'                    => false,
+               'show_admin_column'          => true,
+               'show_in_nav_menus'          => false,
+               'show_tagcloud'              => false,
+               'rewrite'                    => false,
+               'show_in_rest'               => true,
+       );
+
+       register_taxonomy( Mentors\PREFIX . '_task_category', array( Mentors\PREFIX . '_task' ), $args );
+}
+
+/**
+ * Register custom post statuses.
+ *
+ * @since 1.0.0
+ */
+function register_status() {
+       $stati = array(
+               Mentors\PREFIX . '_task_pending'  => esc_html__( 'Pending',  'wordcamporg' ),
+               Mentors\PREFIX . '_task_complete' => esc_html__( 'Complete', 'wordcamporg' ),
+               Mentors\PREFIX . '_task_skipped'  => esc_html__( 'Skipped',  'wordcamporg' ),
+       );
+
+       foreach ( $stati as $id => $label ) {
+               register_post_status(
+                       $id,
+                       array(
+                               'label'       => $label,
+                               'public'                    => false,
+                               // Custom parameter to flag its use with the Task CPT.
+                               Mentors\PREFIX . '_task' => true,
+                       )
+               );
+       }
+}
+
+/**
+ * Register fields to include in REST responses.
+ *
+ * @since 1.0.0
+ *
+ * @return void
+ */
+function register_rest_fields() {
+       register_rest_field(
+               Mentors\PREFIX . '_task',
+               'lastModifier',
+               array(
+                       'get_callback' => function( $object ) {
+                               $object = (object) $object;
+
+                               return get_post_meta( $object->id, Mentors\PREFIX . '-last-modifier', true );
+                       },
+                       'schema'       => array(
+                               'description' => __( 'Username of the last user to modify the task post.', 'wordcamporg' ),
+                               'type'        => 'string',
+                               'context'     => array( 'view', 'edit' ),
+                               'arg_options' => array(
+                                       'sanitize_callback' => 'sanitize_user',
+                               ),
+                       ),
+               )
+       );
+
+       register_rest_field(
+               Mentors\PREFIX . '_task',
+               'helpLink',
+               array(
+                       'schema'       => array(
+                               'description' => __( 'A help link for more information about a task.', 'wordcamporg' ),
+                               'type'        => 'object',
+                               'context'     => array( 'view' ),
+                               'properties'  => array(
+                                       'text' => array(
+                                               'description' => __( 'The help link text.', 'wordcamporg' ),
+                                               'type'        => 'string',
+                                               'context'     => array( 'view' ),
+                                       ),
+                                       'url'  => array(
+                                               'description' => __( 'The help link URL.', 'wordcamporg' ),
+                                               'type'        => 'string',
+                                               'context'     => array( 'view' ),
+                                       ),
+                               ),
+                       ),
+               )
+       );
+}
+
+add_action( 'rest_api_init', __NAMESPACE__ . '\register_rest_fields' );
+
+/**
+ * Get the array of Task-specific status objects.
+ *
+ * @since 1.0.0
+ *
+ * @return array
+ */
+function get_task_statuses() {
+       return get_post_stati(
+               array(
+                       Mentors\PREFIX . '_task' => true,
+               ),
+               false
+       );
+}
+
+/**
+ * Add a page to the Dashboard menu.
+ *
+ * @since 1.0.0
+ *
+ * @return void
+ */
+function add_tasks_page() {
+       \add_submenu_page(
+               'index.php',
+               __( 'Planning Checklist', 'wordcamporg' ),
+               __( 'Planning', 'wordcamporg' ),
+               Mentors\ORGANIZER_CAP,
+               Mentors\PREFIX . '-planning-checklist',
+               __NAMESPACE__ . '\render_tasks_page'
+       );
+}
+
+add_action( 'admin_menu', __NAMESPACE__ . '\add_tasks_page' );
+
+/**
+ * Render callback for the page.
+ *
+ * @since 1.0.0
+ *
+ * return void
+ */
+function render_tasks_page() {
+       $list_table = new List_Table();
+       $list_table->prepare_items();
+
+       include Mentors\get_views_dir_path() . 'tasks.php';
+}
+
+/**
+ * Enqueue JavaScript and CSS assets for the Tasks Dashboard page.
+ *
+ * @since 1.0.0
+ *
+ * @param string $hook_suffix The current admin page.
+ *
+ * @return void
+ */
+function enqueue_page_assets( $hook_suffix ) {
+       if ( 'dashboard_page_' . Mentors\PREFIX . '-planning-checklist' !== $hook_suffix ) {
+               return;
+       }
+
+       wp_enqueue_style(
+               Mentors\PREFIX . '-planning-checklist',
+               Mentors\get_css_url() . 'tasks/dashboard.css',
+               array(),
+               Mentors\CSS_VERSION
+       );
+
+       $script_dependencies = array(
+               'wp-api',
+               'wp-util',
+               'utils',
+       );
+
+       wp_enqueue_script(
+               Mentors\PREFIX . '-planning-checklist',
+               Mentors\get_js_url() . 'tasks/dashboard.js',
+               $script_dependencies,
+               Mentors\JS_VERSION,
+               true
+       );
+
+       wp_localize_script(
+               Mentors\PREFIX . '-planning-checklist',
+               'WordCampMentors',
+               array(
+                       'prefix'  => Mentors\PREFIX,
+                       'l10n'    => array(
+                               'confirmReset' => esc_html__( 'Are you sure you want to reset the task data? This action cannot be undone.', 'wordcamporg' ),
+                       ),
+                       'stati'   => get_task_statuses(),
+               )
+       );
+}
+
+add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\enqueue_page_assets', 20 );
+
+/**
+ * Render JS templates.
+ *
+ * @since 1.0.0
+ *
+ * @return void
+ */
+function print_templates() {
+       $js_list_table = new List_Table( array(
+               'js' => true,
+       ) );
+       $js_list_table->prepare_items();
+
+       $columns = $js_list_table->get_column_count();
+       ?>
+       <script id="tmpl-<?php echo esc_attr( Mentors\PREFIX ); ?>-task" type="text/template">
+               <?php $js_list_table->single_row_columns( $js_list_table->items[0] ); ?>
+       </script>
+       <script id="tmpl-<?php echo esc_attr( Mentors\PREFIX ); ?>-more" type="text/template">
+               <?php include Mentors\get_views_dir_path() . 'task-more.php'; ?>
+       </script>
+       <?php
+       // Initial data.
+       $request_url = add_query_arg( array(
+               'per_page' => 300,
+               'orderby'  => 'menu_order',
+               'order'    => 'asc',
+       ), get_rest_url( null, 'wp/v2/' . Mentors\PREFIX . '_task' ) );
+
+       $initial_tasks = rest_do_request( \WP_REST_Request::from_url( $request_url ) );
+
+       $request_url = add_query_arg( array(
+               'per_page' => 100,
+       ), get_rest_url( null, 'wp/v2/' . Mentors\PREFIX . '_task_category' ) );
+
+       $initial_task_categories = rest_do_request( \WP_REST_Request::from_url( $request_url ) );
+       ?>
+       <script type="text/javascript">
+               /* <![CDATA[ */
+               var WordCampMentorsTaskData = <?php echo wp_json_encode( $initial_tasks->data ); ?>;
+               var WordCampMentorsTaskCategoryData = <?php echo wp_json_encode( $initial_task_categories->data ); ?>;
+               /* ]]> */
+       </script>
+       <?php
+}
+
+add_action( 'admin_print_footer_scripts-dashboard_page_' . Mentors\PREFIX . '-planning-checklist', __NAMESPACE__ . '\print_templates' );
+
+/**
+ * Display admin notices at the top of the Planning Checklist page.
+ *
+ * @since 1.0.0
+ *
+ * @return void
+ */
+function admin_notices() {
+       global $pagenow;
+
+       if ( 'index.php' !== $pagenow ||
+            ! isset( $_GET['page'], $_GET['status'] ) ||
+            Mentors\PREFIX . '-planning-checklist' !== $_GET['page'] ) {
+               return;
+       }
+
+       $type = 'error';
+       $message = '';
+
+       switch ( $_GET['status'] ) {
+               case 'invalid-nonce' :
+                       $message = __( 'Invalid nonce.', 'wordcamporg' );
+                       break;
+
+               case 'insufficient-permissions' :
+                       $message = __( 'Insufficient permissions to reset task data.', 'wordcamporg' );
+                       break;
+
+               case 'reset-errors' :
+                       $message = __( 'Checklist data reset with errors.', 'wordcamporg' );
+                       break;
+
+               case 'reset-success' :
+                       $type = 'success';
+                       $message = __( 'Checklist data successfully reset.', 'wordcamporg' );
+                       break;
+       }
+
+       if ( $message ) : ?>
+       <div class="notice notice-<?php echo esc_attr( $type ); ?> is-dismissible">
+               <?php echo wpautop( esc_html( $message ) ); ?>
+       </div>
+       <?php endif;
+}
</ins></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsjstasksdashboardjs"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/js/tasks/dashboard.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-mentors/js/tasks/dashboard.js                            (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/js/tasks/dashboard.js      2017-04-14 22:35:45 UTC (rev 5336)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,633 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/*global jQuery, Backbone, _, wp, wordcamp, WordCampMentors, WordCampMentorsTaskData, WordCampMentorsTaskCategoryData, setUserSetting */
+
+( function( window, $ ) {
+
+       'use strict';
+
+       window.wordcamp = window.wordcamp || {};
+
+       wordcamp.mentors = $.extend( {
+               views: {}
+       }, WordCampMentors );
+
+       var $document = $( document ),
+               prefix = wordcamp.mentors.prefix;
+
+       /**
+        * A Backbone view for the list of tasks.
+        */
+       wordcamp.mentors.views.List = Backbone.View.extend( {
+               /**
+                * Time increment in ms for polling the server.
+                */
+               tick: 5000,
+
+               /**
+                * Unix timestamp of the last activity within the app.
+                */
+               lastActive: 0,
+
+               /**
+                *
+                */
+               hibernating: false,
+
+               /**
+                * Data to submit when fetching a task collection.
+                */
+               taskRequest: {
+                       data: {
+                               per_page: 300,
+                               orderby: 'menu_order',
+                               order: 'asc'
+                       },
+                       remove: false
+               },
+
+               /**
+                * Initialize the List view.
+                *
+                * @returns {wordcamp.mentors.views.List}
+                */
+               initialize: function() {
+                       var view = this;
+
+                       this.setLastActive();
+
+                       this.tasks      = new wp.api.collections.Wcm_task( WordCampMentorsTaskData );
+                       this.categories = new wp.api.collections.Wcm_task_category( WordCampMentorsTaskCategoryData );
+                       this.filter     = new wordcamp.mentors.views.Filter( { el: '#tasks-filter', list: this } );
+                       this.reset      = new wordcamp.mentors.views.Reset( { el: '#tasks-reset' } );
+
+                       this.listeners();
+
+                       if ( this.tasks.length ) {
+                               view.render();
+
+                               view.ticker = setInterval( function() {
+                                       view.trigger( 'tick:' + view.tick );
+                               }, view.tick );
+                       }
+
+                       return this;
+               },
+
+               /**
+                * Render the List view.
+                *
+                * @returns {wordcamp.mentors.views.List}
+                */
+               render: function() {
+                       var view = this;
+
+                       this.$el.empty();
+
+                       this.tasks.each( function( model ) {
+                               var categories, task;
+
+                               categories = _.filter( view.categories.models, function( category ) {
+                                       return _.contains( model.get( prefix + '_task_category' ), category.get( 'id' ) );
+                               });
+
+                               task = new wordcamp.mentors.views.Task( {
+                                       model: model,
+                                       list: view,
+                                       categories: categories
+                               });
+
+                               view.$el.append( task.$el );
+                       });
+
+                       this.trigger( 'setFilter', { skipHighlight: true } );
+
+                       return this;
+               },
+
+               /**
+                * Set up event listeners.
+                *
+                * @returns {wordcamp.mentors.views.List}
+                */
+               listeners: function() {
+                       this.listenTo( this, 'tick:' + this.tick,   this.pollCollectionOrHibernate );
+                       this.listenTo( this, 'hibernate',           this.hibernate );
+                       this.listenTo( this.filter, 'filter:tasks', this.updateVisibleTasks );
+
+                       return this;
+               },
+
+               /**
+                * Poll the task collection for changes, or stop polling if the user is inactive.
+                *
+                * @returns {wordcamp.mentors.views.List}
+                */
+               pollCollectionOrHibernate: function() {
+                       var elapsed = Date.now() - this.lastActive;
+
+                       if ( elapsed < 30000 ) {
+                               this.tasks.fetch( this.taskRequest );
+                       } else if ( ! this.hibernating ) {
+                               this.trigger( 'hibernate' );
+                       }
+
+                       return this;
+               },
+
+               /**
+                * Update the visibility of tasks in the list based on filter parameters.
+                *
+                * @param {object} filter Required parameters to determine which tasks should be visible.
+                * @param {object} data   Optional parameters to pass to the event trigger.
+                *
+                * @returns {wordcamp.mentors.views.List}
+                */
+               updateVisibleTasks: function( filter, data ) {
+                       this.tasks.each( function( task ) {
+                               var tests = true;
+
+                               if ( ! _.isEmpty( filter ) ) {
+                                       tests = _.map( filter, function( value, key ) {
+                                               var attribute = task.get( key );
+
+                                               if ( _.isArray( attribute ) ) {
+                                                       return _.contains( attribute, parseInt( value, 10 ) );
+                                               }
+
+                                               return attribute === value;
+                                       });
+                               }
+
+                               if ( _.every( tests ) ) {
+                                       task.trigger( 'visibility', 'show', data );
+                               } else {
+                                       task.trigger( 'visibility', 'hide', data );
+                               }
+                       });
+
+                       return this;
+               },
+
+               /**
+                * Set or update the application as active so that it will poll for changes.
+                *
+                * @returns {wordcamp.mentors.views.List}
+                */
+               setLastActive: function() {
+                       this.lastActive  = Date.now();
+                       this.hibernating = false;
+
+                       // Stop listening for activity
+                       $document.off( '.' + prefix + '-tasks' );
+
+                       return this;
+               },
+
+               /**
+                * Set the application as inactive so that it won't poll for changes.
+                *
+                * @returns {wordcamp.mentors.views.List}
+                */
+               hibernate: function() {
+                       var view = this;
+
+                       this.hibernating = true;
+
+                       $document.on(
+                               'mouseover.' + prefix + '-tasks keyup.' + prefix + '-tasks touchend.' + prefix + '-tasks',
+                               function() {
+                                       view.setLastActive();
+                               }
+                       );
+
+                       return this;
+               }
+       });
+
+       /**
+        * A Backbone view for an individual task.
+        */
+       wordcamp.mentors.views.Task = Backbone.View.extend( {
+               /**
+                * HTML element to use as a container.
+                */
+               tagName: 'tr',
+
+               /**
+                * HTML element ID attribute.
+                *
+                * @returns {string}
+                */
+               id: function() {
+                       return prefix + '-task-' + this.model.get( 'id' );
+               },
+
+               /**
+                * HTML element class attribute.
+                */
+               className: prefix + '-task',
+
+               /**
+                * The templating function for rendering the task.
+                */
+               template: wp.template( prefix + '-task' ),
+
+               /**
+                * Combine model attributes with other data necessary for rendering.
+                *
+                * @private
+                *
+                * @param {wp.api.models.Wcm_task} model
+                *
+                * @returns {object}
+                */
+               _compileData: function( model ) {
+                       return $.extend( {}, model.attributes, {
+                               task_category: this.categories,
+                               stati: wordcamp.mentors.stati
+                       } );
+               },
+
+               /**
+                * Initialize a task view.
+                *
+                * @param {object} options
+                *
+                * @returns {wordcamp.mentors.views.Task}
+                */
+               initialize: function( options ) {
+                       this.list       = options.list;
+                       this.categories = options.categories;
+                       this.more       = new wordcamp.mentors.views.TaskMore( { task: this } );
+                       this.expanded   = false;
+
+                       this.listeners();
+
+                       this.render( this._compileData( this.model ) );
+
+                       return this;
+               },
+
+               /**
+                * Render a task.
+                *
+                * @param {object} data
+                *
+                * @returns {wordcamp.mentors.views.Task}
+                */
+               render: function( data ) {
+                       this.$el.html( this.template( data ) );
+
+                       return this;
+               },
+
+               /**
+                * Set up event listeners.
+                *
+                * @returns {wordcamp.mentors.views.Task}
+                */
+               listeners: function() {
+                       this.listenTo( this.model, 'visibility',      this.changeVisibility );
+                       this.listenTo( this.model, 'change:status',   this.changeStatus );
+                       this.listenTo( this.model, 'change:modified', this.changeModified );
+                       this.listenTo( this.list,  'collapseAll',     this.maybeCollapse );
+
+                       return this;
+               },
+
+               /**
+                * Change the visibility of this view's element.
+                *
+                * @param {string} action
+                * @param {object} options
+                *
+                * @returns {wordcamp.mentors.views.Task}
+                */
+               changeVisibility: function( action, options ) {
+                       if ( this.expanded ) {
+                               return this;
+                       }
+
+                       options = _.defaults( options || {}, {
+                               skipHighlight: false
+                       } );
+
+                       var duration = ( options.skipHighlight ) ? 0 : 500;
+
+                       if ( false === options.skipHighlight ) {
+                               this.$el.addClass( prefix + '-highlight' );
+                       }
+
+                       switch ( action ) {
+                               case 'show' :
+                                       this.$el.fadeIn( duration );
+                                       break;
+
+                               case 'hide' :
+                                       this.$el.fadeOut( duration );
+                                       break;
+                       }
+
+                       this.$el.promise().done( function() {
+                               $( this ).removeClass( prefix + '-highlight' );
+                       });
+
+                       return this;
+               },
+
+               /**
+                * Re-render this task when the status changes.
+                *
+                * @param model
+                */
+               changeStatus: function( model ) {
+                       var list = this.list;
+
+                       this.$el.addClass( prefix + '-highlight' );
+
+                       this.render( this._compileData( model ) );
+
+                       // Slight delay before re-filtering the list
+                       setTimeout( function() {
+                               list.trigger( 'setFilter' );
+                       }, 1000 );
+               },
+
+               /**
+                * Re-render this task when the modified timestamp changes.
+                *
+                * @param {wp.api.models.Wcm_task} model
+                */
+               changeModified: function( model ) {
+                       this.render( this._compileData( model ) );
+               },
+
+               /**
+                * Toggle this task's more row to hidden if it is currently expanded and if
+                * this isn't the task triggering the collapseAll event.
+                *
+                * @param {int} instigator The ID
+                */
+               maybeCollapse: function( instigator ) {
+                       if ( this.expanded && instigator !== this.model.get( 'id' ) ) {
+                               this.toggleMore();
+                       }
+               },
+
+               /**
+                * Event binding.
+                */
+               events: {
+                       'click': 'toggleMore',
+                       'click .column-status select': 'stopPropagation',
+                       'change .column-status select': 'updateStatus'
+               },
+
+               /**
+                * Toggle the visibility of the "more" row for this task. If this expands the task, collapse
+                * all other expanded tasks.
+                */
+               toggleMore: function() {
+                       this.expanded = ! this.expanded;
+
+                       if ( this.expanded ) {
+                               this.list.trigger( 'collapseAll', this.model.get( 'id' ) );
+                       }
+
+                       this.more.trigger( 'toggle', this._compileData( this.model ) );
+               },
+
+               /**
+                * Stop the event on a target from propagating up to the target's parent nodes.
+                *
+                * Used to prevent clicks on interactive controls on a task from triggering the "more" row to toggle.
+                *
+                * @param {event} event
+                */
+               stopPropagation: function( event ) {
+                       event.stopPropagation();
+               },
+
+               /**
+                * Save a new status value.
+                *
+                * @param {event} event
+                *
+                * @returns {wordcamp.mentors.views.Task}
+                */
+               updateStatus: function( event ) {
+                       var value = $( event.target ).val();
+
+                       this.updateTask( {
+                               status: value
+                       });
+
+                       return this;
+               },
+
+               /**
+                * Save new task attribute values to the server.
+                *
+                * @param {object} attributes
+                *
+                * @returns {wordcamp.mentors.views.Task}
+                */
+               updateTask: function( attributes ) {
+                       this.model.save( attributes, {
+                               patch: true,
+                               wait:  true
+                       });
+
+                       return this;
+               }
+       });
+
+       /**
+        * A Backbone view for an individual task's additional info.
+        */
+       wordcamp.mentors.views.TaskMore = Backbone.View.extend( {
+               /**
+                * HTML element to use as a container.
+                */
+               tagName: 'tr',
+
+               /**
+                * HTML element class attribute.
+                */
+               className: prefix + '-more',
+
+               /**
+                * The templating function for rendering the task.
+                */
+               template: wp.template( prefix + '-more' ),
+
+               /**
+                * Initialize a task's "more" view.
+                *
+                * @param {object} options
+                *
+                * @returns {wordcamp.mentors.views.TaskMore}
+                */
+               initialize: function( options ) {
+                       this.task = options.task;
+                       this.visible = false;
+
+                       this.listeners();
+
+                       this.$el.hide();
+
+                       return this;
+               },
+
+               /**
+                * Render a task's "more" view.
+                *
+                * @param {object} data
+                *
+                * @returns {wordcamp.mentors.views.TaskMore}
+                */
+               render: function( data ) {
+                       this.$el.html( this.template( data ) );
+
+                       return this;
+               },
+
+               /**
+                * Set up event listeners.
+                *
+                * @returns {wordcamp.mentors.views.TaskMore}
+                */
+               listeners: function() {
+                       this.listenTo( this, 'toggle', this.toggle );
+
+                       return this;
+               },
+
+               /**
+                * Toggle the visibility of the view element.
+                *
+                * @param {object} data
+                *
+                * @returns {wordcamp.mentors.views.TaskMore}
+                */
+               toggle: function( data ) {
+                       var $more;
+
+                       if ( ! $( this.task.$el ).next( '.hidden' ).length ) {
+                               $more = $( '<tr class="hidden">' ).add( this.$el );
+                               this.task.$el.after( $more );
+                       }
+
+                       if ( this.visible ) {
+                               this.$el.hide();
+                               this.task.$el.removeClass( prefix + '-expanded' );
+                               this.$el.removeClass( prefix + '-expanded' );
+                               this.task.list.trigger( 'setFilter' );
+                       } else {
+                               this.task.$el.addClass( prefix + '-expanded' );
+                               this.$el.addClass( prefix + '-expanded' );
+                               this.render( data );
+                               this.$el.fadeIn( 300 );
+                       }
+
+                       this.visible = ! this.visible;
+
+                       return this;
+               }
+       });
+
+       /**
+        * A Backbone view for the controls that filter visible tasks.
+        */
+       wordcamp.mentors.views.Filter = Backbone.View.extend( {
+               /**
+                * Initialize the filter view.
+                *
+                * @param {object} options
+                *
+                * @returns {wordcamp.mentors.views.Filter}
+                */
+               initialize: function( options ) {
+                       this.list = options.list;
+
+                       this.listeners();
+
+                       return this;
+               },
+
+               /**
+                * Set up event listeners.
+                *
+                * @returns {wordcamp.mentors.views.Filter}
+                */
+               listeners: function() {
+                       this.listenTo( this.list, 'setFilter', function( data ) {
+                               this.$el.trigger( 'submit', data );
+                       } );
+
+                       return this;
+               },
+
+               /**
+                * Event binding.
+                */
+               events: {
+                       'submit': 'setFilter'
+               },
+
+               /**
+                * Gather the parameters set for the list filter and pass them via event trigger.
+                *
+                * @param {event}  event
+                * @param {object} data
+                *
+                * @returns {wordcamp.mentors.views.Filter}
+                */
+               setFilter: function( event, data ) {
+                       event.preventDefault();
+
+                       var filter = {},
+                               settingPrefix = wordcamp.mentors.prefix;
+
+                       $( event.target ).find( 'select' ).each( function() {
+                               var attribute = $( this ).data( 'attribute' ),
+                                       value     = $( this ).val();
+
+                               // Save the filter value as a user setting.
+                               setUserSetting(
+                                       settingPrefix + '-' + attribute,
+                                       value
+                               );
+
+                               // Don't include attributes set to "any".
+                               if ( 'any' !== value ) {
+                                       filter[ attribute ] = value;
+                               }
+                       });
+
+                       this.trigger( 'filter:tasks', filter, data );
+
+                       return this;
+               }
+       });
+
+       /**
+        * A Backbone view for the button to reset task data.
+        */
+       wordcamp.mentors.views.Reset = Backbone.View.extend( {
+               events: {
+                       'submit': 'confirm'
+               },
+
+               confirm: function( event ) {
+                       if ( ! window.confirm( wordcamp.mentors.l10n.confirmReset ) ) {
+                               event.preventDefault();
+                       }
+               }
+       });
+
+       // Ensure the Backbone client is loaded before getting started.
+       wp.api.loadPromise.done( function () {
+               wordcamp.mentors.list = new wordcamp.mentors.views.List( { el: '#the-list' } );
+       });
+
+} )( window, jQuery );
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsviewstaskmorephp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/views/task-more.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-mentors/views/task-more.php                              (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/views/task-more.php        2017-04-14 22:35:45 UTC (rev 5336)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,28 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Template for the "more" row that displays additional information about a task.
+ *
+ * @package WordCamp\Mentors
+ */
+
+namespace WordCamp\Mentors\Tasks\Dashboard;
+defined( 'WPINC' ) || die();
+
+use WordCamp\Mentors;
+
+/* @var int $columns */
+
+?>
+<# if ( data.excerpt.rendered || data.helpLink.text ) { #>
+       <td class="task column-task">
+               {{ data.excerpt.rendered }}
+               <# if ( data.helpLink.text && data.helpLink.url ) { #>
+                       <br /><br />
+                       <a href="{{ data.helpLink.url }}" target="_blank" class="<?php echo esc_attr( Mentors\PREFIX ); ?>-help-link">
+                               {{ data.helpLink.text }}
+                               <span class="dashicons dashicons-external" aria-hidden="true"></span>
+                       </a>
+               <# } #>
+       </td>
+       <td class="" colspan="<?php echo esc_attr( $columns - 1 ); ?>"></td>
+<# } #>
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorsviewstasksphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/views/tasks.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-mentors/views/tasks.php                          (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/views/tasks.php    2017-04-14 22:35:45 UTC (rev 5336)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,20 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Template for the Planning Checklist page.
+ *
+ * @package WordCamp\Mentors
+ */
+
+namespace WordCamp\Mentors\Tasks\Dashboard;
+defined( 'WPINC' ) || die();
+
+use WordCamp\Mentors\Tasks;
+
+/* @var Tasks\List_Table $list_table */
+
+?>
+<div class="wrap">
+       <h1><?php esc_html_e( 'Planning Checklist', 'wordcamporg' ); ?></h1>
+
+       <?php $list_table->display(); ?>
+</div>
</ins></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampmentorswordcampmentorsphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/wordcamp-mentors.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-mentors/wordcamp-mentors.php                             (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-mentors/wordcamp-mentors.php       2017-04-14 22:35:45 UTC (rev 5336)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,96 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Plugin Name:     WordCamp Mentors
+ * Plugin URI:      https://wordcamp.org
+ * Description:     Tools for mentors and organizers of WordCamps.
+ * Author:          WordCamp.org
+ * Author URI:      https://wordcamp.org
+ * Version:         1.0.0
+ *
+ * @package         WordCamp\Mentors
+ */
+
+namespace WordCamp\Mentors;
+defined( 'WPINC' ) || die();
+
+const JS_VERSION     = '20170323';
+const CSS_VERSION    = '20170323';
+const DATA_VERSION   = '20170323';
+
+define( __NAMESPACE__ . '\PLUGIN_DIR', \plugin_dir_path( __FILE__ ) );
+define( __NAMESPACE__ . '\PLUGIN_URL', \plugins_url( '/', __FILE__ ) );
+
+const PREFIX = 'wcm';
+
+const MENTOR_CAP    = 'switch_themes'; // Not all mentors are super admins, so the capability is currently the same as for organizers.
+const ORGANIZER_CAP = 'switch_themes';
+
+/**
+ * Get the path for the includes directory.
+ *
+ * @since 1.0.0
+ *
+ * @return string Path with trailing slash
+ */
+function get_includes_dir_path() {
+       return trailingslashit( PLUGIN_DIR ) . 'includes/';
+}
+
+/**
+ * Get the path for the views directory.
+ *
+ * @since 1.0.0
+ *
+ * @return string Path with trailing slash
+ */
+function get_views_dir_path() {
+       return trailingslashit( PLUGIN_DIR ) . 'views/';
+}
+
+/**
+ * The the URL for the JavaScript assets directory.
+ *
+ * @since 1.0.0
+ *
+ * @return string URL with trailing slash
+ */
+function get_js_url() {
+       return trailingslashit( PLUGIN_URL ) . 'js/';
+}
+
+/**
+ * Get the URL for the CSS assets directory.
+ *
+ * @since 1.0.0
+ *
+ * @return string URL with trailing slash
+ */
+function get_css_url() {
+       return trailingslashit( PLUGIN_URL ) . 'css/';
+}
+
+/**
+ * Load the plugin's files.
+ *
+ * @since 1.0.0
+ *
+ * @return void
+ */
+function load_files() {
+       $files = array(
+               'tasks.php',
+               'tasks-list.php',
+               'tasks-controller.php',
+               'tasks-data.php',
+       );
+
+       foreach ( $files as $file ) {
+               $file = get_includes_dir_path() . $file;
+
+               if ( is_readable( $file ) ) {
+                       require_once $file;
+               }
+       }
+}
+
+add_action( 'plugins_loaded', __NAMESPACE__ . '\load_files' );
</ins></span></pre>
</div>
</div>

</body>
</html>