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 NPresenter extends NControl 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(NDIContainer $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(NPresenterRequest $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 (NAbortException $e) {
203:
204: if ($this->isAjax()) try {
205: $hasPayload = (array) $this->payload; unset($hasPayload['state']);
206: if ($this->response instanceof NTextResponse && $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 (NAbortException $e) { }
215:
216: if ($this->hasFlashSession()) {
217: $this->getFlashSession()->setExpiration($this->response instanceof NRedirectResponse ? '+ 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 NForbiddenRequestException;
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 NBadSignalException("The signal receiver component '$this->signalReceiver' is not found.", NULL, isset($e) ? $e : NULL);
297:
298: } elseif (!$component instanceof ISignalReceiver) {
299: throw new NBadSignalException("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 NComponent) {
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) && NStrings::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 NTextResponse($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 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 NJsonResponse($this->payload));
575: }
576:
577:
578:
579:
580:
581: 582: 583: 584: 585:
586: public function sendResponse(IPresenterResponse $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 NAbortException();
605: }
606:
607:
608: 609: 610: 611: 612: 613: 614:
615: public function forward($destination, $args = array())
616: {
617: if ($destination instanceof NPresenterRequest) {
618: $this->sendResponse(new NForwardResponse($destination));
619: }
620:
621: $_args=func_get_args(); $this->createRequest($this, $destination, is_array($args) ? $args : array_slice($_args, 1), 'forward');
622: $this->sendResponse(new NForwardResponse($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: ? IHttpResponse::S303_POST_GET
642: : IHttpResponse::S302_FOUND;
643: }
644: $this->sendResponse(new NRedirectResponse($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 = IHttpResponse::S404_NOT_FOUND)
663: {
664: throw new NBadRequestException($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 (NInvalidLinkException $e) {}
710: if (isset($url) && !$this->getHttpRequest()->getUrl()->isEqual($url)) {
711: $this->sendResponse(new NRedirectResponse($url, IHttpResponse::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: 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 NUrl($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 NPresenter || 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 NInvalidLinkException("Signal must be non-empty string.");
799: }
800: $destination = 'this';
801: }
802:
803: if ($destination == NULL) {
804: throw new NInvalidLinkException("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 NInvalidLinkException("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 (NInvalidPresenterException $e) {
835: throw new NInvalidLinkException($e->getMessage(), NULL, $e);
836: }
837: }
838:
839:
840: if (isset($signal)) {
841: $reflection = new NPresenterComponentReflection(get_class($component));
842: if ($signal === 'this') {
843: $signal = '';
844: if (array_key_exists(0, $args)) {
845: throw new NInvalidLinkException("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 NInvalidLinkException("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 NPresenterComponentReflection($presenterClass);
882: if ($args || $destination === 'this') {
883:
884: $method = call_user_func(array($presenterClass, 'formatActionMethod'), $action);
885: if (!$reflection->hasCallableMethod($method)) {
886: $method = call_user_func(array($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 NInvalidLinkException("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: if ($action) {
930: $args[self::ACTION_KEY] = $action;
931: }
932: if (!empty($signal)) {
933: $args[self::SIGNAL_KEY] = $component->getParameterId($signal);
934: $current = $current && $args[self::SIGNAL_KEY] === $this->getParameter(self::SIGNAL_KEY);
935: }
936: if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) {
937: $args[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
938: }
939:
940: $this->lastCreatedRequest = new NPresenterRequest(
941: $presenter,
942: NPresenterRequest::FORWARD,
943: $args,
944: array(),
945: array()
946: );
947: $this->lastCreatedRequestFlag = array('current' => $current);
948:
949: if ($mode === 'forward' || $mode === 'test') {
950: return;
951: }
952:
953:
954: $url = $router->constructUrl($this->lastCreatedRequest, $refUrl);
955: if ($url === NULL) {
956: unset($args[self::ACTION_KEY]);
957: $params = urldecode(http_build_query($args, NULL, ', '));
958: throw new NInvalidLinkException("No route for $presenter:$action($params)");
959: }
960:
961:
962: if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) {
963: $hostUrl = $refUrl->getHostUrl() . '/';
964: if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) {
965: $url = substr($url, strlen($hostUrl) - 1);
966: }
967: }
968:
969: return $url . $fragment;
970: }
971:
972:
973: 974: 975: 976: 977: 978: 979: 980: 981:
982: private static function argsToParams($class, $method, & $args, $supplemental = array())
983: {
984: $i = 0;
985: $rm = new ReflectionMethod($class, $method);
986: foreach ($rm->getParameters() as $param) {
987: $name = $param->getName();
988: if (array_key_exists($i, $args)) {
989: $args[$name] = $args[$i];
990: unset($args[$i]);
991: $i++;
992:
993: } elseif (array_key_exists($name, $args)) {
994:
995:
996: } elseif (array_key_exists($name, $supplemental)) {
997: $args[$name] = $supplemental[$name];
998:
999: } else {
1000: continue;
1001: }
1002:
1003: if ($args[$name] === NULL) {
1004: continue;
1005: }
1006:
1007: $def = $param->isDefaultValueAvailable() && $param->isOptional() ? $param->getDefaultValue() : NULL;
1008: $type = $param->isArray() ? 'array' : gettype($def);
1009: if (!NPresenterComponentReflection::convertType($args[$name], $type)) {
1010: throw new NInvalidLinkException("Invalid value for parameter '$name' in method $class::$method(), expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
1011: }
1012:
1013: if ($args[$name] === $def || ($def === NULL && is_scalar($args[$name]) && (string) $args[$name] === '')) {
1014: $args[$name] = NULL;
1015: }
1016: }
1017:
1018: if (array_key_exists($i, $args)) {
1019: $method = $rm->getName();
1020: throw new NInvalidLinkException("Passed more parameters than method $class::$method() expects.");
1021: }
1022: }
1023:
1024:
1025: 1026: 1027: 1028: 1029:
1030: protected function handleInvalidLink(NInvalidLinkException $e)
1031: {
1032: if ($this->invalidLinkMode === self::INVALID_LINK_SILENT) {
1033: return '#';
1034:
1035: } elseif ($this->invalidLinkMode === self::INVALID_LINK_WARNING) {
1036: return 'error: ' . $e->getMessage();
1037:
1038: } else {
1039: throw $e;
1040: }
1041: }
1042:
1043:
1044:
1045:
1046:
1047: 1048: 1049: 1050: 1051:
1052: public function storeRequest($expiration = '+ 10 minutes')
1053: {
1054: $session = $this->getSession('Nette.Application/requests');
1055: do {
1056: $key = NStrings::random(5);
1057: } while (isset($session[$key]));
1058:
1059: $session[$key] = array($this->getUser()->getId(), $this->request);
1060: $session->setExpiration($expiration, $key);
1061: return $key;
1062: }
1063:
1064:
1065: 1066: 1067: 1068: 1069:
1070: public function restoreRequest($key)
1071: {
1072: $session = $this->getSession('Nette.Application/requests');
1073: if (!isset($session[$key]) || ($session[$key][0] !== NULL && $session[$key][0] !== $this->getUser()->getId())) {
1074: return;
1075: }
1076: $request = clone $session[$key][1];
1077: unset($session[$key]);
1078: $request->setFlag(NPresenterRequest::RESTORED, TRUE);
1079: $params = $request->getParameters();
1080: $params[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
1081: $request->setParameters($params);
1082: $this->sendResponse(new NForwardResponse($request));
1083: }
1084:
1085:
1086:
1087:
1088:
1089: 1090: 1091: 1092: 1093:
1094: public static function getPersistentComponents()
1095: {
1096: return (array) NClassReflection::from(func_get_arg(0))
1097: ->getAnnotation('persistent');
1098: }
1099:
1100:
1101: 1102: 1103: 1104:
1105: private function getGlobalState($forClass = NULL)
1106: {
1107: $sinces = & $this->globalStateSinces;
1108:
1109: if ($this->globalState === NULL) {
1110: $state = array();
1111: foreach ($this->globalParams as $id => $params) {
1112: $prefix = $id . self::NAME_SEPARATOR;
1113: foreach ($params as $key => $val) {
1114: $state[$prefix . $key] = $val;
1115: }
1116: }
1117: $this->saveState($state, $forClass ? new NPresenterComponentReflection($forClass) : NULL);
1118:
1119: if ($sinces === NULL) {
1120: $sinces = array();
1121: foreach ($this->getReflection()->getPersistentParams() as $name => $meta) {
1122: $sinces[$name] = $meta['since'];
1123: }
1124: }
1125:
1126: $components = $this->getReflection()->getPersistentComponents();
1127: $iterator = $this->getComponents(TRUE, 'IStatePersistent');
1128:
1129: foreach ($iterator as $name => $component) {
1130: if ($iterator->getDepth() === 0) {
1131:
1132: $since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE;
1133: }
1134: $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
1135: $params = array();
1136: $component->saveState($params);
1137: foreach ($params as $key => $val) {
1138: $state[$prefix . $key] = $val;
1139: $sinces[$prefix . $key] = $since;
1140: }
1141: }
1142:
1143: } else {
1144: $state = $this->globalState;
1145: }
1146:
1147: if ($forClass !== NULL) {
1148: $since = NULL;
1149: foreach ($state as $key => $foo) {
1150: if (!isset($sinces[$key])) {
1151: $x = strpos($key, self::NAME_SEPARATOR);
1152: $x = $x === FALSE ? $key : substr($key, 0, $x);
1153: $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE;
1154: }
1155: if ($since !== $sinces[$key]) {
1156: $since = $sinces[$key];
1157: $ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since);
1158: }
1159: if (!$ok) {
1160: unset($state[$key]);
1161: }
1162: }
1163: }
1164:
1165: return $state;
1166: }
1167:
1168:
1169: 1170: 1171: 1172:
1173: protected function saveGlobalState()
1174: {
1175: $this->globalParams = array();
1176: $this->globalState = $this->getGlobalState();
1177: }
1178:
1179:
1180: 1181: 1182: 1183: 1184:
1185: private function initGlobalParameters()
1186: {
1187:
1188: $this->globalParams = array();
1189: $selfParams = array();
1190:
1191: $params = $this->request->getParameters();
1192: if ($this->isAjax()) {
1193: $params += $this->request->getPost();
1194: }
1195:
1196: foreach ($params as $key => $value) {
1197: if (!preg_match('#^((?:[a-z0-9_]+-)*)((?!\d+\z)[a-z0-9_]+)\z#i', $key, $matches)) {
1198: continue;
1199: } elseif (!$matches[1]) {
1200: $selfParams[$key] = $value;
1201: } else {
1202: $this->globalParams[substr($matches[1], 0, -1)][$matches[2]] = $value;
1203: }
1204: }
1205:
1206:
1207: $this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION);
1208:
1209:
1210: $this->signalReceiver = $this->getUniqueId();
1211: if (isset($selfParams[self::SIGNAL_KEY])) {
1212: $param = $selfParams[self::SIGNAL_KEY];
1213: if (!is_string($param)) {
1214: $this->error('Signal name is not string.');
1215: }
1216: $pos = strrpos($param, '-');
1217: if ($pos) {
1218: $this->signalReceiver = substr($param, 0, $pos);
1219: $this->signal = substr($param, $pos + 1);
1220: } else {
1221: $this->signalReceiver = $this->getUniqueId();
1222: $this->signal = $param;
1223: }
1224: if ($this->signal == NULL) {
1225: $this->signal = NULL;
1226: }
1227: }
1228:
1229: $this->loadState($selfParams);
1230: }
1231:
1232:
1233: 1234: 1235: 1236: 1237:
1238: public function popGlobalParameters($id)
1239: {
1240: if (isset($this->globalParams[$id])) {
1241: $res = $this->globalParams[$id];
1242: unset($this->globalParams[$id]);
1243: return $res;
1244:
1245: } else {
1246: return array();
1247: }
1248: }
1249:
1250:
1251:
1252:
1253:
1254: 1255: 1256: 1257:
1258: public function hasFlashSession()
1259: {
1260: return !empty($this->params[self::FLASH_KEY])
1261: && $this->getSession()->hasSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1262: }
1263:
1264:
1265: 1266: 1267: 1268:
1269: public function getFlashSession()
1270: {
1271: if (empty($this->params[self::FLASH_KEY])) {
1272: $this->params[self::FLASH_KEY] = NStrings::random(4);
1273: }
1274: return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1275: }
1276:
1277:
1278:
1279:
1280:
1281: 1282: 1283:
1284: public function injectPrimary(NDIContainer $context)
1285: {
1286: $this->context = $context;
1287: }
1288:
1289:
1290: 1291: 1292: 1293:
1294: public function getContext()
1295: {
1296: return $this->context;
1297: }
1298:
1299:
1300: 1301: 1302:
1303: public function getService($name)
1304: {
1305: return $this->context->getService($name);
1306: }
1307:
1308:
1309: 1310: 1311:
1312: protected function getHttpRequest()
1313: {
1314: return $this->context->getByType('IHttpRequest');
1315: }
1316:
1317:
1318: 1319: 1320:
1321: protected function getHttpResponse()
1322: {
1323: return $this->context->getByType('IHttpResponse');
1324: }
1325:
1326:
1327: 1328: 1329:
1330: protected function getHttpContext()
1331: {
1332: return $this->context->getByType('NHttpContext');
1333: }
1334:
1335:
1336: 1337: 1338:
1339: public function getApplication()
1340: {
1341: return $this->context->getByType('NApplication');
1342: }
1343:
1344:
1345: 1346: 1347: 1348:
1349: public function getSession($namespace = NULL)
1350: {
1351: $handler = $this->context->getByType('NSession');
1352: return $namespace === NULL ? $handler : $handler->getSection($namespace);
1353: }
1354:
1355:
1356: 1357: 1358:
1359: public function getUser()
1360: {
1361: return $this->context->getByType('NUser');
1362: }
1363:
1364: }
1365: