file = $file;
self::$instance->version = get_plugin_data( self::$instance->file, false, false )['Version'] ?? '';
self::$instance->setup_constants();
self::$instance->load_textdomain();
self::$instance->includes();
self::$instance->hooks();
self::$instance->functions = new AffiliateWP_Affiliate_Area_Tabs_Functions();
}
return self::$instance;
}
/**
* Throw error on object clone
*
* The whole idea of the singleton design pattern is that there is a single
* object therefore, we don't want the object to be cloned.
*
* @since 1.0.0
* @access protected
* @return void
*/
public function __clone() {
// Cloning instances of the class is forbidden.
_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'affiliatewp-affiliate-area-tabs' ), '1.0' );
}
/**
* Disable unserializing of the class
*
* @since 1.0.0
* @access protected
* @return void
*/
public function __wakeup() {
// Unserializing instances of the class is forbidden.
_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'affiliatewp-affiliate-area-tabs' ), '1.0' );
}
/**
* Constructor Function
*
* @since 1.0.0
* @access private
*/
private function __construct() {
self::$instance = $this;
}
/**
* Reset the instance of the class
*
* @since 1.0.0
* @access public
* @static
*/
public static function reset() {
self::$instance = null;
}
/**
* Setup plugin constants
*
* @access private
* @since 1.0.0
* @return void
*/
private function setup_constants() {
// Plugin version.
if ( ! defined( 'AFFWP_AAT_VERSION' ) ) {
define( 'AFFWP_AAT_VERSION', $this->version );
}
// Plugin Folder Path.
if ( ! defined( 'AFFWP_AAT_PLUGIN_DIR' ) ) {
define( 'AFFWP_AAT_PLUGIN_DIR', plugin_dir_path( $this->file ) );
}
// Plugin Folder URL.
if ( ! defined( 'AFFWP_AAT_PLUGIN_URL' ) ) {
define( 'AFFWP_AAT_PLUGIN_URL', plugin_dir_url( $this->file ) );
}
// Plugin Root File.
if ( ! defined( 'AFFWP_AAT_PLUGIN_FILE' ) ) {
define( 'AFFWP_AAT_PLUGIN_FILE', $this->file );
}
}
/**
* Loads the plugin language files
*
* @access public
* @since 1.0.0
* @return void
*/
public function load_textdomain() {
// Set filter for plugin's languages directory.
$lang_dir = dirname( plugin_basename( $this->file ) ) . '/languages/';
$lang_dir = apply_filters( 'affiliatewp_affiliate_area_tabs_languages_directory', $lang_dir );
// Traditional WordPress plugin locale filter.
$locale = apply_filters( 'plugin_locale', get_locale(), 'affiliatewp-affiliate-area-tabs' );
$mofile = sprintf( '%1$s-%2$s.mo', 'affiliatewp-affiliate-area-tabs', $locale );
// Setup paths to current locale file.
$mofile_local = $lang_dir . $mofile;
$mofile_global = WP_LANG_DIR . '/affiliatewp-affiliate-area-tabs/' . $mofile;
if ( file_exists( $mofile_global ) ) {
// Look in global /wp-content/languages/affiliatewp-affiliate-area-tabs/ folder.
load_textdomain( 'affiliatewp-affiliate-area-tabs', $mofile_global );
} elseif ( file_exists( $mofile_local ) ) {
// Look in local /wp-content/plugins/affiliatewp-affiliate-area-tabs/languages/ folder.
load_textdomain( 'affiliatewp-affiliate-area-tabs', $mofile_local );
} else {
// Load the default language files.
load_plugin_textdomain( 'affiliatewp-affiliate-area-tabs', false, $lang_dir );
}
}
/**
* Include necessary files
*
* @access private
* @since 1.0.0
* @return void
*/
private function includes() {
// Functions class.
require_once AFFWP_AAT_PLUGIN_DIR . 'includes/class-functions.php';
// Upgrade class.
require_once AFFWP_AAT_PLUGIN_DIR . 'includes/class-upgrades.php';
/**
* Compatibility class.
* This provides compatibility with AffiliateWP v1.8 - v2.1.6.1
*/
if ( $this->has_1_8() && ! $this->has_2_1_7() ) {
require_once AFFWP_AAT_PLUGIN_DIR . 'includes/class-compatibility.php';
}
// Admin class.
if ( is_admin() ) {
require_once AFFWP_AAT_PLUGIN_DIR . 'includes/class-admin.php';
}
}
/**
* Setup the default hooks and actions
*
* @since 1.0.0
*
* @return void
*/
private function hooks() {
// plugin meta.
add_filter( 'plugin_row_meta', [ $this, 'plugin_meta' ], null, 2 );
// Render the tab content.
add_filter( 'affwp_render_affiliate_dashboard_tab', [ $this, 'render_custom_tab' ], 10, 2 );
// Redirect if non-affiliate tries to access a tab's page.
add_action( 'template_redirect', [ $this, 'redirect' ] );
// Apply the same restriction to every non-front-end reader, since template_redirect
// does not fire for REST or sitemap requests.
add_filter( 'rest_page_query', [ $this, 'exclude_protected_pages' ], 10, 2 );
add_filter( 'rest_prepare_page', [ $this, 'gate_protected_page_response' ], 10, 2 );
add_filter( 'wp_sitemaps_posts_query_args', [ $this, 'exclude_protected_from_sitemap' ], 10, 2 );
// User has at least AffiliateWP version 2.1.7.
if ( $this->has_2_1_7() ) {
/**
* Filter the tabs in the Affiliate Area and in the admin.
*
* @since 1.1
* @since 1.2 Increased priority to 9999 so we can better listen for other tabs being added. E.g. Direct Link Tracking.
*/
add_filter( 'affwp_affiliate_area_tabs', [ $this, 'affiliate_area_tabs' ], 9999 );
}
// Hide tabs in the Affiliate Area.
add_filter( 'affwp_affiliate_area_show_tab', [ $this, 'hide_existing_tabs' ], 10, 2 );
}
/**
* Filter an existing tab's content
*
* @since 1.1.2
* @param string $content The tab content.
* @param string $active_tab The slug of the active tab.
*
* @return string $content The content to show in the tab.
*/
public function render_custom_tab( $content, $active_tab ) {
if ( ! $this->functions->is_custom_tab( $active_tab ) ) {
// Not a custom tab, return the content.
return $content;
}
// Only active affiliates (or admins) may read custom-tab content.
if ( ! $this->viewer_may_read_tab_pages() ) {
return $content;
}
// Get the tab's content.
$content = $this->custom_tab_content( $active_tab );
// Return the $content.
return $content;
}
/**
* The custom tab's content.
*
* @since 1.1.2
* @param string $active_tab The slug of the active tab.
*
* @return void
*/
public function custom_tab_content( $active_tab ) {
?>
functions->get_custom_tab_content( $active_tab ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Page content, rendered like the_content. ?>
settings->get( 'affiliate_area_tabs', [] );
if ( $tabs ) {
foreach ( $tabs as $key => $tab_array ) {
if ( isset( $tab_array['slug'] ) && $tab_array['slug'] === $tab && ( isset( $tab_array['hide'] ) && 'yes' === $tab_array['hide'] ) ) {
$show = false;
}
}
}
return $show;
}
/**
* Affiliate Area Tabs.
*
* @since 1.2
*
* @param array $tabs Array of tabs.
* @return array $tabs The tabs to show in the Affiliate Area
*/
public function affiliate_area_tabs( $tabs ) {
// Get the Affiliate Area Tabs.
$affiliate_area_tabs = affiliate_wp()->settings->get( 'affiliate_area_tabs' );
if ( $affiliate_area_tabs ) {
$new_tabs = [];
$saved_tab_slugs = [];
// Create a new array in the needed format of tab slug => tab title.
foreach ( $affiliate_area_tabs as $key => $tab ) {
if ( isset( $tab['slug'] ) ) {
$new_tabs[ $tab['slug'] ] = $tab['title'];
/**
* If the saved tab slug exists inside $affiliate_area_tabs, but not in $tabs (tabs from filter),
* and it's not a custom tab added via the admin then it should be unset from $affiliate_area_tabs immediately.
*/
if ( ! array_key_exists( $tab['slug'], $tabs ) && ! $this->functions->is_custom_tab( $tab['slug'] ) ) {
/**
* Tabs added by add-ons should always be visible in the admin tab list
* and only visible in the Affiliate Area if the affiliate has access.
*/
if ( array_key_exists( $tab['slug'], $this->functions->add_on_tabs() ) && ! is_admin() ) {
unset( $new_tabs[ $tab['slug'] ] );
} else {
unset( $new_tabs[ $tab['slug'] ] );
}
}
// Store an array of tab slugs.
$saved_tab_slugs[] = $tab['slug'];
}
}
/**
* If the tab slug exists in $tabs (added via filter), but not in $affiliate_area_tabs (because its already been saved),
* and the tab slug isn't a custom tab (it's ID will be 0), append the tab to the end of $affiliate_area_tabs.
* That way it can be re-ordered by admin and saved into its new location.
*/
foreach ( $tabs as $tab_slug => $tab_title ) {
if ( ! in_array( $tab_slug, $saved_tab_slugs ) && ! $this->functions->is_custom_tab( $tab_slug ) ) {
$new_tabs[ $tab_slug ] = $tab_title;
}
}
return $new_tabs;
}
return $tabs;
}
/**
* Determine if the user is on version 1.8 of AffiliateWP or newer
*
* @since 1.1
*
* @return boolean
*/
public function has_1_8() {
$return = true;
if ( version_compare( AFFILIATEWP_VERSION, '1.8', '<' ) ) {
$return = false;
}
return $return;
}
/**
* Determine if the user is on version 2.1.7 of AffiliateWP or later.
*
* @since 1.2
*
* @return boolean
*/
public function has_2_1_7() {
$return = true;
if ( version_compare( AFFILIATEWP_VERSION, '2.1.7', '<' ) ) {
$return = false;
}
return $return;
}
/**
* Determine if the user is on version 2.6 of AffiliateWP or later.
*
* @since 1.2.9
*
* @return boolean
*/
public function has_2_6() {
$return = true;
if ( version_compare( AFFILIATEWP_VERSION, '2.6', '<' ) ) {
$return = false;
}
return $return;
}
/**
* Determines if the post content has blocks.
*
* @since 1.2.9
*
* @param string $post_content Post content.
* @return bool True if the post content has blocks, otherwise false.
*/
public function has_blocks( $post_content ) {
$return = true;
if ( ! function_exists( 'has_blocks' ) || ! has_blocks( $post_content ) ) {
$return = false;
}
return $return;
}
/**
* Redirect to affiliate login page if content is accessed.
*
* @since 1.0.1
* @return void
*/
public function redirect() {
$protected = (array) affiliatewp_affiliate_area_tabs()->functions->protected_page_ids();
if ( empty( $protected ) ) {
return;
}
// On archives the queried object is a term or user whose ID can collide with a page ID.
if ( ! is_singular() ) {
return;
}
$post_id = get_queried_object_id();
if ( ! in_array( (int) $post_id, array_map( 'absint', $protected ), true ) ) {
return;
}
if ( $this->viewer_may_read_tab_pages() ) {
return;
}
$redirect = affiliate_wp()->settings->get( 'affiliates_page' ) ? get_permalink( affiliate_wp()->settings->get( 'affiliates_page' ) ) : site_url();
$redirect = apply_filters( 'affiliatewp-affiliate-area-tabs', $redirect );
wp_safe_redirect( $redirect );
exit;
}
/**
* Whether the current viewer may read the pages backing custom tabs.
*
* Matches the rule AffiliateWP applies to the Affiliate Area dashboard: active affiliates
* only. Administrators are allowed through so the page stays editable and previewable.
*
* @since 1.4.3
*
* @return bool
*/
public function viewer_may_read_tab_pages() {
if ( current_user_can( 'manage_affiliate_options' ) ) {
return true;
}
return affwp_is_affiliate() && affwp_is_active_affiliate();
}
/**
* Removes protected tab pages from REST collection queries for viewers who may not read them.
*
* @since 1.4.3
*
* @param array $args Query args.
* @param \WP_REST_Request $request REST request.
* @return array
*/
public function exclude_protected_pages( $args, $request ) {
$protected = (array) affiliatewp_affiliate_area_tabs()->functions->protected_page_ids();
// Editors, shop managers and other page editors still need the page in the block editor.
if ( empty( $protected ) || $this->viewer_may_read_tab_pages() || current_user_can( 'edit_pages' ) ) {
return $args;
}
$args['post__not_in'] = array_merge(
(array) ( isset( $args['post__not_in'] ) ? $args['post__not_in'] : array() ),
array_map( 'absint', $protected )
);
return $args;
}
/**
* Strips a protected tab page's body from any REST response for viewers who may not read it.
*
* @since 1.4.3
*
* @param \WP_REST_Response $response Response.
* @param \WP_Post $post Post.
* @return \WP_REST_Response
*/
public function gate_protected_page_response( $response, $post ) {
$protected = (array) affiliatewp_affiliate_area_tabs()->functions->protected_page_ids();
if ( ! in_array( (int) $post->ID, array_map( 'absint', $protected ), true ) || $this->viewer_may_read_tab_pages() || current_user_can( 'edit_post', $post->ID ) ) {
return $response;
}
$data = $response->get_data();
foreach ( array( 'content', 'excerpt' ) as $field ) {
if ( isset( $data[ $field ] ) ) {
$data[ $field ] = array( 'rendered' => '', 'protected' => true );
}
}
$response->set_data( $data );
return $response;
}
/**
* Keeps protected tab pages out of the sitemap.
*
* @since 1.4.3
*
* @param array $args Query args.
* @param string $post_type Post type.
* @return array
*/
public function exclude_protected_from_sitemap( $args, $post_type ) {
if ( 'page' !== $post_type ) {
return $args;
}
$args['post__not_in'] = array_merge(
(array) ( isset( $args['post__not_in'] ) ? $args['post__not_in'] : array() ),
array_map( 'absint', (array) affiliatewp_affiliate_area_tabs()->functions->protected_page_ids() )
);
return $args;
}
/**
* Modify plugin metalinks.
*
* @access public
* @since 1.0.0
* @param array $links The current links array.
* @param string $file A specific plugin table entry.
* @return array $links The modified links array
*/
public function plugin_meta( $links, $file ) {
if ( plugin_basename( $this->file ) === $file ) {
$plugins_link = [
'' . __( 'More add-ons', 'affiliatewp-affiliate-area-tabs' ) . '',
];
$links = array_merge( $links, $plugins_link );
}
return $links;
}
}
/**
* The main function responsible for returning the one true AffiliateWP_Affiliate_Area_Tabs
* Instance to functions everywhere.
*
* Use this function like you would a global variable, except without needing
* to declare the global.
*
* Example:
*
* @since 1.0
* @return object The one true AffiliateWP_Affiliate_Area_Tabs Instance
*/
function affiliatewp_affiliate_area_tabs() {
return AffiliateWP_Affiliate_Area_Tabs::instance();
}
}