140 lines
4.6 KiB
PHP
140 lines
4.6 KiB
PHP
<?php
|
|
defined( 'ABSPATH' ) || exit;
|
|
|
|
/**
|
|
* Place-based URLs from the Viator Structure: /{region}/, /{city}/ and
|
|
* /{city}/{tour}/. Old /tour-destination/… and /tours/… addresses 301 to
|
|
* the new ones, and a tour opened under the wrong city 301s to its one
|
|
* canonical city.
|
|
*/
|
|
class VC_Routing {
|
|
private static $product_city = array();
|
|
|
|
public static function init() {
|
|
// After the CPT/taxonomies register (default priority 10).
|
|
add_action( 'init', array( __CLASS__, 'add_rules' ), 20 );
|
|
add_filter( 'term_link', array( __CLASS__, 'term_link' ), 10, 3 );
|
|
add_filter( 'post_type_link', array( __CLASS__, 'post_type_link' ), 10, 2 );
|
|
// Ahead of core's redirect_canonical (priority 10).
|
|
add_action( 'template_redirect', array( __CLASS__, 'redirect' ), 5 );
|
|
add_action( 'wp_head', array( __CLASS__, 'term_canonical' ), 1 );
|
|
}
|
|
|
|
public static function add_rules() {
|
|
foreach ( VC_Structure::rewrite_rules( VC_Structure::get() ) as $regex => $query ) {
|
|
add_rewrite_rule( $regex, $query, 'top' );
|
|
}
|
|
if ( get_option( 'vc_flush_rewrite' ) ) {
|
|
delete_option( 'vc_flush_rewrite' );
|
|
flush_rewrite_rules( false );
|
|
}
|
|
}
|
|
|
|
public static function is_configured_term( $term ) {
|
|
if ( ! $term instanceof WP_Term || 'viator_destination' !== $term->taxonomy ) {
|
|
return false;
|
|
}
|
|
if ( get_term_meta( $term->term_id, '_vc_region', true ) ) {
|
|
return true;
|
|
}
|
|
$viator_id = (string) get_term_meta( $term->term_id, '_vc_viator_destination_id', true );
|
|
if ( '' === $viator_id ) {
|
|
return false;
|
|
}
|
|
$cities = VC_Structure::cities( VC_Structure::get() );
|
|
return isset( $cities[ $viator_id ] );
|
|
}
|
|
|
|
/**
|
|
* Slug of the city a tour's URL lives under, or '' (it stays at /tours/).
|
|
*/
|
|
public static function product_city( $post_id ) {
|
|
$post_id = (int) $post_id;
|
|
if ( isset( self::$product_city[ $post_id ] ) ) {
|
|
return self::$product_city[ $post_id ];
|
|
}
|
|
$dest_ids = array();
|
|
$destinations = json_decode( (string) get_post_meta( $post_id, '_vc_destinations', true ), true );
|
|
foreach ( is_array( $destinations ) ? $destinations : array() as $destination ) {
|
|
$ref = is_array( $destination ) ? ( $destination['ref'] ?? ( $destination['destinationId'] ?? '' ) ) : $destination;
|
|
if ( '' !== (string) $ref ) {
|
|
$dest_ids[] = (string) $ref;
|
|
}
|
|
}
|
|
$primary = (string) get_post_meta( $post_id, '_vc_primary_destination', true );
|
|
|
|
self::$product_city[ $post_id ] = VC_Structure::canonical_city( VC_Structure::get(), $dest_ids, $primary );
|
|
return self::$product_city[ $post_id ];
|
|
}
|
|
|
|
public static function term_link( $url, $term, $taxonomy ) {
|
|
if ( 'viator_destination' === $taxonomy && self::is_configured_term( $term ) ) {
|
|
return home_url( '/' . $term->slug . '/' );
|
|
}
|
|
return $url;
|
|
}
|
|
|
|
public static function post_type_link( $url, $post ) {
|
|
if ( 'viator_tour' !== $post->post_type || '' === $post->post_name ) {
|
|
return $url;
|
|
}
|
|
$city = self::product_city( $post->ID );
|
|
if ( '' === $city ) {
|
|
return $url;
|
|
}
|
|
return home_url( '/' . $city . '/' . $post->post_name . '/' );
|
|
}
|
|
|
|
public static function redirect() {
|
|
if ( is_preview() ) {
|
|
return;
|
|
}
|
|
$canonical = '';
|
|
if ( is_singular( 'viator_tour' ) ) {
|
|
$post = get_queried_object();
|
|
if ( $post && 'publish' === $post->post_status ) {
|
|
$canonical = get_permalink( $post );
|
|
}
|
|
} elseif ( is_tax( 'viator_destination' ) ) {
|
|
$term = get_queried_object();
|
|
if ( self::is_configured_term( $term ) ) {
|
|
$canonical = get_term_link( $term );
|
|
}
|
|
}
|
|
if ( ! $canonical || is_wp_error( $canonical ) ) {
|
|
return;
|
|
}
|
|
|
|
$request = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
|
$parts = explode( '?', $request, 2 );
|
|
$current = untrailingslashit( rawurldecode( $parts[0] ) );
|
|
$target = untrailingslashit( rawurldecode( (string) wp_parse_url( $canonical, PHP_URL_PATH ) ) );
|
|
if ( $current === $target ) {
|
|
return;
|
|
}
|
|
if ( isset( $parts[1] ) && '' !== $parts[1] ) {
|
|
$canonical .= '?' . $parts[1];
|
|
}
|
|
wp_safe_redirect( $canonical, 301 );
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Core prints rel=canonical for single tours only; add it for region and
|
|
* city archives unless an SEO plugin already handles canonicals.
|
|
*/
|
|
public static function term_canonical() {
|
|
if ( defined( 'WPSEO_VERSION' ) || defined( 'RANK_MATH_VERSION' ) || ! is_tax( 'viator_destination' ) ) {
|
|
return;
|
|
}
|
|
$term = get_queried_object();
|
|
if ( ! self::is_configured_term( $term ) ) {
|
|
return;
|
|
}
|
|
$link = get_term_link( $term );
|
|
if ( ! is_wp_error( $link ) ) {
|
|
echo '<link rel="canonical" href="' . esc_url( $link ) . '">' . "\n";
|
|
}
|
|
}
|
|
}
|