Files
hoponhopofftickets/viator-collector/includes/class-vc-routing.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";
}
}
}