Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy

Classes

  • Control
  • Form
  • Multiplier
  • Presenter
  • PresenterComponent

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