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