'',
'plural' => '',
];
/**
* Keeps track of the current view mode for this particular list table.
*
* @since 2.0.0
* @var string
*/
public $current_mode = 'list';
/**
* Sets the allowed modes.
*
* @since 2.0.0
* @var array
*/
public $modes = ['list' => 'List'];
/**
* The list table context.
*
* Can be page, if the table is on a list page;
* or widget, if the table is inside a widget.
*
* We use this to determine how to encapsulate the fields for filtering
* and to support multiple list tables with ajax pagination per page.
*
* @since 2.0.0
* @var string
*/
public $context = 'page';
/**
* Returns the table id.
*
* @since 2.0.0
*/
public function get_table_id(): string {
return strtolower(substr(strrchr(static::class, '\\'), 1));
}
/**
* Changes the context of the list table.
*
* Available contexts are 'page' and 'widget'.
*
* @since 2.0.0
*
* @param string $context The new context to set.
* @return void
*/
public function set_context($context = 'page'): void {
$this->context = $context;
}
/**
* Initializes the table.
*
* @since 2.0.0
*
* @param array $args Arguments of the list table.
*/
public function __construct($args = []) {
$this->id = $this->get_table_id();
$args = wp_parse_args(
$args,
[
'screen' => $this->id,
]
);
parent::__construct($args);
$this->labels = shortcode_atts($this->labels, $args);
add_action('admin_enqueue_scripts', [$this, 'register_scripts']);
add_action('in_admin_header', [$this, 'add_default_screen_options']);
$this->set_list_mode();
$this->_args['add_new'] = wu_get_isset($args, 'add_new', []);
}
/**
* Adds the screen option fields.
*
* @since 2.0.0
* @return void
*/
public function add_default_screen_options(): void {
$args = [
'default' => 20,
'label' => $this->get_per_page_option_label(),
'option' => $this->get_per_page_option_name(),
];
add_screen_option('per_page', $args);
}
/**
* Adds the select all button for the Grid Mode.
*
* @since 2.0.0
*
* @param string $which Bottom or top navbar.
* @return void
*/
protected function extra_tablenav($which) {
if ($this->current_mode === 'grid') {
printf(
'',
__('Select All', 'wp-ultimo')
);
}
}
/**
* Set the list display mode for the list table.
*
* @since 2.0.0
* @return void
*/
public function set_list_mode(): void {
if ($this->context !== 'page') {
$this->current_mode = 'list';
return;
}
$list_table_name = $this->id;
if ( ! empty($_REQUEST['mode'])) {
$mode = $_REQUEST['mode'];
if (in_array($mode, array_keys($this->modes), true)) {
$mode = $_REQUEST['mode'];
}
set_user_setting("{$list_table_name}_list_mode", $mode);
} else {
$mode = get_user_setting("{$list_table_name}_list_mode", current(array_keys($this->modes)));
}
$this->current_mode = $mode;
}
/**
* Returns a label.
*
* @since 2.0.0
*
* @param string $label singular or plural.
* @return string
*/
public function get_label($label = 'singular') {
return $this->labels[ $label ] ?? 'Object';
}
/**
* Uses the query class to return the items to be displayed.
*
* @since 2.0.0
*
* @param integer $per_page Number of items to display per page.
* @param integer $page_number Current page.
* @param boolean $count If we should count records or return the actual records.
* @return array
*/
public function get_items($per_page = 5, $page_number = 1, $count = false) {
$query_args = [
'number' => $per_page,
'offset' => ($page_number - 1) * $per_page,
'orderby' => wu_request('orderby', 'id'),
'order' => wu_request('order', 'DESC'),
'search' => wu_request('s', false),
'count' => $count,
];
$extra_query_args = [
'status',
'type',
];
foreach ($extra_query_args as $extra_query_arg) {
$query = wu_request($extra_query_arg, 'all');
if ('all' !== $query) {
$query_args[ $extra_query_arg ] = $query;
}
}
/**
* Accounts for hashes
*/
if (isset($query_args['search']) && strlen((string) $query_args['search']) === Hash::LENGTH) {
$item_id = Hash::decode($query_args['search']);
if ($item_id) {
unset($query_args['search']);
$query_args['id'] = $item_id;
}
}
return $this->_get_items($query_args);
}
/**
* General purpose get_items.
*
* @since 2.0.0
*
* @param array $query_args The query args.
* @return mixed
*/
protected function _get_items($query_args) {
$query_class = new $this->query_class();
$query_args = array_merge($query_args, array_filter($this->get_extra_query_fields()));
$query_args = apply_filters("wu_{$this->id}_get_items", $query_args, $this);
$function_name = 'wu_get_' . $query_class->get_plural_name();
if (function_exists($function_name)) {
$query = $function_name($query_args);
} else {
$query = $query_class->query($query_args);
}
return $query;
}
/**
* Checks if we have any items at all.
*
* @since 2.0.0
* @return boolean
*/
public function has_items() {
$key = $this->get_table_id();
$results = $this->get_items(1, 1, false);
return (int) $results > 0;
}
/**
* Returns the total record count. Used on pagination.
*
* @since 2.0.0
* @return int
*/
public function record_count() {
return $this->get_items(9_999_999, 1, true);
}
/**
* Returns the slug of the per_page option for this data type.
*
* @since 2.0.0
*/
public function get_per_page_option_name(): string {
return sprintf('%s_per_page', $this->id);
}
/**
* Returns the label for the per_page option for this data_type.
*
* @since 2.0.0
*/
public function get_per_page_option_label(): string {
// translators: %s will be replaced by the data type plural name. e.g. Books.
return sprintf(__('%s per page'), $this->get_label('plural'));
}
/**
* Uses the query class to determine if there's any searchable fields.
* If that's the case, we automatically add the search field.
*
* @since 2.0.0
* @return boolean
*/
protected function has_search() {
return ! empty($this->get_schema_columns(['searchable' => true]));
}
/**
* Generates the search field label, based on the table labels.
*
* @since 2.0.0
*/
public function get_search_input_label(): string {
// translators: %s will be replaced with the data type plural name. e.g. Books.
return sprintf(__('Search %s'), $this->get_label('plural'));
}
/**
* Prepare the list table before actually displaying records.
*
* @since 2.0.0
* @return void
*/
public function prepare_items(): void {
$this->_column_headers = $this->get_column_info();
$per_page = $this->get_items_per_page($this->get_per_page_option_name(), 10);
$current_page = $this->get_pagenum();
$total_items = $this->record_count();
$this->set_pagination_args(
[
'total_items' => $total_items, // We have to calculate the total number of items.
'per_page' => $per_page, // We have to determine how many items to show on a page.
]
);
$this->items = $this->get_items($per_page, $current_page);
}
/**
* Register Scripts that might be needed for ajax pagination and so on.
*
* @since 2.0.0
* @return void
*/
public function register_scripts(): void {
wp_localize_script(
'wu-ajax-list-table',
'wu_list_table',
[
'base_url' => wu_get_form_url('bulk_actions'),
'model' => strstr($this->get_table_id(), '_', true),
'i18n' => [
'confirm' => __('Confirm Action', 'wp-ultimo'),
],
]
);
wp_enqueue_script('wu-ajax-list-table');
}
/**
* Adds the hidden fields necessary to handle pagination.
*
* @since 2.0.0
* @return void
*/
public function display_ajax_filters(): void {
/**
* Add the nonce field before we generate the results
*/
wp_nonce_field(sprintf('ajax-%s-nonce', $this->_get_js_var_name()), sprintf('_ajax_%s_nonce', $this->_get_js_var_name()));
/**
* Page attribute to be used with the push state
*/
printf('', esc_attr(wu_request('page', '')));
/**
* ID attribute to be used with the push state
*/
if (wu_request('id')) {
printf('', esc_attr(wu_request('id')));
}
foreach ($this->get_hidden_fields() as $field_name => $field_value) {
/**
* Add a hidden field to later be sent via the ajax call.
*/
printf('', esc_attr($field_name), esc_attr($field_name), esc_attr($field_value));
}
}
/**
* Handles the default display for list mode.
*
* @since 2.0.0
* @return void
*/
public function display_view_list(): void {
printf('
';
}
/**
* Displays the table.
*
* Adds a Nonce field and calls parent's display method.
*
* @since 3.1.0
* @access public
*/
public function display(): void {
/*
* Any items at all?
*/
if ( ! $this->has_items() && $this->context === 'page') {
echo wu_render_empty_state(
[
'message' => sprintf(__("You don't have any %s yet.", 'wp-ultimo'), $this->labels['plural']),
'sub_message' => $this->_args['add_new'] ? __('How about we create a new one?', 'wp-ultimo') : __('...but you will see them here once they get created.', 'wp-ultimo'),
// translators: %s is the singular value of the model, such as Product, or Payment.
'link_label' => sprintf(__('Create a new %s', 'wp-ultimo'), $this->labels['singular']),
'link_url' => wu_get_isset($this->_args['add_new'], 'url', ''),
'link_classes' => wu_get_isset($this->_args['add_new'], 'classes', ''),
'link_icon' => 'dashicons-wu-circle-with-plus',
]
);
} else {
call_user_func([$this, "display_view_{$this->current_mode}"]);
}
}
/**
* Display the filters if they exist.
*
* @todo: refator
* @since 2.0.0
* @return void
*/
public function filters(): void {
$filters = $this->get_filters();
$views = apply_filters("wu_{$this->id}_get_views", $this->get_views());
if (true) {
$args = array_merge(
$filters,
[
'filters_el_id' => sprintf('%s-filters', $this->id),
'has_search' => $this->has_search(),
'search_label' => $this->get_search_input_label(),
'views' => $views,
'has_view_switch' => ! empty($this->modes),
'table' => $this,
]
);
wu_get_template('base/filter', $args);
}
}
/**
* Overrides the single row method to create different methods depending on the mode.
*
* @since 2.0.0
*
* @param mixed $item The line item being displayed.
* @return void
*/
public function single_row($item): void {
call_user_func([$this, "single_row_{$this->current_mode}"], $item);
}
/**
* Handles the item display for list mode.
*
* @since 2.0.0
*
* @param mixed $item The line item being displayed.
* @return void
*/
public function single_row_list($item): void {
parent::single_row($item);
}
/**
* Handles the item display for grid mode.
*
* @since 2.0.0
*
* @param mixed $item The line item being displayed.
* @return void
*/
public function single_row_grid($item) {}
/**
* Displays a base div when there is not item.
*
* @since 2.0.0
* @return void
*/
public function no_items(): void {
printf(
'
%s
',
__('No items found', 'wp-ultimo')
);
}
/**
* Returns an associative array containing the bulk action
*
* @return array
*/
public function get_bulk_actions() {
$default_bulk_actions = [
'delete' => __('Delete', 'wp-ultimo'),
];
$has_active = $this->get_schema_columns(
[
'name' => 'active',
]
);
if ($has_active) {
$default_bulk_actions['activate'] = __('Activate', 'wp-ultimo');
$default_bulk_actions['deactivate'] = __('Deactivate', 'wp-ultimo');
}
return apply_filters('wu_bulk_actions', $default_bulk_actions, $this->id);
}
/**
* Process single action.
*
* @since 2.0.0
* @return void
*/
public function process_single_action() {}
/**
* Handles the bulk processing.
*
* @since 2.0.0
* @return bool
*/
public static function process_bulk_action() {
global $wpdb;
$bulk_action = wu_request('bulk_action');
$model = wu_request('model');
if ('checkout' === $model) {
$model = 'checkout_form';
} elseif ('discount' === $model) {
$model = 'discount_code';
}
$item_ids = explode(',', (string) wu_request('ids', ''));
$prefix = apply_filters('wu_bulk_action_function_prefix', 'wu_get_', $model);
$func_name = $prefix . $model;
if ( ! function_exists($func_name)) {
return new \WP_Error('func-not-exists', __('Something went wrong.', 'wp-ultimo'));
}
switch ($bulk_action) {
case 'activate':
foreach ($item_ids as $item_id) {
$item = $func_name($item_id);
$item->set_active(true);
$item->save();
}
break;
case 'deactivate':
foreach ($item_ids as $item_id) {
$item = $func_name($item_id);
$item->set_active(false);
$item->save();
}
break;
case 'delete':
foreach ($item_ids as $item_id) {
$item = $func_name($item_id);
$item->delete();
}
break;
default:
do_action('wu_process_bulk_action', $bulk_action);
break;
}
return true;
}
/**
* Handles ajax requests for pagination and filtering.
*
* @since 2.0.0
* @return void
*/
public function ajax_response(): void {
check_ajax_referer(sprintf('ajax-%s-nonce', $this->_get_js_var_name()), sprintf('_ajax_%s_nonce', $this->_get_js_var_name()));
$this->set_context('ajax');
$this->prepare_items();
extract($this->_args); // phpcs:ignore
extract($this->_pagination_args, EXTR_SKIP); // phpcs:ignore
/**
* Load the rows
*/
ob_start();
if ( ! empty($_REQUEST['no_placeholder'])) {
$this->display_rows();
} else {
$this->display_rows_or_placeholder();
}
$rows = ob_get_clean();
/**
* Get headers into a variable
*/
ob_start();
$this->print_column_headers();
$headers = ob_get_clean();
/**
* Get the top bar into a variable
*/
ob_start();
$this->pagination('top');
$pagination_top = ob_get_clean();
/**
* Get the bottom nav into a variable
*/
ob_start();
$this->pagination('bottom');
$pagination_bottom = ob_get_clean();
/**
* Build the response
*/
$response = ['rows' => $rows];
$response['pagination']['top'] = $pagination_top;
$response['pagination']['bottom'] = $pagination_bottom;
$response['column_headers'] = $headers;
$response['count'] = $this->record_count();
$response['type'] = wu_request('type', 'all');
if (isset($total_items)) {
$response['total_items_i18n'] = sprintf(_n('1 item', '%s items', $total_items), number_format_i18n($total_items)); // phpcs:ignore
}
if (isset($total_pages)) {
$response['total_pages'] = $total_pages;
$response['total_pages_i18n'] = number_format_i18n($total_pages);
}
/**
* Send the response
*/
wp_send_json($response);
exit;
}
/**
* Render a column when no column specific method exist.
*
* @param array $item Item object/array.
* @param string $column_name Column name being displayed.
*
* @return string
*/
public function column_default($item, $column_name) {
$value = call_user_func([$item, "get_{$column_name}"]);
$datetime_columns = array_column(
$this->get_schema_columns(
[
'date_query' => true,
]
),
'name'
);
if (in_array($column_name, $datetime_columns, true)) {
return $this->_column_datetime($value);
}
return $value;
}
/**
* Handles the default displaying of datetime columns.
*
* @since 2.0.0
*
* @param string $date Valid date to be used inside a strtotime call.
* @return string
*/
public function _column_datetime($date) {
if ( ! wu_validate_date($date)) {
return __('--', 'wp-ultimo');
}
$time = strtotime(get_date_from_gmt((string) $date));
$formatted_value = date_i18n(get_option('date_format'), $time);
$placeholder = wu_get_current_time('timestamp') > $time ? __('%s ago', 'wp-ultimo') : __('In %s', 'wp-ultimo'); // phpcs:ignore
$text = $formatted_value . sprintf(' %s', sprintf($placeholder, human_time_diff($time)));
return sprintf('%s', wu_tooltip_text(date_i18n('Y-m-d H:i:s', $time)), $text);
}
/**
* Returns the membership object associated with this object.
*
* @since 2.0.0
*
* @param object $item Object.
* @return string
*/
public function column_membership($item) {
$membership = $item->get_membership();
if ( ! $membership) {
$not_found = __('No membership found', 'wp-ultimo');
return "