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