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