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
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Component
  • ComponentReflection
  • Control
  • Form
  • Link
  • MethodReflection
  • Multiplier
  • Presenter

Interfaces

  • IRenderable
  • ISignalReceiver
  • IStatePersistent
  • ITemplate
  • ITemplateFactory

Exceptions

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