1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Application\UI;
9:
10: use Nette,
11: Nette\Application,
12: Nette\Application\Responses,
13: Nette\Http,
14: 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) && !$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: public function findLayoutTemplateFile()
474: {
475: if ($this->layout === FALSE) {
476: return;
477: }
478: $files = $this->formatLayoutTemplateFiles();
479: foreach ($files as $file) {
480: if (is_file($file)) {
481: return $file;
482: }
483: }
484:
485: if ($this->layout) {
486: $file = preg_replace('#^.*([/\\\\].{1,70})\z#U', "\xE2\x80\xA6\$1", reset($files));
487: $file = strtr($file, '/', DIRECTORY_SEPARATOR);
488: throw new Nette\FileNotFoundException("Layout not found. Missing template '$file'.");
489: }
490: }
491:
492:
493: 494: 495: 496:
497: public function formatLayoutTemplateFiles()
498: {
499: $name = $this->getName();
500: $presenter = substr($name, strrpos(':' . $name, ':'));
501: $layout = $this->layout ? $this->layout : 'layout';
502: $dir = dirname($this->getReflection()->getFileName());
503: $dir = is_dir("$dir/templates") ? $dir : dirname($dir);
504: $list = array(
505: "$dir/templates/$presenter/@$layout.latte",
506: "$dir/templates/$presenter.@$layout.latte",
507: "$dir/templates/$presenter/@$layout.phtml",
508: "$dir/templates/$presenter.@$layout.phtml",
509: );
510: do {
511: $list[] = "$dir/templates/@$layout.latte";
512: $list[] = "$dir/templates/@$layout.phtml";
513: $dir = dirname($dir);
514: } while ($dir && ($name = substr($name, 0, strrpos($name, ':'))));
515: return $list;
516: }
517:
518:
519: 520: 521: 522:
523: public function formatTemplateFiles()
524: {
525: $name = $this->getName();
526: $presenter = substr($name, strrpos(':' . $name, ':'));
527: $dir = dirname($this->getReflection()->getFileName());
528: $dir = is_dir("$dir/templates") ? $dir : dirname($dir);
529: return array(
530: "$dir/templates/$presenter/$this->view.latte",
531: "$dir/templates/$presenter.$this->view.latte",
532: "$dir/templates/$presenter/$this->view.phtml",
533: "$dir/templates/$presenter.$this->view.phtml",
534: );
535: }
536:
537:
538: 539: 540: 541: 542:
543: public static function formatActionMethod($action)
544: {
545: return 'action' . $action;
546: }
547:
548:
549: 550: 551: 552: 553:
554: public static function formatRenderMethod($view)
555: {
556: return 'render' . $view;
557: }
558:
559:
560:
561:
562:
563: 564: 565:
566: public function getPayload()
567: {
568: return $this->payload;
569: }
570:
571:
572: 573: 574: 575:
576: public function isAjax()
577: {
578: if ($this->ajaxMode === NULL) {
579: $this->ajaxMode = $this->httpRequest->isAjax();
580: }
581: return $this->ajaxMode;
582: }
583:
584:
585: 586: 587: 588: 589:
590: public function sendPayload()
591: {
592: $this->sendResponse(new Responses\JsonResponse($this->payload));
593: }
594:
595:
596: 597: 598: 599: 600: 601:
602: public function sendJson($data)
603: {
604: $this->sendResponse(new Responses\JsonResponse($data));
605: }
606:
607:
608:
609:
610:
611: 612: 613: 614: 615:
616: public function sendResponse(Application\IResponse $response)
617: {
618: $this->response = $response;
619: $this->terminate();
620: }
621:
622:
623: 624: 625: 626: 627:
628: public function terminate()
629: {
630: if (func_num_args() !== 0) {
631: trigger_error(__METHOD__ . ' is not intended to send a Application\Response; use sendResponse() instead.', E_USER_WARNING);
632: $this->sendResponse(func_get_arg(0));
633: }
634: throw new Application\AbortException();
635: }
636:
637:
638: 639: 640: 641: 642: 643: 644:
645: public function forward($destination, $args = array())
646: {
647: if ($destination instanceof Application\Request) {
648: $this->sendResponse(new Responses\ForwardResponse($destination));
649: }
650:
651: $this->createRequest($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1), 'forward');
652: $this->sendResponse(new Responses\ForwardResponse($this->lastCreatedRequest));
653: }
654:
655:
656: 657: 658: 659: 660: 661: 662:
663: public function redirectUrl($url, $code = NULL)
664: {
665: if ($this->isAjax()) {
666: $this->payload->redirect = (string) $url;
667: $this->sendPayload();
668:
669: } elseif (!$code) {
670: $code = $this->httpRequest->isMethod('post')
671: ? Http\IResponse::S303_POST_GET
672: : Http\IResponse::S302_FOUND;
673: }
674: $this->sendResponse(new Responses\RedirectResponse($url, $code));
675: }
676:
677:
678: 679: 680: 681: 682: 683: 684:
685: public function error($message = NULL, $code = Http\IResponse::S404_NOT_FOUND)
686: {
687: throw new Application\BadRequestException($message, $code);
688: }
689:
690:
691: 692: 693: 694:
695: public function backlink()
696: {
697: return $this->getAction(TRUE);
698: }
699:
700:
701: 702: 703: 704:
705: public function getLastCreatedRequest()
706: {
707: return $this->lastCreatedRequest;
708: }
709:
710:
711: 712: 713: 714: 715:
716: public function getLastCreatedRequestFlag($flag)
717: {
718: return !empty($this->lastCreatedRequestFlag[$flag]);
719: }
720:
721:
722: 723: 724: 725: 726:
727: public function canonicalize()
728: {
729: if (!$this->isAjax() && ($this->request->isMethod('get') || $this->request->isMethod('head'))) {
730: try {
731: $url = $this->createRequest($this, $this->action, $this->getGlobalState() + $this->request->getParameters(), 'redirectX');
732: } catch (InvalidLinkException $e) {}
733: if (isset($url) && !$this->httpRequest->getUrl()->isEqual($url)) {
734: $this->sendResponse(new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY));
735: }
736: }
737: }
738:
739:
740: 741: 742: 743: 744: 745: 746: 747:
748: public function lastModified($lastModified, $etag = NULL, $expire = NULL)
749: {
750: if ($expire !== NULL) {
751: $this->httpResponse->setExpiration($expire);
752: }
753:
754: if (!$this->httpContext->isModified($lastModified, $etag)) {
755: $this->terminate();
756: }
757: }
758:
759:
760: 761: 762: 763: 764: 765: 766: 767: 768: 769:
770: protected function createRequest($component, $destination, array $args, $mode)
771: {
772:
773:
774:
775: static $presenterFactory, $router, $refUrl;
776: if ($presenterFactory === NULL) {
777: $presenterFactory = $this->application->getPresenterFactory();
778: $router = $this->application->getRouter();
779: $refUrl = new Http\Url($this->httpRequest->getUrl());
780: $refUrl->setPath($this->httpRequest->getUrl()->getScriptPath());
781: }
782:
783: $this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL;
784:
785:
786:
787: $a = strpos($destination, '#');
788: if ($a === FALSE) {
789: $fragment = '';
790: } else {
791: $fragment = substr($destination, $a);
792: $destination = substr($destination, 0, $a);
793: }
794:
795:
796: $a = strpos($destination, '?');
797: if ($a !== FALSE) {
798: parse_str(substr($destination, $a + 1), $args);
799: $destination = substr($destination, 0, $a);
800: }
801:
802:
803: $a = strpos($destination, '//');
804: if ($a === FALSE) {
805: $scheme = FALSE;
806: } else {
807: $scheme = substr($destination, 0, $a);
808: $destination = substr($destination, $a + 2);
809: }
810:
811:
812: if (!$component instanceof Presenter || substr($destination, -1) === '!') {
813: $signal = rtrim($destination, '!');
814: $a = strrpos($signal, ':');
815: if ($a !== FALSE) {
816: $component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-'));
817: $signal = (string) substr($signal, $a + 1);
818: }
819: if ($signal == NULL) {
820: throw new InvalidLinkException("Signal must be non-empty string.");
821: }
822: $destination = 'this';
823: }
824:
825: if ($destination == NULL) {
826: throw new InvalidLinkException("Destination must be non-empty string.");
827: }
828:
829:
830: $current = FALSE;
831: $a = strrpos($destination, ':');
832: if ($a === FALSE) {
833: $action = $destination === 'this' ? $this->action : $destination;
834: $presenter = $this->getName();
835: $presenterClass = get_class($this);
836:
837: } else {
838: $action = (string) substr($destination, $a + 1);
839: if ($destination[0] === ':') {
840: if ($a < 2) {
841: throw new InvalidLinkException("Missing presenter name in '$destination'.");
842: }
843: $presenter = substr($destination, 1, $a - 1);
844:
845: } else {
846: $presenter = $this->getName();
847: $b = strrpos($presenter, ':');
848: if ($b === FALSE) {
849: $presenter = substr($destination, 0, $a);
850: } else {
851: $presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a);
852: }
853: }
854: try {
855: $presenterClass = $presenterFactory->getPresenterClass($presenter);
856: } catch (Application\InvalidPresenterException $e) {
857: throw new InvalidLinkException($e->getMessage(), NULL, $e);
858: }
859: }
860:
861:
862: if (isset($signal)) {
863: $reflection = new PresenterComponentReflection(get_class($component));
864: if ($signal === 'this') {
865: $signal = '';
866: if (array_key_exists(0, $args)) {
867: throw new InvalidLinkException("Unable to pass parameters to 'this!' signal.");
868: }
869:
870: } elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) {
871:
872: $method = $component->formatSignalMethod($signal);
873: if (!$reflection->hasCallableMethod($method)) {
874: throw new InvalidLinkException("Unknown signal '$signal', missing handler {$reflection->name}::$method()");
875: }
876: if ($args) {
877: self::argsToParams(get_class($component), $method, $args);
878: }
879: }
880:
881:
882: if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
883: $component->saveState($args);
884: }
885:
886: if ($args && $component !== $this) {
887: $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
888: foreach ($args as $key => $val) {
889: unset($args[$key]);
890: $args[$prefix . $key] = $val;
891: }
892: }
893: }
894:
895:
896: if (is_subclass_of($presenterClass, __CLASS__)) {
897: if ($action === '') {
898: $action = self::DEFAULT_ACTION;
899: }
900:
901: $current = ($action === '*' || strcasecmp($action, $this->action) === 0) && $presenterClass === get_class($this);
902:
903: $reflection = new PresenterComponentReflection($presenterClass);
904: if ($args || $destination === 'this') {
905:
906: $method = $presenterClass::formatActionMethod($action);
907: if (!$reflection->hasCallableMethod($method)) {
908: $method = $presenterClass::formatRenderMethod($action);
909: if (!$reflection->hasCallableMethod($method)) {
910: $method = NULL;
911: }
912: }
913:
914:
915: if ($method === NULL) {
916: if (array_key_exists(0, $args)) {
917: throw new InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method.");
918: }
919:
920: } elseif ($destination === 'this') {
921: self::argsToParams($presenterClass, $method, $args, $this->params);
922:
923: } else {
924: self::argsToParams($presenterClass, $method, $args);
925: }
926: }
927:
928:
929: if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
930: $this->saveState($args, $reflection);
931: }
932:
933: if ($mode === 'redirect') {
934: $this->saveGlobalState();
935: }
936:
937: $globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass);
938: if ($current && $args) {
939: $tmp = $globalState + $this->params;
940: foreach ($args as $key => $val) {
941: if (http_build_query(array($val)) !== (isset($tmp[$key]) ? http_build_query(array($tmp[$key])) : '')) {
942: $current = FALSE;
943: break;
944: }
945: }
946: }
947: $args += $globalState;
948: }
949:
950:
951: if ($action) {
952: $args[self::ACTION_KEY] = $action;
953: }
954: if (!empty($signal)) {
955: $args[self::SIGNAL_KEY] = $component->getParameterId($signal);
956: $current = $current && $args[self::SIGNAL_KEY] === $this->getParameter(self::SIGNAL_KEY);
957: }
958: if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) {
959: $args[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
960: }
961:
962: $this->lastCreatedRequest = new Application\Request(
963: $presenter,
964: Application\Request::FORWARD,
965: $args,
966: array(),
967: array()
968: );
969: $this->lastCreatedRequestFlag = array('current' => $current);
970:
971: if ($mode === 'forward' || $mode === 'test') {
972: return;
973: }
974:
975:
976: $url = $router->constructUrl($this->lastCreatedRequest, $refUrl);
977: if ($url === NULL) {
978: unset($args[self::ACTION_KEY]);
979: $params = urldecode(http_build_query($args, NULL, ', '));
980: throw new InvalidLinkException("No route for $presenter:$action($params)");
981: }
982:
983:
984: if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) {
985: $hostUrl = $refUrl->getHostUrl() . '/';
986: if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) {
987: $url = substr($url, strlen($hostUrl) - 1);
988: }
989: }
990:
991: return $url . $fragment;
992: }
993:
994:
995: 996: 997: 998: 999: 1000: 1001: 1002: 1003:
1004: private static function argsToParams($class, $method, & $args, $supplemental = array())
1005: {
1006: $i = 0;
1007: $rm = new \ReflectionMethod($class, $method);
1008: foreach ($rm->getParameters() as $param) {
1009: $name = $param->getName();
1010: if (array_key_exists($i, $args)) {
1011: $args[$name] = $args[$i];
1012: unset($args[$i]);
1013: $i++;
1014:
1015: } elseif (array_key_exists($name, $args)) {
1016:
1017:
1018: } elseif (array_key_exists($name, $supplemental)) {
1019: $args[$name] = $supplemental[$name];
1020:
1021: } else {
1022: continue;
1023: }
1024:
1025: if ($args[$name] === NULL) {
1026: continue;
1027: }
1028:
1029: $def = $param->isDefaultValueAvailable() && $param->isOptional() ? $param->getDefaultValue() : NULL;
1030: $type = $param->isArray() ? 'array' : gettype($def);
1031: if (!PresenterComponentReflection::convertType($args[$name], $type)) {
1032: throw new InvalidLinkException("Invalid value for parameter '$name' in method $class::$method(), expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
1033: }
1034:
1035: if ($args[$name] === $def || ($def === NULL && is_scalar($args[$name]) && (string) $args[$name] === '')) {
1036: $args[$name] = NULL;
1037: }
1038: }
1039:
1040: if (array_key_exists($i, $args)) {
1041: $method = $rm->getName();
1042: throw new InvalidLinkException("Passed more parameters than method $class::$method() expects.");
1043: }
1044: }
1045:
1046:
1047: 1048: 1049: 1050: 1051:
1052: protected function handleInvalidLink(InvalidLinkException $e)
1053: {
1054: if ($this->invalidLinkMode === self::INVALID_LINK_SILENT) {
1055: return '#';
1056:
1057: } elseif ($this->invalidLinkMode === self::INVALID_LINK_WARNING) {
1058: return 'error: ' . $e->getMessage();
1059:
1060: } else {
1061: throw $e;
1062: }
1063: }
1064:
1065:
1066:
1067:
1068:
1069: 1070: 1071: 1072: 1073:
1074: public function storeRequest($expiration = '+ 10 minutes')
1075: {
1076: $session = $this->session->getSection('Nette.Application/requests');
1077: do {
1078: $key = Nette\Utils\Strings::random(5);
1079: } while (isset($session[$key]));
1080:
1081: $session[$key] = array($this->user->getId(), $this->request);
1082: $session->setExpiration($expiration, $key);
1083: return $key;
1084: }
1085:
1086:
1087: 1088: 1089: 1090: 1091:
1092: public function restoreRequest($key)
1093: {
1094: $session = $this->session->getSection('Nette.Application/requests');
1095: if (!isset($session[$key]) || ($session[$key][0] !== NULL && $session[$key][0] !== $this->user->getId())) {
1096: return;
1097: }
1098: $request = clone $session[$key][1];
1099: unset($session[$key]);
1100: $request->setFlag(Application\Request::RESTORED, TRUE);
1101: $params = $request->getParameters();
1102: $params[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
1103: $request->setParameters($params);
1104: $this->sendResponse(new Responses\ForwardResponse($request));
1105: }
1106:
1107:
1108:
1109:
1110:
1111: 1112: 1113: 1114: 1115:
1116: public static function getPersistentComponents()
1117: {
1118: return (array) Reflection\ClassType::from(get_called_class())->getAnnotation('persistent');
1119: }
1120:
1121:
1122: 1123: 1124: 1125:
1126: private function getGlobalState($forClass = NULL)
1127: {
1128: $sinces = & $this->globalStateSinces;
1129:
1130: if ($this->globalState === NULL) {
1131: $state = array();
1132: foreach ($this->globalParams as $id => $params) {
1133: $prefix = $id . self::NAME_SEPARATOR;
1134: foreach ($params as $key => $val) {
1135: $state[$prefix . $key] = $val;
1136: }
1137: }
1138: $this->saveState($state, $forClass ? new PresenterComponentReflection($forClass) : NULL);
1139:
1140: if ($sinces === NULL) {
1141: $sinces = array();
1142: foreach ($this->getReflection()->getPersistentParams() as $name => $meta) {
1143: $sinces[$name] = $meta['since'];
1144: }
1145: }
1146:
1147: $components = $this->getReflection()->getPersistentComponents();
1148: $iterator = $this->getComponents(TRUE, 'Nette\Application\UI\IStatePersistent');
1149:
1150: foreach ($iterator as $name => $component) {
1151: if ($iterator->getDepth() === 0) {
1152:
1153: $since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE;
1154: }
1155: $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
1156: $params = array();
1157: $component->saveState($params);
1158: foreach ($params as $key => $val) {
1159: $state[$prefix . $key] = $val;
1160: $sinces[$prefix . $key] = $since;
1161: }
1162: }
1163:
1164: } else {
1165: $state = $this->globalState;
1166: }
1167:
1168: if ($forClass !== NULL) {
1169: $since = NULL;
1170: foreach ($state as $key => $foo) {
1171: if (!isset($sinces[$key])) {
1172: $x = strpos($key, self::NAME_SEPARATOR);
1173: $x = $x === FALSE ? $key : substr($key, 0, $x);
1174: $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE;
1175: }
1176: if ($since !== $sinces[$key]) {
1177: $since = $sinces[$key];
1178: $ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since);
1179: }
1180: if (!$ok) {
1181: unset($state[$key]);
1182: }
1183: }
1184: }
1185:
1186: return $state;
1187: }
1188:
1189:
1190: 1191: 1192: 1193:
1194: protected function saveGlobalState()
1195: {
1196: $this->globalParams = array();
1197: $this->globalState = $this->getGlobalState();
1198: }
1199:
1200:
1201: 1202: 1203: 1204: 1205:
1206: private function initGlobalParameters()
1207: {
1208:
1209: $this->globalParams = array();
1210: $selfParams = array();
1211:
1212: $params = $this->request->getParameters();
1213: if ($this->isAjax()) {
1214: $params += $this->request->getPost();
1215: }
1216:
1217: foreach ($params as $key => $value) {
1218: if (!preg_match('#^((?:[a-z0-9_]+-)*)((?!\d+\z)[a-z0-9_]+)\z#i', $key, $matches)) {
1219: continue;
1220: } elseif (!$matches[1]) {
1221: $selfParams[$key] = $value;
1222: } else {
1223: $this->globalParams[substr($matches[1], 0, -1)][$matches[2]] = $value;
1224: }
1225: }
1226:
1227:
1228: $this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION);
1229:
1230:
1231: $this->signalReceiver = $this->getUniqueId();
1232: if (isset($selfParams[self::SIGNAL_KEY])) {
1233: $param = $selfParams[self::SIGNAL_KEY];
1234: if (!is_string($param)) {
1235: $this->error('Signal name is not string.');
1236: }
1237: $pos = strrpos($param, '-');
1238: if ($pos) {
1239: $this->signalReceiver = substr($param, 0, $pos);
1240: $this->signal = substr($param, $pos + 1);
1241: } else {
1242: $this->signalReceiver = $this->getUniqueId();
1243: $this->signal = $param;
1244: }
1245: if ($this->signal == NULL) {
1246: $this->signal = NULL;
1247: }
1248: }
1249:
1250: $this->loadState($selfParams);
1251: }
1252:
1253:
1254: 1255: 1256: 1257: 1258:
1259: public function popGlobalParameters($id)
1260: {
1261: if (isset($this->globalParams[$id])) {
1262: $res = $this->globalParams[$id];
1263: unset($this->globalParams[$id]);
1264: return $res;
1265:
1266: } else {
1267: return array();
1268: }
1269: }
1270:
1271:
1272:
1273:
1274:
1275: 1276: 1277: 1278:
1279: public function hasFlashSession()
1280: {
1281: return !empty($this->params[self::FLASH_KEY])
1282: && $this->session->hasSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1283: }
1284:
1285:
1286: 1287: 1288: 1289:
1290: public function getFlashSession()
1291: {
1292: if (empty($this->params[self::FLASH_KEY])) {
1293: $this->params[self::FLASH_KEY] = Nette\Utils\Strings::random(4);
1294: }
1295: return $this->session->getSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1296: }
1297:
1298:
1299:
1300:
1301:
1302: 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)
1303: {
1304: if ($this->application !== NULL) {
1305: throw new Nette\InvalidStateException("Method " . __METHOD__ . " is intended for initialization and should not be called more than once.");
1306: }
1307:
1308: $this->context = $context;
1309: $this->application = $application;
1310: $this->httpContext = $httpContext;
1311: $this->httpRequest = $httpRequest;
1312: $this->httpResponse = $httpResponse;
1313: $this->session = $session;
1314: $this->user = $user;
1315: }
1316:
1317:
1318: 1319: 1320: 1321: 1322:
1323: public function getContext()
1324: {
1325: return $this->context;
1326: }
1327:
1328:
1329: 1330: 1331:
1332: public function getService($name)
1333: {
1334: trigger_error(__METHOD__ . '() is deprecated; use dependency injection instead.', E_USER_DEPRECATED);
1335: return $this->context->getService($name);
1336: }
1337:
1338:
1339: 1340: 1341:
1342: protected function getHttpRequest()
1343: {
1344: return $this->httpRequest;
1345: }
1346:
1347:
1348: 1349: 1350:
1351: protected function getHttpResponse()
1352: {
1353: return $this->httpResponse;
1354: }
1355:
1356:
1357: 1358: 1359:
1360: protected function getHttpContext()
1361: {
1362: trigger_error(__METHOD__ . '() is deprecated; use dependency injection instead.', E_USER_DEPRECATED);
1363: return $this->httpContext;
1364: }
1365:
1366:
1367: 1368: 1369:
1370: public function getApplication()
1371: {
1372: trigger_error(__METHOD__ . '() is deprecated; use dependency injection instead.', E_USER_DEPRECATED);
1373: return $this->application;
1374: }
1375:
1376:
1377: 1378: 1379: 1380:
1381: public function getSession($namespace = NULL)
1382: {
1383: $handler = $this->session;
1384: return $namespace === NULL ? $handler : $handler->getSection($namespace);
1385: }
1386:
1387:
1388: 1389: 1390:
1391: public function getUser()
1392: {
1393: return $this->user;
1394: }
1395:
1396: }
1397: