XylotrechusZ
<?php
namespace WeDevs\Dokan;
/**
* Dokan rewrite rules class
*
* @package Dokan
*/
class Rewrites {
public $query_vars = [];
public $custom_store_url = '';
/**
* Hook into the functions
*/
public function __construct() {
$this->custom_store_url = dokan_get_option( 'custom_store_url', 'dokan_general', 'store' );
add_action( 'init', [ $this, 'register_rule' ] );
add_action( 'pre_get_posts', [ $this, 'store_query_filter' ] );
add_filter( 'woocommerce_get_query_vars', [ $this, 'resolve_wc_query_conflict' ] );
add_filter( 'template_include', [ $this, 'store_template' ], 99 );
add_filter( 'template_include', [ $this, 'product_edit_template' ], 99 );
add_filter( 'template_include', [ $this, 'store_toc_template' ], 99 );
add_filter( 'query_vars', [ $this, 'register_query_var' ] );
add_filter( 'woocommerce_get_breadcrumb', [ $this, 'store_page_breadcrumb' ] );
add_filter( 'tiny_mce_before_init', [ $this, 'remove_h1_from_heading_in_edit_product_page' ] );
add_action( 'wp', [ $this, 'maybe_flash_rewrite_rules' ], 99 );
}
/**
* Generate breadcrumb for store page
*
* @param array $crumbs
*
* @since 2.4.7
*
* @return void | array $crumbs
*/
public function store_page_breadcrumb( $crumbs ) {
if ( ! dokan_is_store_page() ) {
return $crumbs;
}
if ( function_exists( 'yoast_breadcrumb' ) && \WPSEO_Options::get( 'breadcrumbs-enable' ) ) {
unset( $crumbs );
return;
}
$author = get_query_var( $this->custom_store_url );
$seller_info = get_user_by( 'slug', $author );
if ( ! $seller_info ) {
return;
}
$crumbs[1] = [ ucwords( $this->custom_store_url ), get_permalink( dokan_get_option( 'store_listing', 'dokan_pages' ) ) ];
$crumbs[2] = [ $author, dokan_get_store_url( $seller_info->data->ID ) ];
return $crumbs;
}
/**
* Check if WooCommerce installed or not
*
* @return boolean
*/
public function is_woo_installed() {
return function_exists( 'WC' );
}
/**
* Register the rewrite rule
*
* @return void
*/
public function register_rule() {
$this->query_vars = apply_filters(
'dokan_query_var_filter', [
'products',
'new-product',
'orders',
'withdraw',
'withdraw-requests',
'reverse-withdrawal',
'settings',
'edit-account',
'account-migration',
]
);
foreach ( $this->query_vars as $var ) {
add_rewrite_endpoint( $var, EP_PAGES );
}
$permalinks = get_option( 'woocommerce_permalinks', [] );
if ( isset( $permalinks['product_base'] ) ) {
$base = substr( $permalinks['product_base'], 1 );
}
// default base is product
$base = empty( $base ) ? 'product' : $base;
// get custom store url from settings so that we can use it from other places
$this->custom_store_url = dokan_get_option( 'custom_store_url', 'dokan_general', 'store' );
// special treatment for product cat
if ( stripos( $base, 'product_cat' ) ) {
// get the category base. usually: shop
$base_array = explode( '/', ltrim( $base, '/' ) ); // remove first '/' and explode
$cat_base = isset( $base_array[0] ) ? $base_array[0] : 'shop';
add_rewrite_rule( $cat_base . '/(.+?)/([^/]+)(/[0-9]+)?/edit?$', 'index.php?product_cat=$matches[1]&product=$matches[2]&page=$matches[3]&edit=true', 'top' );
} else {
add_rewrite_rule( $base . '/([^/]+)(/[0-9]+)?/edit/?$', 'index.php?product=$matches[1]&page=$matches[2]&edit=true', 'top' );
}
add_rewrite_rule( $this->custom_store_url . '/([^/]+)/?$', 'index.php?' . $this->custom_store_url . '=$matches[1]', 'top' );
add_rewrite_rule( $this->custom_store_url . '/([^/]+)/page/?([0-9]{1,})/?$', 'index.php?' . $this->custom_store_url . '=$matches[1]&paged=$matches[2]', 'top' );
add_rewrite_rule( $this->custom_store_url . '/([^/]+)/section/?([0-9]{1,})/?$', 'index.php?' . $this->custom_store_url . '=$matches[1]&term=$matches[2]&term_section=true', 'top' );
add_rewrite_rule( $this->custom_store_url . '/([^/]+)/section/?([0-9]{1,})/page/?([0-9]{1,})/?$', 'index.php?' . $this->custom_store_url . '=$matches[1]&term=$matches[2]&paged=$matches[3]&term_section=true', 'top' );
add_rewrite_rule( $this->custom_store_url . '/([^/]+)/toc?$', 'index.php?' . $this->custom_store_url . '=$matches[1]&toc=true', 'top' );
add_rewrite_rule( $this->custom_store_url . '/([^/]+)/toc/page/?([0-9]{1,})/?$', 'index.php?' . $this->custom_store_url . '=$matches[1]&paged=$matches[2]&toc=true', 'top' );
do_action( 'dokan_rewrite_rules_loaded', $this->custom_store_url );
}
/**
* Resolve query var conflicts with WooCommerce
*
* @param array $query_vars
*
* @since 2.9.13
*
* @return array
*/
public function resolve_wc_query_conflict( $query_vars ) {
global $post;
$dashboard_id = apply_filters( 'dokan_get_dashboard_page_id', dokan_get_option( 'dashboard', 'dokan_pages', 0 ) );
if ( ! empty( $post->ID ) && apply_filters( 'dokan_get_current_page_id', $post->ID ) === absint( $dashboard_id ) ) {
unset( $query_vars['orders'] );
unset( $query_vars['edit-account'] );
}
return $query_vars;
}
/**
* Register the query var
*
* @param array $vars
*
* @return array
*/
public function register_query_var( $vars ) {
$vars[] = $this->custom_store_url;
$vars[] = 'edit';
$vars[] = 'term_section';
$vars[] = 'toc';
foreach ( $this->query_vars as $var ) {
$vars[] = $var;
}
return $vars;
}
/**
* Include store template
*
* @param type $template
*
* @return string
*/
public function store_template( $template ) {
$store_name = get_query_var( $this->custom_store_url );
if ( ! $this->is_woo_installed() ) {
return $template;
}
if ( ! empty( $store_name ) ) {
$store_user = get_user_by( 'slug', $store_name );
if ( ! $store_user ) {
return get_404_template();
}
// Bell out for Vendor Stuff extensions
if ( ! is_super_admin( $store_user->ID ) && user_can( $store_user->ID, 'vendor_staff' ) ) {
return get_404_template();
}
// no user found
if ( ! $store_user ) {
return get_404_template();
}
// check if the user is seller
if ( ! dokan_is_user_seller( $store_user->ID ) ) {
return get_404_template();
}
return dokan_locate_template( 'store.php' );
}
return $template;
}
/**
* Returns the terms_and_conditions template
*
* @param string $template
*
* @since 2.3
*
* @return string
*/
public function store_toc_template( $template ) {
if ( ! $this->is_woo_installed() ) {
return $template;
}
if ( get_query_var( 'toc' ) ) {
$store_name = get_query_var( $this->custom_store_url );
if ( ! empty( $store_name ) ) {
$seller = get_user_by( 'slug', $store_name );
//redirect to 404 if no seller found
if ( ! $seller ) {
return get_404_template();
}
return dokan_locate_template( 'store-toc.php' );
}
}
return $template;
}
/**
* Returns the edit product template
*
* @param string $template
*
* @return string
*/
public function product_edit_template( $template ) {
if ( ! $this->is_woo_installed() ) {
return $template;
}
if ( ! current_user_can( 'dokan_edit_product' ) ) {
return $template;
}
if ( ! ( get_query_var( 'edit' ) && is_singular( 'product' ) ) ) {
return $template;
}
$edit_product_url = dokan_locate_template( 'products/edit-product-single.php' );
return apply_filters( 'dokan_get_product_edit_template', $edit_product_url );
}
/**
* Remove h1 tag in edit product page.
*
* @param $args
*
* @return mixed
*/
public function remove_h1_from_heading_in_edit_product_page( $args ) {
global $wp;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ( dokan_is_seller_dashboard() && isset( $wp->query_vars['settings'] ) && $wp->query_vars['settings'] === 'store' ) || ( ! empty( $_GET['product_id'] ) && ! empty( $_GET['action'] ) && ! empty( $_GET['_dokan_edit_product_nonce'] ) ) ) {
// Just omit h1 from the list
$args['block_formats'] = 'Paragraph=p;Heading 2=h2;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6;Pre=pre';
return $args;
}
return $args;
}
/**
* Store query filter
*
* Handles the product filtering by category in store page
*
* @param object $query
*
* @return void
*/
public function store_query_filter( $query ) {
global $wp_query;
$author = get_query_var( $this->custom_store_url );
if ( ! is_admin() && $query->is_main_query() && ! empty( $author ) ) {
$seller_info = get_user_by( 'slug', $author );
if ( ! $seller_info ) {
return get_404_template();
}
$store_info = dokan_get_store_info( $seller_info->data->ID );
$product_ppp = dokan_get_option( 'store_products_per_page', 'dokan_general', 12 );
do_action( 'dokan_store_page_query_filter', $query, $store_info );
set_query_var( 'posts_per_page', apply_filters( 'dokan_store_products_per_page', $product_ppp ) );
$query->set( 'post_type', 'product' );
$query->set( 'author_name', $author );
$tax_query = [];
$query->query['term_section'] = isset( $query->query['term_section'] ) ? $query->query['term_section'] : [];
$attributes = dokan_get_chosen_taxonomy_attributes();
if ( ! empty( $attributes ) ) {
foreach ( $attributes as $key => $attribute ) {
$tax_query[] = [
'taxonomy' => $key,
'field' => 'slug',
'terms' => $attribute['terms'],
];
}
}
if ( $query->query['term_section'] ) {
array_push(
$tax_query, [
'taxonomy' => 'product_cat',
'field' => 'term_id',
'terms' => $query->query['term'],
]
);
}
// Hide out of stock products
$product_visibility_terms = wc_get_product_visibility_term_ids();
$product_visibility_not_in = [ is_search() && $query->is_main_query() ? $product_visibility_terms['exclude-from-search'] : $product_visibility_terms['exclude-from-catalog'] ];
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$product_visibility_not_in[] = $product_visibility_terms['outofstock'];
}
if ( ! empty( $product_visibility_not_in ) ) {
array_push(
$tax_query, [
'taxonomy' => 'product_visibility',
'field' => 'term_taxonomy_id',
'terms' => $product_visibility_not_in,
'operator' => 'NOT IN',
]
);
}
$query->set( 'tax_query', apply_filters( 'dokan_store_tax_query', $tax_query ) );
if ( ! empty( $_GET['product_name'] ) ) { //phpcs:ignore
$product_name = wc_clean( wp_unslash( $_GET['product_name'] ) ); //phpcs:ignore
$query->set( 's', $product_name );
}
// set orderby param
$ordering = $this->get_catalog_ordering_args();
$query->set( 'orderby', $ordering['orderby'] );
$query->set( 'order', $ordering['order'] );
}
}
/**
* Returns an array of arguments for ordering products based on the selected values.
*
* @since 3.2.7
*
* @param string $orderby Order by param
* @param string $order Order param
*
* @return array
*/
public function get_catalog_ordering_args( $orderby = '', $order = '' ) {
// Get ordering from query string unless defined.
if ( ! $orderby ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$orderby_value = isset( $_GET['product_orderby'] ) ? wc_clean( wp_unslash( $_GET['product_orderby'] ) ) : wc_clean( get_query_var( 'product_orderby' ) ); //phpcs:ignore
if ( ! $orderby_value ) {
if ( is_search() ) {
$orderby_value = 'relevance';
} else {
$orderby_value = apply_filters( 'dokan_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby', 'menu_order' ) );
}
}
// Get order + orderby args from string.
$orderby_value = is_array( $orderby_value ) ? $orderby_value : explode( '-', $orderby_value );
$orderby = esc_attr( $orderby_value[0] );
$order = ! empty( $orderby_value[1] ) ? $orderby_value[1] : $order;
}
// Convert to correct format.
$orderby = strtolower( is_array( $orderby ) ? (string) current( $orderby ) : (string) $orderby );
$order = strtoupper( is_array( $order ) ? (string) current( $order ) : (string) $order );
$args = array(
'orderby' => $orderby,
'order' => ( 'DESC' === $order ) ? 'DESC' : 'ASC',
'meta_key' => '', // @codingStandardsIgnoreLine
);
switch ( $orderby ) {
case 'id':
$args['orderby'] = 'ID';
break;
case 'menu_order':
$args['orderby'] = 'menu_order title';
break;
case 'title':
$args['orderby'] = 'title';
$args['order'] = ( 'DESC' === $order ) ? 'DESC' : 'ASC';
break;
case 'relevance':
$args['orderby'] = 'relevance';
$args['order'] = 'DESC';
break;
case 'rand':
$args['orderby'] = 'rand'; // @codingStandardsIgnoreLine
break;
case 'date':
$args['orderby'] = 'date ID';
$args['order'] = ( 'ASC' === $order ) ? 'ASC' : 'DESC';
break;
case 'price':
$callback = 'DESC' === $order ? 'order_by_price_desc_post_clauses' : 'order_by_price_asc_post_clauses';
add_filter( 'posts_clauses', [ $this, $callback ] );
break;
case 'popularity':
add_filter( 'posts_clauses', [ $this, 'order_by_popularity_post_clauses' ] );
break;
case 'rating':
add_filter( 'posts_clauses', [ $this, 'order_by_rating_post_clauses' ] );
break;
}
return apply_filters( 'dokan_get_store_products_ordering_args', $args, $orderby, $order );
}
/**
* Handle numeric price sorting
*
* @since 3.2.7
*
* @param array $args Query args
*
* @return array
*/
public function order_by_price_asc_post_clauses( $args ) {
$args['join'] = $this->append_product_sorting_table_join( $args['join'] );
$args['orderby'] = ' wc_product_meta_lookup.min_price ASC, wc_product_meta_lookup.product_id ASC ';
return $args;
}
/**
* Handle numeric price sorting
*
* @since 3.2.7
*
* @param array $args Query args
*
* @return array
*/
public function order_by_price_desc_post_clauses( $args ) {
$args['join'] = $this->append_product_sorting_table_join( $args['join'] );
$args['orderby'] = ' wc_product_meta_lookup.max_price DESC, wc_product_meta_lookup.product_id DESC ';
return $args;
}
/**
* WP Core does not let us change the sort direction for individual orderby params
*
* This lets us sort by meta value desc, and have a second orderby param
*
* @since 3.2.7
*
* @param array $args Query args
*
* @return array
*/
public function order_by_popularity_post_clauses( $args ) {
$args['join'] = $this->append_product_sorting_table_join( $args['join'] );
$args['orderby'] = ' wc_product_meta_lookup.total_sales DESC, wc_product_meta_lookup.product_id DESC ';
return $args;
}
/**
* Order by rating post clauses
*
* @since 3.2.7
*
* @param array $args Query args
*
* @return array
*/
public function order_by_rating_post_clauses( $args ) {
$args['join'] = $this->append_product_sorting_table_join( $args['join'] );
$args['orderby'] = ' wc_product_meta_lookup.average_rating DESC, wc_product_meta_lookup.rating_count DESC, wc_product_meta_lookup.product_id DESC ';
return $args;
}
/**
* Join wc_product_meta_lookup to posts if not already joined.
*
* @since 3.2.7
*
* @param string $sql SQL join
*
* @return string
*/
private function append_product_sorting_table_join( $sql ) {
global $wpdb;
if ( ! strstr( $sql, 'wc_product_meta_lookup' ) ) {
$sql .= " LEFT JOIN {$wpdb->wc_product_meta_lookup} wc_product_meta_lookup ON $wpdb->posts.ID = wc_product_meta_lookup.product_id ";
}
return $sql;
}
/**
* Flush rewrite rules if the version is 4.0 or above.
*
* @since 4.0.0
*
* @return void
*/
public function maybe_flash_rewrite_rules() {
$flash = get_option( 'dokan_rewrite_rules_needs_flashing', 'yes' );
if ( 'yes' !== $flash ) {
return;
}
dokan()->flush_rewrite_rules();
update_option( 'dokan_rewrite_rules_needs_flashing', 'no', true );
}
}