From 930b7e053462cc2a349ca47a2004c31613e4b9a4 Mon Sep 17 00:00:00 2001 From: Amir Bagheri Date: Thu, 29 Oct 2026 15:33:01 +0330 Subject: [PATCH] fix: paginated region/city pages, no routes before first save, safer structure save - Add /{slug}/page/N/ rules and keep the page number through 301s - Route only regions synced to a term, so unsaved presets never claim /europe/ etc. - Flush rewrites on the request after activation so structure routes survive - Suggest the existing term slug for new cities (keeps live /tour-destination/ URLs) - Refuse saves without the editor sentinel; confirm before saving an empty structure - Show term-sync failures to the admin instead of reporting success Co-Authored-By: Claude Opus 5.5 --- viator-collector.zip | Bin 180138 -> 181484 bytes .../assets/js/vc-structure-admin.js | 25 +++++++- viator-collector/includes/class-vc-plugin.php | 3 + .../includes/class-vc-routing.php | 8 +++ .../includes/class-vc-settings.php | 27 +++++++++ .../includes/class-vc-structure-admin.php | 55 +++++++++++++++--- .../includes/class-vc-structure.php | 25 ++++++-- viator-collector/tests/test-structure.php | 46 +++++++++++---- 8 files changed, 161 insertions(+), 28 deletions(-) diff --git a/viator-collector.zip b/viator-collector.zip index 65ab1eba13cbb29e82cb4cec18bb031fc69ef845..7a24480e1d167f20c489f8176a9107005a9cf6d9 100644 GIT binary patch delta 21472 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 Bh9Up} diff --git a/viator-collector/assets/js/vc-structure-admin.js b/viator-collector/assets/js/vc-structure-admin.js index 1547fa3..bf7dea7 100644 --- a/viator-collector/assets/js/vc-structure-admin.js +++ b/viator-collector/assets/js/vc-structure-admin.js @@ -43,7 +43,7 @@ var row = fromTemplate('vc-tpl-city'); row.querySelector('[data-f="id"]').value = item.id; row.querySelector('[data-f="name"]').value = item.name; - row.querySelector('[data-f="slug"]').value = slugify(item.name); + row.querySelector('[data-f="slug"]').value = item.slug || slugify(item.name); row.querySelector('.vc-city__name').textContent = item.name; row.querySelector('.vc-city__id').textContent = item.id; region.querySelector('.vc-cities tbody').appendChild(row); @@ -147,8 +147,27 @@ region.querySelector('[data-f="name"]').focus(); }); - form.addEventListener('submit', function () { - list.querySelectorAll('.vc-region').forEach(function (region, i) { + function hidden(name) { + var input = document.createElement('input'); + input.type = 'hidden'; + input.name = name; + input.value = '1'; + form.appendChild(input); + } + + form.addEventListener('submit', function (e) { + var regions = list.querySelectorAll('.vc-region'); + if (!regions.length) { + if (!window.confirm(i18n.confirmEmpty)) { + e.preventDefault(); + return; + } + hidden('vc_confirm_empty'); + } + // Proves the editor ran: the server refuses a save without it, so a + // failed script can never submit an empty structure by accident. + hidden('vc_structure_present'); + regions.forEach(function (region, i) { region.querySelectorAll('.vc-region__head [data-f]').forEach(function (input) { input.name = 'vc_structure[' + i + '][' + input.dataset.f + ']'; }); diff --git a/viator-collector/includes/class-vc-plugin.php b/viator-collector/includes/class-vc-plugin.php index b067e0f..388d6cd 100644 --- a/viator-collector/includes/class-vc-plugin.php +++ b/viator-collector/includes/class-vc-plugin.php @@ -60,6 +60,9 @@ final class VC_Plugin { VC_Taxonomies::register(); VC_Cron::maybe_schedule(); flush_rewrite_rules(); + // Structure routes are added on init, which has not run for this + // request; flush again on the next one so they are included. + update_option( 'vc_flush_rewrite', 1 ); } public static function deactivate() { diff --git a/viator-collector/includes/class-vc-routing.php b/viator-collector/includes/class-vc-routing.php index 67569dd..b308f0b 100644 --- a/viator-collector/includes/class-vc-routing.php +++ b/viator-collector/includes/class-vc-routing.php @@ -99,6 +99,13 @@ class VC_Routing { $term = get_queried_object(); if ( self::is_configured_term( $term ) ) { $canonical = get_term_link( $term ); + // Keep the page: /tour-destination/x/page/2/ and /x/?paged=2 + // both land on /x/page/2/ (the form core's redirect_canonical + // would produce anyway). + $paged = (int) get_query_var( 'paged' ); + if ( $paged > 1 && ! is_wp_error( $canonical ) ) { + $canonical = trailingslashit( $canonical ) . 'page/' . $paged . '/'; + } } } if ( ! $canonical || is_wp_error( $canonical ) ) { @@ -114,6 +121,7 @@ class VC_Routing { } if ( isset( $parts[1] ) && '' !== $parts[1] ) { $canonical .= '?' . $parts[1]; + $canonical = remove_query_arg( 'paged', $canonical ); } wp_safe_redirect( $canonical, 301 ); exit; diff --git a/viator-collector/includes/class-vc-settings.php b/viator-collector/includes/class-vc-settings.php index 9ce82b1..71bdc63 100644 --- a/viator-collector/includes/class-vc-settings.php +++ b/viator-collector/includes/class-vc-settings.php @@ -63,6 +63,33 @@ class VC_Settings { } } + // Suggested URL slug for the Viator Structure screen: keep an existing + // term's slug so live /tour-destination/{slug}/ links still resolve + // (and 301), otherwise WordPress's own slug for the name. + $existing = array(); + if ( $results ) { + $terms = get_terms( + array( + 'taxonomy' => 'viator_destination', + 'hide_empty' => false, + 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery + array( + 'key' => '_vc_viator_destination_id', + 'value' => array_column( $results, 'id' ), + 'compare' => 'IN', + ), + ), + ) + ); + foreach ( is_wp_error( $terms ) ? array() : $terms as $term ) { + $existing[ (string) get_term_meta( $term->term_id, '_vc_viator_destination_id', true ) ] = $term->slug; + } + } + foreach ( $results as &$result ) { + $result['slug'] = isset( $existing[ $result['id'] ] ) ? $existing[ $result['id'] ] : sanitize_title( $result['name'] ); + } + unset( $result ); + wp_send_json_success( array( 'results' => $results ) ); } diff --git a/viator-collector/includes/class-vc-structure-admin.php b/viator-collector/includes/class-vc-structure-admin.php index 590ba76..ac0d60a 100644 --- a/viator-collector/includes/class-vc-structure-admin.php +++ b/viator-collector/includes/class-vc-structure-admin.php @@ -10,6 +10,8 @@ defined( 'ABSPATH' ) || exit; class VC_Structure_Admin { const PAGE = 'viator-collector-structure'; + private static $sync_errors = array(); + public static function init() { add_action( 'admin_menu', array( __CLASS__, 'admin_menu' ) ); add_action( 'admin_post_vc_save_structure', array( __CLASS__, 'handle_save' ) ); @@ -44,7 +46,8 @@ class VC_Structure_Admin { 'failed' => __( 'Search failed.', 'viator-collector' ), 'add' => __( 'Add', 'viator-collector' ), 'used' => __( 'Already added', 'viator-collector' ), - 'confirm' => __( 'Remove this region and all its cities from the structure?', 'viator-collector' ), + 'confirm' => __( 'Remove this region and all its cities from the structure?', 'viator-collector' ), + 'confirmEmpty' => __( 'Save with no regions at all? Every region and city URL will stop working.', 'viator-collector' ), ), ) ); @@ -66,6 +69,11 @@ class VC_Structure_Admin { $errors = (array) $draft['errors']; $structure = (array) $draft['structure']; } + $warnings = get_transient( self::warnings_key() ); + $warnings = is_array( $warnings ) ? $warnings : array(); + if ( $warnings ) { + delete_transient( self::warnings_key() ); + } $notice = isset( $_GET['vc_notice'] ) ? sanitize_text_field( wp_unslash( $_GET['vc_notice'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification ?>
@@ -73,7 +81,16 @@ class VC_Structure_Admin {

-

+

+ + +
+
    + +
  • + +
+
@@ -190,7 +207,11 @@ class VC_Structure_Admin { unset( $region ); $map = ( new VC_API() )->get_destination_map(); - if ( is_wp_error( $map ) ) { + if ( empty( $_POST['vc_structure_present'] ) ) { + $errors = array( __( 'The structure editor did not load correctly, so nothing was saved. Reload the page and try again.', 'viator-collector' ) ); + } elseif ( ! $structure && empty( $_POST['vc_confirm_empty'] ) ) { + $errors = array( __( 'Saving an empty structure needs confirmation; nothing was saved.', 'viator-collector' ) ); + } elseif ( is_wp_error( $map ) ) { $errors = array( __( 'Could not load Viator destinations; nothing was saved.', 'viator-collector' ) ); } else { $errors = VC_Structure::validate( $structure, (array) $map, self::taken_slugs( $structure ) ); @@ -211,21 +232,39 @@ class VC_Structure_Admin { self::draft_key(), array( 'errors' => $errors, - 'structure' => $structure, + // Without the editor's fields there is no draft worth keeping. + 'structure' => empty( $_POST['vc_structure_present'] ) ? VC_Structure::get() : $structure, ), 10 * MINUTE_IN_SECONDS ); self::redirect(); } - $structure = self::sync_terms( VC_Structure::get(), $structure ); + self::$sync_errors = array(); + $structure = self::sync_terms( VC_Structure::get(), $structure ); update_option( VC_Structure::OPTION, $structure ); VC_Structure::flush_cache(); update_option( 'vc_flush_rewrite', 1 ); VC_Logger::info( sprintf( 'Structure saved: %d regions, %d cities.', count( $structure ), count( VC_Structure::cities( $structure ) ) ) ); + if ( self::$sync_errors ) { + set_transient( self::warnings_key(), self::$sync_errors, 10 * MINUTE_IN_SECONDS ); + self::redirect( __( 'Structure saved, but some pages could not be set up (see below).', 'viator-collector' ) ); + } self::redirect( __( 'Structure saved.', 'viator-collector' ) ); } + private static function warnings_key() { + return 'vc_structure_warnings_' . get_current_user_id(); + } + + /** + * Record a term-sync failure: logged, and shown to the admin after save. + */ + private static function sync_error( $message ) { + VC_Logger::error( 'Structure: ' . $message ); + self::$sync_errors[] = $message; + } + private static function redirect( $notice = '' ) { $args = array( 'page' => self::PAGE ); if ( '' !== $notice ) { @@ -335,7 +374,7 @@ class VC_Structure_Admin { $result = wp_insert_term( $region['name'], $tax, array( 'slug' => $region['slug'] ) ); } if ( is_wp_error( $result ) ) { - VC_Logger::error( 'Structure: region "' . $region['name'] . '": ' . $result->get_error_message() ); + self::sync_error( 'region "' . $region['name'] . '": ' . $result->get_error_message() ); continue; } $region['term_id'] = (int) $result['term_id']; @@ -344,7 +383,7 @@ class VC_Structure_Admin { foreach ( $region['cities'] as $city ) { $city_term = VC_Taxonomies::get_or_create_destination_by_id( $city['id'], $city['name'] ); if ( ! $city_term ) { - VC_Logger::error( 'Structure: could not create city term for ' . $city['id'] ); + self::sync_error( 'could not create the page for city ' . $city['id'] ); continue; } $result = wp_update_term( @@ -356,7 +395,7 @@ class VC_Structure_Admin { ) ); if ( is_wp_error( $result ) ) { - VC_Logger::error( 'Structure: city "' . $city['name'] . '": ' . $result->get_error_message() ); + self::sync_error( 'city "' . $city['name'] . '": ' . $result->get_error_message() ); } } } diff --git a/viator-collector/includes/class-vc-structure.php b/viator-collector/includes/class-vc-structure.php index fadf350..97c3300 100644 --- a/viator-collector/includes/class-vc-structure.php +++ b/viator-collector/includes/class-vc-structure.php @@ -193,15 +193,28 @@ class VC_Structure { /** * Explicit per-slug rules (no catch-alls), regions first, then cities. + * Only regions synced to a term (term_id set) are routed, so the unsaved + * presets never claim /europe/ etc. over existing pages. Archives get a + * /page/N/ rule because core's redirect_canonical turns ?paged=N into it. */ public static function rewrite_rules( array $structure ) { - $rules = array(); - foreach ( $structure as $region ) { - $rules[ '^' . $region['slug'] . '/?$' ] = 'index.php?viator_destination=' . $region['slug']; + $synced = array_values( + array_filter( + $structure, + function ( $region ) { + return (int) $region['term_id'] > 0; + } + ) + ); + $rules = array(); + foreach ( $synced as $region ) { + $rules[ '^' . $region['slug'] . '/page/([0-9]+)/?$' ] = 'index.php?viator_destination=' . $region['slug'] . '&paged=$matches[1]'; + $rules[ '^' . $region['slug'] . '/?$' ] = 'index.php?viator_destination=' . $region['slug']; } - foreach ( self::cities( $structure ) as $city ) { - $rules[ '^' . $city['slug'] . '/?$' ] = 'index.php?viator_destination=' . $city['slug']; - $rules[ '^' . $city['slug'] . '/([^/]+)/?$' ] = 'index.php?viator_tour=$matches[1]'; + foreach ( self::cities( $synced ) as $city ) { + $rules[ '^' . $city['slug'] . '/page/([0-9]+)/?$' ] = 'index.php?viator_destination=' . $city['slug'] . '&paged=$matches[1]'; + $rules[ '^' . $city['slug'] . '/?$' ] = 'index.php?viator_destination=' . $city['slug']; + $rules[ '^' . $city['slug'] . '/([^/]+)/?$' ] = 'index.php?viator_tour=$matches[1]'; } return $rules; } diff --git a/viator-collector/tests/test-structure.php b/viator-collector/tests/test-structure.php index 3acdc9f..08f4866 100644 --- a/viator-collector/tests/test-structure.php +++ b/viator-collector/tests/test-structure.php @@ -127,23 +127,47 @@ function test_primary_destination_id() { vc_assert_same( '', VC_Structure::primary_destination_id( array() ) ); } +function vc_fixture_synced() { + $s = vc_fixture_structure(); + $s[0]['term_id'] = 11; + $s[1]['term_id'] = 12; + return $s; +} + function test_rewrite_rules() { vc_assert_same( array( - '^europe/?$' => 'index.php?viator_destination=europe', - '^americas/?$' => 'index.php?viator_destination=americas', - '^amsterdam/?$' => 'index.php?viator_destination=amsterdam', - '^amsterdam/([^/]+)/?$' => 'index.php?viator_tour=$matches[1]', - '^paris/?$' => 'index.php?viator_destination=paris', - '^paris/([^/]+)/?$' => 'index.php?viator_tour=$matches[1]', - '^boston/?$' => 'index.php?viator_destination=boston', - '^boston/([^/]+)/?$' => 'index.php?viator_tour=$matches[1]', + '^europe/page/([0-9]+)/?$' => 'index.php?viator_destination=europe&paged=$matches[1]', + '^europe/?$' => 'index.php?viator_destination=europe', + '^americas/page/([0-9]+)/?$' => 'index.php?viator_destination=americas&paged=$matches[1]', + '^americas/?$' => 'index.php?viator_destination=americas', + '^amsterdam/page/([0-9]+)/?$' => 'index.php?viator_destination=amsterdam&paged=$matches[1]', + '^amsterdam/?$' => 'index.php?viator_destination=amsterdam', + '^amsterdam/([^/]+)/?$' => 'index.php?viator_tour=$matches[1]', + '^paris/page/([0-9]+)/?$' => 'index.php?viator_destination=paris&paged=$matches[1]', + '^paris/?$' => 'index.php?viator_destination=paris', + '^paris/([^/]+)/?$' => 'index.php?viator_tour=$matches[1]', + '^boston/page/([0-9]+)/?$' => 'index.php?viator_destination=boston&paged=$matches[1]', + '^boston/?$' => 'index.php?viator_destination=boston', + '^boston/([^/]+)/?$' => 'index.php?viator_tour=$matches[1]', ), - VC_Structure::rewrite_rules( vc_fixture_structure() ) + VC_Structure::rewrite_rules( vc_fixture_synced() ) ); } function test_rewrite_rules_empty_region() { - $rules = VC_Structure::rewrite_rules( array( array( 'name' => 'Europe', 'slug' => 'europe', 'term_id' => 0, 'cities' => array() ) ) ); - vc_assert_same( array( '^europe/?$' => 'index.php?viator_destination=europe' ), $rules ); + $rules = VC_Structure::rewrite_rules( array( array( 'name' => 'Europe', 'slug' => 'europe', 'term_id' => 11, 'cities' => array() ) ) ); + vc_assert_same( + array( + '^europe/page/([0-9]+)/?$' => 'index.php?viator_destination=europe&paged=$matches[1]', + '^europe/?$' => 'index.php?viator_destination=europe', + ), + $rules + ); +} + +// Before the structure is ever saved, get() returns the unsynced presets: +// they must not claim /uk/, /europe/, … over existing pages. +function test_rewrite_rules_skip_unsynced_regions() { + vc_assert_same( array(), VC_Structure::rewrite_rules( VC_Structure::default_regions() ) ); }