<?php
/**
 * Custom List Table for displaying data in a table format in WordPress plugin.
 *
 * @link       https://personalizewp.com/
 * @since      3.2.0 Merged into PWP
 * @since      1.0.0
 *
 * @package    PersonalizeWP
 * @subpackage PersonalizeWP/Admin
 */

namespace PersonalizeWP\Admin;

use PersonalizeWP\Model\Activity;
use PersonalizeWP\Model\Contact;
use PersonalizeWP\Admin\Admin;
use PersonalizeWP\Admin\PageActivities;
use PersonalizeWP\Admin\PageContacts;
use PersonalizeWP\Admin\Utils;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

if ( ! class_exists( 'WP_List_Table' ) ) {
	require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}

/**
 * Custom List Table for displaying data in a table format in WordPress plugin.
 */
class ListTableActivity extends \WP_List_Table {

	/**
	 * The screen name.
	 *
	 * @var string
	 */
	public $screen_id = 'pwp_activity';

	/**
	 * Default number of items per page
	 */
	const ITEMS_PER_PAGE = 20;

	/**
	 * Class constructor
	 */
	public function __construct() {
		parent::__construct(
			[
				'singular' => 'activity',
				'plural'   => 'activities',
				'ajax'     => false,
				'screen'   => $this->screen_id,
			]
		);

		add_action( 'personalizewp_activity_filter_fields', [ $this, 'event_type_dropdown' ], 5 );
	}

	/**
	 * Retrieve the table columns.
	 *
	 * @return array Associative array of table columns with column name as key and column title as value.
	 */
	public function get_columns() {

		// Base activity columns
		$columns = array(
			'user_name'     => __( 'Name', 'personalizewp' ),
			'activity_type' => __( 'Event Name', 'personalizewp' ),
			'object_id'     => __( 'Event Value', 'personalizewp' ),
			'created'       => __( 'Date / Time', 'personalizewp' ),
			'lead_score'    => __( 'Lead Score', 'personalizewp' ),
		);

		/**
		 * Filters the activity list table columns.
		 *
		 * @since 3.3.0
		 *
		 * @param array $columns Activity table columns.
		 */
		return apply_filters( 'personalizewp_activity_table_columns', $columns );
	}

	/**
	 * Get sortable columns
	 *
	 * @return array
	 */
	protected function get_sortable_columns() {
		// Recent Events has no sortable columns by default (live representation)
		$sortable = array();

		/**
		 * Filters the sortable columns for activity list tables.
		 *
		 * @since 3.3.0
		 *
		 * @param array $sortable Activity table sortable columns.
		 */
		return apply_filters( 'personalizewp_activity_table_sortable_columns', $sortable );
	}

	/**
	 * Return default hidden columns
	 *
	 * @param string[]   $hidden Array of IDs of columns hidden by default.
	 * @param \WP_Screen $screen WP_Screen object of the current screen.
	 *
	 * @return array
	 */
	public function default_hidden_columns( $hidden, $screen ) {
		if ( $this->screen_id === $screen->id ) {
			$hidden[] = 'url';
		}

		return $hidden;
	}

	/**
	 * Gets the name of the default primary column.
	 *
	 * @return string Name of the default primary column, in this case, 'user_name'.
	 */
	protected function get_default_primary_column_name() {
		return 'user_name';
	}

	/**
	 * Handles the activity type column output.
	 *
	 * @param array $item Activity
	 *
	 * @return string
	 */
	protected function column_activity_type( $item ) {
		return esc_html( PageActivities::translate_activity_type( $item['activity_type'] ) );
	}

	/**
	 * Handles the user name column output.
	 *
	 * @since 3.3.0
	 *
	 * @param array $item Activity
	 *
	 * @return string
	 */
	protected function column_user_name( $item ) {
		$title = __( 'Anonymous User', 'personalizewp' );

		$contact = Contact::get_instance( $item['contact_id'] );
		if ( $contact && ( ! empty( $contact->first_name ) || ! empty( $contact->last_name ) ) ) {
			$title = sprintf(
				'%1$s %2$s',
				$contact->first_name,
				$contact->last_name
			);
		}

		// Link to contact view
		return sprintf(
			'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
			admin_url( PageContacts::$view_url . $item['contact_id'] ),
			/* translators: %s: Contact name. */
			esc_attr( sprintf( _x( 'View &#8220;%s&#8221;', 'Contact name', 'personalizewp' ), $title ) ),
			esc_html( $title )
		);
	}

	/**
	 * Generates and displays row action links.
	 *
	 * @param array  $item        Item being acted upon.
	 * @param string $column_name Current column name.
	 * @param string $primary     Primary column name.
	 *
	 * @return string Row actions output for Activity, or an empty string if the current column is not the primary column.
	 */
	protected function handle_row_actions( $item, $column_name, $primary ) {
		if ( $primary !== $column_name ) {
			return '';
		}

		$title     = PageActivities::translate_activity_type( $item['activity_type'] );
		$actions   = array();
		$edit_link = $this->get_edit_link( $item );
		$view_link = ''; // Start empty

		// Handle built-in activity types.
		switch ( $item['activity_type'] ) {
			case 'post':
				$view_link = get_permalink( $item['object_id'] );
				break;

			case 'comment':
				$view_link = get_comment_link( $item['object_id'] );
				break;

			case 'purchase':
				if ( ! empty( $item['object_id'] ) ) {
					switch ( $item['object_type'] ) {
						case 'woocommerce':
							$view_link = $edit_link;
							break;
					}
				}
				break;

		} // End switch

		/**
		 * Filter the view link URL of a single activity item in the displayed list table.
		 *
		 * The dynamic portion of the hook name, `$item['activity_type']`, refers to
		 * the Activity type slug.
		 *
		 * Possible hook names include:
		 *
		 *  - `personalizewp_activity_table_view_link_form`
		 *  - `personalizewp_activity_table_view_link_comment`
		 *
		 * @since 3.3.0
		 *
		 * @param string $view_link URL to view the referenced activity item
		 * @param array  $item      Activity entry
		 */
		$view_link = apply_filters( "personalizewp_activity_table_view_link_{$item['activity_type']}", $view_link, $item );

		if ( ! empty( $edit_link ) ) {
			$actions['edit'] = sprintf(
				'<a href="%s" aria-label="%s">%s</a>',
				esc_url( $edit_link ),
				/* translators: %s: Activity Item title. */
				esc_attr( sprintf( _x( 'Edit &#8220;%s&#8221; item', 'Item title', 'personalizewp' ), $title ) ),
				esc_html__( 'Edit', 'personalizewp' )
			);
		}

		// All activity deletions now handled by PageActivities
		$delete_url = PageActivities::$delete_activity_url;

		$actions['delete'] = sprintf(
			'<a class="trash" data-show-modal="#deleteActivityModal" href="%s" aria-label="%s">%s</a>',
			wp_nonce_url( admin_url( $delete_url . $item['ID'] ), 'delete-activity-' . $item['ID'] ),
			/* translators: %s: Activity name. */
			esc_attr( sprintf( _x( 'Delete &#8220;%s&#8221; activity', 'Activity name', 'personalizewp' ), $title ) ),
			esc_html__( 'Delete', 'personalizewp' )
		);

		if ( ! empty( $view_link ) ) {
			$actions['view'] = sprintf(
				'<a href="%s" target="_blank" aria-label="%s">%s</a>',
				esc_url( $view_link ),
				/* translators: %s: Activity Item title. */
				esc_attr( sprintf( _x( 'View &#8220;%s&#8221; item', 'Item title', 'personalizewp' ), $title ) ),
				esc_html__( 'View', 'personalizewp' )
			);
		}

		/**
		 * Filters the row actions for activity items.
		 *
		 * @since 3.3.0
		 *
		 * @param array $actions Row actions.
		 * @param array $item    Activity item data.
		 */
		$actions = apply_filters( 'personalizewp_activity_row_actions', $actions, $item );

		return $this->row_actions( $actions );
	}

	/**
	 * Returns the URL to edit the activity item.
	 *
	 * @since 2.3.0
	 *
	 * @param array $item Activity
	 *
	 * @return string The link URL or empty if no link possible
	 */
	protected function get_edit_link( $item ) {

		// Start empty
		$url = '';

		// Handle built-in activity types.
		switch ( $item['activity_type'] ) {
			case 'post':
				if ( ! empty( $item['object_id'] ) ) {
					$url = get_edit_post_link( $item['object_id'] );
				}
				break;

			case 'form':
				if ( ! empty( $item['object_id'] ) ) {
					$url = admin_url( Utils::get_edit_form_link( (int) $item['object_id'], $item['object_type'] ) );
				}
				break;

			case 'purchase':
				if ( ! empty( $item['object_id'] ) ) {
					switch ( $item['object_type'] ) {
						case 'woocommerce':
							// WC doesn't have an easy to use function to get this edit link
							$url = admin_url( 'post.php?post=' . absint( $item['object_id'] ) ) . '&action=edit';
							break;
					}
				}
				break;

			case 'home':
				// Check for page on front
				$front_page_id = get_option( 'page_on_front' );
				if ( ! empty( $front_page_id ) ) {
					$url = get_edit_post_link( $front_page_id );
				}
				break;
		}

		/**
		 * Filter the edit link URL of a single activity item in the displayed list table.
		 *
		 * The dynamic portion of the hook name, `$item['activity_type']`, refers to
		 * the Activity type slug.
		 *
		 * Possible hook names include:
		 *
		 *  - `personalizewp_activity_table_edit_link_form`
		 *  - `personalizewp_activity_table_edit_link_purchase`
		 *
		 * @since 3.3.0
		 *
		 * @param string $url  URL to edit the referenced activity item
		 * @param array  $item Activity entry
		 */
		return apply_filters( "personalizewp_activity_table_edit_link_{$item['activity_type']}", $url, $item );
	}

	/**
	 * Object ID column renderer
	 *
	 * @since 2.3.0
	 *
	 * @param array $item Activity
	 *
	 * @return string
	 */
	protected function column_object_id( $item ) {

		// Start empty
		$title = '';

		// Handle built-in activity types.
		switch ( $item['activity_type'] ) {
			case 'post':
				if ( ! empty( $item['object_id'] ) ) {
					$title = get_the_title( $item['object_id'] );
				}
				break;

			case 'form':
				if ( ! empty( $item['object_id'] ) ) {
					$title = Utils::get_form_title( (int) $item['object_id'], $item['object_type'] );
				}
				break;

			case 'tax':
				if ( ! empty( $item['object_id'] ) ) {
					$title = get_term_field( 'name', $item['object_id'] );
				}
				break;

			case 'comment':
				if ( ! empty( $item['object_id'] ) ) {
					$comment = get_comment( $item['object_id'] );
					$title   = sprintf(
						/* translators: %s: Comment post title. */
						__( 'Comment on %s', 'personalizewp' ),
						get_the_title( $comment->comment_post_ID )
					);
				}
				break;

			case 'purchase':
				if ( ! empty( $item['object_id'] ) ) {
					switch ( $item['object_type'] ) {
						case 'woocommerce':
							// WooCommece uses an automated order "title", but that isn't visible
							// within their UI. So just show the direct order number.
							$title = sprintf(
								/* translators: %d: Order number. */
								__( 'Order #%d', 'personalizewp' ),
								esc_attr( $item['object_id'] )
							);
							break;
					}
				}
				break;

			case 'login':
				$title = '-';
				break;
		}

		// Check and simply remove any errored titles. Might be due to not longer existing CPT or Term.
		if ( is_wp_error( $title ) ) {
			$title = '';
		}

		// Merge URL into Event Value when no title is available.
		if ( empty( $title ) && ! empty( $item['url'] ) ) {
			$title = $item['url'];
		}

		$link_title = '';
		$link       = $this->get_edit_link( $item );
		if ( ! empty( $link ) ) {
			$link_title = sprintf(
				'<a href="%2$s" aria-label="%3$s">%1$s</a>',
				esc_html( $title ),
				esc_url( $link ),
				esc_attr(
					sprintf(
						/* translators: %s: Object title. */
						__( 'Edit &#8220;%s&#8221;', 'personalizewp' ),
						$title
					)
				)
			);
		} else {
			$link_title = $title;
		}

		/**
		 * Filter the linked title of a single activity item in the displayed list table.
		 *
		 * The dynamic portion of the hook name, `$item['activity_type']`, refers to
		 * the Activity type slug.
		 *
		 * Possible hook names include:
		 *
		 *  - `personalizewp_activity_table_event_value_post`
		 *  - `personalizewp_activity_table_event_value_purchase`
		 *
		 * @since 3.3.0
		 *
		 * @param string $link_title HTML title, with link (if set)
		 * @param string $link       URL to edit/view the referenced activity item
		 * @param string $title      Title of Activity item
		 * @param array  $item       Activity entry
		 */
		$link_title = apply_filters( "personalizewp_activity_table_event_value_{$item['activity_type']}", $link_title, $link, $title, $item );

		return wp_kses(
			$link_title,
			[
				'a' => [
					'href'       => [],
					'aria-label' => [],
				],
			]
		);
	}

	/**
	 * URL column renderer
	 *
	 * @since 2.3.0
	 *
	 * @param array $item Activity
	 *
	 * @return string
	 */
	protected function column_url( $item ) {

		if ( empty( $item['url'] ) ) {
			return '';
		}

		return sprintf(
			'<a href="%2$s" target="_blank">%1$s</a>',
			esc_html( $item['url'] ),
			esc_url( $item['url'] ),
		);
	}

	/**
	 * Default column renderer
	 *
	 * @param array  $item        Activity
	 * @param string $column_name List table column name
	 *
	 * @return string
	 */
	protected function column_default( $item, $column_name ) {

		// Check that the column name exists.
		$content = $item[ $column_name ] ?? '';

		/**
		 * Filter each custom column of a specific activity type in the Activity list table.
		 *
		 * Defaults to the column name of the activity item.
		 *
		 * The dynamic portion of the hook name, `$item['activity_type']`, refers to
		 * the Activity type slug.
		 *
		 * Possible hook names include:
		 *
		 *  - `personalizewp_activity_table_custom_column_post`
		 *  - `personalizewp_activity_table_custom_column_form`
		 *
		 * @since 3.3.0
		 *
		 * @param string $content     Content of the $column_name index in $item array
		 * @param string $column_name List table column name
		 * @param array  $item        Activity entry
		 */
		$content = apply_filters( "personalizewp_activity_table_custom_column_{$item['activity_type']}", $content, $column_name, $item );

		/**
		 * Filter each custom column in the Activity list table.
		 *
		 * Defaults to the column name of the activity item.
		 *
		 * @since 3.3.0
		 *
		 * @param string $content     Content of the $column_name index in $item array
		 * @param string $column_name List table column name
		 * @param array  $item        Activity entry
		 */
		$content = apply_filters( 'personalizewp_activity_table_custom_column', $content, $column_name, $item );

		return wp_kses(
			$content,
			[
				'a' => [
					'href'       => [],
					'aria-label' => [],
				],
			]
		);
	}

	/**
	 * Prepare the table items. Retrieves and sets the data for the table.
	 *
	 * @return void
	 */
	public function prepare_items() {
		global $wpdb;

		$join  = '';
		$where = '';

		// Process Event Type Filtering
		$event_type = isset( $_REQUEST['event_type'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['event_type'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: used for filtering
		if ( ! empty( $event_type ) ) {
			$where .= $wpdb->prepare( ' AND activity_type = %s', $event_type );
		}

		// Process Search functionality
		$search = isset( $_REQUEST['search'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: used for filtering
		if ( ! empty( $search ) ) {
			$contact_ids_filter = '';
			// First query: Get contact IDs that match search criteria
			$search_cols    = array(
				'first_name',
				'last_name',
				'email',
			);
			$search_clauses = array();

			foreach ( $search_cols as $col ) {
				$search_clauses[] = $wpdb->prepare( '%i LIKE %s', $col, '%' . $wpdb->esc_like( $search ) . '%' );
			}
			$search_where_contacts = ' AND (' . implode( ' OR ', $search_clauses ) . ')';

			// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery -- Reason: custom db table
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: inputs sanitised/validated above
			$contact_ids = $wpdb->get_col(
				"SELECT ID FROM {$wpdb->prefix}pwp_contacts WHERE 1=1 $search_where_contacts"
			);
			// phpcs:enable

			if ( ! empty( $contact_ids ) ) {
				$contact_ids_placeholders = implode( ', ', array_fill( 0, count( $contact_ids ), '%d' ) );
				$contact_ids_filter       = $wpdb->prepare( " AND contact_id IN ( {$contact_ids_placeholders} )", $contact_ids ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: using placeholders
			} else {
				// No matching contacts found, return no results
				$contact_ids_filter = ' AND 1=0';
			}
			// Combine all WHERE conditions
			$where .= $contact_ids_filter;
		}

		// Process Ordering
		$orderby = 'created DESC, lead_score '; // Default when not custom, 2nd order is added below
		if ( ! empty( $_GET['orderby'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$_orderby = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: verified elsewhere
			$parsed   = $this->parse_orderby( $_orderby );
			if ( $parsed ) {
				$orderby = $parsed;
			}
		}
		$order   = ! empty( $_GET['order'] ) ? sanitize_text_field( wp_unslash( $_GET['order'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: verified elsewhere
		$order   = $this->parse_order( $order );
		$orderby = 'ORDER BY ' . $orderby . ' ' . $order;

		// Process pagination
		$per_page = $this->get_items_per_page( 'edit_pwp_recent_events_per_page', self::ITEMS_PER_PAGE );

		$page   = $this->get_pagenum();
		$offset = absint( ( $page - 1 ) * $per_page );
		$limits = 'LIMIT ' . $offset . ', ' . $per_page;

		$pieces = array( 'join', 'where' );
		/**
		 * Filters Activity query clauses at once, for convenience.
		 *
		 * Covers the JOIN and WHERE clauses. Used by PageContacts to restrict
		 * activities to a single Contact ID when viewing a contact profile.
		 *
		 * @since 3.3.0
		 *
		 * @param string[] $clauses {
		 *     Associative array of the clauses for the query.
		 *
		 *     @type string $join  The JOIN clause of the query.
		 *     @type string $where The WHERE clause of the query.
		 * }
		 */
		$clauses = (array) apply_filters_ref_array( 'personalizewp_activity_request_clauses', array( compact( $pieces ) ) );
		$join    = isset( $clauses['join'] ) ? $clauses['join'] : '';
		$where   = isset( $clauses['where'] ) ? $clauses['where'] : '';

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery -- Reason: custom db table
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: inputs sanitised/validated above
		$this->items = $wpdb->get_results(
			"SELECT SQL_CALC_FOUND_ROWS *
			FROM {$wpdb->prefix}pwp_activity $join
			WHERE 1=1 $where
			$orderby $limits", // nosemgrep: audit.php.wp.security.sqli.input-in-sinks -- Reason: inputs sanitised/validated above
			ARRAY_A
		);
		// phpcs:enable

		$total_items = (int) $wpdb->get_var( 'SELECT FOUND_ROWS()' );
		$item_ids    = wp_list_pluck( $this->items, 'ID' );

		// Prep Activity cache for use.
		Activity::update_meta_cache( $item_ids );

		$this->set_pagination_args(
			array(
				'total_items' => $total_items,
				'per_page'    => $per_page,
			)
		);
	}

	/**
	 * Validates that the 'orderby' is valid
	 *
	 * @param string $orderby The 'orderby' query variable.
	 * @return string The sanitized 'orderby' query variable.
	 */
	protected function parse_orderby( $orderby ) {

		$orderby = strtolower( $orderby );

		// Used to filter values.
		$allowed_keys = array_keys( $this->get_sortable_columns() );

		if ( ! in_array( $orderby, $allowed_keys, true ) ) {
			return false;
		}

		return $orderby;
	}

	/**
	 * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
	 *
	 * @param string $order The 'order' query variable.
	 * @return string The sanitized 'order' query variable.
	 */
	protected function parse_order( $order ) {
		if ( ! is_string( $order ) || empty( $order ) ) {
			return 'DESC';
		}

		if ( 'ASC' === strtoupper( $order ) ) {
			return 'ASC';
		} else {
			return 'DESC';
		}
	}

	/**
	 * Add hidden inputs to trigger correct form processing. Only outputs once.
	 *
	 * @since 3.3.0
	 *
	 * @return void
	 */
	private function add_form_actions() {
		static $displayed;

		if ( isset( $displayed ) ) {
			return;
		}
		$displayed = true;

		$page   = isset( $_REQUEST['page'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: nonce verification not required
		$action = isset( $_REQUEST[ Admin::URL_ACTION ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ Admin::URL_ACTION ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: nonce verification not required
		?>
			<input type="hidden" name="page" value="<?php echo esc_attr( $page ); ?>" />
			<input type="hidden" name="<?php echo esc_attr( Admin::URL_ACTION ); ?>" value="<?php echo esc_attr( $action ); ?>" />
		<?php
	}

	/**
	 * Displays the search box
	 *
	 * @since 3.3.0
	 *
	 * @param string $text     The 'submit' button label.
	 * @param string $input_id ID attribute value for the search input field.
	 *
	 * @return void
	 */
	public function search_box( $text, $input_id ) {

		$this->add_form_actions();

		$search = isset( $_REQUEST['search'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: nonce verification not required
		?>
		<p class="search-box">

			<?php
			/**
			 * Fires during the Activity search form.
			 */
			do_action( 'personalizewp_activity_search_fields' );
			?>

			<label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo esc_html( $text ); ?>:</label>
			<input type="text" class="search-icon" id="<?php echo esc_attr( $input_id ); ?>" name="search" value="<?php echo esc_attr( $search ); ?>"
				placeholder="<?php esc_attr_e( 'Search name or email', 'personalizewp' ); ?>" />
			<input type="submit" class="secondary btn" value="<?php echo esc_html( $text ); ?>" />
		</p>
		<?php
	}

	/**
	 * Display event type filter dropdown.
	 *
	 * @used-by 'personalizewp_activity_filter_fields' action
	 *
	 * @since 3.3.0
	 *
	 * @return void
	 */
	public function event_type_dropdown() {

		$current_type = isset( $_REQUEST['event_type'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['event_type'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: used for filtering

		$event_types = [];

		// Use all registered events.
		$all_event_types = personalizewp()->get_registered_event_types();
		if ( ! empty( $all_event_types ) ) {
			// Restrict to just key and label.
			$event_types = wp_list_pluck( $all_event_types, 'label', 'name' );
		}

		/**
		 * Filters the event types available for filtering. Excludes 'All Events' option.
		 *
		 * @since 3.3.0
		 *
		 * @param array $event_types Event type filter options.
		 */
		$event_types = apply_filters( 'personalizewp_activity_filter_event_types', $event_types );

		// Order alphabetically, all events is always first, being not included in filter but added below.
		asort( $event_types );
		?>
		<label for="event-type-filter" class="screen-reader-text"><?php esc_html_e( 'Filter by event type', 'personalizewp' ); ?></label>
		<select name="event_type" id="event-type-filter">
			<option value="" <?php selected( $current_type, '' ); ?>><?php esc_html_e( 'All Event Types', 'personalizewp' ); ?></option>
			<?php foreach ( $event_types as $value => $label ) : ?>
				<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $current_type, $value ); ?>>
					<?php echo esc_html( $label ); ?>
				</option>
			<?php endforeach; ?>
		</select>
		<?php
	}

	/**
	 * Adds table filtering
	 *
	 * @since 3.3.0
	 *
	 * @param string $which Top or bottom of table
	 *
	 * @return void
	 */
	public function extra_tablenav( $which ) {
		if ( 'top' !== $which ) {
			return;
		}
		?>
		<div class="alignleft actions">
			<?php
			/**
			 * Action hook to allow additional inputs in the activity/event filter dropdown area.
			 *
			 * @since 3.3.0
			 */
			do_action( 'personalizewp_activity_filter_fields' );

			// If something has added filter inputs, add the necessary form inputs and submit button.
			if ( has_action( 'personalizewp_activity_filter_fields' ) ) :
				$this->add_form_actions();
				?>
				<input type="submit" class="secondary btn" value="<?php esc_attr_e( 'Filter', 'personalizewp' ); ?>" />
				<?php
			endif;
			?>
		</div>
		<?php
	}

	/**
	 * Generates the table navigation above or below the table
	 *
	 * Overridden to remove bulk actions and nonces
	 *
	 * @since 3.3.0
	 *
	 * @param string $which Top or bottom of table
	 *
	 * @return void
	 */
	protected function display_tablenav( $which ) {
		?>
	<div class="tablenav <?php echo esc_attr( $which ); ?>">

		<?php
		$this->extra_tablenav( $which );
		$this->pagination( $which );
		?>

	</div>
		<?php
	}
}
