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