Changeset 7588
- Timestamp:
- 08/02/2018 07:12:19 PM (6 years ago)
- Location:
- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/assets/js/wordcamp-status.js
r6662 r7588 18 18 }; 19 19 20 this.setupFieldsToggle(); 20 21 this.setupSingleToggles(); 21 22 this.setupBulkToggles(); 22 23 24 $( '#status' ).select2(); 25 23 26 $( document ).ready( function() { 24 27 self.cache.$hideAll.trigger( 'click' ); 28 } ); 29 }, 30 31 setupFieldsToggle: function() { 32 var self = this; 33 34 self.cache.$fieldsToggle = $( '#fields-toggle' ); 35 self.cache.$fieldsContainer = $( '#fields-section' ); 36 37 self.cache.$fieldsToggle.on( 'click', function( event ) { 38 event.preventDefault(); 39 40 if ( self.cache.$fieldsContainer.hasClass( 'hidden' ) ) { 41 self.cache.$fieldsContainer.removeClass( 'hidden' ); 42 } else { 43 self.cache.$fieldsContainer.addClass( 'hidden' ); 44 } 25 45 } ); 26 46 }, -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/classes/report/class-wordcamp-details.php
r7550 r7588 9 9 use Exception; 10 10 use DateTime; 11 use WP_Post, WP_Query ;11 use WP_Post, WP_Query, WP_Error; 12 12 use function WordCamp\Reports\{get_assets_url, get_assets_dir_path, get_views_dir_path}; 13 13 use WordCamp\Reports\Utility\Date_Range; 14 use function WordCamp\Reports\Validation\{validate_date_range, validate_wordcamp_id , validate_wordcamp_status};14 use function WordCamp\Reports\Validation\{validate_date_range, validate_wordcamp_id}; 15 15 use WordCamp_Admin, WordCamp_Loader; 16 16 use WordCamp\Utilities\Export_CSV; … … 21 21 * A report class for exporting a spreadsheet of WordCamps. 22 22 * 23 * Note that this report does not use caching because it is only used in WP Admin and has a large number of 24 * optional parameters. 25 * 23 26 * @package WordCamp\Reports\Report 24 27 */ … … 43 46 * @var string 44 47 */ 45 public static $description = ' Details about WordCamps occurring within a specified date range.';48 public static $description = 'Create a spreadsheet of details about WordCamps that match optional criteria.'; 46 49 47 50 /** … … 52 55 public static $methodology = " 53 56 <ol> 54 <li>Retrieve WordCamp posts that fit within the date range and other optionalcriteria.</li>57 <li>Retrieve WordCamp posts that fit within the criteria.</li> 55 58 <li>Extract the data for each post that match the fields requested.</li> 56 <li>Walk all of the extracted data and format it for display.</li>59 <li>Walk through all of the extracted data and format it for display.</li> 57 60 </ol> 58 61 "; … … 66 69 67 70 /** 68 * The date range that defines the scope of the report data.71 * A date range that WordCamp events must fall within. 69 72 * 70 73 * @var null|Date_Range … … 73 76 74 77 /** 75 * The status to filter for in the report.76 * 77 * @var string78 */ 79 public $ status = '';80 81 /** 82 * Whether to include data for WordCamps that don't have a date set.78 * A list of WordCamp post IDs. 79 * 80 * @var array 81 */ 82 public $wordcamp_ids = []; 83 84 /** 85 * Whether to include counts of various post types for each WordCamp. 83 86 * 84 87 * @var bool 85 88 */ 86 public $include_dateless = false;87 88 /**89 * Whether to include counts of various post types for each WordCamp.90 *91 * @var bool92 */93 89 public $include_counts = false; 94 90 … … 110 106 * WordCamp_Details constructor. 111 107 * 112 * @param string $start_date The start of the date range for the report. 113 * @param string $end_date The end of the date range for the report. 114 * @param string $status Optional. The status ID to filter for in the report. 115 * @param bool $include_dateless Optional. True to include data for WordCamps that don't have a date set. Default false. 116 * @param bool $include_counts Optional. True to include counts of various post types for each WordCamp. Default false. 117 * @param array $options { 108 * @param Date_Range $date_range Optional. A date range that WordCamp events must fall within. 109 * @param array $wordcamp_ids Optional. A list of WordCamp post IDs to include in the results. 110 * @param bool $include_counts Optional. True to include counts of various post types for each WordCamp. 111 * Default false. 112 * @param array $options { 118 113 * Optional. Additional report parameters. 119 114 * See Base::__construct and the functions in WordCamp\Reports\Validation for additional parameters. … … 123 118 * } 124 119 */ 125 public function __construct( $start_date, $end_date, $status = '', $include_dateless = false, $include_counts = false, array $options = [] ) {120 public function __construct( Date_Range $date_range = null, array $wordcamp_ids = [], $include_counts = false, array $options = [] ) { 126 121 // Report-specific options. 127 122 $options = wp_parse_args( $options, [ 128 'status_subset' => [], 129 'fields' => [], 123 'fields' => [], 130 124 ] ); 131 125 132 126 parent::__construct( $options ); 133 127 134 try { 135 $this->range = validate_date_range( $start_date, $end_date, $options ); 136 } catch ( Exception $e ) { 137 $this->error->add( 138 self::$slug . '-date-error', 139 $e->getMessage() 140 ); 141 } 142 143 if ( $status && 'any' !== $status ) { 144 try { 145 $this->status = validate_wordcamp_status( $status, $options ); 146 } catch ( Exception $e ) { 147 $this->error->add( 148 self::$slug . '-status-error', 149 $e->getMessage() 150 ); 151 } 152 } 153 154 $this->include_dateless = wp_validate_boolean( $include_dateless ); 155 $this->include_counts = wp_validate_boolean( $include_counts ); 128 if ( $date_range instanceof Date_Range ) { 129 $this->range = $date_range; 130 } 131 132 if ( ! empty( $wordcamp_ids ) ) { 133 foreach ( $wordcamp_ids as $wordcamp_id ) { 134 try { 135 $this->wordcamp_ids[] = validate_wordcamp_id( $wordcamp_id, [ 'require_site' => false ] )->post_id; 136 } catch ( Exception $e ) { 137 $this->error->add( 138 self::$slug . '-wordcamp-id-error', 139 $e->getMessage() 140 ); 141 142 break; 143 } 144 } 145 } 146 147 $this->include_counts = wp_validate_boolean( $include_counts ); 156 148 157 149 $public_data_field_keys = array_merge( 158 150 [ 159 151 'Name', 160 'Status',161 152 ], 162 153 WordCamp_Loader::get_public_meta_keys() … … 167 158 [ 168 159 'ID', 160 'Created', 161 'Status', 169 162 'Tickets', 170 163 'Speakers', … … 211 204 212 205 /** 213 * Generate a cache key.214 *215 * @return string216 */217 protected function get_cache_key() {218 $cache_key_segments = [219 parent::get_cache_key(),220 $this->range->generate_cache_key_segment(),221 ];222 223 if ( $this->status ) {224 $cache_key_segments[] = $this->status;225 }226 227 if ( $this->include_dateless ) {228 $cache_key_segments[] = '+dateless';229 }230 231 if ( $this->include_counts ) {232 $cache_key_segments[] = '+counts';233 }234 235 return implode( '_', $cache_key_segments );236 }237 238 /**239 * Generate a cache expiration interval.240 *241 * @return int A time interval in seconds.242 */243 protected function get_cache_expiration() {244 return $this->range->generate_cache_duration( parent::get_cache_expiration() );245 }246 247 /**248 206 * Query and parse the data for the report. 249 207 * … … 254 212 if ( ! empty( $this->error->get_error_messages() ) ) { 255 213 return array(); 256 }257 258 // Maybe use cached data.259 $data = $this->maybe_get_cached_data();260 if ( is_array( $data ) ) {261 return $data;262 214 } 263 215 … … 277 229 $row = array_intersect_key( array_replace( $field_order, $row ), $row ); 278 230 } ); 279 280 $this->maybe_cache_data( $data );281 231 282 232 return $data; … … 312 262 array_keys( $wordcamp_admin->meta_keys( 'wordcamp' ) ), 313 263 [ 264 'Created', 314 265 'Status', 315 266 'Tickets', … … 339 290 * @return array 340 291 */ 341 p rotectedfunction prepare_data_for_display( array $data ) {292 public function prepare_data_for_display( array $data ) { 342 293 $all_statuses = WordCamp_Loader::get_post_statuses(); 343 294 … … 381 332 382 333 /** 383 * Get all current WordCamp posts.384 * 385 * @return array 334 * Get WordCamp posts that fit the report criteria. 335 * 336 * @return array An array of WP_Post objects. 386 337 */ 387 338 protected function get_wordcamp_posts() { … … 393 344 'no_found_rows' => false, 394 345 'ignore_sticky_posts' => true, 395 'orderby' => ' meta_value_num title',346 'orderby' => 'id', 396 347 'order' => 'ASC', 397 'meta_query' => [ 348 ); 349 350 if ( $this->range instanceof Date_Range ) { 351 // This replaces the default meta query. 352 $post_args['meta_query'] = [ 398 353 [ 399 354 'key' => 'Start Date (YYYY-mm-dd)', … … 402 357 'type' => 'NUMERIC', 403 358 ], 404 ],405 );406 407 if ( $this->include_dateless ) {408 $post_args['meta_query'] = array_merge( $post_args['meta_query'], [409 'relation' => 'OR',410 [411 'key' => 'Start Date (YYYY-mm-dd)',412 'compare' => 'NOT EXISTS',413 ],414 [415 'key' => 'Start Date (YYYY-mm-dd)',416 'compare' => '=',417 'value' => '',418 ],419 ] );420 421 // Don't include really old camps with no date or ones that didn't exist during the date range.422 $post_args['date_query'] = [423 [424 'before' => $this->range->end->format( 'Y-m-d' ),425 'after' => $this->range->start->format( 'Y-m-d' ) . ' - 1 year',426 ],427 359 ]; 360 $post_args['orderby'] = 'meta_value_num title'; 361 } 362 363 if ( ! empty( $this->wordcamp_ids ) ) { 364 $post_args['post__in'] = $this->wordcamp_ids; 428 365 } 429 366 … … 432 369 } 433 370 434 if ( $this->status ) {435 $status_report = new WordCamp_Status(436 $this->range->start->format( 'Y-m-d' ),437 $this->range->end->format( 'Y-m-d' ),438 $this->status,439 $this->options440 );441 442 $post_ids = array_keys( $status_report->get_data() );443 444 if ( empty( $post_ids ) ) {445 return [];446 }447 448 $post_args['post__in'] = $post_ids;449 }450 451 371 return get_posts( $post_args ); 452 372 } … … 463 383 464 384 $row = [ 465 'ID' => $wordcamp->ID, 466 'Name' => $wordcamp->post_title, 467 'Status' => $wordcamp->post_status, 385 'ID' => $wordcamp->ID, 386 'Name' => $wordcamp->post_title, 387 'Created' => get_the_date( 'Y-m-d', $wordcamp->ID ), 388 'Status' => $wordcamp->post_status, 468 389 ]; 469 390 … … 502 423 * Count the number of various post types for a WordCamp. 503 424 * 425 * If the WordCamp doesn't have a site yet, the counts will all be zero. 426 * 504 427 * @param WP_Post $wordcamp 505 428 * … … 515 438 516 439 try { 517 $id s = validate_wordcamp_id( $wordcamp->ID, [ 'require_site' => true ] );440 $id = validate_wordcamp_id( $wordcamp->ID, [ 'require_site' => false ] ); 518 441 } catch ( Exception $e ) { 442 return $counts; 443 } 444 445 if ( ! $id->site_id ) { 519 446 return $counts; 520 447 } … … 530 457 }; 531 458 532 switch_to_blog( $id s['site_id']);459 switch_to_blog( $id->site_id ); 533 460 534 461 // Tickets … … 549 476 550 477 /** 478 * Render HTML form inputs for the fields that are available for inclusion in the spreadsheet. 479 * 480 * @param string $context 'public' or 'private'. Default 'public'. 481 * @param array $field_defaults Optional. An associative array where the keys are field keys and the values 482 * are extra attributes for those field inputs. Examples: checked or required. 483 */ 484 public static function render_available_fields( $context = 'public', array $field_defaults = [] ) { 485 $field_order = array_fill_keys( self::get_field_order(), '' ); 486 $field_defaults = array_replace( $field_order, $field_defaults ); 487 488 $shadow_report = new self( null, [], false, [ 'public' => ( 'private' === $context ) ? false : true ] ); 489 490 $available_fields = array_intersect_key( $field_defaults, $shadow_report->get_data_fields_safelist() ); 491 ?> 492 <fieldset class="fields-container"> 493 <legend class="fields-label">Available Fields</legend> 494 495 <?php foreach ( $available_fields as $field_name => $extra_props ) : ?> 496 <div class="field-checkbox"> 497 <input 498 type="checkbox" 499 id="fields-<?php echo esc_attr( $field_name ); ?>" 500 name="fields[]" 501 value="<?php echo esc_attr( $field_name ); ?>" 502 <?php if ( $extra_props && is_string( $extra_props ) ) echo esc_html( $extra_props ); ?> 503 /> 504 <label for="fields-<?php echo esc_attr( $field_name ); ?>"> 505 <?php echo esc_attr( $field_name ); ?> 506 </label> 507 </div> 508 <?php endforeach; ?> 509 </fieldset> 510 <?php 511 } 512 513 /** 551 514 * Register all assets used by this report. 552 515 * 553 516 * @return void 554 517 */ 555 p rotectedstatic function register_assets() {518 public static function register_assets() { 556 519 wp_register_script( 557 520 self::$slug, … … 589 552 */ 590 553 public static function render_admin_page() { 591 $start_date = filter_input( INPUT_POST, 'start-date' ); 592 $end_date = filter_input( INPUT_POST, 'end-date' ); 593 $include_dateless = filter_input( INPUT_POST, 'include_dateless', FILTER_VALIDATE_BOOLEAN ); 594 $status = filter_input( INPUT_POST, 'status' ); 595 $refresh = filter_input( INPUT_POST, 'refresh', FILTER_VALIDATE_BOOLEAN ); 596 $action = filter_input( INPUT_POST, 'action' ); 597 $nonce = filter_input( INPUT_POST, self::$slug . '-nonce' ); 598 $statuses = WordCamp_Loader::get_post_statuses(); 599 600 $field_order = array_fill_keys( self::get_field_order(), '' ); 601 $field_defaults = array_replace( $field_order, [ 554 $field_defaults = [ 602 555 'ID' => 'checked', 603 556 'Name' => 'checked disabled', … … 606 559 'Location' => 'checked', 607 560 'URL' => 'checked', 608 ] ); 609 610 $shadow_report = new self( '', '', '', false, false, [ 'public' => false ] ); 611 $available_fields = array_intersect_key( $field_defaults, $shadow_report->get_data_fields_safelist() ); 561 ]; 612 562 613 563 include get_views_dir_path() . 'report/wordcamp-details.php'; … … 622 572 $start_date = filter_input( INPUT_POST, 'start-date' ); 623 573 $end_date = filter_input( INPUT_POST, 'end-date' ); 624 $include_dateless = filter_input( INPUT_POST, 'include_dateless', FILTER_VALIDATE_BOOLEAN );625 $status = filter_input( INPUT_POST, 'status' );626 574 $fields = filter_input( INPUT_POST, 'fields', FILTER_SANITIZE_STRING, [ 'flags' => FILTER_REQUIRE_ARRAY ] ); 627 $refresh = filter_input( INPUT_POST, 'refresh', FILTER_VALIDATE_BOOLEAN );628 575 $action = filter_input( INPUT_POST, 'action' ); 629 576 $nonce = filter_input( INPUT_POST, self::$slug . '-nonce' ); … … 636 583 637 584 if ( wp_verify_nonce( $nonce, 'run-report' ) && current_user_can( 'manage_network' ) ) { 585 $error = null; 586 $range = null; 587 588 if ( $start_date || $end_date ) { 589 try { 590 $range = validate_date_range( $start_date, $end_date, [ 591 'allow_future_start' => true, 592 'earliest_start' => new DateTime( '2006-01-01' ), // No WordCamp posts before 2006., 593 ] ); 594 } catch ( Exception $e ) { 595 $error = new WP_Error( 596 self::$slug . '-date-range-error', 597 $e->getMessage() 598 ); 599 } 600 } 601 638 602 $include_counts = false; 639 603 if ( ! empty( array_intersect( $fields, [ 'Tickets', 'Speakers', 'Sponsors', 'Organizers' ] ) ) ) { … … 646 610 647 611 $options = array( 648 'fields' => $fields, 649 'public' => false, 650 'earliest_start' => new DateTime( '2006-01-01' ), // No WordCamp posts before 2006. 612 'fields' => $fields, 613 'public' => false, 651 614 ); 652 615 653 if ( $status ) { 654 $options['earliest_start'] = new DateTime( '2015-01-01' ); // No status log data before 2015. 655 } 656 657 if ( $refresh ) { 658 $options['flush_cache'] = true; 659 } 660 661 $report = new self( $start_date, $end_date, $status, $include_dateless, $include_counts, $options ); 616 $report = new self( $range, [], $include_counts, $options ); 662 617 663 618 $filename = [ $report::$name ]; 664 $filename[] = $report->range->start->format( 'Y-m-d' ); 665 $filename[] = $report->range->end->format( 'Y-m-d' ); 666 if ( $report->status ) { 667 $filename[] = $report->status; 668 } 669 if ( $report->include_dateless ) { 670 $filename[] = 'include-dateless'; 619 if ( $report->range instanceof Date_Range ) { 620 $filename[] = $report->range->start->format( 'Y-m-d' ); 621 $filename[] = $report->range->end->format( 'Y-m-d' ); 671 622 } 672 623 if ( $report->include_counts ) { … … 688 639 } 689 640 641 if ( $error instanceof WP_Error ) { 642 $exporter->error = $report->merge_errors( $error, $exporter->error ); 643 } 644 690 645 $exporter->emit_file(); 691 646 } // End if(). -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/classes/report/class-wordcamp-status.php
r7516 r7588 8 8 9 9 use Exception; 10 use WordCamp\Reports; 11 use function WordCamp\Reports\Validation\validate_wordcamp_status; 10 use DateTime; 11 use WP_Error, WP_Post; 12 use function WordCamp\Reports\{get_assets_url, get_assets_dir_path, get_views_dir_path}; 13 use WordCamp\Reports\Utility\Date_Range; 14 use function WordCamp\Reports\Validation\{validate_date_range, validate_wordcamp_status}; 15 use function WordCamp\Reports\Time\{year_array, quarter_array, month_array, convert_time_period_to_date_range}; 12 16 use WordCamp_Loader; 17 use WordCamp\Utilities\Export_CSV; 13 18 14 19 /** … … 19 24 * @package WordCamp\Reports\Report 20 25 */ 21 class WordCamp_Status extends Date_Range {26 class WordCamp_Status extends Base { 22 27 /** 23 28 * Report name. … … 69 74 70 75 /** 76 * The date range that defines the scope of the report data. 77 * 78 * @var null|Date_Range 79 */ 80 public $range = null; 81 82 /** 71 83 * The status to filter for in the report. 72 84 * … … 106 118 ) ); 107 119 108 parent::__construct( $start_date, $end_date, $options ); 120 parent::__construct( $options ); 121 122 try { 123 $this->range = validate_date_range( $start_date, $end_date, $options ); 124 } catch ( Exception $e ) { 125 $this->error->add( 126 self::$slug . '-date-error', 127 $e->getMessage() 128 ); 129 } 109 130 110 131 if ( $status && 'any' !== $status ) { … … 139 160 */ 140 161 protected function get_cache_key() { 141 $cache_key = parent::get_cache_key(); 162 $cache_key_segments = [ 163 parent::get_cache_key(), 164 $this->range->generate_cache_key_segment(), 165 ]; 142 166 143 167 if ( $this->status ) { 144 $cache_key .= '_' . $this->status; 145 } 146 147 return $cache_key; 168 $cache_key_segments[] = $this->status; 169 } 170 171 return implode( '_', $cache_key_segments ); 172 } 173 174 /** 175 * Generate a cache expiration interval. 176 * 177 * @return int A time interval in seconds. 178 */ 179 protected function get_cache_expiration() { 180 return $this->range->generate_cache_duration( parent::get_cache_expiration() ); 148 181 } 149 182 … … 177 210 // Trim log entries occurring after the date range. 178 211 $logs = array_filter( $logs, function( $entry ) { 179 if ( $entry['timestamp'] > $this-> end_date->getTimestamp() ) {212 if ( $entry['timestamp'] > $this->range->end->getTimestamp() ) { 180 213 return false; 181 214 } … … 195 228 // Trim log entries occurring before the date range. 196 229 $logs = array_filter( $logs, function( $entry ) { 197 if ( $entry['timestamp'] < $this-> start_date->getTimestamp() ) {230 if ( $entry['timestamp'] < $this->range->start->getTimestamp() ) { 198 231 return false; 199 232 } … … 300 333 'key' => 'Start Date (YYYY-mm-dd)', 301 334 'compare' => '>=', 302 'value' => strtotime( '-3 months', $this-> start_date->getTimestamp() ),335 'value' => strtotime( '-3 months', $this->range->start->getTimestamp() ), 303 336 'type' => 'NUMERIC', 304 337 ), … … 312 345 * Retrieve the log of status changes for a particular WordCamp. 313 346 * 314 * @param \WP_Post $wordcamp A WordCamp post.347 * @param WP_Post $wordcamp A WordCamp post. 315 348 * 316 349 * @return array 317 350 */ 318 protected function get_wordcamp_status_logs( \WP_Post $wordcamp ) {351 protected function get_wordcamp_status_logs( WP_Post $wordcamp ) { 319 352 $log_entries = get_post_meta( $wordcamp->ID, '_status_change' ); 320 353 … … 398 431 public function render_html() { 399 432 $data = $this->compile_report_data( $this->get_data() ); 400 $start_date = $this-> start_date;401 $end_date = $this-> end_date;433 $start_date = $this->range->start; 434 $end_date = $this->range->end; 402 435 $status = $this->status; 403 436 … … 407 440 408 441 if ( ! empty( $this->error->get_error_messages() ) ) { 409 ?> 410 <div class="notice notice-error"> 411 <?php foreach ( $this->error->get_error_messages() as $message ) : ?> 412 <?php echo wpautop( wp_kses_post( $message ) ); ?> 413 <?php endforeach; ?> 414 </div> 415 <?php 442 $this->render_error_html(); 416 443 } else { 417 include Reports\get_views_dir_path() . 'html/wordcamp-status.php';444 include get_views_dir_path() . 'html/wordcamp-status.php'; 418 445 } 419 446 } … … 427 454 wp_register_script( 428 455 self::$slug, 429 Reports\get_assets_url() . 'js/' . self::$slug . '.js',430 array( 'jquery' ),431 Reports\JS_VERSION,456 get_assets_url() . 'js/' . self::$slug . '.js', 457 array( 'jquery', 'select2' ), 458 filemtime( get_assets_dir_path() . 'js/' . self::$slug . '.js' ), 432 459 true 433 460 ); … … 435 462 wp_register_style( 436 463 self::$slug, 437 Reports\get_assets_url() . 'css/' . self::$slug . '.css',438 array( ),439 Reports\CSS_VERSION,464 get_assets_url() . 'css/' . self::$slug . '.css', 465 array( 'select2' ), 466 filemtime( get_assets_dir_path() . 'css/' . self::$slug . '.css' ), 440 467 'screen' 441 468 ); … … 449 476 public static function enqueue_admin_assets() { 450 477 self::register_assets(); 451 478 WordCamp_Details::register_assets(); 479 480 wp_enqueue_style( WordCamp_Details::$slug ); 452 481 wp_enqueue_script( self::$slug ); 453 482 wp_enqueue_style( self::$slug ); … … 468 497 $statuses = WordCamp_Loader::get_post_statuses(); 469 498 499 $field_defaults = [ 500 'ID' => 'checked', 501 'Name' => 'checked disabled', 502 'Start Date (YYYY-mm-dd)' => 'checked', 503 'End Date (YYYY-mm-dd)' => 'checked', 504 'Location' => 'checked', 505 'URL' => 'checked', 506 'Created' => 'checked', 507 'Status' => 'checked', 508 ]; 509 470 510 $report = null; 471 511 472 if ( 'run-report' === $action && wp_verify_nonce( $nonce, 'run-report' ) ) { 512 if ( 'Show results' === $action 513 && wp_verify_nonce( $nonce, 'run-report' ) 514 && current_user_can( 'manage_network' ) 515 ) { 473 516 $options = array( 474 'earliest_start' => new \DateTime( '2015-01-01' ), // No status log data before 2015. 517 'public' => false, 518 'earliest_start' => new DateTime( '2015-01-01' ), // No status log data before 2015. 475 519 ); 476 520 … … 480 524 481 525 $report = new self( $start_date, $end_date, $status, $options ); 482 483 // The report adjusts the end date in some circumstances. 484 if ( empty( $report->error->get_error_messages() ) ) { 485 $end_date = $report->end_date->format( 'Y-m-d' ); 486 } 487 } 488 489 include Reports\get_views_dir_path() . 'report/wordcamp-status.php'; 526 } 527 528 include get_views_dir_path() . 'report/wordcamp-status.php'; 529 } 530 531 /** 532 * Export the report data to a file. 533 * 534 * @return void 535 */ 536 public static function export_to_file() { 537 $start_date = filter_input( INPUT_POST, 'start-date' ); 538 $end_date = filter_input( INPUT_POST, 'end-date' ); 539 $status = filter_input( INPUT_POST, 'status' ); 540 $fields = filter_input( INPUT_POST, 'fields', FILTER_SANITIZE_STRING, [ 'flags' => FILTER_REQUIRE_ARRAY ] ); 541 $refresh = filter_input( INPUT_POST, 'refresh', FILTER_VALIDATE_BOOLEAN ); 542 $action = filter_input( INPUT_POST, 'action' ); 543 $nonce = filter_input( INPUT_POST, self::$slug . '-nonce' ); 544 545 if ( 'Export CSV' !== $action ) { 546 return; 547 } 548 549 if ( wp_verify_nonce( $nonce, 'run-report' ) && current_user_can( 'manage_network' ) ) { 550 $error = null; 551 552 $options = array( 553 'public' => false, 554 'earliest_start' => new DateTime( '2015-01-01' ), // No status log data before 2015. 555 ); 556 557 if ( $refresh ) { 558 $options['flush_cache'] = true; 559 } 560 561 $status_report = new self( $start_date, $end_date, $status, $options ); 562 $wordcamp_ids = array_keys( $status_report->get_data() ); 563 564 if ( ! empty( $status_report->error->get_error_messages() ) ) { 565 $error = $status_report->error; 566 } elseif ( empty( $wordcamp_ids ) ) { 567 $error = new WP_Error( 568 self::$slug . '-export-error', 569 'No status data available for the given criteria.' 570 ); 571 } 572 573 $include_counts = false; 574 if ( ! empty( array_intersect( $fields, [ 'Tickets', 'Speakers', 'Sponsors', 'Organizers' ] ) ) ) { 575 $include_counts = true; 576 } 577 578 // The "Name" field should always be included, but does not get submitted because the input is disabled, 579 // so add it in here. 580 $fields[] = 'Name'; 581 582 $options = array( 583 'fields' => $fields, 584 'public' => false, 585 ); 586 587 $details_report = new WordCamp_Details( null, $wordcamp_ids, $include_counts, $options ); 588 589 $filename = [ $status_report::$name ]; 590 $filename[] = $status_report->range->start->format( 'Y-m-d' ); 591 $filename[] = $status_report->range->end->format( 'Y-m-d' ); 592 if ( $status_report->status ) { 593 $filename[] = $status_report->status; 594 } 595 if ( $details_report->include_counts ) { 596 $filename[] = 'include-counts'; 597 } 598 599 $data = $details_report->prepare_data_for_display( $details_report->get_data() ); 600 601 $headers = ( ! empty( $data ) ) ? array_keys( $data[0] ) : []; 602 603 $exporter = new Export_CSV( array( 604 'filename' => $filename, 605 'headers' => $headers, 606 'data' => $data, 607 ) ); 608 609 if ( ! empty( $details_report->error->get_error_messages() ) ) { 610 $exporter->error = $details_report->merge_errors( $details_report->error, $exporter->error ); 611 } 612 613 if ( $error instanceof WP_Error ) { 614 $exporter->error = $details_report->merge_errors( $error, $exporter->error ); 615 } 616 617 $exporter->emit_file(); 618 } // End if(). 490 619 } 491 620 … … 503 632 self::register_assets(); 504 633 634 wp_enqueue_style( 'select2' ); 505 635 wp_enqueue_script( self::$slug ); 506 636 … … 525 655 $action = filter_input( INPUT_GET, 'action' ); 526 656 527 $years = self::year_array( absint( date( 'Y' ) ), 2015 ); 528 $months = self::month_array(); 657 $years = year_array( absint( date( 'Y' ) ), 2015 ); 658 $quarters = quarter_array(); 659 $months = month_array(); 529 660 $statuses = WordCamp_Loader::get_post_statuses(); 530 661 … … 540 671 541 672 if ( 'Show results' === $action ) { 542 $range = self::convert_time_period_to_date_range( $year, $period ); 673 try { 674 $range = convert_time_period_to_date_range( $year, $period ); 675 } catch ( Exception $e ) { 676 $error = new WP_Error( 677 self::$slug . '-time-period-error', 678 $e->getMessage() 679 ); 680 } 543 681 544 682 $options = array( 545 'earliest_start' => new \DateTime( '2015-01-01' ), // No status log data before 2015.683 'earliest_start' => new DateTime( '2015-01-01' ), // No status log data before 2015. 546 684 ); 547 685 548 $report = new self( $range ['start_date'], $range['end_date'], $status, $options );549 } 550 551 include Reports\get_views_dir_path() . 'public/wordcamp-status.php';686 $report = new self( $range->start, $range->end, $status, $options ); 687 } 688 689 include get_views_dir_path() . 'public/wordcamp-status.php'; 552 690 } 553 691 } -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/includes/validation.php
r7573 r7588 5 5 6 6 use Exception; 7 use stdClass; 7 8 use DateTime, DateTimeImmutable, DateInterval; 8 9 use WP_Post; … … 121 122 * } 122 123 * 123 * @return array An associative array containingvalid post ID and site ID integers for the WordCamp.124 * @return object An object containing properties for valid post ID and site ID integers for the WordCamp. 124 125 * @throws Exception 125 126 */ … … 140 141 141 142 if ( ! $wordcamp_post instanceof WP_Post || WCPT_POST_TYPE_ID !== get_post_type( $wordcamp_post ) ) { 142 throw new Exception( 'Please enter a valid WordCamp ID.' ); 143 } 144 145 $valid = [ 146 'post_id' => $post_id, 147 'site_id' => 0, 148 ]; 149 150 if ( $config['require_site'] ) { 151 $site_id = get_wordcamp_site_id( $wordcamp_post ); 152 153 if ( ! $site_id ) { 154 throw new Exception( 'The specified WordCamp does not have a site yet.' ); 155 } 156 157 $valid['site_id'] = $site_id; 158 } 143 throw new Exception( sprintf( 144 'Invalid WordCamp ID: %s', 145 esc_html( $post_id ) 146 ) ); 147 } 148 149 $valid = new stdClass(); 150 151 $valid->post_id = $post_id; 152 $valid->site_id = get_wordcamp_site_id( $wordcamp_post ); 159 153 160 154 if ( $switched ) { 161 155 restore_current_blog(); 156 } 157 158 if ( $config['require_site'] && ! $valid->site_id ) { 159 throw new Exception( sprintf( 160 'The WordCamp with ID %d does not have a site.', 161 absint( $post_id ) 162 ) ); 162 163 } 163 164 -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/index.php
r7573 r7588 134 134 __NAMESPACE__ . '\Report\Payment_Activity', 135 135 __NAMESPACE__ . '\Report\Sponsorship_Grants', 136 __NAMESPACE__ . '\Report\WordCamp_Status', 136 137 __NAMESPACE__ . '\Report\WordCamp_Details', 137 __NAMESPACE__ . '\Report\WordCamp_Status',138 138 __NAMESPACE__ . '\Report\Meetup_Groups', 139 139 __NAMESPACE__ . '\Report\Meetup_Events', -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/html/wordcamp-status.php
r6662 r7588 7 7 defined( 'WPINC' ) || die(); 8 8 9 /** @var \DateTime $start_date */ 10 /** @var \DateTime $end_date */ 9 use DateTime; 10 11 /** @var DateTime $start_date */ 12 /** @var DateTime $end_date */ 11 13 /** @var string $status */ 12 14 /** @var array $active_camps */ … … 15 17 ?> 16 18 17 <?php if ( ! empty( $active_camps ) ) : ?>19 <?php if ( count( $active_camps ) ) : ?> 18 20 <h3 id="active-heading"> 19 21 <?php if ( $status ) : ?> … … 28 30 <?php endif; ?> 29 31 </h3> 32 33 <table class="striped widefat but-not-too-wide"> 34 <tr> 35 <td>Active WordCamps</td> 36 <td class="number"><?php echo number_format_i18n( count( $active_camps ) ); ?></td> 37 </tr> 38 </table> 30 39 31 40 <?php foreach ( $active_camps as $active_camp ) : ?> … … 43 52 <?php endif; ?> 44 53 45 <?php if ( ! empty( $inactive_camps ) ) : ?>54 <?php if ( count( $inactive_camps ) ) : ?> 46 55 <h3 id="inactive-heading"> 47 56 WordCamps … … 57 66 </h3> 58 67 68 <table class="striped widefat but-not-too-wide"> 69 <tr> 70 <td>Inactive WordCamps</td> 71 <td class="number"><?php echo number_format_i18n( count( $inactive_camps ) ); ?></td> 72 </tr> 73 </table> 74 59 75 <ul> 60 76 <?php foreach ( $inactive_camps as $inactive_camp ) : ?> … … 69 85 70 86 <?php if ( empty( $active_camps ) && empty( $inactive_camps ) ) : ?> 71 < h3 id="no-data-heading">87 <p> 72 88 No data 73 89 <?php if ( $status ) : ?> … … 79 95 between <?php echo esc_html( $start_date->format( 'M jS, Y' ) ); ?> and <?php echo esc_html( $end_date->format( 'M jS, Y' ) ); ?> 80 96 <?php endif; ?> 81 </ h3>97 </p> 82 98 <?php endif; ?> -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/public/wordcamp-status.php
r6662 r7588 13 13 /** @var string $status */ 14 14 /** @var array $years */ 15 /** @var array $quarters */ 15 16 /** @var array $months */ 16 17 /** @var array $statuses */ … … 37 38 <select id="period" name="period"> 38 39 <option value="all"<?php selected( 'all' === $period ); ?>>Entire year</option> 39 <option value="q1"<?php selected( 'q1' === $period ); ?>>1st quarter</option> 40 <option value="q2"<?php selected( 'q2' === $period ); ?>>2nd quarter</option> 41 <option value="q3"<?php selected( 'q3' === $period ); ?>>3rd quarter</option> 42 <option value="q4"<?php selected( 'q4' === $period ); ?>>4th quarter</option> 40 <?php foreach ( $quarters as $quarter_value => $quarter_label ) : ?> 41 <option value="<?php echo esc_attr( $quarter_value ); ?>"<?php selected( $quarter_value, $period ); ?>><?php echo esc_html( $quarter_label ); ?></option> 42 <?php endforeach; ?> 43 43 <?php foreach ( $months as $month_value => $month_label ) : ?> 44 44 <option value="<?php echo esc_attr( $month_value ); ?>"<?php selected( $month_value, $period ); ?>><?php echo esc_html( $month_label ); ?></option> -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/report/wordcamp-details.php
r7550 r7588 10 10 use WordCamp\Reports\Report; 11 11 12 /** @var string $start_date */ 13 /** @var string $end_date */ 14 /** @var bool $include_dateless */ 15 /** @var string $status */ 16 /** @var array $statuses */ 17 /** @var array $available_fields */ 12 /** @var array $field_defaults */ 18 13 ?> 19 14 … … 38 33 <tbody> 39 34 <tr> 40 <th scope="row"><label for="start-date">Start Date </label></th>41 <td><input type="date" id="start-date" name="start-date" value=" <?php echo esc_attr( $start_date ) ?>" required/></td>35 <th scope="row"><label for="start-date">Start Date (optional)</label></th> 36 <td><input type="date" id="start-date" name="start-date" value="" /></td> 42 37 </tr> 43 38 <tr> 44 <th scope="row"><label for="end-date">End Date</label></th> 45 <td><input type="date" id="end-date" name="end-date" value="<?php echo esc_attr( $end_date ) ?>" required /></td> 46 </tr> 47 <tr> 48 <th scope="row"><label for="include_dateless">Include WordCamps without a date</label></th> 49 <td><input type="checkbox" id="include_dateless" name="include_dateless"<?php checked( $include_dateless ); ?> /></td> 50 </tr> 51 <tr> 52 <th scope="row"><label for="status">Status (optional)</label></th> 53 <td> 54 <select id="status" name="status"> 55 <option value="any"<?php selected( ( ! $status || 'any' === $status ) ); ?>>Any</option> 56 <?php foreach ( $statuses as $value => $label ) : ?> 57 <option value="<?php echo esc_attr( $value ); ?>"<?php selected( $value, $status ); ?>><?php echo esc_attr( $label ); ?></option> 58 <?php endforeach; ?> 59 </select> 60 </td> 61 </tr> 62 <tr> 63 <th scope="row"><label for="refresh">Refresh results</label></th> 64 <td><input type="checkbox" id="refresh" name="refresh" /></td> 39 <th scope="row"><label for="end-date">End Date (optional)</label></th> 40 <td><input type="date" id="end-date" name="end-date" value="" /></td> 65 41 </tr> 66 42 </tbody> 67 43 </table> 68 44 69 <fieldset class="fields-container"> 70 <legend class="fields-label">Available Fields</legend> 71 72 <?php foreach ( $available_fields as $field_name => $extra_props ) : ?> 73 <div class="field-checkbox"> 74 <input 75 type="checkbox" 76 id="fields-<?php echo esc_attr( $field_name ); ?>" 77 name="fields[]" 78 value="<?php echo esc_attr( $field_name ); ?>" 79 <?php if ( $extra_props && is_string( $extra_props ) ) echo esc_html( $extra_props ); ?> 80 /> 81 <label for="fields-<?php echo esc_attr( $field_name ); ?>"> 82 <?php echo esc_attr( $field_name ); ?> 83 </label> 84 </div> 85 <?php endforeach; ?> 86 </fieldset> 45 <?php Report\WordCamp_Details::render_available_fields( 'private', $field_defaults ) ?> 87 46 88 47 <?php submit_button( 'Export CSV', 'primary', 'action', false ); ?> -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/views/report/wordcamp-status.php
r7434 r7588 14 14 /** @var string $status */ 15 15 /** @var array $statuses */ 16 /** @var array $field_defaults */ 16 17 /** @var Report\WordCamp_Status|null $report */ 17 18 ?> … … 38 39 <tr> 39 40 <th scope="row"><label for="start-date">Start Date</label></th> 40 <td><input type="date" id="start-date" name="start-date" value="<?php echo esc_attr( $start_date ) ?>" /></td>41 <td><input type="date" id="start-date" name="start-date" value="<?php echo esc_attr( $start_date ) ?>" required /></td> 41 42 </tr> 42 43 <tr> 43 44 <th scope="row"><label for="end-date">End Date</label></th> 44 <td><input type="date" id="end-date" name="end-date" value="<?php echo esc_attr( $end_date ) ?>" /></td>45 <td><input type="date" id="end-date" name="end-date" value="<?php echo esc_attr( $end_date ) ?>" required /></td> 45 46 </tr> 46 47 <tr> 47 48 <th scope="row"><label for="status">Status (optional)</label></th> 48 49 <td> 49 <select id="status" name="status" >50 <select id="status" name="status" class="select2-container"> 50 51 <option value="any"<?php selected( ( ! $status || 'any' === $status ) ); ?>>Any</option> 51 52 <?php foreach ( $statuses as $value => $label ) : ?> … … 53 54 <?php endforeach; ?> 54 55 </select> 56 <p class="description"> 57 This will select all WordCamps that had this status at any time during the date range. 58 </p> 55 59 </td> 56 60 </tr> … … 62 66 </table> 63 67 64 <?php submit_button( 'Show results', 'primary', '' ); ?> 68 <?php submit_button( 'Show results', 'primary', 'action', false ); ?> 69 70 <button id="fields-toggle" class="secondary button-secondary">Export resulting WordCamps as a CSV</button> 71 72 <section id="fields-section" class="hidden"> 73 <?php Report\WordCamp_Details::render_available_fields( 'private', $field_defaults ); ?> 74 <?php submit_button( 'Export CSV', 'primary', 'action', false ); ?> 75 </section> 65 76 </form> 66 77
Note: See TracChangeset
for help on using the changeset viewer.