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