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