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\IContainer $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: $component = $this->signalReceiver === '' ? $this : $this->getComponent($this->signalReceiver, FALSE);
307: if ($component === NULL) {
308: throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not found.");
309:
310: } elseif (!$component instanceof ISignalReceiver) {
311: throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not ISignalReceiver implementor.");
312: }
313:
314: $component->signalReceived($this->signal);
315: $this->signal = NULL;
316: }
317:
318:
319:
320: 321: 322: 323:
324: final public function getSignal()
325: {
326: return $this->signal === NULL ? NULL : array($this->signalReceiver, $this->signal);
327: }
328:
329:
330:
331: 332: 333: 334: 335: 336:
337: final public function isSignalReceiver($component, $signal = NULL)
338: {
339: if ($component instanceof Nette\ComponentModel\Component) {
340: $component = $component === $this ? '' : $component->lookupPath(__CLASS__, TRUE);
341: }
342:
343: if ($this->signal === NULL) {
344: return FALSE;
345:
346: } elseif ($signal === TRUE) {
347: return $component === ''
348: || strncmp($this->signalReceiver . '-', $component . '-', strlen($component) + 1) === 0;
349:
350: } elseif ($signal === NULL) {
351: return $this->signalReceiver === $component;
352:
353: } else {
354: return $this->signalReceiver === $component && strcasecmp($signal, $this->signal) === 0;
355: }
356: }
357:
358:
359:
360:
361:
362:
363:
364: 365: 366: 367:
368: final public function getAction($fullyQualified = FALSE)
369: {
370: return $fullyQualified ? ':' . $this->getName() . ':' . $this->action : $this->action;
371: }
372:
373:
374:
375: 376: 377: 378: 379:
380: public function changeAction($action)
381: {
382: if (is_string($action) && Nette\Utils\Strings::match($action, '#^[a-zA-Z0-9][a-zA-Z0-9_\x7f-\xff]*$#')) {
383: $this->action = $action;
384: $this->view = $action;
385:
386: } else {
387: $this->error('Action name is not alphanumeric string.');
388: }
389: }
390:
391:
392:
393: 394: 395: 396:
397: final public function getView()
398: {
399: return $this->view;
400: }
401:
402:
403:
404: 405: 406: 407: 408:
409: public function setView($view)
410: {
411: $this->view = (string) $view;
412: return $this;
413: }
414:
415:
416:
417: 418: 419: 420:
421: final public function getLayout()
422: {
423: return $this->layout;
424: }
425:
426:
427:
428: 429: 430: 431: 432:
433: public function setLayout($layout)
434: {
435: $this->layout = $layout === FALSE ? FALSE : (string) $layout;
436: return $this;
437: }
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 instanceof Nette\Templating\IFileTemplate && !$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})$#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: if ($this->layout !== FALSE) {
470: $files = $this->formatLayoutTemplateFiles();
471: foreach ($files as $file) {
472: if (is_file($file)) {
473: $template->layout = $file;
474: $template->_extends = $file;
475: break;
476: }
477: }
478:
479: if (empty($template->layout) && $this->layout !== NULL) {
480: $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files));
481: $file = strtr($file, '/', DIRECTORY_SEPARATOR);
482: throw new Nette\FileNotFoundException("Layout not found. Missing template '$file'.");
483: }
484: }
485:
486: $this->sendResponse(new Responses\TextResponse($template));
487: }
488:
489:
490:
491: 492: 493: 494:
495: public function formatLayoutTemplateFiles()
496: {
497: $name = $this->getName();
498: $presenter = substr($name, strrpos(':' . $name, ':'));
499: $layout = $this->layout ? $this->layout : 'layout';
500: $dir = dirname(dirname($this->getReflection()->getFileName()));
501: $list = array(
502: "$dir/templates/$presenter/@$layout.latte",
503: "$dir/templates/$presenter.@$layout.latte",
504: "$dir/templates/$presenter/@$layout.phtml",
505: "$dir/templates/$presenter.@$layout.phtml",
506: );
507: do {
508: $list[] = "$dir/templates/@$layout.latte";
509: $list[] = "$dir/templates/@$layout.phtml";
510: $dir = dirname($dir);
511: } while ($dir && ($name = substr($name, 0, strrpos($name, ':'))));
512: return $list;
513: }
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(dirname($this->getReflection()->getFileName()));
526: return array(
527: "$dir/templates/$presenter/$this->view.latte",
528: "$dir/templates/$presenter.$this->view.latte",
529: "$dir/templates/$presenter/$this->view.phtml",
530: "$dir/templates/$presenter.$this->view.phtml",
531: );
532: }
533:
534:
535:
536: 537: 538: 539: 540:
541: protected static function formatActionMethod($action)
542: {
543: return 'action' . $action;
544: }
545:
546:
547:
548: 549: 550: 551: 552:
553: protected static function formatRenderMethod($view)
554: {
555: return 'render' . $view;
556: }
557:
558:
559:
560:
561:
562:
563:
564: 565: 566:
567: final public function getPayload()
568: {
569: return $this->payload;
570: }
571:
572:
573:
574: 575: 576: 577:
578: public function isAjax()
579: {
580: if ($this->ajaxMode === NULL) {
581: $this->ajaxMode = $this->getHttpRequest()->isAjax();
582: }
583: return $this->ajaxMode;
584: }
585:
586:
587:
588: 589: 590: 591: 592:
593: public function sendPayload()
594: {
595: $this->sendResponse(new Responses\JsonResponse($this->payload));
596: }
597:
598:
599:
600:
601:
602:
603:
604: 605: 606: 607: 608: 609:
610: public function sendResponse(Application\IResponse $response)
611: {
612: $this->response = $response;
613: $this->terminate();
614: }
615:
616:
617:
618: 619: 620: 621: 622:
623: public function terminate()
624: {
625: if (func_num_args() !== 0) {
626: trigger_error(__METHOD__ . ' is not intended to send a Application\Response; use sendResponse() instead.', E_USER_WARNING);
627: $this->sendResponse(func_get_arg(0));
628: }
629: throw new Application\AbortException();
630: }
631:
632:
633:
634: 635: 636: 637: 638: 639: 640:
641: public function forward($destination, $args = array())
642: {
643: if ($destination instanceof Application\Request) {
644: $this->sendResponse(new Responses\ForwardResponse($destination));
645:
646: } elseif (!is_array($args)) {
647: $args = func_get_args();
648: array_shift($args);
649: }
650:
651: $this->createRequest($this, $destination, $args, 'forward');
652: $this->sendResponse(new Responses\ForwardResponse($this->lastCreatedRequest));
653: }
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->getHttpRequest()->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: function redirectUri($url, $code = NULL)
680: {
681: trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::redirectUrl() instead.', E_USER_WARNING);
682: $this->redirectUrl($url, $code);
683: }
684:
685:
686:
687: 688: 689: 690: 691: 692: 693:
694: public function error($message = NULL, $code = Http\IResponse::S404_NOT_FOUND)
695: {
696: throw new Application\BadRequestException($message, $code);
697: }
698:
699:
700:
701: 702: 703: 704:
705: public function backlink()
706: {
707: return $this->getAction(TRUE);
708: }
709:
710:
711:
712: 713: 714: 715:
716: public function getLastCreatedRequest()
717: {
718: return $this->lastCreatedRequest;
719: }
720:
721:
722:
723: 724: 725: 726: 727:
728: public function getLastCreatedRequestFlag($flag)
729: {
730: return !empty($this->lastCreatedRequestFlag[$flag]);
731: }
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->getHttpRequest()->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: 762:
763: public function lastModified($lastModified, $etag = NULL, $expire = NULL)
764: {
765: if ($expire !== NULL) {
766: $this->getHttpResponse()->setExpiration($expire);
767: }
768:
769: if (!$this->getHttpContext()->isModified($lastModified, $etag)) {
770: $this->terminate();
771: }
772: }
773:
774:
775:
776: 777: 778: 779: 780: 781: 782: 783: 784: 785:
786: final protected function createRequest($component, $destination, array $args, $mode)
787: {
788:
789:
790:
791: static $presenterFactory, $router, $refUrl;
792: if ($presenterFactory === NULL) {
793: $presenterFactory = $this->getApplication()->getPresenterFactory();
794: $router = $this->getApplication()->getRouter();
795: $refUrl = new Http\Url($this->getHttpRequest()->getUrl());
796: $refUrl->setPath($this->getHttpRequest()->getUrl()->getScriptPath());
797: }
798:
799: $this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL;
800:
801:
802:
803: $a = strpos($destination, '#');
804: if ($a === FALSE) {
805: $fragment = '';
806: } else {
807: $fragment = substr($destination, $a);
808: $destination = substr($destination, 0, $a);
809: }
810:
811:
812: $a = strpos($destination, '?');
813: if ($a !== FALSE) {
814: parse_str(substr($destination, $a + 1), $args);
815: $destination = substr($destination, 0, $a);
816: }
817:
818:
819: $a = strpos($destination, '//');
820: if ($a === FALSE) {
821: $scheme = FALSE;
822: } else {
823: $scheme = substr($destination, 0, $a);
824: $destination = substr($destination, $a + 2);
825: }
826:
827:
828: if (!$component instanceof Presenter || substr($destination, -1) === '!') {
829: $signal = rtrim($destination, '!');
830: $a = strrpos($signal, ':');
831: if ($a !== FALSE) {
832: $component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-'));
833: $signal = (string) substr($signal, $a + 1);
834: }
835: if ($signal == NULL) {
836: throw new InvalidLinkException("Signal must be non-empty string.");
837: }
838: $destination = 'this';
839: }
840:
841: if ($destination == NULL) {
842: throw new InvalidLinkException("Destination must be non-empty string.");
843: }
844:
845:
846: $current = FALSE;
847: $a = strrpos($destination, ':');
848: if ($a === FALSE) {
849: $action = $destination === 'this' ? $this->action : $destination;
850: $presenter = $this->getName();
851: $presenterClass = get_class($this);
852:
853: } else {
854: $action = (string) substr($destination, $a + 1);
855: if ($destination[0] === ':') {
856: if ($a < 2) {
857: throw new InvalidLinkException("Missing presenter name in '$destination'.");
858: }
859: $presenter = substr($destination, 1, $a - 1);
860:
861: } else {
862: $presenter = $this->getName();
863: $b = strrpos($presenter, ':');
864: if ($b === FALSE) {
865: $presenter = substr($destination, 0, $a);
866: } else {
867: $presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a);
868: }
869: }
870: try {
871: $presenterClass = $presenterFactory->getPresenterClass($presenter);
872: } catch (Application\InvalidPresenterException $e) {
873: throw new InvalidLinkException($e->getMessage(), NULL, $e);
874: }
875: }
876:
877:
878: if (isset($signal)) {
879: $reflection = new PresenterComponentReflection(get_class($component));
880: if ($signal === 'this') {
881: $signal = '';
882: if (array_key_exists(0, $args)) {
883: throw new InvalidLinkException("Unable to pass parameters to 'this!' signal.");
884: }
885:
886: } elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) {
887:
888: $method = $component->formatSignalMethod($signal);
889: if (!$reflection->hasCallableMethod($method)) {
890: throw new InvalidLinkException("Unknown signal '$signal', missing handler {$reflection->name}::$method()");
891: }
892: if ($args) {
893: self::argsToParams(get_class($component), $method, $args);
894: }
895: }
896:
897:
898: if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
899: $component->saveState($args);
900: }
901:
902: if ($args && $component !== $this) {
903: $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
904: foreach ($args as $key => $val) {
905: unset($args[$key]);
906: $args[$prefix . $key] = $val;
907: }
908: }
909: }
910:
911:
912: if (is_subclass_of($presenterClass, __CLASS__)) {
913: if ($action === '') {
914: $action = self::DEFAULT_ACTION;
915: }
916:
917: $current = ($action === '*' || strcasecmp($action, $this->action) === 0) && $presenterClass === get_class($this);
918:
919: $reflection = new PresenterComponentReflection($presenterClass);
920: if ($args || $destination === 'this') {
921:
922: $method = $presenterClass::formatActionMethod($action);
923: if (!$reflection->hasCallableMethod($method)) {
924: $method = $presenterClass::formatRenderMethod($action);
925: if (!$reflection->hasCallableMethod($method)) {
926: $method = NULL;
927: }
928: }
929:
930:
931: if ($method === NULL) {
932: if (array_key_exists(0, $args)) {
933: throw new InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method.");
934: }
935:
936: } elseif ($destination === 'this') {
937: self::argsToParams($presenterClass, $method, $args, $this->params);
938:
939: } else {
940: self::argsToParams($presenterClass, $method, $args);
941: }
942: }
943:
944:
945: if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
946: $this->saveState($args, $reflection);
947: }
948:
949: $globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass);
950: if ($current && $args) {
951: $tmp = $globalState + $this->params;
952: foreach ($args as $key => $val) {
953: if ((string) $val !== (isset($tmp[$key]) ? (string) $tmp[$key] : '')) {
954: $current = FALSE;
955: break;
956: }
957: }
958: }
959: $args += $globalState;
960: }
961:
962:
963: $args[self::ACTION_KEY] = $action;
964: if (!empty($signal)) {
965: $args[self::SIGNAL_KEY] = $component->getParameterId($signal);
966: $current = $current && $args[self::SIGNAL_KEY] === $this->getParameter(self::SIGNAL_KEY);
967: }
968: if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) {
969: $args[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
970: }
971:
972: $this->lastCreatedRequest = new Application\Request(
973: $presenter,
974: Application\Request::FORWARD,
975: $args,
976: array(),
977: array()
978: );
979: $this->lastCreatedRequestFlag = array('current' => $current);
980:
981: if ($mode === 'forward') {
982: return;
983: }
984:
985:
986: $url = $router->constructUrl($this->lastCreatedRequest, $refUrl);
987: if ($url === NULL) {
988: unset($args[self::ACTION_KEY]);
989: $params = urldecode(http_build_query($args, NULL, ', '));
990: throw new InvalidLinkException("No route for $presenter:$action($params)");
991: }
992:
993:
994: if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) {
995: $hostUrl = $refUrl->getHostUrl();
996: if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) {
997: $url = substr($url, strlen($hostUrl));
998: }
999: }
1000:
1001: return $url . $fragment;
1002: }
1003:
1004:
1005:
1006: 1007: 1008: 1009: 1010: 1011: 1012: 1013: 1014:
1015: private static function argsToParams($class, $method, & $args, $supplemental = array())
1016: {
1017: $i = 0;
1018: $rm = new \ReflectionMethod($class, $method);
1019: foreach ($rm->getParameters() as $param) {
1020: $name = $param->getName();
1021: if (array_key_exists($i, $args)) {
1022: $args[$name] = $args[$i];
1023: unset($args[$i]);
1024: $i++;
1025:
1026: } elseif (array_key_exists($name, $args)) {
1027:
1028:
1029: } elseif (array_key_exists($name, $supplemental)) {
1030: $args[$name] = $supplemental[$name];
1031:
1032: } else {
1033: continue;
1034: }
1035:
1036:
1037: $def = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL;
1038: $val = $args[$name];
1039: if ($val === NULL) {
1040: continue;
1041: } elseif ($param->isArray() || is_array($def)) {
1042: if (!is_array($val)) {
1043: throw new InvalidLinkException("Invalid value for parameter '$name', expected array.");
1044: }
1045: } elseif ($param->getClass() || is_object($val)) {
1046:
1047: } elseif (!is_scalar($val)) {
1048: throw new InvalidLinkException("Invalid value for parameter '$name', expected scalar.");
1049:
1050: } elseif ($def === NULL) {
1051: if ((string) $val === '') {
1052: $args[$name] = NULL;
1053: }
1054: continue;
1055: } else {
1056: settype($args[$name], gettype($def));
1057: if ((string) $args[$name] !== (string) $val) {
1058: throw new InvalidLinkException("Invalid value for parameter '$name', expected ".gettype($def).".");
1059: }
1060: }
1061:
1062: if ($args[$name] === $def) {
1063: $args[$name] = NULL;
1064: }
1065: }
1066:
1067: if (array_key_exists($i, $args)) {
1068: $method = $rm->getName();
1069: throw new InvalidLinkException("Passed more parameters than method $class::$method() expects.");
1070: }
1071: }
1072:
1073:
1074:
1075: 1076: 1077: 1078: 1079: 1080:
1081: protected function handleInvalidLink($e)
1082: {
1083: if ($this->invalidLinkMode === self::INVALID_LINK_SILENT) {
1084: return '#';
1085:
1086: } elseif ($this->invalidLinkMode === self::INVALID_LINK_WARNING) {
1087: return 'error: ' . $e->getMessage();
1088:
1089: } else {
1090: throw $e;
1091: }
1092: }
1093:
1094:
1095:
1096:
1097:
1098:
1099:
1100: 1101: 1102: 1103: 1104:
1105: public static function getPersistentComponents()
1106: {
1107: return (array) Reflection\ClassType::from(get_called_class())
1108: ->getAnnotation('persistent');
1109: }
1110:
1111:
1112:
1113: 1114: 1115: 1116:
1117: private function getGlobalState($forClass = NULL)
1118: {
1119: $sinces = & $this->globalStateSinces;
1120:
1121: if ($this->globalState === NULL) {
1122: $state = array();
1123: foreach ($this->globalParams as $id => $params) {
1124: $prefix = $id . self::NAME_SEPARATOR;
1125: foreach ($params as $key => $val) {
1126: $state[$prefix . $key] = $val;
1127: }
1128: }
1129: $this->saveState($state, $forClass ? new PresenterComponentReflection($forClass) : NULL);
1130:
1131: if ($sinces === NULL) {
1132: $sinces = array();
1133: foreach ($this->getReflection()->getPersistentParams() as $nm => $meta) {
1134: $sinces[$nm] = $meta['since'];
1135: }
1136: }
1137:
1138: $components = $this->getReflection()->getPersistentComponents();
1139: $iterator = $this->getComponents(TRUE, 'Nette\Application\UI\IStatePersistent');
1140:
1141: foreach ($iterator as $name => $component) {
1142: if ($iterator->getDepth() === 0) {
1143:
1144: $since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE;
1145: }
1146: $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
1147: $params = array();
1148: $component->saveState($params);
1149: foreach ($params as $key => $val) {
1150: $state[$prefix . $key] = $val;
1151: $sinces[$prefix . $key] = $since;
1152: }
1153: }
1154:
1155: } else {
1156: $state = $this->globalState;
1157: }
1158:
1159: if ($forClass !== NULL) {
1160: $since = NULL;
1161: foreach ($state as $key => $foo) {
1162: if (!isset($sinces[$key])) {
1163: $x = strpos($key, self::NAME_SEPARATOR);
1164: $x = $x === FALSE ? $key : substr($key, 0, $x);
1165: $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE;
1166: }
1167: if ($since !== $sinces[$key]) {
1168: $since = $sinces[$key];
1169: $ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since);
1170: }
1171: if (!$ok) {
1172: unset($state[$key]);
1173: }
1174: }
1175: }
1176:
1177: return $state;
1178: }
1179:
1180:
1181:
1182: 1183: 1184: 1185:
1186: protected function saveGlobalState()
1187: {
1188:
1189: foreach ($this->globalParams as $id => $foo) {
1190: $this->getComponent($id, FALSE);
1191: }
1192:
1193: $this->globalParams = array();
1194: $this->globalState = $this->getGlobalState();
1195: }
1196:
1197:
1198:
1199: 1200: 1201: 1202: 1203:
1204: private function initGlobalParameters()
1205: {
1206:
1207: $this->globalParams = array();
1208: $selfParams = array();
1209:
1210: $params = $this->request->getParameters();
1211: if ($this->isAjax()) {
1212: $params += $this->request->getPost();
1213: }
1214:
1215: foreach ($params as $key => $value) {
1216: $a = strlen($key) > 2 ? strrpos($key, self::NAME_SEPARATOR, -2) : FALSE;
1217: if (!$a) {
1218: $selfParams[$key] = $value;
1219: } else {
1220: $this->globalParams[substr($key, 0, $a)][substr($key, $a + 1)] = $value;
1221: }
1222: }
1223:
1224:
1225: $this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION);
1226:
1227:
1228: $this->signalReceiver = $this->getUniqueId();
1229: if (isset($selfParams[self::SIGNAL_KEY])) {
1230: $param = $selfParams[self::SIGNAL_KEY];
1231: if (!is_string($param)) {
1232: $this->error('Signal name is not string.');
1233: }
1234: $pos = strrpos($param, '-');
1235: if ($pos) {
1236: $this->signalReceiver = substr($param, 0, $pos);
1237: $this->signal = substr($param, $pos + 1);
1238: } else {
1239: $this->signalReceiver = $this->getUniqueId();
1240: $this->signal = $param;
1241: }
1242: if ($this->signal == NULL) {
1243: $this->signal = NULL;
1244: }
1245: }
1246:
1247: $this->loadState($selfParams);
1248: }
1249:
1250:
1251:
1252: 1253: 1254: 1255: 1256:
1257: final public function popGlobalParameters($id)
1258: {
1259: if (isset($this->globalParams[$id])) {
1260: $res = $this->globalParams[$id];
1261: unset($this->globalParams[$id]);
1262: return $res;
1263:
1264: } else {
1265: return array();
1266: }
1267: }
1268:
1269:
1270:
1271:
1272:
1273:
1274:
1275: 1276: 1277: 1278:
1279: public function hasFlashSession()
1280: {
1281: return !empty($this->params[self::FLASH_KEY])
1282: && $this->getSession()->hasSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1283: }
1284:
1285:
1286:
1287: 1288: 1289: 1290:
1291: public function getFlashSession()
1292: {
1293: if (empty($this->params[self::FLASH_KEY])) {
1294: $this->params[self::FLASH_KEY] = Nette\Utils\Strings::random(4);
1295: }
1296: return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1297: }
1298:
1299:
1300:
1301:
1302:
1303:
1304:
1305: 1306: 1307: 1308:
1309: final public function getContext()
1310: {
1311: return $this->context;
1312: }
1313:
1314:
1315:
1316: 1317: 1318: 1319: 1320:
1321: final public function getService($name)
1322: {
1323: return $this->context->getService($name);
1324: }
1325:
1326:
1327:
1328: 1329: 1330:
1331: protected function getHttpRequest()
1332: {
1333: return $this->context->getByType('Nette\Http\IRequest');
1334: }
1335:
1336:
1337:
1338: 1339: 1340:
1341: protected function getHttpResponse()
1342: {
1343: return $this->context->getByType('Nette\Http\IResponse');
1344: }
1345:
1346:
1347:
1348: 1349: 1350:
1351: protected function getHttpContext()
1352: {
1353: return $this->context->getByType('Nette\Http\Context');
1354: }
1355:
1356:
1357:
1358: 1359: 1360:
1361: public function getApplication()
1362: {
1363: return $this->context->getByType('Nette\Application\Application');
1364: }
1365:
1366:
1367:
1368: 1369: 1370:
1371: public function getSession($namespace = NULL)
1372: {
1373: $handler = $this->context->getByType('Nette\Http\Session');
1374: return $namespace === NULL ? $handler : $handler->getSection($namespace);
1375: }
1376:
1377:
1378:
1379: 1380: 1381:
1382: public function getUser()
1383: {
1384: return $this->context->getByType('Nette\Security\User');
1385: }
1386:
1387: }
1388: