ACC SHELL
<?php
/**
* This file is part of the Nette Framework (http://nette.org)
*
* Copyright (c) 2004 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
namespace Nette\Application\UI;
use Nette,
Nette\Application,
Nette\Application\Responses,
Nette\Http,
Nette\Reflection;
/**
* Presenter component represents a webpage instance. It converts Request to IResponse.
*
* @author David Grudl
*
* @property-read Nette\Application\Request $request
* @property-read array|NULL $signal
* @property-read string $action
* @property string $view
* @property string $layout
* @property-read \stdClass $payload
* @property-read bool $ajax
* @property-read Nette\Application\Request $lastCreatedRequest
* @property-read Nette\Http\SessionSection $flashSession
* @property-read \SystemContainer|Nette\DI\Container $context
* @property-read Nette\Application\Application $application
* @property-read Nette\Http\Session $session
* @property-read Nette\Security\User $user
*/
abstract class Presenter extends Control implements Application\IPresenter
{
/** bad link handling {@link Presenter::$invalidLinkMode} */
const INVALID_LINK_SILENT = 1,
INVALID_LINK_WARNING = 2,
INVALID_LINK_EXCEPTION = 3;
/** @internal special parameter key */
const SIGNAL_KEY = 'do',
ACTION_KEY = 'action',
FLASH_KEY = '_fid',
DEFAULT_ACTION = 'default';
/** @var int */
public $invalidLinkMode;
/** @var array of function(Presenter $sender, IResponse $response = NULL); Occurs when the presenter is shutting down */
public $onShutdown;
/** @var Nette\Application\Request */
private $request;
/** @var Nette\Application\IResponse */
private $response;
/** @var bool automatically call canonicalize() */
public $autoCanonicalize = TRUE;
/** @var bool use absolute Urls or paths? */
public $absoluteUrls = FALSE;
/** @var array */
private $globalParams;
/** @var array */
private $globalState;
/** @var array */
private $globalStateSinces;
/** @var string */
private $action;
/** @var string */
private $view;
/** @var string */
private $layout;
/** @var \stdClass */
private $payload;
/** @var string */
private $signalReceiver;
/** @var string */
private $signal;
/** @var bool */
private $ajaxMode;
/** @var bool */
private $startupCheck;
/** @var Nette\Application\Request */
private $lastCreatedRequest;
/** @var array */
private $lastCreatedRequestFlag;
/** @var \SystemContainer|Nette\DI\Container */
private $context;
public function __construct(Nette\DI\Container $context = NULL)
{
$this->context = $context;
if ($context && $this->invalidLinkMode === NULL) {
$this->invalidLinkMode = $context->parameters['productionMode'] ? self::INVALID_LINK_SILENT : self::INVALID_LINK_WARNING;
}
}
/**
* @return Nette\Application\Request
*/
final public function getRequest()
{
return $this->request;
}
/**
* Returns self.
* @return Presenter
*/
final public function getPresenter($need = TRUE)
{
return $this;
}
/**
* Returns a name that uniquely identifies component.
* @return string
*/
final public function getUniqueId()
{
return '';
}
/********************* interface IPresenter ****************d*g**/
/**
* @return Nette\Application\IResponse
*/
public function run(Application\Request $request)
{
try {
// STARTUP
$this->request = $request;
$this->payload = new \stdClass;
$this->setParent($this->getParent(), $request->getPresenterName());
$this->initGlobalParameters();
$this->checkRequirements($this->getReflection());
$this->startup();
if (!$this->startupCheck) {
$class = $this->getReflection()->getMethod('startup')->getDeclaringClass()->getName();
throw new Nette\InvalidStateException("Method $class::startup() or its descendant doesn't call parent::startup().");
}
// calls $this->action<Action>()
$this->tryCall($this->formatActionMethod($this->getAction()), $this->params);
if ($this->autoCanonicalize) {
$this->canonicalize();
}
if ($this->getHttpRequest()->isMethod('head')) {
$this->terminate();
}
// SIGNAL HANDLING
// calls $this->handle<Signal>()
$this->processSignal();
// RENDERING VIEW
$this->beforeRender();
// calls $this->render<View>()
$this->tryCall($this->formatRenderMethod($this->getView()), $this->params);
$this->afterRender();
// save component tree persistent state
$this->saveGlobalState();
if ($this->isAjax()) {
$this->payload->state = $this->getGlobalState();
}
// finish template rendering
$this->sendTemplate();
} catch (Application\AbortException $e) {
// continue with shutting down
if ($this->isAjax()) try {
$hasPayload = (array) $this->payload; unset($hasPayload['state']);
if ($this->response instanceof Responses\TextResponse && $this->isControlInvalid()) { // snippets - TODO
$this->snippetMode = TRUE;
$this->response->send($this->getHttpRequest(), $this->getHttpResponse());
$this->sendPayload();
} elseif (!$this->response && $hasPayload) { // back compatibility for use terminate() instead of sendPayload()
$this->sendPayload();
}
} catch (Application\AbortException $e) { }
if ($this->hasFlashSession()) {
$this->getFlashSession()->setExpiration($this->response instanceof Responses\RedirectResponse ? '+ 30 seconds' : '+ 3 seconds');
}
// SHUTDOWN
$this->onShutdown($this, $this->response);
$this->shutdown($this->response);
return $this->response;
}
}
/**
* @return void
*/
protected function startup()
{
$this->startupCheck = TRUE;
}
/**
* Common render method.
* @return void
*/
protected function beforeRender()
{
}
/**
* Common render method.
* @return void
*/
protected function afterRender()
{
}
/**
* @param Nette\Application\IResponse
* @return void
*/
protected function shutdown($response)
{
}
/**
* Checks authorization.
* @return void
*/
public function checkRequirements($element)
{
$user = (array) $element->getAnnotation('User');
if (in_array('loggedIn', $user) && !$this->getUser()->isLoggedIn()) {
throw new Application\ForbiddenRequestException;
}
}
/********************* signal handling ****************d*g**/
/**
* @return void
* @throws BadSignalException
*/
public function processSignal()
{
if ($this->signal === NULL) {
return;
}
try {
$component = $this->signalReceiver === '' ? $this : $this->getComponent($this->signalReceiver, FALSE);
} catch (Nette\InvalidArgumentException $e) {}
if (isset($e) || $component === NULL) {
throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not found.", NULL, isset($e) ? $e : NULL);
} elseif (!$component instanceof ISignalReceiver) {
throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not ISignalReceiver implementor.");
}
$component->signalReceived($this->signal);
$this->signal = NULL;
}
/**
* Returns pair signal receiver and name.
* @return array|NULL
*/
final public function getSignal()
{
return $this->signal === NULL ? NULL : array($this->signalReceiver, $this->signal);
}
/**
* Checks if the signal receiver is the given one.
* @param mixed component or its id
* @param string signal name (optional)
* @return bool
*/
final public function isSignalReceiver($component, $signal = NULL)
{
if ($component instanceof Nette\ComponentModel\Component) {
$component = $component === $this ? '' : $component->lookupPath(__CLASS__, TRUE);
}
if ($this->signal === NULL) {
return FALSE;
} elseif ($signal === TRUE) {
return $component === ''
|| strncmp($this->signalReceiver . '-', $component . '-', strlen($component) + 1) === 0;
} elseif ($signal === NULL) {
return $this->signalReceiver === $component;
} else {
return $this->signalReceiver === $component && strcasecmp($signal, $this->signal) === 0;
}
}
/********************* rendering ****************d*g**/
/**
* Returns current action name.
* @return string
*/
final public function getAction($fullyQualified = FALSE)
{
return $fullyQualified ? ':' . $this->getName() . ':' . $this->action : $this->action;
}
/**
* Changes current action. Only alphanumeric characters are allowed.
* @param string
* @return void
*/
public function changeAction($action)
{
if (is_string($action) && Nette\Utils\Strings::match($action, '#^[a-zA-Z0-9][a-zA-Z0-9_\x7f-\xff]*\z#')) {
$this->action = $action;
$this->view = $action;
} else {
$this->error('Action name is not alphanumeric string.');
}
}
/**
* Returns current view.
* @return string
*/
final public function getView()
{
return $this->view;
}
/**
* Changes current view. Any name is allowed.
* @param string
* @return self
*/
public function setView($view)
{
$this->view = (string) $view;
return $this;
}
/**
* Returns current layout name.
* @return string|FALSE
*/
final public function getLayout()
{
return $this->layout;
}
/**
* Changes or disables layout.
* @param string|FALSE
* @return self
*/
public function setLayout($layout)
{
$this->layout = $layout === FALSE ? FALSE : (string) $layout;
return $this;
}
/**
* @return void
* @throws Nette\Application\BadRequestException if no template found
* @throws Nette\Application\AbortException
*/
public function sendTemplate()
{
$template = $this->getTemplate();
if (!$template) {
return;
}
if ($template instanceof Nette\Templating\IFileTemplate && !$template->getFile()) { // content template
$files = $this->formatTemplateFiles();
foreach ($files as $file) {
if (is_file($file)) {
$template->setFile($file);
break;
}
}
if (!$template->getFile()) {
$file = preg_replace('#^.*([/\\\\].{1,70})\z#U', "\xE2\x80\xA6\$1", reset($files));
$file = strtr($file, '/', DIRECTORY_SEPARATOR);
$this->error("Page not found. Missing template '$file'.");
}
}
$this->sendResponse(new Responses\TextResponse($template));
}
/**
* Finds layout template file name.
* @return string
*/
public function findLayoutTemplateFile()
{
if ($this->layout === FALSE) {
return;
}
$files = $this->formatLayoutTemplateFiles();
foreach ($files as $file) {
if (is_file($file)) {
return $file;
}
}
if ($this->layout) {
$file = preg_replace('#^.*([/\\\\].{1,70})\z#U', "\xE2\x80\xA6\$1", reset($files));
$file = strtr($file, '/', DIRECTORY_SEPARATOR);
throw new Nette\FileNotFoundException("Layout not found. Missing template '$file'.");
}
}
/**
* Formats layout template file names.
* @return array
*/
public function formatLayoutTemplateFiles()
{
$name = $this->getName();
$presenter = substr($name, strrpos(':' . $name, ':'));
$layout = $this->layout ? $this->layout : 'layout';
$dir = dirname($this->getReflection()->getFileName());
$dir = is_dir("$dir/templates") ? $dir : dirname($dir);
$list = array(
"$dir/templates/$presenter/@$layout.latte",
"$dir/templates/$presenter.@$layout.latte",
"$dir/templates/$presenter/@$layout.phtml",
"$dir/templates/$presenter.@$layout.phtml",
);
do {
$list[] = "$dir/templates/@$layout.latte";
$list[] = "$dir/templates/@$layout.phtml";
$dir = dirname($dir);
} while ($dir && ($name = substr($name, 0, strrpos($name, ':'))));
return $list;
}
/**
* Formats view template file names.
* @return array
*/
public function formatTemplateFiles()
{
$name = $this->getName();
$presenter = substr($name, strrpos(':' . $name, ':'));
$dir = dirname($this->getReflection()->getFileName());
$dir = is_dir("$dir/templates") ? $dir : dirname($dir);
return array(
"$dir/templates/$presenter/$this->view.latte",
"$dir/templates/$presenter.$this->view.latte",
"$dir/templates/$presenter/$this->view.phtml",
"$dir/templates/$presenter.$this->view.phtml",
);
}
/**
* Formats action method name.
* @param string
* @return string
*/
protected static function formatActionMethod($action)
{
return 'action' . $action;
}
/**
* Formats render view method name.
* @param string
* @return string
*/
protected static function formatRenderMethod($view)
{
return 'render' . $view;
}
/********************* partial AJAX rendering ****************d*g**/
/**
* @return \stdClass
*/
public function getPayload()
{
return $this->payload;
}
/**
* Is AJAX request?
* @return bool
*/
public function isAjax()
{
if ($this->ajaxMode === NULL) {
$this->ajaxMode = $this->getHttpRequest()->isAjax();
}
return $this->ajaxMode;
}
/**
* Sends AJAX payload to the output.
* @return void
* @throws Nette\Application\AbortException
*/
public function sendPayload()
{
$this->sendResponse(new Responses\JsonResponse($this->payload));
}
/********************* navigation & flow ****************d*g**/
/**
* Sends response and terminates presenter.
* @return void
* @throws Nette\Application\AbortException
*/
public function sendResponse(Application\IResponse $response)
{
$this->response = $response;
$this->terminate();
}
/**
* Correctly terminates presenter.
* @return void
* @throws Nette\Application\AbortException
*/
public function terminate()
{
if (func_num_args() !== 0) {
trigger_error(__METHOD__ . ' is not intended to send a Application\Response; use sendResponse() instead.', E_USER_WARNING);
$this->sendResponse(func_get_arg(0));
}
throw new Application\AbortException();
}
/**
* Forward to another presenter or action.
* @param string|Request
* @param array|mixed
* @return void
* @throws Nette\Application\AbortException
*/
public function forward($destination, $args = array())
{
if ($destination instanceof Application\Request) {
$this->sendResponse(new Responses\ForwardResponse($destination));
}
$this->createRequest($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1), 'forward');
$this->sendResponse(new Responses\ForwardResponse($this->lastCreatedRequest));
}
/**
* Redirect to another URL and ends presenter execution.
* @param string
* @param int HTTP error code
* @return void
* @throws Nette\Application\AbortException
*/
public function redirectUrl($url, $code = NULL)
{
if ($this->isAjax()) {
$this->payload->redirect = (string) $url;
$this->sendPayload();
} elseif (!$code) {
$code = $this->getHttpRequest()->isMethod('post')
? Http\IResponse::S303_POST_GET
: Http\IResponse::S302_FOUND;
}
$this->sendResponse(new Responses\RedirectResponse($url, $code));
}
/** @deprecated */
function redirectUri($url, $code = NULL)
{
trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::redirectUrl() instead.', E_USER_WARNING);
$this->redirectUrl($url, $code);
}
/**
* Throws HTTP error.
* @param string
* @param int HTTP error code
* @return void
* @throws Nette\Application\BadRequestException
*/
public function error($message = NULL, $code = Http\IResponse::S404_NOT_FOUND)
{
throw new Application\BadRequestException($message, $code);
}
/**
* Link to myself.
* @return string
*/
public function backlink()
{
return $this->getAction(TRUE);
}
/**
* Returns the last created Request.
* @return Nette\Application\Request
*/
public function getLastCreatedRequest()
{
return $this->lastCreatedRequest;
}
/**
* Returns the last created Request flag.
* @param string
* @return bool
*/
public function getLastCreatedRequestFlag($flag)
{
return !empty($this->lastCreatedRequestFlag[$flag]);
}
/**
* Conditional redirect to canonicalized URI.
* @return void
* @throws Nette\Application\AbortException
*/
public function canonicalize()
{
if (!$this->isAjax() && ($this->request->isMethod('get') || $this->request->isMethod('head'))) {
try {
$url = $this->createRequest($this, $this->action, $this->getGlobalState() + $this->request->getParameters(), 'redirectX');
} catch (InvalidLinkException $e) {}
if (isset($url) && !$this->getHttpRequest()->getUrl()->isEqual($url)) {
$this->sendResponse(new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY));
}
}
}
/**
* Attempts to cache the sent entity by its last modification date.
* @param string|int|DateTime last modified time
* @param string strong entity tag validator
* @param mixed optional expiration time
* @return void
* @throws Nette\Application\AbortException
* @deprecated
*/
public function lastModified($lastModified, $etag = NULL, $expire = NULL)
{
if ($expire !== NULL) {
$this->getHttpResponse()->setExpiration($expire);
}
if (!$this->getHttpContext()->isModified($lastModified, $etag)) {
$this->terminate();
}
}
/**
* Request/URL factory.
* @param PresenterComponent base
* @param string destination in format "[[module:]presenter:]action" or "signal!" or "this"
* @param array array of arguments
* @param string forward|redirect|link
* @return string URL
* @throws InvalidLinkException
* @internal
*/
final protected function createRequest($component, $destination, array $args, $mode)
{
// note: createRequest supposes that saveState(), run() & tryCall() behaviour is final
// cached services for better performance
static $presenterFactory, $router, $refUrl;
if ($presenterFactory === NULL) {
$presenterFactory = $this->getApplication()->getPresenterFactory();
$router = $this->getApplication()->getRouter();
$refUrl = new Http\Url($this->getHttpRequest()->getUrl());
$refUrl->setPath($this->getHttpRequest()->getUrl()->getScriptPath());
}
$this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL;
// PARSE DESTINATION
// 1) fragment
$a = strpos($destination, '#');
if ($a === FALSE) {
$fragment = '';
} else {
$fragment = substr($destination, $a);
$destination = substr($destination, 0, $a);
}
// 2) ?query syntax
$a = strpos($destination, '?');
if ($a !== FALSE) {
parse_str(substr($destination, $a + 1), $args); // requires disabled magic quotes
$destination = substr($destination, 0, $a);
}
// 3) URL scheme
$a = strpos($destination, '//');
if ($a === FALSE) {
$scheme = FALSE;
} else {
$scheme = substr($destination, 0, $a);
$destination = substr($destination, $a + 2);
}
// 4) signal or empty
if (!$component instanceof Presenter || substr($destination, -1) === '!') {
$signal = rtrim($destination, '!');
$a = strrpos($signal, ':');
if ($a !== FALSE) {
$component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-'));
$signal = (string) substr($signal, $a + 1);
}
if ($signal == NULL) { // intentionally ==
throw new InvalidLinkException("Signal must be non-empty string.");
}
$destination = 'this';
}
if ($destination == NULL) { // intentionally ==
throw new InvalidLinkException("Destination must be non-empty string.");
}
// 5) presenter: action
$current = FALSE;
$a = strrpos($destination, ':');
if ($a === FALSE) {
$action = $destination === 'this' ? $this->action : $destination;
$presenter = $this->getName();
$presenterClass = get_class($this);
} else {
$action = (string) substr($destination, $a + 1);
if ($destination[0] === ':') { // absolute
if ($a < 2) {
throw new InvalidLinkException("Missing presenter name in '$destination'.");
}
$presenter = substr($destination, 1, $a - 1);
} else { // relative
$presenter = $this->getName();
$b = strrpos($presenter, ':');
if ($b === FALSE) { // no module
$presenter = substr($destination, 0, $a);
} else { // with module
$presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a);
}
}
try {
$presenterClass = $presenterFactory->getPresenterClass($presenter);
} catch (Application\InvalidPresenterException $e) {
throw new InvalidLinkException($e->getMessage(), NULL, $e);
}
}
// PROCESS SIGNAL ARGUMENTS
if (isset($signal)) { // $component must be IStatePersistent
$reflection = new PresenterComponentReflection(get_class($component));
if ($signal === 'this') { // means "no signal"
$signal = '';
if (array_key_exists(0, $args)) {
throw new InvalidLinkException("Unable to pass parameters to 'this!' signal.");
}
} elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) { // TODO: AppForm exception
// counterpart of signalReceived() & tryCall()
$method = $component->formatSignalMethod($signal);
if (!$reflection->hasCallableMethod($method)) {
throw new InvalidLinkException("Unknown signal '$signal', missing handler {$reflection->name}::$method()");
}
if ($args) { // convert indexed parameters to named
self::argsToParams(get_class($component), $method, $args);
}
}
// counterpart of IStatePersistent
if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
$component->saveState($args);
}
if ($args && $component !== $this) {
$prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
foreach ($args as $key => $val) {
unset($args[$key]);
$args[$prefix . $key] = $val;
}
}
}
// PROCESS ARGUMENTS
if (is_subclass_of($presenterClass, __CLASS__)) {
if ($action === '') {
$action = self::DEFAULT_ACTION;
}
$current = ($action === '*' || strcasecmp($action, $this->action) === 0) && $presenterClass === get_class($this); // TODO
$reflection = new PresenterComponentReflection($presenterClass);
if ($args || $destination === 'this') {
// counterpart of run() & tryCall()
$method = $presenterClass::formatActionMethod($action);
if (!$reflection->hasCallableMethod($method)) {
$method = $presenterClass::formatRenderMethod($action);
if (!$reflection->hasCallableMethod($method)) {
$method = NULL;
}
}
// convert indexed parameters to named
if ($method === NULL) {
if (array_key_exists(0, $args)) {
throw new InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method.");
}
} elseif ($destination === 'this') {
self::argsToParams($presenterClass, $method, $args, $this->params);
} else {
self::argsToParams($presenterClass, $method, $args);
}
}
// counterpart of IStatePersistent
if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
$this->saveState($args, $reflection);
}
if ($mode === 'redirect') {
$this->saveGlobalState();
}
$globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass);
if ($current && $args) {
$tmp = $globalState + $this->params;
foreach ($args as $key => $val) {
if (http_build_query(array($val)) !== (isset($tmp[$key]) ? http_build_query(array($tmp[$key])) : '')) {
$current = FALSE;
break;
}
}
}
$args += $globalState;
}
// ADD ACTION & SIGNAL & FLASH
$args[self::ACTION_KEY] = $action;
if (!empty($signal)) {
$args[self::SIGNAL_KEY] = $component->getParameterId($signal);
$current = $current && $args[self::SIGNAL_KEY] === $this->getParameter(self::SIGNAL_KEY);
}
if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) {
$args[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
}
$this->lastCreatedRequest = new Application\Request(
$presenter,
Application\Request::FORWARD,
$args,
array(),
array()
);
$this->lastCreatedRequestFlag = array('current' => $current);
if ($mode === 'forward' || $mode === 'test') {
return;
}
// CONSTRUCT URL
$url = $router->constructUrl($this->lastCreatedRequest, $refUrl);
if ($url === NULL) {
unset($args[self::ACTION_KEY]);
$params = urldecode(http_build_query($args, NULL, ', '));
throw new InvalidLinkException("No route for $presenter:$action($params)");
}
// make URL relative if possible
if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) {
$hostUrl = $refUrl->getHostUrl();
if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) {
$url = substr($url, strlen($hostUrl));
}
}
return $url . $fragment;
}
/**
* Converts list of arguments to named parameters.
* @param string class name
* @param string method name
* @param array arguments
* @param array supplemental arguments
* @return void
* @throws InvalidLinkException
*/
private static function argsToParams($class, $method, & $args, $supplemental = array())
{
$i = 0;
$rm = new \ReflectionMethod($class, $method);
foreach ($rm->getParameters() as $param) {
$name = $param->getName();
if (array_key_exists($i, $args)) {
$args[$name] = $args[$i];
unset($args[$i]);
$i++;
} elseif (array_key_exists($name, $args)) {
// continue with process
} elseif (array_key_exists($name, $supplemental)) {
$args[$name] = $supplemental[$name];
} else {
continue;
}
if ($args[$name] === NULL) {
continue;
}
$def = $param->isDefaultValueAvailable() && $param->isOptional() ? $param->getDefaultValue() : NULL; // see PHP bug #62988
$type = $param->isArray() ? 'array' : gettype($def);
if (!PresenterComponentReflection::convertType($args[$name], $type)) {
throw new InvalidLinkException("Invalid value for parameter '$name' in method $class::$method(), expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
}
if ($args[$name] === $def || ($def === NULL && is_scalar($args[$name]) && (string) $args[$name] === '')) {
$args[$name] = NULL; // value transmit is unnecessary
}
}
if (array_key_exists($i, $args)) {
$method = $rm->getName();
throw new InvalidLinkException("Passed more parameters than method $class::$method() expects.");
}
}
/**
* Invalid link handler. Descendant can override this method to change default behaviour.
* @return string
* @throws InvalidLinkException
*/
protected function handleInvalidLink(InvalidLinkException $e)
{
if ($this->invalidLinkMode === self::INVALID_LINK_SILENT) {
return '#';
} elseif ($this->invalidLinkMode === self::INVALID_LINK_WARNING) {
return 'error: ' . $e->getMessage();
} else { // self::INVALID_LINK_EXCEPTION
throw $e;
}
}
/********************* request serialization ****************d*g**/
/**
* Stores current request to session.
* @param mixed optional expiration time
* @return string key
*/
public function storeRequest($expiration = '+ 10 minutes')
{
$session = $this->getSession('Nette.Application/requests');
do {
$key = Nette\Utils\Strings::random(5);
} while (isset($session[$key]));
$session[$key] = array($this->getUser()->getId(), $this->request);
$session->setExpiration($expiration, $key);
return $key;
}
/**
* Restores current request to session.
* @param string key
* @return void
*/
public function restoreRequest($key)
{
$session = $this->getSession('Nette.Application/requests');
if (!isset($session[$key]) || ($session[$key][0] !== NULL && $session[$key][0] !== $this->getUser()->getId())) {
return;
}
$request = clone $session[$key][1];
unset($session[$key]);
$request->setFlag(Application\Request::RESTORED, TRUE);
$params = $request->getParameters();
$params[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
$request->setParameters($params);
$this->sendResponse(new Responses\ForwardResponse($request));
}
/********************* interface IStatePersistent ****************d*g**/
/**
* Returns array of persistent components.
* This default implementation detects components by class-level annotation @persistent(cmp1, cmp2).
* @return array
*/
public static function getPersistentComponents()
{
return (array) Reflection\ClassType::from(get_called_class())
->getAnnotation('persistent');
}
/**
* Saves state information for all subcomponents to $this->globalState.
* @return array
*/
private function getGlobalState($forClass = NULL)
{
$sinces = & $this->globalStateSinces;
if ($this->globalState === NULL) {
$state = array();
foreach ($this->globalParams as $id => $params) {
$prefix = $id . self::NAME_SEPARATOR;
foreach ($params as $key => $val) {
$state[$prefix . $key] = $val;
}
}
$this->saveState($state, $forClass ? new PresenterComponentReflection($forClass) : NULL);
if ($sinces === NULL) {
$sinces = array();
foreach ($this->getReflection()->getPersistentParams() as $name => $meta) {
$sinces[$name] = $meta['since'];
}
}
$components = $this->getReflection()->getPersistentComponents();
$iterator = $this->getComponents(TRUE, 'Nette\Application\UI\IStatePersistent');
foreach ($iterator as $name => $component) {
if ($iterator->getDepth() === 0) {
// counts with Nette\Application\RecursiveIteratorIterator::SELF_FIRST
$since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE; // FALSE = nonpersistent
}
$prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
$params = array();
$component->saveState($params);
foreach ($params as $key => $val) {
$state[$prefix . $key] = $val;
$sinces[$prefix . $key] = $since;
}
}
} else {
$state = $this->globalState;
}
if ($forClass !== NULL) {
$since = NULL;
foreach ($state as $key => $foo) {
if (!isset($sinces[$key])) {
$x = strpos($key, self::NAME_SEPARATOR);
$x = $x === FALSE ? $key : substr($key, 0, $x);
$sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE;
}
if ($since !== $sinces[$key]) {
$since = $sinces[$key];
$ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since);
}
if (!$ok) {
unset($state[$key]);
}
}
}
return $state;
}
/**
* Permanently saves state information for all subcomponents to $this->globalState.
* @return void
*/
protected function saveGlobalState()
{
// load lazy components
foreach ($this->globalParams as $id => $foo) {
$this->getComponent($id, FALSE);
}
$this->globalParams = array();
$this->globalState = $this->getGlobalState();
}
/**
* Initializes $this->globalParams, $this->signal & $this->signalReceiver, $this->action, $this->view. Called by run().
* @return void
* @throws Nette\Application\BadRequestException if action name is not valid
*/
private function initGlobalParameters()
{
// init $this->globalParams
$this->globalParams = array();
$selfParams = array();
$params = $this->request->getParameters();
if ($this->isAjax()) {
$params += $this->request->getPost();
}
foreach ($params as $key => $value) {
if (!preg_match('#^((?:[a-z0-9_]+-)*)((?!\d+\z)[a-z0-9_]+)\z#i', $key, $matches)) {
continue;
} elseif (!$matches[1]) {
$selfParams[$key] = $value;
} else {
$this->globalParams[substr($matches[1], 0, -1)][$matches[2]] = $value;
}
}
// init & validate $this->action & $this->view
$this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION);
// init $this->signalReceiver and key 'signal' in appropriate params array
$this->signalReceiver = $this->getUniqueId();
if (isset($selfParams[self::SIGNAL_KEY])) {
$param = $selfParams[self::SIGNAL_KEY];
if (!is_string($param)) {
$this->error('Signal name is not string.');
}
$pos = strrpos($param, '-');
if ($pos) {
$this->signalReceiver = substr($param, 0, $pos);
$this->signal = substr($param, $pos + 1);
} else {
$this->signalReceiver = $this->getUniqueId();
$this->signal = $param;
}
if ($this->signal == NULL) { // intentionally ==
$this->signal = NULL;
}
}
$this->loadState($selfParams);
}
/**
* Pops parameters for specified component.
* @param string component id
* @return array
*/
final public function popGlobalParameters($id)
{
if (isset($this->globalParams[$id])) {
$res = $this->globalParams[$id];
unset($this->globalParams[$id]);
return $res;
} else {
return array();
}
}
/********************* flash session ****************d*g**/
/**
* Checks if a flash session namespace exists.
* @return bool
*/
public function hasFlashSession()
{
return !empty($this->params[self::FLASH_KEY])
&& $this->getSession()->hasSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
}
/**
* Returns session namespace provided to pass temporary data between redirects.
* @return Nette\Http\SessionSection
*/
public function getFlashSession()
{
if (empty($this->params[self::FLASH_KEY])) {
$this->params[self::FLASH_KEY] = Nette\Utils\Strings::random(4);
}
return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
}
/********************* services ****************d*g**/
/**
* @return void
*/
final public function injectPrimary(Nette\DI\Container $context)
{
$this->context = $context;
}
/**
* Gets the context.
* @return \SystemContainer|Nette\DI\Container
*/
final public function getContext()
{
return $this->context;
}
/**
* @deprecated
*/
final public function getService($name)
{
return $this->context->getService($name);
}
/**
* @return Nette\Http\Request
*/
protected function getHttpRequest()
{
return $this->context->getByType('Nette\Http\IRequest');
}
/**
* @return Nette\Http\Response
*/
protected function getHttpResponse()
{
return $this->context->getByType('Nette\Http\IResponse');
}
/**
* @return Nette\Http\Context
*/
protected function getHttpContext()
{
return $this->context->getByType('Nette\Http\Context');
}
/**
* @return Nette\Application\Application
*/
public function getApplication()
{
return $this->context->getByType('Nette\Application\Application');
}
/**
* @param string
* @return Nette\Http\Session|Nette\Http\SessionSection
*/
public function getSession($namespace = NULL)
{
$handler = $this->context->getByType('Nette\Http\Session');
return $namespace === NULL ? $handler : $handler->getSection($namespace);
}
/**
* @return Nette\Security\User
*/
public function getUser()
{
return $this->context->getByType('Nette\Security\User');
}
}
ACC SHELL 2018