first commit
This commit is contained in:
@@ -0,0 +1,506 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/core/PKPPageRouter.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PKPPageRouter
|
||||
*
|
||||
* @ingroup core
|
||||
*
|
||||
* @brief Class mapping an HTTP request to a handler or context.
|
||||
*/
|
||||
|
||||
namespace PKP\core;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\security\Role;
|
||||
use PKP\security\Validation;
|
||||
use PKP\session\SessionManager;
|
||||
|
||||
define('ROUTER_DEFAULT_PAGE', './pages/index/index.php');
|
||||
define('ROUTER_DEFAULT_OP', 'index');
|
||||
|
||||
class PKPPageRouter extends PKPRouter
|
||||
{
|
||||
/** @var array pages that don't need an installed system to be displayed */
|
||||
public $_installationPages = ['install', 'help', 'header', 'sidebar'];
|
||||
|
||||
//
|
||||
// Internal state cache variables
|
||||
// NB: Please do not access directly but
|
||||
// only via their respective getters/setters
|
||||
//
|
||||
/** @var string the requested page */
|
||||
public $_page;
|
||||
/** @var string the requested operation */
|
||||
public $_op;
|
||||
/** @var string cache filename */
|
||||
public $_cacheFilename;
|
||||
|
||||
/**
|
||||
* get the installation pages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInstallationPages()
|
||||
{
|
||||
return $this->_installationPages;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the cacheable pages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCacheablePages()
|
||||
{
|
||||
// Can be overridden by sub-classes.
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not the request is cacheable.
|
||||
*
|
||||
* @param PKPRequest $request
|
||||
* @param bool $testOnly required for unit test to
|
||||
* bypass session check.
|
||||
*
|
||||
*/
|
||||
public function isCacheable($request, $testOnly = false): bool
|
||||
{
|
||||
if (SessionManager::isDisabled() && !$testOnly) {
|
||||
return false;
|
||||
}
|
||||
if (Application::isUnderMaintenance()) {
|
||||
return false;
|
||||
}
|
||||
if (!empty($_POST) || Validation::isLoggedIn()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($_GET)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (in_array($this->getRequestedPage($request), $this->getCacheablePages())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the page requested in the URL.
|
||||
*
|
||||
* @param PKPRequest $request the request to be routed
|
||||
*
|
||||
* @return string the page path (under the "pages" directory)
|
||||
*/
|
||||
public function getRequestedPage($request)
|
||||
{
|
||||
if (!isset($this->_page)) {
|
||||
$this->_page = $this->_getRequestedUrlParts(['Core', 'getPage'], $request);
|
||||
}
|
||||
return $this->_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the operation requested in the URL (assumed to exist in the requested page handler).
|
||||
*
|
||||
* @param PKPRequest $request the request to be routed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRequestedOp($request)
|
||||
{
|
||||
if (!isset($this->_op)) {
|
||||
$this->_op = $this->_getRequestedUrlParts(['Core', 'getOp'], $request);
|
||||
}
|
||||
return $this->_op;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the arguments requested in the URL.
|
||||
*
|
||||
* @param PKPRequest $request the request to be routed
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRequestedArgs($request)
|
||||
{
|
||||
return $this->_getRequestedUrlParts(['Core', 'getArgs'], $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the anchor (#anchor) requested in the URL
|
||||
*
|
||||
* @para $request PKPRequest the request to be routed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRequestedAnchor($request)
|
||||
{
|
||||
$url = $request->getRequestUrl();
|
||||
$parts = explode('#', $url);
|
||||
if (count($parts) < 2) {
|
||||
return '';
|
||||
}
|
||||
return $parts[1];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Implement template methods from PKPRouter
|
||||
//
|
||||
/**
|
||||
* @copydoc PKPRouter::getCacheFilename()
|
||||
*/
|
||||
public function getCacheFilename($request)
|
||||
{
|
||||
if (!isset($this->_cacheFilename)) {
|
||||
$id = $_SERVER['PATH_INFO'] ?? 'index';
|
||||
$id .= '-' . Locale::getLocale();
|
||||
$path = Core::getBaseDir();
|
||||
$this->_cacheFilename = $path . '/cache/wc-' . md5($id) . '.html';
|
||||
}
|
||||
return $this->_cacheFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPRouter::route()
|
||||
*/
|
||||
public function route($request)
|
||||
{
|
||||
// Determine the requested page and operation
|
||||
$page = $this->getRequestedPage($request);
|
||||
$op = $this->getRequestedOp($request);
|
||||
|
||||
// If the application has not yet been installed we only
|
||||
// allow installer pages to be displayed.
|
||||
if (!Application::isInstalled()) {
|
||||
if (!in_array($page, $this->getInstallationPages())) {
|
||||
// A non-installation page was called although
|
||||
// the system is not yet installed. Redirect to
|
||||
// the installation page.
|
||||
$request->redirect('index', 'install');
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect requests from logged-out users to a context which is not
|
||||
// publicly enabled
|
||||
if (!SessionManager::isDisabled()) {
|
||||
$user = $request->getUser();
|
||||
$currentContext = $request->getContext();
|
||||
if ($currentContext && !$currentContext->getEnabled() && !$user instanceof \PKP\user\User) {
|
||||
if ($page != 'login') {
|
||||
$request->redirect(null, 'login');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the page index file. This file contains the
|
||||
// logic to resolve a page to a specific handler class.
|
||||
$sourceFile = sprintf('pages/%s/index.php', $page);
|
||||
|
||||
// If a hook has been registered to handle this page, give it the
|
||||
// opportunity to load required resources and set the handler.
|
||||
$handler = null;
|
||||
if (!Hook::call('LoadHandler', [&$page, &$op, &$sourceFile, &$handler])) {
|
||||
if (file_exists($sourceFile)) {
|
||||
$result = require('./' . $sourceFile);
|
||||
if (is_object($result)) {
|
||||
$handler = $result;
|
||||
}
|
||||
} elseif (file_exists(PKP_LIB_PATH . "/{$sourceFile}")) {
|
||||
$result = require('./' . PKP_LIB_PATH . "/{$sourceFile}");
|
||||
if (is_object($result)) {
|
||||
$handler = $result;
|
||||
}
|
||||
} elseif (empty($page)) {
|
||||
require(ROUTER_DEFAULT_PAGE);
|
||||
} else {
|
||||
$dispatcher = $this->getDispatcher();
|
||||
$dispatcher->handle404();
|
||||
}
|
||||
}
|
||||
|
||||
if (!SessionManager::isDisabled()) {
|
||||
// Initialize session
|
||||
SessionManager::getManager();
|
||||
}
|
||||
|
||||
// Call the selected handler's index operation if
|
||||
// no operation was defined in the request.
|
||||
if (empty($op)) {
|
||||
$op = ROUTER_DEFAULT_OP;
|
||||
}
|
||||
|
||||
// Redirect to 404 if the operation doesn't exist
|
||||
// for the handler.
|
||||
$methods = [];
|
||||
if ($handler) {
|
||||
$methods = get_class_methods($handler);
|
||||
} elseif (defined('HANDLER_CLASS')) {
|
||||
// The use of HANDLER_CLASS is DEPRECATED with 3.4.0 pkp/pkp-lib#6019
|
||||
$methods = get_class_methods(HANDLER_CLASS);
|
||||
}
|
||||
if (!in_array($op, $methods)) {
|
||||
$dispatcher = $this->getDispatcher();
|
||||
$dispatcher->handle404();
|
||||
}
|
||||
|
||||
// Instantiate the handler class
|
||||
if (!$handler) {
|
||||
// The use of HANDLER_CLASS is DEPRECATED with 3.4.0 pkp/pkp-lib#6019
|
||||
$handlerClass = HANDLER_CLASS;
|
||||
$handler = new $handlerClass($request);
|
||||
}
|
||||
$this->setHandler($handler);
|
||||
|
||||
// Authorize and initialize the request but don't call the
|
||||
// validate() method on page handlers.
|
||||
// FIXME: We should call the validate() method for page
|
||||
// requests also (last param = true in the below method
|
||||
// call) once we've made sure that all validate() calls can
|
||||
// be removed from handler operations without damage (i.e.
|
||||
// they don't depend on actions being performed before the
|
||||
// call to validate().
|
||||
$args = $this->getRequestedArgs($request);
|
||||
$serviceEndpoint = [$handler, $op];
|
||||
$this->_authorizeInitializeAndCallRequest($serviceEndpoint, $request, $args, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPRouter::url()
|
||||
*
|
||||
* @param null|mixed $newContext
|
||||
* @param null|mixed $page
|
||||
* @param null|mixed $op
|
||||
* @param null|mixed $path
|
||||
* @param null|mixed $params
|
||||
* @param null|mixed $anchor
|
||||
*/
|
||||
public function url(
|
||||
PKPRequest $request,
|
||||
?string $newContext = null,
|
||||
$page = null,
|
||||
$op = null,
|
||||
$path = null,
|
||||
$params = null,
|
||||
$anchor = null,
|
||||
$escape = false
|
||||
) {
|
||||
//
|
||||
// Base URL and Context
|
||||
//
|
||||
$baseUrlAndContext = $this->_urlGetBaseAndContext($request, $newContext);
|
||||
$baseUrl = array_shift($baseUrlAndContext);
|
||||
$context = array_shift($baseUrlAndContext);
|
||||
|
||||
//
|
||||
// Additional path info
|
||||
//
|
||||
if (empty($path)) {
|
||||
$additionalPath = [];
|
||||
} else {
|
||||
if (is_array($path)) {
|
||||
$additionalPath = array_map('rawurlencode', $path);
|
||||
} else {
|
||||
$additionalPath = [rawurlencode($path)];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Page and Operation
|
||||
//
|
||||
|
||||
// Are we in a page request?
|
||||
$currentRequestIsAPageRequest = $request->getRouter() instanceof \PKP\core\PKPPageRouter;
|
||||
|
||||
// Determine the operation
|
||||
if ($op) {
|
||||
// If an operation has been explicitly set then use it.
|
||||
$op = rawurlencode($op);
|
||||
} else {
|
||||
// No operation has been explicitly set so let's determine a sensible
|
||||
// default operation.
|
||||
if (empty($newContext) && empty($page) && $currentRequestIsAPageRequest) {
|
||||
// If we remain in the existing context and on the existing page then
|
||||
// we will default to the current operation. We can only determine a
|
||||
// current operation if the current request is a page request.
|
||||
$op = $this->getRequestedOp($request);
|
||||
} else {
|
||||
// If a new context (or page) has been set then we'll default to the
|
||||
// index operation within the new context (or on the new page).
|
||||
if (empty($additionalPath)) {
|
||||
// If no additional path is set we can simply leave the operation
|
||||
// undefined which automatically defaults to the index operation
|
||||
// but gives shorter (=nicer) URLs.
|
||||
$op = null;
|
||||
} else {
|
||||
// If an additional path is set then we have to explicitly set the
|
||||
// index operation to disambiguate the path info.
|
||||
$op = 'index';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the page
|
||||
if ($page) {
|
||||
// If a page has been explicitly set then use it.
|
||||
$page = rawurlencode($page);
|
||||
} else {
|
||||
// No page has been explicitly set so let's determine a sensible default page.
|
||||
if (empty($newContext) && $currentRequestIsAPageRequest) {
|
||||
// If we remain in the existing context then we will default to the current
|
||||
// page. We can only determine a current page if the current request is a
|
||||
// page request.
|
||||
$page = $this->getRequestedPage($request);
|
||||
} else {
|
||||
// If a new context has been set then we'll default to the index page
|
||||
// within the new context.
|
||||
if (empty($op)) {
|
||||
// If no explicit operation is set we can simply leave the page
|
||||
// undefined which automatically defaults to the index page but gives
|
||||
// shorter (=nicer) URLs.
|
||||
$page = null;
|
||||
} else {
|
||||
// If an operation is set then we have to explicitly set the index
|
||||
// page to disambiguate the path info.
|
||||
$page = 'index';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Additional query parameters
|
||||
//
|
||||
$additionalParameters = $this->_urlGetAdditionalParameters($request, $params, $escape);
|
||||
|
||||
//
|
||||
// Anchor
|
||||
//
|
||||
$anchor = (empty($anchor) ? '' : '#' . preg_replace("/[^a-zA-Z0-9\-\_\/\.\~]/", '', $anchor));
|
||||
|
||||
//
|
||||
// Assemble URL
|
||||
//
|
||||
// Context, page, operation and additional path go into the path info.
|
||||
$pathInfoArray = $context;
|
||||
if (!empty($page)) {
|
||||
$pathInfoArray[] = $page;
|
||||
if (!empty($op)) {
|
||||
$pathInfoArray[] = $op;
|
||||
}
|
||||
}
|
||||
$pathInfoArray = array_merge($pathInfoArray, $additionalPath);
|
||||
|
||||
// Query parameters
|
||||
$queryParametersArray = $additionalParameters;
|
||||
|
||||
return $this->_urlFromParts($baseUrl, $pathInfoArray, $queryParametersArray, $anchor, $escape);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPRouter::handleAuthorizationFailure()
|
||||
*/
|
||||
public function handleAuthorizationFailure(
|
||||
$request,
|
||||
$authorizationMessage,
|
||||
array $messageParams = []
|
||||
) {
|
||||
// Redirect to the authorization denied page.
|
||||
if (!$request->getUser()) {
|
||||
Validation::redirectLogin();
|
||||
}
|
||||
$request->redirect(null, 'user', 'authorizationDenied', null, ['message' => $authorizationMessage]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to user home page (or the user group home page if the user has one user group).
|
||||
*/
|
||||
public function redirectHome(PKPRequest $request)
|
||||
{
|
||||
$request->redirectUrl($this->getHomeUrl($request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's "home" page URL (e.g. where they are sent after login).
|
||||
*
|
||||
* @param PKPRequest $request the request to be routed
|
||||
*/
|
||||
public function getHomeUrl($request)
|
||||
{
|
||||
$user = $request->getUser();
|
||||
$userId = $user->getId();
|
||||
|
||||
if ($context = $this->getContext($request)) {
|
||||
// If the user has no roles, or only one role and this is reader, go to "Index" page.
|
||||
// Else go to "submissions" page
|
||||
$userGroups = Repo::userGroup()->userUserGroups($userId, $context->getId());
|
||||
|
||||
if ($userGroups->isEmpty()
|
||||
|| ($userGroups->count() == 1 && $userGroups->first()->getRoleId() == Role::ROLE_ID_READER)
|
||||
) {
|
||||
return $request->url(null, 'index');
|
||||
}
|
||||
|
||||
return $request->url(null, 'submissions');
|
||||
} else {
|
||||
// The user is at the site context, check to see if they are
|
||||
// only registered in one place w/ one role
|
||||
$userGroups = Repo::userGroup()->userUserGroups($userId, \PKP\core\PKPApplication::CONTEXT_ID_NONE);
|
||||
|
||||
if ($userGroups->count() == 1) {
|
||||
$firstUserGroup = $userGroups->first();
|
||||
$contextDao = Application::getContextDAO();
|
||||
$context = $contextDao->getById($firstUserGroup->getContextId());
|
||||
if (!isset($context)) {
|
||||
$request->redirect('index', 'index');
|
||||
}
|
||||
if ($firstUserGroup->getRoleId() == Role::ROLE_ID_READER) {
|
||||
$request->redirect(null, 'index');
|
||||
}
|
||||
}
|
||||
return $request->url('index', 'index');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Private helper methods.
|
||||
//
|
||||
/**
|
||||
* Retrieve part of the current requested
|
||||
* url using the passed callback method.
|
||||
*
|
||||
* @param array $callback Core method to retrieve
|
||||
* page, operation or arguments from url.
|
||||
* @param PKPRequest $request
|
||||
*
|
||||
* @return array|string|null
|
||||
*/
|
||||
private function _getRequestedUrlParts($callback, &$request)
|
||||
{
|
||||
$url = null;
|
||||
assert($request->getRouter() instanceof \PKP\core\PKPPageRouter);
|
||||
|
||||
if (isset($_SERVER['PATH_INFO'])) {
|
||||
$url = $_SERVER['PATH_INFO'];
|
||||
}
|
||||
|
||||
$userVars = $request->getUserVars();
|
||||
return call_user_func_array($callback, [$url, true, $userVars]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\core\PKPPageRouter', '\PKPPageRouter');
|
||||
}
|
||||
Reference in New Issue
Block a user