Describe the issue or problem
On a journal hosted at a domain that contains the literal substring “action” (our domain is interaction.id), every legacy grid AJAX request is sent to a corrupted domain. For example, the Discussions (queries) grid in the editorial workflow requests:
https://interfetch-grid.id/jiid/$$$call$$$/grid/queries/queries-grid/action
instead of:
https://interaction.id/jiid/$$$call$$$/grid/queries/queries-grid/fetch-grid
The corrupted request returns 503 and the workflow shows the popup “An unexpected error has occurred. Please reload the page and try again.” The Discussions panel never loads.
What application are you using?
OJS 3.5.0.4
Additional information / root cause
PKPTemplateManager injects the page context variable legacyGridBaseUrl as an absolute URL:
https://interaction.id/jiid/$$$call$$$/component/action
The compiled frontend (build.js) then builds the final grid URL with something equivalent to:
legacyGridBaseUrl.replace("component", component).replace("action", op)
Because String.prototype.replace() with a string argument replaces only the FIRST occurrence, .replace("action", op) matches the “action” inside the domain name “interACTION.id” rather than the intended path placeholder at the end of the URL. The host becomes “inter” + “fetch-grid” = “interfetch-grid”, while the trailing “/action” placeholder is left untouched. The same class of bug would affect any domain containing the substring “component” or “action”.
Steps to reproduce
- Install OJS 3.5.x on a domain whose name contains the substring “action” (e.g. interaction.id).
-
- Open any editorial submission workflow that loads a legacy grid (for example the Pre-Review Discussions / queries grid).
-
- Observe the failing AJAX request to the mangled host and the “An unexpected error has occurred” dialog.
Suggested fix
- Observe the failing AJAX request to the mangled host and the “An unexpected error has occurred” dialog.
Scope the placeholder replacement to the path only, so the host can never be matched. Options:
- Anchor the op replacement to the end of the string in the frontend, e.g.
.replace(/action$/, op)instead of.replace("action", op); and similarly constrain the “component” replacement. -
- Or use unambiguous placeholder tokens (e.g.
%component%/%action%) that cannot appear in a hostname.
Server-side workaround (what we applied locally)
- Or use unambiguous placeholder tokens (e.g.
Emit legacyGridBaseUrl as a host-relative URL so the domain is no longer part of the string being searched. In lib/pkp/classes/template/PKPTemplateManager.php we wrapped the dispatcher call:
'legacyGridBaseUrl' => preg_replace('#^https?://[^/]+#', '', $dispatcher->url(
$request,
Application::ROUTE_COMPONENT,
null,
'componentHandler',
'action',
null,
)),
This yields "/jiid/$$$call$$$/component/action". The string no longer contains “action” in a host, so .replace() hits the correct path placeholder, and the browser still resolves the relative URL against the correct origin. After this change the grid request correctly goes to https://interaction.id/.../queries-grid/fetch-grid (HTTP 200) and the Discussions panel loads normally.