[label, type, help]. Pure (no WordPress calls) so tests can
* check the bundled data against it.
*/
public static function fields() {
return array(
'tagline' => array( 'Tagline', 'text', 'One short line under the city name in the banner, e.g. "Where is America’s tropical star?"' ),
'language' => array( 'Language', 'text', 'Facts bar. E.g. "English, Spanish".' ),
'currency' => array( 'Currency', 'text', 'Facts bar. E.g. "US Dollar (USD)".' ),
'timezone' => array( 'Time zone', 'text', 'Facts bar. E.g. "GMT-5".' ),
'best_time' => array( 'Best time to visit', 'text', 'Facts bar. E.g. "March – May".' ),
'about_title' => array( 'About heading', 'text', 'Heading of the About section. Empty uses "Discover {city}".' ),
'about' => array( 'About text', 'html', 'History and introduction. Long text folds behind a "Read more" button.' ),
'about_image' => array( 'About photo', 'image', 'Photo beside the About text. Empty uses one of the city\'s tour photos.' ),
'spring' => array( 'Spring', 'textarea', 'Things to do in spring (2–3 sentences).' ),
'summer' => array( 'Summer', 'textarea', 'Things to do in summer.' ),
'fall' => array( 'Fall', 'textarea', 'Things to do in fall.' ),
'winter' => array( 'Winter', 'textarea', 'Things to do in winter.' ),
'southern' => array( 'Southern hemisphere', 'checkbox', 'Tick for cities south of the equator (e.g. Sydney, Cape Town) so the season months are flipped.' ),
);
}
public static function get( $term_id ) {
$saved = get_term_meta( (int) $term_id, self::META, true );
$saved = is_array( $saved ) ? $saved : array();
$guide = array();
foreach ( self::fields() as $key => $field ) {
$guide[ $key ] = isset( $saved[ $key ] ) ? $saved[ $key ] : ( 'checkbox' === $field[1] ? false : '' );
}
return $guide;
}
public static function sanitize( array $raw ) {
$clean = array();
foreach ( self::fields() as $key => $field ) {
$value = isset( $raw[ $key ] ) ? $raw[ $key ] : '';
switch ( $field[1] ) {
case 'html':
$clean[ $key ] = trim( wp_kses_post( (string) $value ) );
break;
case 'textarea':
$clean[ $key ] = trim( sanitize_textarea_field( (string) $value ) );
break;
case 'image':
$clean[ $key ] = esc_url_raw( (string) $value );
break;
case 'checkbox':
$clean[ $key ] = ! empty( $value );
break;
default:
$clean[ $key ] = trim( sanitize_text_field( (string) $value ) );
}
}
return $clean;
}
/** True when no text field is filled (the hemisphere flag alone doesn't count). */
public static function is_empty( array $guide ) {
foreach ( self::fields() as $key => $field ) {
if ( 'checkbox' !== $field[1] && 'about_image' !== $key && ! empty( $guide[ $key ] ) ) {
return false;
}
}
return true;
}
/**
* $guide with its empty fields taken from $defaults. Pure.
*/
public static function fill_empty( array $guide, array $defaults ) {
foreach ( $defaults as $key => $value ) {
if ( empty( $guide[ $key ] ) ) {
$guide[ $key ] = $value;
}
}
return $guide;
}
/**
* Months for each season. Pure.
*/
public static function season_months( $southern ) {
$north = array(
'spring' => 'March – May',
'summer' => 'June – August',
'fall' => 'September – November',
'winter' => 'December – February',
);
if ( ! $southern ) {
return $north;
}
return array(
'spring' => $north['fall'],
'summer' => $north['winter'],
'fall' => $north['spring'],
'winter' => $north['summer'],
);
}
/* ------------------------------------------------------------------ admin */
public static function edit_fields( $term ) {
if ( get_term_meta( $term->term_id, '_vc_region', true ) ) {
return; // Regions use their own page; the guide is for cities.
}
$guide = self::get( $term->term_id );
wp_nonce_field( 'vc_guide_save', 'vc_guide_nonce' );
?>
|
|
$field ) {
list( $label, $type, $help ) = $field;
$name = 'vc_guide[' . $key . ']';
$id = 'vc-guide-' . $key;
$value = $guide[ $key ];
?>
|
$name,
'textarea_rows' => 8,
'media_buttons' => false,
'teeny' => true,
)
);
} elseif ( 'textarea' === $type ) {
echo '';
} elseif ( 'checkbox' === $type ) {
echo '';
} elseif ( 'image' === $type ) {
self::image_field( $id, $name, (string) $value );
} else {
echo '';
}
?>
|
term_id, self::META, self::fill_empty( self::get( $term->term_id ), self::sanitize( $data[ $slug ] ) ) );
$done[] = $slug;
}
update_option( self::IMPORTED_OPTION, array_values( array_unique( $done ) ), false );
if ( array_diff( array_keys( $data ), $done ) ) {
set_transient( 'vc_city_guides_check', 1, HOUR_IN_SECONDS );
}
}
/* --------------------------------------------------------------- front end */
/** The guide is shown on page 1 of an unfiltered city listing only. */
public static function is_visible_here() {
$paged = max( (int) get_query_var( 'paged' ), isset( $_GET['paged'] ) ? absint( $_GET['paged'] ) : 1 ); // phpcs:ignore WordPress.Security.NonceVerification
return $paged <= 1 && ! VC_Display::filters_active( VC_Display::get_filter_state() );
}
/**
* Facts bar, About and Seasons for $term, or '' when the guide is empty.
*/
public static function render( $term ) {
$guide = self::get( $term->term_id );
if ( self::is_empty( $guide ) ) {
return '';
}
$city = $term->name;
$out = '';
$facts = array(
'language' => array( __( 'Language', 'viator-collector' ), 'lang' ),
'currency' => array( __( 'Currency', 'viator-collector' ), 'cur' ),
'timezone' => array( __( 'Time zone', 'viator-collector' ), 'time' ),
'best_time' => array( __( 'Best time to visit', 'viator-collector' ), 'sun' ),
);
$items = '';
foreach ( $facts as $key => $fact ) {
if ( '' !== $guide[ $key ] ) {
$items .= '' . self::icon( $fact[1] ) . '' . esc_html( $fact[0] ) . '' . esc_html( $guide[ $key ] ) . '';
}
}
if ( $items ) {
// Wrapped: direct children of .vc-app are forced full width.
$out .= '';
}
if ( '' !== $guide['about'] ) {
$img = $guide['about_image'] ? $guide['about_image'] : self::fallback_image( $term );
/* translators: %s: city name. */
$title = '' !== $guide['about_title'] ? $guide['about_title'] : sprintf( __( 'Discover %s', 'viator-collector' ), $city );
$out .= '';
if ( $img ) {
$out .= '
 . ')
';
}
/* translators: %s: city name. */
$out .= '
' . esc_html( sprintf( __( 'About %s', 'viator-collector' ), $city ) ) . '
'
. '
' . esc_html( $title ) . '
'
. '
' . wpautop( $guide['about'] ) . '
'
. '
'
. '
';
}
$months = self::season_months( $guide['southern'] );
$seasons = array(
'spring' => array( __( 'Spring', 'viator-collector' ), 'flower' ),
'summer' => array( __( 'Summer', 'viator-collector' ), 'sun' ),
'fall' => array( __( 'Fall', 'viator-collector' ), 'leaf' ),
'winter' => array( __( 'Winter', 'viator-collector' ), 'snow' ),
);
$cards = '';
foreach ( $seasons as $key => $season ) {
if ( '' !== $guide[ $key ] ) {
$cards .= '' . self::icon( $season[1] ) . ''
. '' . esc_html( $season[0] ) . '
' . esc_html( $months[ $key ] ) . ''
. '' . esc_html( $guide[ $key ] ) . '
';
}
}
if ( $cards ) {
/* translators: %s: city name. */
$out .= '' . esc_html__( 'Seasonal activities', 'viator-collector' ) . '
'
. '
' . esc_html( sprintf( __( 'What to do in %s, all year round', 'viator-collector' ), $city ) ) . '
'
. '
';
}
return $out;
}
public static function tagline( $term ) {
return self::get( $term->term_id )['tagline'];
}
/**
* A tour photo of the city that isn't the banner photo, cached for a day.
*/
private static function fallback_image( $term ) {
$key = 'vc_guide_img_' . $term->term_id;
$cached = get_transient( $key );
if ( false !== $cached ) {
return $cached;
}
$hero = vc_term_image_url( $term->term_id, $term->taxonomy );
$query = new WP_Query(
array(
'post_type' => 'viator_tour',
'post_status' => 'publish',
'posts_per_page' => 10,
'fields' => 'ids',
'no_found_rows' => true,
'update_post_term_cache' => false,
'tax_query' => array(
array(
'taxonomy' => $term->taxonomy,
'field' => 'term_id',
'terms' => $term->term_id,
),
),
)
);
$url = '';
foreach ( $query->posts as $post_id ) {
$candidate = (string) get_post_meta( (int) $post_id, '_vc_primary_image_url', true );
if ( '' !== $candidate && $candidate !== $hero ) {
$url = $candidate;
break;
}
}
set_transient( $key, $url, $url ? DAY_IN_SECONDS : HOUR_IN_SECONDS );
return $url;
}
private static function icon( $name ) {
$paths = array(
'lang' => '',
'cur' => '',
'time' => '',
'sun' => '',
'flower' => '',
'leaf' => '',
'snow' => '',
);
return '';
}
}