Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Component
  • Control
  • Form
  • Link
  • Multiplier
  • Presenter

Interfaces

  • IRenderable
  • ISignalReceiver
  • IStatePersistent
  • ITemplate
  • ITemplateFactory

Exceptions

  • BadSignalException
  • InvalidLinkException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
   1: <?php
   2: 
   3: /**
   4:  * This file is part of the Nette Framework (https://nette.org)
   5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
   6:  */
   7: 
   8: namespace Nette\Application\UI;
   9: 
  10: use Nette;
  11: use Nette\Application;
  12: use Nette\Application\Helpers;
  13: use Nette\Application\Responses;
  14: use Nette\Http;
  15: 
  16: 
  17: /**
  18:  * Presenter component represents a webpage instance. It converts Request to IResponse.
  19:  *
  20:  * @property-read Nette\Application\Request $request
  21:  * @property-read string $action
  22:  * @property      string $view
  23:  * @property      string $layout
  24:  * @property-read \stdClass $payload
  25:  * @property-read Nette\DI\Container $context
  26:  * @property-read Nette\Http\Session $session
  27:  * @property-read Nette\Security\User $user
  28:  */
  29: abstract class Presenter extends Control implements Application\IPresenter
  30: {
  31:     /** bad link handling {@link Presenter::$invalidLinkMode} */
  32:     const INVALID_LINK_SILENT = 0b0000,
  33:         INVALID_LINK_WARNING = 0b0001,
  34:         INVALID_LINK_EXCEPTION = 0b0010,
  35:         INVALID_LINK_TEXTUAL = 0b0100;
  36: 
  37:     /** @internal special parameter key */
  38:     const SIGNAL_KEY = 'do',
  39:         ACTION_KEY = 'action',
  40:         FLASH_KEY = '_fid',
  41:         DEFAULT_ACTION = 'default';
  42: 
  43:     /** @var int */
  44:     public $invalidLinkMode;
  45: 
  46:     /** @var callable[]  function (Presenter $sender, IResponse $response = null); Occurs when the presenter is shutting down */
  47:     public $onShutdown;
  48: 
  49:     /** @var bool  automatically call canonicalize() */
  50:     public $autoCanonicalize = true;
  51: 
  52:     /** @var bool  use absolute Urls or paths? */
  53:     public $absoluteUrls = false;
  54: 
  55:     /** @var Nette\Application\Request|null */
  56:     private $request;
  57: 
  58:     /** @var Nette\Application\IResponse */
  59:     private $response;
  60: 
  61:     /** @var array */
  62:     private $globalParams;
  63: 
  64:     /** @var array */
  65:     private $globalState;
  66: 
  67:     /** @var array */
  68:     private $globalStateSinces;
  69: 
  70:     /** @var string */
  71:     private $action;
  72: 
  73:     /** @var string */
  74:     private $view;
  75: 
  76:     /** @var string */
  77:     private $layout;
  78: 
  79:     /** @var \stdClass */
  80:     private $payload;
  81: 
  82:     /** @var string */
  83:     private $signalReceiver;
  84: 
  85:     /** @var string */
  86:     private $signal;
  87: 
  88:     /** @var bool */
  89:     private $ajaxMode;
  90: 
  91:     /** @var bool */
  92:     private $startupCheck;
  93: 
  94:     /** @var Nette\Application\Request|null */
  95:     private $lastCreatedRequest;
  96: 
  97:     /** @var array */
  98:     private $lastCreatedRequestFlag;
  99: 
 100:     /** @var Nette\DI\Container */
 101:     private $context;
 102: 
 103:     /** @var Nette\Http\IRequest */
 104:     private $httpRequest;
 105: 
 106:     /** @var Nette\Http\IResponse */
 107:     private $httpResponse;
 108: 
 109:     /** @var Nette\Http\Session */
 110:     private $session;
 111: 
 112:     /** @var Nette\Application\IPresenterFactory */
 113:     private $presenterFactory;
 114: 
 115:     /** @var Nette\Application\IRouter */
 116:     private $router;
 117: 
 118:     /** @var Nette\Security\User */
 119:     private $user;
 120: 
 121:     /** @var ITemplateFactory */
 122:     private $templateFactory;
 123: 
 124:     /** @var Nette\Http\Url */
 125:     private $refUrlCache;
 126: 
 127: 
 128:     public function __construct()
 129:     {
 130:         $this->payload = new \stdClass;
 131:     }
 132: 
 133: 
 134:     /**
 135:      * @return Nette\Application\Request|null
 136:      */
 137:     public function getRequest()
 138:     {
 139:         return $this->request;
 140:     }
 141: 
 142: 
 143:     /**
 144:      * Returns self.
 145:      * @return Presenter
 146:      */
 147:     public function getPresenter($throw = true)
 148:     {
 149:         return $this;
 150:     }
 151: 
 152: 
 153:     /**
 154:      * Returns a name that uniquely identifies component.
 155:      * @return string
 156:      */
 157:     public function getUniqueId()
 158:     {
 159:         return '';
 160:     }
 161: 
 162: 
 163:     /********************* interface IPresenter ****************d*g**/
 164: 
 165: 
 166:     /**
 167:      * @return Nette\Application\IResponse
 168:      */
 169:     public function run(Application\Request $request)
 170:     {
 171:         try {
 172:             // STARTUP
 173:             $this->request = $request;
 174:             $this->payload = $this->payload ?: new \stdClass;
 175:             $this->setParent($this->getParent(), $request->getPresenterName());
 176: 
 177:             if (!$this->httpResponse->isSent()) {
 178:                 $this->httpResponse->addHeader('Vary', 'X-Requested-With');
 179:             }
 180: 
 181:             $this->initGlobalParameters();
 182:             $this->checkRequirements($this->getReflection());
 183:             $this->startup();
 184:             if (!$this->startupCheck) {
 185:                 $class = $this->getReflection()->getMethod('startup')->getDeclaringClass()->getName();
 186:                 throw new Nette\InvalidStateException("Method $class::startup() or its descendant doesn't call parent::startup().");
 187:             }
 188:             // calls $this->action<Action>()
 189:             $this->tryCall($this->formatActionMethod($this->action), $this->params);
 190: 
 191:             // autoload components
 192:             foreach ($this->globalParams as $id => $foo) {
 193:                 $this->getComponent($id, false);
 194:             }
 195: 
 196:             if ($this->autoCanonicalize) {
 197:                 $this->canonicalize();
 198:             }
 199:             if ($this->httpRequest->isMethod('head')) {
 200:                 $this->terminate();
 201:             }
 202: 
 203:             // SIGNAL HANDLING
 204:             // calls $this->handle<Signal>()
 205:             $this->processSignal();
 206: 
 207:             // RENDERING VIEW
 208:             $this->beforeRender();
 209:             // calls $this->render<View>()
 210:             $this->tryCall($this->formatRenderMethod($this->view), $this->params);
 211:             $this->afterRender();
 212: 
 213:             // save component tree persistent state
 214:             $this->saveGlobalState();
 215:             if ($this->isAjax()) {
 216:                 $this->payload->state = $this->getGlobalState();
 217:             }
 218: 
 219:             // finish template rendering
 220:             if ($this->getTemplate()) {
 221:                 $this->sendTemplate();
 222:             }
 223: 
 224:         } catch (Application\AbortException $e) {
 225:             // continue with shutting down
 226:             if ($this->isAjax()) {
 227:                 try {
 228:                     $hasPayload = (array) $this->payload;
 229:                     unset($hasPayload['state']);
 230:                     if ($this->response instanceof Responses\TextResponse && $this->isControlInvalid()) {
 231:                         $this->snippetMode = true;
 232:                         $this->response->send($this->httpRequest, $this->httpResponse);
 233:                         $this->sendPayload();
 234:                     } elseif (!$this->response && $hasPayload) { // back compatibility for use terminate() instead of sendPayload()
 235:                         trigger_error('Use $presenter->sendPayload() instead of terminate() to send payload.');
 236:                         $this->sendPayload();
 237:                     }
 238:                 } catch (Application\AbortException $e) {
 239:                 }
 240:             }
 241: 
 242:             if ($this->hasFlashSession()) {
 243:                 $this->getFlashSession()->setExpiration($this->response instanceof Responses\RedirectResponse ? '+ 30 seconds' : '+ 3 seconds');
 244:             }
 245: 
 246:             // SHUTDOWN
 247:             $this->onShutdown($this, $this->response);
 248:             $this->shutdown($this->response);
 249: 
 250:             return $this->response;
 251:         }
 252:     }
 253: 
 254: 
 255:     /**
 256:      * @return void
 257:      */
 258:     protected function startup()
 259:     {
 260:         $this->startupCheck = true;
 261:     }
 262: 
 263: 
 264:     /**
 265:      * Common render method.
 266:      * @return void
 267:      */
 268:     protected function beforeRender()
 269:     {
 270:     }
 271: 
 272: 
 273:     /**
 274:      * Common render method.
 275:      * @return void
 276:      */
 277:     protected function afterRender()
 278:     {
 279:     }
 280: 
 281: 
 282:     /**
 283:      * @param  Nette\Application\IResponse
 284:      * @return void
 285:      */
 286:     protected function shutdown($response)
 287:     {
 288:     }
 289: 
 290: 
 291:     /**
 292:      * Checks authorization.
 293:      * @return void
 294:      */
 295:     public function checkRequirements($element)
 296:     {
 297:         $user = (array) ComponentReflection::parseAnnotation($element, 'User');
 298:         if (in_array('loggedIn', $user, true) && !$this->getUser()->isLoggedIn()) {
 299:             throw new Application\ForbiddenRequestException;
 300:         }
 301:     }
 302: 
 303: 
 304:     /********************* signal handling ****************d*g**/
 305: 
 306: 
 307:     /**
 308:      * @return void
 309:      * @throws BadSignalException
 310:      */
 311:     public function processSignal()
 312:     {
 313:         if ($this->signal === null) {
 314:             return;
 315:         }
 316: 
 317:         $component = $this->signalReceiver === '' ? $this : $this->getComponent($this->signalReceiver, false);
 318:         if ($component === null) {
 319:             throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not found.");
 320: 
 321:         } elseif (!$component instanceof ISignalReceiver) {
 322:             throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not ISignalReceiver implementor.");
 323:         }
 324: 
 325:         $component->signalReceived($this->signal);
 326:         $this->signal = null;
 327:     }
 328: 
 329: 
 330:     /**
 331:      * Returns pair signal receiver and name.
 332:      * @return array|null
 333:      */
 334:     public function getSignal()
 335:     {
 336:         return $this->signal === null ? null : [$this->signalReceiver, $this->signal];
 337:     }
 338: 
 339: 
 340:     /**
 341:      * Checks if the signal receiver is the given one.
 342:      * @param  mixed  component or its id
 343:      * @param  string signal name (optional)
 344:      * @return bool
 345:      */
 346:     public function isSignalReceiver($component, $signal = null)
 347:     {
 348:         if ($component instanceof Nette\ComponentModel\Component) {
 349:             $component = $component === $this ? '' : $component->lookupPath(__CLASS__, true);
 350:         }
 351: 
 352:         if ($this->signal === null) {
 353:             return false;
 354: 
 355:         } elseif ($signal === true) {
 356:             return $component === ''
 357:                 || strncmp($this->signalReceiver . '-', $component . '-', strlen($component) + 1) === 0;
 358: 
 359:         } elseif ($signal === null) {
 360:             return $this->signalReceiver === $component;
 361: 
 362:         } else {
 363:             return $this->signalReceiver === $component && strcasecmp($signal, $this->signal) === 0;
 364:         }
 365:     }
 366: 
 367: 
 368:     /********************* rendering ****************d*g**/
 369: 
 370: 
 371:     /**
 372:      * Returns current action name.
 373:      * @return string
 374:      */
 375:     public function getAction($fullyQualified = false)
 376:     {
 377:         return $fullyQualified ? ':' . $this->getName() . ':' . $this->action : $this->action;
 378:     }
 379: 
 380: 
 381:     /**
 382:      * Changes current action. Only alphanumeric characters are allowed.
 383:      * @param  string
 384:      * @return void
 385:      */
 386:     public function changeAction($action)
 387:     {
 388:         if (is_string($action) && Nette\Utils\Strings::match($action, '#^[a-zA-Z0-9][a-zA-Z0-9_\x7f-\xff]*\z#')) {
 389:             $this->action = $action;
 390:             $this->view = $action;
 391: 
 392:         } else {
 393:             $this->error('Action name is not alphanumeric string.');
 394:         }
 395:     }
 396: 
 397: 
 398:     /**
 399:      * Returns current view.
 400:      * @return string
 401:      */
 402:     public function getView()
 403:     {
 404:         return $this->view;
 405:     }
 406: 
 407: 
 408:     /**
 409:      * Changes current view. Any name is allowed.
 410:      * @param  string
 411:      * @return static
 412:      */
 413:     public function setView($view)
 414:     {
 415:         $this->view = (string) $view;
 416:         return $this;
 417:     }
 418: 
 419: 
 420:     /**
 421:      * Returns current layout name.
 422:      * @return string|false
 423:      */
 424:     public function getLayout()
 425:     {
 426:         return $this->layout;
 427:     }
 428: 
 429: 
 430:     /**
 431:      * Changes or disables layout.
 432:      * @param  string|false
 433:      * @return static
 434:      */
 435:     public function setLayout($layout)
 436:     {
 437:         $this->layout = $layout === false ? false : (string) $layout;
 438:         return $this;
 439:     }
 440: 
 441: 
 442:     /**
 443:      * @return void
 444:      * @throws Nette\Application\BadRequestException if no template found
 445:      * @throws Nette\Application\AbortException
 446:      */
 447:     public function sendTemplate()
 448:     {
 449:         $template = $this->getTemplate();
 450:         if (!$template->getFile()) {
 451:             $files = $this->formatTemplateFiles();
 452:             foreach ($files as $file) {
 453:                 if (is_file($file)) {
 454:                     $template->setFile($file);
 455:                     break;
 456:                 }
 457:             }
 458: 
 459:             if (!$template->getFile()) {
 460:                 $file = preg_replace('#^.*([/\\\\].{1,70})\z#U', "\xE2\x80\xA6\$1", reset($files));
 461:                 $file = strtr($file, '/', DIRECTORY_SEPARATOR);
 462:                 $this->error("Page not found. Missing template '$file'.");
 463:             }
 464:         }
 465: 
 466:         $this->sendResponse(new Responses\TextResponse($template));
 467:     }
 468: 
 469: 
 470:     /**
 471:      * Finds layout template file name.
 472:      * @return string|null
 473:      * @internal
 474:      */
 475:     public function findLayoutTemplateFile()
 476:     {
 477:         if ($this->layout === false) {
 478:             return;
 479:         }
 480:         $files = $this->formatLayoutTemplateFiles();
 481:         foreach ($files as $file) {
 482:             if (is_file($file)) {
 483:                 return $file;
 484:             }
 485:         }
 486: 
 487:         if ($this->layout) {
 488:             $file = preg_replace('#^.*([/\\\\].{1,70})\z#U', "\xE2\x80\xA6\$1", reset($files));
 489:             $file = strtr($file, '/', DIRECTORY_SEPARATOR);
 490:             throw new Nette\FileNotFoundException("Layout not found. Missing template '$file'.");
 491:         }
 492:     }
 493: 
 494: 
 495:     /**
 496:      * Formats layout template file names.
 497:      * @return array
 498:      */
 499:     public function formatLayoutTemplateFiles()
 500:     {
 501:         if (preg_match('#/|\\\\#', $this->layout)) {
 502:             return [$this->layout];
 503:         }
 504:         list($module, $presenter) = Helpers::splitName($this->getName());
 505:         $layout = $this->layout ? $this->layout : 'layout';
 506:         $dir = dirname($this->getReflection()->getFileName());
 507:         $dir = is_dir("$dir/templates") ? $dir : dirname($dir);
 508:         $list = [
 509:             "$dir/templates/$presenter/@$layout.latte",
 510:             "$dir/templates/$presenter.@$layout.latte",
 511:         ];
 512:         do {
 513:             $list[] = "$dir/templates/@$layout.latte";
 514:             $dir = dirname($dir);
 515:         } while ($dir && $module && (list($module) = Helpers::splitName($module)));
 516:         return $list;
 517:     }
 518: 
 519: 
 520:     /**
 521:      * Formats view template file names.
 522:      * @return array
 523:      */
 524:     public function formatTemplateFiles()
 525:     {
 526:         list(, $presenter) = Helpers::splitName($this->getName());
 527:         $dir = dirname($this->getReflection()->getFileName());
 528:         $dir = is_dir("$dir/templates") ? $dir : dirname($dir);
 529:         return [
 530:             "$dir/templates/$presenter/$this->view.latte",
 531:             "$dir/templates/$presenter.$this->view.latte",
 532:         ];
 533:     }
 534: 
 535: 
 536:     /**
 537:      * Formats action method name.
 538:      * @param  string
 539:      * @return string
 540:      */
 541:     public static function formatActionMethod($action)
 542:     {
 543:         return 'action' . $action;
 544:     }
 545: 
 546: 
 547:     /**
 548:      * Formats render view method name.
 549:      * @param  string
 550:      * @return string
 551:      */
 552:     public static function formatRenderMethod($view)
 553:     {
 554:         return 'render' . $view;
 555:     }
 556: 
 557: 
 558:     /**
 559:      * @return ITemplate
 560:      */
 561:     protected function createTemplate()
 562:     {
 563:         return $this->getTemplateFactory()->createTemplate($this);
 564:     }
 565: 
 566: 
 567:     /********************* partial AJAX rendering ****************d*g**/
 568: 
 569: 
 570:     /**
 571:      * @return \stdClass
 572:      */
 573:     public function getPayload()
 574:     {
 575:         return $this->payload;
 576:     }
 577: 
 578: 
 579:     /**
 580:      * Is AJAX request?
 581:      * @return bool
 582:      */
 583:     public function isAjax()
 584:     {
 585:         if ($this->ajaxMode === null) {
 586:             $this->ajaxMode = $this->httpRequest->isAjax();
 587:         }
 588:         return $this->ajaxMode;
 589:     }
 590: 
 591: 
 592:     /**
 593:      * Sends AJAX payload to the output.
 594:      * @return void
 595:      * @throws Nette\Application\AbortException
 596:      */
 597:     public function sendPayload()
 598:     {
 599:         $this->sendResponse(new Responses\JsonResponse($this->payload));
 600:     }
 601: 
 602: 
 603:     /**
 604:      * Sends JSON data to the output.
 605:      * @param  mixed
 606:      * @return void
 607:      * @throws Nette\Application\AbortException
 608:      */
 609:     public function sendJson($data)
 610:     {
 611:         $this->sendResponse(new Responses\JsonResponse($data));
 612:     }
 613: 
 614: 
 615:     /********************* navigation & flow ****************d*g**/
 616: 
 617: 
 618:     /**
 619:      * Sends response and terminates presenter.
 620:      * @return void
 621:      * @throws Nette\Application\AbortException
 622:      */
 623:     public function sendResponse(Application\IResponse $response)
 624:     {
 625:         $this->response = $response;
 626:         $this->terminate();
 627:     }
 628: 
 629: 
 630:     /**
 631:      * Correctly terminates presenter.
 632:      * @return void
 633:      * @throws Nette\Application\AbortException
 634:      */
 635:     public function terminate()
 636:     {
 637:         throw new Application\AbortException;
 638:     }
 639: 
 640: 
 641:     /**
 642:      * Forward to another presenter or action.
 643:      * @param  string|Nette\Application\Request
 644:      * @param  array|mixed
 645:      * @return void
 646:      * @throws Nette\Application\AbortException
 647:      */
 648:     public function forward($destination, $args = [])
 649:     {
 650:         if ($destination instanceof Application\Request) {
 651:             $this->sendResponse(new Responses\ForwardResponse($destination));
 652:         }
 653: 
 654:         $args = func_num_args() < 3 && is_array($args) ? $args : array_slice(func_get_args(), 1);
 655:         $this->createRequest($this, $destination, $args, 'forward');
 656:         $this->sendResponse(new Responses\ForwardResponse($this->lastCreatedRequest));
 657:     }
 658: 
 659: 
 660:     /**
 661:      * Redirect to another URL and ends presenter execution.
 662:      * @param  string
 663:      * @param  int HTTP error code
 664:      * @return void
 665:      * @throws Nette\Application\AbortException
 666:      */
 667:     public function redirectUrl($url, $httpCode = null)
 668:     {
 669:         if ($this->isAjax()) {
 670:             $this->payload->redirect = (string) $url;
 671:             $this->sendPayload();
 672: 
 673:         } elseif (!$httpCode) {
 674:             $httpCode = $this->httpRequest->isMethod('post')
 675:                 ? Http\IResponse::S303_POST_GET
 676:                 : Http\IResponse::S302_FOUND;
 677:         }
 678:         $this->sendResponse(new Responses\RedirectResponse($url, $httpCode));
 679:     }
 680: 
 681: 
 682:     /**
 683:      * Throws HTTP error.
 684:      * @param  string
 685:      * @param  int HTTP error code
 686:      * @return void
 687:      * @throws Nette\Application\BadRequestException
 688:      */
 689:     public function error($message = null, $httpCode = Http\IResponse::S404_NOT_FOUND)
 690:     {
 691:         throw new Application\BadRequestException($message, $httpCode);
 692:     }
 693: 
 694: 
 695:     /**
 696:      * Link to myself.
 697:      * @return string
 698:      * @deprecated
 699:      */
 700:     public function backlink()
 701:     {
 702:         trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
 703:         return $this->getAction(true);
 704:     }
 705: 
 706: 
 707:     /**
 708:      * Returns the last created Request.
 709:      * @return Nette\Application\Request|null
 710:      * @internal
 711:      */
 712:     public function getLastCreatedRequest()
 713:     {
 714:         return $this->lastCreatedRequest;
 715:     }
 716: 
 717: 
 718:     /**
 719:      * Returns the last created Request flag.
 720:      * @param  string
 721:      * @return bool
 722:      * @internal
 723:      */
 724:     public function getLastCreatedRequestFlag($flag)
 725:     {
 726:         return !empty($this->lastCreatedRequestFlag[$flag]);
 727:     }
 728: 
 729: 
 730:     /**
 731:      * Conditional redirect to canonicalized URI.
 732:      * @param  string
 733:      * @return void
 734:      * @throws Nette\Application\AbortException
 735:      */
 736:     public function canonicalize($destination = null, array $args = [])
 737:     {
 738:         if (!$this->isAjax() && ($this->request->isMethod('get') || $this->request->isMethod('head'))) {
 739:             try {
 740:                 $url = $this->createRequest(
 741:                     $this,
 742:                     $destination ?: $this->action,
 743:                     $args + $this->getGlobalState() + $this->request->getParameters(),
 744:                     'redirectX'
 745:                 );
 746:             } catch (InvalidLinkException $e) {
 747:             }
 748:             if (isset($url) && !$this->httpRequest->getUrl()->isEqual($url)) {
 749:                 $this->sendResponse(new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY));
 750:             }
 751:         }
 752:     }
 753: 
 754: 
 755:     /**
 756:      * Attempts to cache the sent entity by its last modification date.
 757:      * @param  string|int|\DateTimeInterface  last modified time
 758:      * @param  string strong entity tag validator
 759:      * @param  mixed  optional expiration time
 760:      * @return void
 761:      * @throws Nette\Application\AbortException
 762:      */
 763:     public function lastModified($lastModified, $etag = null, $expire = null)
 764:     {
 765:         if ($expire !== null) {
 766:             $this->httpResponse->setExpiration($expire);
 767:         }
 768:         $helper = new Http\Context($this->httpRequest, $this->httpResponse);
 769:         if (!$helper->isModified($lastModified, $etag)) {
 770:             $this->terminate();
 771:         }
 772:     }
 773: 
 774: 
 775:     /**
 776:      * Request/URL factory.
 777:      * @param  Component  base
 778:      * @param  string   destination in format "[//] [[[module:]presenter:]action | signal! | this] [#fragment]"
 779:      * @param  array    array of arguments
 780:      * @param  string   forward|redirect|link
 781:      * @return string|null   URL
 782:      * @throws InvalidLinkException
 783:      * @internal
 784:      */
 785:     protected function createRequest($component, $destination, array $args, $mode)
 786:     {
 787:         // note: createRequest supposes that saveState(), run() & tryCall() behaviour is final
 788: 
 789:         $this->lastCreatedRequest = $this->lastCreatedRequestFlag = null;
 790: 
 791:         // PARSE DESTINATION
 792:         // 1) fragment
 793:         $a = strpos($destination, '#');
 794:         if ($a === false) {
 795:             $fragment = '';
 796:         } else {
 797:             $fragment = substr($destination, $a);
 798:             $destination = substr($destination, 0, $a);
 799:         }
 800: 
 801:         // 2) ?query syntax
 802:         $a = strpos($destination, '?');
 803:         if ($a !== false) {
 804:             parse_str(substr($destination, $a + 1), $args);
 805:             $destination = substr($destination, 0, $a);
 806:         }
 807: 
 808:         // 3) URL scheme
 809:         $a = strpos($destination, '//');
 810:         if ($a === false) {
 811:             $scheme = false;
 812:         } else {
 813:             $scheme = substr($destination, 0, $a);
 814:             $destination = substr($destination, $a + 2);
 815:         }
 816: 
 817:         // 4) signal or empty
 818:         if (!$component instanceof self || substr($destination, -1) === '!') {
 819:             list($cname, $signal) = Helpers::splitName(rtrim($destination, '!'));
 820:             if ($cname !== '') {
 821:                 $component = $component->getComponent(strtr($cname, ':', '-'));
 822:             }
 823:             if ($signal === '') {
 824:                 throw new InvalidLinkException('Signal must be non-empty string.');
 825:             }
 826:             $destination = 'this';
 827:         }
 828: 
 829:         if ($destination == null) {  // intentionally ==
 830:             throw new InvalidLinkException('Destination must be non-empty string.');
 831:         }
 832: 
 833:         // 5) presenter: action
 834:         $current = false;
 835:         list($presenter, $action) = Helpers::splitName($destination);
 836:         if ($presenter === '') {
 837:             $action = $destination === 'this' ? $this->action : $action;
 838:             $presenter = $this->getName();
 839:             $presenterClass = get_class($this);
 840: 
 841:         } else {
 842:             if ($presenter[0] === ':') { // absolute
 843:                 $presenter = substr($presenter, 1);
 844:                 if (!$presenter) {
 845:                     throw new InvalidLinkException("Missing presenter name in '$destination'.");
 846:                 }
 847:             } else { // relative
 848:                 list($module, , $sep) = Helpers::splitName($this->getName());
 849:                 $presenter = $module . $sep . $presenter;
 850:             }
 851:             if (!$this->presenterFactory) {
 852:                 throw new Nette\InvalidStateException('Unable to create link to other presenter, service PresenterFactory has not been set.');
 853:             }
 854:             try {
 855:                 $presenterClass = $this->presenterFactory->getPresenterClass($presenter);
 856:             } catch (Application\InvalidPresenterException $e) {
 857:                 throw new InvalidLinkException($e->getMessage(), 0, $e);
 858:             }
 859:         }
 860: 
 861:         // PROCESS SIGNAL ARGUMENTS
 862:         if (isset($signal)) { // $component must be IStatePersistent
 863:             $reflection = new ComponentReflection(get_class($component));
 864:             if ($signal === 'this') { // means "no signal"
 865:                 $signal = '';
 866:                 if (array_key_exists(0, $args)) {
 867:                     throw new InvalidLinkException("Unable to pass parameters to 'this!' signal.");
 868:                 }
 869: 
 870:             } elseif (strpos($signal, self::NAME_SEPARATOR) === false) {
 871:                 // counterpart of signalReceived() & tryCall()
 872:                 $method = $component->formatSignalMethod($signal);
 873:                 if (!$reflection->hasCallableMethod($method)) {
 874:                     throw new InvalidLinkException("Unknown signal '$signal', missing handler {$reflection->getName()}::$method()");
 875:                 }
 876:                 // convert indexed parameters to named
 877:                 self::argsToParams(get_class($component), $method, $args, [], $missing);
 878:             }
 879: 
 880:             // counterpart of IStatePersistent
 881:             if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
 882:                 $component->saveState($args);
 883:             }
 884: 
 885:             if ($args && $component !== $this) {
 886:                 $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
 887:                 foreach ($args as $key => $val) {
 888:                     unset($args[$key]);
 889:                     $args[$prefix . $key] = $val;
 890:                 }
 891:             }
 892:         }
 893: 
 894:         // PROCESS ARGUMENTS
 895:         if (is_subclass_of($presenterClass, __CLASS__)) {
 896:             if ($action === '') {
 897:                 $action = self::DEFAULT_ACTION;
 898:             }
 899: 
 900:             $current = ($action === '*' || strcasecmp($action, $this->action) === 0) && $presenterClass === get_class($this);
 901: 
 902:             $reflection = new ComponentReflection($presenterClass);
 903: 
 904:             // counterpart of run() & tryCall()
 905:             $method = $presenterClass::formatActionMethod($action);
 906:             if (!$reflection->hasCallableMethod($method)) {
 907:                 $method = $presenterClass::formatRenderMethod($action);
 908:                 if (!$reflection->hasCallableMethod($method)) {
 909:                     $method = null;
 910:                 }
 911:             }
 912: 
 913:             // convert indexed parameters to named
 914:             if ($method === null) {
 915:                 if (array_key_exists(0, $args)) {
 916:                     throw new InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method.");
 917:                 }
 918:             } else {
 919:                 self::argsToParams($presenterClass, $method, $args, $destination === 'this' ? $this->params : [], $missing);
 920:             }
 921: 
 922:             // counterpart of IStatePersistent
 923:             if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
 924:                 $this->saveState($args, $reflection);
 925:             }
 926: 
 927:             if ($mode === 'redirect') {
 928:                 $this->saveGlobalState();
 929:             }
 930: 
 931:             $globalState = $this->getGlobalState($destination === 'this' ? null : $presenterClass);
 932:             if ($current && $args) {
 933:                 $tmp = $globalState + $this->params;
 934:                 foreach ($args as $key => $val) {
 935:                     if (http_build_query([$val]) !== (isset($tmp[$key]) ? http_build_query([$tmp[$key]]) : '')) {
 936:                         $current = false;
 937:                         break;
 938:                     }
 939:                 }
 940:             }
 941:             $args += $globalState;
 942:         }
 943: 
 944:         if ($mode !== 'test' && !empty($missing)) {
 945:             foreach ($missing as $rp) {
 946:                 if (!array_key_exists($rp->getName(), $args)) {
 947:                     throw new InvalidLinkException("Missing parameter \${$rp->getName()} required by {$rp->getDeclaringClass()->getName()}::{$rp->getDeclaringFunction()->getName()}()");
 948:                 }
 949:             }
 950:         }
 951: 
 952:         // ADD ACTION & SIGNAL & FLASH
 953:         if ($action) {
 954:             $args[self::ACTION_KEY] = $action;
 955:         }
 956:         if (!empty($signal)) {
 957:             $args[self::SIGNAL_KEY] = $component->getParameterId($signal);
 958:             $current = $current && $args[self::SIGNAL_KEY] === $this->getParameter(self::SIGNAL_KEY);
 959:         }
 960:         if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) {
 961:             $args[self::FLASH_KEY] = $this->getFlashKey();
 962:         }
 963: 
 964:         $this->lastCreatedRequest = new Application\Request($presenter, Application\Request::FORWARD, $args);
 965:         $this->lastCreatedRequestFlag = ['current' => $current];
 966: 
 967:         return $mode === 'forward' || $mode === 'test'
 968:             ? null
 969:             : $this->requestToUrl($this->lastCreatedRequest, $mode === 'link' && $scheme === false && !$this->absoluteUrls) . $fragment;
 970:     }
 971: 
 972: 
 973:     /**
 974:      * Converts Request to URL.
 975:      * @return string
 976:      * @internal
 977:      */
 978:     protected function requestToUrl(Application\Request $request, $relative = null)
 979:     {
 980:         if ($this->refUrlCache === null) {
 981:             $this->refUrlCache = new Http\Url($this->httpRequest->getUrl());
 982:             $this->refUrlCache->setPath($this->httpRequest->getUrl()->getScriptPath());
 983:         }
 984:         if (!$this->router) {
 985:             throw new Nette\InvalidStateException('Unable to generate URL, service Router has not been set.');
 986:         }
 987: 
 988:         $url = $this->router->constructUrl($request, $this->refUrlCache);
 989:         if ($url === null) {
 990:             $params = $request->getParameters();
 991:             unset($params[self::ACTION_KEY]);
 992:             $params = urldecode(http_build_query($params, '', ', '));
 993:             throw new InvalidLinkException("No route for {$request->getPresenterName()}:{$request->getParameter('action')}($params)");
 994:         }
 995: 
 996:         if ($relative === null ? !$this->absoluteUrls : $relative) {
 997:             $hostUrl = $this->refUrlCache->getHostUrl() . '/';
 998:             if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) {
 999:                 $url = substr($url, strlen($hostUrl) - 1);
1000:             }
1001:         }
1002: 
1003:         return $url;
1004:     }
1005: 
1006: 
1007:     /**
1008:      * Converts list of arguments to named parameters.
1009:      * @param  string  class name
1010:      * @param  string  method name
1011:      * @param  array   arguments
1012:      * @param  array   supplemental arguments
1013:      * @param  ReflectionParameter[]  missing arguments
1014:      * @return void
1015:      * @throws InvalidLinkException
1016:      * @internal
1017:      */
1018:     public static function argsToParams($class, $method, &$args, $supplemental = [], &$missing = [])
1019:     {
1020:         $i = 0;
1021:         $rm = new \ReflectionMethod($class, $method);
1022:         foreach ($rm->getParameters() as $param) {
1023:             list($type, $isClass) = ComponentReflection::getParameterType($param);
1024:             $name = $param->getName();
1025: 
1026:             if (array_key_exists($i, $args)) {
1027:                 $args[$name] = $args[$i];
1028:                 unset($args[$i]);
1029:                 $i++;
1030: 
1031:             } elseif (array_key_exists($name, $args)) {
1032:                 // continue with process
1033: 
1034:             } elseif (array_key_exists($name, $supplemental)) {
1035:                 $args[$name] = $supplemental[$name];
1036:             }
1037: 
1038:             if (!isset($args[$name])) {
1039:                 if (!$param->isDefaultValueAvailable() && !$param->allowsNull() && $type !== 'NULL' && $type !== 'array') {
1040:                     $missing[] = $param;
1041:                     unset($args[$name]);
1042:                 }
1043:                 continue;
1044:             }
1045: 
1046:             if (!ComponentReflection::convertType($args[$name], $type, $isClass)) {
1047:                 throw new InvalidLinkException(sprintf(
1048:                     'Argument $%s passed to %s() must be %s, %s given.',
1049:                     $name,
1050:                     $rm->getDeclaringClass()->getName() . '::' . $rm->getName(),
1051:                     $type === 'NULL' ? 'scalar' : $type,
1052:                     is_object($args[$name]) ? get_class($args[$name]) : gettype($args[$name])
1053:                 ));
1054:             }
1055: 
1056:             $def = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null;
1057:             if ($args[$name] === $def || ($def === null && $args[$name] === '')) {
1058:                 $args[$name] = null; // value transmit is unnecessary
1059:             }
1060:         }
1061: 
1062:         if (array_key_exists($i, $args)) {
1063:             throw new InvalidLinkException("Passed more parameters than method $class::{$rm->getName()}() expects.");
1064:         }
1065:     }
1066: 
1067: 
1068:     /**
1069:      * Invalid link handler. Descendant can override this method to change default behaviour.
1070:      * @return string
1071:      * @throws InvalidLinkException
1072:      */
1073:     protected function handleInvalidLink(InvalidLinkException $e)
1074:     {
1075:         if ($this->invalidLinkMode & self::INVALID_LINK_EXCEPTION) {
1076:             throw $e;
1077:         } elseif ($this->invalidLinkMode & self::INVALID_LINK_WARNING) {
1078:             trigger_error('Invalid link: ' . $e->getMessage(), E_USER_WARNING);
1079:         }
1080:         return $this->invalidLinkMode & self::INVALID_LINK_TEXTUAL
1081:             ? '#error: ' . $e->getMessage()
1082:             : '#';
1083:     }
1084: 
1085: 
1086:     /********************* request serialization ****************d*g**/
1087: 
1088: 
1089:     /**
1090:      * Stores current request to session.
1091:      * @param  mixed  optional expiration time
1092:      * @return string key
1093:      */
1094:     public function storeRequest($expiration = '+ 10 minutes')
1095:     {
1096:         $session = $this->getSession('Nette.Application/requests');
1097:         do {
1098:             $key = Nette\Utils\Random::generate(5);
1099:         } while (isset($session[$key]));
1100: 
1101:         $session[$key] = [$this->user ? $this->user->getId() : null, $this->request];
1102:         $session->setExpiration($expiration, $key);
1103:         return $key;
1104:     }
1105: 
1106: 
1107:     /**
1108:      * Restores request from session.
1109:      * @param  string key
1110:      * @return void
1111:      */
1112:     public function restoreRequest($key)
1113:     {
1114:         $session = $this->getSession('Nette.Application/requests');
1115:         if (!isset($session[$key]) || ($session[$key][0] !== null && $session[$key][0] !== $this->getUser()->getId())) {
1116:             return;
1117:         }
1118:         $request = clone $session[$key][1];
1119:         unset($session[$key]);
1120:         $request->setFlag(Application\Request::RESTORED, true);
1121:         $params = $request->getParameters();
1122:         $params[self::FLASH_KEY] = $this->getFlashKey();
1123:         $request->setParameters($params);
1124:         $this->sendResponse(new Responses\ForwardResponse($request));
1125:     }
1126: 
1127: 
1128:     /********************* interface IStatePersistent ****************d*g**/
1129: 
1130: 
1131:     /**
1132:      * Returns array of persistent components.
1133:      * This default implementation detects components by class-level annotation @persistent(cmp1, cmp2).
1134:      * @return array
1135:      */
1136:     public static function getPersistentComponents()
1137:     {
1138:         return (array) ComponentReflection::parseAnnotation(new \ReflectionClass(get_called_class()), 'persistent');
1139:     }
1140: 
1141: 
1142:     /**
1143:      * Saves state information for all subcomponents to $this->globalState.
1144:      * @return array
1145:      */
1146:     protected function getGlobalState($forClass = null)
1147:     {
1148:         $sinces = &$this->globalStateSinces;
1149: 
1150:         if ($this->globalState === null) {
1151:             $state = [];
1152:             foreach ($this->globalParams as $id => $params) {
1153:                 $prefix = $id . self::NAME_SEPARATOR;
1154:                 foreach ($params as $key => $val) {
1155:                     $state[$prefix . $key] = $val;
1156:                 }
1157:             }
1158:             $this->saveState($state, $forClass ? new ComponentReflection($forClass) : null);
1159: 
1160:             if ($sinces === null) {
1161:                 $sinces = [];
1162:                 foreach ($this->getReflection()->getPersistentParams() as $name => $meta) {
1163:                     $sinces[$name] = $meta['since'];
1164:                 }
1165:             }
1166: 
1167:             $components = $this->getReflection()->getPersistentComponents();
1168:             $iterator = $this->getComponents(true);
1169: 
1170:             foreach ($iterator as $name => $component) {
1171:                 if ($iterator->getDepth() === 0) {
1172:                     // counts with Nette\Application\RecursiveIteratorIterator::SELF_FIRST
1173:                     $since = isset($components[$name]['since']) ? $components[$name]['since'] : false; // false = nonpersistent
1174:                 }
1175:                 if (!$component instanceof IStatePersistent) {
1176:                     continue;
1177:                 }
1178:                 $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
1179:                 $params = [];
1180:                 $component->saveState($params);
1181:                 foreach ($params as $key => $val) {
1182:                     $state[$prefix . $key] = $val;
1183:                     $sinces[$prefix . $key] = $since;
1184:                 }
1185:             }
1186: 
1187:         } else {
1188:             $state = $this->globalState;
1189:         }
1190: 
1191:         if ($forClass !== null) {
1192:             $since = null;
1193:             foreach ($state as $key => $foo) {
1194:                 if (!isset($sinces[$key])) {
1195:                     $x = strpos($key, self::NAME_SEPARATOR);
1196:                     $x = $x === false ? $key : substr($key, 0, $x);
1197:                     $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : false;
1198:                 }
1199:                 if ($since !== $sinces[$key]) {
1200:                     $since = $sinces[$key];
1201:                     $ok = $since && is_a($forClass, $since, true);
1202:                 }
1203:                 if (!$ok) {
1204:                     unset($state[$key]);
1205:                 }
1206:             }
1207:         }
1208: 
1209:         return $state;
1210:     }
1211: 
1212: 
1213:     /**
1214:      * Saves state informations for next request.
1215:      */
1216:     public function saveState(array &$params, ComponentReflection $reflection = null)
1217:     {
1218:         $reflection = $reflection ?: $this->getReflection();
1219:         $reflection->saveState($this, $params);
1220:     }
1221: 
1222: 
1223:     /**
1224:      * Permanently saves state information for all subcomponents to $this->globalState.
1225:      * @return void
1226:      */
1227:     protected function saveGlobalState()
1228:     {
1229:         $this->globalParams = [];
1230:         $this->globalState = $this->getGlobalState();
1231:     }
1232: 
1233: 
1234:     /**
1235:      * Initializes $this->globalParams, $this->signal & $this->signalReceiver, $this->action, $this->view. Called by run().
1236:      * @return void
1237:      * @throws Nette\Application\BadRequestException if action name is not valid
1238:      */
1239:     private function initGlobalParameters()
1240:     {
1241:         // init $this->globalParams
1242:         $this->globalParams = [];
1243:         $selfParams = [];
1244: 
1245:         $params = $this->request->getParameters();
1246:         if (($tmp = $this->request->getPost('_' . self::SIGNAL_KEY)) !== null) {
1247:             $params[self::SIGNAL_KEY] = $tmp;
1248:         } elseif ($this->isAjax()) {
1249:             $params += $this->request->getPost();
1250:             if (($tmp = $this->request->getPost(self::SIGNAL_KEY)) !== null) {
1251:                 $params[self::SIGNAL_KEY] = $tmp;
1252:             }
1253:         }
1254: 
1255:         foreach ($params as $key => $value) {
1256:             if (!preg_match('#^((?:[a-z0-9_]+-)*)((?!\d+\z)[a-z0-9_]+)\z#i', $key, $matches)) {
1257:                 continue;
1258:             } elseif (!$matches[1]) {
1259:                 $selfParams[$key] = $value;
1260:             } else {
1261:                 $this->globalParams[substr($matches[1], 0, -1)][$matches[2]] = $value;
1262:             }
1263:         }
1264: 
1265:         // init & validate $this->action & $this->view
1266:         $this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION);
1267: 
1268:         // init $this->signalReceiver and key 'signal' in appropriate params array
1269:         $this->signalReceiver = $this->getUniqueId();
1270:         if (isset($selfParams[self::SIGNAL_KEY])) {
1271:             $param = $selfParams[self::SIGNAL_KEY];
1272:             if (!is_string($param)) {
1273:                 $this->error('Signal name is not string.');
1274:             }
1275:             $pos = strrpos($param, '-');
1276:             if ($pos) {
1277:                 $this->signalReceiver = substr($param, 0, $pos);
1278:                 $this->signal = substr($param, $pos + 1);
1279:             } else {
1280:                 $this->signalReceiver = $this->getUniqueId();
1281:                 $this->signal = $param;
1282:             }
1283:             if ($this->signal == null) { // intentionally ==
1284:                 $this->signal = null;
1285:             }
1286:         }
1287: 
1288:         $this->loadState($selfParams);
1289:     }
1290: 
1291: 
1292:     /**
1293:      * Pops parameters for specified component.
1294:      * @param  string  component id
1295:      * @return array
1296:      * @internal
1297:      */
1298:     public function popGlobalParameters($id)
1299:     {
1300:         if (isset($this->globalParams[$id])) {
1301:             $res = $this->globalParams[$id];
1302:             unset($this->globalParams[$id]);
1303:             return $res;
1304: 
1305:         } else {
1306:             return [];
1307:         }
1308:     }
1309: 
1310: 
1311:     /********************* flash session ****************d*g**/
1312: 
1313: 
1314:     /**
1315:      * @return string|null
1316:      */
1317:     private function getFlashKey()
1318:     {
1319:         $flashKey = $this->getParameter(self::FLASH_KEY);
1320:         return is_string($flashKey) && $flashKey !== ''
1321:             ? $flashKey
1322:             : null;
1323:     }
1324: 
1325: 
1326:     /**
1327:      * Checks if a flash session namespace exists.
1328:      * @return bool
1329:      */
1330:     public function hasFlashSession()
1331:     {
1332:         $flashKey = $this->getFlashKey();
1333:         return $flashKey !== null
1334:             && $this->getSession()->hasSection('Nette.Application.Flash/' . $flashKey);
1335:     }
1336: 
1337: 
1338:     /**
1339:      * Returns session namespace provided to pass temporary data between redirects.
1340:      * @return Nette\Http\SessionSection
1341:      */
1342:     public function getFlashSession()
1343:     {
1344:         $flashKey = $this->getFlashKey();
1345:         if ($flashKey === null) {
1346:             $this->params[self::FLASH_KEY] = $flashKey = Nette\Utils\Random::generate(4);
1347:         }
1348:         return $this->getSession('Nette.Application.Flash/' . $flashKey);
1349:     }
1350: 
1351: 
1352:     /********************* services ****************d*g**/
1353: 
1354: 
1355:     public function injectPrimary(Nette\DI\Container $context = null, Application\IPresenterFactory $presenterFactory = null, Application\IRouter $router = null,
1356:         Http\IRequest $httpRequest, Http\IResponse $httpResponse, Http\Session $session = null, Nette\Security\User $user = null, ITemplateFactory $templateFactory = null)
1357:     {
1358:         if ($this->presenterFactory !== null) {
1359:             throw new Nette\InvalidStateException('Method ' . __METHOD__ . ' is intended for initialization and should not be called more than once.');
1360:         }
1361: 
1362:         $this->context = $context;
1363:         $this->presenterFactory = $presenterFactory;
1364:         $this->router = $router;
1365:         $this->httpRequest = $httpRequest;
1366:         $this->httpResponse = $httpResponse;
1367:         $this->session = $session;
1368:         $this->user = $user;
1369:         $this->templateFactory = $templateFactory;
1370:     }
1371: 
1372: 
1373:     /**
1374:      * Gets the context.
1375:      * @return Nette\DI\Container
1376:      * @deprecated
1377:      */
1378:     public function getContext()
1379:     {
1380:         if (!$this->context) {
1381:             throw new Nette\InvalidStateException('Context has not been set.');
1382:         }
1383:         return $this->context;
1384:     }
1385: 
1386: 
1387:     /**
1388:      * @return Nette\Http\IRequest
1389:      */
1390:     public function getHttpRequest()
1391:     {
1392:         return $this->httpRequest;
1393:     }
1394: 
1395: 
1396:     /**
1397:      * @return Nette\Http\IResponse
1398:      */
1399:     public function getHttpResponse()
1400:     {
1401:         return $this->httpResponse;
1402:     }
1403: 
1404: 
1405:     /**
1406:      * @param  string
1407:      * @return Nette\Http\Session|Nette\Http\SessionSection
1408:      */
1409:     public function getSession($namespace = null)
1410:     {
1411:         if (!$this->session) {
1412:             throw new Nette\InvalidStateException('Service Session has not been set.');
1413:         }
1414:         return $namespace === null ? $this->session : $this->session->getSection($namespace);
1415:     }
1416: 
1417: 
1418:     /**
1419:      * @return Nette\Security\User
1420:      */
1421:     public function getUser()
1422:     {
1423:         if (!$this->user) {
1424:             throw new Nette\InvalidStateException('Service User has not been set.');
1425:         }
1426:         return $this->user;
1427:     }
1428: 
1429: 
1430:     /**
1431:      * @return ITemplateFactory
1432:      */
1433:     public function getTemplateFactory()
1434:     {
1435:         if (!$this->templateFactory) {
1436:             throw new Nette\InvalidStateException('Service TemplateFactory has not been set.');
1437:         }
1438:         return $this->templateFactory;
1439:     }
1440: }
1441: 
Nette 2.4-20170829 API API documentation generated by ApiGen 2.8.0