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>
258 lines
8.8 KiB
PHP
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 ) );
|
|
}
|
|
}
|
|
}
|
|
}
|