|
<?php |
|
|
|
// Exit if accessed directly. |
|
if ( ! defined( 'ABSPATH' ) ) exit; |
|
|
|
if ( ! class_exists( 'Mai_WooCommerce_Account_Tab' ) ): |
|
/** |
|
* A class to create new WooCommerce account tabs. |
|
* |
|
* @version 0.1.0 |
|
* |
|
* @link https://gist.github.com/JiveDig/0d3658676127f30a098859228fc5d8eb |
|
* |
|
* 1. Register a new tab on the My Account page. |
|
* 2. Optionally position the tab before or after an existing tab by endpoint name. |
|
* Default is last item before logout, if it exists, otherwise it's added as last item. |
|
* 3. Optionally add a condition to show or hide the tab. |
|
* 4. Use `Mai_WooCommerce_Account_Tab::is_tab( $endpoint )` to check if the current page is the tab. |
|
* This is useful for enqueueing scripts or styles only on the tab page. |
|
* |
|
* Example usage: |
|
* add_action( 'woocommerce_init', function() { |
|
* new Mai_WooCommerce_Account_Tab( |
|
* [ |
|
* 'endpoint' => 'new-endpoint', // Required. |
|
* 'label' => __( 'New Endpoint', 'textdomain' ), // Required. |
|
* 'content' => '', // Optional. String or callable. |
|
* 'condition' => current_user_can( 'edit_posts' ), // Optional. Boolean or callable. |
|
* 'position' => [ // Optional. |
|
* 'after' => 'edit-account', |
|
* ], |
|
* ] |
|
* ); |
|
* }); |
|
* |
|
* // Helper method: |
|
* `Mai_WooCommerce_Account_Tab::is_tab( 'new-endpoint' )` |
|
* |
|
* @return void |
|
*/ |
|
class Mai_WooCommerce_Account_Tab { |
|
/** |
|
* The args. |
|
* |
|
* @var array |
|
*/ |
|
protected $args; |
|
|
|
/** |
|
* Gets is started. |
|
* |
|
* @since 0.1.0 |
|
*/ |
|
function __construct( $args ) { |
|
// Set the data. |
|
$this->args = $this->sanitize( $args ); |
|
|
|
// Bail if no args. |
|
if ( ! $this->args ) { |
|
return; |
|
} |
|
|
|
// Hooks. |
|
$this->hooks(); |
|
} |
|
|
|
/** |
|
* Checks if the current page is the account page endpoint. |
|
* |
|
* @since 0.1.0 |
|
* |
|
* @return bool |
|
*/ |
|
static function is_tab( $endpoint ) { |
|
static $cache = []; |
|
|
|
// Maybe return cached value. |
|
if ( isset( $cache[ $endpoint ] ) ) { |
|
return $cache; |
|
} |
|
|
|
// Set cache. |
|
$cache[ $endpoint ] = class_exists( 'WooCommerce' ) && is_account_page() && is_wc_endpoint_url( $endpoint ); |
|
|
|
return $cache[ $endpoint ]; |
|
} |
|
|
|
/** |
|
* Runs hooks. |
|
* |
|
* @since 0.1.0 |
|
*/ |
|
function hooks() { |
|
add_filter( 'woocommerce_get_query_vars', [ $this, 'add_query_vars' ] ); |
|
add_filter( 'woocommerce_account_menu_items', [ $this, 'add_menu_items' ] ); |
|
add_action( 'template_redirect', [ $this, 'do_redirect' ] ); |
|
|
|
// Bail if no content. |
|
if ( ! $this->args['content'] ) { |
|
return; |
|
} |
|
|
|
add_action( "woocommerce_account_{$this->args['endpoint']}_endpoint", [ $this, 'do_content' ] ); |
|
} |
|
|
|
/** |
|
* Adds WooCommerce query vars. |
|
* Woo uses these to add the endpoint. |
|
* |
|
* @since 0.1.0 |
|
* |
|
* @param array $vars The existing query vars. |
|
* |
|
* @return array |
|
*/ |
|
function add_query_vars( $vars ) { |
|
$vars[ $this->args['endpoint'] ] = $this->args['endpoint']; |
|
|
|
return $vars; |
|
} |
|
|
|
/** |
|
* Adds account menu item. |
|
* |
|
* @since 0.1.0 |
|
* |
|
* @param array $items The existing items. |
|
* |
|
* @return array |
|
*/ |
|
function add_menu_items( $items ) { |
|
// Bail if condition is not met. |
|
if ( ! $this->condition_met() ) { |
|
return $items; |
|
} |
|
|
|
// Get values. |
|
$location = array_key_first( $this->args['position'] ); |
|
$item = reset( $this->args['position'] ); |
|
|
|
// Bail if no location or item. |
|
if ( ! $location || ! $item ) { |
|
return $items; |
|
} |
|
|
|
// If the item exists. |
|
if ( isset( $items[ $item ] ) ) { |
|
// Add the new item. |
|
switch ( $location ) { |
|
case 'before': |
|
$items = $this->insert_before( $items, $item, [ $this->args['endpoint'] => $this->args['label'] ] ); |
|
break; |
|
case 'after': |
|
$items = $this->insert_after( $items, $item, [ $this->args['endpoint'] => $this->args['label'] ] ); |
|
break; |
|
} |
|
} |
|
// Doesn't exist, check for logout. |
|
elseif ( isset( $items['customer-logout'] ) ) { |
|
$items = $this->insert_before( $items, 'customer-logout', [ $this->args['endpoint'] => $this->args['label'] ] ); |
|
} |
|
// Fallback to the last item. |
|
else { |
|
$items[] = [ $this->args['endpoint'] => $this->args['label'] ]; |
|
} |
|
|
|
return $items; |
|
} |
|
|
|
/** |
|
* Redirect endpoint if condition is not met. |
|
* |
|
* @since 0.1.0 |
|
* |
|
* @return void |
|
*/ |
|
function do_redirect() { |
|
// Bail if not this endpoint. |
|
if ( ! self::is_tab( $this->args['endpoint'] ) ) { |
|
return; |
|
} |
|
|
|
// Bail if conditions are met. |
|
if ( $this->condition_met() ) { |
|
return; |
|
} |
|
|
|
// Safely redirect to the main account page. |
|
wp_safe_redirect( wc_get_page_permalink( 'myaccount' ) ); |
|
exit; |
|
} |
|
|
|
/** |
|
* Adds content to the tab. |
|
* |
|
* @since 0.1.0 |
|
* |
|
* @return string |
|
*/ |
|
function do_content() { |
|
// Bail if condition is not met. |
|
if ( ! $this->condition_met() ) { |
|
return; |
|
} |
|
|
|
// If callable, run it. |
|
if ( is_callable( $this->args['content'] ) ) { |
|
call_user_func( $this->args['content'] ); |
|
return; |
|
} |
|
|
|
// Output content. |
|
echo $this->args['content']; |
|
} |
|
|
|
/** |
|
* Checks if the condition is met. |
|
* |
|
* @since 0.1.0 |
|
* |
|
* @return bool |
|
*/ |
|
function condition_met() { |
|
static $cache = null; |
|
|
|
// Maybe return cached value. |
|
if ( ! is_null( $cache ) ) { |
|
return $cache; |
|
} |
|
|
|
// Bail if callable condition is not met. |
|
if ( is_callable( $this->args['condition'] ) && (bool) ! call_user_func( $this->args['condition'] ) ) { |
|
$cache = false; |
|
return $cache; |
|
} |
|
|
|
// Bail if condition and it's not met. |
|
if ( '' !== $this->args['condition'] && ! $this->args['condition'] ) { |
|
$cache = false; |
|
return $cache; |
|
} |
|
|
|
// Set cache. |
|
$cache = true; |
|
|
|
return $cache; |
|
} |
|
|
|
/** |
|
* Insert a value or key/value pair before a specific key in an array. |
|
* If key doesn't exist, value is appended to the end of the array. |
|
* |
|
* @since 0.1.0 |
|
* |
|
* @param array $array |
|
* @param string $key |
|
* @param array $new |
|
* |
|
* @return array |
|
*/ |
|
function insert_before( array $array, $key, array $new ) { |
|
$keys = array_keys( $array ); |
|
$index = array_search( $key, $keys ); |
|
$pos = $index !== false ? $index : count( $array ); // If key doesn't exist, insert at the end. |
|
|
|
return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); |
|
} |
|
|
|
/** |
|
* Insert a value or key/value pair after a specific key in an array. |
|
* If key doesn't exist, value is appended to the end of the array. |
|
* |
|
* @since 0.1.0 |
|
* |
|
* @param array $array |
|
* @param string $key |
|
* @param array $new |
|
* |
|
* @return array |
|
*/ |
|
function insert_after( array $array, $key, array $new ) { |
|
$keys = array_keys( $array ); |
|
$index = array_search( $key, $keys ); |
|
$pos = false === $index ? count( $array ) : $index + 1; |
|
|
|
return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); |
|
} |
|
|
|
/** |
|
* Sanitizes the args. |
|
* |
|
* @since 0.1.0 |
|
* |
|
* @param array $args The args. |
|
* |
|
* @return array |
|
*/ |
|
function sanitize( $args ) { |
|
$sanitized = []; |
|
$args = wp_parse_args( $args, [ |
|
'endpoint' => '', |
|
'label' => '', |
|
'content' => '', |
|
'condition' => '', |
|
'position' => [], |
|
] ); |
|
|
|
// Bail if no endpoint or label. |
|
if ( ! $args['endpoint'] || ! $args['label'] ) { |
|
return $sanitized; |
|
} |
|
|
|
// Sanitize. |
|
foreach ( $args as $key => $value ) { |
|
switch ( $key ) { |
|
case 'endpoint': |
|
$sanitized[ $key ] = sanitize_title( $value ); |
|
break; |
|
case 'label': |
|
$sanitized[ $key ] = sanitize_text_field( $value ); |
|
break; |
|
default: |
|
$sanitized[ $key ] = $value; |
|
break; |
|
} |
|
} |
|
|
|
return $sanitized; |
|
} |
|
} |
|
endif; |