| Total Complexity | 90 | 
| Total Lines | 698 | 
| Duplicated Lines | 0 % | 
| Changes | 0 | ||
Complex classes like Controller often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Controller, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 23 | class Controller extends RequestHandler implements TemplateGlobalProvider  | 
            ||
| 24 | { | 
            ||
| 25 | |||
| 26 | /**  | 
            ||
| 27 | * An array of arguments extracted from the URL.  | 
            ||
| 28 | *  | 
            ||
| 29 | * @var array  | 
            ||
| 30 | */  | 
            ||
| 31 | protected $urlParams;  | 
            ||
| 32 | |||
| 33 | /**  | 
            ||
| 34 |      * Contains all GET and POST parameters passed to the current {@link HTTPRequest}. | 
            ||
| 35 | *  | 
            ||
| 36 | * @var array  | 
            ||
| 37 | */  | 
            ||
| 38 | protected $requestParams;  | 
            ||
| 39 | |||
| 40 | /**  | 
            ||
| 41 | * The URL part matched on the current controller as determined by the "$Action" part of the  | 
            ||
| 42 |      * {@link $url_handlers} definition. Should correlate to a public method on this controller. | 
            ||
| 43 | *  | 
            ||
| 44 |      * Used in {@link render()} and {@link getViewer()} to determine action-specific templates. | 
            ||
| 45 | *  | 
            ||
| 46 | * @var string  | 
            ||
| 47 | */  | 
            ||
| 48 | protected $action;  | 
            ||
| 49 | |||
| 50 | /**  | 
            ||
| 51 | * Stack of current controllers. Controller::$controller_stack[0] is the current controller.  | 
            ||
| 52 | *  | 
            ||
| 53 | * @var array  | 
            ||
| 54 | */  | 
            ||
| 55 | protected static $controller_stack = [];  | 
            ||
| 56 | |||
| 57 | /**  | 
            ||
| 58 | * Assign templates for this controller.  | 
            ||
| 59 | * Map of action => template name  | 
            ||
| 60 | *  | 
            ||
| 61 | * @var array  | 
            ||
| 62 | */  | 
            ||
| 63 | protected $templates = [];  | 
            ||
| 64 | |||
| 65 | /**  | 
            ||
| 66 | * @deprecated 4.1.0:5.0.0 Add this controller's url to  | 
            ||
| 67 | * SilverStripe\Security\BasicAuthMiddleware.URLPatterns injected property instead of setting false  | 
            ||
| 68 | * @var bool  | 
            ||
| 69 | */  | 
            ||
| 70 | protected $basicAuthEnabled = true;  | 
            ||
| 71 | |||
| 72 | /**  | 
            ||
| 73 | * The response object that the controller returns.  | 
            ||
| 74 | *  | 
            ||
| 75 |      * Set in {@link handleRequest()}. | 
            ||
| 76 | *  | 
            ||
| 77 | * @var HTTPResponse  | 
            ||
| 78 | */  | 
            ||
| 79 | protected $response;  | 
            ||
| 80 | |||
| 81 | /**  | 
            ||
| 82 | * Default URL handlers.  | 
            ||
| 83 | *  | 
            ||
| 84 | * @var array  | 
            ||
| 85 | */  | 
            ||
| 86 | private static $url_handlers = [  | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 87 | '$Action//$ID/$OtherID' => 'handleAction',  | 
            ||
| 88 | ];  | 
            ||
| 89 | |||
| 90 | /**  | 
            ||
| 91 | * @var array  | 
            ||
| 92 | */  | 
            ||
| 93 | private static $allowed_actions = [  | 
            ||
| 94 | 'handleAction',  | 
            ||
| 95 | 'handleIndex',  | 
            ||
| 96 | ];  | 
            ||
| 97 | |||
| 98 | /**  | 
            ||
| 99 | * Initialisation function that is run before any action on the controller is called.  | 
            ||
| 100 | *  | 
            ||
| 101 | * @uses BasicAuth::requireLogin()  | 
            ||
| 102 | */  | 
            ||
| 103 | protected function init()  | 
            ||
| 104 |     { | 
            ||
| 105 | // @todo This will be removed in 5.0 and will be controlled by middleware instead  | 
            ||
| 106 |         if ($this->basicAuthEnabled) { | 
            ||
| 107 | BasicAuth::protect_site_if_necessary();  | 
            ||
| 108 | }  | 
            ||
| 109 | |||
| 110 | // This is used to test that subordinate controllers are actually calling parent::init() - a common bug  | 
            ||
| 111 | $this->baseInitCalled = true;  | 
            ||
| 112 | }  | 
            ||
| 113 | |||
| 114 | /**  | 
            ||
| 115 | * A stand in function to protect the init function from failing to be called as well as providing before and  | 
            ||
| 116 | * after hooks for the init function itself  | 
            ||
| 117 | *  | 
            ||
| 118 | * This should be called on all controllers before handling requests  | 
            ||
| 119 | */  | 
            ||
| 120 | public function doInit()  | 
            ||
| 121 |     { | 
            ||
| 122 | //extension hook  | 
            ||
| 123 |         $this->extend('onBeforeInit'); | 
            ||
| 124 | |||
| 125 | // Safety call  | 
            ||
| 126 | $this->baseInitCalled = false;  | 
            ||
| 127 | $this->init();  | 
            ||
| 128 |         if (!$this->baseInitCalled) { | 
            ||
| 129 | $class = static::class;  | 
            ||
| 130 | user_error(  | 
            ||
| 131 |                 "init() method on class '{$class}' doesn't call Controller::init()." | 
            ||
| 132 | . "Make sure that you have parent::init() included.",  | 
            ||
| 133 | E_USER_WARNING  | 
            ||
| 134 | );  | 
            ||
| 135 | }  | 
            ||
| 136 | |||
| 137 |         $this->extend('onAfterInit'); | 
            ||
| 138 | }  | 
            ||
| 139 | |||
| 140 | /**  | 
            ||
| 141 |      * {@inheritdoc} | 
            ||
| 142 | *  | 
            ||
| 143 | * Also set the URLParams  | 
            ||
| 144 | */  | 
            ||
| 145 | public function setRequest($request)  | 
            ||
| 146 |     { | 
            ||
| 147 | $return = parent::setRequest($request);  | 
            ||
| 148 | $this->setURLParams($this->getRequest()->allParams());  | 
            ||
| 149 | |||
| 150 | return $return;  | 
            ||
| 151 | }  | 
            ||
| 152 | |||
| 153 | /**  | 
            ||
| 154 | * A bootstrap for the handleRequest method  | 
            ||
| 155 | *  | 
            ||
| 156 | * @todo setDataModel and setRequest are redundantly called in parent::handleRequest() - sort this out  | 
            ||
| 157 | *  | 
            ||
| 158 | * @param HTTPRequest $request  | 
            ||
| 159 | */  | 
            ||
| 160 | protected function beforeHandleRequest(HTTPRequest $request)  | 
            ||
| 161 |     { | 
            ||
| 162 | //Set up the internal dependencies (request, response)  | 
            ||
| 163 | $this->setRequest($request);  | 
            ||
| 164 | //Push the current controller to protect against weird session issues  | 
            ||
| 165 | $this->pushCurrent();  | 
            ||
| 166 | $this->setResponse(new HTTPResponse());  | 
            ||
| 167 | //kick off the init functionality  | 
            ||
| 168 | $this->doInit();  | 
            ||
| 169 | }  | 
            ||
| 170 | |||
| 171 | /**  | 
            ||
| 172 | * Cleanup for the handleRequest method  | 
            ||
| 173 | */  | 
            ||
| 174 | protected function afterHandleRequest()  | 
            ||
| 175 |     { | 
            ||
| 176 | //Pop the current controller from the stack  | 
            ||
| 177 | $this->popCurrent();  | 
            ||
| 178 | }  | 
            ||
| 179 | |||
| 180 | /**  | 
            ||
| 181 |      * Executes this controller, and return an {@link HTTPResponse} object with the result. | 
            ||
| 182 | *  | 
            ||
| 183 |      * This method defers to {@link RequestHandler->handleRequest()} to determine which action | 
            ||
| 184 | * should be executed  | 
            ||
| 185 | *  | 
            ||
| 186 | * Note: You should rarely need to overload handleRequest() -  | 
            ||
| 187 | * this kind of change is only really appropriate for things like nested  | 
            ||
| 188 |      * controllers - {@link ModelAsController} and {@link RootURLController} | 
            ||
| 189 | * are two examples here. If you want to make more  | 
            ||
| 190 |      * orthodox functionality, it's better to overload {@link init()} or {@link index()}. | 
            ||
| 191 | *  | 
            ||
| 192 | * Important: If you are going to overload handleRequest,  | 
            ||
| 193 | * make sure that you start the method with $this->beforeHandleRequest()  | 
            ||
| 194 | * and end the method with $this->afterHandleRequest()  | 
            ||
| 195 | *  | 
            ||
| 196 | * @param HTTPRequest $request  | 
            ||
| 197 | * @return HTTPResponse  | 
            ||
| 198 | */  | 
            ||
| 199 | public function handleRequest(HTTPRequest $request)  | 
            ||
| 200 |     { | 
            ||
| 201 |         if (!$request) { | 
            ||
| 202 |             throw new \RuntimeException('Controller::handleRequest() not passed a request!'); | 
            ||
| 203 | }  | 
            ||
| 204 | |||
| 205 | //set up the controller for the incoming request  | 
            ||
| 206 | $this->beforeHandleRequest($request);  | 
            ||
| 207 | |||
| 208 | //if the before handler manipulated the response in a way that we shouldn't proceed, then skip our request  | 
            ||
| 209 | // handling  | 
            ||
| 210 |         if (!$this->getResponse()->isFinished()) { | 
            ||
| 211 | //retrieve the response for the request  | 
            ||
| 212 | $response = parent::handleRequest($request);  | 
            ||
| 213 | |||
| 214 | //prepare the response (we can receive an assortment of response types (strings/objects/HTTPResponses)  | 
            ||
| 215 | $this->prepareResponse($response);  | 
            ||
| 216 | }  | 
            ||
| 217 | |||
| 218 | //after request work  | 
            ||
| 219 | $this->afterHandleRequest();  | 
            ||
| 220 | |||
| 221 | //return the response  | 
            ||
| 222 | return $this->getResponse();  | 
            ||
| 223 | }  | 
            ||
| 224 | |||
| 225 | /**  | 
            ||
| 226 | * Prepare the response (we can receive an assortment of response types (strings/objects/HTTPResponses) and  | 
            ||
| 227 | * changes the controller response object appropriately  | 
            ||
| 228 | *  | 
            ||
| 229 | * @param HTTPResponse|Object $response  | 
            ||
| 230 | */  | 
            ||
| 231 | protected function prepareResponse($response)  | 
            ||
| 232 |     { | 
            ||
| 233 |         if (!is_object($response)) { | 
            ||
| 234 | $this->getResponse()->setBody($response);  | 
            ||
| 235 |         } elseif ($response instanceof HTTPResponse) { | 
            ||
| 236 |             if (isset($_REQUEST['debug_request'])) { | 
            ||
| 237 | $class = static::class;  | 
            ||
| 238 | Debug::message(  | 
            ||
| 239 |                     "Request handler returned HTTPResponse object to {$class} controller;" | 
            ||
| 240 | . "returning it without modification."  | 
            ||
| 241 | );  | 
            ||
| 242 | }  | 
            ||
| 243 | $this->setResponse($response);  | 
            ||
| 244 |         } else { | 
            ||
| 245 | // Could be Controller, or ViewableData_Customised controller wrapper  | 
            ||
| 246 |             if (ClassInfo::hasMethod($response, 'getViewer')) { | 
            ||
| 247 |                 if (isset($_REQUEST['debug_request'])) { | 
            ||
| 248 | $class = static::class;  | 
            ||
| 249 | $responseClass = get_class($response);  | 
            ||
| 250 | Debug::message(  | 
            ||
| 251 |                         "Request handler {$responseClass} object to {$class} controller;" | 
            ||
| 252 |                         . "rendering with template returned by {$responseClass}::getViewer()" | 
            ||
| 253 | );  | 
            ||
| 254 | }  | 
            ||
| 255 | $response = $response->getViewer($this->getAction())->process($response);  | 
            ||
| 256 | }  | 
            ||
| 257 | |||
| 258 | $this->getResponse()->setBody($response);  | 
            ||
| 259 | }  | 
            ||
| 260 | |||
| 261 | //deal with content if appropriate  | 
            ||
| 262 | ContentNegotiator::process($this->getResponse());  | 
            ||
| 263 | }  | 
            ||
| 264 | |||
| 265 | /**  | 
            ||
| 266 | * Controller's default action handler. It will call the method named in "$Action", if that method  | 
            ||
| 267 | * exists. If "$Action" isn't given, it will use "index" as a default.  | 
            ||
| 268 | *  | 
            ||
| 269 | * @param HTTPRequest $request  | 
            ||
| 270 | * @param string $action  | 
            ||
| 271 | *  | 
            ||
| 272 | * @return DBHTMLText|HTTPResponse  | 
            ||
| 273 | */  | 
            ||
| 274 | protected function handleAction($request, $action)  | 
            ||
| 275 |     { | 
            ||
| 276 |         foreach ($request->latestParams() as $k => $v) { | 
            ||
| 277 |             if ($v || !isset($this->urlParams[$k])) { | 
            ||
| 278 | $this->urlParams[$k] = $v;  | 
            ||
| 279 | }  | 
            ||
| 280 | }  | 
            ||
| 281 | |||
| 282 | $this->action = $action;  | 
            ||
| 283 | $this->requestParams = $request->requestVars();  | 
            ||
| 284 | |||
| 285 |         if ($this->hasMethod($action)) { | 
            ||
| 286 | $result = parent::handleAction($request, $action);  | 
            ||
| 287 | |||
| 288 | // If the action returns an array, customise with it before rendering the template.  | 
            ||
| 289 |             if (is_array($result)) { | 
            ||
| 290 | return $this->getViewer($action)->process($this->customise($result));  | 
            ||
| 291 |             } else { | 
            ||
| 292 | return $result;  | 
            ||
| 293 | }  | 
            ||
| 294 | }  | 
            ||
| 295 | |||
| 296 | // Fall back to index action with before/after handlers  | 
            ||
| 297 |         $beforeResult = $this->extend('beforeCallActionHandler', $request, $action); | 
            ||
| 298 |         if ($beforeResult) { | 
            ||
| 299 | return reset($beforeResult);  | 
            ||
| 300 | }  | 
            ||
| 301 | |||
| 302 | $result = $this->getViewer($action)->process($this);  | 
            ||
| 303 | |||
| 304 |         $afterResult = $this->extend('afterCallActionHandler', $request, $action, $result); | 
            ||
| 305 |         if ($afterResult) { | 
            ||
| 306 | return reset($afterResult);  | 
            ||
| 307 | }  | 
            ||
| 308 | |||
| 309 | return $result;  | 
            ||
| 310 | }  | 
            ||
| 311 | |||
| 312 | /**  | 
            ||
| 313 | * @param array $urlParams  | 
            ||
| 314 | * @return $this  | 
            ||
| 315 | */  | 
            ||
| 316 | public function setURLParams($urlParams)  | 
            ||
| 317 |     { | 
            ||
| 318 | $this->urlParams = $urlParams;  | 
            ||
| 319 | return $this;  | 
            ||
| 320 | }  | 
            ||
| 321 | |||
| 322 | /**  | 
            ||
| 323 |      * Returns the parameters extracted from the URL by the {@link Director}. | 
            ||
| 324 | *  | 
            ||
| 325 | * @return array  | 
            ||
| 326 | */  | 
            ||
| 327 | public function getURLParams()  | 
            ||
| 328 |     { | 
            ||
| 329 | return $this->urlParams;  | 
            ||
| 330 | }  | 
            ||
| 331 | |||
| 332 | /**  | 
            ||
| 333 | * Returns the HTTPResponse object that this controller is building up. Can be used to set the  | 
            ||
| 334 | * status code and headers.  | 
            ||
| 335 | *  | 
            ||
| 336 | * @return HTTPResponse  | 
            ||
| 337 | */  | 
            ||
| 338 | public function getResponse()  | 
            ||
| 339 |     { | 
            ||
| 340 |         if (!$this->response) { | 
            ||
| 341 | $this->setResponse(new HTTPResponse());  | 
            ||
| 342 | }  | 
            ||
| 343 | return $this->response;  | 
            ||
| 344 | }  | 
            ||
| 345 | |||
| 346 | /**  | 
            ||
| 347 | * Sets the HTTPResponse object that this controller is building up.  | 
            ||
| 348 | *  | 
            ||
| 349 | * @param HTTPResponse $response  | 
            ||
| 350 | *  | 
            ||
| 351 | * @return $this  | 
            ||
| 352 | */  | 
            ||
| 353 | public function setResponse(HTTPResponse $response)  | 
            ||
| 357 | }  | 
            ||
| 358 | |||
| 359 | /**  | 
            ||
| 360 | * @var bool  | 
            ||
| 361 | */  | 
            ||
| 362 | protected $baseInitCalled = false;  | 
            ||
| 363 | |||
| 364 | /**  | 
            ||
| 365 | * This is the default action handler used if a method doesn't exist. It will process the  | 
            ||
| 366 |      * controller object with the template returned by {@link getViewer()}. | 
            ||
| 367 | *  | 
            ||
| 368 | * @param string $action  | 
            ||
| 369 | * @return DBHTMLText  | 
            ||
| 370 | */  | 
            ||
| 371 | public function defaultAction($action)  | 
            ||
| 374 | }  | 
            ||
| 375 | |||
| 376 | /**  | 
            ||
| 377 | * Returns the action that is being executed on this controller.  | 
            ||
| 378 | *  | 
            ||
| 379 | * @return string  | 
            ||
| 380 | */  | 
            ||
| 381 | public function getAction()  | 
            ||
| 382 |     { | 
            ||
| 383 | return $this->action;  | 
            ||
| 384 | }  | 
            ||
| 385 | |||
| 386 | /**  | 
            ||
| 387 | * Return the viewer identified being the default handler for this Controller/Action combination.  | 
            ||
| 388 | *  | 
            ||
| 389 | * @param string $action  | 
            ||
| 390 | *  | 
            ||
| 391 | * @return SSViewer  | 
            ||
| 392 | */  | 
            ||
| 393 | public function getViewer($action)  | 
            ||
| 394 |     { | 
            ||
| 395 | // Hard-coded templates  | 
            ||
| 396 |         if (isset($this->templates[$action]) && $this->templates[$action]) { | 
            ||
| 397 | $templates = $this->templates[$action];  | 
            ||
| 398 |         } elseif (isset($this->templates['index']) && $this->templates['index']) { | 
            ||
| 399 | $templates = $this->templates['index'];  | 
            ||
| 400 |         } elseif ($this->template) { | 
            ||
| 401 | $templates = $this->template;  | 
            ||
| 402 |         } else { | 
            ||
| 403 | // Build templates based on class hierarchy  | 
            ||
| 404 | $actionTemplates = [];  | 
            ||
| 405 | $classTemplates = [];  | 
            ||
| 406 | $parentClass = static::class;  | 
            ||
| 407 |             while ($parentClass !== parent::class) { | 
            ||
| 408 | // _action templates have higher priority  | 
            ||
| 409 |                 if ($action && $action != 'index') { | 
            ||
| 410 | $actionTemplates[] = strtok($parentClass, '_') . '_' . $action;  | 
            ||
| 411 | }  | 
            ||
| 412 | // class templates have lower priority  | 
            ||
| 413 | $classTemplates[] = strtok($parentClass, '_');  | 
            ||
| 414 | $parentClass = get_parent_class($parentClass);  | 
            ||
| 415 | }  | 
            ||
| 416 | |||
| 417 | // Add controller templates for inheritance chain  | 
            ||
| 418 | $templates = array_unique(array_merge($actionTemplates, $classTemplates));  | 
            ||
| 419 | }  | 
            ||
| 420 | |||
| 421 | return SSViewer::create($templates);  | 
            ||
| 422 | }  | 
            ||
| 423 | |||
| 424 | /**  | 
            ||
| 425 | * @param string $action  | 
            ||
| 426 | *  | 
            ||
| 427 | * @return bool  | 
            ||
| 428 | */  | 
            ||
| 429 | public function hasAction($action)  | 
            ||
| 432 | }  | 
            ||
| 433 | |||
| 434 | /**  | 
            ||
| 435 | * Removes all the "action" part of the current URL and returns the result. If no action parameter  | 
            ||
| 436 | * is present, returns the full URL.  | 
            ||
| 437 | *  | 
            ||
| 438 | * @param string $fullURL  | 
            ||
| 439 | * @param null|string $action  | 
            ||
| 440 | *  | 
            ||
| 441 | * @return string  | 
            ||
| 442 | */  | 
            ||
| 443 | public function removeAction($fullURL, $action = null)  | 
            ||
| 444 |     { | 
            ||
| 445 |         if (!$action) { | 
            ||
| 446 | $action = $this->getAction(); //default to current action  | 
            ||
| 447 | }  | 
            ||
| 448 | $returnURL = $fullURL;  | 
            ||
| 449 | |||
| 450 |         if (($pos = strpos($fullURL, $action)) !== false) { | 
            ||
| 451 | $returnURL = substr($fullURL, 0, $pos);  | 
            ||
| 452 | }  | 
            ||
| 453 | |||
| 454 | return $returnURL;  | 
            ||
| 455 | }  | 
            ||
| 456 | |||
| 457 | /**  | 
            ||
| 458 | * Return the class that defines the given action, so that we know where to check allowed_actions.  | 
            ||
| 459 | * Overrides RequestHandler to also look at defined templates.  | 
            ||
| 460 | *  | 
            ||
| 461 | * @param string $action  | 
            ||
| 462 | *  | 
            ||
| 463 | * @return string  | 
            ||
| 464 | */  | 
            ||
| 465 | protected function definingClassForAction($action)  | 
            ||
| 466 |     { | 
            ||
| 467 | $definingClass = parent::definingClassForAction($action);  | 
            ||
| 468 |         if ($definingClass) { | 
            ||
| 469 | return $definingClass;  | 
            ||
| 470 | }  | 
            ||
| 471 | |||
| 472 | $class = static::class;  | 
            ||
| 473 |         while ($class != 'SilverStripe\\Control\\RequestHandler') { | 
            ||
| 474 | $templateName = strtok($class, '_') . '_' . $action;  | 
            ||
| 475 |             if (SSViewer::hasTemplate($templateName)) { | 
            ||
| 476 | return $class;  | 
            ||
| 477 | }  | 
            ||
| 478 | |||
| 479 | $class = get_parent_class($class);  | 
            ||
| 480 | }  | 
            ||
| 481 | |||
| 482 | return null;  | 
            ||
| 483 | }  | 
            ||
| 484 | |||
| 485 | /**  | 
            ||
| 486 | * Returns TRUE if this controller has a template that is specifically designed to handle a  | 
            ||
| 487 | * specific action.  | 
            ||
| 488 | *  | 
            ||
| 489 | * @param string $action  | 
            ||
| 490 | *  | 
            ||
| 491 | * @return bool  | 
            ||
| 492 | */  | 
            ||
| 493 | public function hasActionTemplate($action)  | 
            ||
| 494 |     { | 
            ||
| 495 |         if (isset($this->templates[$action])) { | 
            ||
| 496 | return true;  | 
            ||
| 497 | }  | 
            ||
| 498 | |||
| 499 | $parentClass = static::class;  | 
            ||
| 500 | $templates = [];  | 
            ||
| 501 | |||
| 502 |         while ($parentClass != __CLASS__) { | 
            ||
| 503 | $templates[] = strtok($parentClass, '_') . '_' . $action;  | 
            ||
| 504 | $parentClass = get_parent_class($parentClass);  | 
            ||
| 505 | }  | 
            ||
| 506 | |||
| 507 | return SSViewer::hasTemplate($templates);  | 
            ||
| 508 | }  | 
            ||
| 509 | |||
| 510 | /**  | 
            ||
| 511 |      * Render the current controller with the templates determined by {@link getViewer()}. | 
            ||
| 512 | *  | 
            ||
| 513 | * @param array $params  | 
            ||
| 514 | *  | 
            ||
| 515 | * @return string  | 
            ||
| 516 | */  | 
            ||
| 517 | public function render($params = null)  | 
            ||
| 518 |     { | 
            ||
| 519 | $template = $this->getViewer($this->getAction());  | 
            ||
| 520 | |||
| 521 | // if the object is already customised (e.g. through Controller->run()), use it  | 
            ||
| 522 | $obj = $this->getCustomisedObj() ?: $this;  | 
            ||
| 523 | |||
| 524 |         if ($params) { | 
            ||
| 525 | $obj = $this->customise($params);  | 
            ||
| 526 | }  | 
            ||
| 527 | |||
| 528 | return $template->process($obj);  | 
            ||
| 529 | }  | 
            ||
| 530 | |||
| 531 | /**  | 
            ||
| 532 | * Call this to disable site-wide basic authentication for a specific controller. This must be  | 
            ||
| 533 | * called before Controller::init(). That is, you must call it in your controller's init method  | 
            ||
| 534 | * before it calls parent::init().  | 
            ||
| 535 | *  | 
            ||
| 536 | * @deprecated 4.1.0:5.0.0 Add this controller's url to  | 
            ||
| 537 | * SilverStripe\Security\BasicAuthMiddleware.URLPatterns injected property instead of setting false  | 
            ||
| 538 | */  | 
            ||
| 539 | public function disableBasicAuth()  | 
            ||
| 540 |     { | 
            ||
| 541 | Deprecation::notice(  | 
            ||
| 542 | '5.0',  | 
            ||
| 543 | 'Add this controller\'s url to ' . BasicAuthMiddleware::class . '.URLPatterns injected property instead'  | 
            ||
| 544 | );  | 
            ||
| 545 | $this->basicAuthEnabled = false;  | 
            ||
| 546 | }  | 
            ||
| 547 | |||
| 548 | /**  | 
            ||
| 549 | * Returns the current controller.  | 
            ||
| 550 | *  | 
            ||
| 551 | * @return Controller  | 
            ||
| 552 | */  | 
            ||
| 553 | public static function curr()  | 
            ||
| 554 |     { | 
            ||
| 555 |         if (Controller::$controller_stack) { | 
            ||
| 556 | return Controller::$controller_stack[0];  | 
            ||
| 557 | }  | 
            ||
| 558 |         user_error("No current controller available", E_USER_WARNING); | 
            ||
| 559 | return null;  | 
            ||
| 560 | }  | 
            ||
| 561 | |||
| 562 | /**  | 
            ||
| 563 | * Tests whether we have a currently active controller or not. True if there is at least 1  | 
            ||
| 564 | * controller in the stack.  | 
            ||
| 565 | *  | 
            ||
| 566 | * @return bool  | 
            ||
| 567 | */  | 
            ||
| 568 | public static function has_curr()  | 
            ||
| 569 |     { | 
            ||
| 570 | return Controller::$controller_stack ? true : false;  | 
            ||
| 571 | }  | 
            ||
| 572 | |||
| 573 | /**  | 
            ||
| 574 | * Returns true if the member is allowed to do the given action. Defaults to the currently logged  | 
            ||
| 575 | * in user.  | 
            ||
| 576 | *  | 
            ||
| 577 | * @param string $perm  | 
            ||
| 578 | * @param null|member $member  | 
            ||
| 579 | *  | 
            ||
| 580 | * @return bool  | 
            ||
| 581 | */  | 
            ||
| 582 | public function can($perm, $member = null)  | 
            ||
| 583 |     { | 
            ||
| 584 |         if (!$member) { | 
            ||
| 585 | $member = Security::getCurrentUser();  | 
            ||
| 586 | }  | 
            ||
| 587 |         if (is_array($perm)) { | 
            ||
| 588 | $perm = array_map([$this, 'can'], $perm, array_fill(0, count($perm), $member));  | 
            ||
| 589 | return min($perm);  | 
            ||
| 590 | }  | 
            ||
| 591 |         if ($this->hasMethod($methodName = 'can' . $perm)) { | 
            ||
| 592 | return $this->$methodName($member);  | 
            ||
| 593 |         } else { | 
            ||
| 594 | return true;  | 
            ||
| 595 | }  | 
            ||
| 596 | }  | 
            ||
| 597 | |||
| 598 | /**  | 
            ||
| 599 | * Pushes this controller onto the stack of current controllers. This means that any redirection,  | 
            ||
| 600 | * session setting, or other things that rely on Controller::curr() will now write to this  | 
            ||
| 601 | * controller object.  | 
            ||
| 602 | *  | 
            ||
| 603 | * Note: Ensure this controller is assigned a request with a valid session before pushing  | 
            ||
| 604 | * it to the stack.  | 
            ||
| 605 | */  | 
            ||
| 606 | public function pushCurrent()  | 
            ||
| 607 |     { | 
            ||
| 608 | // Ensure this controller has a valid session  | 
            ||
| 609 | $this->getRequest()->getSession();  | 
            ||
| 610 | array_unshift(self::$controller_stack, $this);  | 
            ||
| 611 | }  | 
            ||
| 612 | |||
| 613 | /**  | 
            ||
| 614 | * Pop this controller off the top of the stack.  | 
            ||
| 615 | */  | 
            ||
| 616 | public function popCurrent()  | 
            ||
| 617 |     { | 
            ||
| 618 |         if ($this === self::$controller_stack[0]) { | 
            ||
| 619 | array_shift(self::$controller_stack);  | 
            ||
| 620 |         } else { | 
            ||
| 621 | $class = static::class;  | 
            ||
| 622 | user_error(  | 
            ||
| 623 |                 "popCurrent called on {$class} controller, but it wasn't at the top of the stack", | 
            ||
| 624 | E_USER_WARNING  | 
            ||
| 625 | );  | 
            ||
| 626 | }  | 
            ||
| 627 | }  | 
            ||
| 628 | |||
| 629 | /**  | 
            ||
| 630 | * Redirect to the given URL.  | 
            ||
| 631 | *  | 
            ||
| 632 | * @param string $url  | 
            ||
| 633 | * @param int $code  | 
            ||
| 634 | * @return HTTPResponse  | 
            ||
| 635 | */  | 
            ||
| 636 | public function redirect($url, $code = 302)  | 
            ||
| 637 |     { | 
            ||
| 638 |         if ($this->getResponse()->getHeader('Location') && $this->getResponse()->getHeader('Location') != $url) { | 
            ||
| 639 |             user_error("Already directed to " . $this->getResponse()->getHeader('Location') | 
            ||
| 640 | . "; now trying to direct to $url", E_USER_WARNING);  | 
            ||
| 641 | return null;  | 
            ||
| 642 | }  | 
            ||
| 643 | $response = parent::redirect($url, $code);  | 
            ||
| 644 | $this->setResponse($response);  | 
            ||
| 645 | return $response;  | 
            ||
| 646 | }  | 
            ||
| 647 | |||
| 648 | /**  | 
            ||
| 649 | * Tests whether a redirection has been requested. If redirect() has been called, it will return  | 
            ||
| 650 | * the URL redirected to. Otherwise, it will return null.  | 
            ||
| 651 | *  | 
            ||
| 652 | * @return null|string  | 
            ||
| 653 | */  | 
            ||
| 654 | public function redirectedTo()  | 
            ||
| 657 | }  | 
            ||
| 658 | |||
| 659 | /**  | 
            ||
| 660 | * Joins two or more link segments together, putting a slash between them if necessary. Use this  | 
            ||
| 661 |      * for building the results of {@link Link()} methods. If either of the links have query strings, | 
            ||
| 662 | * then they will be combined and put at the end of the resulting url.  | 
            ||
| 663 | *  | 
            ||
| 664 | * Caution: All parameters are expected to be URI-encoded already.  | 
            ||
| 665 | *  | 
            ||
| 666 | * @param string|array $arg One or more link segments, or list of link segments as an array  | 
            ||
| 667 | * @return string  | 
            ||
| 668 | */  | 
            ||
| 669 | public static function join_links($arg = null)  | 
            ||
| 670 |     { | 
            ||
| 671 |         if (func_num_args() === 1 && is_array($arg)) { | 
            ||
| 672 | $args = $arg;  | 
            ||
| 673 |         } else { | 
            ||
| 674 | $args = func_get_args();  | 
            ||
| 675 | }  | 
            ||
| 676 | $result = "";  | 
            ||
| 677 | $queryargs = [];  | 
            ||
| 678 | $fragmentIdentifier = null;  | 
            ||
| 679 | |||
| 712 | }  | 
            ||
| 713 | |||
| 714 | /**  | 
            ||
| 715 | * @return array  | 
            ||
| 716 | */  | 
            ||
| 717 | public static function get_template_global_variables()  | 
            ||
| 724 |