diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ad946a582b..d80992d3af8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Fixed a “This password does not use the Bcrypt algorithm” error that could occur when logging in with a user whose password was set in an earlier version of Craft. - Fixed a “File name is not a string” error that could occur when an error was encountered when rendering a string template. ([#19122](https://github.com/craftcms/cms/pull/19122)) - Fixed a bug where parsed site names would get saved to the project config. ([#19123](https://github.com/craftcms/cms/issues/19123)) +- Fixed several issues that occurred when Craft was configured with a custom (or no) `cpTrigger`. ([#19127](https://github.com/craftcms/cms/pull/19127)) ## 6.0.0-alpha.8 - 2026-06-17 diff --git a/packages/craftcms-cp/src/utilities/api/actionClient.ts b/packages/craftcms-cp/src/utilities/api/actionClient.ts index 4969ff9b1a4..6cac50742ce 100644 --- a/packages/craftcms-cp/src/utilities/api/actionClient.ts +++ b/packages/craftcms-cp/src/utilities/api/actionClient.ts @@ -1,11 +1,13 @@ import axios, {type RawAxiosRequestHeaders} from 'axios'; import {Csrf} from '@src/services/Csrf'; +import {ConfigService} from '@src/services/Config'; /** - * @TODO + * Builds an action URL using the runtime-configured action base + * (`Url::actionUrl()`), so the CP trigger isn't hard-coded to `/admin`. */ export function getActionUrl(action: string = '') { - return `/admin/actions/${action}`; + return ConfigService.getInstance().getActionUrl(action); } /** @@ -27,13 +29,15 @@ export function actionHeaders(): RawAxiosRequestHeaders { return headers; } -export const actionClient = axios.create({ - baseURL: getActionUrl(), -}); +export const actionClient = axios.create(); const csrf = new Csrf(); actionClient.interceptors.request.use(async (config) => { + // Resolve the base URL lazily so it reflects the runtime CP trigger. Config + // isn't guaranteed to be initialized when this module is first imported. + config.baseURL = getActionUrl(); + // Set X-Requested-With header config.headers.set('X-Requested-With', 'XMLHttpRequest'); diff --git a/resources/js/bootstrap/cp.ts b/resources/js/bootstrap/cp.ts index 60275f799cb..503c6f9b595 100644 --- a/resources/js/bootstrap/cp.ts +++ b/resources/js/bootstrap/cp.ts @@ -15,6 +15,7 @@ import AssetIndexes from '@/modules/utilities/components/asset-indexes/AssetInde import SystemMessages from '@/modules/utilities/components/system-messages/SystemMessages.vue'; import DeprecationErrorsToolbar from '@/modules/utilities/components/deprecation-errors/DeprecationErrorsToolbar.vue'; import {setTranslations} from '@craftcms/cp/utilities/translate.ts.mjs'; +import {setUrlDefaults} from '@/wayfinder'; let bootedCallbacks: Array<(instance: any) => void> = []; let bootingCallbacks: Array<(instance: any) => void> = []; @@ -23,6 +24,14 @@ let bootingCallbacks: Array<(instance: any) => void> = []; const config = ConfigService.getInstance(); const queue = QueueService.getInstance(); +function routeSegment(value: unknown): string { + if (value === null || value === undefined) { + return ''; + } + + return value.toString().replace(/^\/+|\/+$/g, ''); +} + // Create our object const Cp = { initialConfig: {} as Record, @@ -53,6 +62,12 @@ const Cp = { init() { config.initialize(this.initialConfig); + + setUrlDefaults(() => ({ + cpTrigger: routeSegment(config.get('cpTrigger')), + actionTrigger: routeSegment(config.get('actionTrigger')), + })); + queue.initialize({ runAutomatically: config.get('runQueueAutomatically', true), enabled: true, diff --git a/resources/js/common/components/CurrentUser.vue b/resources/js/common/components/CurrentUser.vue index 30c21fdc165..184c14f52c6 100644 --- a/resources/js/common/components/CurrentUser.vue +++ b/resources/js/common/components/CurrentUser.vue @@ -24,7 +24,9 @@