Skip to content

Commit

Permalink
Show locale in url in multilingual contexts
Browse files Browse the repository at this point in the history
  • Loading branch information
jyhein committed Jan 24, 2024
1 parent c1714da commit c3c2400
Show file tree
Hide file tree
Showing 14 changed files with 201 additions and 51 deletions.
30 changes: 25 additions & 5 deletions classes/core/Core.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
use PKP\cache\CacheManager;
use PKP\cache\FileCache;
use PKP\config\Config;
use PKP\facades\Locale;
use SplFileInfo;
use PKP\middleware\Localize;

define('PKP_LIB_PATH', 'lib/pkp');
define('COUNTER_USER_AGENTS_FILE', Core::getBaseDir() . '/' . PKP_LIB_PATH . '/lib/counterBots/generated/COUNTER_Robots_list.txt');
Expand Down Expand Up @@ -146,12 +148,22 @@ public static function isUserAgentBot($userAgent, $botRegexpsFile = COUNTER_USER
*
* @param string $urlInfo Full url or just path info.
*/
public static function getContextPath(string $urlInfo): string
public static function getContextPath(?string $urlInfo): string
{
$contextPaths = explode('/', trim($urlInfo, '/'), 2);
$contextPaths = explode('/', trim($urlInfo ?? '', '/'), 2);
return self::cleanFileVar($contextPaths[0] ?: 'index');
}

/**
* Get localization path present into the passed
* url information.
*/
public static function getLocalization(?string $urlInfo): string
{
$locale = self::_getUrlComponents($urlInfo, false, 0);
return self::cleanFileVar(Locale::isLocaleValid($locale) ? $locale : "");
}

/**
* Get the page present into
* the passed url information. It expects that urls
Expand All @@ -167,7 +179,7 @@ public static function getContextPath(string $urlInfo): string
*/
public static function getPage($urlInfo, $isPathInfo, $userVars = [])
{
$page = Core::_getUrlComponents($urlInfo, $isPathInfo, 0, 'page', $userVars);
$page = Core::_getUrlComponents($urlInfo, $isPathInfo, self::_getOffset($urlInfo, 0), 'page', $userVars);
return Core::cleanFileVar(is_null($page) ? '' : $page);
}

Expand All @@ -186,7 +198,7 @@ public static function getPage($urlInfo, $isPathInfo, $userVars = [])
*/
public static function getOp($urlInfo, $isPathInfo, $userVars = [])
{
$operation = Core::_getUrlComponents($urlInfo, $isPathInfo, 1, 'op', $userVars);
$operation = Core::_getUrlComponents($urlInfo, $isPathInfo, self::_getOffset($urlInfo, 1), 'op', $userVars);
return Core::cleanFileVar(empty($operation) ? 'index' : $operation);
}

Expand All @@ -206,7 +218,7 @@ public static function getOp($urlInfo, $isPathInfo, $userVars = [])
*/
public static function getArgs($urlInfo, $isPathInfo, $userVars = [])
{
return Core::_getUrlComponents($urlInfo, $isPathInfo, 2, 'path', $userVars);
return Core::_getUrlComponents($urlInfo, $isPathInfo, self::_getOffset($urlInfo, 2), 'path', $userVars);
}

/**
Expand Down Expand Up @@ -487,6 +499,14 @@ private static function _getUrlComponents($urlInfo, $isPathInfo, $offset, $varNa
return $component;
}

/**
* Get offset. Add 1 extra if localization present in URL
*/
private static function _getOffset(?string $urlInfo, int $varOffset): int
{
return $varOffset + (int) !empty(self::getLocalization($urlInfo));
}

/**
* Extract the class name from the given file path.
*
Expand Down
6 changes: 4 additions & 2 deletions classes/core/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ public function dispatch($request)
* @param array $params Optional set of name => value pairs to pass as user parameters
* @param string $anchor Optional name of anchor to add to URL
* @param bool $escape Whether or not to escape ampersands for this URL; default false.
* @param string $urlLocaleForPage Whether or not to override locale for this URL.
*
* @return string the URL
*/
Expand All @@ -191,14 +192,15 @@ public function url(
$path = null,
$params = null,
$anchor = null,
$escape = false
$escape = false,
?string $urlLocaleForPage = null,
) {
// Instantiate the requested router
assert(isset($this->_routerNames[$shortcut]));
$routerName = $this->_routerNames[$shortcut];
$router = & $this->_instantiateRouter($routerName, $shortcut);

return $router->url($request, $newContext, $handler, $op, $path, $params, $anchor, $escape);
return $router->url($request, $newContext, $handler, $op, $path, $params, $anchor, $escape, $urlLocaleForPage);
}

//
Expand Down
84 changes: 82 additions & 2 deletions classes/core/PKPPageRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

use APP\core\Application;
use APP\facades\Repo;
use PKP\context\Context;
use PKP\db\DAORegistry;
use PKP\facades\Locale;
use PKP\plugins\Hook;
use PKP\security\Role;
Expand Down Expand Up @@ -238,6 +240,12 @@ public function route($request)
SessionManager::getManager();
}

// Set locale from URL or from 'setLocale'-op/search-params
$setLocale = ($op === 'setLocale' ? ($this->getRequestedArgs($request)[0] ?? null)
: ($page === 'install' ? ($_GET['setLocale'] ?? null)
: null));
$this->_setLocale($request, $setLocale);

// Call the selected handler's index operation if
// no operation was defined in the request.
if (empty($op)) {
Expand Down Expand Up @@ -288,6 +296,7 @@ public function route($request)
* @param null|mixed $path
* @param null|mixed $params
* @param null|mixed $anchor
* @param null|string $urlLocaleForPage
*/
public function url(
PKPRequest $request,
Expand All @@ -297,7 +306,8 @@ public function url(
$path = null,
$params = null,
$anchor = null,
$escape = false
$escape = false,
?string $urlLocaleForPage = null,
) {
//
// Base URL and Context
Expand Down Expand Up @@ -394,8 +404,12 @@ public function url(
//
// Assemble URL
//
// Context, page, operation and additional path go into the path info.
// Context, locale?, page, operation and additional path go into the path info.
$pathInfoArray = $context;
[$contextObject, $contextLocales] = $this->_getContextAndLocales($request, $context[0] ?? "");
if (count($contextLocales) > 1) {
$pathInfoArray[] = $this->_getLocaleForUrl($request, $contextObject, $contextLocales, $urlLocaleForPage);
}
if (!empty($page)) {
$pathInfoArray[] = $page;
if (!empty($op)) {
Expand Down Expand Up @@ -501,6 +515,72 @@ private function _getRequestedUrlParts($callback, &$request)
$userVars = $request->getUserVars();
return call_user_func_array($callback, [$url, true, $userVars]);
}

/**
* Get context object and context/site/all locales.
*/
private function _getContextAndLocales(PKPRequest $request, string $contextPath): array
{
return [
$context = $this->getCurrentContext() ?? (($contextPath === 'index' || !$contextPath || $contextPath === Application::CONTEXT_ID_ALL)
? null
: DAORegistry::getDAO(ucfirst(Application::get()->getContextName()) . 'DAO')->getByPath($contextPath)),
$context?->getSupportedLocales()
?? (($contextPath === 'index')
? (Application::isInstalled()
? $request->getSite()->getSupportedLocales()
: array_keys(Locale::getSupportedLocales()))
: [])
];
}

/**
* Get locale for URL from session or primary
*/
private function _getLocaleForUrl(PKPRequest $request, ?Context $context, array $locales, ?string $urlLocaleForPage): string
{
return in_array($locale = $urlLocaleForPage ?: Locale::getLocale(), $locales)
? $locale
: (($context ?? $request->getSite())?->getPrimaryLocale() ?? Locale::getPrimaryLocale());
}

/**
* Change the locale for the current user.
* Redirect to url with(out) locale if locale changed or context set to multi/monolingual.
*/
private function _setLocale(PKPRequest $request, ?string $setLocale): void
{
$contextPath = $this->_getRequestedUrlParts(['Core', 'getContextPath'], $request);
$urlLocale = $this->_getRequestedUrlParts(['Core', 'getLocalization'], $request);
$multiLingual = count($this->_getContextAndLocales($request, $contextPath)[1]) > 1;

if (!isset($setLocale) && (!$multiLingual && !$urlLocale || $multiLingual && $urlLocale === Locale::getLocale())) {
return;
}

(function (string $l) use ($request): void {
if (Locale::isSupported($l) && $l !== Locale::getLocale()) {
Locale::setLocale($l);
if (!Application::isInstalled()) {
$request->setCookieVar('currentLocale', Locale::getLocale());
}
}
})($setLocale ?? $urlLocale);

if (preg_match('#^/\w#', $source = $request->getUserVar('source')) === 1) {
$request->redirectUrl($source);
}

$uri = Core::removeBaseUrl((isset($_SERVER['HTTP_REFERER']) && $setLocale)
? $_SERVER['HTTP_REFERER']
: ($_SERVER['REQUEST_URI'] ?? ""));
$newLocale = $multiLingual ? Locale::getLocale() . "/" : "";
$pathInfo = ($uri)
? preg_replace("#^/$contextPath" . ($urlLocale ? "/$urlLocale" : "") . "(/|$)#", "/$contextPath/$newLocale", $uri, 1)
: "/index/$newLocale";

$request->redirectUrl($request->getBaseUrl() . $pathInfo);
}
}

if (!PKP_STRICT_MODE) {
Expand Down
5 changes: 3 additions & 2 deletions classes/core/PKPRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -763,11 +763,12 @@ public function setCookieVar($key, $value, $expire = 0)
* @param mixed $path string or array containing path info for redirect.
* @param array $params Map of name => value pairs for additional parameters
* @param string $anchor Name of desired anchor on the target page
* @param string $urlLocaleForPage Locale to include in url
*/
public function redirect($context = null, $page = null, $op = null, $path = null, $params = null, $anchor = null)
public function redirect($context = null, $page = null, $op = null, $path = null, $params = null, $anchor = null, ?string $urlLocaleForPage = null)
{
$dispatcher = $this->getDispatcher();
$this->redirectUrl($dispatcher->url($this, PKPApplication::ROUTE_PAGE, $context, $page, $op, $path, $params, $anchor));
$this->redirectUrl($dispatcher->url($this, PKPApplication::ROUTE_PAGE, $context, $page, $op, $path, $params, $anchor, false, $urlLocaleForPage));
}

/**
Expand Down
8 changes: 8 additions & 0 deletions classes/core/PKPRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ public function setApplication(Application $application)
$this->_application = $application;
}

/**
* get the current context
*/
public function getCurrentContext(): ?Context
{
return $this->_context;
}

/**
* get the dispatcher
*/
Expand Down
3 changes: 3 additions & 0 deletions classes/i18n/Locale.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ public function setLocale($locale): void
$this->locale = $locale;
setlocale(LC_ALL, "{$locale}.utf-8", $locale);
putenv("LC_ALL={$locale}");
if (SessionManager::hasSession()) {
SessionManager::getManager()->getUserSession()->setSessionVar('currentLocale', $locale);
}
}

/**
Expand Down
4 changes: 4 additions & 0 deletions classes/security/Validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ class Validation
*/
public static function login($username, $password, &$reason, $remember = false)
{
if (!isset($username)) {
return false;
}

$reason = null;
$authKey = static::AUTH_KEY_USERNAME;

Expand Down
7 changes: 7 additions & 0 deletions classes/template/PKPTemplateManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,13 @@ public function initialize($request)
$this->addHeader('customHeaders', $customHeaders);
}
}

if (count($supportedLocales = Locale::getSupportedlocales()) > 1) {
$page = $request->getBaseUrl() . "/" . ($currentContext?->getData('urlPath') ?? 'index');
collect($supportedLocales)
->each(fn ($_, string $l) => $this->addHeader("language-$l", "<link rel='alternate' hreflang='" . strtolower(str_replace('_', '-', $l)) . "' href='$page/$l' />"));
$this->addHeader("language-xdefault", "<link rel='alternate' hreflang='x-default' href='$page/' />");
}
}

if ($currentContext && !$currentContext->getEnabled()) {
Expand Down
2 changes: 1 addition & 1 deletion cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Cypress.Commands.add('install', function() {
Cypress.Commands.add('login', (username, password, context) => {
context = context || 'index';
password = password || (username + username);
cy.visit('index.php/' + context + '/login/signIn', {
cy.visit('index.php/' + context + '/en/login/signIn', {
method: 'POST',
body: {username: username, password: password}
});
Expand Down
4 changes: 2 additions & 2 deletions cypress/tests/integration/oai/DC.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

describe('Dublin Core OAI tests', function() {
it('Exercise records', function() {
cy.request('index.php/index/oai?verb=ListRecords&metadataPrefix=oai_dc').then(response => {
cy.request('index.php/index/en/oai?verb=ListRecords&metadataPrefix=oai_dc').then(response => {
var identifier = null;

// Ensure we got a valid XML response
Expand Down Expand Up @@ -43,7 +43,7 @@ describe('Dublin Core OAI tests', function() {
expect(identifier).to.not.eq(null);

// Fetch an individual record by identifier
cy.request('index.php/index/oai?verb=GetRecord&metadataPrefix=oai_dc&identifier=' + encodeURI(identifier)).then(response => {
cy.request('index.php/index/en/oai?verb=GetRecord&metadataPrefix=oai_dc&identifier=' + encodeURI(identifier)).then(response => {
// Ensure we got a valid XML response
expect(response.status).to.eq(200);
expect(response.headers['content-type']).to.eq('text/xml;charset=utf-8');
Expand Down
32 changes: 0 additions & 32 deletions pages/user/PKPUserHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,38 +35,6 @@ public function index($args, $request)
$request->redirect(null, null, 'profile');
}

/**
* Change the locale for the current user.
*
* @param array $args first parameter is the new locale
*/
public function setLocale($args, $request)
{
$setLocale = array_shift($args);

$site = $request->getSite();
$context = $request->getContext();
if ($context != null) {
$contextSupportedLocales = (array) $context->getSupportedLocales();
}

if (Locale::isLocaleValid($setLocale) && (!isset($contextSupportedLocales) || in_array($setLocale, $contextSupportedLocales)) && in_array($setLocale, $site->getSupportedLocales())) {
$session = $request->getSession();
$session->setSessionVar('currentLocale', $setLocale);
}

$source = $request->getUserVar('source');
if (preg_match('#^/\w#', $source) === 1) {
$request->redirectUrl($source);
}

if (isset($_SERVER['HTTP_REFERER'])) {
$request->redirectUrl($_SERVER['HTTP_REFERER']);
}

$request->redirect(null, 'index');
}

/**
* Get interests for reviewer interests autocomplete.
*
Expand Down
Loading

0 comments on commit c3c2400

Please sign in to comment.