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