diff --git a/.github/workflows/plugin-quality.yml b/.github/workflows/plugin-quality.yml index 78f58cd..4fbbd27 100644 --- a/.github/workflows/plugin-quality.yml +++ b/.github/workflows/plugin-quality.yml @@ -123,7 +123,7 @@ jobs: "testsEnvironment": false, "plugins": [ "https://downloads.wordpress.org/plugin/plugin-check.zip" ], "mappings": { - "wp-content/plugins/waypoints": "$plugin_dir" + "wp-content/plugins/waypoints-trip-planner": "$plugin_dir" } } EOF @@ -154,10 +154,10 @@ jobs: wp-env run cli wp plugin list-checks wp-env run cli wp plugin list-check-categories - wp-env run cli wp plugin activate waypoints + wp-env run cli wp plugin activate waypoints-trip-planner set +e - wp-env run cli wp plugin check waypoints \ + wp-env run cli wp plugin check waypoints-trip-planner \ --format=json \ --ignore-warnings \ --exclude-files=.distignore,DECISIONS.md,phpcs.xml.dist,phpunit.xml.dist,.wp-env.json \ diff --git a/.github/workflows/wp-submission-readiness.yml b/.github/workflows/wp-submission-readiness.yml index 681fa63..e5e3ba8 100644 --- a/.github/workflows/wp-submission-readiness.yml +++ b/.github/workflows/wp-submission-readiness.yml @@ -7,24 +7,24 @@ on: description: Public plugin display name expected for WordPress.org submission. required: true type: string - default: Waypoints + default: "Waypoints: Trip Planner" expected_slug: description: Permanent WordPress.org plugin slug and text domain expected for submission. required: true type: string - default: waypoints + default: waypoints-trip-planner workflow_call: inputs: expected_name: description: Public plugin display name expected for WordPress.org submission. required: false type: string - default: Waypoints + default: "Waypoints: Trip Planner" expected_slug: description: Permanent WordPress.org plugin slug and text domain expected for submission. required: false type: string - default: waypoints + default: waypoints-trip-planner permissions: contents: read diff --git a/README.md b/README.md index 5ca2dcf..3ea62c1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Waypoints +# Waypoints: Trip Planner -Waypoints is a configurable WordPress plugin for building day-trip planners with -Google Maps and Places data. The plugin source lives in +Waypoints: Trip Planner is a configurable WordPress plugin for building +day-trip planners with Google Maps and Places data. The plugin source lives in `plugin/waypoints/`. ## Status @@ -75,22 +75,18 @@ and WordPress Plugin Check. ## Naming Note -The public plugin name is **Waypoints**. The source directory, text domain, -REST namespace, block name, and preferred shortcode now use `waypoints`. Some -legacy compatibility surfaces still use identifiers from the original name: -`[plan_your_day]` remains as a shortcode alias, `plan_your_day_*` remains for -settings/options/hooks, `Acodebeard\PlanYourDay` remains the PHP namespace, and -some asset/CSS identifiers remain unchanged to avoid unnecessary migration -risk. +The public plugin name is **Waypoints: Trip Planner**. The WordPress.org slug, +release artifact folder, and text domain use `waypoints-trip-planner`. The +source directory remains `plugin/waypoints/`, and compatibility identifiers +such as `[waypoints]`, `[plan_your_day]`, `plan_your_day_*`, +`Acodebeard\PlanYourDay`, REST namespace, block name, and asset/CSS handles +remain unchanged to avoid unnecessary migration risk. ## Credits Special thanks to [Hagan](https://github.com/hagan) and [Datapoke](https://github.com/datapoke) for development help. -Thanks also to [Destination Kona Coast](https://destinationkonacoast.org) for -the idea that started the project. - ## Documentation Start with [docs/README.md](docs/README.md). Current docs cover installation, @@ -99,20 +95,21 @@ security, and troubleshooting. ## License -Waypoints is licensed under GPLv2 or later. See [LICENSE](LICENSE). +Waypoints: Trip Planner is licensed under GPLv2 or later. See +[LICENSE](LICENSE). ## Local Source Installation 1. Copy or symlink `plugin/waypoints/` into a WordPress installation at - `wp-content/plugins/waypoints/`. + `wp-content/plugins/waypoints-trip-planner/`. 2. From the plugin directory, install the Composer autoloader: ```sh composer install ``` -3. Activate **Waypoints** from the WordPress Plugins screen. -4. Open **Settings > Waypoints**. +3. Activate **Waypoints: Trip Planner** from the WordPress Plugins screen. +4. Open **Settings > Waypoints: Trip Planner**. 5. Configure the required default location and Google API keys. Release zips should include generated Composer autoload files so production @@ -126,9 +123,9 @@ From `plugin/waypoints/`, build an installable WordPress admin zip with: ./tools/build-release-zip.sh ``` -The script creates `dist/waypoints-1.0.zip` at the repository root, +The script creates `dist/waypoints-trip-planner-1.0.2.zip` at the repository root, installs production-only Composer autoload files into a temporary staging copy, -and packages the final artifact with a top-level `waypoints/` directory +and packages the final artifact with a top-level `waypoints-trip-planner/` directory suitable for **Plugins > Add New > Upload Plugin**. See [docs/RELEASES.md](docs/RELEASES.md) for the full manual GitHub release @@ -142,8 +139,9 @@ candidate scan before submitting to WordPress.org. It is reusable through The scan builds the release zip, runs the normal PHP checks, PHPStan, browser smoke coverage, WordPress Plugin Check against the packaged artifact, and a -repo-local metadata check. The metadata check intentionally expects **Waypoints** -to use the permanent WordPress.org slug and text domain `waypoints`. +repo-local metadata check. The metadata check intentionally expects +**Waypoints: Trip Planner** to use the permanent WordPress.org slug and text +domain `waypoints-trip-planner`. ## Configuration diff --git a/docs/ADMIN.md b/docs/ADMIN.md index 3645edd..f8cffe9 100644 --- a/docs/ADMIN.md +++ b/docs/ADMIN.md @@ -1,9 +1,9 @@ # Admin Workflows -Waypoints settings live under: +Waypoints: Trip Planner settings live under: ```text -Settings > Waypoints +Settings > Waypoints: Trip Planner ``` Only administrators with `manage_options` can change plugin settings or run the diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 259ae52..d809d2f 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,6 +1,6 @@ # Architecture -Waypoints is a generic WordPress plugin for building configurable day-trip +Waypoints: Trip Planner is a generic WordPress plugin for building configurable day-trip planners with Google Maps and Places data. ## Plugin Entry Point @@ -46,7 +46,7 @@ plan_your_day_settings The admin page is registered under: ```text -Settings > Waypoints +Settings > Waypoints: Trip Planner ``` The admin screen currently exposes required default location fields, planner diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 2f997dc..bb6bd50 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -19,7 +19,7 @@ plugin/waypoints/ For local development, copy or symlink that directory into a WordPress install: ```sh -ln -s /path/to/waypoints/plugin/waypoints /path/to/wordpress/wp-content/plugins/waypoints +ln -s /path/to/waypoints/plugin/waypoints /path/to/wordpress/wp-content/plugins/waypoints-trip-planner ``` Generate the Composer autoloader from the plugin directory: @@ -32,11 +32,11 @@ composer install Then activate the plugin from the WordPress admin Plugins screen or with WP-CLI: ```sh -wp --path=/path/to/wordpress plugin activate waypoints +wp --path=/path/to/wordpress plugin activate waypoints-trip-planner ``` -In the WordPress admin, the plugin appears as **Waypoints**. Open -**Settings > Waypoints** after activation. +In the WordPress admin, the plugin appears as **Waypoints: Trip Planner**. Open +**Settings > Waypoints: Trip Planner** after activation. ## Local Test Install @@ -44,7 +44,7 @@ One local development setup can look like this: ```text /path/to/wordpress -/path/to/wordpress/wp-content/plugins/waypoints +/path/to/wordpress/wp-content/plugins/waypoints-trip-planner -> /path/to/waypoints/plugin/waypoints ``` @@ -54,7 +54,7 @@ Useful local commands: wp \ --path=/path/to/wordpress \ --url=https://example.test \ - plugin status waypoints \ + plugin status waypoints-trip-planner \ ``` The admin settings screen is: @@ -94,5 +94,5 @@ cd plugin/waypoints ./tools/build-release-zip.sh ``` -The script writes `dist/waypoints-1.0.zip` at the repository root, which +The script writes `dist/waypoints-trip-planner-1.0.2.zip` at the repository root, which can be uploaded through the WordPress Plugins screen. diff --git a/docs/QA.md b/docs/QA.md index db36908..01de476 100644 --- a/docs/QA.md +++ b/docs/QA.md @@ -1,6 +1,6 @@ # Frontend QA -Waypoints keeps a lightweight browser smoke suite for the public planner +Waypoints: Trip Planner keeps a lightweight browser smoke suite for the public planner frontend. The suite focuses on the shipped shortcode and block entry points, the same-site REST flow, and a small set of accessibility-sensitive interactions. diff --git a/docs/README.md b/docs/README.md index ec477a0..e54c716 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,4 @@ -# Waypoints Documentation +# Waypoints: Trip Planner Documentation This directory documents the WordPress plugin as it exists today. The plugin is installable and has admin/settings, Google API, cache, planner helper, planner diff --git a/docs/RELEASES.md b/docs/RELEASES.md index 16a276c..85f65f8 100644 --- a/docs/RELEASES.md +++ b/docs/RELEASES.md @@ -1,14 +1,16 @@ # Release Process -Waypoints v1 ships as a manual GitHub release zip. The plugin does not include a -private updater or a custom `Update URI` workflow right now. +Waypoints: Trip Planner v1 ships as a manual GitHub release zip. The plugin +does not include a private updater or a custom `Update URI` workflow right now. ## Release Metadata Keep these values aligned before building a release: - plugin header `Version` in the root PHP bootstrap file +- plugin header `Plugin Name` and `Text Domain` - `PLAN_YOUR_DAY_VERSION` +- `PLAN_YOUR_DAY_TEXT_DOMAIN` - `PLAN_YOUR_DAY_SCHEMA_VERSION` - `plugin/waypoints/release.json` - the artifact filename in `release.json` @@ -18,7 +20,7 @@ The release builder validates that: - `release.json.version` matches `PLAN_YOUR_DAY_VERSION` - `release.json.schemaVersion` matches `PLAN_YOUR_DAY_SCHEMA_VERSION` -- the artifact filename matches the plugin slug and version +- the artifact filename and top-level zip folder match the plugin slug and version ## Standard Release Steps @@ -46,17 +48,16 @@ The release builder validates that: The current release artifact is an installable WordPress admin zip with: -- a top-level `waypoints/` directory +- a top-level `waypoints-trip-planner/` directory - production Composer autoload files included - development-only files excluded through `.distignore` Production sites should install the built zip and should not need to run Composer on the server. -Current v1.0 artifact: +Current v1.0.2 artifact: -- filename: `waypoints-1.0.zip` -- SHA-256: `acc93ed02e41585f4f9e2799d739d38fc374bf4e416852bdf0f16394d7af6e6c` +- filename: `waypoints-trip-planner-1.0.2.zip` ## Update Expectations diff --git a/docs/SETTINGS.md b/docs/SETTINGS.md index c3b2899..ea1cc45 100644 --- a/docs/SETTINGS.md +++ b/docs/SETTINGS.md @@ -1,6 +1,6 @@ # Settings Reference -Waypoints stores plugin configuration in: +Waypoints: Trip Planner stores plugin configuration in: ```text plan_your_day_settings diff --git a/docs/USAGE.md b/docs/USAGE.md index eb4cd58..c5fea6c 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -1,15 +1,15 @@ # Frontend Usage -Waypoints can be rendered through either the block editor or the shortcode. Both -entry points use the same server-rendered planner and the same frontend CSS and -JavaScript assets. +Waypoints: Trip Planner can be rendered through either the block editor or the +shortcode. Both entry points use the same server-rendered planner and the same +frontend CSS and JavaScript assets. ## Before You Place The Planner Make sure the plugin has been configured under: ```text -Settings > Waypoints +Settings > Waypoints: Trip Planner ``` At minimum, set: @@ -26,7 +26,7 @@ instead of the full planner. The plugin registers a dynamic block named: ```text -Waypoints +Waypoints: Trip Planner ``` Editor behavior: diff --git a/package.json b/package.json index c539e85..2b774e7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "waypoints-qa", "private": true, - "description": "Browser smoke tests for the Waypoints plugin repository.", + "description": "Browser smoke tests for the Waypoints: Trip Planner plugin repository.", "scripts": { "browser-smoke": "playwright test" }, diff --git a/plugin/waypoints/assets/css/admin-settings.css b/plugin/waypoints/assets/css/admin-settings.css index 1ae01c6..45acd52 100644 --- a/plugin/waypoints/assets/css/admin-settings.css +++ b/plugin/waypoints/assets/css/admin-settings.css @@ -284,6 +284,81 @@ padding-left: 0; } +.plan-your-day-api-key-field { + display: inline-flex; + align-items: center; + gap: 0.4rem; + max-width: 100%; +} + +.plan-your-day-api-key-field input.regular-text { + max-width: min(32rem, calc(100vw - 12rem)); +} + +.plan-your-day-api-key-reveal { + position: relative; + display: inline-flex !important; + align-items: center; + justify-content: center; + width: 2.25rem; + min-width: 2.25rem; + height: 2rem; + padding: 0 !important; + line-height: 1; +} + +.plan-your-day-api-key-reveal-icon { + position: relative; + display: block; + width: 1rem; + height: 1rem; + color: currentColor; +} + +.plan-your-day-api-key-reveal-icon::before { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 0.9rem; + height: 0.9rem; + border: 2px solid currentColor; + border-radius: 75% 0; + transform: translate(-50%, -50%) rotate(45deg); +} + +.plan-your-day-api-key-reveal-icon::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 0.35rem; + height: 0.35rem; + border-radius: 50%; + background: currentColor; + transform: translate(-50%, -50%); +} + +.plan-your-day-api-key-reveal::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 1.35rem; + height: 2px; + background: currentColor; + transform: translate(-50%, -50%) rotate(-35deg); + transform-origin: center; +} + +.plan-your-day-api-key-field.is-revealed .plan-your-day-api-key-reveal::after { + display: none; +} + +.plan-your-day-api-key-field.is-revealed input.regular-text { + background: #fff8e5; +} + .plan-your-day-admin-back-to-top { position: fixed; right: 1.5rem; diff --git a/plugin/waypoints/assets/js/admin-settings.js b/plugin/waypoints/assets/js/admin-settings.js index 94fa741..2247a40 100644 --- a/plugin/waypoints/assets/js/admin-settings.js +++ b/plugin/waypoints/assets/js/admin-settings.js @@ -1,6 +1,7 @@ (() => { const CATEGORY_ROW_SELECTOR = '[data-plan-category-row]'; const CATEGORY_DRAG_HANDLE_SELECTOR = '[data-plan-category-drag-handle]'; + const API_KEY_REVEAL_DURATION_MS = 10000; const getCategoryRows = (rows) => Array.from(rows.querySelectorAll(CATEGORY_ROW_SELECTOR)); @@ -276,6 +277,76 @@ }); }; + const initializeApiKeyRevealButtons = () => { + document.querySelectorAll('[data-plan-api-key-reveal]').forEach((button) => { + if (!(button instanceof HTMLButtonElement) || 'true' === button.dataset.planApiKeyRevealReady) { + return; + } + + const field = button.closest('.plan-your-day-api-key-field'); + const input = field?.querySelector('[data-plan-api-key-input]'); + const label = button.querySelector('[data-plan-api-key-reveal-label]'); + + if (!(field instanceof HTMLElement) || !(input instanceof HTMLInputElement)) { + return; + } + + let hideTimer = 0; + const showLabel = button.getAttribute('data-plan-show-label') || 'Show API key'; + const hideLabel = button.getAttribute('data-plan-hide-label') || 'Hide API key'; + const setButtonLabel = (text) => { + if (label instanceof HTMLElement) { + label.textContent = text; + } + + button.setAttribute('aria-label', text); + }; + const clearHideTimer = () => { + if (0 !== hideTimer) { + window.clearTimeout(hideTimer); + hideTimer = 0; + } + }; + const hideKey = () => { + clearHideTimer(); + input.type = 'password'; + button.setAttribute('aria-pressed', 'false'); + field.classList.remove('is-revealed'); + setButtonLabel(showLabel); + }; + const showKey = () => { + clearHideTimer(); + input.type = 'text'; + button.setAttribute('aria-pressed', 'true'); + field.classList.add('is-revealed'); + setButtonLabel(hideLabel); + hideTimer = window.setTimeout(hideKey, API_KEY_REVEAL_DURATION_MS); + }; + + button.dataset.planApiKeyRevealReady = 'true'; + setButtonLabel(showLabel); + + button.addEventListener('pointerdown', (event) => { + event.preventDefault(); + }); + + button.addEventListener('click', () => { + if ('text' === input.type) { + hideKey(); + } else { + showKey(); + } + + input.focus({ + preventScroll: true, + }); + }); + + input.addEventListener('blur', hideKey); + button.form?.addEventListener('submit', hideKey); + }); + }; + const initializeBackToTop = () => { const topButton = document.querySelector('[data-plan-back-to-top]'); const topTarget = document.getElementById('plan-your-day-settings-top'); @@ -322,6 +393,7 @@ const initialize = () => { initializeCategoryEditors(); + initializeApiKeyRevealButtons(); initializeBackToTop(); }; diff --git a/plugin/waypoints/assets/js/plan-block.js b/plugin/waypoints/assets/js/plan-block.js index 84841c3..93accbe 100644 --- a/plugin/waypoints/assets/js/plan-block.js +++ b/plugin/waypoints/assets/js/plan-block.js @@ -24,12 +24,12 @@ el( PanelBody, { - title: __( 'Planner settings', 'waypoints' ), + title: __( 'Planner settings', 'waypoints-trip-planner' ), initialOpen: true, }, el( TextControl, { - label: __( 'Action URL', 'waypoints' ), - help: __( 'Optional. Submit planner updates to a specific page URL instead of the current page.', 'waypoints' ), + label: __( 'Action URL', 'waypoints-trip-planner' ), + help: __( 'Optional. Submit planner updates to a specific page URL instead of the current page.', 'waypoints-trip-planner' ), value: attributes.actionUrl || '', onChange: function( value ) { props.setAttributes( { actionUrl: value } ); @@ -43,13 +43,13 @@ el( Placeholder, { - label: __( 'Waypoints', 'waypoints' ), - instructions: __( 'The frontend uses the same planner renderer as the shortcode. Configure an optional action URL in the block settings.', 'waypoints' ), + label: __( 'Waypoints: Trip Planner', 'waypoints-trip-planner' ), + instructions: __( 'The frontend uses the same planner renderer as the shortcode. Configure an optional action URL in the block settings.', 'waypoints-trip-planner' ), }, el( 'p', null, - __( 'Planner styles and interactions load only when this block is rendered on the page.', 'waypoints' ) + __( 'Planner styles and interactions load only when this block is rendered on the page.', 'waypoints-trip-planner' ) ) ) ) diff --git a/plugin/waypoints/blocks/planner/block.json b/plugin/waypoints/blocks/planner/block.json index c2b6e09..6e76c94 100644 --- a/plugin/waypoints/blocks/planner/block.json +++ b/plugin/waypoints/blocks/planner/block.json @@ -2,10 +2,10 @@ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "waypoints/planner", - "title": "Waypoints", + "title": "Waypoints: Trip Planner", "category": "widgets", - "description": "Render the Waypoints planner through the block editor.", - "textdomain": "waypoints", + "description": "Render the Waypoints: Trip Planner interface through the block editor.", + "textdomain": "waypoints-trip-planner", "keywords": [ "planner", "maps", diff --git a/plugin/waypoints/composer.json b/plugin/waypoints/composer.json index 958a1f9..95cc8db 100644 --- a/plugin/waypoints/composer.json +++ b/plugin/waypoints/composer.json @@ -1,5 +1,5 @@ { - "name": "acodebeard/waypoints", + "name": "acodebeard/waypoints-trip-planner", "description": "A configurable day planning plugin for WordPress.", "type": "wordpress-plugin", "license": "GPL-2.0-or-later", diff --git a/plugin/waypoints/composer.lock b/plugin/waypoints/composer.lock index 609beaf..867516e 100644 --- a/plugin/waypoints/composer.lock +++ b/plugin/waypoints/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5da5570b9cea2c3b2b233b891b615f2e", + "content-hash": "e6de3e47ce2ee563e11d2eb98d71ac8f", "packages": [], "packages-dev": [ { diff --git a/plugin/waypoints/phpcs.xml.dist b/plugin/waypoints/phpcs.xml.dist index 033db34..ab52dbe 100644 --- a/plugin/waypoints/phpcs.xml.dist +++ b/plugin/waypoints/phpcs.xml.dist @@ -1,6 +1,6 @@ - - WordPress Coding Standards checks for Waypoints plugin code. + + WordPress Coding Standards checks for Waypoints: Trip Planner plugin code. diff --git a/plugin/waypoints/phpunit.xml.dist b/plugin/waypoints/phpunit.xml.dist index 38c58d8..94173d9 100644 --- a/plugin/waypoints/phpunit.xml.dist +++ b/plugin/waypoints/phpunit.xml.dist @@ -7,7 +7,7 @@ failOnWarning="true" > - + tests diff --git a/plugin/waypoints/plan-your-day.php b/plugin/waypoints/plan-your-day.php index 71c5c32..c9c35d0 100644 --- a/plugin/waypoints/plan-your-day.php +++ b/plugin/waypoints/plan-your-day.php @@ -1,12 +1,12 @@ init(); function plan_your_day_missing_autoloader_message(): string { - return 'Waypoints is missing Composer dependencies. Install a built release zip that includes vendor/autoload.php, ' + return 'Waypoints: Trip Planner is missing Composer dependencies. Install a built release zip that includes vendor/autoload.php, ' . 'or run composer install inside the plugin directory for a source checkout.'; } @@ -47,7 +47,7 @@ function plan_your_day_missing_autoloader_activation(): void { wp_die( esc_html( plan_your_day_missing_autoloader_message() ), - esc_html( 'Waypoints activation failed' ), + esc_html( 'Waypoints: Trip Planner activation failed' ), [ 'back_link' => true ] ); } diff --git a/plugin/waypoints/readme.txt b/plugin/waypoints/readme.txt index 19f74e6..3134150 100644 --- a/plugin/waypoints/readme.txt +++ b/plugin/waypoints/readme.txt @@ -1,10 +1,10 @@ -=== Waypoints === +=== Waypoints: Trip Planner === Contributors: acodebeard Tags: planning, maps, wayfinding Requires at least: 6.8 Tested up to: 7.0 Requires PHP: 8.2 -Stable tag: 1.0 +Stable tag: 1.0.2 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -12,9 +12,9 @@ A configurable day planning plugin for WordPress. == Description == -Waypoints is a configurable day planning plugin for WordPress. +Waypoints: Trip Planner is a configurable day planning plugin for WordPress. -Use Waypoints to add a searchable trip-planning interface to a page or post. +Use Waypoints: Trip Planner to add a searchable trip-planning interface to a page or post. Visitors can search Google place results, add stops to a waypoint list, reorder their trip, preview the route when map embeds are configured, and open the route in Google Maps. @@ -24,29 +24,26 @@ in Google Maps. Special thanks to Hagan Franks (https://github.com/hagan) and Christopher Reaume (https://github.com/datapoke) for development help. -Thanks also to Destination Kona Coast (https://destinationkonacoast.com) for -the idea that started the project. - == Installation == Release zip: -1. Build or download the packaged `waypoints` release zip. +1. Build or download the packaged `waypoints-trip-planner` release zip. 2. Upload it through the Plugins screen in WordPress. 3. Activate the plugin. Release zips include generated Composer autoload files. Source checkout: -1. Copy or symlink this plugin directory as `/wp-content/plugins/waypoints/`. +1. Copy or symlink this plugin directory as `/wp-content/plugins/waypoints-trip-planner/`. 2. Run `composer install` inside the plugin directory to generate `vendor/autoload.php`. 3. Activate the plugin through the Plugins screen in WordPress. -4. Open Settings > Waypoints and configure the required default location +4. Open Settings > Waypoints: Trip Planner and configure the required default location and Google API keys. == Configuration == -Settings are managed through Settings > Waypoints. +Settings are managed through Settings > Waypoints: Trip Planner. Current settings include: @@ -62,7 +59,7 @@ Current settings include: == External services == -Waypoints uses Google services to load place results, place details, +Waypoints: Trip Planner uses Google services to load place results, place details, geocoding data, embedded map previews, and Google Maps handoff links. The plugin can send data to Google from the server when: @@ -95,12 +92,12 @@ Google provides these services. Review their terms and privacy information: = What do I need before using it on a live site? = Configure the required default location and Google API keys in Settings > -Waypoints. For best results, restrict Google API keys in Google Cloud Console +Waypoints: Trip Planner. For best results, restrict Google API keys in Google Cloud Console and test the planner flow on a staging site before adding it to a public page. = How do I display the planner? = -Use the Waypoints block in the block editor or add `[waypoints]` to a page, +Use the Waypoints: Trip Planner block in the block editor or add `[waypoints]` to a page, post, or Shortcode block. The optional `action_url` shortcode attribute and `Action URL` block setting can submit planner updates to a specific page URL. @@ -111,8 +108,15 @@ architecture, settings, security, and troubleshooting notes. == Changelog == += 1.0.2 = +* Updated the WordPress.org submission name and slug to Waypoints: Trip Planner. + += 1.0.1 = +* Added an admin control for briefly revealing saved Google API keys while checking copied values. +* Hardened Google API key settings against browser autofill and non-key values. + = 1.0 = -* Renamed the public plugin surface to Waypoints, including the text domain, REST namespace, block name, preferred shortcode, release metadata, and package artifact. +* Renamed the original public plugin surface to Waypoints, including the REST namespace, block name, preferred shortcode, release metadata, and package artifact. * Added WordPress.org submission-readiness checks covering PHP quality, PHPStan, browser smoke tests, WordPress Plugin Check, release packaging, and metadata validation. * Hardened the release artifact so it ships as a single `waypoints/` plugin directory with production Composer autoload files and normalized file permissions. * Added admin-only API request counting for troubleshooting Google API usage. diff --git a/plugin/waypoints/release.json b/plugin/waypoints/release.json index 5f3b1c1..ae83230 100644 --- a/plugin/waypoints/release.json +++ b/plugin/waypoints/release.json @@ -1,9 +1,9 @@ { - "name": "Waypoints", - "slug": "waypoints", - "version": "1.0", + "name": "Waypoints: Trip Planner", + "slug": "waypoints-trip-planner", + "version": "1.0.2", "schemaVersion": 5, - "artifact": "../../dist/waypoints-1.0.zip", + "artifact": "../../dist/waypoints-trip-planner-1.0.2.zip", "distribution": "github-release-zip", "notes": "Installable WordPress admin zip built from this source repository with Composer production autoload files included." } diff --git a/plugin/waypoints/src/Admin/SettingsPage.php b/plugin/waypoints/src/Admin/SettingsPage.php index e75166a..4ba1c5b 100644 --- a/plugin/waypoints/src/Admin/SettingsPage.php +++ b/plugin/waypoints/src/Admin/SettingsPage.php @@ -13,6 +13,7 @@ final class SettingsPage { private const GOOGLE_TEST_TRANSIENT_PREFIX = 'plan_your_day_google_test_'; + private const GOOGLE_API_KEY_PATTERN = 'AIza[0-9A-Za-z_\\-]{35}'; private Settings $settings; private GoogleApiCache $google_api_cache; @@ -33,8 +34,8 @@ public function __construct( public function register(): void { add_options_page( - __( 'Waypoints Settings', 'waypoints' ), - __( 'Waypoints', 'waypoints' ), + __( 'Waypoints: Trip Planner Settings', 'waypoints-trip-planner' ), + __( 'Waypoints: Trip Planner', 'waypoints-trip-planner' ), 'manage_options', Settings::PAGE_SLUG, [ $this, 'render' ] @@ -42,15 +43,15 @@ public function register(): void { add_settings_section( 'plan_your_day_default_location', - __( 'Default Location', 'waypoints' ), + __( 'Default Location', 'waypoints-trip-planner' ), [ $this, 'render_default_location_section' ], Settings::PAGE_SLUG ); $this->add_field( 'default_location_label', - __( 'Default location label', 'waypoints' ), - __( 'Required. Human-readable label used for the default trip start.', 'waypoints' ), + __( 'Default location label', 'waypoints-trip-planner' ), + __( 'Required. Human-readable label used for the default trip start.', 'waypoints-trip-planner' ), 'text', [], 'plan_your_day_default_location' @@ -58,8 +59,8 @@ public function register(): void { $this->add_field( 'default_location_address', - __( 'Default location address or search phrase', 'waypoints' ), - __( 'Required. Address, landmark, or search phrase used when the planner needs a stable starting area.', 'waypoints' ), + __( 'Default location address or search phrase', 'waypoints-trip-planner' ), + __( 'Required. Address, landmark, or search phrase used when the planner needs a stable starting area.', 'waypoints-trip-planner' ), 'textarea', [ 'rows' => 3, @@ -69,8 +70,8 @@ public function register(): void { $this->add_field( 'default_location_latitude', - __( 'Default latitude', 'waypoints' ), - __( 'Optional. Decimal latitude from -90 to 90 for distance hints and search biasing.', 'waypoints' ), + __( 'Default latitude', 'waypoints-trip-planner' ), + __( 'Optional. Decimal latitude from -90 to 90 for distance hints and search biasing.', 'waypoints-trip-planner' ), 'number', [ 'min' => -90, @@ -82,8 +83,8 @@ public function register(): void { $this->add_field( 'default_location_longitude', - __( 'Default longitude', 'waypoints' ), - __( 'Optional. Decimal longitude from -180 to 180 for distance hints and search biasing.', 'waypoints' ), + __( 'Default longitude', 'waypoints-trip-planner' ), + __( 'Optional. Decimal longitude from -180 to 180 for distance hints and search biasing.', 'waypoints-trip-planner' ), 'number', [ 'min' => -180, @@ -95,8 +96,8 @@ public function register(): void { $this->add_field( 'default_location_place_id', - __( 'Default Google Place ID', 'waypoints' ), - __( 'Optional. Save the exact Google Place ID for the default location when you want a stable place reference alongside the address.', 'waypoints' ), + __( 'Default Google Place ID', 'waypoints-trip-planner' ), + __( 'Optional. Save the exact Google Place ID for the default location when you want a stable place reference alongside the address.', 'waypoints-trip-planner' ), 'text', [], 'plan_your_day_default_location' @@ -104,15 +105,15 @@ public function register(): void { add_settings_section( 'plan_your_day_appearance', - __( 'Appearance', 'waypoints' ), + __( 'Appearance', 'waypoints-trip-planner' ), [ $this, 'render_appearance_section' ], Settings::PAGE_SLUG ); $this->add_field( 'color_mode_default', - __( 'Default color mode', 'waypoints' ), - __( 'Choose the public planner color mode before a visitor makes their own choice. System follows the visitor browser or OS preference.', 'waypoints' ), + __( 'Default color mode', 'waypoints-trip-planner' ), + __( 'Choose the public planner color mode before a visitor makes their own choice. System follows the visitor browser or OS preference.', 'waypoints-trip-planner' ), 'select', [ 'choices' => Settings::color_mode_choices(), @@ -122,15 +123,15 @@ public function register(): void { add_settings_section( 'plan_your_day_planner_behavior', - __( 'Planner Behavior', 'waypoints' ), + __( 'Planner Behavior', 'waypoints-trip-planner' ), [ $this, 'render_planner_behavior_section' ], Settings::PAGE_SLUG ); $this->add_field( 'allowed_start_modes', - __( 'Allowed start modes', 'waypoints' ), - __( 'Choose which starting-point controls the planner may offer.', 'waypoints' ), + __( 'Allowed start modes', 'waypoints-trip-planner' ), + __( 'Choose which starting-point controls the planner may offer.', 'waypoints-trip-planner' ), 'checkbox_group', [ 'choices' => Settings::start_mode_choices(), @@ -140,8 +141,8 @@ public function register(): void { $this->add_field( 'max_waypoints', - __( 'Max waypoints', 'waypoints' ), - __( 'Maximum selected Google places a public request may resolve.', 'waypoints' ), + __( 'Max waypoints', 'waypoints-trip-planner' ), + __( 'Maximum selected Google places a public request may resolve.', 'waypoints-trip-planner' ), 'number', [ 'min' => 1, @@ -152,8 +153,8 @@ public function register(): void { $this->add_field( 'result_count', - __( 'Result count', 'waypoints' ), - __( 'Maximum Google text-search results requested per browse action.', 'waypoints' ), + __( 'Result count', 'waypoints-trip-planner' ), + __( 'Maximum Google text-search results requested per browse action.', 'waypoints-trip-planner' ), 'number', [ 'min' => 1, @@ -164,8 +165,8 @@ public function register(): void { $this->add_field( 'distance_unit', - __( 'Distance unit', 'waypoints' ), - __( 'Unit for approximate distance hints.', 'waypoints' ), + __( 'Distance unit', 'waypoints-trip-planner' ), + __( 'Unit for approximate distance hints.', 'waypoints-trip-planner' ), 'select', [ 'choices' => Settings::distance_unit_choices(), @@ -175,8 +176,8 @@ public function register(): void { $this->add_field( 'map_preview_enabled', - __( 'Map preview', 'waypoints' ), - __( 'Allow on-page Google Maps preview rendering in the public planner.', 'waypoints' ), + __( 'Map preview', 'waypoints-trip-planner' ), + __( 'Allow on-page Google Maps preview rendering in the public planner.', 'waypoints-trip-planner' ), 'checkbox', [], 'plan_your_day_planner_behavior' @@ -184,8 +185,8 @@ public function register(): void { $this->add_field( 'maps_handoff_enabled', - __( 'Google Maps handoff', 'waypoints' ), - __( 'Allow outbound Google Maps links from the public planner.', 'waypoints' ), + __( 'Google Maps handoff', 'waypoints-trip-planner' ), + __( 'Allow outbound Google Maps links from the public planner.', 'waypoints-trip-planner' ), 'checkbox', [], 'plan_your_day_planner_behavior' @@ -193,7 +194,7 @@ public function register(): void { add_settings_section( 'plan_your_day_categories', - __( 'Categories', 'waypoints' ), + __( 'Categories', 'waypoints-trip-planner' ), [ $this, 'render_categories_section' ], Settings::PAGE_SLUG ); @@ -211,36 +212,36 @@ public function register(): void { add_settings_section( 'plan_your_day_google_api', - __( 'Google API', 'waypoints' ), + __( 'Google API', 'waypoints-trip-planner' ), [ $this, 'render_google_api_section' ], Settings::PAGE_SLUG ); $this->add_field( 'google_maps_embed_api_key', - __( 'Maps Embed API key', 'waypoints' ), - __( 'Browser-facing key for Google Maps Embed previews. This key can appear in frontend iframe URLs.', 'waypoints' ), - 'password' + __( 'Maps Embed API key', 'waypoints-trip-planner' ), + __( 'Browser-facing key for Google Maps Embed previews. This key can appear in frontend iframe URLs.', 'waypoints-trip-planner' ), + 'api_key' ); $this->add_field( 'google_places_api_key', - __( 'Places API key', 'waypoints' ), - __( 'Server-side key for Places API (New) text search and place details. This key is never sent to browser config.', 'waypoints' ), - 'password' + __( 'Places API key', 'waypoints-trip-planner' ), + __( 'Server-side key for Places API (New) text search and place details. This key is never sent to browser config.', 'waypoints-trip-planner' ), + 'api_key' ); $this->add_field( 'google_geocoding_api_key', - __( 'Geocoding API key', 'waypoints' ), - __( 'Optional server-side key for Geocoding API. Leave empty to use the Places API key for geocoding.', 'waypoints' ), - 'password' + __( 'Geocoding API key', 'waypoints-trip-planner' ), + __( 'Optional server-side key for Geocoding API. Leave empty to use the Places API key for geocoding.', 'waypoints-trip-planner' ), + 'api_key' ); $this->add_field( 'google_api_timeout', - __( 'API timeout', 'waypoints' ), - __( 'Request timeout in seconds. Saved values are clamped between 1 and 30 seconds.', 'waypoints' ), + __( 'API timeout', 'waypoints-trip-planner' ), + __( 'Request timeout in seconds. Saved values are clamped between 1 and 30 seconds.', 'waypoints-trip-planner' ), 'number', [ 'min' => 1, @@ -250,15 +251,15 @@ public function register(): void { add_settings_section( 'plan_your_day_google_cache', - __( 'Google API Cache', 'waypoints' ), + __( 'Google API Cache', 'waypoints-trip-planner' ), [ $this, 'render_google_cache_section' ], Settings::PAGE_SLUG ); $this->add_field( 'google_text_search_cache_ttl', - __( 'Text search cache TTL', 'waypoints' ), - __( 'Seconds to cache successful Google text search responses. Use 0 to disable this cache.', 'waypoints' ), + __( 'Text search cache TTL', 'waypoints-trip-planner' ), + __( 'Seconds to cache successful Google text search responses. Use 0 to disable this cache.', 'waypoints-trip-planner' ), 'number', [ 'min' => 0, @@ -269,8 +270,8 @@ public function register(): void { $this->add_field( 'google_place_details_cache_ttl', - __( 'Place details cache TTL', 'waypoints' ), - __( 'Seconds to cache successful Google place details responses. Use 0 to disable this cache.', 'waypoints' ), + __( 'Place details cache TTL', 'waypoints-trip-planner' ), + __( 'Seconds to cache successful Google place details responses. Use 0 to disable this cache.', 'waypoints-trip-planner' ), 'number', [ 'min' => 0, @@ -281,8 +282,8 @@ public function register(): void { $this->add_field( 'google_geocoding_cache_ttl', - __( 'Geocoding cache TTL', 'waypoints' ), - __( 'Seconds to cache successful Google geocoding responses. Use 0 to disable this cache.', 'waypoints' ), + __( 'Geocoding cache TTL', 'waypoints-trip-planner' ), + __( 'Seconds to cache successful Google geocoding responses. Use 0 to disable this cache.', 'waypoints-trip-planner' ), 'number', [ 'min' => 0, @@ -293,22 +294,22 @@ public function register(): void { add_settings_section( 'plan_your_day_interface_copy', - __( 'Interface Copy', 'waypoints' ), + __( 'Interface Copy', 'waypoints-trip-planner' ), [ $this, 'render_interface_copy_section' ], Settings::PAGE_SLUG ); add_settings_section( 'plan_your_day_rate_limiting', - __( 'Rate Limiting', 'waypoints' ), + __( 'Rate Limiting', 'waypoints-trip-planner' ), [ $this, 'render_rate_limiting_section' ], Settings::PAGE_SLUG ); $this->add_field( 'rate_limit_per_minute', - __( 'Requests per minute', 'waypoints' ), - __( 'Per-minute budget for the active public planner REST rate limiter. Requests are enforced by client IP and planner scope.', 'waypoints' ), + __( 'Requests per minute', 'waypoints-trip-planner' ), + __( 'Per-minute budget for the active public planner REST rate limiter. Requests are enforced by client IP and planner scope.', 'waypoints-trip-planner' ), 'number', [ 'min' => 1, @@ -319,8 +320,8 @@ public function register(): void { $this->add_field( 'debug_api_counter_enabled', - __( 'API call counter', 'waypoints' ), - __( 'Show a fixed frontend API call counter only to admins for troubleshooting request behavior.', 'waypoints' ), + __( 'API call counter', 'waypoints-trip-planner' ), + __( 'Show a fixed frontend API call counter only to admins for troubleshooting request behavior.', 'waypoints-trip-planner' ), 'checkbox', [], 'plan_your_day_rate_limiting' @@ -328,15 +329,15 @@ public function register(): void { add_settings_section( 'plan_your_day_advanced', - __( 'Advanced', 'waypoints' ), + __( 'Advanced', 'waypoints-trip-planner' ), [ $this, 'render_advanced_section' ], Settings::PAGE_SLUG ); $this->add_field( 'trusted_proxy_cidrs', - __( 'Trusted proxy CIDRs', 'waypoints' ), - __( 'Optional. One IP or CIDR per line. Invalid entries are discarded on save.', 'waypoints' ), + __( 'Trusted proxy CIDRs', 'waypoints-trip-planner' ), + __( 'Optional. One IP or CIDR per line. Invalid entries are discarded on save.', 'waypoints-trip-planner' ), 'textarea', [ 'rows' => 5, @@ -347,7 +348,7 @@ public function register(): void { public function render(): void { if ( ! current_user_can( 'manage_options' ) ) { - wp_die( esc_html__( 'You do not have permission to manage Waypoints settings.', 'waypoints' ) ); + wp_die( esc_html__( 'You do not have permission to manage Waypoints: Trip Planner settings.', 'waypoints-trip-planner' ) ); } ?> @@ -364,50 +365,50 @@ public function render(): void { ?>
-

-

+

+

- +
-

-

+

+

- + - +
- + - +

-

-

+

+

- +
render_google_test_results_panel(); ?> -

+

- - - + + + @@ -500,54 +501,54 @@ private function build_setup_status_checks(): array { return [ [ - 'label' => __( 'Required location settings', 'waypoints' ), - 'status' => [] === $missing_required ? __( 'Ready', 'waypoints' ) : __( 'Needs setup', 'waypoints' ), + 'label' => __( 'Required location settings', 'waypoints-trip-planner' ), + 'status' => [] === $missing_required ? __( 'Ready', 'waypoints-trip-planner' ) : __( 'Needs setup', 'waypoints-trip-planner' ), 'detail' => [] === $missing_required - ? __( 'The planner has the required default location label and address.', 'waypoints' ) + ? __( 'The planner has the required default location label and address.', 'waypoints-trip-planner' ) : sprintf( /* translators: %s is a comma-separated list of missing settings. */ - __( 'Missing: %s.', 'waypoints' ), + __( 'Missing: %s.', 'waypoints-trip-planner' ), implode( ', ', $missing_required ) ), 'type' => [] === $missing_required ? 'success' : 'warning', ], [ - 'label' => __( 'Places API key', 'waypoints' ), - 'status' => '' !== $places_key ? __( 'Ready', 'waypoints' ) : __( 'Missing', 'waypoints' ), + 'label' => __( 'Places API key', 'waypoints-trip-planner' ), + 'status' => '' !== $places_key ? __( 'Ready', 'waypoints-trip-planner' ) : __( 'Missing', 'waypoints-trip-planner' ), 'detail' => '' !== $places_key - ? __( 'Server-side Places requests can run.', 'waypoints' ) - : __( 'Add a Places API key before trying browse or place-detail requests.', 'waypoints' ), + ? __( 'Server-side Places requests can run.', 'waypoints-trip-planner' ) + : __( 'Add a Places API key before trying browse or place-detail requests.', 'waypoints-trip-planner' ), 'type' => '' !== $places_key ? 'success' : 'warning', ], [ - 'label' => __( 'Geocoding configuration', 'waypoints' ), - 'status' => '' !== $geocoding_key ? __( 'Ready', 'waypoints' ) : __( 'Missing', 'waypoints' ), + 'label' => __( 'Geocoding configuration', 'waypoints-trip-planner' ), + 'status' => '' !== $geocoding_key ? __( 'Ready', 'waypoints-trip-planner' ) : __( 'Missing', 'waypoints-trip-planner' ), 'detail' => '' !== $geocoding_key ? ( $geocoding_key === $places_key - ? __( 'Geocoding will use the Places API key fallback.', 'waypoints' ) - : __( 'A dedicated Geocoding API key is configured.', 'waypoints' ) ) - : __( 'Add a Geocoding API key or a Places API key fallback before testing location resolution.', 'waypoints' ), + ? __( 'Geocoding will use the Places API key fallback.', 'waypoints-trip-planner' ) + : __( 'A dedicated Geocoding API key is configured.', 'waypoints-trip-planner' ) ) + : __( 'Add a Geocoding API key or a Places API key fallback before testing location resolution.', 'waypoints-trip-planner' ), 'type' => '' !== $geocoding_key ? 'success' : 'warning', ], [ - 'label' => __( 'Maps Embed preview key', 'waypoints' ), - 'status' => '' !== $embed_key ? __( 'Ready', 'waypoints' ) : __( 'Optional', 'waypoints' ), + 'label' => __( 'Maps Embed preview key', 'waypoints-trip-planner' ), + 'status' => '' !== $embed_key ? __( 'Ready', 'waypoints-trip-planner' ) : __( 'Optional', 'waypoints-trip-planner' ), 'detail' => '' !== $embed_key - ? __( 'On-page Google Maps embeds can render when preview mode is enabled.', 'waypoints' ) - : __( 'Missing this key only affects on-page embed previews; Google Maps handoff links can still work.', 'waypoints' ), + ? __( 'On-page Google Maps embeds can render when preview mode is enabled.', 'waypoints-trip-planner' ) + : __( 'Missing this key only affects on-page embed previews; Google Maps handoff links can still work.', 'waypoints-trip-planner' ), 'type' => '' !== $embed_key ? 'success' : 'optional', ], [ - 'label' => __( 'Planner categories', 'waypoints' ), - 'status' => [] !== $active_categories ? __( 'Ready', 'waypoints' ) : __( 'Needs setup', 'waypoints' ), + 'label' => __( 'Planner categories', 'waypoints-trip-planner' ), + 'status' => [] !== $active_categories ? __( 'Ready', 'waypoints-trip-planner' ) : __( 'Needs setup', 'waypoints-trip-planner' ), 'detail' => [] !== $active_categories ? sprintf( /* translators: 1: active category count, 2: saved category count. */ - __( '%1$d active categories are available. %2$d categories are saved in settings.', 'waypoints' ), + __( '%1$d active categories are available. %2$d categories are saved in settings.', 'waypoints-trip-planner' ), count( $active_categories ), count( $saved_categories ) ) - : __( 'No active categories are available. Add at least one category, or leave the planner to custom search only.', 'waypoints' ), + : __( 'No active categories are available. Add at least one category, or leave the planner to custom search only.', 'waypoints-trip-planner' ), 'type' => [] !== $active_categories ? 'success' : 'warning', ], ]; @@ -556,35 +557,35 @@ private function build_setup_status_checks(): array { public function render_google_api_section(): void { printf( '

%s

', - esc_html__( 'Configure only the Google keys and request behavior needed by the backend Google client.', 'waypoints' ) + esc_html__( 'Configure only the Google keys and request behavior needed by the backend Google client.', 'waypoints-trip-planner' ) ); } public function render_default_location_section(): void { printf( '

%s

', - esc_html__( 'Set the required generic starting area. Site-specific destination values belong here, not in plugin defaults.', 'waypoints' ) + esc_html__( 'Set the required generic starting area. Site-specific destination values belong here, not in plugin defaults.', 'waypoints-trip-planner' ) ); } public function render_planner_behavior_section(): void { printf( '

%s

', - esc_html__( 'Register conservative behavior limits for later renderer and REST endpoint work.', 'waypoints' ) + esc_html__( 'Register conservative behavior limits for later renderer and REST endpoint work.', 'waypoints-trip-planner' ) ); } public function render_appearance_section(): void { printf( '

%s

', - esc_html__( 'Set the public planner default color mode. Visitors can switch modes on the planner without changing the saved plugin setting.', 'waypoints' ) + esc_html__( 'Set the public planner default color mode. Visitors can switch modes on the planner without changing the saved plugin setting.', 'waypoints-trip-planner' ) ); } public function render_interface_copy_section(): void { printf( '

%s

', - esc_html__( 'Edit the public planner copy here. Button labels and accessible labels fall back to defaults if saved blank. Optional helper text fields can be saved blank to hide them. Dynamic tokens such as {count}, {search}, {place}, and {start} are replaced automatically.', 'waypoints' ) + esc_html__( 'Edit the public planner copy here. Button labels and accessible labels fall back to defaults if saved blank. Optional helper text fields can be saved blank to hide them. Dynamic tokens such as {count}, {search}, {place}, and {start} are replaced automatically.', 'waypoints-trip-planner' ) ); $this->render_interface_copy_accordion(); @@ -593,28 +594,28 @@ public function render_interface_copy_section(): void { public function render_categories_section(): void { printf( '

%s

', - esc_html__( 'Edit the category buttons shown on the public planner. You can add, disable, delete, and reorder rows here, or leave the list empty to use custom search only.', 'waypoints' ) + esc_html__( 'Edit the category buttons shown on the public planner. You can add, disable, delete, and reorder rows here, or leave the list empty to use custom search only.', 'waypoints-trip-planner' ) ); } public function render_google_cache_section(): void { printf( '

%s

', - esc_html__( 'Configure transient-based caching for successful Google API responses.', 'waypoints' ) + esc_html__( 'Configure transient-based caching for successful Google API responses.', 'waypoints-trip-planner' ) ); } public function render_rate_limiting_section(): void { printf( '

%s

', - esc_html__( 'Control the active runtime limiter for public planner REST requests. Higher-cost requests can consume more of this budget before external Google work starts.', 'waypoints' ) + esc_html__( 'Control the active runtime limiter for public planner REST requests. Higher-cost requests can consume more of this budget before external Google work starts.', 'waypoints-trip-planner' ) ); } public function render_advanced_section(): void { printf( '

%s

', - esc_html__( 'Advanced networking settings used by later security and rate-limit code.', 'waypoints' ) + esc_html__( 'Advanced networking settings used by later security and rate-limit code.', 'waypoints-trip-planner' ) ); } @@ -631,18 +632,18 @@ public function render_missing_required_settings_notice(): void { esc_html( sprintf( /* translators: %s is a comma-separated list of missing settings. */ - __( 'Waypoints needs required settings before the public planner can render: %s.', 'waypoints' ), + __( 'Waypoints: Trip Planner needs required settings before the public planner can render: %s.', 'waypoints-trip-planner' ), $missing ) ), esc_url( $url ), - esc_html__( 'Open settings', 'waypoints' ) + esc_html__( 'Open settings', 'waypoints-trip-planner' ) ); } public function handle_clear_google_cache(): void { if ( ! current_user_can( 'manage_options' ) ) { - wp_die( esc_html__( 'You do not have permission to manage Waypoints settings.', 'waypoints' ) ); + wp_die( esc_html__( 'You do not have permission to manage Waypoints: Trip Planner settings.', 'waypoints-trip-planner' ) ); } check_admin_referer( 'plan_your_day_clear_google_cache' ); @@ -662,7 +663,7 @@ public function handle_clear_google_cache(): void { public function handle_clear_google_cache_scope(): void { if ( ! current_user_can( 'manage_options' ) ) { - wp_die( esc_html__( 'You do not have permission to manage Waypoints settings.', 'waypoints' ) ); + wp_die( esc_html__( 'You do not have permission to manage Waypoints: Trip Planner settings.', 'waypoints-trip-planner' ) ); } check_admin_referer( 'plan_your_day_clear_google_cache_scope' ); @@ -684,7 +685,7 @@ public function handle_clear_google_cache_scope(): void { public function handle_clear_google_cache_place(): void { if ( ! current_user_can( 'manage_options' ) ) { - wp_die( esc_html__( 'You do not have permission to manage Waypoints settings.', 'waypoints' ) ); + wp_die( esc_html__( 'You do not have permission to manage Waypoints: Trip Planner settings.', 'waypoints-trip-planner' ) ); } check_admin_referer( 'plan_your_day_clear_google_cache_place' ); @@ -706,7 +707,7 @@ public function handle_clear_google_cache_place(): void { public function handle_test_google_api(): void { if ( ! current_user_can( 'manage_options' ) ) { - wp_die( esc_html__( 'You do not have permission to manage Waypoints settings.', 'waypoints' ) ); + wp_die( esc_html__( 'You do not have permission to manage Waypoints: Trip Planner settings.', 'waypoints-trip-planner' ) ); } check_admin_referer( 'plan_your_day_test_google_api' ); @@ -753,38 +754,38 @@ private function run_google_api_test(): array { : null; $checks = [ [ - 'label' => __( 'Geocoding probe', 'waypoints' ), + 'label' => __( 'Geocoding probe', 'waypoints-trip-planner' ), 'success' => $geocode_result->is_success(), 'detail' => $geocode_result->is_success() ? sprintf( /* translators: 1: latitude, 2: longitude. */ - __( 'Resolved the default location to %1$s, %2$s.', 'waypoints' ), + __( 'Resolved the default location to %1$s, %2$s.', 'waypoints-trip-planner' ), (string) $origin_latitude, (string) $origin_longitude ) : $geocode_result->message(), ], [ - 'label' => __( 'Text search probe', 'waypoints' ), + 'label' => __( 'Text search probe', 'waypoints-trip-planner' ), 'success' => $text_search_result->is_success(), 'detail' => $text_search_result->is_success() ? sprintf( /* translators: 1: query text, 2: result count. */ - __( 'Query "%1$s" returned %2$d place results.', 'waypoints' ), + __( 'Query "%1$s" returned %2$d place results.', 'waypoints-trip-planner' ), $probe_query, count( $places ) ) : $text_search_result->message(), ], [ - 'label' => __( 'Place details probe', 'waypoints' ), + 'label' => __( 'Place details probe', 'waypoints-trip-planner' ), 'success' => $place_details_result instanceof \Acodebeard\PlanYourDay\Google\GoogleApiResult && $place_details_result->is_success(), 'detail' => null === $place_details_result - ? __( 'Skipped because the text search probe did not return a place ID to inspect.', 'waypoints' ) + ? __( 'Skipped because the text search probe did not return a place ID to inspect.', 'waypoints-trip-planner' ) : ( $place_details_result->is_success() ? sprintf( /* translators: %s is a place label. */ - __( 'Loaded details for %s.', 'waypoints' ), + __( 'Loaded details for %s.', 'waypoints-trip-planner' ), (string) ( $place_details_result->data()['place']['label'] ?? $place_id ) ) : $place_details_result->message() ), @@ -842,7 +843,7 @@ private function render_google_test_notice(): void { esc_html( sprintf( /* translators: 1: passed checks, 2: total checks. */ - __( 'Google API test completed: %1$d of %2$d probes passed.', 'waypoints' ), + __( 'Google API test completed: %1$d of %2$d probes passed.', 'waypoints-trip-planner' ), (int) ( $results['success_count'] ?? 0 ), (int) ( $results['total_count'] ?? 0 ) ) @@ -858,13 +859,13 @@ private function render_google_test_results_panel(): void { return; } ?> -

+

- - - + + + @@ -884,7 +885,7 @@ private function render_google_test_results_panel(): void { - + @@ -978,7 +979,7 @@ public function render_field( array $args ): void { esc_attr( $name ), esc_attr( $id ), checked( true, (bool) $value, false ), - esc_html__( 'Enabled', 'waypoints' ) + esc_html__( 'Enabled', 'waypoints-trip-planner' ) ); } elseif ( 'checkbox_group' === $type ) { $this->render_checkbox_group( $name, $id, is_array( $value ) ? $value : [], $attributes ); @@ -988,6 +989,8 @@ public function render_field( array $args ): void { $this->render_categories_editor( $name ); } elseif ( 'interface_copy_group' === $type ) { $this->render_interface_copy_group( (string) ( $attributes['group'] ?? '' ) ); + } elseif ( 'api_key' === $type ) { + $this->render_api_key_field( $name, $id, (string) $value ); } else { printf( '', @@ -1003,6 +1006,18 @@ public function render_field( array $args ): void { } } + private function render_api_key_field( string $name, string $id, string $value ): void { + printf( + '', + esc_attr( $id ), + esc_attr( $name ), + esc_attr( $value ), + esc_attr( self::GOOGLE_API_KEY_PATTERN ), + esc_attr( __( 'Show API key', 'waypoints-trip-planner' ) ), + esc_attr( __( 'Hide API key', 'waypoints-trip-planner' ) ) + ); + } + private function render_interface_copy_group( string $group ): void { $definitions = InterfaceCopy::definitions_for_group( $group ); $copy = $this->settings->get_frontend_copy(); @@ -1102,7 +1117,7 @@ private function render_categories_editor( string $name ): void {

+ data-plan-delete-category-confirm=""> @@ -1114,12 +1129,12 @@ class="plan-your-day-categories-editor" - - - - - - + + + + + + @@ -1129,7 +1144,7 @@ class="plan-your-day-categories-editor"

- +