Changeset 7734
- Timestamp:
- 10/16/2018 05:30:06 PM (7 years ago)
- Location:
- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports
- Files:
-
- 8 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/classes/report/class-base.php
r7516 r7734 135 135 136 136 /** 137 * Determine the data fields safelist based on the context of the report.138 *139 * @return array The list of fields that are safe to include.140 */141 protected function get_data_fields_safelist() {142 $safelist = $this->public_data_fields;143 144 if ( false === $this->options['public'] ) {145 $safelist = array_merge( $safelist, $this->private_data_fields );146 }147 148 return $safelist;149 }150 151 /**152 137 * Filter the report data prior to caching and compiling. 153 138 * … … 315 300 return new \WP_REST_Response( $response_data ); 316 301 } 302 303 /** 304 * Determine the data fields safelist based on the context of the report. 305 * 306 * @return array The list of fields that are safe to include. 307 */ 308 public function get_data_fields_safelist() { 309 $safelist = $this->public_data_fields; 310 311 if ( false === $this->options['public'] ) { 312 $safelist = array_merge( $safelist, $this->private_data_fields ); 313 } 314 315 return $safelist; 316 } 317 317 } -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/classes/report/class-wordcamp-details.php
r7588 r7734 9 9 use Exception; 10 10 use DateTime; 11 use WP_Post, WP_Query , WP_Error;12 use function WordCamp\Reports\{get_ assets_url, get_assets_dir_path, get_views_dir_path};11 use WP_Post, WP_Query; 12 use function WordCamp\Reports\{get_views_dir_path}; 13 13 use WordCamp\Reports\Utility\Date_Range; 14 14 use function WordCamp\Reports\Validation\{validate_date_range, validate_wordcamp_id}; 15 15 use WordCamp_Admin, WordCamp_Loader; 16 use WordCamp\Utilities\Export_CSV;17 16 18 17 /** … … 26 25 * @package WordCamp\Reports\Report 27 26 */ 28 class WordCamp_Details extends Base {27 class WordCamp_Details extends Base_Details { 29 28 /** 30 29 * Report name. … … 69 68 70 69 /** 71 * A date range that WordCamp events must fall within.72 *73 * @var null|Date_Range74 */75 public $range = null;76 77 /**78 * A list of WordCamp post IDs.79 *80 * @var array81 */82 public $wordcamp_ids = [];83 84 /**85 70 * Whether to include counts of various post types for each WordCamp. 86 71 * … … 88 73 */ 89 74 public $include_counts = false; 90 91 /**92 * Data fields that can be visible in a public context.93 *94 * @var array An associative array of key/default value pairs.95 */96 protected $public_data_fields = [];97 98 /**99 * Data fields that should only be visible in a private context.100 *101 * @var array An associative array of key/default value pairs.102 */103 protected $private_data_fields = [];104 75 105 76 /** … … 118 89 * } 119 90 */ 120 public function __construct( Date_Range $date_range = null, array $wordcamp_ids = [], $include_counts = false, array $options = [] ) {91 public function __construct( Date_Range $date_range = null, array $wordcamp_ids = null, $include_counts = false, array $options = [] ) { 121 92 // Report-specific options. 122 $options = wp_parse_args( $options, [ 123 'fields' => [], 124 ] ); 125 126 parent::__construct( $options ); 127 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 } 93 94 parent::__construct( $date_range, $wordcamp_ids, $options ); 95 if ( ! is_null( $wordcamp_ids ) ) { 96 $this->event_ids = []; 97 $this->validate_wordcamp_ids( $wordcamp_ids ); 145 98 } 146 99 147 100 $this->include_counts = wp_validate_boolean( $include_counts ); 148 149 $public_data_field_keys = array_merge( 101 } 102 103 /** 104 * Return fields that can be displayed in public context. 105 * 106 * @return array 107 */ 108 public function get_public_data_fields() { 109 return array_merge( 150 110 [ 151 111 'Name', … … 153 113 WordCamp_Loader::get_public_meta_keys() 154 114 ); 155 $this->public_data_fields = array_fill_keys( $public_data_field_keys, '' ); 156 157 $private_data_field_keys = array_merge( 115 } 116 117 /** 118 * Return fields that can be viewed in private context. 119 * 120 * @return array 121 */ 122 public function get_private_data_fields() { 123 return array_merge( 158 124 [ 159 125 'ID', … … 165 131 'Organizers', 166 132 ], 167 array_diff( $this->get_meta_keys(), array_keys( $this-> public_data_fields) )133 array_diff( $this->get_meta_keys(), array_keys( $this->get_public_data_fields() ) ) 168 134 ); 169 $this->private_data_fields = array_fill_keys( $private_data_field_keys, '' ); 170 171 try { 172 $this->options['fields'] = $this->validate_fields_input( $this->options['fields'] ); 173 } catch ( Exception $e ) { 174 $this->error->add( 175 self::$slug . '-fields-error', 176 $e->getMessage() 177 ); 178 } 179 } 180 181 /** 182 * Check the array of fields to include in the spreadsheet against the safelist of data fields. 183 * 184 * @param array $fields 185 * 186 * @return array The validated fields. 187 * @throws Exception 188 */ 189 protected function validate_fields_input( array $fields ) { 190 $valid_fields = $this->get_data_fields_safelist(); 191 $fields = array_unique( $fields ); 192 193 foreach ( $fields as $field ) { 194 if ( ! array_key_exists( $field, $valid_fields ) ) { 195 throw new Exception( sprintf( 196 'Invalid field: %s', 197 esc_html( $field ) 198 ) ); 135 } 136 137 /** 138 * Validate WordCamp ids and filter those without a site. 139 * 140 * @param $wordcamp_ids 141 */ 142 public function validate_wordcamp_ids( $wordcamp_ids ) { 143 if ( ! empty( $wordcamp_ids ) ) { 144 foreach ( $wordcamp_ids as $wordcamp_id ) { 145 try { 146 $this->event_ids[] = validate_wordcamp_id( $wordcamp_id, [ 'require_site' => false ] )->post_id; 147 } catch ( Exception $e ) { 148 $this->error->add( 149 self::$slug . '-wordcamp-id-error', 150 $e->getMessage() 151 ); 152 153 break; 154 } 199 155 } 200 156 } 201 202 return $fields;203 }204 205 /**206 * Query and parse the data for the report.207 *208 * @return array209 */210 public function get_data() {211 // Bail if there are errors.212 if ( ! empty( $this->error->get_error_messages() ) ) {213 return array();214 }215 216 $data = [];217 218 $wordcamp_posts = $this->get_wordcamp_posts();219 220 foreach ( $wordcamp_posts as $post ) {221 $data[] = $this->fill_data_row( $post );222 }223 224 $data = $this->filter_data_fields( $data );225 226 // Reorder of the fields in each row.227 $field_order = array_fill_keys( self::get_field_order(), '' );228 array_walk( $data, function( &$row ) use ( $field_order ) {229 $row = array_intersect_key( array_replace( $field_order, $row ), $row );230 } );231 232 return $data;233 }234 235 /**236 * Compile the report data into results.237 *238 * Currently unused.239 *240 * @param array $data The data to compile.241 *242 * @return array243 */244 public function compile_report_data( array $data ) {245 return $data;246 157 } 247 158 … … 251 162 * @return array 252 163 */ 253 p rotectedstatic function get_field_order() {164 public static function get_field_order() { 254 165 /* @var WordCamp_Admin $wordcamp_admin */ 255 166 global $wordcamp_admin; … … 336 247 * @return array An array of WP_Post objects. 337 248 */ 338 p rotected function get_wordcamp_posts() {249 public function get_event_posts() { 339 250 $post_args = array( 340 251 'post_type' => WCPT_POST_TYPE_ID, … … 361 272 } 362 273 363 if ( ! empty( $this-> wordcamp_ids ) ) {364 $post_args['post__in'] = $this-> wordcamp_ids;274 if ( ! empty( $this->event_ids ) ) { 275 $post_args['post__in'] = $this->event_ids; 365 276 } 366 277 … … 373 284 374 285 /** 375 * Get the values of all the relevant post meta keys for a WordCamp post.376 *377 * @param WP_Post $wordcamp378 *379 * @return array380 */381 protected function fill_data_row( WP_Post $wordcamp ) {382 $meta_keys = $this->get_meta_keys();383 384 $row = [385 'ID' => $wordcamp->ID,386 'Name' => $wordcamp->post_title,387 'Created' => get_the_date( 'Y-m-d', $wordcamp->ID ),388 'Status' => $wordcamp->post_status,389 ];390 391 if ( $this->include_counts ) {392 $row = array_merge( $row, $this->get_counts( $wordcamp ) );393 }394 395 foreach ( $meta_keys as $key ) {396 $row[ $key ] = get_post_meta( $wordcamp->ID, $key, true ) ?: '';397 }398 399 return $row;400 }401 402 /**403 286 * Get a list of all the relevant meta keys for WordCamp posts. 404 287 * 405 288 * @return array 406 289 */ 407 p rotectedfunction get_meta_keys() {290 public function get_meta_keys() { 408 291 /* @var WordCamp_Admin $wordcamp_admin */ 409 292 global $wordcamp_admin; … … 420 303 } 421 304 305 public function fill_data_row( $event ) { 306 $row = parent::fill_data_row( $event ); 307 308 if ( $this->include_counts ) { 309 $row = array_merge( $row, $this->get_counts( $event ) ); 310 } 311 312 return $row; 313 } 314 315 /** 316 * Create an object of this class with relevant requirements passed to constructor 317 * 318 * @param string $context Can be 'public' or 'private' 319 * 320 * @return Base_Details 321 */ 322 static public function create_shadow_report_obj( $context ) { 323 return new self( null, null, false, ['public' => 'public' === $context ] ); 324 } 325 326 /** 327 * Render list of fields that can be present in exported CSV. 328 * 329 * @param string $context 330 * @param array $field_defaults 331 */ 332 static public function render_available_fields( $context = 'public', array $field_defaults = [] ) { 333 $shadow_report = self::create_shadow_report_obj( $context ); 334 self::render_available_fields_in_report( $shadow_report, $context, $field_defaults ); 335 } 336 422 337 /** 423 338 * Count the number of various post types for a WordCamp. … … 429 344 * @return array 430 345 */ 431 p rotectedfunction get_counts( WP_Post $wordcamp ) {346 public function get_counts( WP_Post $wordcamp ) { 432 347 $counts = [ 433 348 'Tickets' => 0, … … 473 388 474 389 return $counts; 475 }476 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 values482 * 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 <input498 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 <?php511 }512 513 /**514 * Register all assets used by this report.515 *516 * @return void517 */518 public static function register_assets() {519 wp_register_script(520 self::$slug,521 get_assets_url() . 'js/' . self::$slug . '.js',522 array(),523 filemtime( get_assets_dir_path() . 'js/' . self::$slug . '.js' ),524 true525 );526 527 wp_register_style(528 self::$slug,529 get_assets_url() . 'css/' . self::$slug . '.css',530 array(),531 filemtime( get_assets_dir_path() . 'css/' . self::$slug . '.css' ),532 'screen'533 );534 }535 536 /**537 * Enqueue JS and CSS assets for this report's admin interface.538 *539 * @return void540 */541 public static function enqueue_admin_assets() {542 self::register_assets();543 544 wp_enqueue_script( self::$slug );545 wp_enqueue_style( self::$slug );546 390 } 547 391 … … 582 426 } 583 427 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 } 428 if ( ! wp_verify_nonce( $nonce, 'run-report' ) ) { 429 return; 430 } 431 432 if ( ! current_user_can( 'manage_network' ) ) { 433 return; 434 } 435 436 $error = null; 437 $range = null; 438 439 if ( $start_date || $end_date ) { 440 try { 441 $range = validate_date_range( $start_date, $end_date, [ 442 'allow_future_start' => true, 443 'earliest_start' => new DateTime( '2006-01-01' ), // No WordCamp posts before 2006., 444 ] ); 445 } catch ( Exception $e ) { 446 $error = new WP_Error( 447 self::$slug . '-date-range-error', 448 $e->getMessage() 449 ); 600 450 } 601 602 $include_counts = false; 603 if ( ! empty( array_intersect( $fields, [ 'Tickets', 'Speakers', 'Sponsors', 'Organizers' ] ) ) ) { 604 $include_counts = true; 605 } 606 607 // The "Name" field should always be included, but does not get submitted because the input is disabled, 608 // so add it in here. 609 $fields[] = 'Name'; 610 611 $options = array( 612 'fields' => $fields, 613 'public' => false, 614 ); 615 616 $report = new self( $range, [], $include_counts, $options ); 617 618 $filename = [ $report::$name ]; 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' ); 622 } 623 if ( $report->include_counts ) { 624 $filename[] = 'include-counts'; 625 } 626 627 $data = $report->prepare_data_for_display( $report->get_data() ); 628 629 $headers = ( ! empty( $data ) ) ? array_keys( $data[0] ) : []; 630 631 $exporter = new Export_CSV( array( 632 'filename' => $filename, 633 'headers' => $headers, 634 'data' => $data, 635 ) ); 636 637 if ( ! empty( $report->error->get_error_messages() ) ) { 638 $exporter->error = $report->merge_errors( $report->error, $exporter->error ); 639 } 640 641 if ( $error instanceof WP_Error ) { 642 $exporter->error = $report->merge_errors( $error, $exporter->error ); 643 } 644 645 $exporter->emit_file(); 646 } // End if(). 647 } 451 } 452 453 $include_counts = false; 454 if ( ! empty( array_intersect( $fields, [ 'Tickets', 'Speakers', 'Sponsors', 'Organizers' ] ) ) ) { 455 $include_counts = true; 456 } 457 458 // The "Name" field should always be included, but does not get submitted because the input is disabled, 459 // so add it in here. 460 $fields[] = 'Name'; 461 462 $options = array( 463 'fields' => $fields, 464 'public' => false, 465 ); 466 467 468 $report = new self( $range, null, $include_counts, $options ); 469 470 self::export_to_file_common( $report ); 471 } 472 648 473 } -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/classes/report/class-wordcamp-status.php
r7588 r7734 10 10 use DateTime; 11 11 use WP_Error, WP_Post; 12 use function WordCamp\Reports\{get_ assets_url, get_assets_dir_path, get_views_dir_path};12 use function WordCamp\Reports\{get_views_dir_path}; 13 13 use WordCamp\Reports\Utility\Date_Range; 14 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};15 use function WordCamp\Reports\Time\{year_array, quarter_array, month_array}; 16 16 use WordCamp_Loader; 17 17 use WordCamp\Utilities\Export_CSV; … … 24 24 * @package WordCamp\Reports\Report 25 25 */ 26 class WordCamp_Status extends Base {26 class WordCamp_Status extends Base_Status { 27 27 /** 28 28 * Report name. … … 223 223 224 224 $latest_log = end( $logs ); 225 $latest_status = $this->get_log_status_result( $latest_log );225 $latest_status = $this->get_log_status_result( $latest_log, WordCamp_Loader::get_post_statuses() ); 226 226 reset( $logs ); 227 227 … … 349 349 * @return array 350 350 */ 351 protected function get_wordcamp_status_logs( WP_Post $wordcamp ) { 352 $log_entries = get_post_meta( $wordcamp->ID, '_status_change' ); 353 354 if ( ! empty( $log_entries ) ) { 355 // Sort log entries in chronological order. 356 usort( $log_entries, function( $a, $b ) { 357 if ( $a['timestamp'] === $b['timestamp'] ) { 358 return 0; 359 } 360 361 return ( $a['timestamp'] > $b['timestamp'] ) ? 1 : -1; 362 } ); 363 364 return $log_entries; 365 } 366 367 return array(); 368 } 369 370 /** 371 * Determine the ending status of a particular status change event. 372 * 373 * E.g. for this event: 374 * 375 * Needs Vetting → More Info Requested 376 * 377 * The ending status would be "More Info Requested". 378 * 379 * @param array $log_entry A status change log entry. 380 * 381 * @return string 382 */ 383 protected function get_log_status_result( $log_entry ) { 384 if ( isset( $log_entry['message'] ) ) { 385 $pieces = explode( ' → ', $log_entry['message'] ); 386 387 if ( isset( $pieces[1] ) ) { 388 return $this->get_status_id_from_name( $pieces[1] ); 389 } 390 } 391 392 return ''; 393 } 394 395 /** 396 * Given the ID of a WordCamp status, determine the ID string. 397 * 398 * @param string $status_name A WordCamp status name. 399 * 400 * @return string 401 */ 402 protected function get_status_id_from_name( $status_name ) { 403 $statuses = array_flip( WordCamp_Loader::get_post_statuses() ); 404 405 if ( isset( $statuses[ $status_name ] ) ) { 406 return $statuses[ $status_name ]; 407 } 408 409 return ''; 351 protected function get_wordcamp_status_logs( \WP_Post $wordcamp ) { 352 return $this->sort_logs( get_post_meta( $wordcamp->ID, '_status_change' ) ); 410 353 } 411 354 … … 447 390 448 391 /** 449 * Register all assets used by this report. 392 * Determine whether to render the public report form. 393 * 394 * This shortcode is limited to use on pages. 395 * 396 * @return string HTML content to display shortcode. 397 */ 398 public static function handle_shortcode() { 399 $html = ''; 400 401 if ( 'page' === get_post_type() ) { 402 self::register_assets(); 403 404 wp_enqueue_style( 'select2' ); 405 wp_enqueue_script( self::$slug ); 406 407 ob_start(); 408 self::render_public_page(); 409 $html = ob_get_clean(); 410 } 411 412 return $html; 413 } 414 415 /** 416 * Render the page for this report on the front end. 450 417 * 451 418 * @return void 452 419 */ 453 protected static function register_assets() { 454 wp_register_script( 455 self::$slug, 456 get_assets_url() . 'js/' . self::$slug . '.js', 457 array( 'jquery', 'select2' ), 458 filemtime( get_assets_dir_path() . 'js/' . self::$slug . '.js' ), 459 true 460 ); 461 462 wp_register_style( 463 self::$slug, 464 get_assets_url() . 'css/' . self::$slug . '.css', 465 array( 'select2' ), 466 filemtime( get_assets_dir_path() . 'css/' . self::$slug . '.css' ), 467 'screen' 468 ); 469 } 470 471 /** 472 * Enqueue JS and CSS assets for this report's admin interface. 473 * 474 * @return void 475 */ 476 public static function enqueue_admin_assets() { 477 self::register_assets(); 478 WordCamp_Details::register_assets(); 479 480 wp_enqueue_style( WordCamp_Details::$slug ); 481 wp_enqueue_script( self::$slug ); 482 wp_enqueue_style( self::$slug ); 420 public static function render_public_page() { 421 $params = self::parse_public_report_input(); 422 $years = year_array( absint( date( 'Y' ) ), 2015 ); 423 $quarters = quarter_array(); 424 $months = month_array(); 425 $statuses = WordCamp_Loader::get_post_statuses(); 426 427 $error = $params['error']; 428 $report = null; 429 $period = $params['period']; 430 $year = $params['year']; 431 $status = $params['status']; 432 if ( ! empty( $params ) && isset( $params['range'] ) ) { 433 $report = new self( $params['range']->start, $params['range']->end, $params['status'], $params['options'] ); 434 } 435 436 include get_views_dir_path() . 'public/wordcamp-status.php'; 483 437 } 484 438 … … 619 573 } 620 574 621 /** 622 * Determine whether to render the public report form. 623 * 624 * This shortcode is limited to use on pages. 625 * 626 * @return string HTML content to display shortcode. 627 */ 628 public static function handle_shortcode() { 629 $html = ''; 630 631 if ( 'page' === get_post_type() ) { 632 self::register_assets(); 633 634 wp_enqueue_style( 'select2' ); 635 wp_enqueue_script( self::$slug ); 636 637 ob_start(); 638 self::render_public_page(); 639 $html = ob_get_clean(); 640 } 641 642 return $html; 643 } 644 645 /** 646 * Render the page for this report on the front end. 647 * 648 * @return void 649 */ 650 public static function render_public_page() { 651 // Apparently 'year' is a reserved URL parameter on the front end, so we prepend 'report-'. 652 $year = filter_input( INPUT_GET, 'report-year', FILTER_VALIDATE_INT ); 653 $period = filter_input( INPUT_GET, 'period' ); 654 $status = filter_input( INPUT_GET, 'status' ); 655 $action = filter_input( INPUT_GET, 'action' ); 656 657 $years = year_array( absint( date( 'Y' ) ), 2015 ); 658 $quarters = quarter_array(); 659 $months = month_array(); 660 $statuses = WordCamp_Loader::get_post_statuses(); 661 662 if ( ! $year ) { 663 $year = absint( date( 'Y' ) ); 664 } 665 666 if ( ! $period ) { 667 $period = absint( date( 'm' ) ); 668 } 669 670 $report = null; 671 672 if ( 'Show results' === $action ) { 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 } 681 682 $options = array( 683 'earliest_start' => new DateTime( '2015-01-01' ), // No status log data before 2015. 684 ); 685 686 $report = new self( $range->start, $range->end, $status, $options ); 687 } 688 689 include get_views_dir_path() . 'public/wordcamp-status.php'; 690 } 575 static public function get_report_object( $date_range, $status, $options ) { 576 return new self( $date_range->start, $date_range->end, $status, $options ); 577 } 578 691 579 } -
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/index.php
r7588 r7734 139 139 __NAMESPACE__ . '\Report\Meetup_Events', 140 140 __NAMESPACE__ . '\Report\WordCamp_Payment_Methods', 141 __NAMESPACE__ . '\Report\Meetup_Status', 142 __NAMESPACE__ . '\Report\Meetup_Details', 141 143 ); 142 144 }
Note: See TracChangeset
for help on using the changeset viewer.