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