| Server IP : 172.67.201.108 / Your IP : 216.73.216.11 Web Server : Apache/2.4.68 (Amazon Linux) OpenSSL/3.5.5 System : Linux ip-172-31-69-123.ec2.internal 6.1.176-223.369.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Fri Jul 24 13:34:27 UTC 2026 x86_64 User : ec2-user ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/tgnew/wp-content/plugins/events-manager/classes/ |
Upload File : |
<?php
namespace EM {
use WP_List_Table;
// WP_List_Table is not loaded automatically so we need to load it in our application
if( ! class_exists( 'WP_List_Table' ) ) {
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}
/**
* Create a new table class that will extend the WP_List_Table
*/
class List_Table extends WP_List_Table {
/**
* Results of given table search.
* @var array[]|\EM_Object[]
*/
public $items = array();
/**
* array containing the columns to display, the keys and values would be the column key, information about the column is referred to in $cols_template
* @var array
*/
public $cols = array();
/**
* Asoociative array of available collumn keys and corresponding headers, which will be used to display this table of items
* @var array
*/
public $cols_template = array();
public $cols_template_groups = array();
public static $cols_allowed_html = array();
/**
* Object context we're viewing bookings in relation to, such as events, locations, tickets, etc. used for saving view settings like columns and determining what data to show
* @var string
*/
public $context;
public $context_views = array();
/**
* Link/Data for available actions on each row for this table. Populated by self::get_action_data() method.
* @var array
*/
public $action_data = array();
public static $basename = 'em_list_table';
public static $template_component_name = 'list-table';
public static $export_action = 'em_list_table_export';
public static $export_delimiter = ',';
/**
* Class is in the process of exporting when true. Assume that nonce has been verified once set to true.
* @var bool
*/
public static $exporting = false;
/**
* Added to the form wrapping the actual list table, use this for unique class names
* @var string
*/
public static $form_class = '';
/**
* Set to the base name used for a class or unique ID
* @var string
*/
public $id = 'em-list-table';
/**
* Set to a unique id made up of $this->id dash rand() int during __construct
* @var string
*/
public $uid = 'em-list-table-0';
/**
* Maximum number of rows to show
* @var int
*/
public $limit = 20;
public $order = 'ASC';
/**
* Field to order by default, overriding classes must set this!
* @var string
*/
public $orderby;
public $page = 1;
public $offset = 0;
public $filters = array();
public $per_page = 20;
public $total_items = 0;
public $per_page_var = 'limit';
/**
* Destination output this is intended for, for example html is default, but could be csv, excel, etc.
* @var string
*/
public $format = 'html';
/**
* Does this table have checkboxes and, therefore, are bulk actions available? If this is overriden with an id (booking_id, event_id etc.) then it is assumed checkboxes and bulk actions are available.
* @var bool
*/
public $checkbox_id = false;
/**
* Whether results can be exported.
* @var bool
*/
public static $is_exportable = true;
/**
* Flag for whether this is a frontend or backend table display.
* @var bool
*/
public static $is_frontend = null;
/**
* Set to true if current class has filters by extending the extra_tablenav() base method. This allows for adding classes to table without using things like ReflectionClass to figure it out.
* @var bool
*/
public static $has_filters = false;
/**
* If there are filters, child classes can add the post values here and they can be automatically saved in the settings page, otherwise handle them via the set_default_settings() method.
* @var array
*/
public static $filter_vars = [
'search' => [
'param' => 'em_search',
'default' => '',
],
'scope' => [ 'default' => 'future' ]
];
/**
* Show or hide filters by default
* @var bool
*/
public static $show_filters = true;
/**
* Show or prevent repeating tablenav at bottom of table, including pagination
* @var bool
*/
public static $show_bottom_tablenav = true;
/**
* Show or prevent repeating tablenav actions at bottom of table, such as filters and bulk actions. If set to false, bulk actions and filters are ommitted regardless of other settings.
* @var bool
*/
public static $show_bottom_tablenav_actions = false;
/**
* Show or prevent repeating bulk actions at bottom of table, including settings, export, show/hide filter and expand/collapse (responsive) icons.
* @var bool
*/
public static $show_bottom_tablenav_bulkactions = true;
/**
* Show or prevent repeating the filters at bottom of table.
* @var bool
*/
public static $show_bottom_tablenav_extra_tablenav = true;
/**
* Show or prevent repeating the pagination at bottom of table.
* @var bool
*/
public static $show_bottom_tablenav_pagination = true;
/*
* Show or hide responsive meta line below the primary column line when in responsive mode
*/
public static $show_responsive_meta = true;
public function __construct ( $args = array() ) {
// unique ID
$this->uid = $this->id . '-' . rand(1,99999);
// load settings, then let query data override
$this->load_current_context();
//Set basic vars
$this->order = ( !empty($_REQUEST['order']) && ($_REQUEST['order'] == 'DESC' || $_REQUEST['order'] == 'desc') ) ? 'DESC':'ASC';
$this->orderby = ( !empty($_REQUEST['orderby']) ) ? sanitize_sql_orderby($_REQUEST['orderby']): $this->orderby;
if( defined('DOING_AJAX') ) {
$_GET['order'] = strtolower($this->order); // for WP_List_Table
$_GET['orderby'] = $this->orderby;
}
$this->limit = ( !empty($_REQUEST['limit']) && is_numeric($_REQUEST['limit'])) ? $_REQUEST['limit'] : $this->limit;//Default limit
$this->page = ( !empty($_REQUEST['pno']) && is_numeric($_REQUEST['pno']) ) ? $_REQUEST['pno']:1;
$_REQUEST['paged'] = $this->page;
$this->offset = ( $this->page > 1 ) ? ($this->page-1)*$this->limit : 0;
// Basic Vars for List_Table
$this->per_page = $this->limit;
em_list_table_create_funcitions();
// load the columns now
$this->load_columns();
//calculate collumns if post requests
if( !empty($_REQUEST['cols']) ){
$this->cols = array();
if( is_array($_REQUEST['cols']) ){
$cols = $_REQUEST['cols'];
}else{
$cols = explode(',',$_REQUEST['cols']);
}
foreach( $cols as $col ){
if( array_key_exists($col, $this->cols_template) ){
$this->cols[$col] = $col;
}
}
}
//save collumns depending on context and user preferences
if( !empty($_REQUEST['cols']) && !empty($_REQUEST['save']) && wp_verify_nonce($_REQUEST['_emnonce'], static::$basename) ){ //save view settings for next time
$settings = $this->set_default_settings();
$this->save_default_settings( $settings );
}
// parent
parent::__construct( $args );
$this->constructor();
// do action
do_action( static::$basename, $this );
// clean any columns from saved views that no longer exist - at this point they shoudl have been added
foreach($this->cols as $col_key => $col_name){
if( !is_string($col_name) || !array_key_exists($col_name, $this->cols_template)){
unset($this->cols[$col_key]);
}
}
}
/**
* Fired after __constructor(), so that child classes can overwrite this method and do more just before the static::$basename action is fired.
* @return void
*/
public function constructor(){}
public function load_columns(){
_doing_it_wrong( __FUNCTION__, 'This method must be overwritten in child classes', '6.4.11' );
}
public function __get( $prop ) {
if ( $prop === 'cols_view' ) {
return $this->context;
}
return parent::__get( $prop );
}
public function __set( $prop, $value ) {
if ( $prop === 'cols_view' ) {
$this->context = $value;
}
parent::__set( $prop, $value );
}
public function __isset ( $prop ) {
if ( $prop === 'cols_view' ) {
return isset($this->context);
}
return parent::__isset( $prop );
}
/**
* Loads the current context and writes the current settings into the object.
*
* Child classes should overwrite this method to set the context first, or do it before this function is executed in the constructor.
*
* @return mixed|null
*/
public function load_current_context() {
// get the actual context
$settings = $this->get_current_context();
// set things like limit, cols, etc.
$this->cols = $settings['cols'] ?? $this->cols;
$this->limit = $settings['limit'] ?? $this->limit;
$this->filters = $settings['filters'] ?? $this->filters;
$this->orderby = $settings['orderby'] ?? $this->orderby;
// set default filters - child classes could set them here or after this parent constructor is called
foreach ( static::$filter_vars as $filter_key => $filter_vars ) {
$default = false;
if( is_array($filter_vars) ) {
$filter_var = $filter_vars['param'] ?? $filter_key; // allows for further expansion such as special filtering etc.
$default = $filter_vars['default'] ?? false;
if( !empty($filter_vars['in_array']) ) {
$in_array = $filter_vars['in_array'];
}
} else {
if ( is_int($filter_key) ) {
$filter_key = $filter_vars;
}
$filter_var = $filter_vars;
}
if( isset($_REQUEST[$filter_var]) ) {
static::$show_filters = true;
}
if ( isset($_REQUEST[$filter_var]) ) {
// check in_array validation
if( !empty($in_array) ) {
if( is_array($in_array) && in_array($_REQUEST[$filter_var], $in_array) ) {
$this->filters[$filter_key] = $_REQUEST[$filter_var];
} elseif ( !empty($this->{$in_array}) && in_array($_REQUEST[$filter_var], $this->{$in_array}) ) {
$this->filters[$filter_key] = $_REQUEST[$filter_var];
}
} elseif( !empty($array_key) ) {
if ( !empty($this->{$array_key}) && array_key_exists($_REQUEST[$filter_var], $this->{$in_array}) ) {
$this->filters[$filter_key] = $_REQUEST[$filter_var];
} elseif ( is_array($array_key) && array_key_exists($_REQUEST[$filter_var], $array_key) ) {
$this->filters[$filter_key] = $_REQUEST[$filter_var];
}
} else {
$this->filters[$filter_key] = sanitize_text_field($_REQUEST[$filter_var]);
}
}
$this->filters[$filter_key] = $this->filters[$filter_key] ?? $default;
}
return apply_filters( static::$basename . 'load_current_context', $settings, $this);
}
public function get_current_context() {
$default_settings = $this->get_default_settings();
if ( $this->context ) {
if ( !empty($default_settings['contexts'][$this->context]) ) {
$settings = $default_settings['contexts'][$this->context];
} else {
$settings = $default_settings;
}
$settings['context'] = $this->context;
} else {
$settings = $default_settings;
}
if( empty($settings['cols']) || !is_array($settings['cols']) ){
// check if we have template views in place
$context_default = $this->get_current_context_default();
$default_cols = $context_default['cols'] ?? $this->cols;
// load default cols
$settings = array(
'cols' => $default_cols,
);
}
// merge current settings into default settings without contexts, so base settings fill out any missing settings (if set)
$current_context = static::merge_settings( $default_settings, $settings );
unset( $current_context['contexts'] );
return apply_filters( 'em_list_table_get_current_context', $current_context, $this);
}
public function get_current_context_default() {
if( $this->context && !empty( $this->context_views[ $this->context ] ) ) {
return $this->context_views[$this->context];
}
return false;
}
public function get_default_settings() {
$settings = get_user_meta(get_current_user_id(), static::$basename . '_settings', true );
if( !is_array($settings) ) $settings = array();
return apply_filters( static::$basename . '_get_default_settings', $settings, $this);
}
/**
* Sets default settings for this table under the current context. If $settings is provided, it is considered the context to be saved under.
* Otherwise, the default context settings will be loaded and overwritten with currently loaded settings.
*
* @param $settings
*
* @return array
*/
public function set_default_settings( $settings = array() ) {
if ( empty($settings) ) $settings = $this->get_current_context();
$settings['cols'] = $this->cols;
$settings['limit'] = $this->limit;
// if filters are also to be saved, do it here
if( !empty($_REQUEST['save_filters']) ){
// go through filters array and save stuff here
$filters = array();
foreach ( static::$filter_vars as $filter_key => $filter_var ) {
if( is_array($filter_var) ) {
$filter_var = $filter_var['param'] ?? $filter_key; // allows for further expansion such as special filtering etc.
} elseif ( is_int($filter_key) ) {
$filter_key = $filter_var;
}
if( isset($_REQUEST[$filter_var]) ) {
$filters[$filter_key] = wp_kses_post_deep($_REQUEST[$filter_var]);
}
}
if( !empty($filters) ) {
$settings['filters'] = $filters;
} else {
unset( $settings['filters'] );
}
// save orderby as well
if( $this->orderby ) {
$settings['orderby'] = $this->orderby;
} else {
unset( $settings['orderby'] );
}
}
return $settings;
}
public function save_default_settings( $current_settings ) {
$settings = $this->get_default_settings();
if ( !empty($current_settings['context']) ) {
// we're in a context, so we add it to settings as a context
if ( empty($settings['contexts'] ) ) {
$settings['contexts'] = array();
}
$context = $current_settings['context'];
$context_settings = $settings['contexts'][$context] ?? array();
$current_settings = static::merge_settings( $context_settings, $current_settings );
$current_settings = apply_filters( static::$basename . '_save_default_settings_current', $current_settings, $this );
unset( $context_settings['contexts'], $current_settings['contexts'], $current_settings['context'] ); // delete any errant or unecessary data
$settings['contexts'][$context] = $current_settings;
} else {
$context = false;
$current_settings = apply_filters( static::$basename . '_save_default_settings_current', $current_settings, $this );
$settings = static::merge_settings( $settings, $current_settings );
}
$settings = apply_filters( static::$basename . '_save_default_settings_pre', $settings, $current_settings, $this, ['context' => $context] );
update_user_meta(get_current_user_id(), static::$basename . '_settings', $settings );
do_action( static::$basename . '_save_default_settings', $settings, $current_settings, $this, ['context' => $context] );
return $settings;
}
/**
* Merges two settings array together, by either replacing the key value in $default with $settings, or merging within the arrays by replacing equivalent keys in $settings.
* The only exception is the cols key which is overwritten by $settings if it exists.
*
* @param $default
* @param $settings
*
* @return mixed
*/
public static function merge_settings ( $default, $settings ) {
foreach( $settings as $key => $value ) {
if ( is_array($value) ) {
// anything but cols gets merged in joining values
if ( $key !== 'cols' && !empty($default[$key]) && is_array($default[$key]) ) {
$default[$key] = static::merge_settings( $default[$key], $value );
} else {
$default[$key] = $value;
}
} else {
$default[$key] = $value;
}
}
return $default;
}
public static function get_item_limits() {
$limits = [ 5, 10, 20, 50, 100 ];
return apply_filters( static::$basename . '_get_item_limits', $limits );
}
/* ------------------ Static and AJAX Methods ------------------ */
public static function init(){
add_action('wp_ajax_'. static::$basename, array( static::class, 'em_ajax_table') );
add_action('wp_ajax_'. static::$basename .'_row', array( static::class, 'em_ajax_table_row') );
if ( static::$is_exportable ) {
add_action( 'wp_loaded', array( static::class, 'export_init' ) );
}
// determine if we're public or not, can't determine that when doing AJAX
if( static::$is_frontend === null ){
static::$is_frontend = !is_admin() || !empty($_REQUEST['is_public']);
}
}
/**
* Handles AJAX Bookings admin table filtering, view changes and pagination
*/
public static function em_ajax_table(){
if( !empty($_REQUEST['_emnonce']) && check_admin_referer( static::$basename, '_emnonce') ){
$class_name = static::class;
$EM_List_Table = new $class_name();
$EM_List_Table->display();
}else{
check_admin_referer(static::$basename);
$class_name = static::class;
$EM_List_Table = new $class_name();
if( !empty($_REQUEST['table_id']) ) { // so modals work linked to the ID
$EM_List_Table->uid = $EM_List_Table->id . '-' . absint($_REQUEST['table_id']);
}
$EM_List_Table->output_table();
}
exit();
}
public static function em_ajax_table_row(){
if( !empty($_REQUEST['_emnonce']) && check_admin_referer(static::$basename, '_emnonce') && !empty($_REQUEST['row_action']) ){
static::em_ajax_table_row_action( $_REQUEST['row_action'] );
}
exit();
}
public static function em_ajax_table_row_action( $requested_action ){}
/* ------------------ Bulk and Row action links/options ------------------ */
public function get_action_url( $item ) {
return esc_url_raw ( add_query_arg( array( 'action' => static::$basename . '_row', 'row_id' => $item->ID ) ) );
}
public function get_action_data() {
if( !empty($this->action_data) ) return $this->action_data;
$actions = $this->get_action_data_items();
// add confirmation messages context
foreach( $actions as $action => $action_data ) {
// get link confirmation messages
if( isset($action_data['actions']) ) {
// sub-array of actions
foreach( $action_data['actions'] as $the_action => $the_action_data ) {
// get bulk and link confirmation messages - confirmation messages according to context on each action
$actions[ $action ]['actions'][ $the_action ]['confirm'] = $this->get_action_message( $the_action, $the_action_data['data']['context'] );
$actions[ $action ]['actions'][ $the_action ]['confirm-bulk'] = $this->get_bulk_action_message( $the_action, $the_action_data['data']['context'] );
}
} else {
$actions[ $action ]['confirm'] = $this->get_action_message( $action, $action_data['data']['context'] );
$actions[ $action ]['confirm-bulk'] = $this->get_bulk_action_message( $action, $action_data['data']['context'] );
}
}
$this->action_data = apply_filters( static::$basename . '_get_action_data', $actions, $this );
return $this->action_data;
}
public function get_action_data_items() {
$data_template = [ 'action' => static::$basename . '_row', 'context' => $this->context ];
$actions = [
'delete' => [
'label' => __('Delete','events-manager'),
'data' => array_merge( $data_template, [ 'row_action' => 'delete' ] ),
],
];
return apply_filters( 'em_list_table_get_action_data_items', $actions, $this, ['data_template' => $data_template] );
}
/**
* Gets action data and appends extra data to the 'data' of each action.
* @param array $extra_data
*
* @return array
*/
public function get_action_data_extra( $extra_data ) {
$actions = $this->get_action_data();
foreach( $actions as $action => $action_data ) {
if ( isset($action_data['actions']) ) {
// go one level deeper
foreach( $action_data['actions'] as $the_action => $the_action_data ) {
$actions[$action]['actions'][$the_action]['data'] = array_merge( $the_action_data['data'], $extra_data );
}
} else {
$actions[$action]['data'] = array_merge( $actions[$action]['data'], $extra_data );
}
}
return apply_filters( 'em_list_table_get_action_data_extra', $actions, $this, ['extra_data' => $extra_data] );;
}
public function get_action_message( $action, $context ) {
if( $action === 'delete' ) {
$message = __("Are you sure you want to delete? This action cannot be undone.",'events-manager');
}
return apply_filters( 'em_list_table_get_action_message', $message ?? false, $this );
}
public function get_bulk_action_message( $action, $context ) {
if ( $action === 'delete' ) {
$message = __( "Are you sure you want to delete this item? This action cannot be undone.", 'events-manager' );
}
return apply_filters( 'em_list_table_get_bulk_action_message', $message ?? '', $this );
}
/**
* Generate a list of booking action links
*
* @param $item
*
* @return array
*/
public function get_action_links( $item ) {
$action_data = $this->get_action_data();
$url = $this->get_action_url( $item );
$booking_actions = $this->build_action_link( $action_data, $url );
return apply_filters(static::$basename . '_cols_col_action', $booking_actions, $item, $this);
}
public function build_action_link ( $data, $url = '' ) {
$action_links = array();
foreach( $data as $action => $action_data ) {
if( !empty($action_data['actions']) ) {
$action_links[$action] = [
'actions' => $this->build_action_link( $action_data['actions'], $url ),
'label' => $action['label'] ?? '',
];
} else {
$data = array_merge( $action_data['data'], ['nonce' => wp_create_nonce( $action_data['data']['action'] ?? $action )] );
$attributes = array();
foreach( $data as $att => $att_val ) {
$attributes[] = 'data-' . esc_html($att) . '="' . esc_attr($att_val) . '"';
}
$action_links[$action] = '<a class="em-list-table-row-action" href="'.em_add_get_params($url, $action_data['data']).'"'. implode(' ', $attributes) .'>'.$action_data['label'].'</a>';
}
}
return $action_links;
}
public function get_bulk_actions() {
$bulk_actions = $this->get_action_data();
return apply_filters(static::$basename . '_get_bulk_actions', $bulk_actions, $this);
}
public function output_action_links ( $action_links ) {
ob_start();
foreach( $action_links as $action => $action_link ) {
if( isset($action_link['actions']) ) {
echo '<section class="'. esc_attr($action) .'">';
echo $this->output_action_links( $action_link['actions'] );
echo '</section>';
} else {
echo $action_link;
}
}
return ob_get_clean();
}
/* ------------------ Table Data ------------------ */
public static function sanitize_spreadsheet_cell( $cell ){
if ( !empty($cell) || is_numeric($cell) ) {
if ( is_array($cell) ) {
$cell = implode( ',', $cell );
}
if ( !is_array( $cell ) ) {
$cell = html_entity_decode( $cell ); //remove things like & which may have been saved to the DB directly
return preg_replace( '/^([;=@\+\-])/', "'$1", $cell );
}
}
return '';
}
/**
* Prepare the items for the table to process
*
* @return Void
*/
public function prepare_items(){
$columns = $this->get_columns();
// wrap column data up in case we have html in them
$hidden = $this->get_hidden_columns();
$sortable = $this->get_sortable_columns();
$this->per_page = $this->get_items_per_page( $this->per_page_var, $this->per_page );
$this->items = $this->table_data();
$this->set_pagination_args( array(
'total_items' => $this->total_items,
'per_page' => $this->per_page,
'total_pages' => ceil($this->total_items / $this->per_page),
) );
$this->_column_headers = array( $columns, $hidden, $sortable );
}
protected function get_items_per_page( $option, $default_value = 20 ) {
return $this->limit;
}
/* ------------------ Table Output ------------------ */
/**
* Displays the table
* @return void
*/
public function display(){
do_action(static::$basename . '_header', $this); //won't be overwritten by JS
$this->prepare_items();
$uid = esc_attr($this->uid);
// setup additional classes
$extra_classes = $this->display_classes();
$atts = array();
foreach( $this->display_attributes() as $att => $att_val ) {
$atts[] = esc_html($att) . '="' . esc_attr($att_val) . '"';
}
// get confirmation messages for actions and put it in the form element, to keep the wrapper a little less cluttered
$actions = $this->get_action_data();
$action_messages = [];
foreach ( $actions as $action => $action_data ) {
if( isset($action_data['actions']) ) {
foreach ( $action_data['actions'] as $the_action => $the_action_data ) {
if ( !empty($the_action_data['confirm']) ) {
$action_messages[ $the_action ] = esc_attr( $the_action_data['confirm'] );
}
}
} else {
if ( !empty($action_data['confirm']) ) {
$action_messages[$action] = esc_attr($action_data['confirm']);
}
}
}
$action_messages = apply_filters( static::$basename . '_action_messages', $action_messages, $this );
?>
<div class="em-list-table <?php em_template_classes(static::$template_component_name); echo ' ' . implode(' ', $extra_classes); ?>" id="<?php echo $uid; ?>" data-basename="<?php echo static::$basename; ?>" <?php echo implode(' ', $atts); ?>>
<form class='<?php echo static::$form_class ?> em-list-table-form' method='post' action='' id="<?php echo $uid; ?>-form" data-action-messages='<?php echo esc_attr(json_encode($action_messages)); ?>'>
<input type="hidden" name="is_public" value="<?php echo ( static::$is_frontend ) ? 1:0; ?>">
<input type="hidden" name="pno" value='<?php echo esc_attr($this->page); ?>'>
<input type="hidden" name="order" value='<?php echo esc_attr($this->order); ?>' data-persist>
<input type="hidden" name="orderby" value='<?php echo esc_attr($this->orderby); ?>' data-persist>
<input type="hidden" name="action" value="<?php echo static::$basename; ?>">
<input type="hidden" name="cols" value="<?php if ( !empty($_REQUEST['cols']) ) echo esc_attr(implode(',', $this->cols)); ?>">
<input type="hidden" name="limit" value="<?php echo esc_attr($this->limit); ?>">
<input type="hidden" name="_emnonce" value="<?php echo wp_create_nonce(static::$basename); ?>">
<input type="hidden" name="save" value="0">
<input type="hidden" name="save_filters" value="0">
<input type="hidden" name="table_id" value="<?php echo esc_attr( str_replace($this->id . '-', '', $uid)); ?>">
<?php
$this->display_hidden_input();
parent::display();
?>
</form>
<?php
$this->output_overlays();
?>
</div>
<?php
do_action(static::$basename . '_footer',$this); //won't be overwritten by JS
}
public function display_classes() {
$extra_classes = array();
$extra_classes[] = $this->total_items && $this->checkbox_id ? 'has-checkboxes':'no-checkboxes';
$extra_classes[] = $this->total_items && static::$is_exportable ? 'has-export':'no-export';
$extra_classes[] = static::$has_filters ? 'has-filter':'no-filter';
$extra_classes[] = static::$is_frontend ? 'frontend':'backend';
return $extra_classes;
}
public function display_attributes() {
return array();
}
/**
* Override to output table-specific hidden columns
* @return void
*/
public function display_hidden_input() {}
/**
* Copies and overrides the parent WP_List_Table method.
*
* Adds a wrapper to the top/bottom of the actual table of values, and wraps the tablenav actions in another div.alignleft.actions
*
* This will allow for better responsive reactions with regards to pagination.
*
* @param $which
*
* @return void
*/
protected function display_tablenav( $which ) {
if ( 'top' === $which ) {
wp_nonce_field( 'bulk-' . $this->_args['plural'] );
} else {
echo '</div>';
if ( !self::$show_bottom_tablenav ) return;
}
?>
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<?php if ( 'top' === $which || self::$show_bottom_tablenav_actions ) : ?>
<?php
if ( ( 'top' === $which || self::$show_bottom_tablenav_bulkactions ) && $this->has_items() ) {
?>
<div class="alignleft actions bulkactions">
<?php $this->bulk_actions( $which ); ?>
</div>
<?php
}
if ( ( 'top' === $which || self::$show_bottom_tablenav_extra_tablenav ) ) {
$this->extra_tablenav( $which );
}
?>
<?php endif; ?>
<?php
if( ( 'top' === $which || self::$show_bottom_tablenav_pagination ) ) {
$this->pagination( $which );
}
?>
</div>
<?php
if ( 'top' === $which ) {
echo '<div class="table-wrap">';
}
}
function output_overlays(){
$this->output_overlay_settings();
if( static::$is_exportable ) {
$this->output_overlay_export();
}
}
function output_overlay_export(){
// join all fields into one set of cols, with general taking precendence over events, then tickets then attendees
$cols_template = $this->get_cols_template();
$grouped_fields = array();
foreach( $this->cols_template_groups as $group_data ){
$grouped_fields = array_merge($grouped_fields, $group_data['fields']);
}
$ungrouped_fields = array_diff(array_keys($cols_template), $grouped_fields);
$uid = esc_attr($this->uid);
$id = esc_attr($this->id);
?>
<div class="em pixelbones em-modal em-list-table-export em-list-table-modal" id="<?php echo $uid; ?>-export-modal">
<form id="<?php echo $uid; ?>-export-form" class="em-list-table-form em-list-table-export-form" action="" method="post" rel="#<?php echo $uid . '-form'; ?>">
<div class="em-modal-popup">
<header>
<a class="em-close-modal"></a><!-- close modal -->
<div class="em-modal-title"><?php esc_attr_e('Bookings Table Settings','events-manager'); ?></div>
</header>
<div class="em-modal-content input">
<p><?php esc_html_e('Select the options below and export all the records you have currently filtered (all pages) into a spreadsheet format.','events-manager') ?></p>
<?php do_action( static::$basename . '_export_options', $this); ?>
<?php
static::output_overlay_columns_selection( array(
'text' => array(
'show' => array(
'title' => __('Columns to export','events-manager'),
),
'choose' => array(
'title' => __('Exporter','events-manager'),
)
),
));
?>
</div>
<footer class="em-submit-section input">
<div>
<div class="<?php echo $id; ?>-filters em-list-table-filters" style="display:none; visibility:hidden;"><!-- houses filter data as modified for export --></div>
<input type="hidden" name="no_save" value='1'>
<input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( static::$export_action ); ?>">
<input type="hidden" name="_emnonce" value="<?php echo wp_create_nonce( static::$export_action ); ?>">
<input type="hidden" name="action" value="<?php echo esc_attr(static::$export_action); ?>">
<button type="submit" class="button button-primary"><?php esc_html_e('Export', 'events-manager'); ?></button>
</div>
</footer>
</div>
</form>
</div>
<br class="clear">
<?php
}
function output_overlay_settings(){
$uid = esc_attr($this->uid);
$id = esc_attr($this->id);
?>
<div class="em pixelbones em-modal em-list-table-settings em-list-table-modal" id="<?php echo $uid; ?>-settings-modal">
<form id="<?php echo $uid; ?>-settings-form" class="em-list-table-form em-list-table-settings-form" action="" method="post" rel="#<?php echo $uid . '-form'; ?>">
<div class="em-modal-popup">
<header>
<a class="em-close-modal"></a><!-- close modal -->
<div class="em-modal-title"><?php esc_attr_e('Bookings Table Settings','events-manager'); ?></div>
</header>
<div class="em-modal-content input">
<p><?php _e('Modify what information is displayed in this booking table.','events-manager') ?></p>
<div class="<?php echo $uid; ?>-rows-setting em-list-table-setting">
<label for="<?php echo $uid; ?>-rows-setting"><strong><?php esc_html_e('Results per Page', 'events-manager'); ?></strong></label>
<select name="limit" class="<?php echo $id; ?>-filter" id="<?php echo $uid; ?>-rows-setting">
<?php foreach ( static::get_item_limits() as $limit ) : ?>
<option value="<?php echo esc_attr($limit) ?>" <?php selected($limit, $this->limit); ?>><?php echo esc_html(sprintf(__('%s Rows','events-manager'),$limit)); ?></option>
<?php endforeach; ?>
</select>
</div>
<?php do_action( static::$basename . '_settings_options', $this); ?>
<?php static::output_overlay_columns_selection(); ?>
</div>
<footer class="em-submit-section input">
<div>
<button type="submit" class="button button-primary"><?php esc_html_e('Save Settings', 'events-manager'); ?></button>
</div>
<div class="em-list-table-settings-remember">
<label>
<?php esc_html_e('Remember these settings', 'events-manager'); ?>
<?php $this->output_overlay_settings_remember_tooltip(); ?>
<input type="checkbox" name="save" value="1" checked="checked" data-setting>
</label>
<label>
<?php esc_html_e('Remember filtering options', 'events-manager'); ?>
<?php $this->output_overlay_settings_remember_filters_tooltip(); ?>
<input type="checkbox" name="save_filters" value="1" data-setting>
</label>
</div>
</footer>
</div>
</form>
</div>
<?php
}
public function output_overlay_settings_remember_tooltip() {}
public function output_overlay_settings_remember_filters_tooltip() {
?>
<span class="em-icon em-icon-info s-15 em-tooltip" aria-label="<?php esc_html_e('Your search filter settings will be selected automatically.', 'events-manager'); ?>" data-tippy-maxWidth="250px"></span>
<?php
}
public function output_overlay_columns_selection ( $options = array() ) {
$options = array_replace_recursive( array(
'text' => array(
'show' => array(
'title' => __('Columns to show','events-manager'),
'description' => __('Remove or reorder columns below.','events-manager')
),
'choose' => array(
'title' => __('Columns to choose','events-manager'),
'description' => __('Add to your table from the columns below.','events-manager')
),
),
), $options);
// join all fields into one set of cols, with general taking precendence over events, then tickets then attendees
$cols_template = $this->get_cols_template();
$grouped_field_keys = array();
$grouped_fields = array();
// get the keys of all grouped data, and also get the labels into grouped array so we can order alphabetically now whilst we're at it
foreach( $this->cols_template_groups as $group_key => $group_data ){
$grouped_field_keys = array_merge($grouped_field_keys, $group_data['fields']);
$grouped_fields[$group_key] = [];
foreach( $group_data['fields'] as $col ) {
$grouped_fields[$group_key][$col] = $cols_template[$col]['column_header'] ?? $cols_template[$col];
}
uasort( $grouped_fields[$group_key], function ( $a, $b ){
$col_label_a = $a['label'] ?? $a;
$col_label_b = $b['label'] ?? $b;
return strnatcasecmp($col_label_a, $col_label_b);
});
}
// get ungrouped keys and sort them alphabetically too
$ungrouped_fields = array_diff_key($cols_template, array_combine($grouped_field_keys, $grouped_field_keys));
uasort( $ungrouped_fields, function ( $a, $b ){
$col_label_a = $a['label'] ?? $a;
$col_label_b = $b['label'] ?? $b;
return strnatcasecmp($col_label_a, $col_label_b);
});
?>
<div class="em-list-table-cols">
<div class="em-list-table-cols-selected">
<p>
<strong><?php echo $options['text']['show']['title']; ?></strong><br>
<?php echo $options['text']['show']['description'] ?>
</p>
<div class="em-list-table-cols-sortable">
<?php foreach( $this->cols as $col_key ): ?>
<div class="item" data-value="<?php echo esc_attr($col_key); ?>">
<span>
<?php
$col_header = $this->cols_template[$col_key]['column_header'] ?? $this->cols_template[$col_key];
echo esc_html($col_header);
?>
</span>
<a href="#" class="remove" tabindex="-1" title="Remove">×</a>
<input type="hidden" name="cols[<?php echo esc_attr($col_key); ?>]" value="1" class="em-bookings-col-item">
</div>
<?php endforeach; ?>
</div>
</div>
<div class="em-list-table-cols-select">
<p>
<strong><?php echo $options['text']['choose']['title']; ?></strong><br>
<?php echo $options['text']['choose']['description'] ?>
</p>
<select class="em-list-table-cols-inactive em-selectize always-open checkboxes" multiple>
<?php foreach( $grouped_fields as $group_key => $cols ): ?>
<optgroup label="<?php echo esc_attr($this->cols_template_groups[$group_key]['label']); ?>" data-data='{"type":"<?php echo esc_attr($group_key) ?>"}'>
<?php foreach( $cols as $col_key => $col_data ): ?>
<?php
$col_header = $col_data['column_header'] ?? $col_data;
$col_label = $col_data['label'] ?? $col_data;
?>
<option value="<?php echo esc_attr($col_key); ?>" data-data='{"type":"<?php echo esc_attr($col_label) ?>", "header":"<?php echo esc_attr($col_header); ?>"}' <?php if( in_array($col_key, $this->cols) ) echo 'selected'; ?>>
<?php echo esc_html($col_label); ?>
</option>
<?php endforeach; ?>
</optgroup>
<?php endforeach; ?>
<optgroup label="<?php esc_html_e('Other', 'events-manager'); ?>" data-data='{"type":"other"}'>
<?php foreach( $ungrouped_fields as $col_key => $col_data ): ?>
<?php
$col_header = $col_data['column_header'] ?? $col_data;
$col_label = $col_data['label'] ?? $col_data;
?>
<option value="<?php echo esc_attr($col_key); ?>" data-data='{"type":"other", "header":"<?php echo esc_attr($col_header); ?>"}' <?php if( in_array($col_key, $this->cols) ) echo 'selected'; ?>>
<?php echo esc_html($col_label); ?>
</option>
<?php endforeach; ?>
</optgroup>
</select>
</div>
</div>
<?php
}
/**
* Overrides default by adding and then unsetting $_GET['orderby'] to show the inisial ordering mechanism
*
* @param bool $with_id Whether to set the ID attribute or not
*/
public function print_column_headers( $with_id = true ) {
if( $this->orderby && !isset($_GET['orderby']) ) {
// set and unset $_GET to have a default ordering
$_GET['orderby'] = $this->orderby;
parent::print_column_headers( $with_id );
unset($_GET['orderby']);
} else {
// just output witout modifying $_GET
parent::print_column_headers( $with_id );
}
}
/**
* Define which columns are hidden
*
* @return array
*/
public function get_hidden_columns(){
return array();
}
/**
* Get the table data, can get overrwritten for more customization.
*
* @return array
* @uses $this->get_items()
*/
protected function table_data(){
// Do the search and return result
return $this->get_items();
}
/**
* Performs a search based on requested table data and populates the $this->items array with the results, returning the same.
*
* @return array
*/
protected function get_items () {
return array();
}
/**
* Gets header columns for this list table.
* @deprecated @param bool $csv
*
* @return mixed|null
*/
function get_headers( $csv = false ) {
// temp switch $this->format so we're consistent but adhere to the deprecated $csv parameter
$old_format = $this->format;
$this->format = $csv ? 'csv' : $this->format;
$headers = array();
foreach ( $this->cols as $col ) {
$spreadsheet_format = in_array( $this->format, ['csv', 'xls', 'xlsx'] );
if ( $col == 'actions' ) {
if ( !$spreadsheet_format ) {
$headers[ $col ] = ' ';
}
} elseif ( array_key_exists( $col, $this->cols_template ) ) {
if( is_array($this->cols_template[ $col ]) ) {
$v = $this->cols_template[ $col ]['column_header'] ?? $this->cols_template[ $col ]['label'];
} else {
$v = $this->cols_template[ $col ];
}
//csv/excel escaping
if ( $spreadsheet_format ) {
$v = static::sanitize_spreadsheet_cell( $v );
$headers[ $col ] = $v;
} else {
$headers[ $col ] = $v;
}
}
}
$headers = apply_filters( 'em_list_table_get_headers', $headers, $this );
$headers = apply_filters( static::$basename . '_get_headers', $headers, $this );
if ( $csv ) {
// undo backpat for $csv
$this->format = $old_format;
}
return $headers;
}
/**
* Gets a single row in HTML format for output to table HTML format.
* @param \EM_Object $item
*
* @return void
*/
public function single_row( $item ) {
echo '<tr data-id="'. esc_attr($item->id) .'">';
$this->single_row_columns( $item );
echo '</tr>';
}
/**
* Simlar to single_row(), but returns an array of data, excluding checkboxes and actions, which can be used for export.
*
* IMPORTANT - this function will return an array of arrays, where each array is a row of data. This allows for splitting of data into multiple rows for export.
*
* @param $EM_Object
* @param $format
*
* @return array[]
*/
public function get_row( $EM_Object ) {
$cols = array();
foreach( $this->cols as $col ){
//TODO fix urls so this works in all pages in front as well
//add to cols
$cols[$col] = $this->default_column_data( $EM_Object, $col );
}
// the specific basename filter will provide a single-dimensional array, you can return a multi-dimensional array at this point
$cols = apply_filters( static::$basename. '_get_row', $cols, $EM_Object, $this );
if( !is_array( current($cols) ) ) {
$cols = array($cols);
}
return apply_filters( 'em_list_table_get_row', $cols, $EM_Object, $this );
}
function get_cols_template(){
return $this->cols_template;
}
/**
* Override the parent columns method. Defines the columns to use in your listing table
*
* @return array
*/
public function get_columns() {
$columns = array();
if( $this->checkbox_id ) {
$columns['cb'] = '<input type="checkbox" />';
}
return $columns + $this->get_headers();
}
/**
* Define what data to show on each column of the table
*
* @param \EM_Object $item Data, usually an EM_Object for this class. Override this method to use your own data.
* @param string $column_name Current column name
*
* @return string
*/
function column_default( $item, $column_name ){
$val = '';
if( in_array($column_name, $this->cols) ){
// get the column values
$primary_column = array_values($this->cols)[0]; // changed from reset() to avoid potential issues resetting nested foreaches
if( $primary_column === $column_name && $this->format == 'html' ){
$val = $this->primary_column_meta( $item, $column_name );
$val .= '<div class="primary-column-content" data-colname="'. $this->_column_headers[0][$column_name] .'">'. $this->default_column_data( $item, $column_name ) . '</div>';
if( static::$show_responsive_meta ) {
$val .= '<div class="em-list-table-row-responsive-meta">' . $this->primary_column_responsive_meta( $item, $column_name ) . '</div>';
}
} else {
$val = $this->default_column_data( $item, $column_name );
}
}
return $val;
}
public function primary_column_meta( $item, $column_name ) {
$content = '';
// ajax status icons
if( !empty( $item->feedback_message) ){
$css_icon = empty( $item->errors ) ? 'updated' : 'cross-circle';
$content = '<span href="#" class="em-icon em-icon-'.$css_icon.' em-tooltip" aria-label="'. $item->feedback_message .'"></span> ';
}
return $content;
}
public function primary_column_responsive_meta( $item, $column_name ) { return ''; }
/**
* Gets data for columns that don't have a specific method defined.
*
* This method MUST be overwritten by child classes and ALSO must run the return value through the default_column_sanitize_data() method.
* @param $item
* @param $col
*
* @return string
*/
public function default_column_data( $item, $col ){
$val = $this->get_default_column_data( $item, $col );
// interept data before it is sanitized
$val = apply_filters( 'em_list_table_rows_col_'.$col, $val, $item, $this );
$val = apply_filters( static::$basename.'_rows_col_'.$col, $val, $item, $this, $this->format, $this); // IGNORE LAST $this in your filters (bottom one too), typo and we don't want to remove it just in case it breaks sites implementing it by mistake
$val = apply_filters( static::$basename.'_rows_col', $val, $col, $item, $this, $this->format, $this); // use the above filter instead for better performance
// sanitize data, intercepting data should set and unset (0) itself each time accordingly from the $this->allow_html array
return $this->default_column_sanitize_data( $val, $col, $item );
}
public function get_default_column_data( $item, $col ) {
_doing_it_wrong( __METHOD__, 'You must overwrite this method in your child class.', '5.9.0' );
return '';
}
public function default_column_sanitize_data( $val, $col, $item, $allow_html = false ) {
$format = $this->format;
//escape all HTML if destination is HTML or not defined
if( empty($allow_html) && ($format == 'html' || empty($format)) && empty(static::$cols_allowed_html[$col]) ){
$val = esc_html($val);
}
//csv/excel escaping
if( in_array( $format, ['csv', 'xls', 'xlsx'] ) ){
$val = self::sanitize_spreadsheet_cell($val);
}
return $val;
}
/**
* Bulk Edit Checkbox
* @param array $item
* @return string
*/
function column_cb( $item ) {
if( is_object($item) ){
$id = $item->{$this->checkbox_id};
}else{
$id = $item[$this->checkbox_id];
}
return sprintf('<input type="checkbox" name="column_id[]" value="%s" />', $id);
}
public function bulk_actions ( $which = '' ) {
$uid = esc_attr($this->uid);
$id = esc_attr($this->id);
?>
<div class="alignleft actions <?php echo $id; ?>-settings em-list-table-triggers">
<a href="#" class="<?php echo $id; ?>-export <?php echo $id; ?>-trigger em-list-table-trigger em-list-table-export-trigger em-icon em-icon-download em-tooltip" id="<?php echo $id; ?>-export-trigger" rel="#<?php echo $uid; ?>-export-modal" aria-label="<?php _e('Export these bookings.','events-manager'); ?>"></a>
<a href="#" class="<?php echo $id; ?>-settings <?php echo $id; ?>-trigger em-list-table-trigger em-list-table-settings-trigger em-icon em-icon-settings em-tooltip" id="<?php echo $id; ?>-settings-trigger" rel="#<?php echo $uid; ?>-settings-modal" aria-label="<?php _e('Settings','events-manager'); ?>"></a>
</div>
<?php $this->extra_tablenav_trigger( $which ); ?>
<?php
if( $this->checkbox_id ) {
$this->bulk_actions_input( $which );
}
}
public function bulk_actions_input( $which = '' ) {
$id = esc_attr($this->id);
$uid = esc_attr($this->uid);
?>
<div class="alignleft actions bulkactions-input">
<label for="<?php echo $uid; ?>-bulk-action-selector-top" class="screen-reader-text"><?php esc_html_e('Select bulk action'); ?></label>
<select class="bulk-action-selector em-list-table-bulk-action" id="<?php echo $uid; ?>-bulk-action-selector<?php if( $which ) echo '-'.$which; ?>">
<option value="-1"><?php esc_html_e('Bulk actions', 'events-manager'); ?></option>
<?php foreach( $this->get_bulk_actions() as $action => $action_data ): ?>
<?php if( !empty($action_data['actions']) ): ?>
<optgroup label="<?php echo esc_attr($action_data['label'] ?? '---'); ?>">
<?php foreach( $action_data['actions'] as $sub_action => $sub_action_data ): ?>
<?php
$data = array();
$sub_action_data['data']['confirm'] = $sub_action_data['confirm-bulk'] ?? '';
foreach( $sub_action_data['data'] as $key => $value ){
if( $value ) $data[] = 'data-' .esc_attr($key) . '="' . esc_attr($value) . '"';
}
?>
<option value="<?php echo esc_attr($sub_action); ?>" <?php echo implode(' ', $data); ?>><?php echo esc_html($sub_action_data['label']); ?></option>
<?php endforeach; ?>
</optgroup>
<?php else: ?>
<?php
$data = array();
$action_data['data']['confirm'] = $action_data['confirm-bulk'] ?? ''; // add bulk message
foreach( $action_data['data'] as $key => $value ){
if( $value ) $data[] = 'data-' . esc_attr($key) . '="' . esc_attr($value) . '"';
}
?>
<option value="<?php echo esc_attr($action); ?>" <?php echo implode(' ', $data); ?>><?php echo esc_html($action_data['label']); ?></option>
<?php endif; ?>
<?php endforeach; ?>
</select>
<button class="button <?php echo $id; ?>-bulk-action em-list-table-bulk-action"><?php esc_html_e('Apply'); ?></button>
</div>
<?php
}
public function extra_tablenav_trigger ( $which = '' ) {
$tooltip_text = static::$show_filters ? esc_html__('Hide Search', 'events-manager') : esc_html__('Show Search', 'events-manager');
$id = esc_attr($this->id);
?>
<div class="alignleft actions <?php echo $id; ?>-settings">
<button class="button filters-trigger <?php echo esc_attr($this->id); ?>-filters-trigger em-tooltip <?php if ( !static::$show_filters ) echo 'hidden'; ?>" aria-label="<?php echo $tooltip_text; ?>"
data-label-show="<?php esc_html_e('Show Search', 'events-manager'); ?>"
data-label-hide="<?php esc_html_e('Hide Search', 'events-manager'); ?>"
><?php esc_html_e('Show/Hide Search'); ?></button>
<button class="button small-expand-trigger <?php echo esc_attr($this->id); ?>-small-expand-trigger"><?php esc_html_e('Expand Columns'); ?></button>
</div>
<?php
}
/* START EXPORTING */
/**
* Invoked on wp_loaded, allowing for exports via AJAX or directly on any page, checks $_REQUEST vars and verifies nonce, triggers an export if request is valid.
* @return void
*/
public static function export_init() {
if ( !empty($_REQUEST['action']) && !empty($_REQUEST['_emnonce']) && $_REQUEST['action'] === static::$export_action ) {
if( wp_verify_nonce($_REQUEST['_emnonce'], static::$export_action) ) {
static::$exporting = true;
static::export_csv();
} else {
wp_die( 'Cannot export, security nonce verification failed.' );
}
}
}
/**
* Exports and outputs a CSV downloadable file to requester.
* @return void
*/
public static function export_csv () {
do_action( 'export_csv_pre', static::class );
// open the file handle
$handle = fopen("php://output", "w");
// setup delimiter
$delimiter = !defined('EM_CSV_DELIMITER') ? static::$export_delimiter : EM_CSV_DELIMITER;
static::$export_delimiter = apply_filters('em_csv_delimiter', $delimiter); // set it static, we're exiting after export finishes
// send headers
static::export_csv_http_headers();
// output header title
static::export_csv_header_title( $handle );
// prep table headers $_REQUEST cols and limit for use when setting up list table
if( !empty($_REQUEST['cols']) && is_array($_REQUEST['cols']) ){
$cols = array();
foreach($_REQUEST['cols'] as $col => $active){
if( $active ){ $cols[] = $col; }
}
$_REQUEST['cols'] = $cols;
}
$_REQUEST['limit'] = 0;
// setup list table
$EM_List_Table = static::export_setup_list_table( $handle );
// output table headers
static::export_csv_table_headers( $EM_List_Table, $handle );
// output table rows
static::export_csv_table_rows( $EM_List_Table, $handle );
// close and exit
fclose($handle);
exit();
}
public static function export_csv_http_headers() {
//generate bookings export according to search request
if( !empty($_REQUEST['event_id']) ){
$EM_Event = em_get_event( absint($_REQUEST['event_id']) );
}
header("Content-Type: application/octet-stream; charset=utf-8");
$file_name = $EM_Event->event_slug ?? get_bloginfo();
header("Content-Disposition: Attachment; filename=".sanitize_title($file_name)."-export.csv");
do_action('em_csv_header_output');
echo "\xEF\xBB\xBF"; // UTF-8 for MS Excel (a little hacky... but does the job)
}
public static function export_csv_header_title( $handle ) {
// get Event ID
if( !empty($_REQUEST['event_id']) ){
$EM_Event = em_get_event( absint($_REQUEST['event_id']) );
}
// csv headers
if ( !defined('EM_CSV_DISABLE_HEADERS') || !EM_CSV_DISABLE_HEADERS ) {
if( !empty($_REQUEST['event_id']) ) {
fputcsv($handle, array( __('Event','events-manager') . ' : ' . $EM_Event->event_name ), static::$export_delimiter);
if( $EM_Event->location_id > 0 ) {
fputcsv($handle, array( __('Where','events-manager') . ' - ' . $EM_Event->get_location()->location_name ), static::$export_delimiter);
}
fputcsv($handle, array( __('When','events-manager') . ' : ' . $EM_Event->output('#_EVENTDATES - #_EVENTTIMES') ), static::$export_delimiter);
}
$EM_DateTime = new \EM_DateTime(current_time('timestamp'));
fputcsv($handle, array( sprintf(__('Exported on %s','events-manager'), $EM_DateTime->format('D d M Y h:i')) ), static::$export_delimiter);
fputcsv($handle, array(), static::$export_delimiter);
}
}
public static function export_setup_list_table( $handle, $format = 'csv' ) {
$class = static::class;
$EM_List_Table = new $class();
$EM_List_Table->format = $format;
$EM_List_Table->limit = 150; //if you're having server memory issues, try messing with this number
return $EM_List_Table;
}
public static function export_csv_table_headers( $EM_List_Table, $handle ) {
fputcsv($handle, $EM_List_Table->get_headers(true), static::$export_delimiter);
}
/**
* @param resource $handle
* @param List_Table $EM_List_Table
*
* @return void
*/
public static function export_csv_table_rows( $EM_List_Table, $handle ) {
$items = $EM_List_Table->table_data();
while( !empty($items) ){
foreach( $items as $item ) {
static::export_csv_table_row( $item, $EM_List_Table, $handle );
}
//reiterate loop
$EM_List_Table->offset += $EM_List_Table->limit;
$items = $EM_List_Table->table_data();
}
}
/**
* Exports a single or multiple rows based on what is provided from $this->get_row( $item );
* @param \EM_Object $item
* @param List_Table $EM_List_Table
* @param resource $handle
*
* @return void
*/
public static function export_csv_table_row( $item, $EM_List_Table, $handle ) {
$rows = $EM_List_Table->get_row( $item );
foreach( $rows as $row ) {
fputcsv( $handle, $row, static::$export_delimiter );
}
}
/* END EXPORTING */
}
}
namespace {
function em_list_table_create_funcitions(){
// handle convert_to_screen lacking in places
if( !function_exists('convert_to_screen')){
function convert_to_screen( $hook_name ) {
return new \EM\WP_Screen;
}
}
if( !function_exists('get_column_headers') ){
function get_column_headers( $screen ) {
return array();
}
}
// slightly risky situation... if WP_Screen wasn't loaded but convert_to_screen was defined, we need to make sure WP_Screen exists and hope another plugin isn't adding it later in the page
if( !class_exists('WP_Screen') ) {
class WP_Screen extends \EM\WP_Screen {}
}
}
include('em-wp-screen.php');
}