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