Source for file Presenter.php

Documentation is available at Presenter.php

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