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 = NULL)
117: {
118: $this->context = $context;
119: if ($context && $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: public function run(Application\Request $request)
166: {
167: try {
168:
169: $this->request = $request;
170: $this->payload = new \stdClass;
171: $this->setParent($this->getParent(), $request->getPresenterName());
172:
173: $this->initGlobalParameters();
174: $this->checkRequirements($this->getReflection());
175: $this->startup();
176: if (!$this->startupCheck) {
177: $class = $this->getReflection()->getMethod('startup')->getDeclaringClass()->getName();
178: throw new Nette\InvalidStateException("Method $class::startup() or its descendant doesn't call parent::startup().");
179: }
180:
181: $this->tryCall($this->formatActionMethod($this->getAction()), $this->params);
182:
183: if ($this->autoCanonicalize) {
184: $this->canonicalize();
185: }
186: if ($this->getHttpRequest()->isMethod('head')) {
187: $this->terminate();
188: }
189:
190:
191:
192: $this->processSignal();
193:
194:
195: $this->beforeRender();
196:
197: $this->tryCall($this->formatRenderMethod($this->getView()), $this->params);
198: $this->afterRender();
199:
200:
201: $this->saveGlobalState();
202: if ($this->isAjax()) {
203: $this->payload->state = $this->getGlobalState();
204: }
205:
206:
207: $this->sendTemplate();
208:
209: } catch (Application\AbortException $e) {
210:
211: if ($this->isAjax()) try {
212: $hasPayload = (array) $this->payload; unset($hasPayload['state']);
213: if ($this->response instanceof Responses\TextResponse && $this->isControlInvalid()) {
214: $this->snippetMode = TRUE;
215: $this->response->send($this->getHttpRequest(), $this->getHttpResponse());
216: $this->sendPayload();
217:
218: } elseif (!$this->response && $hasPayload) {
219: $this->sendPayload();
220: }
221: } catch (Application\AbortException $e) { }
222:
223: if ($this->hasFlashSession()) {
224: $this->getFlashSession()->setExpiration($this->response instanceof Responses\RedirectResponse ? '+ 30 seconds' : '+ 3 seconds');
225: }
226:
227:
228: $this->onShutdown($this, $this->response);
229: $this->shutdown($this->response);
230:
231: return $this->response;
232: }
233: }
234:
235:
236:
237: 238: 239:
240: protected function startup()
241: {
242: $this->startupCheck = TRUE;
243: }
244:
245:
246:
247: 248: 249: 250:
251: protected function beforeRender()
252: {
253: }
254:
255:
256:
257: 258: 259: 260:
261: protected function afterRender()
262: {
263: }
264:
265:
266:
267: 268: 269: 270:
271: protected function shutdown($response)
272: {
273: }
274:
275:
276:
277: 278: 279: 280:
281: public function checkRequirements($element)
282: {
283: $user = (array) $element->getAnnotation('User');
284: if (in_array('loggedIn', $user) && !$this->getUser()->isLoggedIn()) {
285: throw new Application\ForbiddenRequestException;
286: }
287: }
288:
289:
290:
291:
292:
293:
294:
295: 296: 297: 298:
299: public function processSignal()
300: {
301: if ($this->signal === NULL) {
302: return;
303: }
304:
305: try {
306: $component = $this->signalReceiver === '' ? $this : $this->getComponent($this->signalReceiver, FALSE);
307: } catch (Nette\InvalidArgumentException $e) {}
308:
309: if (isset($e) || $component === NULL) {
310: throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not found.", NULL, isset($e) ? $e : NULL);
311:
312: } elseif (!$component instanceof ISignalReceiver) {
313: throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not ISignalReceiver implementor.");
314: }
315:
316: $component->signalReceived($this->signal);
317: $this->signal = NULL;
318: }
319:
320:
321:
322: 323: 324: 325:
326: final public function getSignal()
327: {
328: return $this->signal === NULL ? NULL : array($this->signalReceiver, $this->signal);
329: }
330:
331:
332:
333: 334: 335: 336: 337: 338:
339: final public function isSignalReceiver($component, $signal = NULL)
340: {
341: if ($component instanceof Nette\ComponentModel\Component) {
342: $component = $component === $this ? '' : $component->lookupPath(__CLASS__, TRUE);
343: }
344:
345: if ($this->signal === NULL) {
346: return FALSE;
347:
348: } elseif ($signal === TRUE) {
349: return $component === ''
350: || strncmp($this->signalReceiver . '-', $component . '-', strlen($component) + 1) === 0;
351:
352: } elseif ($signal === NULL) {
353: return $this->signalReceiver === $component;
354:
355: } else {
356: return $this->signalReceiver === $component && strcasecmp($signal, $this->signal) === 0;
357: }
358: }
359:
360:
361:
362:
363:
364:
365:
366: 367: 368: 369:
370: final public function getAction($fullyQualified = FALSE)
371: {
372: return $fullyQualified ? ':' . $this->getName() . ':' . $this->action : $this->action;
373: }
374:
375:
376:
377: 378: 379: 380: 381:
382: public function changeAction($action)
383: {
384: if (is_string($action) && Nette\Utils\Strings::match($action, '#^[a-zA-Z0-9][a-zA-Z0-9_\x7f-\xff]*\z#')) {
385: $this->action = $action;
386: $this->view = $action;
387:
388: } else {
389: $this->error('Action name is not alphanumeric string.');
390: }
391: }
392:
393:
394:
395: 396: 397: 398:
399: final public function getView()
400: {
401: return $this->view;
402: }
403:
404:
405:
406: 407: 408: 409: 410:
411: public function setView($view)
412: {
413: $this->view = (string) $view;
414: return $this;
415: }
416:
417:
418:
419: 420: 421: 422:
423: final public function getLayout()
424: {
425: return $this->layout;
426: }
427:
428:
429:
430: 431: 432: 433: 434:
435: public function setLayout($layout)
436: {
437: $this->layout = $layout === FALSE ? FALSE : (string) $layout;
438: return $this;
439: }
440:
441:
442:
443: 444: 445: 446: 447:
448: public function sendTemplate()
449: {
450: $template = $this->getTemplate();
451: if (!$template) {
452: return;
453: }
454:
455: if ($template instanceof Nette\Templating\IFileTemplate && !$template->getFile()) {
456: $files = $this->formatTemplateFiles();
457: foreach ($files as $file) {
458: if (is_file($file)) {
459: $template->setFile($file);
460: break;
461: }
462: }
463:
464: if (!$template->getFile()) {
465: $file = preg_replace('#^.*([/\\\\].{1,70})\z#U', "\xE2\x80\xA6\$1", reset($files));
466: $file = strtr($file, '/', DIRECTORY_SEPARATOR);
467: $this->error("Page not found. Missing template '$file'.");
468: }
469: }
470:
471: $this->sendResponse(new Responses\TextResponse($template));
472: }
473:
474:
475:
476: 477: 478: 479:
480: public function findLayoutTemplateFile()
481: {
482: if ($this->layout === FALSE) {
483: return;
484: }
485: $files = $this->formatLayoutTemplateFiles();
486: foreach ($files as $file) {
487: if (is_file($file)) {
488: return $file;
489: }
490: }
491:
492: if ($this->layout) {
493: $file = preg_replace('#^.*([/\\\\].{1,70})\z#U', "\xE2\x80\xA6\$1", reset($files));
494: $file = strtr($file, '/', DIRECTORY_SEPARATOR);
495: throw new Nette\FileNotFoundException("Layout not found. Missing template '$file'.");
496: }
497: }
498:
499:
500:
501: 502: 503: 504:
505: public function formatLayoutTemplateFiles()
506: {
507: $name = $this->getName();
508: $presenter = substr($name, strrpos(':' . $name, ':'));
509: $layout = $this->layout ? $this->layout : 'layout';
510: $dir = dirname($this->getReflection()->getFileName());
511: $dir = is_dir("$dir/templates") ? $dir : dirname($dir);
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($this->getReflection()->getFileName());
537: $dir = is_dir("$dir/templates") ? $dir : dirname($dir);
538: return array(
539: "$dir/templates/$presenter/$this->view.latte",
540: "$dir/templates/$presenter.$this->view.latte",
541: "$dir/templates/$presenter/$this->view.phtml",
542: "$dir/templates/$presenter.$this->view.phtml",
543: );
544: }
545:
546:
547:
548: 549: 550: 551: 552:
553: protected static function formatActionMethod($action)
554: {
555: return 'action' . $action;
556: }
557:
558:
559:
560: 561: 562: 563: 564:
565: protected static function formatRenderMethod($view)
566: {
567: return 'render' . $view;
568: }
569:
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:
590: public function isAjax()
591: {
592: if ($this->ajaxMode === NULL) {
593: $this->ajaxMode = $this->getHttpRequest()->isAjax();
594: }
595: return $this->ajaxMode;
596: }
597:
598:
599:
600: 601: 602: 603: 604:
605: public function sendPayload()
606: {
607: $this->sendResponse(new Responses\JsonResponse($this->payload));
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 (http_build_query(array($val)) !== (isset($tmp[$key]) ? http_build_query(array($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' || $mode === 'test') {
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: if ($args[$name] === NULL) {
1052: continue;
1053: }
1054:
1055: $def = $param->isDefaultValueAvailable() && $param->isOptional() ? $param->getDefaultValue() : NULL;
1056: $type = $param->isArray() ? 'array' : gettype($def);
1057: if (!PresenterComponentReflection::convertType($args[$name], $type)) {
1058: throw new InvalidLinkException("Invalid value for parameter '$name' in method $class::$method(), expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
1059: }
1060:
1061: if ($args[$name] === $def || ($def === NULL && is_scalar($args[$name]) && (string) $args[$name] === '')) {
1062: $args[$name] = NULL;
1063: }
1064: }
1065:
1066: if (array_key_exists($i, $args)) {
1067: $method = $rm->getName();
1068: throw new InvalidLinkException("Passed more parameters than method $class::$method() expects.");
1069: }
1070: }
1071:
1072:
1073:
1074: 1075: 1076: 1077: 1078:
1079: protected function handleInvalidLink(InvalidLinkException $e)
1080: {
1081: if ($this->invalidLinkMode === self::INVALID_LINK_SILENT) {
1082: return '#';
1083:
1084: } elseif ($this->invalidLinkMode === self::INVALID_LINK_WARNING) {
1085: return 'error: ' . $e->getMessage();
1086:
1087: } else {
1088: throw $e;
1089: }
1090: }
1091:
1092:
1093:
1094:
1095:
1096:
1097:
1098: 1099: 1100: 1101: 1102:
1103: public function storeRequest($expiration = '+ 10 minutes')
1104: {
1105: $session = $this->getSession('Nette.Application/requests');
1106: do {
1107: $key = Nette\Utils\Strings::random(5);
1108: } while (isset($session[$key]));
1109:
1110: $session[$key] = array($this->getUser()->getId(), $this->request);
1111: $session->setExpiration($expiration, $key);
1112: return $key;
1113: }
1114:
1115:
1116:
1117: 1118: 1119: 1120: 1121:
1122: public function restoreRequest($key)
1123: {
1124: $session = $this->getSession('Nette.Application/requests');
1125: if (!isset($session[$key]) || ($session[$key][0] !== NULL && $session[$key][0] !== $this->getUser()->getId())) {
1126: return;
1127: }
1128: $request = clone $session[$key][1];
1129: unset($session[$key]);
1130: $request->setFlag(Application\Request::RESTORED, TRUE);
1131: $params = $request->getParameters();
1132: $params[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
1133: $request->setParameters($params);
1134: $this->sendResponse(new Responses\ForwardResponse($request));
1135: }
1136:
1137:
1138:
1139:
1140:
1141:
1142:
1143: 1144: 1145: 1146: 1147:
1148: public static function getPersistentComponents()
1149: {
1150: return (array) Reflection\ClassType::from(get_called_class())
1151: ->getAnnotation('persistent');
1152: }
1153:
1154:
1155:
1156: 1157: 1158: 1159:
1160: private function getGlobalState($forClass = NULL)
1161: {
1162: $sinces = & $this->globalStateSinces;
1163:
1164: if ($this->globalState === NULL) {
1165: $state = array();
1166: foreach ($this->globalParams as $id => $params) {
1167: $prefix = $id . self::NAME_SEPARATOR;
1168: foreach ($params as $key => $val) {
1169: $state[$prefix . $key] = $val;
1170: }
1171: }
1172: $this->saveState($state, $forClass ? new PresenterComponentReflection($forClass) : NULL);
1173:
1174: if ($sinces === NULL) {
1175: $sinces = array();
1176: foreach ($this->getReflection()->getPersistentParams() as $name => $meta) {
1177: $sinces[$name] = $meta['since'];
1178: }
1179: }
1180:
1181: $components = $this->getReflection()->getPersistentComponents();
1182: $iterator = $this->getComponents(TRUE, 'Nette\Application\UI\IStatePersistent');
1183:
1184: foreach ($iterator as $name => $component) {
1185: if ($iterator->getDepth() === 0) {
1186:
1187: $since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE;
1188: }
1189: $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
1190: $params = array();
1191: $component->saveState($params);
1192: foreach ($params as $key => $val) {
1193: $state[$prefix . $key] = $val;
1194: $sinces[$prefix . $key] = $since;
1195: }
1196: }
1197:
1198: } else {
1199: $state = $this->globalState;
1200: }
1201:
1202: if ($forClass !== NULL) {
1203: $since = NULL;
1204: foreach ($state as $key => $foo) {
1205: if (!isset($sinces[$key])) {
1206: $x = strpos($key, self::NAME_SEPARATOR);
1207: $x = $x === FALSE ? $key : substr($key, 0, $x);
1208: $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE;
1209: }
1210: if ($since !== $sinces[$key]) {
1211: $since = $sinces[$key];
1212: $ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since);
1213: }
1214: if (!$ok) {
1215: unset($state[$key]);
1216: }
1217: }
1218: }
1219:
1220: return $state;
1221: }
1222:
1223:
1224:
1225: 1226: 1227: 1228:
1229: protected function saveGlobalState()
1230: {
1231:
1232: foreach ($this->globalParams as $id => $foo) {
1233: $this->getComponent($id, FALSE);
1234: }
1235:
1236: $this->globalParams = array();
1237: $this->globalState = $this->getGlobalState();
1238: }
1239:
1240:
1241:
1242: 1243: 1244: 1245: 1246:
1247: private function initGlobalParameters()
1248: {
1249:
1250: $this->globalParams = array();
1251: $selfParams = array();
1252:
1253: $params = $this->request->getParameters();
1254: if ($this->isAjax()) {
1255: $params += $this->request->getPost();
1256: }
1257:
1258: foreach ($params as $key => $value) {
1259: if (!preg_match('#^((?:[a-z0-9_]+-)*)((?!\d+\z)[a-z0-9_]+)\z#i', $key, $matches)) {
1260: continue;
1261: } elseif (!$matches[1]) {
1262: $selfParams[$key] = $value;
1263: } else {
1264: $this->globalParams[substr($matches[1], 0, -1)][$matches[2]] = $value;
1265: }
1266: }
1267:
1268:
1269: $this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION);
1270:
1271:
1272: $this->signalReceiver = $this->getUniqueId();
1273: if (isset($selfParams[self::SIGNAL_KEY])) {
1274: $param = $selfParams[self::SIGNAL_KEY];
1275: if (!is_string($param)) {
1276: $this->error('Signal name is not string.');
1277: }
1278: $pos = strrpos($param, '-');
1279: if ($pos) {
1280: $this->signalReceiver = substr($param, 0, $pos);
1281: $this->signal = substr($param, $pos + 1);
1282: } else {
1283: $this->signalReceiver = $this->getUniqueId();
1284: $this->signal = $param;
1285: }
1286: if ($this->signal == NULL) {
1287: $this->signal = NULL;
1288: }
1289: }
1290:
1291: $this->loadState($selfParams);
1292: }
1293:
1294:
1295:
1296: 1297: 1298: 1299: 1300:
1301: final public function popGlobalParameters($id)
1302: {
1303: if (isset($this->globalParams[$id])) {
1304: $res = $this->globalParams[$id];
1305: unset($this->globalParams[$id]);
1306: return $res;
1307:
1308: } else {
1309: return array();
1310: }
1311: }
1312:
1313:
1314:
1315:
1316:
1317:
1318:
1319: 1320: 1321: 1322:
1323: public function hasFlashSession()
1324: {
1325: return !empty($this->params[self::FLASH_KEY])
1326: && $this->getSession()->hasSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1327: }
1328:
1329:
1330:
1331: 1332: 1333: 1334:
1335: public function getFlashSession()
1336: {
1337: if (empty($this->params[self::FLASH_KEY])) {
1338: $this->params[self::FLASH_KEY] = Nette\Utils\Strings::random(4);
1339: }
1340: return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1341: }
1342:
1343:
1344:
1345:
1346:
1347:
1348:
1349: 1350: 1351:
1352: final public function injectPrimary(Nette\DI\Container $context)
1353: {
1354: $this->context = $context;
1355: }
1356:
1357:
1358:
1359: 1360: 1361: 1362:
1363: final public function getContext()
1364: {
1365: return $this->context;
1366: }
1367:
1368:
1369:
1370: 1371: 1372:
1373: final public function getService($name)
1374: {
1375: return $this->context->getService($name);
1376: }
1377:
1378:
1379:
1380: 1381: 1382:
1383: protected function getHttpRequest()
1384: {
1385: return $this->context->getByType('Nette\Http\IRequest');
1386: }
1387:
1388:
1389:
1390: 1391: 1392:
1393: protected function getHttpResponse()
1394: {
1395: return $this->context->getByType('Nette\Http\IResponse');
1396: }
1397:
1398:
1399:
1400: 1401: 1402:
1403: protected function getHttpContext()
1404: {
1405: return $this->context->getByType('Nette\Http\Context');
1406: }
1407:
1408:
1409:
1410: 1411: 1412:
1413: public function getApplication()
1414: {
1415: return $this->context->getByType('Nette\Application\Application');
1416: }
1417:
1418:
1419:
1420: 1421: 1422:
1423: public function getSession($namespace = NULL)
1424: {
1425: $handler = $this->context->getByType('Nette\Http\Session');
1426: return $namespace === NULL ? $handler : $handler->getSection($namespace);
1427: }
1428:
1429:
1430:
1431: 1432: 1433:
1434: public function getUser()
1435: {
1436: return $this->context->getByType('Nette\Security\User');
1437: }
1438:
1439: }
1440: