1: <?php
2:
3: 4: 5: 6: 7:
8:
9:
10:
11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
31: abstract class Presenter extends Control implements IPresenter
32: {
33:
34: const INVALID_LINK_SILENT = 1,
35: INVALID_LINK_WARNING = 2,
36: INVALID_LINK_EXCEPTION = 3;
37:
38:
39: const SIGNAL_KEY = 'do',
40: ACTION_KEY = 'action',
41: FLASH_KEY = '_fid',
42: DEFAULT_ACTION = 'default';
43:
44:
45: public $invalidLinkMode;
46:
47:
48: public $onShutdown;
49:
50:
51: private $request;
52:
53:
54: private $response;
55:
56:
57: public $autoCanonicalize = TRUE;
58:
59:
60: public $absoluteUrls = FALSE;
61:
62:
63: private $globalParams;
64:
65:
66: private $globalState;
67:
68:
69: private $globalStateSinces;
70:
71:
72: private $action;
73:
74:
75: private $view;
76:
77:
78: private $layout;
79:
80:
81: private $payload;
82:
83:
84: private $signalReceiver;
85:
86:
87: private $signal;
88:
89:
90: private $ajaxMode;
91:
92:
93: private $startupCheck;
94:
95:
96: private $lastCreatedRequest;
97:
98:
99: private $lastCreatedRequestFlag;
100:
101:
102: private $context;
103:
104:
105: public function __construct(DIContainer $context = NULL)
106: {
107: $this->context = $context;
108: if ($context && $this->invalidLinkMode === NULL) {
109: $this->invalidLinkMode = $context->parameters['productionMode'] ? self::INVALID_LINK_SILENT : self::INVALID_LINK_WARNING;
110: }
111: }
112:
113:
114: 115: 116:
117: public function getRequest()
118: {
119: return $this->request;
120: }
121:
122:
123: 124: 125: 126:
127: public function getPresenter($need = TRUE)
128: {
129: return $this;
130: }
131:
132:
133: 134: 135: 136:
137: public function getUniqueId()
138: {
139: return '';
140: }
141:
142:
143:
144:
145:
146: 147: 148:
149: public function run(PresenterRequest $request)
150: {
151: try {
152:
153: $this->request = $request;
154: $this->payload = new stdClass;
155: $this->setParent($this->getParent(), $request->getPresenterName());
156:
157: if (!$this->getHttpResponse()->isSent()) {
158: $this->getHttpResponse()->addHeader('Vary', 'X-Requested-With');
159: }
160:
161: $this->initGlobalParameters();
162: $this->checkRequirements($this->getReflection());
163: $this->startup();
164: if (!$this->startupCheck) {
165: $class = $this->getReflection()->getMethod('startup')->getDeclaringClass()->getName();
166: throw new InvalidStateException("Method $class::startup() or its descendant doesn't call parent::startup().");
167: }
168:
169: $this->tryCall($this->formatActionMethod($this->getAction()), $this->params);
170:
171:
172: foreach ($this->globalParams as $id => $foo) {
173: $this->getComponent($id, FALSE);
174: }
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 (AbortException $e) {
203:
204: if ($this->isAjax()) try {
205: $hasPayload = (array) $this->payload; unset($hasPayload['state']);
206: if ($this->response instanceof 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 (AbortException $e) { }
215:
216: if ($this->hasFlashSession()) {
217: $this->getFlashSession()->setExpiration($this->response instanceof 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, TRUE) && !$this->getUser()->isLoggedIn()) {
273: throw new 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 (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: 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: public function isSignalReceiver($component, $signal = NULL)
324: {
325: if ($component instanceof 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: 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) && 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: 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: 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 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 TextResponse($template));
448: }
449:
450:
451: 452: 453: 454: 455:
456: public function findLayoutTemplateFile()
457: {
458: if ($this->layout === FALSE) {
459: return;
460: }
461: $files = $this->formatLayoutTemplateFiles();
462: foreach ($files as $file) {
463: if (is_file($file)) {
464: return $file;
465: }
466: }
467:
468: if ($this->layout) {
469: $file = preg_replace('#^.*([/\\\\].{1,70})\z#U', "\xE2\x80\xA6\$1", reset($files));
470: $file = strtr($file, '/', DIRECTORY_SEPARATOR);
471: throw new FileNotFoundException("Layout not found. Missing template '$file'.");
472: }
473: }
474:
475:
476: 477: 478: 479:
480: public function formatLayoutTemplateFiles()
481: {
482: $name = $this->getName();
483: $presenter = substr($name, strrpos(':' . $name, ':'));
484: $layout = $this->layout ? $this->layout : 'layout';
485: $dir = dirname($this->getReflection()->getFileName());
486: $dir = is_dir("$dir/templates") ? $dir : dirname($dir);
487: $list = array(
488: "$dir/templates/$presenter/@$layout.latte",
489: "$dir/templates/$presenter.@$layout.latte",
490: "$dir/templates/$presenter/@$layout.phtml",
491: "$dir/templates/$presenter.@$layout.phtml",
492: );
493: do {
494: $list[] = "$dir/templates/@$layout.latte";
495: $list[] = "$dir/templates/@$layout.phtml";
496: $dir = dirname($dir);
497: } while ($dir && ($name = substr($name, 0, strrpos($name, ':'))));
498: return $list;
499: }
500:
501:
502: 503: 504: 505:
506: public function formatTemplateFiles()
507: {
508: $name = $this->getName();
509: $presenter = substr($name, strrpos(':' . $name, ':'));
510: $dir = dirname($this->getReflection()->getFileName());
511: $dir = is_dir("$dir/templates") ? $dir : dirname($dir);
512: return array(
513: "$dir/templates/$presenter/$this->view.latte",
514: "$dir/templates/$presenter.$this->view.latte",
515: "$dir/templates/$presenter/$this->view.phtml",
516: "$dir/templates/$presenter.$this->view.phtml",
517: );
518: }
519:
520:
521: 522: 523: 524: 525:
526: protected static function formatActionMethod($action)
527: {
528: return 'action' . $action;
529: }
530:
531:
532: 533: 534: 535: 536:
537: protected static function formatRenderMethod($view)
538: {
539: return 'render' . $view;
540: }
541:
542:
543:
544:
545:
546: 547: 548:
549: public function getPayload()
550: {
551: return $this->payload;
552: }
553:
554:
555: 556: 557: 558:
559: public function isAjax()
560: {
561: if ($this->ajaxMode === NULL) {
562: $this->ajaxMode = $this->getHttpRequest()->isAjax();
563: }
564: return $this->ajaxMode;
565: }
566:
567:
568: 569: 570: 571: 572:
573: public function sendPayload()
574: {
575: $this->sendResponse(new JsonResponse($this->payload));
576: }
577:
578:
579:
580:
581:
582: 583: 584: 585: 586:
587: public function sendResponse(IPresenterResponse $response)
588: {
589: $this->response = $response;
590: $this->terminate();
591: }
592:
593:
594: 595: 596: 597: 598:
599: public function terminate()
600: {
601: if (func_num_args() !== 0) {
602: trigger_error(__METHOD__ . ' is not intended to send a Application\Response; use sendResponse() instead.', E_USER_WARNING);
603: $this->sendResponse(func_get_arg(0));
604: }
605: throw new AbortException();
606: }
607:
608:
609: 610: 611: 612: 613: 614: 615:
616: public function forward($destination, $args = array())
617: {
618: if ($destination instanceof PresenterRequest) {
619: $this->sendResponse(new ForwardResponse($destination));
620: }
621:
622: $_args=func_get_args(); $this->createRequest($this, $destination, is_array($args) ? $args : array_slice($_args, 1), 'forward');
623: $this->sendResponse(new ForwardResponse($this->lastCreatedRequest));
624: }
625:
626:
627: 628: 629: 630: 631: 632: 633:
634: public function redirectUrl($url, $code = NULL)
635: {
636: if ($this->isAjax()) {
637: $this->payload->redirect = (string) $url;
638: $this->sendPayload();
639:
640: } elseif (!$code) {
641: $code = $this->getHttpRequest()->isMethod('post')
642: ? IHttpResponse::S303_POST_GET
643: : IHttpResponse::S302_FOUND;
644: }
645: $this->sendResponse(new RedirectResponse($url, $code));
646: }
647:
648:
649: function redirectUri($url, $code = NULL)
650: {
651: trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::redirectUrl() instead.', E_USER_WARNING);
652: $this->redirectUrl($url, $code);
653: }
654:
655:
656: 657: 658: 659: 660: 661: 662:
663: public function error($message = NULL, $code = IHttpResponse::S404_NOT_FOUND)
664: {
665: throw new BadRequestException($message, $code);
666: }
667:
668:
669: 670: 671: 672:
673: public function backlink()
674: {
675: return $this->getAction(TRUE);
676: }
677:
678:
679: 680: 681: 682: 683:
684: public function getLastCreatedRequest()
685: {
686: return $this->lastCreatedRequest;
687: }
688:
689:
690: 691: 692: 693: 694: 695:
696: public function getLastCreatedRequestFlag($flag)
697: {
698: return !empty($this->lastCreatedRequestFlag[$flag]);
699: }
700:
701:
702: 703: 704: 705: 706:
707: public function canonicalize()
708: {
709: if (!$this->isAjax() && ($this->request->isMethod('get') || $this->request->isMethod('head'))) {
710: try {
711: $url = $this->createRequest($this, $this->action, $this->getGlobalState() + $this->request->getParameters(), 'redirectX');
712: } catch (InvalidLinkException $e) {}
713: if (isset($url) && !$this->getHttpRequest()->getUrl()->isEqual($url)) {
714: $this->sendResponse(new RedirectResponse($url, IHttpResponse::S301_MOVED_PERMANENTLY));
715: }
716: }
717: }
718:
719:
720: 721: 722: 723: 724: 725: 726: 727: 728:
729: public function lastModified($lastModified, $etag = NULL, $expire = NULL)
730: {
731: if ($expire !== NULL) {
732: $this->getHttpResponse()->setExpiration($expire);
733: }
734:
735: if (!$this->getHttpContext()->isModified($lastModified, $etag)) {
736: $this->terminate();
737: }
738: }
739:
740:
741: 742: 743: 744: 745: 746: 747: 748: 749: 750:
751: protected function createRequest($component, $destination, array $args, $mode)
752: {
753:
754:
755:
756: static $presenterFactory, $router, $refUrl;
757: if ($presenterFactory === NULL) {
758: $presenterFactory = $this->getApplication()->getPresenterFactory();
759: $router = $this->getApplication()->getRouter();
760: $refUrl = new Url($this->getHttpRequest()->getUrl());
761: $refUrl->setPath($this->getHttpRequest()->getUrl()->getScriptPath());
762: }
763:
764: $this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL;
765:
766:
767:
768: $a = strpos($destination, '#');
769: if ($a === FALSE) {
770: $fragment = '';
771: } else {
772: $fragment = substr($destination, $a);
773: $destination = substr($destination, 0, $a);
774: }
775:
776:
777: $a = strpos($destination, '?');
778: if ($a !== FALSE) {
779: parse_str(substr($destination, $a + 1), $args);
780: $destination = substr($destination, 0, $a);
781: }
782:
783:
784: $a = strpos($destination, '//');
785: if ($a === FALSE) {
786: $scheme = FALSE;
787: } else {
788: $scheme = substr($destination, 0, $a);
789: $destination = substr($destination, $a + 2);
790: }
791:
792:
793: if (!$component instanceof Presenter || substr($destination, -1) === '!') {
794: $signal = rtrim($destination, '!');
795: $a = strrpos($signal, ':');
796: if ($a !== FALSE) {
797: $component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-'));
798: $signal = (string) substr($signal, $a + 1);
799: }
800: if ($signal == NULL) {
801: throw new InvalidLinkException("Signal must be non-empty string.");
802: }
803: $destination = 'this';
804: }
805:
806: if ($destination == NULL) {
807: throw new InvalidLinkException("Destination must be non-empty string.");
808: }
809:
810:
811: $current = FALSE;
812: $a = strrpos($destination, ':');
813: if ($a === FALSE) {
814: $action = $destination === 'this' ? $this->action : $destination;
815: $presenter = $this->getName();
816: $presenterClass = get_class($this);
817:
818: } else {
819: $action = (string) substr($destination, $a + 1);
820: if ($destination[0] === ':') {
821: if ($a < 2) {
822: throw new InvalidLinkException("Missing presenter name in '$destination'.");
823: }
824: $presenter = substr($destination, 1, $a - 1);
825:
826: } else {
827: $presenter = $this->getName();
828: $b = strrpos($presenter, ':');
829: if ($b === FALSE) {
830: $presenter = substr($destination, 0, $a);
831: } else {
832: $presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a);
833: }
834: }
835: try {
836: $presenterClass = $presenterFactory->getPresenterClass($presenter);
837: } catch (InvalidPresenterException $e) {
838: throw new InvalidLinkException($e->getMessage(), NULL, $e);
839: }
840: }
841:
842:
843: if (isset($signal)) {
844: $reflection = new PresenterComponentReflection(get_class($component));
845: if ($signal === 'this') {
846: $signal = '';
847: if (array_key_exists(0, $args)) {
848: throw new InvalidLinkException("Unable to pass parameters to 'this!' signal.");
849: }
850:
851: } elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) {
852:
853: $method = $component->formatSignalMethod($signal);
854: if (!$reflection->hasCallableMethod($method)) {
855: throw new InvalidLinkException("Unknown signal '$signal', missing handler {$reflection->name}::$method()");
856: }
857: if ($args) {
858: self::argsToParams(get_class($component), $method, $args);
859: }
860: }
861:
862:
863: if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
864: $component->saveState($args);
865: }
866:
867: if ($args && $component !== $this) {
868: $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
869: foreach ($args as $key => $val) {
870: unset($args[$key]);
871: $args[$prefix . $key] = $val;
872: }
873: }
874: }
875:
876:
877: if (is_subclass_of($presenterClass, __CLASS__)) {
878: if ($action === '') {
879: $action = self::DEFAULT_ACTION;
880: }
881:
882: $current = ($action === '*' || strcasecmp($action, $this->action) === 0) && $presenterClass === get_class($this);
883:
884: $reflection = new PresenterComponentReflection($presenterClass);
885: if ($args || $destination === 'this') {
886:
887: $method = call_user_func(array($presenterClass, 'formatActionMethod'), $action);
888: if (!$reflection->hasCallableMethod($method)) {
889: $method = call_user_func(array($presenterClass, 'formatRenderMethod'), $action);
890: if (!$reflection->hasCallableMethod($method)) {
891: $method = NULL;
892: }
893: }
894:
895:
896: if ($method === NULL) {
897: if (array_key_exists(0, $args)) {
898: throw new InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method.");
899: }
900:
901: } elseif ($destination === 'this') {
902: self::argsToParams($presenterClass, $method, $args, $this->params);
903:
904: } else {
905: self::argsToParams($presenterClass, $method, $args);
906: }
907: }
908:
909:
910: if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
911: $this->saveState($args, $reflection);
912: }
913:
914: if ($mode === 'redirect') {
915: $this->saveGlobalState();
916: }
917:
918: $globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass);
919: if ($current && $args) {
920: $tmp = $globalState + $this->params;
921: foreach ($args as $key => $val) {
922: if (http_build_query(array($val)) !== (isset($tmp[$key]) ? http_build_query(array($tmp[$key])) : '')) {
923: $current = FALSE;
924: break;
925: }
926: }
927: }
928: $args += $globalState;
929: }
930:
931:
932: if ($action) {
933: $args[self::ACTION_KEY] = $action;
934: }
935: if (!empty($signal)) {
936: $args[self::SIGNAL_KEY] = $component->getParameterId($signal);
937: $current = $current && $args[self::SIGNAL_KEY] === $this->getParameter(self::SIGNAL_KEY);
938: }
939: if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) {
940: $args[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
941: }
942:
943: $this->lastCreatedRequest = new PresenterRequest(
944: $presenter,
945: PresenterRequest::FORWARD,
946: $args,
947: array(),
948: array()
949: );
950: $this->lastCreatedRequestFlag = array('current' => $current);
951:
952: if ($mode === 'forward' || $mode === 'test') {
953: return;
954: }
955:
956:
957: $url = $router->constructUrl($this->lastCreatedRequest, $refUrl);
958: if ($url === NULL) {
959: unset($args[self::ACTION_KEY]);
960: $params = urldecode(http_build_query($args, NULL, ', '));
961: throw new InvalidLinkException("No route for $presenter:$action($params)");
962: }
963:
964:
965: if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) {
966: $hostUrl = $refUrl->getHostUrl() . '/';
967: if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) {
968: $url = substr($url, strlen($hostUrl) - 1);
969: }
970: }
971:
972: return $url . $fragment;
973: }
974:
975:
976: 977: 978: 979: 980: 981: 982: 983: 984:
985: private static function argsToParams($class, $method, & $args, $supplemental = array())
986: {
987: $i = 0;
988: $rm = new ReflectionMethod($class, $method);
989: foreach ($rm->getParameters() as $param) {
990: $name = $param->getName();
991: if (array_key_exists($i, $args)) {
992: $args[$name] = $args[$i];
993: unset($args[$i]);
994: $i++;
995:
996: } elseif (array_key_exists($name, $args)) {
997:
998:
999: } elseif (array_key_exists($name, $supplemental)) {
1000: $args[$name] = $supplemental[$name];
1001:
1002: } else {
1003: continue;
1004: }
1005:
1006: if ($args[$name] === NULL) {
1007: continue;
1008: }
1009:
1010: $def = $param->isDefaultValueAvailable() && $param->isOptional() ? $param->getDefaultValue() : NULL;
1011: $type = $param->isArray() ? 'array' : gettype($def);
1012: if (!PresenterComponentReflection::convertType($args[$name], $type)) {
1013: throw new InvalidLinkException("Invalid value for parameter '$name' in method $class::$method(), expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
1014: }
1015:
1016: if ($args[$name] === $def || ($def === NULL && is_scalar($args[$name]) && (string) $args[$name] === '')) {
1017: $args[$name] = NULL;
1018: }
1019: }
1020:
1021: if (array_key_exists($i, $args)) {
1022: $method = $rm->getName();
1023: throw new InvalidLinkException("Passed more parameters than method $class::$method() expects.");
1024: }
1025: }
1026:
1027:
1028: 1029: 1030: 1031: 1032:
1033: protected function handleInvalidLink(InvalidLinkException $e)
1034: {
1035: if ($this->invalidLinkMode === self::INVALID_LINK_SILENT) {
1036: return '#';
1037:
1038: } elseif ($this->invalidLinkMode === self::INVALID_LINK_WARNING) {
1039: return 'error: ' . $e->getMessage();
1040:
1041: } else {
1042: throw $e;
1043: }
1044: }
1045:
1046:
1047:
1048:
1049:
1050: 1051: 1052: 1053: 1054:
1055: public function storeRequest($expiration = '+ 10 minutes')
1056: {
1057: $session = $this->getSession('Nette.Application/requests');
1058: do {
1059: $key = Strings::random(5);
1060: } while (isset($session[$key]));
1061:
1062: $session[$key] = array($this->getUser()->getId(), $this->request);
1063: $session->setExpiration($expiration, $key);
1064: return $key;
1065: }
1066:
1067:
1068: 1069: 1070: 1071: 1072:
1073: public function restoreRequest($key)
1074: {
1075: $session = $this->getSession('Nette.Application/requests');
1076: if (!isset($session[$key]) || ($session[$key][0] !== NULL && $session[$key][0] !== $this->getUser()->getId())) {
1077: return;
1078: }
1079: $request = clone $session[$key][1];
1080: unset($session[$key]);
1081: $request->setFlag(PresenterRequest::RESTORED, TRUE);
1082: $params = $request->getParameters();
1083: $params[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
1084: $request->setParameters($params);
1085: $this->sendResponse(new ForwardResponse($request));
1086: }
1087:
1088:
1089:
1090:
1091:
1092: 1093: 1094: 1095: 1096:
1097: public static function getPersistentComponents()
1098: {
1099: return (array) ClassReflection::from(func_get_arg(0))
1100: ->getAnnotation('persistent');
1101: }
1102:
1103:
1104: 1105: 1106: 1107:
1108: private function getGlobalState($forClass = NULL)
1109: {
1110: $sinces = & $this->globalStateSinces;
1111:
1112: if ($this->globalState === NULL) {
1113: $state = array();
1114: foreach ($this->globalParams as $id => $params) {
1115: $prefix = $id . self::NAME_SEPARATOR;
1116: foreach ($params as $key => $val) {
1117: $state[$prefix . $key] = $val;
1118: }
1119: }
1120: $this->saveState($state, $forClass ? new PresenterComponentReflection($forClass) : NULL);
1121:
1122: if ($sinces === NULL) {
1123: $sinces = array();
1124: foreach ($this->getReflection()->getPersistentParams() as $name => $meta) {
1125: $sinces[$name] = $meta['since'];
1126: }
1127: }
1128:
1129: $components = $this->getReflection()->getPersistentComponents();
1130: $iterator = $this->getComponents(TRUE, 'IStatePersistent');
1131:
1132: foreach ($iterator as $name => $component) {
1133: if ($iterator->getDepth() === 0) {
1134:
1135: $since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE;
1136: }
1137: $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
1138: $params = array();
1139: $component->saveState($params);
1140: foreach ($params as $key => $val) {
1141: $state[$prefix . $key] = $val;
1142: $sinces[$prefix . $key] = $since;
1143: }
1144: }
1145:
1146: } else {
1147: $state = $this->globalState;
1148: }
1149:
1150: if ($forClass !== NULL) {
1151: $since = NULL;
1152: foreach ($state as $key => $foo) {
1153: if (!isset($sinces[$key])) {
1154: $x = strpos($key, self::NAME_SEPARATOR);
1155: $x = $x === FALSE ? $key : substr($key, 0, $x);
1156: $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE;
1157: }
1158: if ($since !== $sinces[$key]) {
1159: $since = $sinces[$key];
1160: $ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since);
1161: }
1162: if (!$ok) {
1163: unset($state[$key]);
1164: }
1165: }
1166: }
1167:
1168: return $state;
1169: }
1170:
1171:
1172: 1173: 1174: 1175:
1176: protected function saveGlobalState()
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:
1242: public function popGlobalParameters($id)
1243: {
1244: if (isset($this->globalParams[$id])) {
1245: $res = $this->globalParams[$id];
1246: unset($this->globalParams[$id]);
1247: return $res;
1248:
1249: } else {
1250: return array();
1251: }
1252: }
1253:
1254:
1255:
1256:
1257:
1258: 1259: 1260: 1261:
1262: public function hasFlashSession()
1263: {
1264: return !empty($this->params[self::FLASH_KEY])
1265: && $this->getSession()->hasSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1266: }
1267:
1268:
1269: 1270: 1271: 1272:
1273: public function getFlashSession()
1274: {
1275: if (empty($this->params[self::FLASH_KEY])) {
1276: $this->params[self::FLASH_KEY] = Strings::random(4);
1277: }
1278: return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1279: }
1280:
1281:
1282:
1283:
1284:
1285: 1286: 1287:
1288: public function injectPrimary(DIContainer $context)
1289: {
1290: $this->context = $context;
1291: }
1292:
1293:
1294: 1295: 1296: 1297:
1298: public function getContext()
1299: {
1300: return $this->context;
1301: }
1302:
1303:
1304: 1305: 1306:
1307: public function getService($name)
1308: {
1309: return $this->context->getService($name);
1310: }
1311:
1312:
1313: 1314: 1315:
1316: protected function getHttpRequest()
1317: {
1318: return $this->context->getByType('IHttpRequest');
1319: }
1320:
1321:
1322: 1323: 1324:
1325: protected function getHttpResponse()
1326: {
1327: return $this->context->getByType('IHttpResponse');
1328: }
1329:
1330:
1331: 1332: 1333:
1334: protected function getHttpContext()
1335: {
1336: return $this->context->getByType('HttpContext');
1337: }
1338:
1339:
1340: 1341: 1342:
1343: public function getApplication()
1344: {
1345: return $this->context->getByType('Application');
1346: }
1347:
1348:
1349: 1350: 1351: 1352:
1353: public function getSession($namespace = NULL)
1354: {
1355: $handler = $this->context->getByType('Session');
1356: return $namespace === NULL ? $handler : $handler->getSection($namespace);
1357: }
1358:
1359:
1360: 1361: 1362:
1363: public function getUser()
1364: {
1365: return $this->context->getByType('User');
1366: }
1367:
1368: }
1369: