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