2007-07-19 12:40:28 +02:00
|
|
|
<?php
|
2015-02-23 14:46:00 +01:00
|
|
|
|
2016-08-19 00:51:35 +02:00
|
|
|
namespace SilverStripe\Control;
|
|
|
|
|
|
|
|
use SilverStripe\Core\ClassInfo;
|
|
|
|
use SilverStripe\Dev\Debug;
|
|
|
|
use SilverStripe\ORM\FieldType\DBHTMLText;
|
2016-06-23 01:37:22 +02:00
|
|
|
use SilverStripe\Security\BasicAuth;
|
|
|
|
use SilverStripe\Security\Member;
|
2017-05-20 06:32:25 +02:00
|
|
|
use SilverStripe\Security\Security;
|
2016-08-19 00:51:35 +02:00
|
|
|
use SilverStripe\View\SSViewer;
|
|
|
|
use SilverStripe\View\TemplateGlobalProvider;
|
2016-06-15 06:03:16 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2015-11-13 23:44:49 +01:00
|
|
|
* Controllers are the cornerstone of all site functionality in SilverStripe. The {@link Director}
|
2016-08-19 00:51:35 +02:00
|
|
|
* selects a controller to pass control to, and then calls {@link handleRequest()}. This method will execute
|
2007-07-19 12:40:28 +02:00
|
|
|
* the appropriate action - either by calling the action method, or displaying the action's template.
|
2007-09-14 20:02:03 +02:00
|
|
|
*
|
2007-07-19 12:40:28 +02:00
|
|
|
* See {@link getTemplate()} for information on how the template is chosen.
|
|
|
|
*/
|
2016-11-29 00:31:16 +01:00
|
|
|
class Controller extends RequestHandler implements TemplateGlobalProvider
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An array of arguments extracted from the URL.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $urlParams;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contains all GET and POST parameters passed to the current {@link HTTPRequest}.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $requestParams;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The URL part matched on the current controller as determined by the "$Action" part of the
|
|
|
|
* {@link $url_handlers} definition. Should correlate to a public method on this controller.
|
|
|
|
*
|
|
|
|
* Used in {@link render()} and {@link getViewer()} to determine action-specific templates.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $action;
|
|
|
|
|
|
|
|
/**
|
2017-06-22 12:50:45 +02:00
|
|
|
* Stack of current controllers. Controller::$controller_stack[0] is the current controller.
|
2016-11-29 00:31:16 +01:00
|
|
|
*
|
2017-06-22 12:50:45 +02:00
|
|
|
* @var array
|
2016-11-29 00:31:16 +01:00
|
|
|
*/
|
2017-06-22 12:50:45 +02:00
|
|
|
protected static $controller_stack = array();
|
2016-11-29 00:31:16 +01:00
|
|
|
|
|
|
|
/**
|
2017-06-22 12:50:45 +02:00
|
|
|
* Assign templates for this controller.
|
|
|
|
* Map of action => template name
|
2016-11-29 00:31:16 +01:00
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
2017-06-22 12:50:45 +02:00
|
|
|
protected $templates = [];
|
2016-11-29 00:31:16 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected $basicAuthEnabled = true;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The response object that the controller returns.
|
|
|
|
*
|
|
|
|
* Set in {@link handleRequest()}.
|
|
|
|
*
|
|
|
|
* @var HTTPResponse
|
|
|
|
*/
|
|
|
|
protected $response;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default URL handlers.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
private static $url_handlers = array(
|
|
|
|
'$Action//$ID/$OtherID' => 'handleAction',
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
private static $allowed_actions = array(
|
|
|
|
'handleAction',
|
|
|
|
'handleIndex',
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialisation function that is run before any action on the controller is called.
|
|
|
|
*
|
|
|
|
* @uses BasicAuth::requireLogin()
|
|
|
|
*/
|
|
|
|
protected function init()
|
|
|
|
{
|
|
|
|
if ($this->basicAuthEnabled) {
|
|
|
|
BasicAuth::protect_site_if_necessary();
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is used to test that subordinate controllers are actually calling parent::init() - a common bug
|
|
|
|
$this->baseInitCalled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A stand in function to protect the init function from failing to be called as well as providing before and
|
|
|
|
* after hooks for the init function itself
|
|
|
|
*
|
|
|
|
* This should be called on all controllers before handling requests
|
|
|
|
*/
|
|
|
|
public function doInit()
|
|
|
|
{
|
|
|
|
//extension hook
|
|
|
|
$this->extend('onBeforeInit');
|
|
|
|
|
|
|
|
// Safety call
|
|
|
|
$this->baseInitCalled = false;
|
|
|
|
$this->init();
|
|
|
|
if (!$this->baseInitCalled) {
|
2017-05-17 07:40:13 +02:00
|
|
|
$class = static::class;
|
2016-11-29 00:31:16 +01:00
|
|
|
user_error(
|
2017-05-17 07:40:13 +02:00
|
|
|
"init() method on class '{$class}' doesn't call Controller::init()."
|
2016-11-29 00:31:16 +01:00
|
|
|
. "Make sure that you have parent::init() included.",
|
|
|
|
E_USER_WARNING
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->extend('onAfterInit');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*
|
|
|
|
* Also set the URLParams
|
|
|
|
*/
|
|
|
|
public function setRequest($request)
|
|
|
|
{
|
|
|
|
$return = parent::setRequest($request);
|
|
|
|
$this->setURLParams($this->getRequest()->allParams());
|
|
|
|
|
|
|
|
return $return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A bootstrap for the handleRequest method
|
|
|
|
*
|
|
|
|
* @todo setDataModel and setRequest are redundantly called in parent::handleRequest() - sort this out
|
|
|
|
*
|
|
|
|
* @param HTTPRequest $request
|
|
|
|
*/
|
2017-06-22 12:50:45 +02:00
|
|
|
protected function beforeHandleRequest(HTTPRequest $request)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
2017-06-22 12:50:45 +02:00
|
|
|
//Set up the internal dependencies (request, response)
|
|
|
|
$this->setRequest($request);
|
2016-11-29 00:31:16 +01:00
|
|
|
//Push the current controller to protect against weird session issues
|
|
|
|
$this->pushCurrent();
|
|
|
|
$this->setResponse(new HTTPResponse());
|
|
|
|
//kick off the init functionality
|
|
|
|
$this->doInit();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cleanup for the handleRequest method
|
|
|
|
*/
|
|
|
|
protected function afterHandleRequest()
|
|
|
|
{
|
|
|
|
//Pop the current controller from the stack
|
|
|
|
$this->popCurrent();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Executes this controller, and return an {@link HTTPResponse} object with the result.
|
|
|
|
*
|
|
|
|
* This method defers to {@link RequestHandler->handleRequest()} to determine which action
|
|
|
|
* should be executed
|
|
|
|
*
|
|
|
|
* Note: You should rarely need to overload handleRequest() -
|
|
|
|
* this kind of change is only really appropriate for things like nested
|
|
|
|
* controllers - {@link ModelAsController} and {@link RootURLController}
|
|
|
|
* are two examples here. If you want to make more
|
|
|
|
* orthodox functionality, it's better to overload {@link init()} or {@link index()}.
|
|
|
|
*
|
|
|
|
* Important: If you are going to overload handleRequest,
|
|
|
|
* make sure that you start the method with $this->beforeHandleRequest()
|
|
|
|
* and end the method with $this->afterHandleRequest()
|
|
|
|
*
|
|
|
|
* @param HTTPRequest $request
|
|
|
|
* @return HTTPResponse
|
|
|
|
*/
|
2017-06-22 12:50:45 +02:00
|
|
|
public function handleRequest(HTTPRequest $request)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
|
|
|
if (!$request) {
|
|
|
|
user_error("Controller::handleRequest() not passed a request!", E_USER_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
//set up the controller for the incoming request
|
2017-06-22 12:50:45 +02:00
|
|
|
$this->beforeHandleRequest($request);
|
2016-11-29 00:31:16 +01:00
|
|
|
|
|
|
|
//if the before handler manipulated the response in a way that we shouldn't proceed, then skip our request
|
|
|
|
// handling
|
|
|
|
if (!$this->getResponse()->isFinished()) {
|
|
|
|
//retrieve the response for the request
|
2017-06-22 12:50:45 +02:00
|
|
|
$response = parent::handleRequest($request);
|
2016-11-29 00:31:16 +01:00
|
|
|
|
|
|
|
//prepare the response (we can receive an assortment of response types (strings/objects/HTTPResponses)
|
|
|
|
$this->prepareResponse($response);
|
|
|
|
}
|
|
|
|
|
|
|
|
//after request work
|
|
|
|
$this->afterHandleRequest();
|
|
|
|
|
|
|
|
//return the response
|
|
|
|
return $this->getResponse();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepare the response (we can receive an assortment of response types (strings/objects/HTTPResponses) and
|
|
|
|
* changes the controller response object appropriately
|
|
|
|
*
|
|
|
|
* @param HTTPResponse|Object $response
|
|
|
|
*/
|
|
|
|
protected function prepareResponse($response)
|
|
|
|
{
|
|
|
|
if ($response instanceof HTTPResponse) {
|
|
|
|
if (isset($_REQUEST['debug_request'])) {
|
2017-05-17 07:40:13 +02:00
|
|
|
$class = static::class;
|
2016-11-29 00:31:16 +01:00
|
|
|
Debug::message(
|
2017-05-17 07:40:13 +02:00
|
|
|
"Request handler returned HTTPResponse object to {$class} controller;"
|
2016-11-29 00:31:16 +01:00
|
|
|
. "returning it without modification."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
$this->setResponse($response);
|
|
|
|
} else {
|
2017-05-17 07:40:13 +02:00
|
|
|
// Could be Controller, or ViewableData_Customised controller wrapper
|
|
|
|
if (ClassInfo::hasMethod($response, 'getViewer')) {
|
2016-11-29 00:31:16 +01:00
|
|
|
if (isset($_REQUEST['debug_request'])) {
|
2017-05-17 07:40:13 +02:00
|
|
|
$class = static::class;
|
|
|
|
$responseClass = get_class($response);
|
2016-11-29 00:31:16 +01:00
|
|
|
Debug::message(
|
2017-05-17 07:40:13 +02:00
|
|
|
"Request handler {$responseClass} object to {$class} controller;"
|
|
|
|
. "rendering with template returned by {$responseClass}::getViewer()"
|
2016-11-29 00:31:16 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
$response = $response->getViewer($this->getAction())->process($response);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->getResponse()->setBody($response);
|
|
|
|
}
|
|
|
|
|
|
|
|
//deal with content if appropriate
|
|
|
|
ContentNegotiator::process($this->getResponse());
|
|
|
|
|
|
|
|
//add cache headers
|
|
|
|
HTTP::add_cache_headers($this->getResponse());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Controller's default action handler. It will call the method named in "$Action", if that method
|
|
|
|
* exists. If "$Action" isn't given, it will use "index" as a default.
|
|
|
|
*
|
|
|
|
* @param HTTPRequest $request
|
|
|
|
* @param string $action
|
|
|
|
*
|
|
|
|
* @return DBHTMLText|HTTPResponse
|
|
|
|
*/
|
|
|
|
protected function handleAction($request, $action)
|
|
|
|
{
|
|
|
|
foreach ($request->latestParams() as $k => $v) {
|
|
|
|
if ($v || !isset($this->urlParams[$k])) {
|
|
|
|
$this->urlParams[$k] = $v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->action = $action;
|
|
|
|
$this->requestParams = $request->requestVars();
|
|
|
|
|
|
|
|
if ($this->hasMethod($action)) {
|
|
|
|
$result = parent::handleAction($request, $action);
|
|
|
|
|
|
|
|
// If the action returns an array, customise with it before rendering the template.
|
|
|
|
if (is_array($result)) {
|
|
|
|
return $this->getViewer($action)->process($this->customise($result));
|
|
|
|
} else {
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall back to index action with before/after handlers
|
|
|
|
$beforeResult = $this->extend('beforeCallActionHandler', $request, $action);
|
|
|
|
if ($beforeResult) {
|
|
|
|
return reset($beforeResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
$result = $this->getViewer($action)->process($this);
|
|
|
|
|
|
|
|
$afterResult = $this->extend('afterCallActionHandler', $request, $action, $result);
|
|
|
|
if ($afterResult) {
|
|
|
|
return reset($afterResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $urlParams
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setURLParams($urlParams)
|
|
|
|
{
|
|
|
|
$this->urlParams = $urlParams;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the parameters extracted from the URL by the {@link Director}.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getURLParams()
|
|
|
|
{
|
|
|
|
return $this->urlParams;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the HTTPResponse object that this controller is building up. Can be used to set the
|
|
|
|
* status code and headers.
|
|
|
|
*
|
|
|
|
* @return HTTPResponse
|
|
|
|
*/
|
|
|
|
public function getResponse()
|
|
|
|
{
|
|
|
|
if (!$this->response) {
|
|
|
|
$this->setResponse(new HTTPResponse());
|
|
|
|
}
|
|
|
|
return $this->response;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the HTTPResponse object that this controller is building up.
|
|
|
|
*
|
|
|
|
* @param HTTPResponse $response
|
|
|
|
*
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setResponse(HTTPResponse $response)
|
|
|
|
{
|
|
|
|
$this->response = $response;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected $baseInitCalled = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the default action handler used if a method doesn't exist. It will process the
|
|
|
|
* controller object with the template returned by {@link getViewer()}.
|
|
|
|
*
|
|
|
|
* @param string $action
|
|
|
|
* @return DBHTMLText
|
|
|
|
*/
|
|
|
|
public function defaultAction($action)
|
|
|
|
{
|
|
|
|
return $this->getViewer($action)->process($this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the action that is being executed on this controller.
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getAction()
|
|
|
|
{
|
|
|
|
return $this->action;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the viewer identified being the default handler for this Controller/Action combination.
|
|
|
|
*
|
|
|
|
* @param string $action
|
|
|
|
*
|
|
|
|
* @return SSViewer
|
|
|
|
*/
|
|
|
|
public function getViewer($action)
|
|
|
|
{
|
|
|
|
// Hard-coded templates
|
|
|
|
if (isset($this->templates[$action]) && $this->templates[$action]) {
|
|
|
|
$templates = $this->templates[$action];
|
|
|
|
} elseif (isset($this->templates['index']) && $this->templates['index']) {
|
|
|
|
$templates = $this->templates['index'];
|
|
|
|
} elseif ($this->template) {
|
|
|
|
$templates = $this->template;
|
|
|
|
} else {
|
2017-05-23 01:36:15 +02:00
|
|
|
// Build templates based on class hierarchy
|
|
|
|
$actionTemplates = [];
|
|
|
|
$classTemplates = [];
|
2017-05-17 07:40:13 +02:00
|
|
|
$parentClass = static::class;
|
2017-05-23 01:36:15 +02:00
|
|
|
while ($parentClass !== parent::class) {
|
|
|
|
// _action templates have higher priority
|
|
|
|
if ($action && $action != 'index') {
|
|
|
|
$actionTemplates[] = strtok($parentClass, '_') . '_' . $action;
|
|
|
|
}
|
|
|
|
// class templates have lower priority
|
|
|
|
$classTemplates[] = strtok($parentClass, '_');
|
2016-11-29 00:31:16 +01:00
|
|
|
$parentClass = get_parent_class($parentClass);
|
|
|
|
}
|
|
|
|
|
2017-05-23 01:36:15 +02:00
|
|
|
// Add controller templates for inheritance chain
|
|
|
|
$templates = array_unique(array_merge($actionTemplates, $classTemplates));
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return new SSViewer($templates);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $action
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function hasAction($action)
|
|
|
|
{
|
|
|
|
return parent::hasAction($action) || $this->hasActionTemplate($action);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes all the "action" part of the current URL and returns the result. If no action parameter
|
|
|
|
* is present, returns the full URL.
|
|
|
|
*
|
|
|
|
* @param string $fullURL
|
|
|
|
* @param null|string $action
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function removeAction($fullURL, $action = null)
|
|
|
|
{
|
|
|
|
if (!$action) {
|
|
|
|
$action = $this->getAction(); //default to current action
|
|
|
|
}
|
|
|
|
$returnURL = $fullURL;
|
|
|
|
|
|
|
|
if (($pos = strpos($fullURL, $action)) !== false) {
|
|
|
|
$returnURL = substr($fullURL, 0, $pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $returnURL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the class that defines the given action, so that we know where to check allowed_actions.
|
|
|
|
* Overrides RequestHandler to also look at defined templates.
|
|
|
|
*
|
|
|
|
* @param string $action
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function definingClassForAction($action)
|
|
|
|
{
|
|
|
|
$definingClass = parent::definingClassForAction($action);
|
|
|
|
if ($definingClass) {
|
|
|
|
return $definingClass;
|
|
|
|
}
|
|
|
|
|
2017-05-17 07:40:13 +02:00
|
|
|
$class = static::class;
|
2016-11-29 00:31:16 +01:00
|
|
|
while ($class != 'SilverStripe\\Control\\RequestHandler') {
|
|
|
|
$templateName = strtok($class, '_') . '_' . $action;
|
|
|
|
if (SSViewer::hasTemplate($templateName)) {
|
|
|
|
return $class;
|
|
|
|
}
|
|
|
|
|
|
|
|
$class = get_parent_class($class);
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns TRUE if this controller has a template that is specifically designed to handle a
|
|
|
|
* specific action.
|
|
|
|
*
|
|
|
|
* @param string $action
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function hasActionTemplate($action)
|
|
|
|
{
|
|
|
|
if (isset($this->templates[$action])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-17 07:40:13 +02:00
|
|
|
$parentClass = static::class;
|
2016-11-29 00:31:16 +01:00
|
|
|
$templates = array();
|
|
|
|
|
|
|
|
while ($parentClass != __CLASS__) {
|
|
|
|
$templates[] = strtok($parentClass, '_') . '_' . $action;
|
|
|
|
$parentClass = get_parent_class($parentClass);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SSViewer::hasTemplate($templates);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Render the current controller with the templates determined by {@link getViewer()}.
|
|
|
|
*
|
|
|
|
* @param array $params
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function render($params = null)
|
|
|
|
{
|
|
|
|
$template = $this->getViewer($this->getAction());
|
|
|
|
|
|
|
|
// if the object is already customised (e.g. through Controller->run()), use it
|
2017-06-22 12:50:45 +02:00
|
|
|
$obj = $this->getCustomisedObj() ?: $this;
|
2016-11-29 00:31:16 +01:00
|
|
|
|
|
|
|
if ($params) {
|
|
|
|
$obj = $this->customise($params);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $template->process($obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call this to disable site-wide basic authentication for a specific controller. This must be
|
|
|
|
* called before Controller::init(). That is, you must call it in your controller's init method
|
|
|
|
* before it calls parent::init().
|
|
|
|
*/
|
|
|
|
public function disableBasicAuth()
|
|
|
|
{
|
|
|
|
$this->basicAuthEnabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the current controller.
|
|
|
|
*
|
|
|
|
* @return Controller
|
|
|
|
*/
|
|
|
|
public static function curr()
|
|
|
|
{
|
|
|
|
if (Controller::$controller_stack) {
|
|
|
|
return Controller::$controller_stack[0];
|
|
|
|
}
|
|
|
|
user_error("No current controller available", E_USER_WARNING);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tests whether we have a currently active controller or not. True if there is at least 1
|
|
|
|
* controller in the stack.
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function has_curr()
|
|
|
|
{
|
|
|
|
return Controller::$controller_stack ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the member is allowed to do the given action. Defaults to the currently logged
|
|
|
|
* in user.
|
|
|
|
*
|
|
|
|
* @param string $perm
|
|
|
|
* @param null|member $member
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function can($perm, $member = null)
|
|
|
|
{
|
|
|
|
if (!$member) {
|
2017-05-20 06:32:25 +02:00
|
|
|
$member = Security::getCurrentUser();
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
if (is_array($perm)) {
|
|
|
|
$perm = array_map(array($this, 'can'), $perm, array_fill(0, count($perm), $member));
|
|
|
|
return min($perm);
|
|
|
|
}
|
|
|
|
if ($this->hasMethod($methodName = 'can' . $perm)) {
|
|
|
|
return $this->$methodName($member);
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pushes this controller onto the stack of current controllers. This means that any redirection,
|
|
|
|
* session setting, or other things that rely on Controller::curr() will now write to this
|
|
|
|
* controller object.
|
2017-06-22 12:50:45 +02:00
|
|
|
*
|
|
|
|
* Note: Ensure this controller is assigned a request with a valid session before pushing
|
|
|
|
* it to the stack.
|
2016-11-29 00:31:16 +01:00
|
|
|
*/
|
|
|
|
public function pushCurrent()
|
|
|
|
{
|
2017-06-22 12:50:45 +02:00
|
|
|
// Ensure this controller has a valid session
|
|
|
|
$this->getRequest()->getSession();
|
2016-11-29 00:31:16 +01:00
|
|
|
array_unshift(self::$controller_stack, $this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pop this controller off the top of the stack.
|
|
|
|
*/
|
|
|
|
public function popCurrent()
|
|
|
|
{
|
|
|
|
if ($this === self::$controller_stack[0]) {
|
|
|
|
array_shift(self::$controller_stack);
|
|
|
|
} else {
|
2017-05-17 07:40:13 +02:00
|
|
|
$class = static::class;
|
2016-11-29 00:31:16 +01:00
|
|
|
user_error(
|
2017-05-17 07:40:13 +02:00
|
|
|
"popCurrent called on {$class} controller, but it wasn't at the top of the stack",
|
2016-11-29 00:31:16 +01:00
|
|
|
E_USER_WARNING
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Redirect to the given URL.
|
|
|
|
*
|
|
|
|
* @param string $url
|
|
|
|
* @param int $code
|
|
|
|
* @return HTTPResponse
|
|
|
|
*/
|
|
|
|
public function redirect($url, $code = 302)
|
|
|
|
{
|
|
|
|
if ($this->getResponse()->getHeader('Location') && $this->getResponse()->getHeader('Location') != $url) {
|
|
|
|
user_error("Already directed to " . $this->getResponse()->getHeader('Location')
|
|
|
|
. "; now trying to direct to $url", E_USER_WARNING);
|
|
|
|
return null;
|
|
|
|
}
|
2017-03-02 03:24:38 +01:00
|
|
|
$response = parent::redirect($url, $code);
|
|
|
|
$this->setResponse($response);
|
|
|
|
return $response;
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tests whether a redirection has been requested. If redirect() has been called, it will return
|
|
|
|
* the URL redirected to. Otherwise, it will return null.
|
|
|
|
*
|
|
|
|
* @return null|string
|
|
|
|
*/
|
|
|
|
public function redirectedTo()
|
|
|
|
{
|
|
|
|
return $this->getResponse() && $this->getResponse()->getHeader('Location');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Joins two or more link segments together, putting a slash between them if necessary. Use this
|
|
|
|
* for building the results of {@link Link()} methods. If either of the links have query strings,
|
|
|
|
* then they will be combined and put at the end of the resulting url.
|
|
|
|
*
|
|
|
|
* Caution: All parameters are expected to be URI-encoded already.
|
|
|
|
*
|
|
|
|
* @param string|array $arg,.. One or more link segments, or list of link segments as an array
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public static function join_links($arg = null)
|
|
|
|
{
|
|
|
|
if (func_num_args() === 1 && is_array($arg)) {
|
|
|
|
$args = $arg;
|
|
|
|
} else {
|
|
|
|
$args = func_get_args();
|
|
|
|
}
|
|
|
|
$result = "";
|
|
|
|
$queryargs = array();
|
|
|
|
$fragmentIdentifier = null;
|
|
|
|
|
|
|
|
foreach ($args as $arg) {
|
|
|
|
// Find fragment identifier - keep the last one
|
|
|
|
if (strpos($arg, '#') !== false) {
|
|
|
|
list($arg, $fragmentIdentifier) = explode('#', $arg, 2);
|
|
|
|
}
|
|
|
|
// Find querystrings
|
|
|
|
if (strpos($arg, '?') !== false) {
|
|
|
|
list($arg, $suffix) = explode('?', $arg, 2);
|
|
|
|
parse_str($suffix, $localargs);
|
|
|
|
$queryargs = array_merge($queryargs, $localargs);
|
|
|
|
}
|
|
|
|
if ((is_string($arg) && $arg) || is_numeric($arg)) {
|
|
|
|
$arg = (string) $arg;
|
|
|
|
if ($result && substr($result, -1) != '/' && $arg[0] != '/') {
|
|
|
|
$result .= "/$arg";
|
|
|
|
} else {
|
|
|
|
$result .= (substr($result, -1) == '/' && $arg[0] == '/') ? ltrim($arg, '/') : $arg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($queryargs) {
|
|
|
|
$result .= '?' . http_build_query($queryargs);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($fragmentIdentifier) {
|
|
|
|
$result .= "#$fragmentIdentifier";
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function get_template_global_variables()
|
|
|
|
{
|
|
|
|
return array(
|
|
|
|
'CurrentPage' => 'curr',
|
|
|
|
);
|
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|