diff --git a/ai/cs/@home.texy b/ai/cs/@home.texy new file mode 100644 index 0000000000..e2385cdf80 --- /dev/null +++ b/ai/cs/@home.texy @@ -0,0 +1,11 @@ +Nette AI +******** + +- [Introduction |guide] +- [Getting Started |getting-started] +- [MCP Inspector |mcp-inspector] +- [Claude Code |claude-code] +- [Tips & Best Practices |tips] + +{{maintitle: Nette AI – Vibe Coding with Nette Framework}} +{{description: Build Nette applications with AI assistance. MCP Inspector gives any AI tool deep knowledge of your application's DI container, database, routing, and errors. No hallucinations, just clean code.}} diff --git a/ai/cs/@meta.texy b/ai/cs/@meta.texy new file mode 100644 index 0000000000..e06cc9886c --- /dev/null +++ b/ai/cs/@meta.texy @@ -0,0 +1 @@ +{{sitename: Nette AI}} diff --git a/ai/en/@home.texy b/ai/en/@home.texy new file mode 100644 index 0000000000..e2385cdf80 --- /dev/null +++ b/ai/en/@home.texy @@ -0,0 +1,11 @@ +Nette AI +******** + +- [Introduction |guide] +- [Getting Started |getting-started] +- [MCP Inspector |mcp-inspector] +- [Claude Code |claude-code] +- [Tips & Best Practices |tips] + +{{maintitle: Nette AI – Vibe Coding with Nette Framework}} +{{description: Build Nette applications with AI assistance. MCP Inspector gives any AI tool deep knowledge of your application's DI container, database, routing, and errors. No hallucinations, just clean code.}} diff --git a/ai/en/@left-menu.texy b/ai/en/@left-menu.texy new file mode 100644 index 0000000000..54132f474a --- /dev/null +++ b/ai/en/@left-menu.texy @@ -0,0 +1,5 @@ +- [Introduction |guide] +- [Getting Started |getting-started] +- [MCP Inspector |mcp-inspector] +- [Claude Code |claude-code] +- [Tips & Best Practices |tips] diff --git a/ai/en/@meta.texy b/ai/en/@meta.texy new file mode 100644 index 0000000000..e06cc9886c --- /dev/null +++ b/ai/en/@meta.texy @@ -0,0 +1 @@ +{{sitename: Nette AI}} diff --git a/ai/en/claude-code.texy b/ai/en/claude-code.texy new file mode 100644 index 0000000000..f12fa5faf1 --- /dev/null +++ b/ai/en/claude-code.texy @@ -0,0 +1,251 @@ +Claude Code Plugin +****************** + +
Резултати от търсенето за ' . $search . 'alert("Hacked!")`. Тъй като изходът не е обработен по никакъв начин, той става част от показаната страница: -```html +```latte
Резултати от търсенето за
``` @@ -59,7 +59,7 @@ echo '
```
@@ -91,7 +91,7 @@ echo '#текст
```
@@ -330,7 +330,7 @@ Latte вижда шаблона по същия начин като вас. Ра
Latte вижда шаблона по същия начин като вас. За разлика от Twig, разбира HTML и знае, че променливата се извежда като стойност на атрибут, който не е в кавички. Затова ги допълва. Когато нападателят вмъкне същото описание, резултатният код ще изглежда така:
-```html
+```latte
```
diff --git a/latte/cs/custom-filters.texy b/latte/cs/custom-filters.texy
index 7f7cab0c0c..299d9c1aae 100644
--- a/latte/cs/custom-filters.texy
+++ b/latte/cs/custom-filters.texy
@@ -84,7 +84,7 @@ Registrace pomocí rozšíření
Pro lepší organizaci, zejména při vytváření znovupoužitelných sad filtrů nebo jejich sdílení jako balíčky, je doporučeným způsobem registrovat je v rámci [rozšíření Latte |extending-latte#Latte Extension]:
```php
-namespace App\Latte;
+namespace App\Templating;
use Latte\Extension;
@@ -111,7 +111,7 @@ class MyLatteExtension extends Extension
// Registrace
$latte = new Latte\Engine;
-$latte->addExtension(new App\Latte\MyLatteExtension);
+$latte->addExtension(new MyLatteExtension);
```
Tento přístup udrží logiku vašeho filtru zapouzdřenou a registraci jednoduchou.
diff --git a/latte/cs/custom-functions.texy b/latte/cs/custom-functions.texy
index 8a1e70f188..1bd18de0e3 100644
--- a/latte/cs/custom-functions.texy
+++ b/latte/cs/custom-functions.texy
@@ -67,7 +67,7 @@ Registrace pomocí rozšíření
Pro lepší organizaci a znovupoužitelnost registrujte funkce v rámci [Latte rozšíření |extending-latte#Latte Extension]. Tento přístup je doporučen pro složitější aplikace nebo sdílené knihovny.
```php
-namespace App\Latte;
+namespace App\Templating;
use Latte\Extension;
use Nette\Security\Authorizator;
@@ -95,7 +95,7 @@ class MyLatteExtension extends Extension
}
// Registrace (předpokládáme, že $container obsahuje DIC)
-$extension = $container->getByType(App\Latte\MyLatteExtension::class);
+$extension = $container->getByType(MyLatteExtension::class);
$latte = new Latte\Engine;
$latte->addExtension($extension);
```
diff --git a/latte/cs/custom-tags.texy b/latte/cs/custom-tags.texy
index 7d41e7799d..4c35f4c796 100644
--- a/latte/cs/custom-tags.texy
+++ b/latte/cs/custom-tags.texy
@@ -117,7 +117,7 @@ Vytvořte soubor (např. `DatetimeNode.php`) a definujte třídu:
```php
format()`, která sestavuje výsledný řetězec PHP kódu pro kompilovanou šablonu. První argument, `'echo date('Y-m-d H:i:s') %line;'`, je maska, do které jsou doplněny následující parametry. Zástupný symbol `%line` říká metodě `format()`, aby použila druhý argument, kterým je `$this->position`, a vložila komentář jako `/* line 15 */`, který propojuje vygenerovaný PHP kód zpět na původní řádek šablony, což je klíčové pro ladění.
-Vlastnost `$this->position` je zděděna ze základní třídy `Node` a je automaticky nastavena parserem Latte. Obsahuje objekt [api:Latte\Compiler\Position], který indikuje, kde byl tag nalezen ve zdrojovém souboru `.latte`.
+Vlastnost `$this->position` je zděděna ze základní třídy `Node` a je automaticky nastavena parserem Latte. Obsahuje objekt [api:Latte\Compiler\Range] (potomek třídy `Position` rozšířený o vlastnost `length` v bajtech), který udává, kde se tag v souboru `.latte` nachází. U párových tagů pokrývá rozsah od otevíracího po uzavírací tag a potomci `StatementNode` navíc nabízejí pole `$this->tagRanges` s objekty `Range` pro každý dílčí tag (otevírací, mezilehlé jako `{else}`/`{case}` i uzavírací).
Metoda `getIterator()` je zásadní pro kompilační průchody. Musí poskytovat všechny dětské uzly, ale náš jednoduchý `DatetimeNode` aktuálně nemá žádné argumenty ani obsah, tedy žádné dětské uzly. Nicméně metoda musí stále existovat a být generátorem, tj. klíčové slovo `yield` musí být nějakým způsobem přítomno v těle metody.
@@ -173,7 +173,7 @@ Nakonec informujme Latte o novém tagu. Vytvořte [třídu rozšíření |extend
```php
addExtension(new App\Latte\MyLatteExtension);
+$latte->addExtension(new App\Templating\MyLatteExtension);
```
Vytvořte šablonu:
@@ -255,7 +255,7 @@ S tímto pochopením upravme metodu `create()` v `DatetimeNode` tak, aby parsova
```php
addExtension(new App\Latte\MyLatteExtension($isDev));
+$latte->addExtension(new MyLatteExtension($isDev));
```
A jeho použití v šabloně:
@@ -555,7 +555,7 @@ Upravme `DebugNode::create()` tak, aby očekával `{else}`:
```php
Smazat
```
@@ -1003,7 +1003,7 @@ Zástupné symboly `PrintContext::format()`
- **`%args`**: Argument musí být `Expression\ArrayNode`. Vypíše položky pole formátované jako argumenty pro volání funkce nebo metody (oddělené čárkami, zpracovává pojmenované argumenty, pokud jsou přítomny).
- `$argsNode = new ArrayNode([...]);`
- `$context->format('myFunc(%args);', $argsNode)` -> `myFunc(1, name: 'Joe');`
-- **`%line`**: Argument musí být objekt `Position` (obvykle `$this->position`). Vkládá PHP komentář `/* line X */` indikující číslo řádku zdroje.
+- **`%line`**: Argument musí být objekt `Position` (nebo `Range`, obvykle `$this->position`). Vkládá PHP komentář `/* line X */` indikující číslo řádku zdroje.
- `$context->format('echo "Hi" %line;', $this->position)` -> `echo "Hi" /* line 42:1 */;`
- **`%escape(...)`**: Generuje PHP kód, který *za běhu* escapuje vnitřní výraz pomocí aktuálních kontextově uvědomělých pravidel escapování.
- `$context->format('echo %escape(%node);', $variableNode)`
@@ -1023,7 +1023,7 @@ Zatímco `parseExpression()`, `parseArguments()`, atd., pokrývají mnoho příp
```php
setTempDirectory('/path/to/tempdir');
+$latte->setCacheDirectory('/path/to/tempdir');
$params = [ /* proměnné šablony */ ];
// or $params = new TemplateParameters(/* ... */);
@@ -58,6 +58,20 @@ $latte->setAutoRefresh(false);
Při nasazení na produkčním serveru může prvotní vygenerování cache, zejména u rozsáhlejších aplikací, pochopitelně chviličku trvat. Latte má vestavěnou prevenci před "cache stampede":https://en.wikipedia.org/wiki/Cache_stampede. Jde o situaci, kdy se sejde větší počet souběžných požadavků, které spustí Latte, a protože cache ještě neexistuje, začaly by ji všechny generovat současně. Což by neúměrně zatížilo server. Latte je chytré a při více souběžných požadavcích generuje cache pouze první vlákno, ostatní čekají a následně ji využíjí.
+Způsoby rozšíření Latte
+=======================
+
+Latte můžete přizpůsobit hned několika způsoby, od jednoduchých pomocníků až po vlastní jazykové konstrukce. Podrobně se jim věnuje stránka [rozšiřujeme Latte |extending-latte], zde je stručný přehled:
+
+- **[Vlastní filtry |custom-filters]:** pro formátování nebo transformaci dat ve výstupu šablony (např. `{$var|myFilter}`).
+- **[Vlastní funkce |custom-functions]:** pro vlastní logiku, kterou voláte ve výrazech šablony (např. `{myFunction($arg)}`).
+- **[Vlastní tagy |custom-tags]:** pro zcela nové jazykové konstrukce (`{mytag}...{/mytag}` nebo `n:mytag`).
+- **[Kompilační průchody |compiler-passes]:** funkce, které upravují AST šablony mezi parsováním a generováním PHP kódu (například pro optimalizace nebo bezpečnostní kontroly).
+- **[Vlastní loadery |loaders]:** pro změnu způsobu, jakým Latte vyhledává a načítá soubory šablon.
+
+Pokud chcete svá rozšíření znovu použít v jiných projektech nebo je sdílet s ostatními, zabalte je do třídy [Latte Extension |extending-latte#Latte Extension].
+
+
Parametry jako třída
====================
@@ -184,18 +198,18 @@ Ve striktním režimu parsování Latte kontroluje, zda nechybí uzavírací HTM
```php
$latte = new Latte\Engine;
-$latte->setStrictParsing();
+$latte->setFeature(Latte\Feature::StrictParsing);
```
Generování šablon s hlavičkou `declare(strict_types=1)` zapnete takto:
```php
$latte = new Latte\Engine;
-$latte->setStrictTypes();
+$latte->setFeature(Latte\Feature::StrictTypes);
```
.[note]
-Od verze Latte 3.1 jsou strict types povoleny ve výchozím nastavení. Můžete je deaktivovat pomocí `$latte->setStrictTypes(false)`.
+Od verze Latte 3.1 jsou strict types povoleny ve výchozím nastavení. Můžete je deaktivovat pomocí `$latte->setFeature(Latte\Feature::StrictTypes, false)`.
Migrační varování .{data-version:3.1}
@@ -216,6 +230,70 @@ Pokud je toto zapnuto, Latte kontroluje vykreslované atributy a vyvolá uživat
Jakmile jsou všechna varování vyřešena, vypněte varování o migraci a **odstraňte všechny** filtry `|accept` ze svých šablon, protože již nejsou potřeba.
+Scopované proměnné cyklu .{data-version:3.1.3}
+==============================================
+
+Ve výchozím nastavení zůstávají proměnné definované v cyklu `{foreach}` (jako `$key` a `$value`) dostupné i po jeho skončení – stejně jako v samotném PHP. To může vést k nechtěnému přepsání proměnných, pokud má proměnná cyklu stejný název jako existující proměnná šablony.
+
+Funkce `ScopedLoopVariables` omezí platnost proměnných na tělo cyklu. Po jeho skončení se obnoví původní hodnota proměnné (pokud existovala), nebo se proměnná odstraní:
+
+```php
+$latte = new Latte\Engine;
+$latte->setFeature(Latte\Feature::ScopedLoopVariables);
+```
+
+Příklad rozdílu:
+
+```latte
+{var $item = 'original'}
+{foreach [1, 2] as $item}{$item}, {/foreach}
+{$item}
+```
+
+Bez `ScopedLoopVariables`: vypíše `1, 2, 2` (proměnná je přepsána)
+Se `ScopedLoopVariables`: vypíše `1, 2, original` (proměnná je obnovena)
+
+Funguje to i s destrukturováním, např. `{foreach $array as [$a, $b]}`.
+
+.[note]
+Proměnné cyklu používající reference (`{foreach $array as &$value}`) nebo přiřazení do vlastností (`{foreach $array as $obj->prop}`) nejsou scopovány, protože by to narušilo jejich účel.
+
+
+Automatické odsazení (Dedent) .{toc: Dedent}{data-version:3.1.3}
+================================================================
+
+Při používání párových značek jako `{if}`, `{foreach}` nebo `{block}` se vnořený obsah často odsazuje pro lepší čitelnost. Toto odsazení se ale ve výchozím nastavení přenáší do vygenerovaného výstupu. Funkce `Dedent` ho automaticky odstraní, takže výstup zůstane čistý bez ohledu na úroveň zanoření v šabloně:
+
+```php
+$latte = new Latte\Engine;
+$latte->setFeature(Latte\Feature::Dedent);
+```
+
+Příklad:
+
+```latte
+{if true}
+ Hello
+ World
+{/if}
+```
+
+Bez `Dedent` by výstup obsahoval odsazení (`\tHello\n\tWorld\n`). S `Dedent` se odsazení odstraní a výstupem je `Hello\nWorld\n`.
+
+Hlubší odsazení uvnitř bloku zůstává zachováno relativně k základnímu odsazení:
+
+```latte
+{if true}
+ Hello
+ Indented
+{/if}
+```
+
+Výstup: `Hello\n\tIndented\n`.
+
+Odsazení v bloku musí být konzistentní (buď tabulátory, nebo mezery). Pokud se mísí, Latte vyhodí výjimku `Inconsistent indentation`.
+
+
Překládání v šablonách .{toc: TranslatorExtension}
==================================================
diff --git a/latte/cs/filters.texy b/latte/cs/filters.texy
index bcce55ae3e..5a53e646d0 100644
--- a/latte/cs/filters.texy
+++ b/latte/cs/filters.texy
@@ -10,6 +10,9 @@ V šablonách můžeme používat funkce, které pomáhají upravit nebo přefor
| `breakLines` | [Před konce řádku přidá HTML odřádkování |#breakLines]
| `bytes` | [formátuje velikost v bajtech |#bytes]
| `clamp` | [ohraničí hodnotu do daného rozsahu |#clamp]
+| `column` | [extrahuje jeden sloupec z pole |#column]
+| `commas` | [spojí pole čárkami |#commas]
+| `limit` | [omezí délku pole, řetězce nebo iterátoru |#limit]
| `dataStream` | [konverze pro Data URI protokol |#dataStream]
| `date` | [formátuje datum a čas |#date]
| `explode` | [rozdělí řetězec na pole podle oddělovače |#explode]
@@ -259,6 +262,50 @@ Ohraničí hodnotu do daného inkluzivního rozsahu min a max.
Existuje také jako [funkce |functions#clamp].
+column(string|int|null $columnKey, string|int|null $indexKey=null) .[filter]{data-version:3.1.3}
+------------------------------------------------------------------------------------------------
+Vrátí z vícerozměrného pole hodnoty jednoho sloupce `$columnKey` jako nové pole. Lze použít i na pole objektů pro získání hodnot vlastností.
+
+```latte
+{var $users = [
+ [id: 30, name: 'John', age: 30],
+ [id: 32, name: 'Jane', age: 25],
+ [id: 33, age: 35],
+]}
+
+{$users|column: 'name'}
+{* vrátí ['John', 'Jane'] *}
+
+{$users|column: 'name', 'id'}
+{* vrátí [30 => 'John', 32 => 'Jane'] *}
+```
+
+Pokud předáte `null` jako klíč sloupce, přeindexuje pole podle `$indexKey`.
+
+
+commas(?string $lastGlue=null) .[filter]{data-version:3.1.3}
+------------------------------------------------------------
+Spojí prvky pole čárkou a mezerou (`', '`). Jde o pohodlnou zkratku pro běžný výpis položek v čitelné podobě.
+
+```latte
+{var $items = ['jablka', 'pomeranče', 'banány']}
+{$items|commas}
+{* vypíše 'jablka, pomeranče, banány' *}
+```
+
+Lze zadat i vlastní oddělovač pro poslední dvojici položek:
+
+```latte
+{$items|commas: ' a '}
+{* vypíše 'jablka, pomeranče a banány' *}
+
+{=['PHP', 'JavaScript', 'Python']|commas: ', nebo '}
+{* vypíše 'PHP, JavaScript, nebo Python' *}
+```
+
+Viz také [#implode].
+
+
dataStream(string $mimetype=detect) .[filter]
---------------------------------------------
Konvertuje obsah do data URI scheme. Pomocí něj lze do HTML nebo CSS vkládat obrázky bez nutnosti linkovat externí soubory.
@@ -407,6 +454,8 @@ Můžete také použít alias `join`:
{=[1, 2, 3]|join} {* vypíše '123' *}
```
+Viz také [#commas], [#explode].
+
indent(int $level=1, string $char="\t") .[filter]
-------------------------------------------------
@@ -637,19 +686,21 @@ Pamatujte, že skutečný vzhled čísel se může lišit podle nastavení země
padLeft(int $length, string $pad=' ') .[filter]
-----------------------------------------------
-Doplní řetězec do určité délky jiným řetězcem zleva.
+Doplní řetězec nebo číslo do určité délky jiným řetězcem zleva.
```latte
{='hello'|padLeft: 10, '123'} {* vypíše '12312hello' *}
+{=123|padLeft: 5, '0'} {* vypíše '00123' *}
```
padRight(int $length, string $pad=' ') .[filter]
------------------------------------------------
-Doplní řetězec do určité délky jiným řetězcem zprava.
+Doplní řetězec nebo číslo do určité délky jiným řetězcem zprava.
```latte
{='hello'|padRight: 10, '123'} {* vypíše 'hello12312' *}
+{=123|padRight: 5, '0'} {* vypíše '12300' *}
```
@@ -747,14 +798,14 @@ Viz také [#ceil], [#floor].
slice(int $start, ?int $length=null, bool $preserveKeys=false) .[filter]
------------------------------------------------------------------------
-Extrahuje část pole nebo řetězce.
+Extrahuje část pole, řetězce nebo iterátoru.
```latte
{='hello'|slice: 1, 2} {* vypíše 'el' *}
{=['a', 'b', 'c']|slice: 1, 2} {* vypíše ['b', 'c'] *}
```
-Filtr funguje jako funkce PHP `array_slice` pro pole nebo `mb_substr` pro řetězce s fallbackem na funkci `iconv_substr` v režimu UTF‑8.
+Filtr funguje jako funkce PHP `array_slice` pro pole nebo `mb_substr` pro řetězce. Pro iterátory vrací generátor – prvky se čtou z původního zdroje jeden po druhém a po dosažení limitu se čtení zastaví. Celý iterátor se do paměti nenačítá.
Pokud je start kladný, posloupnost začné posunutá o tento počet od začátku pole/řetezce. Pokud je záporný posloupnost začné posunutá o tolik od konce.
@@ -762,6 +813,21 @@ Pokud je zadaný parametr length a je kladný, posloupnost bude obsahovat tolik
Ve výchozím nastavení filtr změní pořadí a resetuje celočíselného klíče pole. Toto chování lze změnit nastavením preserveKeys na true. Řetězcové klíče jsou vždy zachovány, bez ohledu na tento parametr.
+Viz také [#limit].
+
+
+limit(int $length) .[filter]{data-version:3.1.3}
+------------------------------------------------
+Omezí délku pole, řetězce nebo iterátoru. U polí a iterátorů zachovává klíče. U řetězců respektuje UTF-8.
+
+```latte
+{foreach ($items|limit: 5) as $item}
+ ...
+{/foreach}
+
+{$text|limit: 100}
+```
+
sort(?Closure $comparison, string|int|\Closure|null $by=null, string|int|\Closure|bool $byKey=false) .[filter]
--------------------------------------------------------------------------------------------------------------
diff --git a/latte/cs/recipes.texy b/latte/cs/recipes.texy
index a11d147a0e..172a222a08 100644
--- a/latte/cs/recipes.texy
+++ b/latte/cs/recipes.texy
@@ -7,7 +7,7 @@ Editory a IDE
Pište šablony v editoru nebo IDE, který má podporu pro Latte. Bude to mnohem příjemnější.
-- PhpStorm: nainstalujte v `Settings > Plugins > Marketplace` [plugin Latte|https://plugins.jetbrains.com/plugin/7457-latte]
+- PhpStorm: nainstalujte v `Settings > Plugins > Marketplace` [plugin Latte|https://plugins.jetbrains.com/plugin/24218-latte-support]
- VS Code: nainstalujte [Nette Latte + Neon|https://marketplace.visualstudio.com/items?itemName=Kasik96.latte], [Nette Latte templates|https://marketplace.visualstudio.com/items?itemName=smuuf.latte-lang] nebo nejnovější [Nette for VS Code |https://marketplace.visualstudio.com/items?itemName=franken-ui.nette-for-vscode] plugin
- NetBeans IDE: nativní podpora Latte je součástí instalace
- Sublime Text 3: v Package Control najděte a nainstalujte balíček `Nette` a zvolte Latte ve `View > Syntax`
diff --git a/latte/cs/safety-first.texy b/latte/cs/safety-first.texy
index e76142ec39..b718215a1b 100644
--- a/latte/cs/safety-first.texy
+++ b/latte/cs/safety-first.texy
@@ -33,7 +33,7 @@ echo 'Výsledky vyhledávání pro ' . $search . '
'; Útočník může do vyhledávacího políčka a potažmo do proměnné `$search` zapsat libovolný řetězec, tedy i HTML kód jako ``. Protože výstup není nijak ošetřen, stane se součástí zobrazené stránky: -```html +```latteVýsledky vyhledávání pro
``` @@ -59,7 +59,7 @@ echo '
```
@@ -91,7 +91,7 @@ Kontextově sensitivní escapování
Co se přesně myslí slovem kontext? Jde o místo v dokumentu s vlastními pravidly pro ošetřování vypisovaných dat. Odvíjí se od typu dokumentu (HTML, XML, CSS, JavaScript, plain text, ...) a může se lišit v jeho konkrétních částech. Například v HTML dokumentu je takových míst (kontextů), kde platí velmi odlišná pravidla, celá řada. Možná budete překvapeni, kolik jich je. Tady máme první čtveřici:
-```html
+```latte
#text
```
@@ -330,7 +330,7 @@ Nyní se podíváme, jak si se stejnou šablonou poradí Latte:
Latte vidí šablonu stejně jako vy. Na rozdíl od Twigu chápe HTML a ví, že proměnná se vypisuje jako hodnota atributu, který není v uvozovkách. Proto je doplní. Když útočník vloží stejný popisek, výsledný kód bude vypadat takto:
-```html
+```latte
```
diff --git a/latte/cs/syntax.texy b/latte/cs/syntax.texy
index 1e21ab6dcf..39f1c621de 100644
--- a/latte/cs/syntax.texy
+++ b/latte/cs/syntax.texy
@@ -218,6 +218,41 @@ Uvnitř značek fungují PHP komentáře:
```
+Řízení bílých znaků
+===================
+
+Latte zachází s bílými znaky inteligentně. Kód můžete volně odsazovat pro čitelnost a výstup zůstane čistý. Když se tag objeví na řádku sám, celý řádek (odsazení i konec řádku) se z výstupu odstraní:
+
+```latte
+Suchergebnisse für ' . $search . '
'; Ein Angreifer kann in das Suchfeld und somit in die Variable `$search` eine beliebige Zeichenkette eingeben, also auch HTML-Code wie ``. Da die Ausgabe nicht bereinigt wird, wird sie Teil der angezeigten Seite: -```html +```latteSuchergebnisse für
``` @@ -59,7 +59,7 @@ echo '
```
@@ -91,7 +91,7 @@ Kontextsensitives Escaping
Was genau ist mit dem Wort Kontext gemeint? Es handelt sich um eine Stelle im Dokument mit eigenen Regeln für die Bereinigung ausgegebener Daten. Sie hängt vom Dokumenttyp ab (HTML, XML, CSS, JavaScript, Plain Text, ...) und kann sich in seinen spezifischen Teilen unterscheiden. Beispielsweise gibt es in einem HTML-Dokument eine ganze Reihe solcher Stellen (Kontexte), an denen sehr unterschiedliche Regeln gelten. Vielleicht werden Sie überrascht sein, wie viele es sind. Hier sind die ersten vier:
-```html
+```latte
#text
```
@@ -330,7 +330,7 @@ Sehen wir uns nun an, wie Latte mit demselben Template umgeht:
Latte sieht das Template genauso wie Sie. Im Gegensatz zu Twig versteht es HTML und weiß, dass die Variable als Wert eines Attributs ausgegeben wird, das nicht in Anführungszeichen steht. Deshalb ergänzt es sie. Wenn ein Angreifer dieselbe Beschreibung einfügt, sieht der resultierende Code so aus:
-```html
+```latte
```
diff --git a/latte/el/custom-tags.texy b/latte/el/custom-tags.texy
index cf69c32588..3c041dd29d 100644
--- a/latte/el/custom-tags.texy
+++ b/latte/el/custom-tags.texy
@@ -923,7 +923,7 @@ class MyLatteExtension extends Extension
Παραγόμενο HTML:
-```html
+```latte
Διαγραφή
```
diff --git a/latte/el/safety-first.texy b/latte/el/safety-first.texy
index bb4b7c09d7..65985510ef 100644
--- a/latte/el/safety-first.texy
+++ b/latte/el/safety-first.texy
@@ -33,7 +33,7 @@ echo 'Αποτελέσματα αναζήτησης για ' . $search .
Ένας εισβολέας μπορεί να γράψει στο πεδίο αναζήτησης και κατ' επέκταση στη μεταβλητή `$search` οποιοδήποτε string, δηλαδή και κώδικα HTML όπως ``. Επειδή η έξοδος δεν επεξεργάζεται με κανέναν τρόπο, γίνεται μέρος της εμφανιζόμενης σελίδας:
-```html
+```latte
Αποτελέσματα αναζήτησης για #κείμενο Search results for ' . $search . ' Search results for #text Resultados de la búsqueda para ' . $search . ' Resultados de la búsqueda para #texto Résultats de la recherche pour ' . $search . ' Résultats de la recherche pour #texte Keresési eredmények erre: ' . $search . ' Keresési eredmények erre: #szöveg';
Αρκεί ο εισβολέας να εισάγει ως λεζάντα ένα έξυπνα κατασκευασμένο string `" onload="alert('Hacked!')` και αν η εκτύπωση δεν επεξεργαστεί, ο προκύπτων κώδικας θα μοιάζει ως εξής:
-```html
+```latte
```
@@ -91,7 +91,7 @@ Context-Aware Escaping
Τι ακριβώς εννοούμε με τη λέξη context; Πρόκειται για ένα μέρος στο έγγραφο με τους δικούς του κανόνες για την επεξεργασία των εκτυπωμένων δεδομένων. Εξαρτάται από τον τύπο του εγγράφου (HTML, XML, CSS, JavaScript, plain text, ...) και μπορεί να διαφέρει σε συγκεκριμένα μέρη του. Για παράδειγμα, σε ένα έγγραφο HTML, υπάρχουν πολλά τέτοια μέρη (contexts) όπου ισχύουν πολύ διαφορετικοί κανόνες. Ίσως εκπλαγείτε πόσα είναι. Εδώ έχουμε την πρώτη τετράδα:
-```html
+```latte
@@ -108,7 +108,7 @@ Context-Aware Escaping
Τα contexts μπορούν επίσης να στρωματοποιηθούν, κάτι που συμβαίνει όταν ενσωματώνουμε JavaScript ή CSS σε HTML. Αυτό μπορεί να γίνει με δύο διαφορετικούς τρόπους, με στοιχείο και με attribute:
-```html
+```latte
@@ -132,7 +132,7 @@ Context-Aware Escaping
Αν το εκτυπώσετε σε κείμενο HTML, σε αυτή τη συγκεκριμένη περίπτωση δεν χρειάζεται να κάνετε καμία αντικατάσταση, επειδή το string δεν περιέχει κανέναν χαρακτήρα με ειδική σημασία. Η κατάσταση αλλάζει αν το εκτυπώσετε μέσα σε ένα attribute HTML που περικλείεται σε απλά εισαγωγικά. Σε αυτή την περίπτωση, πρέπει να κάνετε escape τα εισαγωγικά σε οντότητες HTML:
-```html
+```latte
```
@@ -152,13 +152,13 @@ alert('Rock\'n\'Roll');
Αν εισάγουμε αυτόν τον κώδικα σε ένα έγγραφο HTML χρησιμοποιώντας το ` alert('Rock\'n\'Roll');
```
Αν όμως θέλαμε να το εισάγουμε σε ένα attribute HTML, πρέπει ακόμα να κάνουμε escape τα εισαγωγικά σε οντότητες HTML:
-```html
+```latte
```
@@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll
Και όταν εκτυπώνουμε αυτό το string σε ένα attribute, εφαρμόζουμε επιπλέον το escaping σύμφωνα με αυτό το context και αντικαθιστούμε το `&` με `&`:
-```html
+```latte
```
@@ -314,7 +314,7 @@ Latte εναντίον απλοϊκών συστημάτων
Ένας εισβολέας εισάγει ως λεζάντα της εικόνας ένα έξυπνα κατασκευασμένο string `foo onload=alert('Hacked!')`. Γνωρίζουμε ήδη ότι το Twig δεν μπορεί να αναγνωρίσει αν η μεταβλητή εκτυπώνεται στη ροή του κειμένου HTML, μέσα σε ένα attribute, σε ένα σχόλιο HTML κ.λπ., με λίγα λόγια δεν διακρίνει τα contexts. Και απλώς μετατρέπει μηχανικά τους χαρακτήρες `< > & ' "` σε οντότητες HTML. Έτσι, ο προκύπτων κώδικας θα μοιάζει ως εξής:
-```html
+```latte
```
@@ -330,7 +330,7 @@ Latte εναντίον απλοϊκών συστημάτων
Το Latte βλέπει το πρότυπο όπως εσείς. Σε αντίθεση με το Twig, καταλαβαίνει HTML και ξέρει ότι η μεταβλητή εκτυπώνεται ως τιμή ενός attribute που δεν βρίσκεται σε εισαγωγικά. Γι' αυτό τα συμπληρώνει. Όταν ο εισβολέας εισάγει την ίδια λεζάντα, ο προκύπτων κώδικας θα μοιάζει ως εξής:
-```html
+```latte
```
diff --git a/latte/en/custom-filters.texy b/latte/en/custom-filters.texy
index 9fc3ea58b6..db6d77bf8d 100644
--- a/latte/en/custom-filters.texy
+++ b/latte/en/custom-filters.texy
@@ -84,7 +84,7 @@ Registration via Extension
For better organization, especially when creating reusable sets of filters or sharing them as packages, the recommended way is to register them within a [Latte Extension |extending-latte#Latte Extension]:
```php
-namespace App\Latte;
+namespace App\Templating;
use Latte\Extension;
@@ -111,7 +111,7 @@ class MyLatteExtension extends Extension
// Registration
$latte = new Latte\Engine;
-$latte->addExtension(new App\Latte\MyLatteExtension);
+$latte->addExtension(new MyLatteExtension);
```
This approach keeps your filter logic encapsulated and makes registration straightforward.
diff --git a/latte/en/custom-functions.texy b/latte/en/custom-functions.texy
index 4c4a4d423d..eeef7d8008 100644
--- a/latte/en/custom-functions.texy
+++ b/latte/en/custom-functions.texy
@@ -67,7 +67,7 @@ Registration via Extension
For better organization and reusability, register functions within a [Latte Extension |extending-latte#Latte Extension]. This is the recommended approach for non-trivial applications or shared libraries.
```php
-namespace App\Latte;
+namespace App\Templating;
use Latte\Extension;
use Nette\Security\Authorizator;
@@ -95,7 +95,7 @@ class MyLatteExtension extends Extension
}
// Registration (assuming $container holds the DIC)
-$extension = $container->getByType(App\Latte\MyLatteExtension::class);
+$extension = $container->getByType(MyLatteExtension::class);
$latte = new Latte\Engine;
$latte->addExtension($extension);
```
diff --git a/latte/en/custom-tags.texy b/latte/en/custom-tags.texy
index b427e74a27..b162bc8e10 100644
--- a/latte/en/custom-tags.texy
+++ b/latte/en/custom-tags.texy
@@ -117,7 +117,7 @@ Create a file (e.g., `DatetimeNode.php`) and define the class:
```php
format()` method, which assembles the resulting PHP code string for the compiled template. The first argument, `'echo date('Y-m-d H:i:s') %line;'`, is the mask into which the subsequent parameters are substituted. The `%line` placeholder tells the `format()` method to take the second following argument, which is `$this->position`, and inserts a comment like `/* line 15 */` that links the generated PHP code back to the original template line, which is crucial for debugging.
-Property `$this->position` is inherited from the base `Node` class, and is automatically set by Latte's parser. It holds a [api:Latte\Compiler\Position] object indicating where the tag was found in the source `.latte` file.
+Property `$this->position` is inherited from the base `Node` class, and is automatically set by Latte's parser. It holds a [api:Latte\Compiler\Range] object (a subclass of `Position` extended with a `length` in bytes) indicating where the tag is located in the source `.latte` file. For paired tags the range spans from the opening to the closing tag, and `StatementNode` descendants additionally expose `$this->tagRanges` listing the `Range` of every constituent tag (opening, intermediate like `{else}`/`{case}`, and closing).
The `getIterator()` method is vital for compiler passes. It must yield all child nodes, but our simple `DatetimeNode` currently has no arguments or content, thus no child nodes. However, the method must still exist and be a generator, i.e. the `yield` keyword must be somehow present in the method body.
@@ -173,7 +173,7 @@ Finally, tell Latte about the new tag. Create an [Extension class |extending-lat
```php
addExtension(new App\Latte\MyLatteExtension);
+$latte->addExtension(new App\Templating\MyLatteExtension);
```
Create template:
@@ -255,7 +255,7 @@ With that understanding, let's modify the `create()` method in `DatetimeNode` to
```php
addExtension(new App\Latte\MyLatteExtension($isDev));
+$latte->addExtension(new MyLatteExtension($isDev));
```
And use it in a template:
@@ -555,7 +555,7 @@ Let's modify `DebugNode::create()` to expect `{else}`:
```php
Delete
```
@@ -1003,7 +1003,7 @@ We've frequently used `PrintContext::format()` to generate PHP code in the `prin
- **`%args`**: Argument must be an `Expression\ArrayNode`. It prints the array items formatted as arguments for a function or method call (comma-separated, handling named arguments if present).
- `$argsNode = new ArrayNode([...]);`
- `$context->format('myFunc(%args);', $argsNode)` -> `myFunc(1, name: 'Joe');`
-- **`%line`**: Argument must be a `Position` object (usually `$this->position`). It inserts a PHP comment `/* line X */` indicating the source line number.
+- **`%line`**: Argument must be a `Position` (or `Range`) object (usually `$this->position`). It inserts a PHP comment `/* line X */` indicating the source line number.
- `$context->format('echo "Hi" %line;', $this->position)` -> `echo "Hi" /* line 42:1 */;`
- **`%escape(...)`**: It generates PHP code that, *at runtime*, will escape the inner expression using the current context-aware escaping rules.
- `$context->format('echo %escape(%node);', $variableNode)`
@@ -1023,7 +1023,7 @@ While `parseExpression()`, `parseArguments()`, etc., cover many cases, sometimes
```php
setTempDirectory('/path/to/tempdir');
+$latte->setCacheDirectory('/path/to/tempdir');
$params = [ /* template variables */ ];
// or $params = new TemplateParameters(/* ... */);
@@ -58,6 +58,20 @@ $latte->setAutoRefresh(false);
When deployed on a production server, the initial cache generation, especially for larger applications, can understandably take a while. Latte has built-in prevention against "cache stampede":https://en.wikipedia.org/wiki/Cache_stampede. This is a situation where server receives a large number of concurrent requests and because Latte's cache does not yet exist, they would all generate it at the same time. Which spikes CPU. Latte is smart, and when there are multiple concurrent requests, only the first thread generates the cache, the others wait and then use it.
+Ways to Extend Latte
+====================
+
+Latte can be customized in several ways, from simple helpers to entirely new language constructs. The page [extending Latte |extending-latte] covers them in detail; here is a quick overview:
+
+- **[Custom Filters|custom-filters]:** for formatting or transforming data in the template output (e.g., `{$var|myFilter}`).
+- **[Custom Functions|custom-functions]:** for custom logic you call within template expressions (e.g., `{myFunction($arg)}`).
+- **[Custom Tags|custom-tags]:** for entirely new language constructs (`{mytag}...{/mytag}` or `n:mytag`).
+- **[Compiler Passes|compiler-passes]:** functions that modify the template's AST between parsing and PHP code generation (for example, optimizations or security checks).
+- **[Custom Loaders|loaders]:** for changing how Latte locates and loads template files.
+
+If you want to reuse your extensions across projects or share them with others, bundle them into a [Latte Extension |extending-latte#Latte Extension] class.
+
+
Parameters as a Class
=====================
@@ -184,18 +198,18 @@ In strict parsing mode, Latte checks for missing closing HTML tags and also disa
```php
$latte = new Latte\Engine;
-$latte->setStrictParsing();
+$latte->setFeature(Latte\Feature::StrictParsing);
```
To generate templates with the `declare(strict_types=1)` header, do the following:
```php
$latte = new Latte\Engine;
-$latte->setStrictTypes();
+$latte->setFeature(Latte\Feature::StrictTypes);
```
.[note]
-Since Latte 3.1, strict types are enabled by default. You can disable them with `$latte->setStrictTypes(false)`.
+Since Latte 3.1, strict types are enabled by default. You can disable them with `$latte->setFeature(Latte\Feature::StrictTypes, false)`.
Migration Warnings .{data-version:3.1}
@@ -216,6 +230,70 @@ When enabled, Latte checks rendered attributes and triggers a user warning (`E_U
Once all warnings are resolved, disable migration warnings and **remove all** `|accept` filters from your templates, as they are no longer needed.
+Scoped Loop Variables .{data-version:3.1.3}
+===========================================
+
+By default, variables defined in a `{foreach}` loop (like `$key` and `$value`) remain accessible after the loop ends – just like in PHP itself. This can lead to unintended variable overwrites when a loop variable has the same name as an existing template variable.
+
+The `ScopedLoopVariables` feature limits the scope of loop variables to the loop body. After the loop ends, the original variable value is restored (if it existed before), or the variable is unset:
+
+```php
+$latte = new Latte\Engine;
+$latte->setFeature(Latte\Feature::ScopedLoopVariables);
+```
+
+Example of the difference:
+
+```latte
+{var $item = 'original'}
+{foreach [1, 2] as $item}{$item}, {/foreach}
+{$item}
+```
+
+Without `ScopedLoopVariables`: outputs `1, 2, 2` (variable is overwritten)
+With `ScopedLoopVariables`: outputs `1, 2, original` (variable is restored)
+
+This also works with destructuring syntax, e.g. `{foreach $array as [$a, $b]}`.
+
+.[note]
+Loop variables using references (`{foreach $array as &$value}`) or property assignments (`{foreach $array as $obj->prop}`) are not scoped, as this would break their intended purpose.
+
+
+Automatic Dedentation .{toc: Dedent}{data-version:3.1.3}
+========================================================
+
+When using paired tags like `{if}`, `{foreach}`, or `{block}`, you often indent the nested content for readability. However, this indentation is included in the generated output by default. The `Dedent` feature automatically removes it, so the output stays clean regardless of how deeply you nest your Latte tags:
+
+```php
+$latte = new Latte\Engine;
+$latte->setFeature(Latte\Feature::Dedent);
+```
+
+Example:
+
+```latte
+{if true}
+ Hello
+ World
+{/if}
+```
+
+Without `Dedent`, the output would include the indentation (`\tHello\n\tWorld\n`). With `Dedent`, the indentation is stripped and the output is `Hello\nWorld\n`.
+
+Deeper indentation within a block is preserved relative to the base indentation:
+
+```latte
+{if true}
+ Hello
+ Indented
+{/if}
+```
+
+Output: `Hello\n\tIndented\n`.
+
+Indentation within a block must be consistent (either tabs or spaces). If they are mixed, Latte throws an `Inconsistent indentation` exception.
+
+
Translation in Templates .{toc: TranslatorExtension}
====================================================
diff --git a/latte/en/filters.texy b/latte/en/filters.texy
index c87f8ffceb..813daf354d 100644
--- a/latte/en/filters.texy
+++ b/latte/en/filters.texy
@@ -10,6 +10,9 @@ In templates, we can use functions that help modify or reformat data into its fi
| `breakLines` | [Inserts HTML line breaks before all newlines |#breakLines]
| `bytes` | [formats size in bytes |#bytes]
| `clamp` | [clamps a value to the given range |#clamp]
+| `column` | [extracts a single column from an array |#column]
+| `commas` | [joins an array with commas |#commas]
+| `limit` | [limits the length of an array, string, or iterator |#limit]
| `dataStream` | [Data URI protocol conversion |#dataStream]
| `date` | [formats the date and time |#date]
| `explode` | [splits a string into an array by a delimiter |#explode]
@@ -259,6 +262,50 @@ Clamps a value to the given inclusive range of min and max.
Also exists as a [function |functions#clamp].
+column(string|int|null $columnKey, string|int|null $indexKey=null) .[filter]{data-version:3.1.3}
+------------------------------------------------------------------------------------------------
+Returns the values of a single column `$columnKey` from a multidimensional array as a new array. Can also be used on arrays of objects to extract property values.
+
+```latte
+{var $users = [
+ [id: 30, name: 'John', age: 30],
+ [id: 32, name: 'Jane', age: 25],
+ [id: 33, age: 35],
+]}
+
+{$users|column: 'name'}
+{* returns ['John', 'Jane'] *}
+
+{$users|column: 'name', 'id'}
+{* returns [30 => 'John', 32 => 'Jane'] *}
+```
+
+If you pass `null` as the column key, it will reindex the array according to `$indexKey`.
+
+
+commas(?string $lastGlue=null) .[filter]{data-version:3.1.3}
+------------------------------------------------------------
+Joins array elements with a comma and space (`', '`). A convenient shortcut for listing items in a human-readable format.
+
+```latte
+{var $items = ['apples', 'oranges', 'bananas']}
+{$items|commas}
+{* outputs 'apples, oranges, bananas' *}
+```
+
+You can also provide a custom separator for the last pair of items:
+
+```latte
+{$items|commas: ' and '}
+{* outputs 'apples, oranges and bananas' *}
+
+{=['PHP', 'JavaScript', 'Python']|commas: ', or '}
+{* outputs 'PHP, JavaScript, or Python' *}
+```
+
+See also [#implode].
+
+
dataStream(string $mimetype='detect') .[filter]
-----------------------------------------------
Converts content to the data URI scheme. This allows embedding images into HTML or CSS without needing to link external files.
@@ -407,6 +454,8 @@ You can also use the alias `join`:
{=[1, 2, 3]|join} {* outputs '123' *}
```
+See also [#commas], [#explode].
+
indent(int $level=1, string $char="\t") .[filter]
-------------------------------------------------
@@ -637,19 +686,21 @@ Remember that the actual appearance of numbers may vary depending on the country
padLeft(int $length, string $pad=' ') .[filter]
-----------------------------------------------
-Pads a string to a certain length with another string from the left.
+Pads a string or number to a certain length with another string from the left.
```latte
{='hello'|padLeft: 10, '123'} {* outputs '12312hello' *}
+{=123|padLeft: 5, '0'} {* outputs '00123' *}
```
padRight(int $length, string $pad=' ') .[filter]
------------------------------------------------
-Pads a string to a certain length with another string from the right.
+Pads a string or number to a certain length with another string from the right.
```latte
{='hello'|padRight: 10, '123'} {* outputs 'hello12312' *}
+{=123|padRight: 5, '0'} {* outputs '12300' *}
```
@@ -747,14 +798,14 @@ See also [#ceil], [#floor].
slice(int $start, ?int $length=null, bool $preserveKeys=false) .[filter]
------------------------------------------------------------------------
-Extracts a slice of an array or a string.
+Extracts a slice of an array, string, or iterator.
```latte
{='hello'|slice: 1, 2} {* outputs 'el' *}
{=['a', 'b', 'c']|slice: 1, 2} {* outputs ['b', 'c'] *}
```
-The filter works like the PHP function `array_slice` for arrays or `mb_substr` for strings, with a fallback to the `iconv_substr` function in UTF‑8 mode.
+The filter works like the PHP function `array_slice` for arrays or `mb_substr` for strings. For iterators, it returns a generator – elements are consumed from the source one by one and reading stops once the limit is reached. The entire iterator is never loaded into memory.
If `start` is non-negative, the sequence will start at that offset from the beginning of the array/string. If `start` is negative, the sequence will start that far from the end.
@@ -762,6 +813,21 @@ If `length` is given and is positive, then the sequence will have up to that man
By default, the filter reorders and resets the integer array keys. This behavior can be changed by setting `preserveKeys` to true. String keys are always preserved, regardless of this parameter.
+See also [#limit].
+
+
+limit(int $length) .[filter]{data-version:3.1.3}
+------------------------------------------------
+Limits the length of an array, string, or iterator. For arrays and iterators, keys are preserved. For strings, it respects UTF-8.
+
+```latte
+{foreach ($items|limit: 5) as $item}
+ ...
+{/foreach}
+
+{$text|limit: 100}
+```
+
sort(?Closure $comparison, string|int|\Closure|null $by=null, string|int|\Closure|bool $byKey=false) .[filter]
--------------------------------------------------------------------------------------------------------------
diff --git a/latte/en/recipes.texy b/latte/en/recipes.texy
index 37896b3ee5..7547e5b989 100644
--- a/latte/en/recipes.texy
+++ b/latte/en/recipes.texy
@@ -7,7 +7,7 @@ Editors and IDE
Write templates in an editor or IDE that supports Latte. It will be much more pleasant.
-- PhpStorm: install the [Latte plugin|https://plugins.jetbrains.com/plugin/7457-latte] in `Settings > Plugins > Marketplace`
+- PhpStorm: install the [Latte plugin|https://plugins.jetbrains.com/plugin/24218-latte-support] in `Settings > Plugins > Marketplace`
- VS Code: install [Nette Latte + Neon|https://marketplace.visualstudio.com/items?itemName=Kasik96.latte], [Nette Latte templates|https://marketplace.visualstudio.com/items?itemName=smuuf.latte-lang] or the latest [Nette for VS Code |https://marketplace.visualstudio.com/items?itemName=franken-ui.nette-for-vscode] plugin
- NetBeans IDE: native support for Latte is included in the installation
- Sublime Text 3: find and install the `Nette` package in Package Control and choose Latte in `View > Syntax`
diff --git a/latte/en/safety-first.texy b/latte/en/safety-first.texy
index 4e2c9480f4..004656ca94 100644
--- a/latte/en/safety-first.texy
+++ b/latte/en/safety-first.texy
@@ -33,7 +33,7 @@ echo '';
An attacker simply needs to insert a cleverly crafted string `" onload="alert('Hacked!')` as the caption, and if the output is not sanitized, the resulting code will look like this:
-```html
+```latte
```
@@ -91,7 +91,7 @@ Context-Aware Escaping
What exactly is meant by the word context? It's a location within the document with its own rules for handling the data being printed. It depends on the document type (HTML, XML, CSS, JavaScript, plain text, ...) and can differ in specific parts. For example, in an HTML document, there are many places (contexts) where very different rules apply. You might be surprised how many there are. Here are the first four:
-```html
+```latte
@@ -108,7 +108,7 @@ It gets interesting inside HTML comments. Here, HTML entities are not used for e
Contexts can also be layered, which occurs when we embed JavaScript or CSS into HTML. This can be done in two different ways, using an element or an attribute:
-```html
+```latte
@@ -132,7 +132,7 @@ Let's take the string `Rock'n'Roll`.
If you print it in HTML text, in this particular case, no replacement is needed because the string does not contain any characters with special meaning. The situation changes if you print it inside an HTML attribute enclosed in single quotes. In that case, you need to escape the quotes into HTML entities:
-```html
+```latte
```
@@ -152,13 +152,13 @@ alert('Rock\'n\'Roll');
If we insert this code into an HTML document using ` alert('Rock\'n\'Roll');
```
However, if we wanted to insert it into an HTML attribute, we still need to escape the quotes into HTML entities:
-```html
+```latte
```
@@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll
And when we print this string in an attribute, we still apply escaping according to this context and replace `&` with `&`:
-```html
+```latte
```
@@ -314,7 +314,7 @@ Notice that there are no quotes around the attribute values. The coder might hav
An attacker inserts a cleverly crafted string `foo onload=alert('Hacked!')` as the image caption. We already know that Twig cannot determine whether a variable is being printed in the HTML text flow, inside an attribute, an HTML comment, etc.; in short, it does not distinguish contexts. And it just mechanically converts the characters `< > & ' "` into HTML entities. So the resulting code will look like this:
-```html
+```latte
```
@@ -330,7 +330,7 @@ Now let's see how Latte handles the same template:
Latte sees the template the same way you do. Unlike Twig, it understands HTML and knows that the variable is being printed as the value of an attribute that is not enclosed in quotes. Therefore, it adds them. When an attacker inserts the same caption, the resulting code will look like this:
-```html
+```latte
```
diff --git a/latte/en/syntax.texy b/latte/en/syntax.texy
index ef926e75be..aa7e274f5f 100644
--- a/latte/en/syntax.texy
+++ b/latte/en/syntax.texy
@@ -218,6 +218,41 @@ PHP comments work inside tags:
```
+Whitespace Control
+==================
+
+Latte handles whitespace intelligently. You can freely indent your code for readability, and the output stays clean. When a tag appears alone on a line, the entire line (indentation and newline) is removed from the output:
+
+```latte
+
+ {foreach $items as $item}
+
+```
+
+Outputs:
+
+```latte
+
+
+```
+
+What if a tag isn't alone on a line, but appears alongside other content? The whitespace before the tag then belongs *inside* the tag:
+
+```latte
+';
Al atacante le basta con insertar como descripción una cadena hábilmente construida `" onload="alert('Hacked!')` y si la impresión no está saneada, el código resultante se verá así:
-```html
+```latte
```
@@ -91,7 +91,7 @@ Escape sensible al contexto
¿Qué se entiende exactamente por la palabra contexto? Es un lugar en el documento con sus propias reglas para el saneamiento de los datos impresos. Depende del tipo de documento (HTML, XML, CSS, JavaScript, texto plano, ...) y puede diferir en sus partes específicas. Por ejemplo, en un documento HTML hay muchos lugares (contextos) donde se aplican reglas muy diferentes. Quizás se sorprenda de cuántos hay. Aquí tenemos los primeros cuatro:
-```html
+```latte
@@ -108,7 +108,7 @@ Es interesante dentro de los comentarios HTML. Aquí, el escape no se realiza ut
Los contextos también pueden anidarse, lo que ocurre cuando insertamos JavaScript o CSS en HTML. Esto se puede hacer de dos maneras diferentes, con un elemento y con un atributo:
-```html
+```latte
@@ -132,7 +132,7 @@ Tomemos la cadena `Rock'n'Roll`.
Si la imprime en texto HTML, en este caso particular no es necesario realizar ningún reemplazo, porque la cadena no contiene ningún carácter con significado especial. La situación cambia si la imprime dentro de un atributo HTML delimitado por comillas simples. En ese caso, es necesario escapar las comillas a entidades HTML:
-```html
+```latte
```
@@ -152,13 +152,13 @@ alert('Rock\'n\'Roll');
Si insertamos este código en un documento HTML usando ` alert('Rock\'n\'Roll');
```
Sin embargo, si quisiéramos insertarlo en un atributo HTML, aún debemos escapar las comillas a entidades HTML:
-```html
+```latte
```
@@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll
Y cuando imprimimos esta cadena en un atributo, aún aplicamos el escape según este contexto y reemplazamos `&` por `&`:
-```html
+```latte
```
@@ -314,7 +314,7 @@ Observe que no hay comillas alrededor de los valores de los atributos. El codifi
Un atacante inserta como descripción de la imagen una cadena hábilmente construida `foo onload=alert('Hacked!')`. Ya sabemos que Twig no puede saber si la variable se imprime en el flujo de texto HTML, dentro de un atributo, comentario HTML, etc., en resumen, no distingue contextos. Y solo convierte mecánicamente los caracteres `< > & ' "` en entidades HTML. Así que el código resultante se verá así:
-```html
+```latte
```
@@ -330,7 +330,7 @@ Ahora veamos cómo Latte maneja la misma plantilla:
Latte ve la plantilla igual que usted. A diferencia de Twig, entiende HTML y sabe que la variable se imprime como el valor de un atributo que no está entre comillas. Por eso las añade. Cuando un atacante inserta la misma descripción, el código resultante se verá así:
-```html
+```latte
```
diff --git a/latte/fr/custom-tags.texy b/latte/fr/custom-tags.texy
index 29c14a08a8..9983a44429 100644
--- a/latte/fr/custom-tags.texy
+++ b/latte/fr/custom-tags.texy
@@ -923,7 +923,7 @@ Vous pouvez maintenant utiliser `n:confirm` sur des liens, des boutons ou des é
HTML généré :
-```html
+```latte
Supprimer
```
diff --git a/latte/fr/safety-first.texy b/latte/fr/safety-first.texy
index e4b3f5a93a..d9476e4305 100644
--- a/latte/fr/safety-first.texy
+++ b/latte/fr/safety-first.texy
@@ -33,7 +33,7 @@ echo '';
Il suffit à l'attaquant d'insérer comme légende une chaîne habilement construite `" onload="alert('Piraté !')` et si l'affichage n'est pas traité, le code résultant ressemblera à ceci :
-```html
+```latte
```
@@ -91,7 +91,7 @@ Cependant, XSS ne concerne pas seulement l'affichage des données dans les templ
Que signifie exactement le mot contexte ? C'est un endroit dans le document avec ses propres règles pour traiter les données affichées. Il dépend du type de document (HTML, XML, CSS, JavaScript, texte brut, ...) et peut varier dans ses parties spécifiques. Par exemple, dans un document HTML, il existe de nombreux endroits (contextes) où des règles très différentes s'appliquent. Vous serez peut-être surpris de leur nombre. Voici les quatre premiers :
-```html
+```latte
@@ -108,7 +108,7 @@ C'est intéressant à l'intérieur des commentaires HTML. Ici, l'échappement n'
Les contextes peuvent également être imbriqués, ce qui se produit lorsque nous insérons du JavaScript ou du CSS dans du HTML. Cela peut être fait de deux manières différentes, par élément et par attribut :
-```html
+```latte
@@ -132,7 +132,7 @@ Prenons la chaîne `Rock'n'Roll`.
Si vous l'affichez dans du texte HTML, dans ce cas précis, il n'est pas nécessaire de faire de remplacements, car la chaîne ne contient aucun caractère ayant une signification spéciale. La situation change si vous l'affichez à l'intérieur d'un attribut HTML entouré de guillemets simples. Dans ce cas, il faut échapper les guillemets en entités HTML :
-```html
+```latte
```
@@ -152,13 +152,13 @@ alert('Rock\'n\'Roll');
Si nous insérons ce code dans un document HTML à l'aide de ` alert('Rock\'n\'Roll');
```
Cependant, si nous voulions l'insérer dans un attribut HTML, nous devrions encore échapper les guillemets en entités HTML :
-```html
+```latte
```
@@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll
Et lorsque nous affichons cette chaîne dans un attribut, nous appliquons encore l'échappement selon ce contexte et remplaçons `&` par `&`:
-```html
+```latte
```
@@ -314,7 +314,7 @@ Notez qu'il n'y a pas de guillemets autour des valeurs des attributs. Le codeur
L'attaquant insère comme légende de l'image une chaîne habilement construite `foo onload=alert('Piraté !')`. Nous savons déjà que Twig ne peut pas savoir si la variable est affichée dans le flux de texte HTML, à l'intérieur d'un attribut, d'un commentaire HTML, etc., bref, il ne distingue pas les contextes. Et il ne fait que convertir mécaniquement les caractères `< > & ' "` en entités HTML. Le code résultant ressemblera donc à ceci :
-```html
+```latte
```
@@ -330,7 +330,7 @@ Voyons maintenant comment Latte gère le même template :
Latte voit le template de la même manière que vous. Contrairement à Twig, il comprend HTML et sait que la variable est affichée comme valeur d'un attribut qui n'est pas entre guillemets. C'est pourquoi il les ajoute. Lorsque l'attaquant insère la même légende, le code résultant ressemblera à ceci :
-```html
+```latte
```
diff --git a/latte/hu/custom-tags.texy b/latte/hu/custom-tags.texy
index f8e2946305..6b7f70dbe5 100644
--- a/latte/hu/custom-tags.texy
+++ b/latte/hu/custom-tags.texy
@@ -923,7 +923,7 @@ Most már használhatja az `n:confirm`-ot linkeken, gombokon vagy űrlap elemeke
Generált HTML:
-```html
+```latte
Törlés
```
diff --git a/latte/hu/safety-first.texy b/latte/hu/safety-first.texy
index a07f648fe6..a1b2547fa0 100644
--- a/latte/hu/safety-first.texy
+++ b/latte/hu/safety-first.texy
@@ -33,7 +33,7 @@ echo '';
A támadónak elég leírásként egy ügyesen összeállított `" onload="alert('Hacked!')` stringet beilleszteni, és ha a kiírás nincs kezelve, az eredményül kapott kód így fog kinézni:
-```html
+```latte
```
@@ -91,7 +91,7 @@ Kontextusérzékeny escapelés
Mit jelent pontosan a kontextus szó? Ez egy hely a dokumentumban, saját szabályokkal a kiírt adatok kezelésére. A dokumentum típusától (HTML, XML, CSS, JavaScript, plain text, ...) függ, és eltérhet annak konkrét részeiben. Például egy HTML dokumentumban számos ilyen hely (kontextus) van, ahol nagyon eltérő szabályok érvényesek. Talán meglepődik, mennyi van belőlük. Íme az első négy:
-```html
+```latte
@@ -108,7 +108,7 @@ Talán meglepő, de speciális szabályok érvényesek a `