Files
hoponhopofftickets/viator-collector/includes/class-vc-taxonomies.php
Amir Bagheri fd6dba3203 fix: destination cards show a tour photo again
vc_term_image_url() cached empty results for 12h and relied on an SQL
meta comparison to find a tour with a photo; destination cards site-wide
(homepage and region pages) were blank. Read the photo in PHP from up to
10 tours (primary, else first gallery image), use a fresh cache key, and
expire empty results after an hour. Plugin 1.9.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 16:47:01 +03:30

258 lines
8.8 KiB
PHP

<?php
defined( 'ABSPATH' ) || exit;
class VC_Taxonomies {
public static function init() {
add_action( 'init', array( __CLASS__, 'register' ) );
foreach ( array( 'viator_destination', 'viator_tag', 'viator_category' ) as $taxonomy ) {
add_action( "{$taxonomy}_add_form_fields", array( __CLASS__, 'add_image_field' ) );
add_action( "{$taxonomy}_edit_form_fields", array( __CLASS__, 'edit_image_field' ), 10, 1 );
add_action( "created_{$taxonomy}", array( __CLASS__, 'save_image_field' ) );
add_action( "edited_{$taxonomy}", array( __CLASS__, 'save_image_field' ) );
}
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_media' ) );
}
/**
* The media library is only needed on our term screens.
*/
public static function admin_media( $hook ) {
if ( 'term.php' !== $hook && 'edit-tags.php' !== $hook ) {
return;
}
$taxonomy = isset( $_GET['taxonomy'] ) ? sanitize_key( $_GET['taxonomy'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
if ( in_array( $taxonomy, array( 'viator_destination', 'viator_tag', 'viator_category' ), true ) ) {
wp_enqueue_media();
}
}
/**
* Media-library picker shared by the add and edit term forms.
*/
private static function image_picker( $value ) {
wp_nonce_field( 'vc_term_image', 'vc_term_image_nonce' );
?>
<input type="hidden" id="vc_image_url" name="vc_image_url" value="<?php echo esc_attr( $value ); ?>">
<button type="button" class="button" id="vc-term-img-pick"><?php echo esc_html__( 'Select image', 'viator-collector' ); ?></button>
<button type="button" class="button" id="vc-term-img-clear"><?php echo esc_html__( 'Remove', 'viator-collector' ); ?></button>
<div id="vc-term-img-preview" style="margin-top:8px;">
<?php if ( $value ) : ?>
<img src="<?php echo esc_url( $value ); ?>" alt="" style="max-width:200px;height:auto;border-radius:6px;">
<?php endif; ?>
</div>
<p class="description">
<?php echo esc_html__( 'Used on the Top Destinations cards and as this term archive\'s banner. If empty, a tour photo from this term is used automatically.', 'viator-collector' ); ?>
</p>
<script>
jQuery(function ($) {
var frame;
$('#vc-term-img-pick').on('click', function (e) {
e.preventDefault();
frame = wp.media({ library: { type: 'image' }, multiple: false });
frame.on('select', function () {
var img = frame.state().get('selection').first().toJSON();
$('#vc_image_url').val(img.url);
$('#vc-term-img-preview').html($('<img>', { src: img.url }).css({ maxWidth: '200px', height: 'auto', borderRadius: '6px' }));
});
frame.open();
});
$('#vc-term-img-clear').on('click', function (e) {
e.preventDefault();
$('#vc_image_url').val('');
$('#vc-term-img-preview').empty();
});
});
</script>
<?php
}
public static function add_image_field() {
?>
<div class="form-field">
<label><?php echo esc_html__( 'Image', 'viator-collector' ); ?></label>
<?php self::image_picker( '' ); ?>
</div>
<?php
}
public static function edit_image_field( $term ) {
$value = (string) get_term_meta( $term->term_id, '_vc_image_url', true );
?>
<tr class="form-field">
<th scope="row"><label><?php echo esc_html__( 'Image', 'viator-collector' ); ?></label></th>
<td><?php self::image_picker( $value ); ?></td>
</tr>
<?php
}
public static function save_image_field( $term_id ) {
if ( ! current_user_can( 'manage_categories' ) ) {
return;
}
if ( ! isset( $_POST['vc_term_image_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['vc_term_image_nonce'] ) ), 'vc_term_image' ) ) {
return;
}
if ( ! isset( $_POST['vc_image_url'] ) ) {
return;
}
$url = esc_url_raw( wp_unslash( $_POST['vc_image_url'] ) );
if ( '' === $url ) {
delete_term_meta( $term_id, '_vc_image_url' );
} else {
update_term_meta( $term_id, '_vc_image_url', $url );
}
delete_transient( 'vc_term_img2_' . absint( $term_id ) );
}
public static function register() {
self::register_taxonomy( 'viator_destination', __( 'Viator Destinations', 'viator-collector' ), __( 'Viator Destination', 'viator-collector' ), true, 'tour-destination' );
self::register_taxonomy( 'viator_tag', __( 'Viator Tags', 'viator-collector' ), __( 'Viator Tag', 'viator-collector' ), false, 'tour-tag' );
self::register_taxonomy( 'viator_category', __( 'Viator Categories', 'viator-collector' ), __( 'Viator Category', 'viator-collector' ), true, 'tour-category' );
}
private static function register_taxonomy( $taxonomy, $plural, $singular, $hierarchical, $slug ) {
register_taxonomy(
$taxonomy,
array( 'viator_tour' ),
array(
'labels' => array(
'name' => $plural,
'singular_name' => $singular,
),
'public' => true,
'hierarchical' => $hierarchical,
'rewrite' => array( 'slug' => $slug ),
'show_in_rest' => true,
)
);
}
public static function get_or_create_destination( $item ) {
$name = self::term_name( $item );
if ( '' === $name ) {
return 0;
}
$id = vc_first_non_empty( array( vc_array_get( $item, 'destinationId' ), vc_array_get( $item, 'id' ) ) );
$ref = vc_first_non_empty( array( vc_array_get( $item, 'destinationRef' ), vc_array_get( $item, 'ref' ) ) );
return self::get_or_create_term( 'viator_destination', $name, '_vc_viator_destination_id', $id, array( '_vc_viator_destination_ref' => $ref ) );
}
public static function get_or_create_tag( $item ) {
$name = self::term_name( $item );
$id = vc_first_non_empty( array( vc_array_get( $item, 'tagId' ), vc_array_get( $item, 'id' ) ) );
return self::get_or_create_term( 'viator_tag', $name, '_vc_viator_tag_id', $id );
}
/**
* Resolve a destination term from a numeric Viator destination id, using
* a name looked up from the destinations dictionary.
*/
public static function get_or_create_destination_by_id( $id, $name = '' ) {
$id = (string) $id;
if ( '' === $id ) {
return 0;
}
$name = sanitize_text_field( (string) $name );
if ( '' === $name ) {
/* translators: %s: Viator destination id. */
$name = sprintf( __( 'Destination %s', 'viator-collector' ), $id );
}
return self::get_or_create_term( 'viator_destination', $name, '_vc_viator_destination_id', $id );
}
/**
* Resolve a tag term from a numeric Viator tag id, using a name looked up
* from the tags dictionary.
*/
public static function get_or_create_tag_by_id( $id, $name = '' ) {
$id = (string) $id;
if ( '' === $id ) {
return 0;
}
$name = sanitize_text_field( (string) $name );
if ( '' === $name ) {
/* translators: %s: Viator tag id. */
$name = sprintf( __( 'Tag %s', 'viator-collector' ), $id );
}
return self::get_or_create_term( 'viator_tag', $name, '_vc_viator_tag_id', $id );
}
public static function get_or_create_category( $item ) {
$name = self::term_name( $item );
$id = vc_first_non_empty( array( vc_array_get( $item, 'categoryId' ), vc_array_get( $item, 'id' ) ) );
return self::get_or_create_term( 'viator_category', $name, '_vc_viator_category_id', $id );
}
private static function term_name( $item ) {
if ( is_scalar( $item ) ) {
return sanitize_text_field( (string) $item );
}
if ( ! is_array( $item ) ) {
return '';
}
return sanitize_text_field(
vc_first_non_empty(
array(
vc_array_get( $item, 'name' ),
vc_array_get( $item, 'destinationName' ),
vc_array_get( $item, 'tagName' ),
vc_array_get( $item, 'categoryName' ),
vc_array_get( $item, 'title' ),
)
)
);
}
private static function get_or_create_term( $taxonomy, $name, $meta_key, $viator_id = '', $extra_meta = array() ) {
if ( '' === $name ) {
return 0;
}
if ( '' !== (string) $viator_id ) {
$found = get_terms(
array(
'taxonomy' => $taxonomy,
'hide_empty' => false,
'number' => 1,
'meta_query' => array(
array(
'key' => $meta_key,
'value' => (string) $viator_id,
),
),
)
);
if ( ! is_wp_error( $found ) && ! empty( $found ) ) {
self::update_term_meta( $found[0]->term_id, $meta_key, $viator_id, $extra_meta );
return (int) $found[0]->term_id;
}
}
$term = term_exists( $name, $taxonomy );
if ( 0 === $term || null === $term ) {
$term = wp_insert_term( $name, $taxonomy );
}
if ( is_wp_error( $term ) ) {
VC_Logger::log( 'Term error: ' . $term->get_error_message(), true );
return 0;
}
$term_id = is_array( $term ) ? (int) $term['term_id'] : (int) $term;
self::update_term_meta( $term_id, $meta_key, $viator_id, $extra_meta );
return $term_id;
}
private static function update_term_meta( $term_id, $meta_key, $viator_id, $extra_meta ) {
if ( '' !== (string) $viator_id ) {
update_term_meta( $term_id, $meta_key, sanitize_text_field( (string) $viator_id ) );
}
foreach ( $extra_meta as $key => $value ) {
if ( '' !== (string) $value ) {
update_term_meta( $term_id, $key, sanitize_text_field( (string) $value ) );
}
}
}
}