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