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