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