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