$this->backlink() a ErrorPresenter

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
DocX
Člen | 154
+
0
-

Ahoj,

řeším následující problém: v šabloně layoutu mám několik odkazů, které jen mění nějaké parametry a odkazují se zpět na současnou stránku, jako například přepnutí do jiného jazyka {link $backlink, lang=>en}, nebo autentizační formulář. $backlink do templatu nastavuji v BasePresenteru.

Problém nastává ve chvílí, kdy se z nějakého důvodu zobrazí ErrorPresenter (například se 404kou). Aplikace (Application) v tuto chvili vytvoří vnitřne jakoby request na ErrorPresenter a spustí ho. Ten si pak s sebou nese právě zmíněný request. To pak zbůsobí to, že se zmíněné odkazy vygenerují ve tvaru např. ?presenter=Error&lang=en, i když adresa byla např. ?presenter=Blabla.

Působí to hned 2 potíže:

  1. na stránce je odkaz (a akce formuláře) na neexistující stránku
  2. zamezení přístupu ke stránkám, kde musí být uživatel přihlášen mám řešeno přes vyhození vyjímky a následné zpracování ErrorPresenterem. V tom okamžiku nefunguje přihlašovací formulář.

Otázka:

  1. Jak zjistit původní request (ten od klienta, ne ten co je v ErrorPresenteru při jeho běhu) a jeho backlink
  2. Jak backlink na ten původní request předat AppFormu, který si odkaz tvoří sám podle aktuálního presenteru. (když ho zadám přes ->setAction() tak se nevytvoří odkaz na signál).

EDIT: a nebo jak to řešit úplně jinak :)

Děkuji :)

Editoval DocX (20. 8. 2009 15:09)

jasir
Člen | 746
+
0
-

DocX napsal(a):

řeším následující problém: v šabloně layoutu mám několik odkazů, které jen mění nějaké parametry a odkazují se zpět na současnou stránku, jako například přepnutí do jiného jazyka {link $backlink, lang=>en}, nebo autentizační formulář. $backlink do templatu nastavuji v BasePresenteru.

Nemusíš přes backlink, mělo by to jít přes:

{plink this, lang=>en}

Jinak {plink} je odkaz na presenter, {link} je odkaz na komponentu, v layoutu je to presenter.

  1. Jak backlink předat AppFormu, který si odkaz tvoří sám podle aktuálního presenteru. (když ho zadám přes ->setAction() tak se nevytvoří odkaz na signál).

Aktuální presenter v AppFormu získáš pomocí $this->getPresenter() a linky tedy můžeš tvořit $this->getPresenter()->link(...)

Editoval jasir (20. 8. 2009 11:50)

DocX
Člen | 154
+
0
-

jasir napsal(a):

Nemusíš přes backlink, mělo by to jít přes:

{plink this, lang=>en}

nj to jsem nevěděl :)

  1. Jak backlink předat AppFormu, který si odkaz tvoří sám podle aktuálního presenteru. (když ho zadám přes ->setAction() tak se nevytvoří odkaz na signál).

Aktuální presenter v AppFormu získáš pomocí $this->getPresenter() a linky tedy můžeš tvořit $this->getPresenter()->link(...)

Asi jsme se úplně nepochopili :( Já nepotřebuju měnit AppForm, a jak si on tvoří URL do action, je v pohodě.

Ale nejde tady ani tak o konkrétní případy jako jsem uvedl s odkazem a formulářem. Problém prostě je v tom, že při chybě je spuštěn ErrorHandler, který si myslí že na něj bylo přistoupeno přes request Error:. Na to se pak váží všechny linky, které pracují s backlinkem nebo komponenty tvořící signály na aktuální presenter. Vytvoří totiž odkazy s pořadavkem na presenter Error ale ten z pohledu klienta neexistuje – v Application::run() je to speciálně ošetřeno – což je dobře. Jde mi o to, jak donutit ErrorPresenter, aby měl v sobě původní request, který vzešel od klienta/prohlížeče a tím by se všechny odkazy generovali na jeho základě.

DocX
Člen | 154
+
0
-

Nechci být nevděčný, ale tohle mi přijde skoro jako bug.. Myslím, že globální linky (a jiné věci) umístěné v layoutu, které používají backlink, by neměly generovat odkazy na ErrorPresenter, když ten z pohledu uživatele neexistuje.

Co jsem koukal, tak backlink se generuje jednoduše tím, že se slepí název třídy a název action. Nešlo by to udělat tak, že by to nějak vzalo ten originální HttpRequest (který přišel od prohlížeče klienta) a ten to přepracovalo na adresu presenter:action?

Editoval DocX (20. 8. 2009 17:34)

David Grudl
Nette Core | 8228
+
0
-

Originální požadavek lze získat v error presenteru přes $this->application->requests[0]. Jako objekt PresenterRequest a z něj lze vytáhnout jméno presenteru a akce.

Nicméně u formulářů je věc komplikovanější. Nenapadá mě, jak tohle čistě řešit.

DocX
Člen | 154
+
0
-

David Grudl napsal(a):

Originální požadavek lze získat v error presenteru přes $this->application->requests[0]. Jako objekt PresenterRequest a z něj lze vytáhnout jméno presenteru a akce.

Nicméně u formulářů je věc komplikovanější. Nenapadá mě, jak tohle čistě řešit.

Když se dá takhle vytáhnout původní request, možná by stačilo přepsat metodu backlink() v ErrorPresenteru, tak aby vracela link na původní presenter:action… Jdu to ozkoušet ;)

David Grudl
Nette Core | 8228
+
0
-

To zní smysluplně.

DocX
Člen | 154
+
0
-

Tak jak jsem říkal, že by to šlo úpravou $this->backlink(), tak jsem to zkoušel, ale Presenter tvoří veškeré odkazy přimo pomocí $this->link() (i linky ve formulářích).

Nicméně, napadá mě, že by šlo udělat něco jako „nouzový presenter“, který by se nastavil aplikaci stejně jako vlastnost Application::errorPresenter, na který by se právě odkazy bez přesně zadaného presenteru vytvářely, když by byl spuštěn ErrorPresenter. A podle toho patřičně upravit Presenter::link() nebo možná Presenter::createRequest(), ještě nevím jak to půjde.

Zkusím se na to přes víkend kouknout :)

//EDIT: Proč vždycky píši „odkazi“, když jsou to „odkazy“? xD

Editoval DocX (21. 8. 2009 18:28)

DocX
Člen | 154
+
0
-

Tak to mám. Naučil jsem presenter změnit presenter, na který má odkazovat, když link neobsahuje kvalifikovaný název presenteru nebo když je to link na ‚this‘. Ovlivněje to jak klasické linky, tak i ty co se vytváří na AppFormu, což je paráda :)

V Nette jsem relativně začátečník a věcem co jsou v jeho jádru už vůbec moc nerozumím xD. Prosím proto, jestli by jste mi to nezkontrolovali, ale co jsem zkoušel, tak to fungovalo.

<?php

--- /Nette/Application/Presenter.php
+++ /Nette/Application/Presenter.php
@@ -131,6 +131,10 @@
 	/** @var array */
 	private $lastCreatedRequestFlag;

+	/**
+	 * @var string Where link to, when link do not contain presenter name
+	 */
+	private $defaultRequestPresenter = NULL;


@@ -463,6 +467,21 @@
 	}


+	/**
+	 * Change presenter to which link links without defined presenter or links to 'this'
+	 * @param string $presenter Presenter name
+	 * @throws InvalidPresenterException if presenter not found
+	 * @return void
+	 */
+	public function setDefaultRequestPresenter($presenter)
+	{
+		if ($presenter !== NULL)
+			// try to get presenter
+			$this->getApplication()->getPresenterLoader()->getPresenterClass($presenter);
+
+		$this->defaultRequestPresenter = $presenter;
+	}
+

@@ -907,7 +926,7 @@
 		$a = strrpos($destination, ':');
 		if ($a === FALSE) {
 			$action = $destination === 'this' ? $this->action : $destination;
-			$presenter = $this->getName();
+			$presenter = ($this->defaultRequestPresenter === NULL) ? $this->getName() : $this->defaultRequestPresenter;
 			$presenterClass = $this->getClass();

 		} else {
@@ -919,7 +938,7 @@
 				$presenter = substr($destination, 1, $a - 1);

 			} else { // relative
-				$presenter = $this->getName();
+				$presenter = ($this->defaultRequestPresenter === NULL) ? $this->getName() : $this->defaultRequestPresenter;
 				$b = strrpos($presenter, ':');
 				if ($b === FALSE) { // no module
 					$presenter = substr($destination, 0, $a);
?>

Použít se to pak dá následovně:

<?php

class ErrorPresenter extends BasePresenter
{
	function startup()
	{
		parent::startup();
		$this->setDefaultRequestPresenter('Homepage');
	}
}

?>

Potom, když se zobrazí ErrorPresenter a v @layout.phtml mám následující, nevznikne odkaz na neexistující stránku, ale naopak, kamkoli člověk klikne, dostane se minimálně na Homepage. Což myslím, že je nádhera ;)

<a href="{link this, lang => cs}">Czech</a>
<!-- odkaz bude např. /?presenter=Homepage&lang=cs (samozřejmě pokud je Homepage defaultní, nebude tam presenter vůbec ;) -->

Editoval DocX (21. 8. 2009 22:11)