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