Making WordPress.org


Ignore:
Timestamp:
10/16/2018 05:30:06 PM (7 years ago)
Author:
vedjain
Message:

WordCamp Report: Add reporting for meetup applications.

This commit adds functionality for downloading meetup application reports from admin dashboard. Also refactors WordCamp report classes
to make code DRY.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-reports/classes/report/class-wordcamp-details.php

    r7588 r7734  
    99use Exception;
    1010use 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};
     11use WP_Post, WP_Query;
     12use function WordCamp\Reports\{get_views_dir_path};
    1313use WordCamp\Reports\Utility\Date_Range;
    1414use function WordCamp\Reports\Validation\{validate_date_range, validate_wordcamp_id};
    1515use WordCamp_Admin, WordCamp_Loader;
    16 use WordCamp\Utilities\Export_CSV;
    1716
    1817/**
     
    2625 * @package WordCamp\Reports\Report
    2726 */
    28 class WordCamp_Details extends Base {
     27class WordCamp_Details extends Base_Details {
    2928    /**
    3029     * Report name.
     
    6968
    7069    /**
    71      * A date range that WordCamp events must fall within.
    72      *
    73      * @var null|Date_Range
    74      */
    75     public $range = null;
    76 
    77     /**
    78      * A list of WordCamp post IDs.
    79      *
    80      * @var array
    81      */
    82     public $wordcamp_ids = [];
    83 
    84     /**
    8570     * Whether to include counts of various post types for each WordCamp.
    8671     *
     
    8873     */
    8974    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 = [];
    10475
    10576    /**
     
    11889     * }
    11990     */
    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 = [] ) {
    12192        // 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 );
    14598        }
    14699
    147100        $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(
    150110            [
    151111                'Name',
     
    153113            WordCamp_Loader::get_public_meta_keys()
    154114        );
    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(
    158124            [
    159125                'ID',
     
    165131                'Organizers',
    166132            ],
    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() ) )
    168134        );
    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                }
    199155            }
    200156        }
    201 
    202         return $fields;
    203     }
    204 
    205     /**
    206      * Query and parse the data for the report.
    207      *
    208      * @return array
    209      */
    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 array
    243      */
    244     public function compile_report_data( array $data ) {
    245         return $data;
    246157    }
    247158
     
    251162     * @return array
    252163     */
    253     protected static function get_field_order() {
     164    public static function get_field_order() {
    254165        /* @var WordCamp_Admin $wordcamp_admin */
    255166        global $wordcamp_admin;
     
    336247     * @return array An array of WP_Post objects.
    337248     */
    338     protected function get_wordcamp_posts() {
     249    public function get_event_posts() {
    339250        $post_args = array(
    340251            'post_type'           => WCPT_POST_TYPE_ID,
     
    361272        }
    362273
    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;
    365276        }
    366277
     
    373284
    374285    /**
    375      * Get the values of all the relevant post meta keys for a WordCamp post.
    376      *
    377      * @param WP_Post $wordcamp
    378      *
    379      * @return array
    380      */
    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     /**
    403286     * Get a list of all the relevant meta keys for WordCamp posts.
    404287     *
    405288     * @return array
    406289     */
    407     protected function get_meta_keys() {
     290    public function get_meta_keys() {
    408291        /* @var WordCamp_Admin $wordcamp_admin */
    409292        global $wordcamp_admin;
     
    420303    }
    421304
     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
    422337    /**
    423338     * Count the number of various post types for a WordCamp.
     
    429344     * @return array
    430345     */
    431     protected function get_counts( WP_Post $wordcamp ) {
     346    public function get_counts( WP_Post $wordcamp ) {
    432347        $counts = [
    433348            'Tickets'    => 0,
     
    473388
    474389        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 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     /**
    514      * Register all assets used by this report.
    515      *
    516      * @return void
    517      */
    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             true
    525         );
    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 void
    540      */
    541     public static function enqueue_admin_assets() {
    542         self::register_assets();
    543 
    544         wp_enqueue_script( self::$slug );
    545         wp_enqueue_style( self::$slug );
    546390    }
    547391
     
    582426        }
    583427
    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                );
    600450            }
    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
    648473}
Note: See TracChangeset for help on using the changeset viewer.