From d2fc92c1ea33d1fe44b6699d1a2ff68676aae196 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 22 Oct 2025 00:18:43 +0200 Subject: [PATCH 01/25] texy: extensive documentation update --- texy/cs/@home.texy | 126 +++- texy/cs/@menu.texy | 15 +- texy/cs/@try.texy | 15 - texy/cs/api-block-module.texy | 11 - texy/cs/api-blockquote-module.texy | 8 - texy/cs/api-emoticon-module.texy | 47 -- texy/cs/api-figure-module.texy | 33 - texy/cs/api-heading-module.texy | 42 -- texy/cs/api-horizline-module.texy | 8 - texy/cs/api-html-module.texy | 21 - texy/cs/api-htmloutput-module.texy | 17 - texy/cs/api-image-module.texy | 35 - texy/cs/api-link-module.texy | 42 -- texy/cs/api-list-module.texy | 12 - texy/cs/api-longwords-module.texy | 19 - texy/cs/api-paragraph-module.texy | 4 - texy/cs/api-phrase-module.texy | 22 - texy/cs/api-script-module.texy | 23 - texy/cs/api-table-module.texy | 18 - texy/cs/api-texy.texy | 69 -- texy/cs/api-typography-module.texy | 21 - texy/cs/api-zaklady.texy | 25 - texy/cs/api.texy | 28 - texy/cs/architecture.texy | 425 ++++++++++++ texy/cs/configuration.texy | 719 ++++++++++++++++++++ texy/cs/custom-handlers.texy | 850 +++++++++++++++++++++++ texy/cs/custom-syntax.texy | 519 ++++++++++++++ texy/cs/develop.texy | 35 + texy/cs/priklady-vyuziti.texy | 26 - texy/cs/quickstart.texy | 205 ++++++ texy/cs/syntax-podrobne.texy | 889 ------------------------ texy/cs/syntax.texy | 997 +++++++++++++++++++-------- texy/cs/texy-vs-wysiwyg.texy | 23 - texy/en/@home.texy | 125 +++- texy/en/@menu.texy | 10 +- texy/en/@try.texy | 15 - texy/en/architecture.texy | 425 ++++++++++++ texy/en/configuration.texy | 719 ++++++++++++++++++++ texy/en/custom-handlers.texy | 850 +++++++++++++++++++++++ texy/en/custom-syntax.texy | 519 ++++++++++++++ texy/en/develop.texy | 35 + texy/en/quickstart.texy | 205 ++++++ texy/en/syntax-full.texy | 889 ------------------------ texy/en/syntax.texy | 1002 ++++++++++++++++++++-------- 44 files changed, 7159 insertions(+), 2984 deletions(-) delete mode 100644 texy/cs/@try.texy delete mode 100644 texy/cs/api-block-module.texy delete mode 100644 texy/cs/api-blockquote-module.texy delete mode 100644 texy/cs/api-emoticon-module.texy delete mode 100644 texy/cs/api-figure-module.texy delete mode 100644 texy/cs/api-heading-module.texy delete mode 100644 texy/cs/api-horizline-module.texy delete mode 100644 texy/cs/api-html-module.texy delete mode 100644 texy/cs/api-htmloutput-module.texy delete mode 100644 texy/cs/api-image-module.texy delete mode 100644 texy/cs/api-link-module.texy delete mode 100644 texy/cs/api-list-module.texy delete mode 100644 texy/cs/api-longwords-module.texy delete mode 100644 texy/cs/api-paragraph-module.texy delete mode 100644 texy/cs/api-phrase-module.texy delete mode 100644 texy/cs/api-script-module.texy delete mode 100644 texy/cs/api-table-module.texy delete mode 100644 texy/cs/api-texy.texy delete mode 100644 texy/cs/api-typography-module.texy delete mode 100644 texy/cs/api-zaklady.texy delete mode 100644 texy/cs/api.texy create mode 100644 texy/cs/architecture.texy create mode 100644 texy/cs/configuration.texy create mode 100644 texy/cs/custom-handlers.texy create mode 100644 texy/cs/custom-syntax.texy create mode 100644 texy/cs/develop.texy delete mode 100644 texy/cs/priklady-vyuziti.texy create mode 100644 texy/cs/quickstart.texy delete mode 100644 texy/cs/syntax-podrobne.texy delete mode 100644 texy/cs/texy-vs-wysiwyg.texy delete mode 100644 texy/en/@try.texy create mode 100644 texy/en/architecture.texy create mode 100644 texy/en/configuration.texy create mode 100644 texy/en/custom-handlers.texy create mode 100644 texy/en/custom-syntax.texy create mode 100644 texy/en/develop.texy create mode 100644 texy/en/quickstart.texy delete mode 100644 texy/en/syntax-full.texy diff --git a/texy/cs/@home.texy b/texy/cs/@home.texy index 3a23512250..cf88e684e4 100644 --- a/texy/cs/@home.texy +++ b/texy/cs/@home.texy @@ -1,32 +1,120 @@ Texy! je sexy! -============== +************** -Texy je program, díky kterému můžete snadno, bez odborných znalostí, psát texty na webové stránky. +.[perex] +Texy je **výkonný a bezpečný markup procesor** pro PHP, který převádí jednoduchý text do validního HTML. Na rozdíl od jiných markup jazyků není Texy jen další variantou Markdown – je to **plně konfigurovatelný systém**, který můžete přizpůsobit prakticky jakékoliv syntaxi. -Chcete zvýraznit písmo? Vytvořit nadpis či odrážky? Přidat obrázek nebo tabulku? Nemusíte zápasit se složitým textovým editorem. Stačí psát prostý text a Texy už úpravu zvládne za vás. Výsledkem bude hezky zformátovaná stránka. ---> [Vyzkoušejte si to | https://fiddle.nette.org/texy/] +Proč Texy? +========== -Texy dnes používají [tisíce spokojených uživatelů | napsali o Texy]. +Bezpečnost na prvním místě +-------------------------- -Co všechno umí? ---------------- +Texy je navrženo s důrazem na bezpečnost. Automaticky **chrání před XSS útoky**, validuje URL adresy a filtruje nebezpečné HTML značky. Vestavěný `safeMode()` je ideální pro zpracování uživatelského obsahu v komentářích nebo na fórech. -- vytvářet odkazy, odrážky, tabulky,... -- vkládat do textu obrázky -- zná českou typografii -- a navíc je **zdarma!** (pod licencí BSD a GPL) -- generuje vždy validní HTML kód -- vkládá pevné mezery za jednopísmenné předložky -- je dokonale konfigurovatelné a přizpůsobitelné +```php +Texy\Configurator::safeMode($texy); +// Nyní je Texy bezpečné pro obsah od uživatelů +``` -Objevte Texy! -------------- +Konfigurovatelnost bez kompromisů +--------------------------------- + +Chcete používat Markdown syntaxi? Nebo potřebujete úplně vlastní markup? **Texy to zvládne.** Můžete: + +- Vypnout nebo zapnout libovolné části syntaxe +- Změnit výchozí chování pomocí handlerů +- Přidat zcela vlastní syntaktické prvky +- Nakonfigurovat Texy tak, aby zpracovávalo Markdown nebo jakýkoliv jiný formát + +```php +$texy = new Texy; +$texy->allowed['image'] = false; // vypnout obrázky +$texy->allowed['phrase/strong'] = false; // vypnout tučné písmo +``` + + +České typografické speciality +----------------------------- + +Texy **dokonale rozumí češtině**. Automaticky: + +- Vkládá **pevné mezery** za jednopísmenné předložky a spojky: v autě, u okna, s kamarádem +- Rozděluje **dlouhá slova** podle slabik: nejneobhospodařovávatelnějšími +- Používá správné **typografické uvozovky**: „dvojité" a ‚jednoduché' +- Zaměňuje **spojovník za pomlčku**: 10–15 vs. česko-slovenský +- Přidává **nezalomitelné mezery** u telefonních čísel: +420 776 552 046 + + +Validní a wellformed HTML +------------------------- + +Texy generuje **vždy validní HTML5 kód**. Automaticky opravuje chybně vnořené značky, uzavírá nezavřené elementy a dbá na správnou strukturu dokumentu. Výstup je nejen validní, ale i **pěkně naformátovaný** s odsazením. + + +Co je Texy? +=========== + +Texy je **obecný procesor markup textu**. To znamená, že má sice svou výchozí syntaxi (podobnou Markdown, ale mnohem bohatší), ale můžete ji kompletně změnit nebo rozšířit. + +**Není to jen parser** – Texy je komplexní systém s modulární architekturou, kde každý modul zpracovává konkrétní část syntaxe (nadpisy, odkazy, obrázky, tabulky...). Díky systému handlerů můžete zasáhnout do libovolného bodu zpracování a změnit výsledek podle svých potřeb. + + +Texy vs. Markdown +================= + +Základní syntaxe je podobná, ale Texy nabízí mnohem více: + +|--------------------------- +| Funkce | Markdown | Texy +|--------------------------- +| Tučné písmo | `**text**` | `**text**` +| Kurzíva | `*text*` nebo `_text_` | `*text*` nebo `//text//` +| Nadpisy | `# Nadpis` | `# Nadpis` nebo podtržení +| Obrázky | `![alt](url)` | `[* url *]` +| Tabulky | omezené | plná podpora včetně sloučení +| Modifikátory | ne | ano – `.{color:red}[class]` +| Typografie | ne | ano – uvozovky, pomlčky, mezery +| Dělení slov | ne | ano – podle slabik +| Konfigurovatelnost | omezená | úplná – vlastní syntaxe +| Bezpečnost | závisí na impl. | vestavěná (safeMode) + +**Příklad rozdílů:** + +```texy +Markdown: +![Obrázek](image.jpg) + +Texy: +[* image.jpg 300x200 .(Popisek obrázku)[photo] <] +``` + +Texy umožňuje definovat rozměry, třídy, zarovnání a mnoho dalšího přímo v syntaxi. + + +Kdy použít Texy? +================ + +Texy je ideální pro: + +**CMS systémy** Potřebujete bezpečně zpracovávat obsah od editorů? Texy nabízí granulární kontrolu nad tím, co mohou uživatelé použít. + +**Blogy a dokumentace** Bohatá syntaxe pro tabulky, obrázky s popiskami, typografii a kód s syntax highlightingem. + +**Komentáře a diskuzní fóra** SafeMode zajistí, že uživatelé nemohou vložit nebezpečný kód, ale zároveň mají k dispozici formátování textu. + +**Projekty s vlastními požadavky** Potřebujete embed YouTube videí? Speciální syntax pro vaše makra? Vlastní markup jazyk? S Texy to vytvoříte snadno. + + +Historie +======== + +Texy vytvořil David Grudl před **20 lety** v roce 2004 jako jeden z prvních markup procesorů pro PHP. Původně bylo vyvinuto pro **PHP 4**, ale během své dlouhé historie prošlo mnoha aktualizacemi a dnes plně využívá všech možností **PHP 8**. + +Přes dvě dekády aktivního vývoje znamenají **vyzkoušenou a stabilní** knihovnu, které důvěřují stovky projektů. Texy je dnes **mature řešení** s velkou historií, ale stále aktivně udržované a moderní. -- Srovnání [Texy versus WYSIWYG editory | texy-vs-wysiwyg] -- [Příklady využití | priklady-vyuziti] -- [Základy syntaxe | syntax] {{maintitle: Texy – formátovač textů pro PHP}} diff --git a/texy/cs/@menu.texy b/texy/cs/@menu.texy index eb7458d858..bc703bedfb 100644 --- a/texy/cs/@menu.texy +++ b/texy/cs/@menu.texy @@ -1,8 +1,7 @@ -- [úvodní stránka | @home] -- [syntax stručně | syntax] -- [syntax podrobně | syntax-podrobne] -- [fiddle | https://fiddle.nette.org/texy/] -- [manuál | api] -- [blog | https://phpfashion.com/category/texy] -- [API | https://api.nette.org/texy/] -- [GitHub | https://github.com/dg/texy] +- [úvod | @home] +- [syntaxe | syntax] +- [pro programátory | develop] +- "blog .[link-external]":https://phpfashion.com/category/texy +- "hřiště .[link-external]":https://fiddle.nette.org/texy/ +- "API .[link-external]":https://api.nette.org/texy/ +- "GitHub .[link-external]":https://github.com/dg/texy diff --git a/texy/cs/@try.texy b/texy/cs/@try.texy deleted file mode 100644 index 9c443df131..0000000000 --- a/texy/cs/@try.texy +++ /dev/null @@ -1,15 +0,0 @@ -Vítejte! --------- - -Můžete používat syntax Texy!, pokud Vám vyhovuje: -- třeba **tučné** písmo nebo *kurzíva* -- a takto se dělá "odkaz":https://texy.info -- více najdete na stránce "syntax":[syntax] - - -Ale také můžete zůstat u HTML: -- takto HTML -- nebo i úplně hloupě, Texy! to pořeší - - -[syntax]: /cs/syntax diff --git a/texy/cs/api-block-module.texy b/texy/cs/api-block-module.texy deleted file mode 100644 index aae50f9b19..0000000000 --- a/texy/cs/api-block-module.texy +++ /dev/null @@ -1,11 +0,0 @@ -Třída Texy\Modules\BlockModule -****************************** - -Má na starosti zpracování bloků `/-- xxx`. Modul vypneme zakázáním syntaxe `blocks`: - -/--code php -$texy->allowed['blocks'] = false; - -// nebo pro jednotlivé typy bloků -$texy->allowed['block/code'] = false; -\-- diff --git a/texy/cs/api-blockquote-module.texy b/texy/cs/api-blockquote-module.texy deleted file mode 100644 index b9fd28e72a..0000000000 --- a/texy/cs/api-blockquote-module.texy +++ /dev/null @@ -1,8 +0,0 @@ -Třída Texy\Modules\BlockQuoteModule -*********************************** - -Má na starosti blokové citace. Modul vypneme zakázáním syntaxe `blockquote`: - -/--code php -$texy->allowed['blockquote'] = false; -\-- diff --git a/texy/cs/api-emoticon-module.texy b/texy/cs/api-emoticon-module.texy deleted file mode 100644 index 8f0f8fa475..0000000000 --- a/texy/cs/api-emoticon-module.texy +++ /dev/null @@ -1,47 +0,0 @@ -Třída Texy\Modules\EmoticonModule -********************************* - -Má na starosti nahrazování smajlíků za obrázky. Ve výchozím nastavení je modul vypnutý, zapneme jej povolením syntaxe `emoticon`: - -/--code php -$texy->allowed['emoticon'] = true; -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $icons | array | | tabulka všech smajlíků a odpovídajících obrázků -| $class | string | null | CSS třída -| $root | string | | kořenový adresář obrázků na webu -| $fileRoot | string | | kořenový adresář obrázků na disku - -Pokud nenastavíte hodnoty pro `$root` a `$fileRoot`, použijí se ty z modulu [Texy\Modules\ImageModule|api-image-module]. Výchozí seznam smajlíků je tento: - -/--code php -$icons = [ - ':-)' => 'smile.gif', - ':-(' => 'sad.gif', - ';-)' => 'wink.gif', - ':-D' => 'biggrin.gif', - '8-O' => 'eek.gif', - '8-)' => 'cool.gif', - ':-?' => 'confused.gif', - ':-x' => 'mad.gif', - ':-P' => 'razz.gif', - ':-|' => 'neutral.gif', -]; -\-- - -Můžete jej pozměnit přímo zásahem do pole: - -/--code php -$texy->emoticonModule->icons[':-)'] = 'smile.png'; - -unset($texy->emoticonModule->icons[':-P']); -\-- - -Poslední znak smajlíku se může opakovat, tedy klíč. `:-)` akceptuje i smajlík v podobě `:-)))`. diff --git a/texy/cs/api-figure-module.texy b/texy/cs/api-figure-module.texy deleted file mode 100644 index db0d483886..0000000000 --- a/texy/cs/api-figure-module.texy +++ /dev/null @@ -1,33 +0,0 @@ -Třída Texy\Modules\FigureModule -******************************* - -Má na starosti obrázky s popiskou. Module vypneme zakázáním syntaxe `figure`: - -/--code php -$texy->allowed['figure'] = false; -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $class | string | `'figure'` | třída neplovoucího kontejneru `
` -| $leftClass | string | null | třída `
` plovoucího vlevo -| $rightClass | string | null | třída `
` plovoucího vpravo -| $widthDelta | int | 10 | offset pro výpočet šířky - - -Neplovoucím kontejnerům `
` bude přiřazena třída `$class`, plovoucím `$leftClass` nebo `$rightClass`. Pokud není specifikována třída u plovoucích kontejnerů, Texy ji sestaví takto: - -`$texy->figureModule->class . '-' . $texy->alignClasses['left'] resp. 'right'` - -Není-li nastavená třída ani v poli `$alignClasses`, Texy kontejner zarovnává CSS vlastností `float`. - -U plovoucích kontejnerů Texy počítá a nastavuje šířků podle vzorce: - -`šířka
= šířka obrázku + $widthDelta` - -Je tedy nutné zjistit šířku obrázku. K tomu je potřeba korektně nastavit cestu `$texy->imageModule->fileRoot`, viz [Texy\Modules\ImageModule | api-image-module]. diff --git a/texy/cs/api-heading-module.texy b/texy/cs/api-heading-module.texy deleted file mode 100644 index 701de25493..0000000000 --- a/texy/cs/api-heading-module.texy +++ /dev/null @@ -1,42 +0,0 @@ -Třída Texy\Modules\HeadingModule -******************************** - -Má na starosti nadpisy a titulky. Module (de)aktivujeme zakázáním syntaxe: - -/--code php -// podtržené titulky -$texy->allowed['heading/underlined'] = false; - -// ohraničené titulky -$texy->allowed['heading/surrounded'] = false; -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $title | string | null | nejvyšší titulek -| $TOC | array | | zde se vygeneruje obsah -| $generateID | boolean| false | generovat titulkům ID? -| $idPrefix | string | `'toc-'` | prefix pro generované ID -| $top | int | 1 | strop, úroveň nejvyššího titulku -| $moreMeansHigher | boolean | true | více znaků znamená vyšší titulek -| $balancing | int | *dynamic* | způsob vážení titulků -| $levels | array | | tabulka vážení titulků - -Po zpracování textu bude první titulek uložen do proměnné $title (bez HTML kódování, tedy vhodné pro použití v ``). - -Způsob vážení titulků určuje $balancing, které může nabývat hodnot `Texy\Modules\HeadingModule::DYNAMIC` (výchozí) nebo `Texy\Modules\HeadingModule::FIXED`. Více informací [ve fóru | https://forum.texy.info/cs/viewtopic.php?pid=22]. - - -Příklady --------- - -Nejvyšší titulek bude mít úroveň `<h2>` - -/--code php -$texy->headingModule->top = 2; -\-- diff --git a/texy/cs/api-horizline-module.texy b/texy/cs/api-horizline-module.texy deleted file mode 100644 index ebbd47da44..0000000000 --- a/texy/cs/api-horizline-module.texy +++ /dev/null @@ -1,8 +0,0 @@ -Třída Texy\Modules\HorizLineModule -********************************** - -Má na starosti horizontální čáry. Modul vypneme zakázáním syntaxe `horizline`: - -/--code php -$texy->allowed['horizline'] = false; -\-- diff --git a/texy/cs/api-html-module.texy b/texy/cs/api-html-module.texy deleted file mode 100644 index f925f2ade8..0000000000 --- a/texy/cs/api-html-module.texy +++ /dev/null @@ -1,21 +0,0 @@ -Třída Texy\Modules\HtmlModule -***************************** - -Má na starosti HTML značky a komentáře na vstupu. Modul vypneme zakázáním syntaxe: - -/--code php -// vypneme HTML značky -$texy->allowed['html/tag'] = false; - -// vypneme HTML komentáře -$texy->allowed['html/comment'] = false; -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $passComment | boolean | true | zobrazit HTML komentáře na výstupu? diff --git a/texy/cs/api-htmloutput-module.texy b/texy/cs/api-htmloutput-module.texy deleted file mode 100644 index ec049261fa..0000000000 --- a/texy/cs/api-htmloutput-module.texy +++ /dev/null @@ -1,17 +0,0 @@ -Třída Texy\Modules\HtmlOutputModule -*********************************** - -Má na starosti formátování výstupního XHTML / HTML. Formátuje výsledné HTML a zároveň hlídá a koriguje "wellformed" zápis. - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $indent | boolean | true | formátovat výstup do úhledné podoby? -| $baseIndent | int | 0 | minimální odsazení každého řádku -| $lineWrap | int | 80 | maximální šířka řádku - -Volitelné koncové značky se odstraňují pouze v režimu HTML. Zalamování řádků lze vypnout nastavením `$texy->htmlOutputModule->lineWrap = false`. diff --git a/texy/cs/api-image-module.texy b/texy/cs/api-image-module.texy deleted file mode 100644 index bebde249b3..0000000000 --- a/texy/cs/api-image-module.texy +++ /dev/null @@ -1,35 +0,0 @@ -Třída Texy\Modules\ImageModule -****************************** - -Má na starosti obrázky. Tzv. obrázky s popiskou zpracovává [Texy\Modules\FigureModule|api-figure-module]. Modul vypneme zakázáním syntaxe: - -/--code php -// zákaz obrázků -$texy->allowed['image'] = false; - -// zákaz referencí -$texy->allowed['image/definition'] = false; -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $root | string | `'images/'` | kořenový adresář obrázků na webu -| $linkedRoot | string | `'images/'` | kořenový adresář obrázků v pozici odkazu -| $fileRoot | string | | kořenový adresář obrázků na disku -| $leftClass | string | null | třída obrázku plovoucího vlevo -| $rightClass | string | null | třída obrázku plovoucího vpravo -| $defaultAlt | string | `''` | výchozí hodnota atributu `alt` - - -Fyzická cesta k obrázkům `$fileRoot` se používá ke zjištění jich rozměrů, které se pak uvedou v HTML výstupu. Modul se cestu pokusí detekovat z prostředí serveru, nicméně je vhodnější ji nastavit manuálně. - -Proměnná `$linkedRoot` určuje kořenový adresář obrázků, které jsou použity jako cíl odkazu, tedy `[odkaz | * img.jpg *]`. - -Plovoucím obrázkům je nastavena třída `$leftClass` resp. `$rightClass`, nebo `$texy->alignClasses['left']` resp. `'right'`. Pokud není ani jedno specifikováno, Texy obrázky zarovnává CSS vlastností `float`. - -Protože Texy umí překlápět obrázky při přejetí myškou (onmouseover efekt), je nutné zajistit včasné načtení těchto obrázků (preload). K tomu slouží děsně fikaný skript, který se aktivuje událostí `onload` u jednotlivých obrázků. Skript je uložen v proměnné `$onLoad`. diff --git a/texy/cs/api-link-module.texy b/texy/cs/api-link-module.texy deleted file mode 100644 index f9fc26e49a..0000000000 --- a/texy/cs/api-link-module.texy +++ /dev/null @@ -1,42 +0,0 @@ -Třída Texy\Modules\LinkModule -***************************** - -Má na starosti definice, reference, odkazy. Modul vypneme zakázáním syntaxe: - -/--code php -// vypnout zpracování definicí [ref]: www.texy.info -$texy->allowed['link/definition'] = false; - -// vypnout zpracování referencí [ref] -$texy->allowed['link/reference'] = false; - -// vypnout dělání www adres klikatelnými -$texy->allowed['link/url'] = false; - -// vypnout dělání emailových adres klikatelnými -$texy->allowed['link/email'] = false; -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $root | string | `''` | kořenový adresář relativních odkazů -| $imageClass | string | | třída pro odkazy vedoucí na obrázky (od Texy 2.2) -| $forceNoFollow | boolean | false | doplňovat `rel="nofollow"`? -| $shorten | boolean | true | zkracovat URL? - -Texy automaticky doplňuje atribut nofollow odkazům, které mají pseudotřídu `nofollow`, např. `"odkaz .[nofollow]":www.texy.info`. - - -Příklad -------- - -Chceme přidávat nofollow ke všem odkazům - -/--code php -$texy->linkModule->forceNoFollow = true; -\-- diff --git a/texy/cs/api-list-module.texy b/texy/cs/api-list-module.texy deleted file mode 100644 index 222f9d94e4..0000000000 --- a/texy/cs/api-list-module.texy +++ /dev/null @@ -1,12 +0,0 @@ -Třída Texy\Modules\ListModule -***************************** - -Má na starosti číslované, nečíslované a definiční seznamy. Modul vypneme zakázáním syntaxe: - -/--code php -// číslované a nečíslované seznamy -$texy->allowed['list'] = false; - -// definiční listy -$texy->allowed['list/definition'] = false; -\-- diff --git a/texy/cs/api-longwords-module.texy b/texy/cs/api-longwords-module.texy deleted file mode 100644 index 00c5ca765a..0000000000 --- a/texy/cs/api-longwords-module.texy +++ /dev/null @@ -1,19 +0,0 @@ -Třída Texy\Modules\LongWordsModule -********************************** - -Má na starosti rozdělení dlouhých slov. Modul vypneme zakázáním syntaxe `longwords`: - -/--code php -$texy->allowed['longwords'] = false; -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $wordLimit | int | 20 | maximální délka slova - -Texy vkládá do příliš dlouhých slov (nad $wordLimit) značku volitelného zalomení `­`. Snaží se přitom respektovat specifika dělení slov na slabiky. diff --git a/texy/cs/api-paragraph-module.texy b/texy/cs/api-paragraph-module.texy deleted file mode 100644 index 28b10e479e..0000000000 --- a/texy/cs/api-paragraph-module.texy +++ /dev/null @@ -1,4 +0,0 @@ -Třída Texy\Modules\ParagraphModule -********************************** - -Má na starosti jednotlivé odstavce textu. diff --git a/texy/cs/api-phrase-module.texy b/texy/cs/api-phrase-module.texy deleted file mode 100644 index c978f68adc..0000000000 --- a/texy/cs/api-phrase-module.texy +++ /dev/null @@ -1,22 +0,0 @@ -Třída Texy\Modules\PhraseModule -******************************* - -Má na starosti tzv. fráze, tedy úseky textu (tučný text, odkaz, ...). Můžeme vypínat a zapínat jednotlivé syntaxe: - -/--code php -$texy->allowed['phrase/strong'] = false; -... -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $tags | array | | HTML značky pro jednotlivé fráze -| $linksAllowed | boolean | true | je možné k frázi přidat odkaz? - - -Pod pojmem fráze se rozumí jakákoliv řádková syntax jako `**tučné**`, `//kurzíva//`, `subskript_2`, `[odkaz | www.texy.info`]. diff --git a/texy/cs/api-script-module.texy b/texy/cs/api-script-module.texy deleted file mode 100644 index 0e3ace4d82..0000000000 --- a/texy/cs/api-script-module.texy +++ /dev/null @@ -1,23 +0,0 @@ -Třída Texy\Modules\ScriptModule -******************************* - -Má na starosti volání uživatelských funkcí a vkládání externích dat. Modul vypneme zakázáním syntaxe `script`: - -/--code php -$texy->allowed['script'] = false; -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $separator | string | `';'` | oddělovat argumentů - - -Příklady --------- - -Najdete ve [fóru | https://forum.texy.info/cs/viewtopic.php?pid=1954]. diff --git a/texy/cs/api-table-module.texy b/texy/cs/api-table-module.texy deleted file mode 100644 index 6114f24229..0000000000 --- a/texy/cs/api-table-module.texy +++ /dev/null @@ -1,18 +0,0 @@ -Třída Texy\Modules\TableModule -****************************** - -Má na starosti tabulky. Modul vypneme zakázáním syntaxe `table`: - -/--code php -$texy->allowed['table'] = false; -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $oddClass | string | null | CSS třída přiřazená lichým řádkům tabulky -| $evenClass | string | null | CSS třída přiřazená sudým řádkům tabulky diff --git a/texy/cs/api-texy.texy b/texy/cs/api-texy.texy deleted file mode 100644 index 6eba43a35c..0000000000 --- a/texy/cs/api-texy.texy +++ /dev/null @@ -1,69 +0,0 @@ -Třída Texy\Texy -*************** - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| `$allowed` | mixed | | povolené "Texy syntaxe" -| `$allowedTags` | mixed | *validní značky* | povolené HTML značky -| `$allowedClasses` | mixed | Texy::ALL | povolené CSS třídy a identifikátory -| `$allowedStyles` | mixed | Texy::ALL | povolené CSS styly -| `$alignClasses` | array | *vynulované* | CSS třídy pro zarovnání textu a obrázků -| `$mergeLines` | boolean | true | spojovat řádky? -| `$tabWidth` | int | 8 | šířka tabulátorů kvůli převední na mezery -| `$obfuscateEmail` | boolean| true | maskovat emailové adresy před roboty? -| `$urlSchemeFilters` | array| null | - - -Proměnné `$allowedTags`, `$allowedClasses` a `$allowedStyles` mohou nabývat hodnot: - -- `Texy::ALL` - jsou povoleny všechny HTML značky resp. styly resp. třídy -- `Texy::NONE` - naopak jsou všechny zakázány -- *array* - výčet povolených hodnot - - `$allowedTags` - povolené značky tvoří klíče, viz příklady níže - - `$allowedClasses` - seznam tříd a ID, přičemž ID začínají prefixem # - - `$allowedStyles` - seznam CSS vlastností - -Pole `$alignClasses` definuje CSS třídy pro zarovnání textu a obrázků. Blíže popsáno [ve fóru | https://forum.texy.info/cs/viewtopic.php?id=532]. - -Texy spojuje řádky následující za sebou do odstavců. Toto chování je možné vypnout nastavením vlastnosti `$texy->mergeLines = false`. - - -Příklady --------- - -Povolíme pouze HTML elementy `<strong>, <div>, <a>` a některé jejich atributy: - -/--code php -$texy->allowedTags = [ - 'strong' => Texy::NONE, // <strong> nesmí mít žádné attributy - 'div' => Texy::ALL, // <div> může mít jakékoliv atributy - 'a' => ['href', 'lang', 'target'], // <a> může mít jen tyto atributy -]; -\-- - -Všechny HTML značky zakážeme: - -/--code php -$texy->allowedTags = Texy::NONE; -\-- - -Povolíme pouze CSS třídy `class1, class2` a CSS identifikátory `id1, id2` - -/--code php -$texy->allowedClasses = ['class1', 'class2', '#id1', '#id2']; -\-- - -Povolíme pouze CSS vlastnosti `font-size, color, width` - -/--code php -$texy->allowedStyles = ['font-size', 'color', 'width']; -\-- - -Místo přímých stylů `style="text-align:left"` apod. používej třídy `class="left"` apod. - -/--code php -$texy->alignClasses['left'] = 'left'; -$texy->alignClasses['right'] = 'right'; -... // dále možno definovat: center, justify, top, bottom, middle -\-- diff --git a/texy/cs/api-typography-module.texy b/texy/cs/api-typography-module.texy deleted file mode 100644 index a66748609f..0000000000 --- a/texy/cs/api-typography-module.texy +++ /dev/null @@ -1,21 +0,0 @@ -Třída Texy\Modules\TypographyModule -*********************************** - -Má na starosti typografické úpravy výsledného textu. Modul vypneme zakázáním syntaxe `typography`: - -/--code php -$texy->allowed['typography'] = false; -\-- - - -Konfigurace ------------ - -|---------------- -| proměnná | typ | výchozí | popis | -|---------------- -| $locale | string | `'cs'` | určení národních specifik -| static $locales | array | | předvolená specifika - - -Vlastnost $locale může nabývat hodnot: cs, en, fr, de, pl. Další specifika lze doplnit do statické proměnné $locales. diff --git a/texy/cs/api-zaklady.texy b/texy/cs/api-zaklady.texy deleted file mode 100644 index 3672e3ba65..0000000000 --- a/texy/cs/api-zaklady.texy +++ /dev/null @@ -1,25 +0,0 @@ -Základní dovednosti -******************* - -Jak přeformátovat text do HTML? Stačí do kódu začlenit knihovnu Texy, například pomocí Composeru: - -/-- -composer require texy/texy -\-- - -A vytvořit objekt `$texy = new Texy\Texy;`. Celý převod obstará metoda `$html = $texy->process($text)`, kde proměnná `$text` obsahuje vstupní text a vrací se zformátovaný HTML výstup. - -/--php -// vytvoříme objekt -$texy = new Texy\Texy; - -// můžeme jej nakonfigurovat -$texy->imageModule->root = 'images/'; - -// a zpracujeme vstupní $text -$html = $texy->process($text); -\-- - -Pokud potřebujete formátovat jednořádkový text (tedy bez blokových elementů), použijte `$html = $texy->processLine($text)`. - -V ukázce vidíte, jak je možné objekt `$texy` konfigurovat. Protože parametrů je hodně, jsou rozčleněny do logických celků označovaných jako moduly. Instance každého modulu je uložena v atributu `$texy->blockModule`, `$texy->emoticonModule` atd. (viz [jednotlivé moduly | api#moduly]). diff --git a/texy/cs/api.texy b/texy/cs/api.texy deleted file mode 100644 index e0d5c9e4db..0000000000 --- a/texy/cs/api.texy +++ /dev/null @@ -1,28 +0,0 @@ -Programátorský manuál -********************* - -Texy je napsané v objektovém PHP. Minimální požadovaná verze pro Texy 3.0 je PHP 7.1. - ---> [Základní dovednosti | api-zaklady] - ---> [API dokumentace | https://api.nette.org/texy/] - ---> Jednotlivé moduly: .[#moduly] -- [Texy\Texy | api-texy] - jádro Texy -- [Texy\Modules\BlockModule | api-block-module] - zpracování bloků `/-- xxx` -- [Texy\Modules\BlockQuoteModule | api-blockquote-module] - blokové citace -- [Texy\Modules\EmoticonModule | api-emoticon-module] - nahrazování smajlíků za obrázky -- [Texy\Modules\FigureModule | api-figure-module] - obrázky s popiskou -- [Texy\Modules\HeadingModule | api-heading-module] - nadpisy, titulky -- [Texy\Modules\HorizLineModule | api-horizline-module] - horizontální čáry -- [Texy\Modules\HtmlModule | api-html-module] - HTML značky a komentáře na vstupu -- [Texy\Modules\HtmlOutputModule | api-htmloutput-module] - formátování výstupního HTML -- [Texy\Modules\ImageModule | api-image-module] - obrázky -- [Texy\Modules\LinkModule | api-link-module] - odkazy a reference -- [Texy\Modules\ListModule | api-list-module] - číslované, nečíslované a definiční seznamy -- [Texy\Modules\LongWordsModule | api-longwords-module] - rozdělení dlouhých slov -- [Texy\Modules\ParagraphModule | api-paragraph-module] - jednotlivé odstavce textu -- [Texy\Modules\PhraseModule | api-phrase-module] - fráze, tedy úseky textu (tučný text, odkaz, ...) -- [Texy\Modules\ScriptModule | api-script-module] - volání uživatelských funkcí -- [Texy\Modules\TableModule | api-table-module] - tabulky -- [Texy\Modules\TypographyModule | api-typography-module] - typografické úpravy výsledného textu diff --git a/texy/cs/architecture.texy b/texy/cs/architecture.texy new file mode 100644 index 0000000000..bf213b08f0 --- /dev/null +++ b/texy/cs/architecture.texy @@ -0,0 +1,425 @@ +Architektura a principy +####################### + +.[perex] +Texy je nástroj pro převod textu napsaného ve vlastním markup jazyce do HTML. Na rozdíl od jednoduchých převodníků, které text zpracovávají lineárně pomocí série náhrad, používá Texy sofistikovaný systém založený na parsování, modulární architektuře a postupném budování DOM stromu. + +Základní tok zpracování probíhá ve čtyřech hlavních fázích: + +1. Předzpracování textu - normalizace, úprava mezer a tabulátorů, volání notification handlerů pro přípravu +2. Parsování - rozpoznání syntaxí pomocí regulárních výrazů a postupné budování DOM stromu +3. Post-processing - typografické úpravy, zpracování dlouhých slov, wellforming HTML +4. Finální sestavení - konverze DOM stromu do HTML řetězce + +Klíčovým rozdílem oproti naivním přístupům je oddělení fáze rozpoznávání syntaxí od jejich zpracování. Parser nejprve identifikuje, kde se v textu nachází která syntaktická konstrukce, a teprve poté předává nalezené části jednotlivým modulům ke zpracování. To umožňuje vnořování syntaxí a jejich postupné rozbalování. + +*Poznámka: všechny třídy se nacházejí ve jmenném prostoru `Texy`, takže pokud dokument zmiňuje například třídu `HtmlElement`, její plný název je `Texy\HtmlElement`. Moduly jsou ve jmenném prostoru `Texy\Modules`* + + +Klíčové komponenty +================== + +Architektura Texy se skládá z několika hlavních komponent, z nichž každá má jasně vymezenou zodpovědnost: + +Třída Texy funguje jako centrální orchestrátor celého systému. Obsahuje odkazy na všechny moduly, spravuje registrované syntaxe a handlery, udržuje stav zpracování a koordinuje jednotlivé fáze konverze. Je to jediné místo, kde se jednotlivé komponenty propojují. + +**[#Moduly]** představují funkční jednotky zodpovědné za konkrétní oblasti markup jazyka. Každý modul při své konstrukci registruje syntaxe, které rozpoznává, a element handlery, které je zpracovávají. Například PhraseModule se stará o inline formátování jako tučný nebo kurzívou psaný text, zatímco TableModule zpracovává tabulky. Moduly jsou navrženy jako samostatné, znovupoužitelné jednotky s vlastní konfigurací přístupnou přes veřejné properties. + +**[#Parsery]** existují ve dvou variantách podle typu zpracovávaného obsahu. BlockParser zpracovává blokové struktury jako odstavce, nadpisy, seznamy nebo tabulky. Prochází text po řádcích, hledá začátky blokových konstrukcí a předává je *syntax handlerům*. LineParser se stará o inline syntaxe uvnitř řádků - odkazy, obrázky, formátování textu. Na rozdíl od BlockParser umožňuje vnořování syntaxí a jejich postupné rozbalování. + + +Základní terminologie +===================== + +Pro správné pochopení fungování Texy je nutné rozlišovat několik klíčových pojmů, které se v dokumentaci často objevují. + +**Syntax** označuje pojmenovanou syntaktickou konstrukci markup jazyka. Každá syntax má jedinečný název, například `phrase/strong` pro tučný text nebo `image` pro obrázky. Název syntaxe se používá pro její zapnutí či vypnutí v poli `Texy::$allowed` a předává se jako parametr do syntax handlerů pro rozlišení, která konkrétní syntax byla nalezena. + +**Pattern** je regulární výraz, který definuje, jak syntax vypadá v textu. Pattern je implementační detail syntaxe - autor syntaxe musí napsat regex, který ji rozpozná, ale z pohledu uživatele Texy je podstatnější název syntaxe a její význam. Jeden modul typicky registruje více syntaxí s různými patterny. + +**Syntax handler** je funkce volaná parserem ve chvíli, kdy najde výskyt syntaxe v textu. Dostává nalezený text a vrací `HtmlElement` nebo řetězec, který se vloží na původní místo. Syntax handler je místem, kde se rozhoduje, co se s nalezenou syntaxí stane - typicky vyvolá element handler pro vlastní zpracování. + +**Element** je prvek, pro který se generuje HTML reprezentace. Například `image` je element pro obrázky, `linkURL` pro odkazy, `phrase` pro inline formátování. Každý element má svůj výchozí element handler, který se stará o standardní zpracování. + +**Element handler** je funkce registrovaná pro určitý typ prvků a volaná přes systém HandlerInvocation. Charakteristické je použití metody `proceed()`, která umožňuje delegovat zpracování na další handler v řetězu nebo na výchozí handler modulu. Element handlery slouží k modifikaci nebo nahrazení výchozího chování. + +**Notification handler** je funkce volaná pro notifikaci o určité události. Na rozdíl od element handlerů nevrací žádnou hodnotu a nemůže ovlivnit výsledek zpracování. Používá se pro přípravu dat, logování nebo modifikace již vytvořeného DOM stromu. + +Rozdíl mezi jednotlivými handlery je klíčový pro pochopení architektury. Syntax handler je těsně svázán s parserem a konkrétním patternem - řeší otázku *co dělat, když parser najde tento pattern*. Element handlery jsou na vyšší úrovni abstrakce - řeší otázku *jak zpracovat tento typ prvku*, bez ohledu na to, která konkrétní syntax ji vytvořila. + + +Celkový tok zpracování +====================== + +Když Texy dostane vstupní text, projde následujícím procesem zpracování. + +V předzpracování dochází k normalizaci textu. Koncové značky řádků se sjednotí na Unix formát, mezery se standardizují a tabulátory se případně nahradí mezerami. Následně se vyvolají *notification handlery* registrované pro událost `beforeParse`. Tyto handlery mohou provést přípravu dat, například načíst definice referencí nebo upravit konfiguraci podle obsahu textu. + +Samotné parsování začíná vytvořením kořenového `HtmlElement`, který reprezentuje dokument. Texy pak rozhodne, zda text zpracovat jako jeden řádek nebo jako kompletní dokument s blokovými strukturami. V případě blokového zpracování se vytvoří BlockParser, který postupně prochází text a hledá jednotlivé blokové konstrukce. + +LineParser pracuje jinak než BlockParser. Neprochází text lineárně, ale postupně hledá nejbližší výskyt jakékoliv registrované syntaxe. Když nějakou najde, zavolá příslušný syntax handler, který vytvoří odpovídající HTML element. Tento element se pomocí speciálního maskování vloží zpět do textu a parser pokračuje dál. Díky tomu může najít a zpracovat syntaxe vnořené uvnitř již zpracovaných konstrukcí. + +Po dokončení parsování vznikne kompletní DOM strom reprezentující strukturu dokumentu. Texy vyvolá notification handlery pro událost `afterParse`, které mohou provést závěrečné úpravy stromu, například doplnit identifikátory nadpisů nebo sestavit obsah. + +Post-processing probíhá během konverze DOM stromu na HTML řetězec. Každý element se rekurzivně převádí na HTML kód, přičemž se aplikují typografické úpravy jako nahrazení uvozovek, pomlček nebo vkládání nezlomitelných mezer. Dále se provádí wellforming HTML - automatické uzavírání tagů, oprava špatně vnořených elementů, formátování a odsazování kódu. + +Finální fází je dekódování všech maskovaných částí zpět na HTML tagy, odstranění pomocných značek a sestavení výsledného HTML řetězce. + + +Systém syntaxí +************** + +Syntax v terminologii Texy představuje pojmenovanou syntaktickou konstrukci markup jazyka. Je to abstraktní koncept spojující několik prvků: unikátní název, regulární výraz pro rozpoznání a způsob zpracování. Název syntaxe slouží jako identifikátor v celém systému - používá se v poli `Texy::$allowed` pro zapnutí či vypnutí, předává se do handlerů pro rozlišení typu konstrukce a objevuje se v dokumentaci a konfiguračních souborech. + +Jmenné konvence syntaxí následují dva hlavní vzory. Jednodušší syntaxe mají jednoslovný název odpovídající jejich účelu, například `image`, `table` nebo `script`. Složitější oblasti používají hierarchické pojmenování se lomítkem, například `phrase/strong`, `phrase/em` nebo `link/reference`. Lomítko slouží k logickému seskupení souvisejících syntaxí a usnadňuje hromadné operace s nimi. + + +Line syntaxe +============ + +Line syntaxe slouží k rozpoznávání inline prvků uvnitř řádků textu. Typicky jde o formátování jako tučný nebo kurzívou psaný text, odkazy, obrázky nebo inline kód. Charakteristické pro line syntaxe je, že mohou být vnořené do sebe a parser je postupně rozbaluje. + +Registrace line syntaxe probíhá voláním `Texy::registerLinePattern()` s několika parametry. První je syntax handler, tedy callback volaný při nálezu. Druhý parametr je regulární výraz definující podobu syntaxe v textu. Třetí parametr je název syntaxe používaný v celém systému. Volitelný čtvrtý parametr je další regex pro test, zda má smysl pattern vůbec hledat - používá se pro optimalizaci, aby se nespouštěl komplexní pattern na textu, který určitě nemůže matchnout. + +Pattern jako regulární výraz musí splňovat určitá pravidla. Nesmí být kotvený na začátek textu, protože se hledá kdekoliv v řádku. Měl by být co nejkonkrétnější, aby nedošlo k falešným matchům. + +Inline syntaxe uvnitř řádků textu zpracovává [#LineParser]. Když najde match, zavolá příslušný syntax handler. Ten dostává tři parametry. První je instance LineParser, která poskytuje přístup k Texy objektu a dalším informacím o kontextu. Druhý parametr je pole s výsledky regex matche včetně podvýrazů. Třetí parametr je název syntaxe, který je užitečný, když stejný callback obsluhuje více syntaxí. Handler musí vrátit buď `HtmlElement`, nebo řetězec, nebo null pokud zpracování odmítá. + + +Block syntaxe +============= + +Block syntaxe rozpoznávají víceřádkové blokové konstrukce jako nadpisy, seznamy, tabulky, citace nebo speciální bloky. Na rozdíl od line syntaxí se block syntaxe nikdy nepřekrývají - každý řádek textu patří maximálně do jedné blokové konstrukce. + +Registrace block syntaxe používá `Texy::registerBlockPattern()` se třemi parametry. Syntax handler, regulární výraz a název syntaxe. Pattern jako regulární výraz musí splňovat určitá pravidla. Musí matchnout od začátku řádku a často obsahuje kotvu pro konec řádku. BlockParser automaticky přidává modifikátory Am, takže pattern by je neměl obsahovat. + +Block syntaxe uvnitř dokumentu zpracovává [#BlockParser]. Když najde match, zavolá příslušný syntax handler. Ten dostává podobné parametry jako u line syntaxí - BlockParser instanci, pole s matchem a název syntaxe. Vrací `HtmlElement` reprezentující celý zpracovaný blok, nebo null při odmítnutí zpracování. + + +Zapnutí a vypnutí syntaxe +========================= + +Pole `Texy::$allowed` poskytuje jemně granulární kontrolu nad tím, které syntaxe jsou v Texy aktivní. Je to jednoduchý, ale mocný mechanismus pro konfiguraci chování bez nutnosti měnit kód modulů. Když zakážete syntaxi `phrase/strong` tímto nastavením, parser přestane hledat konstrukci tučného textu: + +```php +$texy->allowed['phrase/strong'] = false; +``` + +Kontrola probíhá jednou při začátku parsování, takže dynamická změna `$allowed` během zpracování nemá efekt. + +Při konstrukci modulů se pro většinu syntaxí nastavuje výchozí hodnota `$allowed`. Některé syntaxe jsou ve výchozím stavu zapnuté, protože tvoří základ markup jazyka. Jiné jsou vypnuté, protože jsou pokročilé nebo potenciálně nebezpečné. Například emotikony jsou vypnuté, protože ne každý dokument je potřebuje, zatímco základní formátování je zapnuté. + +Bezpečný režim je situace, kdy zpracováváte nedůvěryhodný vstup, například komentáře od uživatelů. Chcete povolit základní formátování, ale zakázat obrázky, skripty nebo HTML tagy. `Texy\Configurator::safeMode()` nastaví `$allowed` pro bezpečnou kombinaci syntaxí. Typicky zakáže image, figure, script a HTML tagy, ale ponechá odkazy a formátování. + + +Parsery +******* + + +Syntax handler +============== + +Jak jsme si říkali v předchozí části, LineParser nebo BlockParser prochází text a hledají všechny registrované patterny. Když najde match, zavolá příslušný syntax handler a předá mu informace o nálezu - zejména pole s výsledky regex matche. + +Syntax handler analyzuje nalezený text a připravuje data pro zpracování. Může extrahovat části textu z regex skupin, vytvořit pomocné objekty jako `Link` nebo `Image`, parsovat modifikátory. Rozhoduje také, jaký element handler vyvolat. Zavolá `Texy::invokeAroundHandlers()` s názvem elementu a připravenými parametry. Tím začne jejich vykonávání. Vrácený výsledek se dostává zpět do syntax handleru, který ho vrátí parseru. + + +Element handler +=============== + +Element handlery implementují vzor chain of responsibility, který umožňuje složit výsledné chování z více vrstev. + +Registrace element handleru probíhá voláním `Texy::addHandler()` se dvěma parametry - názvem elementu a funkcí handleru. Jeden název elementu může mít zaregistrováno více handlerů, které se pak vykonávají v pořadí od posledně registrovaného k prvnímu. + +Název elementu identifikuje, o jaký typ zpracování jde, například `phrase` pro formátování, `image` pro obrázky nebo `link` pro odkazy (pozor: jde o něco jiného než názvy syntaxe). Někdy se používají složené názvy jako `linkReference` nebo `linkEmail` pro rozlišení různých druhů odkazů. Názvy jsou obecnější než názvy syntaxí - zatímco syntaxe `phrase/strong` je specifická konstrukce, element `phrase` pokrývá všechny druhy inline formátování. + +Vyvolání element handleru používá metodu `Texy::invokeAroundHandlers()`. Tato metoda dostává název prvku, instanci parseru a pole parametrů. Vytvoří HandlerInvocation objekt, který zapouzdřuje celý řetěz zaregistrovaných handlerů. První handler v řetězu dostane kontrolu a rozhoduje, zda zavolat `HandlerInvocation::proceed()` pro pokračování na další handler, nebo vrátit vlastní výsledek. + +HandlerInvocation objekt je klíčem k pochopení, jak řetězení funguje. Obsahuje zásobník všech handlerů pro daný prvek a aktuální pozici v tomto zásobníku. Když handler zavolá `proceed()`, HandlerInvocation posune pozici o jedno místo zpět v zásobníku a zavolá další handler. Pokud handler zavolá `proceed()` s modifikovanými parametry, tyto nové parametry se předají všem následujícím handlerům. Pokud handler vůbec nezavolá `proceed()`, řetěz se přeruší a jeho návratová hodnota se stane výsledkem celého zpracování. + +Pořadí vykonávání handlerů je od posledně zaregistrovaného k prvnímu. To znamená, že uživatelský handler zaregistrovaný dodatečně dostane kontrolu první a může rozhodnout, zda vůbec zavolá výchozí handler modulu. Toto pořadí umožňuje uživatelům přepsat výchozí chování bez nutnosti měnit kód modulu. + +Typické použití element handleru vypadá následovně. Handler zkontroluje vstupní parametry a rozhodne, zda chce zasáhnout do zpracování. Pokud ano, upraví data, zavolá `proceed()` s novými parametry a případně ještě upraví vrácený výsledek. Pokud chce handler úplně nahradit výchozí zpracování, vytvoří vlastní výsledek a vrátí ho bez volání `proceed()`. + + +Notification handler +==================== + +Notification handlery představují jednodušší, jednosměrný komunikační mechanismus. Na rozdíl od element handlerů neslouží k transformaci dat, ale k provedení vedlejších akcí. + +Registrace notification handleru používá stejnou metodu `Texy::addHandler()` jako element handlery. Rozdíl je v tom, jak se handler používá - notification handler nevrací žádnou hodnotu a nemá přístup k HandlerInvocation. První parametr je název události. Používají se popisné názvy jako `beforeParse` a `afterParse` pro globální události okolo parsování, nebo specifičtější jako `afterTable`, `afterList`, `afterBlockquote` pro události po vytvoření konkrétní struktury. Prefix before/after jasně indikuje časování události. + +Vyvolání notification handlerů používá metodu `Texy::invokeHandlers()`. Tato metoda jednoduše zavolá všechny zaregistrované handlery v pořadí a ignoruje jejich návratové hodnoty. Notification handlery dostanou parametry předané při vyvolání, ale nemohou je měnit pro další handlery v řadě. + +Typické použití notification handlerů zahrnuje několik scénářů. Handler pro událost `beforeParse` může načíst definice referencí z textu ještě před začátkem parsování. Handler `afterParse` může projít vytvořený DOM strom a doplnit chybějící atributy nebo sestavit tabulku obsahu. Handlery jako `afterTable` nebo `afterList` umožňují modulům provést závěrečné úpravy vytvořených struktur. + +Důležitý rozdíl oproti element handlerům je v tom, že notification handlery nemohou zabránit dalšímu zpracování. Všechny zaregistrované handlery se vždy vykonají, žádný nemůže přerušit řetěz. To je zamýšlené chování - notification handlery jsou o vedlejších efektech, ne o kontrole toku. + + +LineParser +========== + +LineParser zpracovává inline syntaxe uvnitř řádků textu postupným způsobem, který umožňuje vnořování a složité interakce mezi syntaxemi. + +Základní princip spočívá v hledání prvního výskytu jakékoliv syntaxe. V každé iteraci projde všechny syntaxe a zjistí, která z nich matchuje nejblíže aktuální pozici v textu. Tato syntax *vyhrává* a zpracuje se. Pokud více syntaxí matchuje na stejné pozici, vyhrává ta, která byla registrována dříve - to je priorita podle pořadí registrace. + +Když parser najde nejbližší match, zavolá příslušný syntax handler. Ten vrátí výsledek, který může být `HtmlElement` nebo řetězec. A tímto výsledkem se přepíše nalezený match v textu. + +Poté hledá znovu od aktuální pozice. Tento systém zajišťuje, že parser vždy vidí aktuální stav textu. Když nahradíme match novým textem, který může obsahovat další syntaxe, tyto syntaxe se najdou v příští iteraci. + +Property `$again` na LineParser objektu slouží k jemnému řízení toho, zda by se měla právě matchnutá syntaxe hledat znovu na stejné pozici po zpracování aktuálního matche. Výchozí hodnota je false, která říká: *Na této pozici už nemá smysl hledat tuto stejnou syntaxi. Posuň se dál.* + +Průchod končí, když parser dojde na konec textu nebo když už žádná syntaxe nemá další match. Výsledkem je text, kde všechny rozpoznatelné syntaxe byly zpracovány a nahrazeny výsledky, připravený pro finální konverzi. + + +Vnořování +--------- + +Schopnost zpracovat vnořené syntaxe je jednou z klíčových vlastností LineParser a představuje základní výzvu - jak zabránit tomu, aby již zpracované HTML tagy byly omylem interpretovány jako další syntax k zpracování. + +Když parser zpracovává text obsahující vnořené syntaxe, nejprve najde vnější konstrukci. Například v textu `"odkaz **tučný** text":URL` parser nejprve najde syntaxi pro odkaz s uvozovkami. Pattern pro tuto syntaxi matchuje celý řetězec od první uvozovky po dvojtečku a URL. Syntax handler vytvoří `HtmlElement` pro tag `<a>` a obsah `odkaz **tučný** text` se přidá jako potomek elementu. Tento řetězec vloží zpět do textu a pokračuje v hledání dalších syntaxí (`**tučný**`, který představuje tučný text). + +Ale teď má problém - v textu jsou také HTML značky, která může matchnout jako začátek jiné syntaxe. Parser by začal zpracovávat již hotové HTML tagy jako kdyby byly část původního textu. + +Nechceme, aby parser viděl HTML tagy. Potřebujeme nějaký způsob, jak rozlišit již zpracované části od částí čekajících na zpracování. Metoda `Texy::protect()` řeší tyto problémy elegantním způsobem - nahradí HTML tagy unikátním placeholderem složeným z control characters - speciálních bytů mimo tisknutelné ASCII. + +Když se tedy `HtmlElement` převádí na řetězec (pomocí `toString()`), výsledek nevypadá jako `<a href="...">odkaz **tučný** text</a>`, ale například jako `\x17\x18\x19\x17odkaz **tučný** text\x17\x18\x1A\x17`. + +V textu během parsování tak nejsou nikdy přítomny skutečné HTML tagy. Místo nich jsou pouze placeholdery. Ale vnitřní text zůstává a parser ho normálně vidí a může v něm hledat další syntaxe. To umožňuje postupné vnořování - vnější syntax se zamaskuje, ale její obsah je stále přístupný pro vnitřní syntaxe. + +Na konci zpracování metoda `Texy::unProtect()` projde výsledný HTML řetězec a nahradí všechny placeholdery jejich skutečnými hodnotami. Teprve v tomto okamžiku se do výstupu dostanou skutečné HTML tagy. + + +Úrovně maskování +---------------- + +Různé druhy obsahu používají různé control characters pro své placeholdery, což umožňuje syntaxím selektivně rozhodnout, co mohou obsahovat. + +- `Patterns::CONTENT_MARKUP` označuje běžný HTML markup jako tagy pro formátování nebo odkazy. Je to nejběžnější typ a používá ho většina inline elementů. Placeholder začíná a končí `\x17`. +- `Patterns::CONTENT_REPLACED` označuje obsah, který byl nahrazen něčím jiným, typicky obrázky nebo jiné replaced elementy. Používá `\x16` jako marker. +- `Patterns::CONTENT_TEXTUAL` označuje text, který byl escapován nebo jinak ošetřen, aby se nezpracovával. Používá se pro konstrukce jako code nebo notexy, kde chceme zobrazit původní text včetně markup symbolů, ne jejich interpretaci. +- `Patterns::CONTENT_BLOCK` označuje blokové elementy. Je to nejnižší úroveň v hierarchii. Používá `\x14` jako marker. + +Hierarchie těchto typů není jen konvence, ale má praktický důsledek. Konstantní Patterns::MARK je definována jako `\x14-\x1F`, tedy rozsah pokrývající všechny tyto typy plus rezervu. Syntaxe používají tuto konstantu ve svých patterns pro vyloučení maskovaných částí. + +Různé syntaxe mohou mít různé požadavky na to, co mohou obsahovat za placeholdery. Pattern, který chce vidět pouze čistý text bez jakýchkoliv maskovaných částí, použije vyloučení `[^\x14-\x1F]`. To odmítne všechny placeholdery všech typů. Příkladem je pattern pro obrázky - URL obrázku by neměla obsahovat žádné HTML tagy ani bloky. + +Pattern, který akceptuje nižší úrovně, ale odmítá vyšší, použije užší rozsah. Například `[^\x17-\x1F]` odmítne pouze `CONTENT_MARKUP` a výš, ale akceptuje `CONTENT_BLOCK`, `CONTENT_TEXTUAL` a `CONTENT_REPLACED`. To je užitečné, pokud chceme povolit bloky, ale ne inline markup. Praktickým příkladem je TypographyModule, který provádí typografické úpravy jako nahrazení uvozovek nebo vkládání nezlomitelných mezer. Tyto úpravy by se měly aplikovat na běžný text, ale ne uvnitř bloků kódu nebo preformátovaného textu. + + +Kolize syntaxí +-------------- + +Kolize nastává, když více syntaxí může matchnout na stejné pozici, a systém musí vybrat jednu z nich. + +Typickým příkladem jsou různé délky stejného symbolu. Syntaxe `phrase/strong+em` používá tři hvězdičky pro kombinaci tučného a kurzívy. Syntaxe `phrase/strong` používá dvě hvězdičky pro samotný tučný text. Syntaxe `phrase/em-alt` používá jednu hvězdičku pro kurzívu. Když parser najde text začínající třemi hvězdičkami, všechny tři syntaxe technicky mohou matchnout. + +PhraseModule řeší tuto kolizi registrací syntaxí v pořadí od nejdelší po nejkratší. Nejprve registruje `phrase/strong+em` s patternem pro tři hvězdičky. Pak `phrase/strong` s patternem pro dvě hvězdičky. Nakonec `phrase/em-alt` s patternem pro jednu hvězdičku. Díky tomuto pořadí se při nálezu tří hvězdiček zpracuje nejprve `phrase/strong+em` a kratší syntaxe nedostanou šanci. + +Další příklad jsou odkazy v různých formátech. Syntaxe `phrase/wikilink` používá pattern pro `[text|url]`. Syntaxe `link/reference` používá pattern pro `[ref]`. Oba začínají otevírací hranatou závorkou. Pokud je v textu `[text|url]`, oba patterns technicky mohou začít matchnout. + +Řešením je opět specifičnost patterns. Pattern pro `phrase/wikilink` je specifičtější - vyžaduje svislítko uvnitř závorek. Pokud text obsahuje svislítko, matchne `phrase/wikilink`. Pokud ne, pattern selže a `link/reference` má šanci. Pořadí registrace zde také hraje roli - `phrase/wikilink` by měla být registrována dříve než `link/reference`. + + +BlockParser +=========== + +BlockParser používá fundamentálně odlišný přístup k zpracování, který reflektuje povahu blokových konstrukcí. Základní rozdíl je v absenci prolínání. Zatímco LineParser umožňuje, aby syntaxe byly vnořené do sebe a postupně se rozbalovaly, BlockParser pracuje s předpokladem, že každý blok je samostatná jednotka. Jeden řádek nebo skupina řádků patří k maximálně jednomu bloku. Bloky se nepřekrývají, nekříží a nevnoří na úrovni BlockParser. + +BlockParser začíná vyhledáním všech bloků, respektive jejich začátků. Parser projde všechny registrované block syntaxe a najde všechny jejich výskyty. Pokud více syntaxí matchuje na stejné pozici, použije se pořadí registrace - dříve registrovaná syntaxe má přednost. + + +API pro syntax handler +---------------------- + +BlockParser poskytuje syntax handlerům API pro práci s víceřádkovými strukturami. + +Metoda `BlockParser::moveBackward()` slouží k návratu na předchozí řádky. Přijímá počet řádků, o které se má vrátit. Parser posune svou interní pozici směrem k začátku textu, dokud nepřejde přes specifikovaný počet konců řádků. To umožňuje callbacku začít číst od začátku struktury, i když pattern matchnul až uprostřed nebo na konci. + +Metoda `BlockParser::next()` slouží k čtení dalšího řádku odpovídajícího určitému patternu. Přijímá regex pattern (automaticky přidá modifikátory `Am`) a referenci na proměnnou pro výsledek matche. Pokud další řádek v textu matchuje poskytnutý pattern, metoda naplní výsledek, posune interní pozici za tento řádek a vrátí true. Pokud další řádek nematchuje, metoda vrátí false a pozice se nezmění. + + +Moduly +****** + +Moduly jsou základní organizační jednotkou v architektuře Texy. Každý modul zapouzdřuje kompletní funkcionalitu pro určitou oblast markup jazyka. + +Primární zodpovědností modulu je registrace syntaxí. V konstruktoru modul volá `Texy::registerLinePattern()` nebo `registerBlockPattern()` pro všechny syntaxe, které chce zpracovávat. Tím říká parseru: *Když najdeš tyto patterny, zavolej mě.* Modul tak definuje, které konstrukce v textu rozpoznává. + +Druhá zodpovědnost je implementace element handlerů. Modul registruje handlery pro elementy, které jeho syntaxe vyvolávají. Tyto handlery obsahují logiku pro převod nalezených konstrukcí na HTML elementy. Element handler rozhoduje, jaký element vytvořit, jaké atributy nastavit a jak zpracovat obsah. + +Třetí zodpovědnost je poskytnutí konfigurace. Moduly mají veřejné properties, které umožňují uživatelům Texy upravit chování modulu bez nutnosti měnit jeho kód. Například ImageModule má properties pro nastavení root cesty k obrázkům nebo výchozího alt textu. + +Čtvrtá zodpovědnost je správa stavu specifického pro modul. Například HeadingModule sleduje všechny nalezené nadpisy v poli TOC pro sestavení obsahu. LinkModule spravuje slovník referencí pro odkazy. Tento stav je soukromý pro modul a ostatní části systému k němu nepřistupují přímo. + +Moduly jsou navrženy jako nezávislé jednotky. Každý modul může fungovat samostatně a neměl by záviset na implementačních detailech jiných modulů. Komunikace mezi moduly probíhá přes sdílené objekty jako `Link` nebo `Image`, ne přes přímé volání metod. + + +Struktura typického modulu +========================== + +Většina modulů v Texy následuje podobnou strukturu, která reflektuje jejich úlohu v systému. + +Modul dědí od základní třídy Module, která poskytuje přístup k Texy objektu přes protected property `$texy`. Konstruktor modulu přijímá instanci Texy a uloží si ji. To umožňuje modulu přistupovat ke konfiguraci a volat metody na Texy objektu. + +V konstruktoru probíhá veškerá inicializace. Modul nastaví výchozí hodnoty konfiguračních properties, případně nastaví výchozí hodnoty v poli `Texy::$allowed` pro své syntaxe. Pak registruje své syntaxe voláním `registerLinePattern()` nebo `registerBlockPattern()`. Každá registrace spojuje pattern, syntax handler a název syntaxe. Nakonec modul registruje své element handlery voláním `addHandler()`. + +Syntax handlery jsou metody modulu, které parser volá při nálezu syntaxe. Tyto metody typicky extrahují části z regex matche, vytvářejí pomocné objekty a vyvolávají element handlery. Syntax handler rozhoduje, jaký element handler vyvolat a jaké parametry předat. + +Element handlery jsou metody implementující skutečné zpracování. Dostávají HandlerInvocation objekt jako první parametr, následovaný parametry specifickými pro daný element. Element handler vytváří `HtmlElement`, aplikuje modifikátory, zpracovává obsah a vrací výsledek. Je to místo, kde se rozhoduje o finální podobě HTML. + +Veřejné properties slouží jako rozhraní pro konfiguraci. Uživatel Texy může nastavit tyto properties pro přizpůsobení chování modulu. Properties jsou typicky primitivní typy nebo pole, ne složité objekty, aby konfigurace byla jednoduchá. + + +Přehled klíčových modulů +======================== + +Standardní distribuce Texy obsahuje několik modulů pokrývajících různé aspekty markup jazyka. + +- **PhraseModule** zpracovává inline formátování textu. Registruje syntaxe pro tučný text, kurzívu, podtržení, horní a dolní index, kód a další. Všechny tyto syntaxe vyvolávají společný handler pro element `phrase` a handler rozlišuje podle názvu syntaxe, jaký tag vytvořit. Modul umožňuje konfigurovat, které tagy se použijí pro jednotlivé druhy formátování. + +- **LinkModule** spravuje odkazy v dokumentu. Registruje syntaxe pro různé formáty odkazů - explicitní URL, emailové adresy, reference na definované odkazy. Poskytuje factory metody pro vytváření `Link` objektů a spravuje slovník referencí. Modul umožňuje konfigurovat root pro relativní odkazy, automatické `rel="nofollow"` pro externí odkazy a zkracování dlouhých URL. + +- **ImageModule** zpracovává obrázky podobným způsobem jako LinkModule odkazy. Registruje syntaxi pro inline obrázky a spravuje slovník referencí na definované obrázky. Poskytuje factory metody pro vytváření `Image` objektů a automatickou detekci rozměrů obrázků. Konfigurovatelné jsou cesty k obrázkům, výchozí alt text a CSS třídy pro zarovnání. + +- **HeadingModule** rozpoznává nadpisy v různých formátech - podtržené pomlčkami nebo rovnítky, obklopené mřížkami. Shromažďuje všechny nadpisy do pole TOC pro možné sestavení obsahu. Umožňuje konfigurovat generování ID, top úroveň nadpisů a režim balancování úrovní. + +- **ListModule** zpracovává seznamy - nečíslované, číslované a definiční. Rozpoznává různé typy odrážek a automaticky detekuje vnořování podle odsazení. Umožňuje konfigurovat, které znaky slouží jako odrážky a jaké HTML listy generovat. + +- **TableModule** je jedním z nejkomplexnějších modulů. Rozpoznává tabulky s hlavičkami, těly, titulky a podporuje colspan a rowspan. Zpracovává modifikátory pro řádky i buňky. + +- **BlockModule** zpracovává speciální bloky ohraničené `/--` a `\--`. Podporuje různé typy bloků - code pro kód, html pro přímé HTML, div pro generický kontejner. Umožňuje uživatelům definovat vlastní handlery pro vlastní typy bloků. + +- **TypographyModule** provádí post-processing pro typografické úpravy. Nahrazuje tři tečky elipsou, dvojité pomlčky en-dash, přímé uvozovky typografickými a vkládá nezlomitelné mezery. Pracuje na úrovni finálního řetězce mezi blokovými elementy. + +- **HtmlOutputModule** formátuje finální HTML výstup. Zajišťuje wellformed HTML automatickým zavíráním tagů, opravou nesprávného vnořování, odsazením kódu a zalamováním dlouhých řádků. Umožňuje konfigurovat úroveň odsazení a šířku řádků. + + +Interakce mezi moduly +===================== + +Ačkoliv jsou moduly navrženy jako nezávislé, v některých případech musí spolupracovat. + +Sdílené objekty jsou hlavní mechanismus komunikace. `Link` objekt vytvořený LinkModule může být předán ImageModule pro vytvoření obrázkového odkazu. `Image` objekt vytvořený ImageModule může být předán FigureModule pro vytvoření obrázku s popiskem. Tyto objekty zapouzdřují veškeré potřebné informace a poskytují společné rozhraní. + +Reference systém umožňuje oddělit definici od použití. LinkModule poskytuje metody `addReference()` a `getReference()` pro správu slovníku pojmenovaných odkazů. Uživatel může v jedné části dokumentu definovat referenci a v jiné ji použít. ImageModule má analogický systém pro reference na obrázky. Moduly používající reference volají factory metody, které samy kontrolují, zda jde o referenci nebo přímou hodnotu. + +Element handlers mohou volat jiné element handlery. PhraseModule při zpracování `phrase/span` s odkazem vytvoří `Link` objekt a zavolá element handler LinkModule pro vytvoření odkazu. Tím deleguje odpovědnost za vytvoření a konfiguraci odkazu na specializovaný modul. + +Vztahy mezi moduly jsou typicky jednostranné. PhraseModule zná LinkModule a ImageModule, protože vytváří odkazy a obrázky. Ale LinkModule a ImageModule neznají PhraseModule. To udržuje závislosti jednoduché a umožňuje snadné nahrazení nebo rozšíření modulů. + + +DOM reprezentace +**************** + +`HtmlElement` reprezentuje jeden uzel v DOM stromu a poskytuje rozhraní pro jeho manipulaci a zpracování. + +Základní struktura elementu obsahuje název tagu, asociativní pole atributů a pole potomků. Potomci mohou být další instance `HtmlElement` nebo prostě textové řetězce. Tato kombinace umožňuje reprezentovat libovolnou HTML strukturu. + +Název elementu se nastavuje a získává přes metody `setName()` a `getName()`. Speciální hodnota null jako název znamená transparentní element, který nemá tagy, jen jeho obsah. + +Atributy jsou veřejně přístupné přes property `$attrs` jako asociativní pole. Hodnoty mohou být řetězce, čísla, boolean nebo pole. Boolean true znamená atribut bez hodnoty (jako checked), false nebo null znamená atribut se vůbec nevykreslí. Pokud je hodnota pole, různé prvky se spojí podle typu atributu - pro class mezerami, pro style středníky. Metoda `setAttribute()` nastaví hodnotu atributu. Metoda `getAttribute()` vrací hodnotu atributu nebo null. + +Potomci se spravují přes několik metod. Metoda `add()` přidává potomka na konec. Metoda `insert()` vkládá potomka na specifikovanou pozici, volitelně nahrazuje existujícího potomka. Metoda `create()` vytváří nový `HtmlElement` jako potomka a vrací ho pro další manipulaci. Metoda `removeChildren()` odstraní všechny potomky. + +Element implementuje ArrayAccess interface, takže s potomky lze pracovat jako s polem. Zápis `$el[0]` vrací prvního potomka, `$el[0] = $child` nastaví prvního potomka. Tento přístup je pohodlný pro rychlou manipulaci s konkrétními potomky. + +Metoda `toString()` prochází element a jeho potomky rekurzivně a sestavuje řetězcovou reprezentaci. HTML tagy se okamžitě zamaskují pomocí `Texy::protect()`, takže do výsledku jde placeholder místo skutečných HTML znaků. + +Metody `toHtml()` a `toText()` vrací výsledek nemaskovaný včetně post-processingu. + + +Parsování obsahu +================ + +`HtmlElement` může rekurzivně parsovat svůj obsah, čímž umožňuje postupné budování DOM stromu. + +Metoda `parseLine()` slouží k parsování inline syntaxí v řetězci. Vytvoří novou instanci LineParser s aktuálním elementem jako kontejnerem. Zavolá `parse()` na parseru s poskytnutým textem. LineParser postupně najde a zpracuje všechny inline syntaxe a výsledné elementy nebo řetězce přidá jako potomky aktuálního elementu. Metoda vrací použitý LineParser pro případné další použití. + +Metoda `parseBlock()` parsuje text jako blokový obsah. Vytvoří BlockParser a zavolá na něm `parse()`. BlockParser najde všechny blokové konstrukce v textu, zpracuje je a přidá jako potomky elementu. Text mezi bloky se zpracuje jako odstavce, které interně používají LineParser. Metoda přijímá boolean parametr indikující, zda text pochází z odsazeného bloku, což ovlivňuje zpracování odstavců. + +Tyto parsovací metody umožňují rekurzivní zpracování. Syntax handler může vytvořit element, nastavit jeho základní vlastnosti a pak zavolat `parseLine()` nebo `parseBlock()` pro zpracování obsahu. Výsledkem je, že obsah elementu prochází stejným procesem parsování jako hlavní dokument, včetně rozpoznávání syntaxí a vyvolávání handlerů. + + +Validace +======== + +`HtmlElement` poskytuje mechanismy pro validaci atributů a obsahu podle HTML DTD (Document Type Definition). + +DTD je statické pole definující pro každý HTML tag, které atributy jsou povolené a jaký obsah může obsahovat. Texy načítá DTD ze souboru při inicializaci a uloží ho do statického pole. Struktura DTD mapuje název tagu na dvojici - pole povolených atributů a pole povoleného obsahu. + +Metoda `validateAttrs()` kontroluje atributy elementu podle DTD. Pro daný tag získá seznam povolených atributů. Prochází všechny atributy elementu a ty, které nejsou v seznamu, odstraní. Speciální případy jsou atributy začínající data- nebo aria-, které jsou povolené, pokud je v DTD zástupný záznam `data-*` nebo `aria-*`. + +Tato validace se typicky volá při aplikaci modifikátorů metodou `decorate()`. Zajišťuje, že i když uživatel zadá modifikátor s neplatným atributem pro daný tag, atribut se do finálního HTML nedostane. To je důležité pro bezpečnost a správnost HTML. + +Metoda `validateChild()` kontroluje, zda daný potomek může být obsahem elementu. Přijímá potomka (`HtmlElement` nebo název tagu) a DTD. Pokud je element v DTD definován, metoda zkontroluje, zda potomek je v seznamu povoleného obsahu. Pokud ano, vrací true. Pokud ne, vrací false. + +Tato validace se může použít při dynamickém sestavování DOM stromu pro zajištění korektní struktury. Například paragraph element nesmí obsahovat blokové elementy, takže `validateChild()` by odmítlo přidat div do p. V praxi Texy tuto validaci používá omezeně, protože struktura generovaná moduly je typicky správná by design. + +Kombinace `validateAttrs()` a `validateChild()` poskytuje mechanismus pro zajištění validního HTML, i když vstup obsahuje nedůvěryhodná data nebo špatně formované konstrukce. Texy může být nakonfigurováno pro striktní validaci nebo může validaci vypnout pro maximální flexibilitu. + + +Modifikátory +************ + +Modifikátory poskytují způsob, jak přidat elementům dodatečné atributy, třídy, styly a zarovnání bez nutnosti psát přímé HTML. + +Základní formát modifikátoru je tečka následovaná kombinací různých částí v kulatých, hranatých a složených závorkách: `.(title)[class1 class2 #id]{style:value}<align>^valign`. Celý modifikátor se píše před nebo na konec konstrukce, na kterou se aplikuje. Například `"**text** .(Důležité)[highlight]{color:red}"` vytvoří tučný text se třídou highlight, červenou barvou a title atributem Důležité. + +Kulaté závorky obsahují title atribut nebo alt text. Text uvnitř se použije jako hodnota title atributu na výsledném elementu. Pokud element je obrázek, může se použít jako alt text. Uvnitř kulatých závorek je možné escapovat závorku zpětným lomítkem. + +Hranaté závorky obsahují CSS třídy a volitelně ID. Třídy se píší jako slova oddělená mezerami. ID se píše s prefixem mřížky. Například `[main-content selected #article-5]` nastaví dvě třídy a jedno ID. Pokud je ID uvedeno vícekrát, použije se poslední. + +Složené závorky obsahují CSS styly nebo HTML atributy. Styly se píší ve standardním CSS formátu property:value. Více stylů se odděluje středníky. Některé property jsou rozpoznány jako HTML atributy - například `{href:url}` se převede na atribut href, ne na CSS style. To umožňuje nastavit atributy, které není možné vyjádřit jinak. + +Zarovnání se zadává pomocí speciálních znaků. `<` znamená vlevo, `>` vpravo, `=` pro do bloku, `<>` pro na střed. Vertikální zarovnání používá `^` pro nahoru, `-` pro střed a `_` pro dolů. Tyto zkratky se převádějí buď na CSS třídy nebo inline styly podle konfigurace. + +Části modifikátoru mohou být v libovolném pořadí a některé mohou být vynechány. Platný je modifikátor obsahující jen třídy `.[highlight]`, jen title `.(Poznámka)` nebo jen styl `.{color:blue}`. Parser rozpozná jednotlivé části podle ohraničujících znaků. + + +Modifier třída +============== + +Třída `Modifier` slouží k parsování a uchovávání informací z modifikátoru. + +Instance `Modifier` se typicky vytváří syntax handler, který předá konstruktoru text modifikátoru extrahovaný z regex matche. Konstruktor zavolá metodu `setProperties()`, která parsuje text a naplní properties objektu. + +Veřejné properties obsahují jednotlivé části modifikátoru. Property `$id` obsahuje ID elementu jako řetězec nebo null. Property `$classes` je asociativní pole, kde klíče jsou názvy tříd a hodnoty jsou true. Property `$styles` je asociativní pole mapující CSS property na hodnoty. Property `$attrs` je asociativní pole s HTML atributy, které nejsou styly ani třídy. + +Dvě speciální properties `$hAlign` a `$vAlign` obsahují horizontální a vertikální zarovnání jako řetězce `left`, `right`, `center`, `justify` nebo `top`, `middle`, `bottom`. Tyto hodnoty se později převádějí na CSS třídy nebo styly podle konfigurace Texy. + +Property `$title` obsahuje text z kulatých závorek, který se použije jako title atribut nebo alt text u obrázků. Text je automaticky unescapován z HTML entit a zbaven escapovaných závorek. + + +Aplikace na elementy +==================== + +`Modifier` objekt se aplikuje na `HtmlElement` pomocí metody `Modifier::decorate()`. + +Metoda `decorate()` přijímá instanci Texy a `HtmlElement` jako parametry. Postupně aplikuje jednotlivé části modifikátoru na element s ohledem na konfiguraci Texy, která může některé části zakázat nebo omezit. + +Aplikace atributů kontroluje, které atributy jsou povolené pro daný tag podle `Texy::$allowedTags` konfigurace. Pokud jsou všechny atributy povolené, zkopírují se všechny atributy z `Modifier` do elementu. Pokud je povolen jen seznam konkrétních atributů, zkopírují se pouze ty, které jsou na seznamu. + +Title atribut se vždy aplikuje, pokud je nastaven, ale text prochází typografickým post-processingem pro nahrazení uvozovek a dalších úprav. + +Aplikace tříd a ID kontroluje konfiguraci `Texy::$allowedClasses`. Pokud jsou všechny třídy povolené, přidají se všechny třídy z `Modifier` do elementu a nastaví se ID. Pokud je povolen jen seznam konkrétních tříd, přidají se pouze ty, které jsou na seznamu. ID se přidá, jen pokud je v seznamu povolen řetězec začínající mřížkou. + +Aplikace stylů probíhá podobně s kontrolou `Texy::$allowedStyles`. Povolené CSS properties se přidají do style atributu elementu. Pokud element již měl nějaké styly, modifikátorové styly se přidají nebo přepíšou existující. + +Zarovnání se aplikuje buď jako CSS třída nebo inline style. Pokud je v Texy konfigurováno `Texy::$alignClasses` mapování pro daný typ zarovnání, přidá se odpovídající CSS třída. Pokud ne, přidá se inline style s text-align nebo vertical-align property. + +Výsledkem je, že element má všechny atributy, třídy, styly a další vlastnosti z modifikátoru, ale pouze ty, které jsou povoleny aktuální konfigurací Texy. To zajišťuje bezpečnost při zpracování nedůvěryhodného vstupu. + + +Propagace modifikátorů +====================== + +Modifikátory procházejí systémem v několika fázích, přičemž si zachovávají flexibilitu a umožňují úpravy na různých úrovních. + +Syntax handler extrahuje text modifikátoru z regex matche a vytvoří novou instanci `Modifier` a naplní se jeho properties. + +`Modifier` objekt se předává jako parametr do element handlerů. Handler dostává již parsovaný objekt, ne surový text. To umožňuje handleru snadno přistupovat k jednotlivým částem modifikátoru - třídám, stylům, zarovnání. Handler může modifikátor upravit před aplikací, například přidat další třídy nebo změnit styly. + +Element handler vytváří `HtmlElement` a předá jej metodě `Modifier::decorate()`. V tomto okamžiku se modifikátor aplikuje na element. Metoda `decorate()` kontroluje konfigurace Texy a zajišťuje, že se aplikují pouze povolené části. + +V některých případech modul kombinuje více modifikátorů. Například TableModule parsuje modifikátory na úrovni tabulky, řádků i buněk. Modifikátor buňky je vlastně klony modifikátoru sloupce, na kterém se pak aplikují dodatečné úpravy z modifikátoru konkrétní buňky. To umožňuje výchozí styly pro celý sloupec s možností přepsání v jednotlivých buňkách. diff --git a/texy/cs/configuration.texy b/texy/cs/configuration.texy new file mode 100644 index 0000000000..39f95f7469 --- /dev/null +++ b/texy/cs/configuration.texy @@ -0,0 +1,719 @@ +Konfigurace +*********** + +.[perex] +Kompletní průvodce konfigurací Texy. Naučíte se ovládat všechny moduly, nastavit bezpečnost a přizpůsobit Texy vašim potřebám. + +Texy se konfiguruje pomocí **public properties** hlavní třídy `Texy\Texy` a jejích **modulů**. Každý modul je zodpovědný za zpracování konkrétní části syntaxe (obrázky, odkazy, nadpisy...). + +Základní přístup: + +```php +$texy = new Texy\Texy; + +// Konfigurace hlavní třídy +$texy->allowedTags = Texy\Texy::NONE; + +// Konfigurace modulu +$texy->imageModule->root = '/images/'; +``` + + +Třída Texy\Texy .[#texy-class] +============================== + +Hlavní třída obsahuje globální nastavení a vlastnosti ovlivňující celé zpracování. + + +Povolené syntaxe ($allowed) .{toc: $allowed} +-------------------------------------------- + +Pole `$allowed` kontroluje, které části Texy syntaxe jsou aktivní: + +```php +// Výchozí: všechny syntaxe povoleny (kromě emotikonů) +$texy->allowed['image'] = true; +$texy->allowed['emoticon'] = false; + +// Vypnout obrázky +$texy->allowed['image'] = false; + +// Vypnout HTML značky ve vstupu +$texy->allowed['html/tag'] = false; +$texy->allowed['html/comment'] = false; + +// Vypnout různé typy odkazů +$texy->allowed['link/reference'] = false; +$texy->allowed['link/email'] = false; +$texy->allowed['link/url'] = false; +``` + +**Kompletní seznam syntaxí:** + +|--- +| Klíč | Výchozí | Popis +|--- +| `image` | `true` | Obrázky `[* img.jpg *]` +| `figure` | `true` | Obrázky s popiskou +| `link/reference` | `true` | Reference `[ref]` +| `link/email` | `true` | Email adresy +| `link/url` | `true` | Automatické URL +| `link/definition` | `true` | Definice referencí +| `heading/underlined` | `true` | Podtržené nadpisy +| `heading/surrounded` | `true` | Ohraničené nadpisy +| `horizline` | `true` | Horizontální čáry +| `blockquote` | `true` | Citace +| `list` | `true` | Seznamy +| `list/definition` | `true` | Definiční seznamy +| `table` | `true` | Tabulky +| `phrase/strong` | `true` | Tučné písmo `**text**` +| `phrase/em` | `true` | Kurzíva `//text//` +| `phrase/em-alt` | `true` | Kurzíva `*text*` +| `phrase/code` | `true` | Kód ```text``` +| `phrase/ins` | `false` | Vložený text `++text++` +| `phrase/del` | `false` | Smazaný text `--text--` +| `phrase/sup` | `false` | Horní index `^^text^^` +| `phrase/sub` | `false` | Dolní index `__text__` +| `html/tag` | `true` | HTML značky ve vstupu +| `html/comment` | `true` | HTML komentáře +| `emoticon` | `false` | Emotikony `:-)`, `:-(` +| `blocks` | `true` | Bloky `/-- \--` +| `typography` | `true` | Typografické úpravy +| `longwords` | `true` | Dělení dlouhých slov + + +Povolené HTML značky ($allowedTags) .{toc: $allowedTags} +-------------------------------------------------------- + +Kontroluje, které HTML značky mohou být ve výstupu (a na vstupu): + +```php +// Výchozí: všechny validní HTML5 značky povoleny +$texy->allowedTags = Texy\Texy::ALL; + +// Zakázat všechny HTML značky +$texy->allowedTags = Texy\Texy::NONE; + +// Povolit jen konkrétní značky +$texy->allowedTags = [ + 'strong' => [], // <strong> bez atributů + 'a' => ['href', 'title'], // <a> s atributy + 'img' => Texy\Texy::ALL, // <img> s jakýmikoliv atributy +]; +``` + +**Formáty:** +- `Texy::ALL` – všechny značky povoleny +- `Texy::NONE` – žádné značky povoleny +- Pole – povolené značky jako klíče, povolené atributy jako hodnoty + + +Povolené CSS třídy ($allowedClasses) .{toc: $allowedClasses} +------------------------------------------------------------ + +Kontroluje, které CSS třídy a ID mohou být použity: + +```php +// Výchozí: všechny třídy a ID povoleny +$texy->allowedClasses = Texy\Texy::ALL; + +// Zakázat třídy a ID +$texy->allowedClasses = Texy\Texy::NONE; + +// Povolit konkrétní třídy a ID +$texy->allowedClasses = [ + 'highlight', + 'important', + '#main', // ID začínají # + '#sidebar', +]; +``` + +Použití: +```texy +Text s třídou .[highlight] + +Text s ID .{toc: main} +``` + + +Povolené CSS styly ($allowedStyles) .{toc: $allowedStyles} +---------------------------------------------------------- + +Kontroluje, které inline CSS vlastnosti mohou být použity: + +```php +// Výchozí: všechny styly povoleny +$texy->allowedStyles = Texy\Texy::ALL; + +// Zakázat inline styly +$texy->allowedStyles = Texy\Texy::NONE; + +// Povolit konkrétní CSS vlastnosti +$texy->allowedStyles = [ + 'color', + 'background-color', + 'font-size', +]; +``` + +Použití: +```texy +Text s barvou .{color: red} +``` + + +CSS třídy pro zarovnání ($alignClasses) .{toc: $alignClasses} +------------------------------------------------------------- + +Místo inline stylů `style="text-align:left"` můžete použít CSS třídy: + +```php +// Výchozí: prázdné pole (použijí se inline styly) +$texy->alignClasses = [ + 'left' => null, + 'right' => null, + 'center' => null, + 'justify' => null, + 'top' => null, + 'middle' => null, + 'bottom' => null, +]; + +// Nastavit třídy +$texy->alignClasses['left'] = 'text-left'; +$texy->alignClasses['right'] = 'text-right'; +$texy->alignClasses['center'] = 'text-center'; +``` + +Použití: +```texy +Text zarovnaný doleva . + +Text zarovnaný doprava .> +``` + +S nastaveným `alignClasses` vygeneruje `<p class="text-left">` místo `<p style="text-align:left">`. + + +Další vlastnosti +---------------- + +```php +// Spojování řádků do odstavců (výchozí: true) +$texy->mergeLines = true; + +// Šířka tabulátoru (výchozí: 8) +$texy->tabWidth = 8; + +// Maskování emailů před roboty (výchozí: true) +$texy->obfuscateEmail = true; + +// Odstraňování měkkých spojovníků (výchozí: true) +$texy->removeSoftHyphens = true; + +// Element pro netextové odstavce (výchozí: 'div') +$texy->nontextParagraph = 'div'; +``` + + +Moduly +====== + +Každý modul zpracovává konkrétní část syntaxe. Moduly jsou přístupné jako public properties třídy `Texy\Texy`. + + +HeadingModule +------------- + +Zpracovává nadpisy (podtržené i ohraničené). + +```php +// Úroveň nejvyššího nadpisu (výchozí: 1) +$texy->headingModule->top = 1; // <h1> + +// Generovat automatická ID (výchozí: false) +$texy->headingModule->generateID = true; + +// Prefix pro generovaná ID (výchozí: 'toc-') +$texy->headingModule->idPrefix = 'section-'; + +// Více znaků = vyšší nadpis? (výchozí: true) +$texy->headingModule->moreMeansHigher = true; + +// Režim vyvažování (výchozí: DYNAMIC) +$texy->headingModule->balancing = Texy\Modules\HeadingModule::DYNAMIC; +``` + +Po zpracování: + +```php +// První nadpis (pro <title>) +echo $texy->headingModule->title; + +// Obsah (Table of Contents) +print_r($texy->headingModule->TOC); +``` + + +PhraseModule +------------ + +Zpracovává inline formátování (tučné, kurzíva, odkazy v textu...). + +```php +// HTML značky pro jednotlivé fráze (výchozí: viz níže) +$texy->phraseModule->tags = [ + 'phrase/strong' => 'strong', + 'phrase/em' => 'em', + 'phrase/code' => 'code', + // ... další +]; + +// Povolit odkazy ve frázích (výchozí: true) +$texy->phraseModule->linksAllowed = true; +``` + + +LinkModule +---------- + +Zpracovává odkazy, reference a URL. + +```php +// Kořenová cesta pro odkazy (výchozí: '') +$texy->linkModule->root = '/articles/'; + +// CSS třída pro odkazy na obrázky (výchozí: null) +$texy->linkModule->imageClass = 'image-link'; + +// Vždy přidávat rel="nofollow" (výchozí: false) +$texy->linkModule->forceNoFollow = false; + +// Zkracovat URL na čitelnější formu (výchozí: true) +$texy->linkModule->shorten = true; +``` + +**Reference:** + +```php +// Přidat referenci +$link = new Texy\Link('https://example.com'); +$link->modifier->title = 'Ukázková stránka'; +$link->label = 'Příklad'; +$texy->linkModule->addReference('example', $link); +``` + +Použití: +```texy +Odkaz na [example] +``` + + +ImageModule +----------- + +Zpracovává obrázky. + +```php +// Kořenová cesta pro obrázky (výchozí: 'images/') +$texy->imageModule->root = '/assets/images/'; + +// Kořenová cesta pro linkované obrázky (výchozí: 'images/') +$texy->imageModule->linkedRoot = '/assets/images/full/'; + +// Fyzická cesta na disku (pro zjištění rozměrů) +$texy->imageModule->fileRoot = __DIR__ . '/public/images/'; + +// CSS třída pro plovoucí obrázky (výchozí: null) +$texy->imageModule->leftClass = 'float-left'; +$texy->imageModule->rightClass = 'float-right'; + +// Výchozí alternativní text (výchozí: '') +$texy->imageModule->defaultAlt = 'Obrázek'; +``` + +**Reference:** + +```php +// Přidat referenci +$image = new Texy\Image; +$image->URL = 'photo.jpg'; +$image->modifier->title = 'Fotografie'; +$texy->imageModule->addReference('photo', $image); +``` + + +FigureModule +------------ + +Zpracovává obrázky s popiskou. + +```php +// HTML element (výchozí: 'div') +$texy->figureModule->tagName = 'figure'; + +// CSS třída (výchozí: 'figure') +$texy->figureModule->class = 'photo-figure'; + +// Třídy pro plovoucí obrázky (výchozí: null) +$texy->figureModule->leftClass = 'figure-left'; +$texy->figureModule->rightClass = 'figure-right'; + +// Offset pro výpočet šířky (výchozí: 10) +$texy->figureModule->widthDelta = 20; + +// Vyžadovat popisku (výchozí: true) +$texy->figureModule->requireCaption = true; +``` + + +ListModule +---------- + +Zpracovává odrážkové, číslované a definiční seznamy. + +```php +// Definice odrážek a stylu (výchozí: viz zdrojový kód) +$texy->listModule->bullets = [ + '*' => ['\*[\ \t]', 0, ''], + '-' => ['[\x{2013}-](?![>-])', 0, ''], + // ... další +]; +``` + + +TableModule +----------- + +Zpracovává tabulky. + +```php +// CSS třídy pro řádky (výchozí: null) +$texy->tableModule->oddClass = 'odd'; +$texy->tableModule->evenClass = 'even'; +``` + +*Poznámka: `oddClass` a `evenClass` jsou deprecated.* + + +HorizLineModule +--------------- + +Zpracovává horizontální čáry. + +```php +// CSS třídy podle typu (výchozí: null) +$texy->horizLineModule->classes = [ + '-' => 'hr-line', + '*' => 'hr-star', +]; +``` + + +TypographyModule +---------------- + +Zpracovává typografické úpravy. + +```php +// Locale (výchozí: 'cs') +$texy->typographyModule->locale = 'en'; +``` + +**Podporované locales:** +- `cs` – české uvozovky „text" a ‚text' +- `en` – anglické uvozovky "text" a 'text' +- `fr` – francouzské uvozovky «text» a ‹text› +- `de` – německé uvozovky „text" a ‚text' +- `pl` – polské uvozovky „text" a ‚text' + + +LongWordsModule +--------------- + +Rozděluje dlouhá slova pomocí `­`. + +```php +// Maximální délka slova (výchozí: 20) +$texy->longWordsModule->wordLimit = 25; +``` + + +EmoticonModule +-------------- + +Nahrazuje emotikony za obrázky nebo Unicode znaky. + +```php +// CSS třída (výchozí: null) +$texy->emoticonModule->class = 'emoji'; + +// Cesta k obrázkům (výchozí: null = použije imageModule->root) +$texy->emoticonModule->root = '/images/smilies/'; +$texy->emoticonModule->fileRoot = __DIR__ . '/public/smilies/'; + +// Definice emotikonů (výchozí: základní sada) +$texy->emoticonModule->icons = [ + ':-)' => '🙂', + ':-(' => '☹', + ';-)' => '😉', + // ... nebo cesty k obrázkům + ':cool:' => 'cool.gif', +]; +``` + + +HtmlModule +---------- + +Zpracovává HTML značky a komentáře ve vstupním textu. + +```php +// Zobrazit HTML komentáře na výstupu (výchozí: true) +$texy->htmlModule->passComment = true; +``` + + +HtmlOutputModule +---------------- + +Formátuje výstupní HTML. + +```php +// Formátovat výstup (odsazení) (výchozí: true) +$texy->htmlOutputModule->indent = true; + +// Základní odsazení (výchozí: 0) +$texy->htmlOutputModule->baseIndent = 0; + +// Maximální šířka řádku (výchozí: 80) +$texy->htmlOutputModule->lineWrap = 100; + +// Zachovat mezery v těchto elementech (výchozí: seznam) +$texy->htmlOutputModule->preserveSpaces = [ + 'textarea', 'pre', 'script', 'code', +]; +``` + + +ScriptModule +------------ + +Zpracovává volání `{{makro}}`. + +```php +// Oddělovač argumentů (výchozí: ',') +$texy->scriptModule->separator = ';'; +``` + + +Třída Texy\Configurator .{toc: Texy\Configurator} +================================================= + +Předpřipravené konfigurační sety pro časté use-case. + + +safeMode() – Bezpečný režim .{toc: safeMode()} +---------------------------------------------- + +Konfigurace pro zpracování **nedůvěryhodného obsahu** od uživatelů. + +```php +Texy\Configurator::safeMode($texy); +``` + +**Co dělá:** +- Zakáže třídy a ID (`$allowedClasses = NONE`) +- Zakáže inline styly (`$allowedStyles = NONE`) +- Povolí jen bezpečné HTML značky: + +```php +[ + 'a' => ['href', 'title'], + 'abbr' => ['title'], + 'b' => [], + 'br' => [], + 'cite' => [], + 'code' => [], + 'em' => [], + 'i' => [], + 'strong' => [], + 'sub' => [], + 'sup' => [], + 'q' => [], + 'small' => [], +] +``` + +- Filtruje URL schémata (jen `http:`, `https:`, `ftp:`, `mailto:`) +- Zakáže obrázky +- Zakáže definice referencí +- Zakáže HTML komentáře +- Přidá `rel="nofollow"` ke všem odkazům + + +disableLinks() – Vypnout odkazy .{toc: disableLinks()} +------------------------------------------------------ + +Zakáže všechny typy odkazů. + +```php +Texy\Configurator::disableLinks($texy); +``` + +**Co dělá:** +- Zakáže všechny typy odkazů (`link/reference`, `link/email`, `link/url`, `link/definition`) +- Zakáže odkazy ve frázích (`phraseModule->linksAllowed = false`) +- Odebere `<a>` z povolených značek + + +disableImages() – Vypnout obrázky .{toc: disableImages()} +--------------------------------------------------------- + +Zakáže všechny typy obrázků. + +```php +Texy\Configurator::disableImages($texy); +``` + +**Co dělá:** +- Zakáže obrázky (`image`, `figure`, `image/definition`) +- Odebere `<img>`, `<object>`, `<embed>`, `<applet>` z povolených značek + + +Bezpečnost +========== + +Texy je navrženo s důrazem na bezpečnost. Automaticky chrání před běžnými útoky. + + +Ochrana proti XSS +----------------- + +Cross-Site Scripting (XSS) je útok, kdy útočník vloží škodlivý JavaScript do stránky. + +**Příklad útoků, které Texy zablokuje:** + +```texy +Pokus o útok: <script>alert('XSS')</script> + +Pokus o útok: <img src=x onerror="alert('XSS')"> + +Pokus o útok: "klikni":javascript:alert('XSS') + +Pokus o útok: [* image.jpg onload="alert('XSS')" *] +``` + +Texy automaticky: +- **Validuje HTML** – odstraní nepovolené značky a atributy +- **Filtruje URL** – povolí jen bezpečná schémata (`http:`, `https:`, `mailto:`, `ftp:`) +- **Escapuje obsah** – správně escapuje text v atributech +- **Sanitizuje atributy** – odstraní event handlery (`onclick`, `onerror`, ...) + +```php +$texy = new Texy\Texy; +Texy\Configurator::safeMode($texy); + +$input = '<script>alert("XSS")</script>'; +$output = $texy->process($input); + +// Výstup: prázdný (script tag odstraněn) +``` + + +Validace URL +------------ + +Texy kontroluje URL ve všech odkazech a obrázcích: + +```php +$texy = new Texy\Texy; + +// Nastavit povolená schémata (výchozí v safeMode) +$texy->urlSchemeFilters[Texy\Texy::FILTER_ANCHOR] = + '#https?:|ftp:|mailto:#Ai'; +$texy->urlSchemeFilters[Texy\Texy::FILTER_IMAGE] = + '#https?:#Ai'; +``` + +**Příklady blokovaných URL:** + +```texy +"útok":javascript:alert('XSS') // blokováno +"útok":data:text/html,<script> // blokováno +[* javascript:alert() *] // blokováno +``` + + +Filtrování HTML značek +---------------------- + +Kontrola přes `$allowedTags`: + +```php +$texy = new Texy\Texy; + +// Povolit jen bezpečné značky +$texy->allowedTags = [ + 'p' => [], + 'strong' => [], + 'em' => [], + 'a' => ['href', 'title'], // jen tyto atributy +]; + +$input = '<p>Text <script>alert()</script></p>'; +$output = $texy->process($input); + +// Výstup: <p>Text alert()</p> +// (script tag odstraněn) +``` + + +Praktický příklad +----------------- + +```php +function processComment(string $userInput): string +{ + $texy = new Texy\Texy; + + // Bezpečný režim + Texy\Configurator::safeMode($texy); + + // Dodatečná omezení + $texy->allowed['link/url'] = false; // zakázat auto-linky + $texy->allowed['html/tag'] = false; // zakázat HTML + + // Zpracovat + return $texy->process($userInput); +} + +// Použití +$comment = $_POST['comment']; +$html = processComment($comment); +echo $html; // bezpečný výstup +``` + + +Best practices +-------------- + +1. **Vždy použijte safeMode()** pro uživatelský obsah +2. **Validujte vstup** před předáním Texy (délka, formát) +3. **Limitujte HTML značky** podle potřeby +4. **Kontrolujte výstup** – i když Texy je bezpečné, double-check nikdy neuškodí +5. **Logujte podezřelé pokusy** – může vám pomoci identifikovat útočníky + +```php +$texy = new Texy\Texy; +Texy\Configurator::safeMode($texy); + +// Logování +$texy->addHandler('htmlTag', function($invocation, $el, $isStart) { + if ($el->getName() === 'script') { + error_log('XSS attempt detected!'); + } + return $invocation->proceed(); +}); +``` diff --git a/texy/cs/custom-handlers.texy b/texy/cs/custom-handlers.texy new file mode 100644 index 0000000000..3cc43d4b78 --- /dev/null +++ b/texy/cs/custom-handlers.texy @@ -0,0 +1,850 @@ +Úprava chování prvků +******************** + +.[perex] +Tato kapitola popisuje, jak můžete změnit chování **existujících prvků** v Texy - například upravit, jak se zpracovávají obrázky, odkazy nebo formátování. Pokud chcete přidat **zcela novou syntaxi**, kterou Texy standardně nezná, přečtěte si kapitolu [Přidání vlastní syntaxe |custom-syntax]. + +Představte si, že chcete, aby standardní syntaxe pro obrázky `[* URL *]` rozpoznávala speciální adresu `[* youtube:dQw4w9WgXcQ *]` a místo běžného obrázku vytvořila embedded přehrávač. + +Nebo chcete obarvovat výpisy zdrojového kódu pomocí syntax highlighteru. A tak dále. Přesně k tomu slouží **element handlery** - funkce, které Texy volá při zpracování konkrétních prvků. Například zaregistrujete handler pro element `image`, který zkontroluje URL, a pokud začíná `youtube:`, vrátí iframe místo standardního obrázku. Neměníte syntaxi, jen upravujete, co se s nalezenou konstrukcí stane. + + +Elementy a jejich handlery +========================== + +V terminologii Texy je **element** název pro typ prvku, který může být v dokumentu zpracován. Například `image` je element pro obrázky, `linkURL` pro odkazy, viz [#výchozí elementy]. Každý element má svůj **výchozí handler**, který je implementován v příslušném modulu a stará se o standardní zpracování. + +Když napíšete v textu `[* image.jpg *]`, parser najde tuto syntaxi, vytvoří objekt `Texy\Image` s daty o obrázku a zavolá všechny handlery zaregistrované pro element `image`. Pokud žádný vlastní handler není, zavolá se pouze výchozí handler z `ImageModule`, který vytvoří HTML tag `<img>`. + +Handler zaregistrujete voláním metody `addHandler()`: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + // zde bude vaše logika +}); +``` + +První parametr je název elementu, druhý je callback funkce. Callback dostává jako první parametr vždy objekt `Texy\HandlerInvocation`, následují parametry jsou specifické pro daný element. + +.[note] +Podrobné vysvětlení všech typů handlerů najdete v kapitole [Architektura a principy |architecture]. + + +Jak funguje zpracování +====================== + +Když Texy potřebuje zpracovat element, vytvoří objekt `HandlerInvocation` obsahující všechny zaregistrované handlery pro tento typ prvku. **Váš handler se zavolá jako první** a může: + +- **Delegovat** na další handler voláním `$invocation->proceed()` +- **Upravit vstup** voláním `proceed()` s modifikovanými parametry +- **Upravit výstup** zpracováním výsledku z `proceed()` +- **Přerušit řetěz** vrácením vlastního výsledku bez volání `proceed()` + +Metoda `proceed()` posune zpracování na další handler v řetězu. Pokud už žádný vlastní handler není, zavolá se výchozí implementace z modulu. To znamená, že váš handler má absolutní kontrolu - může rozhodnout, zda se vůbec zavolá výchozí logika. + +Tento mechanismus se nazývá **chain of responsibility** (řetěz zodpovědnosti): + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + // 1. Upravíme vstupní data před zpracováním + $image->modifier->title = 'Modified title'; + + // 2. Zavoláme další handler nebo výchozí zpracování + $element = $invocation->proceed($image, $link); + + // 3. Upravíme výsledný HTML element + $element->attrs['loading'] = 'lazy'; + + return $element; +}); +``` + +Pořadí vykonávání je od **posledně registrovaného k prvnímu**. Pokud modul zaregistruje svůj výchozí handler při konstrukci a vy pak zaregistrujete vlastní handler, váš handler se zavolá první. To vám umožňuje přepsat nebo obalit výchozí chování. + + +Výchozí elementy +================ + +Texy poskytuje několik předpřipravených elementů, pro které můžete registrovat vlastní handlery. Zde je jejich kompletní seznam s parametry, které dostává handler. + + +image +----- + +Zpracovává obrázky. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +): Texy\HtmlElement|string|null +``` + +Parametr `$image` obsahuje URL, rozměry a modifikátory. Parametr `$link` je zadán, pokud je obrázek odkazem (syntaxe `[* img *]:url`). + + +linkReference +------------- + +Zpracovává referenční odkazy typu `[ref]`. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, + string $content, +): Texy\HtmlElement|string|null +``` + +Parametr `$link` obsahuje URL a modifikátory načtené z definice reference. Parametr `$content` je HTML obsah odkazu (již zpracovaný parsováním inline syntaxí). + + +linkEmail +--------- + +Zpracovává automaticky rozpoznané emailové adresy v textu. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, +): Texy\HtmlElement|string|null +``` + +Parametr `$link` obsahuje emailovou adresu v property `URL`. + + +linkURL +------- + +Zpracovává automaticky rozpoznané URL v textu. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, +): Texy\HtmlElement|string|null +``` + +Parametr `$link` obsahuje nalezenou URL. + + +phrase +------ + +Zpracovává inline formátování. + +```php +function( + Texy\HandlerInvocation $invocation, + string $phrase, + string $content, + Texy\Modifier $modifier, + ?Texy\Link $link, +): Texy\HtmlElement|string|null +``` + +Parametr `$phrase` je název syntaxe jako `phrase/strong` nebo `phrase/em`. Parametr `$content` je text uvnitř formátování. Parametr `$modifier` obsahuje CSS třídy, styly a další modifikátory. Parametr `$link` je zadán, pokud má formátování připojený odkaz. + + +newReference +------------ + +Volá se, když parser najde referenci, která není definovaná. + +```php +function( + Texy\HandlerInvocation $invocation, + string $name, +): Texy\HtmlElement|string|null +``` + +Parametr `$name` je název reference. Handler může vytvořit odkaz dynamicky nebo vrátit `null` pro odmítnutí. + + +htmlComment +----------- + +Zpracovává HTML komentáře. + +```php +function( + Texy\HandlerInvocation $invocation, + string $content, +): string +``` + +Parametr `$content` je text mezi `<!--` a `-->`. + + +htmlTag +------- + +Zpracovává HTML tagy v textu. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\HtmlElement $el, + bool $isStart, + ?bool $forceEmpty, +): Texy\HtmlElement|string|null +``` + +Parametr `$el` je element s názvem a atributy. Parametr `$isStart` určuje, zda jde o otevírací tag. Parametr `$forceEmpty` vynutí prázdný element. + + +script +------ + +Zpracovává skripty `{{command: args}}`. + +```php +function( + Texy\HandlerInvocation $invocation, + string $command, + array $args, + ?string $raw, +): Texy\HtmlElement|string|null +``` + +Parametr `$command` je název příkazu. Parametr `$args` je pole argumentů. Parametr `$raw` je původní neparsovaný řetězec argumentů. + + +figure +------ + +Zpracovává obrázky s popiskou. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, + string $content, + Texy\Modifier $modifier, +): Texy\HtmlElement|null +``` + +Parametr `$content` je text popisky pod obrázkem. + + +heading +------- + +Zpracovává nadpisy. + +```php +function( + Texy\HandlerInvocation $invocation, + int $level, + string $content, + Texy\Modifier $modifier, + bool $isSurrounded, +): Texy\HtmlElement +``` + +Parametr `$level` je úroveň nadpisu (0-6). Parametr `$content` je text nadpisu. Parametr `$isSurrounded` určuje, zda jde o ohraničený nadpis (`###`) nebo podtržený. + + +horizline +--------- + +Zpracovává horizontální čáry. + +```php +function( + Texy\HandlerInvocation $invocation, + string $type, + Texy\Modifier $modifier, +): Texy\HtmlElement +``` + +Parametr `$type` je řetězec znaků použitých pro čáru (`---` nebo `***`). + + +block +----- + +Zpracovává speciální bloky `/--type` až `\--`. + +```php +function( + Texy\HandlerInvocation $invocation, + string $blocktype, + string $content, + ?string $param, + Texy\Modifier $modifier, +): Texy\HtmlElement|string +``` + +Parametr `$blocktype` je typ bloku s prefixem `block/`, např. `block/code` nebo `block/html`. Parametr `$content` je obsah bloku. Parametr `$param` je volitelný parametr za typem (např. jazyk u kódu). + + +emoticon +-------- + +Zpracovává emotikony (smajlíky). + +```php +function( + Texy\HandlerInvocation $invocation, + string $emoticon, + string $raw, +): Texy\HtmlElement|string +``` + +Parametr `$emoticon` je rozpoznaný emotikon (např. `:-)` nebo `:-(` ). Parametr `$raw` je původní text včetně případných opakujících se znaků (např. `:-)))))` ). + +.[note] +Emotikony jsou ve výchozím nastavení **vypnuté**. Zapnete je pomocí `$texy->allowed['emoticon'] = true;` + + +Výchozí eventy +============== + +Texy poskytuje několik předpřipravených eventů, pro které můžete registrovat handlery. Říká se jim **notification handlery**. Na rozdíl od element handlerů tyto handlery **nic nevrací**. Používají se pro vedlejší efekty jako logování, sběr statistik nebo úpravy již vytvořeného DOM stromu. + + +beforeParse +----------- + +Volá se před začátkem parsování textu. Umožňuje provést předzpracování nebo načíst definice. + +```php +function( + Texy\Texy $texy, + string &$text, + bool $isSingleLine, +): void +``` + +Parametr `$text` je předán referencí, takže ho můžete upravit. Parametr `$isSingleLine` určuje, zda se parsuje jeden řádek nebo celý dokument. + + +afterParse +---------- + +Volá se po dokončení parsování, před konverzí DOM stromu na HTML. Umožňuje upravit vytvořený DOM. + +```php +function( + Texy\Texy $texy, + Texy\HtmlElement $DOM, + bool $isSingleLine, +): void +``` + +Parametr `$DOM` je kořenový element dokumentu, který můžete procházet a upravovat. + + +afterList +--------- + +Volá se po vytvoření seznamu (číslovaného nebo nečíslovaného). + +```php +function( + Texy\BlockParser $parser, + Texy\HtmlElement $element, + Texy\Modifier $modifier, +): void +``` + +Parametr `$element` je vytvořený element `<ul>` nebo `<ol>`. Parametr `$modifier` obsahuje modifikátory aplikované na celý seznam. + + +afterDefinitionList +------------------- + +Volá se po vytvoření definičního seznamu. + +```php +function( + Texy\BlockParser $parser, + Texy\HtmlElement $element, + Texy\Modifier $modifier, +): void +``` + +Parametr `$element` je vytvořený element `<dl>`. + + +afterTable +---------- + +Volá se po vytvoření tabulky. + +```php +function( + Texy\BlockParser $parser, + Texy\HtmlElement $element, + Texy\Modifier $modifier, +): void +``` + +Parametr `$element` je vytvořený element `<table>`. + + +afterBlockquote +--------------- + +Volá se po vytvoření citace. + +```php +function( + Texy\BlockParser $parser, + Texy\HtmlElement $element, + Texy\Modifier $modifier, +): void +``` + +Parametr `$element` je vytvořený element `<blockquote>`. + + +Základní použití +================ + +Nejjednodušší element handler jen deleguje na výchozí zpracování: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + return $invocation->proceed(); +}); +``` + +Tento handler nic nemění, ale ukazuje základní kostru. Všechny parametry předá dál a vrátí výsledek. + + +Úprava vstupních dat +-------------------- + +Handler může upravit data před jejich zpracováním: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + // přidáme default rozměry, pokud nejsou zadané + $image->width ??= 800; + $image->height ??= 600; + return $invocation->proceed(); +}); +``` + +Změny provedené na objektech `$image` nebo `$link` se projeví v dalším zpracování, včetně výchozího handleru. + + +Úprava výstupního elementu +-------------------------- + +Handler může upravit HTML element vrácený z `proceed()`: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + $element = $invocation->proceed(); + + if ($element) { + // přidáme lazy loading + $element->attrs['loading'] = 'lazy'; + + // přidáme CSS třídu + $element->attrs['class'][] = 'responsive'; + } + + return $element; +}); +``` + + +Podmíněné zpracování +-------------------- + +Handler může zpracovat pouze určité případy a ostatní delegovat: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + // speciální zpracování pro YouTube videa + if (str_starts_with($image->URL, 'youtube:')) { + $id = substr($image->URL, 8); + $iframe = sprintf( + '<iframe src="https://youtube.com/embed/%s"></iframe>', + htmlspecialchars($id) + ); + return $invocation->getTexy() + ->protect($iframe, Texy\Texy::CONTENT_BLOCK); + } + + // ostatní obrázky zpracujeme standardně + return $invocation->proceed(); +}); +``` + + +Přerušení zpracování +-------------------- + +Handler může odmítnout zpracování vrácením `null`: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + // zakážeme externí obrázky + if (str_contains($image->URL, '://')) { + return null; + } + + return $invocation->proceed(); +}); +``` + + +Praktické příklady +================== + +Následující příklady ukazují reálné use-case pro element handlery. + + +YouTube embed +------------- + +Převod speciální syntaxe na embedded video: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + if (str_starts_with($image->URL, 'youtube:')) { + $id = substr($image->URL, 8); + $width = $image->width ?: 560; + $height = $image->height ?: 315; + + $iframe = sprintf( + '<iframe width="%d" height="%d" ' + . 'src="https://youtube.com/embed/%s" ' + . 'frameborder="0" allowfullscreen></iframe>', + $width, $height, htmlspecialchars($id) + ); + + $texy = $invocation->getTexy(); + return $texy->protect($iframe, $texy::CONTENT_BLOCK); + } + + return $invocation->proceed(); +}); +``` + +Použití v textu: + +```texy +[* youtube:dQw4w9WgXcQ 640x360 *] +``` + + +Galerie obrázků +--------------- + +Obalení obrázků do speciálního divu pro lightbox: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + $element = $invocation->proceed(); + + // pokud má obrázek třídu 'gallery' + if (isset($image->modifier->classes['gallery'])) { + // obalíme do divu s lightbox atributy + $wrapper = new Texy\HtmlElement('div'); + $wrapper->attrs['class'][] = 'lightbox-item'; + $wrapper->attrs['data-src'] = $image->URL; + $wrapper->add($element); + + return $wrapper; + } + + return $element; +}); +``` + +Použití: + +```texy +[* image.jpg .[gallery] *] +``` + + +Validace odkazů +--------------- + +Kontrola, zda odkazy nalezené v textu vedou na povolené domény: + +```php +$allowedDomains = ['example.com', 'trusted.org']; + +$texy->addHandler('linkURL', function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, +) use ($allowedDomains) { + $host = parse_url($link->URL, PHP_URL_HOST); + + // pokud doména není v whitelistu, nepovolíme odkaz + if ($host && !in_array($host, $allowedDomains, true)) { + return null; + } + + return $invocation->proceed(); +}); +``` + + +Automatické rel="nofollow" +-------------------------- + +Přidání `nofollow` všem externím odkazům nalezeným v textu: + +```php +$texy->addHandler('linkURL', function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, +) { + $element = $invocation->proceed(); + + // pokud odkaz obsahuje // (tedy je externí) + if (str_contains($link->URL, '://')) { + $element->attrs['rel'] = 'nofollow'; + } + + return $element; +}); +``` + + +Syntax highlighting +------------------- + +Integrace knihovny pro zvýraznění syntaxe: + +```php +$texy->addHandler('block', function( + Texy\HandlerInvocation $invocation, + string $blocktype, + string $content, + ?string $param, + Texy\Modifier $modifier, +) { + // zpracujeme pouze bloky typu 'code' + if ($blocktype !== 'block/code') { + return $invocation->proceed(); + } + + // aplikujeme syntax highlighting + $highlighter = new MyHighlighter(); + $highlighted = $highlighter->highlight($content, $param); + + $el = new Texy\HtmlElement('pre'); + $modifier->decorate($invocation->getTexy(), $el); + $el->attrs['class'][] = 'language-' . $param; + + $code = new Texy\HtmlElement('code'); + $code->add($highlighted); + $el->add($code); + + return $el; +}); +``` + + +Lazy loading +------------ + +Projdeme všechny obrázky a přidáme lazy loading: + +```php +$texy->addHandler('afterParse', function( + Texy\Texy $texy, + Texy\HtmlElement $DOM, + bool $isSingleLine, +) { + foreach ($DOM->getIterator() as $child) { + if ($child instanceof Texy\HtmlElement + && $child->getName() === 'img' + ) { + $child->attrs['loading'] = 'lazy'; + } + } +}); +``` + + +Logování použitých prvků +------------------------ + +Sběr statistik o použitých prvcích v dokumentu: + +```php +$stats = []; + +$texy->addHandler('beforeParse', function( + Texy\Texy $texy, + string &$text, + bool $isSingleLine, +) use (&$stats) { + $stats = ['images' => 0, 'links' => 0, 'headings' => 0]; +}); + +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) use (&$stats) { + $stats['images']++; + return $invocation->proceed(); +}); + +$texy->addHandler('linkURL', function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, +) use (&$stats) { + $stats['links']++; + return $invocation->proceed(); +}); + +$texy->addHandler('heading', function( + Texy\HandlerInvocation $invocation, + int $level, + string $content, + Texy\Modifier $modifier, + bool $isSurrounded, +) use (&$stats) { + $stats['headings']++; + return $invocation->proceed(); +}); +``` + + +Pomocné třídy +============= + +Při práci s handlery budete pracovat s několika důležitými třídami. Zde je jejich přehled s nejdůležitějšími vlastnostmi. + + +Texy\Image +---------- + +Reprezentuje obrázek s jeho parametry: + +```php +$image->URL; // string - cesta k obrázku +$image->linkedURL; // ?string - URL odkazu (pokud je obrázek odkazem) +$image->width; // ?int - šířka v pixelech +$image->height; // ?int - výška v pixelech +$image->asMax; // bool - zda jsou rozměry maximální +$image->modifier; // Modifier - CSS třídy, styly, atributy +$image->name; // ?string - název reference +``` + + +Texy\Link +--------- + +Reprezentuje odkaz s jeho parametry: + +```php +$link->URL; // string - cílová URL +$link->raw; // string - původní URL (před normalizací) +$link->modifier; // Modifier - CSS třídy, styly, atributy +$link->type; // string - typ odkazu (COMMON, BRACKET, IMAGE) +$link->label; // ?string - text odkazu (u referencí) +$link->name; // ?string - název reference +``` + +Konstanty pro typ odkazu: + +```php +Texy\Link::COMMON; // běžný odkaz +Texy\Link::BRACKET; // referenční odkaz [ref] +Texy\Link::IMAGE; // odkaz z obrázku [* img *] +``` + + +Texy\HtmlElement +---------------- + +Reprezentuje HTML element s jeho atributy a obsahem: + +```php +$el = new Texy\HtmlElement('div'); + +// práce s názvem elementu +$el->getName(); // vrací 'div' +$el->setName('section'); // změní na 'section' + +// práce s atributy +$el->attrs['id'] = 'main'; +$el->attrs['class'][] = 'container'; +$el->attrs['style']['color'] = 'red'; + +// práce s obsahem +$el->setText('text'); // nastaví textový obsah +$el->getText(); // vrací textový obsah +$el->add($child); // přidá potomka +$el->insert(0, $child); // vloží potomka na pozici + +// parsování obsahu +$el->parseLine($texy, $text); // parsuje inline text +$el->parseBlock($texy, $text); // parsuje blokový text + +// konverze na HTML +$el->toString($texy); // internal reprezentace +$el->toHtml($texy); // finální HTML +``` + + +Texy\Modifier +------------- + +Reprezentuje modifikátory CSS tříd, stylů a atributů: + +```php +$mod->id; // ?string - HTML id +$mod->classes; // array - pole CSS tříd +$mod->styles; // array - pole CSS stylů +$mod->attrs; // array - HTML atributy +$mod->hAlign; // ?string - horizontální zarovnání (left, right, center, justify) +$mod->vAlign; // ?string - vertikální zarovnání (top, middle, bottom) +$mod->title; // ?string - title atribut nebo alt pro obrázky + +// aplikace modifikátoru na element +$mod->decorate($texy, $element); +``` diff --git a/texy/cs/custom-syntax.texy b/texy/cs/custom-syntax.texy new file mode 100644 index 0000000000..f8f7634b5c --- /dev/null +++ b/texy/cs/custom-syntax.texy @@ -0,0 +1,519 @@ +Přidání vlastní syntaxe +*********************** + +.[perex] +Tato kapitola popisuje, jak přidat do Texy **zcela nové markup konstrukce**, které standardně neexistují. Pokud chcete pouze změnit chování existujících prvků (například upravit zpracování obrázků nebo odkazů), přečtěte si kapitolu [Úprava chování prvků |custom-handlers]. + +Představte si, že chcete v dokumentaci automaticky vytvářet odkazy na uživatelské profily zápisem `@@username`. Nebo potřebujete speciální bloky pro upozornění typu `:::warning`. Texy tyto konstrukce nezná a nemůžete je vytvořit úpravou existujících prvků. + +Vlastní syntaxe vám umožní definovat nové markup konstrukce. Zadáte, jak má konstrukce vypadat (pomocí regulárního výrazu), a napíšete funkci, která ji zpracuje. Texy pak vaši syntaxi rozpozná stejně jako své standardní konstrukce. + + +Registrace syntaxe +================== + +Texy poskytuje dvě metody pro registraci vlastní syntaxe podle toho, zda jde o inline nebo blokový prvek. + + +Line syntaxe +------------ + +Line syntaxe slouží pro inline konstrukce uvnitř řádků textu. Registrujete ji metodou `registerLinePattern()`: + +```php +$texy->registerLinePattern( + callable $handler, + string $pattern, + string $name, + ?string $againTest = null, +); +``` + +**Parametr `$handler`** je callback funkce, která se zavolá při nálezu syntaxe. Může to být název funkce, anonymní funkce nebo pole `[$object, 'method']`. + +**Parametr `$pattern`** je regulární výraz (PCRE), který definuje, jak vaše syntaxe vypadá v textu. Pattern by **neměl být kotvený** na začátek řádku (`^`), protože se hledá kdekoliv v textu. Použijte capturing groups pro zachycení dat, která potřebujete zpracovat. + +**Parametr `$name`** je unikátní název syntaxe. Používá se v poli `$texy->allowed` pro zapnutí/vypnutí a předává se do handleru pro identifikaci. Doporučujeme používat prefixový styl jako `custom/username` nebo `myapp/profile`. + +**Parametr `$againTest`** je volitelný regex pro optimalizaci. Pokud je zadán, Texy nejprve zkontroluje, zda text vůbec obsahuje něco, co by mohlo matchnout váš pattern. Teprve pokud `$againTest` uspěje, spustí se komplexnější pattern. To výrazně zrychlí zpracování, pokud máte složitý pattern a používá se jen zřídka. + +Příklad registrace: + +```php +$texy->registerLinePattern( + 'usernameHandler', + '#@@([a-z0-9_]+)#i', + 'custom/username', +); +``` + + +Block syntaxe +------------- + +Block syntaxe slouží pro víceřádkové blokové konstrukce. Registrujete ji metodou `registerBlockPattern()`: + +```php +$texy->registerBlockPattern( + callable $handler, + string $pattern, + string $name, +); +``` + +Parametry `$handler` a `$name` mají stejný význam jako u line syntaxí. + +**Parametr `$pattern`** je regulární výraz, který **musí být kotvený** na začátek řádku (`^`) a často i na konec (`$`). BlockParser automaticky přidá modifikátory `Am` (anchored, multiline), takže je do patternu nepřidávejte. Pattern by měl matchnout celý blok nebo alespoň jeho začátek. + +Příklad registrace: + +```php +$texy->registerBlockPattern( + 'alertHandler', + '#^:::(warning|info|danger)\n(.+)$#s', + 'custom/alert', +); +``` + + +Syntax handler +============== + +Syntax handler je funkce volaná parserem, když najde výskyt vaší syntaxe v textu. Jeho úkolem je zpracovat nalezená data a vrátit HTML element nebo řetězec. + +Podrobné vysvětlení role syntax handleru v architektuře Texy najdete v kapitole [Architektura a principy |architecture#syntax-handler]. + + +Pro line syntaxe +---------------- + +Signatura syntax handleru pro line syntaxe: + +```php +function( + Texy\LineParser $parser, + array $matches, + string $name, +): Texy\HtmlElement|string|null +``` + +**Parametr `$parser`** poskytuje přístup k parseru a Texy objektu. Nejčastěji použijete `$parser->getTexy()` pro získání Texy instance. + +**Parametr `$matches`** obsahuje výsledky regex matche. `$matches[0]` je celý matchnutý řetězec, `$matches[1]`, `$matches[2]` atd. jsou capturing groups z vašeho patternu. + +**Parametr `$name`** je název syntaxe, který jste zadali při registraci. Užitečné, pokud jeden handler zpracovává více syntaxí. + +**Návratová hodnota** může být `Texy\HtmlElement` pro strukturovaný HTML výstup, `string` pro přímý HTML kód (který musíte protectovat), nebo `null` pro odmítnutí zpracování. + +Handler může nastavit `$parser->again = true`, pokud chce, aby se obsah vytvořeného elementu znovu parsoval pro nalezení vnořených syntaxí. + + +Pro block syntaxe +----------------- + +Signatura syntax handleru pro block syntaxe: + +```php +function( + Texy\BlockParser $parser, + array $matches, + string $name, +): Texy\HtmlElement|string|null +``` + +Parametry mají stejný význam jako u line syntaxí, jen dostáváte `Texy\BlockParser` místo `LineParser`. + +BlockParser poskytuje metody pro práci s víceřádkovými strukturami: + +- **`$parser->next($pattern, &$matches)`** - matchne další řádek proti patternu a vrátí true/false +- **`$parser->moveBackward($lines)`** - vrátí se o zadaný počet řádků zpět +- **`$parser->isIndented()`** - vrací true, pokud je aktuální blok odsazený + + +LineParser API +============== + +Při práci s line syntaxemi máte k dispozici několik užitečných vlastností a metod. + +**Property `$again`** řídí, zda se má právě zpracovaná syntaxe hledat znovu na stejné pozici po zpracování. Výchozí hodnota je `false`. Nastavte na `true`, pokud vytváříte element s obsahem, který může obsahovat další syntaxe: + +```php +function( + Texy\LineParser $parser, + array $matches, + string $name, +): Texy\HtmlElement +{ + $el = new Texy\HtmlElement('span'); + $el->setText($matches[1]); + + // obsah může obsahovat další formátování + $parser->again = true; + + return $el; +} +``` + +**Metoda `getTexy()`** vrací instanci Texy objektu, což potřebujete pro práci s `protect()` nebo přístup ke konfiguraci. + + +BlockParser API +=============== + +Při práci s block syntaxemi máte k dispozici metody pro práce s víceřádkovými strukturami. + +**Metoda `next($pattern, &$matches)`** zkusí matchnout další řádek v textu proti zadanému patternu. Pokud uspěje, naplní `$matches` výsledkem a posune interní pozici za tento řádek. Vrací `true` při úspěchu, `false` při neúspěchu: + +```php +while ($parser->next('#^\-\s+(.+)$#', $matches)) { + // zpracuj další položku seznamu + $item = $matches[1]; +} +``` + +**Metoda `moveBackward($lines = 1)`** vrátí interní pozici o zadaný počet řádků zpět. Užitečné, když váš pattern matchnul víc než začátek bloku a chcete se vrátit na začátek: + +```php +// pattern matchnul 3 řádky, ale chceme číst od prvního +$parser->moveBackward(2); +``` + +**Metoda `isIndented()`** vrací `true`, pokud je aktuální blok odsazený (začíná mezerou nebo tabulátorem). To naznačuje, že jde o vnořený obsah. + + +Praktické příklady +================== + +Následující příklady ukazují reálné use-case pro vlastní syntaxe. + + +Uživatelské profily +------------------- + +Automatické vytváření odkazů na profily zápisem `@@username`: + +```php +$texy->registerLinePattern( + function( + Texy\LineParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $username = $matches[1]; + + $el = new Texy\HtmlElement('a'); + $el->attrs['href'] = '/user/' . urlencode($username); + $el->attrs['class'][] = 'user-profile'; + $el->setText('@' . $username); + + return $el; + }, + '#@@([a-z0-9_]+)#i', + 'custom/username' +); +``` + +Použití v textu: + +```texy +Podívejte se na profil @@johndoe nebo @@jane_smith. +``` + + +Alert boxy +---------- + +Speciální bloky pro upozornění s různými typy: + +```php +$texy->registerBlockPattern( + function( + Texy\BlockParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $type = $matches[1]; // warning, info, danger + $content = $matches[2]; + + $el = new Texy\HtmlElement('div'); + $el->attrs['class'][] = 'alert'; + $el->attrs['class'][] = 'alert-' . $type; + + $texy = $parser->getTexy(); + $el->parseBlock($texy, trim($content)); + + return $el; + }, + '#^:::(warning|info|danger)\n(.+?)(?=\n:::|$)#s', + 'custom/alert' +); +``` + +Použití v textu: + +```texy +:::warning +Toto je důležité upozornění! +::: + +:::info +Pro informaci: aktualizace proběhne zítra. +::: +``` + + +Hashtagy +-------- + +Automatické vytváření odkazů z hashtagů: + +```php +$texy->registerLinePattern( + function( + Texy\LineParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $tag = $matches[1]; + + $el = new Texy\HtmlElement('a'); + $el->attrs['href'] = '/tag/' . urlencode($tag); + $el->attrs['class'][] = 'hashtag'; + $el->setText('#' . $tag); + + return $el; + }, + '#\#([a-z0-9_]+)#i', + 'custom/hashtag', + '#\##' // optimalizace - hledej jen pokud je # v textu +); +``` + +Použití: + +```texy +Článek o #php a #webdesign. +``` + + +Zkratky +------- + +Automatické rozbalení zkratek s vysvětlením: + +```php +$abbreviations = [ + 'HTML' => 'HyperText Markup Language', + 'CSS' => 'Cascading Style Sheets', + 'PHP' => 'PHP: Hypertext Preprocessor', +]; + +$texy->registerLinePattern( + function( + Texy\LineParser $parser, + array $matches, + string $name + ) use ($abbreviations): ?Texy\HtmlElement + { + $abbr = $matches[1]; + + if (!isset($abbreviations[$abbr])) { + return null; // neznámá zkratka + } + + $el = new Texy\HtmlElement('abbr'); + $el->attrs['title'] = $abbreviations[$abbr]; + $el->setText($abbr); + + return $el; + }, + '#\b([A-Z]{2,})\b#', + 'custom/abbreviation' +); +``` + + +Inline ikony +------------ + +Vkládání ikon pomocí speciální syntaxe: + +```php +$texy->registerLinePattern( + function( + Texy\LineParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $icon = $matches[1]; + + $el = new Texy\HtmlElement('i'); + $el->attrs['class'][] = 'icon'; + $el->attrs['class'][] = 'icon-' . $icon; + $el->attrs['aria-hidden'] = 'true'; + + return $el; + }, + '#:icon-([a-z-]+):#', + 'custom/icon' +); +``` + +Použití: + +```texy +Klikněte na tlačítko :icon-download: pro stažení. +``` + + +Poznámkový blok +--------------- + +Blok pro poznámky pod čarou: + +```php +$texy->registerBlockPattern( + function( + Texy\BlockParser $parser, + array $matches, + string $name + ): Texy\HtmlElement + { + $parser->moveBackward(); + + $content = ''; + while ($parser->next('#^NOTE:\s*(.+)$#', $matches)) { + $content .= $matches[1] . "\n"; + } + + $el = new Texy\HtmlElement('aside'); + $el->attrs['class'][] = 'note'; + + $texy = $parser->getTexy(); + $el->parseBlock($texy, trim($content)); + + return $el; + }, + '#^NOTE:\s*(.+)$#m', + 'custom/note' +); +``` + +Použití: + +```texy +NOTE: Toto je důležitá poznámka. +NOTE: Může být víceřádková. +``` + + +Vlastní citace s autorem +------------------------ + +Rozšířená syntaxe pro citace s uvedením autora: + +```php +$texy->registerBlockPattern( + function( + Texy\BlockParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $author = $matches[1]; + $quote = $matches[2]; + + $blockquote = new Texy\HtmlElement('blockquote'); + + $texy = $parser->getTexy(); + $blockquote->parseBlock($texy, trim($quote)); + + $cite = new Texy\HtmlElement('cite'); + $cite->setText($author); + $blockquote->add($cite); + + return $blockquote; + }, + '#^QUOTE\[([^\]]+)\]:\n(.+?)(?=\n\n|$)#s', + 'custom/quote' +); +``` + +Použití: + +```texy +QUOTE[Albert Einstein]: +Fantazie je důležitější než vědění, +protože vědění je omezené. +``` + + +Galerie obrázků +--------------- + +Speciální blok pro vytvoření galerie z více obrázků: + +```php +$texy->registerBlockPattern( + function( + Texy\BlockParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $parser->moveBackward(); + + $gallery = new Texy\HtmlElement('div'); + $gallery->attrs['class'][] = 'gallery'; + + while ($parser->next('#^\[G\]\s*(.+)$#', $matches)) { + $img = new Texy\HtmlElement('img'); + $img->attrs['src'] = trim($matches[1]); + $img->attrs['loading'] = 'lazy'; + $gallery->add($img); + } + + return $gallery; + }, + '#^\[G\]\s*(.+)$#m', + 'custom/gallery' +); +``` + +Použití: + +```texy +[G] image1.jpg +[G] image2.jpg +[G] image3.jpg +``` + + +Kolize syntaxí +============== + +Když registrujete vlastní syntaxi, musíte dávat pozor, aby nekolidovala s existujícími syntaxemi Texy nebo s jinými vlastními syntaxemi. + +**Pořadí registrace záleží.** Line syntaxe se hledají v pořadí, jak byly registrovány. Pokud více syntaxí může matchnout na stejné pozici, vyhrává ta, která byla registrována dříve. Proto registrujte specifičtější syntaxe před obecnějšími. + +**Buďte specifičtí v patterns.** Čím konkrétnější je váš pattern, tím menší je riziko kolize. Pattern `#\#\w+#` matchne i `#hashtag`, což by mohlo kolidovat s nadpisy. Lepší je `#(?<=\s)\#[a-z0-9_]+#i`, který vyžaduje mezeru před hashtagem. + +**Testujte kombinace.** Vyzkoušejte, jak vaše syntaxe funguje v kombinaci s existujícími konstrukcemi. Co se stane, když je váš markup uvnitř odkazu? Co když je uvnitř code bloku? + +**Používejte prefixované názvy.** Místo `username` použijte `custom/username` nebo `myapp/username`. To zabrání konfliktům, pokud by Texy v budoucnu přidalo syntaxi stejného názvu. + + +Best practices +============== + +**Vracejte `null` při neúspěchu.** Pokud handler zjistí, že nemůže nebo nechce zpracovat daný match (například neznámá zkratka), vraťte `null`. Parser pak zkusí další syntaxe. + +**Používejte `protect()` pro HTML.** Pokud vracíte raw HTML string místo `HtmlElement`, musíte ho protectovat pomocí `$texy->protect($html, Texy::CONTENT_...)`. Jinak bude escapován. + +**Nastavte `$parser->again` správně.** Pro line syntaxe, které vytváří element s textovým obsahem, který může obsahovat další syntaxe (formátování, odkazy), nastavte `$parser->again = true`. + +**Respektujte `$texy->allowed`.** Pokud vytváříte modul s více syntaxemi, kontrolujte `$texy->allowed[$name]` před registrací patternu nebo v handleru před zpracováním. diff --git a/texy/cs/develop.texy b/texy/cs/develop.texy new file mode 100644 index 0000000000..cc388cd830 --- /dev/null +++ b/texy/cs/develop.texy @@ -0,0 +1,35 @@ +Pro programátory +**************** + +.[perex] +Vítejte v programátorské dokumentaci Texy! Tato sekce vás provede od základního použití až po pokročilé techniky rozšíření a vlastní syntaxe. + + +[Rychlý start | quickstart] +--------------------------- + +Instalace, první použití a základní konfigurace. Za 5 minut budete mít Texy funkční. + + +[Konfigurace | configuration] +----------------------------- + +Kompletní přehled všech modulů, jejich vlastností a konfiguračních možností. Nastavení bezpečnosti, povolených značek, stylů a tříd. + + +[Úprava chování prvků | custom-handlers] +---------------------------------------- + +Naučte se měnit chování existující syntaxe. YouTube embedování, syntax highlighting, custom validace. + + +[Přidání vlastní syntaxe | custom-syntax] +----------------------------------------- + +Vytvoření zcela nových syntaktických prvků. + + +[Architektura a principy | architecture] +---------------------------------------- + +Pochopení toho, jak Texy interně funguje. Parsing flow, moduly, pattern matching, protect/unprotect mechanismus. diff --git a/texy/cs/priklady-vyuziti.texy b/texy/cs/priklady-vyuziti.texy deleted file mode 100644 index f71b9616dc..0000000000 --- a/texy/cs/priklady-vyuziti.texy +++ /dev/null @@ -1,26 +0,0 @@ -Příklady využití -**************** - - -Běžní uživatelé .[#bfu] ------------------------ - -Texy původně vznikl jako nástroj, který umožnil i uživatelům neznalým HTML **snadno editovat obsah** webových stránek. Záměrem bylo vytvořit silnou **alternativu k WYSIWYG** editorům, které se v praxi [ukazují jako neefektivní | texy-vs-wysiwyg]. - -Běžný uživatel se může plně věnovat psaní "čistého textu .(například v Poznámkovém bloku)[about]", který si formátuje velmi [přirozeným způsobem | syntax]. A nemusí se téměř nic nového učit. - - -Zkušení pisatelé .[#experienced] --------------------------------- - -Zkušení tvůrci internetového obsahu, jakými jsou například bloggeři, často jazyk HTML znají. Ale psát články přímo v něm je nepříjemné a proto si zjednodušují práci používáním chytrých editorů. Jedním z nich je i Texy Je navíc dostupný i přes webové rozhraní a nabízí **neobvykle vysoký konfort**. - -Díky Texy se autor může plně věnovat obsahu dokumentu a nemusí uvažovat nad HTML nebo typografickou úpravou. Texy dokáže psaní výrazně zjednodušit, přitom pokročilého uživatele nijak neomezuje - nabízí mu **vkládat i HTML značky** a CSS formátování. - - -Komentáře a diskuzní fóra .[#comments] --------------------------------------- - -Texy počítá i s nasazením jako formátovač příspěvků v diskuzních fórech a komentářích. Přispívatelé mohou používat jednotnou a **intuitivní syntaxi** a programátorům **odpadne náročná práce** na vlastním formátovači. - -Tato oblast použití je charakteristická tím, že vyžaduje mnohem **přísnější kontrolu** vstupů. Texy proto obsahuje mechanismy, které zakáží nebo omezí použití určitých HTML značek (a jejich atributů), kaskádových stylů a tříd atd. diff --git a/texy/cs/quickstart.texy b/texy/cs/quickstart.texy new file mode 100644 index 0000000000..8c16245ab4 --- /dev/null +++ b/texy/cs/quickstart.texy @@ -0,0 +1,205 @@ +Rychlý start +************ + +.[perex] +Naučte se pracovat s Texy za pár minut. Tato stránka vás provede instalací, prvním použitím a základní konfigurací. + + +Instalace +========= + +Texy využívá moderních vlastností PHP a vyžaduje minimálně verzi 8.1. + +Nejjednodušší způsob instalace je přes Composer: + +```bash +composer require texy/texy +``` + +Composer automaticky stáhne Texy a všechny závislosti. + + +První použití +============= + + +Základní zpracování textu +------------------------- + +Vytvoření instance Texy a zpracování textu je extrémně jednoduché: + +```php +require __DIR__ . '/vendor/autoload.php'; + +$texy = new Texy\Texy; + +$text = 'Toto je **tučný text** a toto //kurzíva//.'; +$html = $texy->process($text); + +echo $html; +``` + +Výstup: +```latte +<p>Toto je <strong>tučný text</strong> a toto <em>kurzíva</em>.</p> +``` + +Metoda `process()` zpracuje celý text včetně blokových elementů (odstavce, nadpisy, seznamy, tabulky...). + + +Jednořádkový text +----------------- + +Pokud zpracováváte pouze jednořádkový text bez blokových elementů (například nadpisy v databázi, krátké popisky): + +```php +$texy = new Texy\Texy; + +$text = 'Odkaz na "homepage":https://example.com'; +$html = $texy->processLine($text); + +echo $html; +``` + +Výstup: +```latte +Odkaz na <a href="https://example.com">homepage</a> +``` + +Metoda `processLine()` nezabaluje výstup do odstavce `<p>` a zpracuje pouze inline elementy. + + +Základní konfigurace +==================== + +Texy funguje "out of the box", ale často budete chtít upravit základní nastavení. + + +Nastavení cest k obrázkům +------------------------- + +Pokud používáte relativní cesty k obrázkům, nastavte kořenový adresář: + +```php +$texy = new Texy\Texy; + +// Cesta na webu (přidá se před relativní URL) +$texy->imageModule->root = '/images/'; + +// Fyzická cesta na disku (pro zjištění rozměrů) +$texy->imageModule->fileRoot = __DIR__ . '/public/images/'; +``` + +Teď když napíšete `[* photo.jpg *]`, Texy vygeneruje `<img src="/images/photo.jpg">` a automaticky zjistí rozměry obrázku. + + +Nastavení cest k odkazům +------------------------ + +Podobně můžete nastavit kořenový adresář pro odkazy: + +```php +$texy->linkModule->root = '/articles/'; +``` + + +Povolení a zakázání syntaxí +--------------------------- + +Každá část Texy syntaxe lze vypnout nebo zapnout pomocí pole `$allowed`: + +```php +$texy = new Texy\Texy; + +// Vypnout obrázky +$texy->allowed['image'] = false; + +// Vypnout HTML značky ve vstupu +$texy->allowed['html/tag'] = false; + +// Povolit emotikony (ve výchozím stavu vypnuté) +$texy->allowed['emoticon'] = true; +``` + +Kompletní seznam syntaxí najdete v [konfiguraci | configuration#allowed]. + + +Bezpečný režim pro uživatelský obsah +------------------------------------ + +Pokud zpracováváte obsah od uživatelů (komentáře, příspěvky na fóru), použijte bezpečný režim: + +```php +$texy = new Texy\Texy; +Texy\Configurator::safeMode($texy); + +$userInput = $_POST['comment']; +$html = $texy->process($userInput); +``` + +SafeMode: +- Povolí jen **bezpečné HTML značky** (`<strong>`, `<em>`, `<a>`, ...) +- Zakáže **třídy a ID** +- Zakáže **inline styly** +- Zakáže **obrázky** +- Přidá `rel="nofollow"` ke všem odkazům +- Filtruje **URL schémata** (jen `http:`, `https:`, `ftp:`, `mailto:`) + +Více o bezpečnosti v kapitole [Konfigurace – Bezpečnost |configuration#Bezpečnost]. + + +Kompletní příklad +================= + +```php +require __DIR__ . '/vendor/autoload.php'; + +$texy = new Texy\Texy; + +// Konfigurace +$texy->imageModule->root = '/images/'; +$texy->linkModule->root = '/'; +$texy->allowed['html/tag'] = false; + +// Text k zpracování +$text = ' + + +Nadpis článku +============= + +Toto je **úvodní odstavec** s odkazem na "homepage":https://example.com. + +- První položka +- Druhá položka +- Třetí položka + +[* photo.jpg .(Fotografie) *] +'; + +// Zpracování +$html = $texy->process($text); + +// Výstup +echo $html; + +// Dodatečné informace +echo "Titulek stránky: " . $texy->headingModule->title; +print_r($texy->summary['links']); +print_r($texy->summary['images']); +``` + +Po zpracování máte k dispozici: +- `$texy->headingModule->title` – první nadpis (vhodné pro `<title>`) +- `$texy->summary['links']` – pole všech použitých odkazů +- `$texy->summary['images']` – pole všech použitých obrázků + + +Další kroky +=========== + +Teď už víte, jak Texy používat. Pokračujte: + +- **[Konfigurace | configuration]** – podrobné nastavení všech modulů +- **[Syntaxe | syntax]** – naučte se Texy markup +- **[Architektura | architecture]** – pochopte, jak Texy funguje uvnitř diff --git a/texy/cs/syntax-podrobne.texy b/texy/cs/syntax-podrobne.texy deleted file mode 100644 index d10b62068c..0000000000 --- a/texy/cs/syntax-podrobne.texy +++ /dev/null @@ -1,889 +0,0 @@ -Podrobný popis syntaxe -********************** - - -- [#Filozofie] -- [#Odstavce textu] -- [#Titulky] -- [#Horizontální čáry] -- [#Kód] -- [#Vypnutí Texy] -- [#Citace] -- [#Odkazy] -- [#Obrázky] -- [#Fráze] -- [#Přímé HTML] -- [#Seznamy] -- [#Modifikátory] -- [#Typografie] -- [#Rozdělení velmi dlouhých slov] -- [#Tabulky] - - -Filozofie -========= - -Nástroj Texy vznikl proto, aby nezkušeným uživatelům umožnil snadno editovat obsah webových stránek. Proto je i syntaxe maximálně intuitivní. Záměrem je, aby text v čisté (nezformátované) formě byl přehledný a jeho formát tušitelný. - -Dnes Texy výborně slouží i zkušeným znalcům jazyka HTML. Dovoluje volně kombinovat Texy zápis s HTML značkami. Zkušení uživatelé se tedy nemusí učit nový meta-jazyk a plně využít svých znalostí. Texy jim pouze zjednodušuje práci. - -Prvotní logikou syntaxe je **žádnou syntaxi nepoužívat**. Jen psát čistý text. Vkládání rozšířených informací, jako třeba CSS třídy nebo odkazy, nenaruší tok textu. A zapíší se způsobem, který snadno pochopí i netechnicky založení uživatelé. - - -Odstavce textu -============== - -Za odstavec se považuje jeden nebo více bezprostředně za sebou následujících řádků textu. Odstavečky jsou od sebe oddělené prázdným řádkem. - -/--code texy -První odstavec lorem ipsum dolor sit amet. - -Druhý odstavec, který tvoří jeden řádek. -A druhý řádek textu. Texy je spojí. -\-- - -/--texysource -První odstavec lorem ipsum dolor sit amet. - -Druhý odstavec, který tvoří jeden řádek. -A druhý řádek textu. Texy je spojí. -\-- - -*V editačním políčku webové stránky (textarea) není rozdělení odstavce na dva řádky patrné. Proto je i Texy považuje za jeden odstavec.* - -Zalomení řádku v odstavci docílíte vložením jedné mezery vlevo: - -/--code texy -Kdoví jestli - jestli jsou na měsíci vůbec nějaký stopy - a proč kope kolem sebe kdo se topí - jakej sval to Zemí otáčí -\-- - -/--texysource -Kdoví jestli - jestli jsou na měsíci vůbec nějaký stopy - a proč kope kolem sebe kdo se topí - jakej sval to Zemí otáčí -\-- - - -Titulky -======= - -Titulky je možné zapsat hned dvěma způsoby: **podtržením** nebo **předsazením**. - -Každý titulek má svůj stupeň. V případě **podtržení** o důležitosti titulku rozhoduje podtrhávací znak. Od nejvyšší po nejnižší jsou to tyto: `#` `*` `=` `-` - -/--code texy -Hlavní titulek -************** - - -Podtitulek -========== -\-- - -/--texysource -Hlavní titulek -************** - - -Podtitulek -========== -\-- - -U titulků zapsaných **předsazením** určuje úroveň počet předsazených znaků. A ty mohou být `#` nebo `=` - -Platí: čím více znaků, tím důležitější titulek (minimum jsou dva znaky, maximum sedm). - -/--code texy -=== Hlavní titulek === - -## Podtitulek -\-- - -Jak vidíte v případě podtitulku, znaky vpravo je možné vynechat. - -*Stupně titulků jsou vždy jen relativní! Tedy Texy najde nejvyšší použitý titulek a ostatní titulky relativně od něj odstupňuje.* - - -Horizontální čáry -================= - -Texy zná tyto způsoby zápisu: - - -/--code texy ------------- - -******** -\-- - - -/--texysource -------------- - -******** -\-- - - -Kód -=== - -Používá se pro vložení zdrojového kódu. Použitím přídavného modulu lze aktivovat i zvýrazňování syntaxe. - -/--code texy - /---code php - function reImage($matches) { - $content = $matches[1]; - $align = $matches[5]; - $href = $matches[6]; - } - \--- -\-- - -/--texysource - /---code php - function reImage($matches) { - $content = $matches[1]; - $align = $matches[5]; - $href = $matches[6]; - } - \--- -\-- - -*Všimněte si slova `php` pro označení jazyka.* - - -Vypnutí Texy -============ - -Klíčové slovo `html` nebo `text` ovlivňuje, jestli obsah bude chápán jako HTML (včetně značek), nebo prostý text. - -/--code texy - /---html - <em>příklad</em>: **this is not strong** - \--- - - - /---text - <em>příklad</em>: **this is not strong** - \--- -\-- - -Pro inline vypnutí Texy je možné použít dvojitý apostrof `''` a obalit s ním část textu, který nemá být Texy zpracováván. - -/--code texy - Příklad: ''**this is not strong**'' -\-- - - -Rozdělování do bloků (div) -========================== - -Tuto schopnost využijete při tvorbě složitějších dokumentů. - -/--code texy - /---div .[header] - - content of div - - \--- -\-- - -/--texysource - /---div .[header] - - content of div - - \--- -\-- - - -Je možné bloky i vnořovat: - -/--code texy - /---div .[header] - - ## This is a header. - - /---div - vnořený div - \--- - - Texy je sexy! - - \--- -\-- - -/--texysource - /---div .[header] - - ## This is a header. - - /---div - vnořený div - \--- - - Texy je sexy! - - \--- -\-- - - -Citace -====== - -Citace jsou odsazené, podobně jako v emailech, znakem `>` - -/--code texy -> This is a blockquote with two paragraphs. -> -> 640 K should be enough for everyone -\-- - -/--texysource -> This is a blockquote with two paragraphs. -> -> 640 K should be enough for everyone -\-- - - -Odkazy -====== - -Odkazy se zapisují tak, že odkazující text uzavřete do uvozovek a následujete dvojtečkou a URL. Texy se snaží inteligentně odhadnout konec URL. Můžete mu i pomoci tím, že URI uzavřete do hranatých závorek. Část `http://` není povinná. - -Jako odkaz je možné vkládat i emaily, Texy je transformuje do podoby, která by měla zmást spamboty. - -/--code texy -Look at homepage:[https://texy.info]. - -Do you know "La Trine":https://www.latrine.cz? - -"Write me":me@example.com -\-- - -/--texysource -Look at homepage:[https://texy.info]. - -Do you know "La Trine":https://www.latrine.cz? - -"Write me":me@example.com -\-- - - -Reference ---------- - -Aby se tok textu "neznečišťoval" vkládáním URL, je možné všechny adresy uvést na jednom místě a pak se na ně jen odkazovat. Tomu se říká reference. Kromě adresy je možné doplnit i text odkazu a [modifikátor | #modifier]. - -/--code texy - [homepage]: https://texy.info/ Texy .(homepage) - [nette]: http://nette.org - -This is [homepage] - -Look at "this site":[nette] -\-- - - -Obrázky -======= - -Zapisují se mezi hranaté závorky s hvězdičkou: - -/--code texy -[* image.gif *] -\-- - -/--texysource line -[* image.gif *] -\-- - -V textových odstavcích je často třeba zvolit, má-li být obrázek zarovnán k levému nebo pravému kraji. Toho docílíte pomocí znaku `<` a `>` použitého před pravou závorkou: - -/--code texy -[* image.gif <] Left-aligned image - -[* image.gif >] Right-aligned image -\-- - -/--texysource -[* image.gif <] Left-aligned image - -[* image.gif >] Right-aligned image -\-- - -*Poznámka: V uvedeném příkladu Texy použil pro zarovnání přímý styl. Je možné systém nakonfigurovat tak, aby místo přiřadil obrázkům zvolenou třídu.* - -*Poznámka: pro všechny (relativní) URL obrázků je možné nastavit výchozí adresář. V uvedeným příkladech to byl `images/`, proto v Texy není adresář uveden, zatímco ve vygenerovaném HTML ano.* - -*Poznámka: pokud není implicitně určen alternativní text (jak na to viz níže), použije Texy výchozí. Zde je to prosté `image`* - - -Rozměry -------- - -U lokálních obrázků Texy zjistí rozměry automaticky. Pokud je chcete určit ručně, zapište je takto: - -/--code texy -[* image.gif 10x20 *] -\-- - -/--texysource line -[* image.gif 10x20 *] -\-- - - -Modifikátory ------------- - -O nich se více dozvíte v [jiné kapitole | #modifier], ale neuškodí si ukázat, jak se u obrázků zapisují. Zkusme si modifikátor pro určení alternativního textu a třídy: - -/--code texy -[* image.gif .(alt text)[foto] *] -\-- - -/--texysource line -[* image.gif .(alt text)[foto] *] -\-- - - -Reference ---------- - -Ze stejných důvodů, jako u odkazů, je i obrázky možné zapisovat pomocí referencí. Je třeba definovat URL (nebo více URL oddělených `|`) a případně i modifikátory. - -/--code texy -What a beautiful girl [* picture*] ! - -[* picture*]: image.gif .(my girl) -\-- - -/--texysource -What a beautiful girl [* picture*] ! - -[* picture*]: image.gif .(my girl) -\-- - - -Obrázek s popiskou ------------------- - -Za obrázkem uveďte tři hvězdičky a následuje popiska: - - -/--code texy -[* image.gif *] *** Toto je *popiska* pod obrázkem -\-- - -/--texysource -[* image.gif *] *** Toto je *popiska* pod obrázkem -\-- - - -Fráze -===== - -Asi nejpoužívanější syntax v Texy. Téměř ve všech případech se používá zdvojený znak. - -/--code texy -//kurzíva// - -*taky kurzíva* - -**tučné** - -superscript^2 vs. subscript_2 -\-- - -/--texysource -//kurzíva// - -*taky kurzíva* - -**tučné** - -superscript^2 vs. subscript_2 -\-- - -/--div .[output] -//kurzíva// - -*taky kurzíva* - -**tučné** - -***nejsilněji zdůrazněné*** - -superscript^2 vs. subscript_2 -\-- - -Speciálním případem fráze je tzv. kód. Od ostatních se liší tím, že jeho obsah nebude nadále formátován a zobrazí se doslovně: - -/--code texy -Odstraňte `<br />` a entitu `&ndash` -\-- - -/--texysource -Odstraňte `<br />` a entitu `&ndash` -\-- - -*Poznámka: jestli se použije element `<code>` nebo jiný (případně žádný) je možné rozhodnout pouhou konfigurací Texy* - - -S modifikátorem ---------------- - -Je možné jej vložit do každé fráze, vždy těsně před uzavírací znak: - -/--code texy -**silný a zelený .{color:green}** jako Hulk -\-- - -/--texysource -**silný a zelený .{color:green}** jako Hulk -\-- - -/--div .[output] -**silný a zelený .{color:green}** jako Hulk -\-- - - -Přímé HTML -========== - -Texy není náhrada za HTML. Nehledá ani alternativní způsoby zápisu HTML. Cílem je zjednodušit psaní obsahu. Pokud se Vám zdá jednodušší zapsat některou strukturu přímo v HTML, můžete tak učinit. HTML značky jsou plně podporované. - -/--code texy -This <strong class=info>is strong</strong> text. -<br> This is not. -\-- - -/--texysource -This <strong class=info>is strong</strong> text. -<br> This is not. -\-- - -*Poznámka: všimněte si, že Texy upraví zápis atributů a značek tak, aby byly validní (i pro XHTML výstup). Stejně tak dbá na **well-formed zápis**!* - -*Poznámka: Rozhodování, která značky a které atributy můžou být v textu použity, je plně uživatelsky ovladatelné. Demonstruje to jeden příklad z distribuce.* - - -Seznamy -======= - -Odrážkové seznamy zapisujeme pomocí `*` `+` nebo `-`. Musí být zapsán hned na začátku řádku a za ním musí následovat mezera. - -/--code texy -- Red -- Green -- Blue -\-- - -/--texysource -- Red -- Green -- Blue -\-- - -/--div .[output] -- Red -- Green -- Blue -\-- - - -Číslované seznamy ------------------ - -Texy zná těchto pět způsobů zápisu (první dva jsou ekvivalentní): - -/--code texy -1) Učit se -2) Učit se -3) Učit se - -a) Dlouhý -b) Široký -c) Krátkozraký - -A) DOS -B) Windows -C) Linux - -I) Yesterday -II) Today -III) Tomorrow -\-- - -/--texysource -1) Učit se -2) Učit se -3) Učit se - -a) Dlouhý -b) Široký -c) Krátkozraký - -A) DOS -B) Windows -C) Linux - -I) Yesterday -II) Today -III) Tomorrow -\-- - -/--div .[output] -1) Učit se -2) Učit se -3) Učit se - -a) Dlouhý -b) Široký -c) Krátkozraký - -A) DOS -B) Windows -C) Linux - -I) Yesterday -II) Today -III) Tomorrow -\-- - - -Vnořené seznamy ---------------- - -/--code texy -a) Bird - I) Bird - - Red - - Green - - Blue - II) McHale - III) Parish -b) McHale -c) Parish - 1) Bird - 2) McHale - 3) Parish -\-- - -/--texysource -a) Bird - I) Bird - - Red - - Green - - Blue - II) McHale - III) Parish -b) McHale -c) Parish - 1) Bird - 2) McHale - 3) Parish -\-- - - -Definiční seznam ----------------- - -/--code texy -Koncert Divokej Bill: - - termín: 9. 12. 2004 - - místo: Hala Vodová, Brno - - Cena: 260 Kč -\-- - -/--texysource -Koncert Divokej Bill: - - termín: 9. 12. 2004 - - místo: Hala Vodová, Brno - - Cena: 260 Kč -\-- - -/--div .[output] -Koncert Divokej Bill: - - termín: 9. 12. 2004 - - místo: Hala Vodová, Brno - - Cena: 260 Kč -\-- - - -S modifikátorem ---------------- - -Modifikátor, který ovlivňuje celý seznam, se uvádí na řádku před ním. Ostatní (klasicky) na konci řádku: - -/--code texy -.{color:red} -triangl: .{color:blue} - - trojúhelník .{color:green} - - neladěný bicí hudební nástroj - - tringulační věž -\-- - -/--div .[output] -.{color:red} -triangl: .{color:blue} - - trojúhelník .{color:green} - - neladěný bicí hudební nástroj - - tringulační věž -\-- - - -Modifikátory -============ - -Nejsilnější zbraň Texy Lze použít tyto druhy modifikátorů: - -- (titulek) popisné, přidají objektu titulek (nebo alternativní text obrázkům) -- `[class1 class2 #id]` určující třídu a / nebo ID prvku -- {class:blue} přímý zápis stylu -- {target:_blank} nebo přímý zápis HTML atributů -- horizontální zarovnání: - - doleva < - - doprava > - - vycentrovaný <> - - do bloku = - -- vertikální zarovnání: (jen u tabulek) - - nahoru ^ - - na střed - - - dolů _ - -Modifikátory se zapisují spojitě (bez mezer) a **musí jim předcházet tečka**. Takže třeba `.(popis)[left]` nastavuje atribut title na `popis` a třídu na `left`. - -**Modifikátory je vždy zapisují zcela doprava**. - -Příklad použití modifikátoru na odstavci textu: - -/--code texy -Vycentrováno modifikátorem .<> - -Obarveno modifikátorem .{color:blue; lang: cs} -\-- - -/--texysource -Vycentrováno modifikátorem .<> - -Obarveno modifikátorem .{color:blue; lang: cs} -\-- - - -Typografie -========== - -Sem patří všechny úpravy a náhrady textu, které upravují jeho vzhled v souladu s typografickými pravidly a podobně: - -/--code texy -- "české" 'typografické' uvozovky -- pomlčka vs. spojovník: 10-15 vs. česko-slovenský -- pomlčka: jedna -- dvě -- typografický křížek u rozměrů 10 x 20 -- šipky <- a -> a <-> ; -- tři tečky... -- zachování HTML entit & -- náhrady(TM) nebo(R) za příslušné entity(C) -\-- - -/--div .[output] -- "české" 'typografické' uvozovky -- pomlčka vs. spojovník: 10-15 vs. česko-slovenský -- pomlčka: jedna -- dvě -- typografický křížek u rozměrů 10 x 20 -- šipky <- a -> a <-> ; -- tři tečky... -- zachování HTML entit & -- náhrady(TM) nebo(R) za příslušné (C)entity -\-- - -práce s mezerami: - -/--code texy -- vkládání nezalomitelných mezer za jednopísmenné předložky (v autě u okna) -- nedělitelné mezery u telefonních čísel +420 776 552 046 -\-- - -/--code html -vkládání nezalomitelných mezer za jednopísmenné předložky (v autě u okna) - -nedělitelné mezery u telefonních čísel +420 776 552 046 -\-- - -*Poznámka: Nahrazování se obvykle řídí dalšími pravidly, které určují, kdy -symbol nahradit a kdy ne. Například šipka `->` nemůže být na konci řádku atd. Proto nebuďte překvapeni, když v některých případech Texy náhradu neprovede. Pokud to považujete za chybu, dejte mi vědět.* - - -Zkratky, akronymy ------------------ - -Používá se zápisu s dvojitou kulatou závorkou: - -/--code texy -jednoslovné: NATO((North Atlantic Treaty Organisation)) - -víceslovné: "et al."((a další)) -\-- - -/--texysource -jednoslovné: NATO((North Atlantic Treaty Organisation)) - -víceslovné: "et al."((a další)) -\-- - - -Klikatelné URI --------------- - -Automatický převod URI do klikatelné formy (včetně emailů) - -/--code texy -další informace na www.texy.info a také ... -\-- - -/--div .[output] -další informace na www.texy.info a také ... -\-- - - -Rozdělení velmi dlouhých slov -============================= - -Velmi zajímavá a důležitá funkce Texy. Dlouhá slova mohou narušit vzhled stránky, proto je vhodné prohlížeči naznačit, kde je může zalomit. Texy tyto místa hledá s přihlédnutím k národním zvyklostem, tedy slovo rozděluje podle slabik: - -/--code texy -nejneobhospodařovávatelnějšími -\-- - -/--code html -nejneobhospoda­řovávatelnější­mi</p -\-- - -*Poznámka: limit délky slova je volitelný* - -*Poznámka: současné prohlížeče na jádru Gecko (Mozilla, Firefox) jsou k naznačenému dělení slepí. Doufám, že vývojáři tento nedostatek brzy odstraní. Nebo zkuste [tohle | https://forum.texy.info/cs/viewtopic.php?id=36]* - - -Tabulky -======= - -Příklad jednoduché tabulky, sloupce se oddělují znakem `|` - -/--code texy -| first col | second col | third col -| Adam | Eva | Franta -\-- - -A výsledek je: - -| first col | second col | third col -| Adam | Eva | Franta - - -Hlavičku tabulky můžeme definovat tímto zápisem: - -/--code texy -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Jesus | Christ | 33 -| Cecilie | Svobodova | 74 -\-- - -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Jesus | Christ | 33 -| Cecilie | Svobodova | 74 - -Pokud hlavičku netvoří řádek (řádky), můžeme ji definovat na úrovni buněk. Stačí vložit hvězdičku ihned po znaku `|` - - -/--code texy -|* First Name | Jesus | Cecilie -|* Last Name | Christ | Svobodova -|* Age | 33 | 74 -\-- - - -|* First Name | Jesus | Cecilie -|* Last Name | Christ | Svobodova -|* Age | 33 | 74 - - -Sloučení sloupců ----------------- - -všimněte si zdvojeného || - -/--code texy -|----------------------------- -| Name || Age -|---------------------------- -| Jesus | Christ | 33 -\-- - -|----------------------------- -| Name || Age -|---------------------------- -| Jesus | Christ | 33 - - -Sloučení řádků --------------- - -Všimněte si znaku `^` symbolizujícího směr nahoru: - - -/--code texy -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Bill || 50 -| ^| 52 -| Jim | Beam | 70 -\-- - -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Bill || 50 -| ^| 52 -| Jim | Beam | 70 - - -Modifikátory ------------- - -Platí tato pravidla: -- modifikátor ovlivňující celou tabulku se vkládá bezprostředně před tabulku -- ovlivňující řádek se vkládá na konec řádku -- ovlivňující sloupec se vkládá na začátek buňky (vlevo v buňce) -- a nakonec ovlivňující buňku se vkládá na konec buňky (pravo v buňce) - -Podívejte se na příklad. - -/--code texy -.(people) -| .{color: green} first col | second col .>| third col | .{font-style:italic} -| Adam | Eva .{color: blue}| Franta | -\-- - -Zde je: -- `.(people)` modifikátor tabulky -- `.{color: green}` modifikátor sloupce -- `.{font-style:italic}` modifikátor řádku -- `.{color: blue}` a také `.>` modifikátor buňky - -Takže výsledná tabulka vypadá takto: - - -.(people) -| .{color: green} first col | second col .>| third col | .{font-style:italic} -| Adam | Eva .{color: blue}| Franta | diff --git a/texy/cs/syntax.texy b/texy/cs/syntax.texy index 9a430f8080..a75396973c 100644 --- a/texy/cs/syntax.texy +++ b/texy/cs/syntax.texy @@ -1,464 +1,891 @@ Syntaxe ******* ---> "Podrobný popis syntaxe":syntax-podrobne +.[perex] +Texy vznikl proto, aby nezkušeným uživatelům umožnil snadno editovat obsah webových stránek. Proto je i syntaxe intuitivní a přehledná. + + +Cheat Sheet +=========== + +| [#Formátování textu] | Syntax +|----------------------------------------------- +| [Tučný text |#Formátování textu] | .[text-code] ''**tučný text**'' +| [Kurzíva |#Formátování textu] | ''*kurzíva*'' nebo ''//kurzíva//'' +| [Inline code |#Formátování textu] | ''`kód`'' +| [#Odkazy] | ''"text":URL'' nebo ''[text](URL)'' +| [#Obrázky] | ''[* image.jpg *]'' +| [#Vypnutí formátování] | ''specialní znaky'' +|----------------------------------------------- +| Elementy +|----------------------------------------------- +| [#Podtržené nadpisy] | H1 <br> === +| [#Ohraničené nadpisy] | ''### H1'' <br> ## H2 +| [#Odrážkové seznamy] | ''- první'' <br> ''- druhá'' +| [#Číslované seznamy] | ''1) první'' <br> ''2) druhá'' +| [#Seznamy definic] | term: <br>   ''- první'' +| [#Citace] | ''> blockquote'' +| [#Horizontální čáry] | ''---'' +| [#Tabulky] | ''\| buňka \| buňka \|'' +| [Bloky kódu|#Předformátovaný text] | ''/--'' <br> ... <br> ''\--'' +|----------------------------------------------- +| Modifikátory .[#toc-modifikatory] +|-------------------------------------------------------- +| titulek | ''.(titulek)'' +| CSS třída | ''.[btn btn-primary]'' +| ID | ''.[#id]'' +| CSS styl nebo HTML atribut | ''.{color: blue}'' nebo ''.{target: _blank}'' +| horizontální zarovnání | ''.< .> .<> .='' +| vertikální zarovnání | ''.^ .- ._'' + + +Odstavce textu +============== + +Za odstavec považuje Texy jeden nebo více řádků textu, které následují těsně za sebou. Jakmile mezi nimi necháte **jeden prázdný řádek**, Texy automaticky pochopí, že má začít nový odstavec. + +To znamená, že Texy spojí řádky, které patří k sobě. Nemusíte se tak bát, že se vám věta zalomí uprostřed, když si zmenšíte okno editoru. + +```texy +Toto je první odstavec. Může mít klidně více řádků +a Texy je spojí do jednoho souvislého bloku textu. + +Až tady, po prázdném řádku, začíná úplně nový, druhý odstavec. +``` + +Spojování řádků lze nicméně vypnout v konfiguraci a pak se každý řádek považuje za samostatný odstavec: + +/--php +$texy->mergeLines = false; +\-- + + +Zalomení řádků +-------------- +Co když ale potřebujete text jen odřádkovat, aniž byste vytvářeli celý nový odstavec? To se typicky hodí u básní, textů písní nebo při psaní adresy. **Začněte nový řádek jednou mezerou**. -Nástroj Texy vznikl proto, aby nezkušeným uživatelům umožnil snadno editovat obsah webových stránek. Proto je i syntaxe maximálně intuitivní. Záměrem je, aby text v čisté (nezformátované) formě byl přehledný a jeho formát tušitelný. +```texy +Karel Novák, + U Tiché pošty 5 + 150 00 Praha 5 +``` -Dnes Texy výborně slouží i zkušeným znalcům jazyka HTML. Dovoluje volně kombinovat Texy zápis s HTML značkami. Zkušení uživatelé se tedy nemusí učit nový meta-jazyk a plně využít svých znalostí. Texy jim pouze zjednodušuje práci. -Prvotní logikou syntaxe je **žádnou syntaxi nepoužívat**. Jen psát čistý text. Vkládání rozšířených informací, jako třeba CSS třídy nebo odkazy, nenaruší tok textu. A zapíší se způsobem, který snadno pochopí i netechnicky založení uživatelé. +Stylování odstavců +------------------ +Někdy potřebujete celý odstavec nějak odlišit – například z něj udělat úvodní perex článku, vycentrovat ho nebo mu přiřadit specifický styl pro rámeček. K tomu slouží [#modifikátory], které můžete umístit buď na samostatný řádek **před** odstavec, nebo na konec jeho posledního řádku. -Odstavce textu .[#paragraph] -============================ +```texy +.[perex] +Toto je úvodní odstavec článku, který díky modifikátoru +dostane CSS třídu "perex" a může tak vypadat jinak než zbytek textu. -Za odstavec se považuje jeden nebo více bezprostředně za sebou následujících řádků textu. Odstavečky jsou od sebe oddělené prázdným řádkem. +Tento odstavec má zase přiřazené unikátní ID. .[#sekce-uvod] -/--code texy -První odstavec lorem ipsum dolor sit amet. +A tento odstavec bude vycentrován. .<> +``` -Druhý odstavec, který tvoří jeden řádek. -A druhý řádek textu. Texy je spojí. -\-- -Zalomení řádku v odstavci docílíte vložením jedné mezery vlevo: +Formátování textu +================= -/--code texy -Kdoví jestli - jestli jsou na měsíci vůbec nějaký stopy - a proč kope kolem sebe kdo se topí - jakej sval to Zemí otáčí +| syntax | výstup | ID syntaxe +|----------------------------------------------------------------------------- +| .[text-code] ''**tučný text**'' | **tučný text** | `phrase/strong` +| ''*kurzíva* nebo //kurzíva//'' | *kurzíva* | `phrase/em-alt`, `phrase/em` +| ''***tučná kurzíva***'' | ***tučná kurzíva*** | `phrase/strong+em` +| ''`inline kód`'' | `inline kód` | `phrase/code` +| ''x^2 … O_2'' | x^2 … O_2 | `phrase/sup-alt`, `phrase/sub-alt` +| ''x^^2^^ … O__2__'' | x^2 … O_2 | `phrase/sup`🔸, `phrase/sup`🔸 +| ''++vložený text++'' | <ins>vložený text</ins> | `phrase/ins`🔸 +| ''--smazaný text--'' | <del>smazaný text</del> | `phrase/del`🔸 +| ''>>citovaný text<<'' | >>citovaný text<< | `phrase/quote` +| ''"modrý text .{color: blue}"'' | "modrý text .{color: blue}" | `phrase/span` +| ''~modrý text .{color: blue}~'' | ~modrý text .{color: blue}~ | `phrase/span-alt` +| ''"et al."((a další))'' | "et al."((a další)) | `phrase/acronym` +| ''NBA((National Basketball Association))'' | NBA((National Basketball Association)) | `phrase/acronym-alt` + +Syntaxe označené 🔸 nejsou ve výchozím stavu povolené a musíte je zapnout. Příklad: + +/--php +$texy->allowed['phrase/ins'] = true; \-- +Pro jednoduché číselné indexy můžete použít zkrácenou syntaxi `x^2` a `O_2`, ale pro složitější případy je robustnější varianta s dvojitými znaky, nebo můžete použít HTML značky `<sup>` a `<sub>`. -Titulky .[#heading] -=================== +Uvnitř syntaktických znaků **nesmí být mezery**: -Titulky je možné zapsat hned dvěma způsoby: **podtržením** nebo **předsazením**. +```texy +Špatně: ** toto nebude tučné ** +Správně: **toto bude tučné** +``` -Každý titulek má svůj stupeň. V případě **podtržení** o důležitosti titulku rozhoduje podtrhávací znak. Od nejvyšší po nejnižší jsou to tyto: `#` `*` `=` `-` -/--code texy -Hlavní titulek -************** +Stylování textu +--------------- +Tohle je jedna z nejsilnějších vlastností Texy. Ke každému formátovanému textu můžete "přilepit" [#modifikátory] a přidat mu tak CSS třídu, ID nebo přímý styl. Modifikátor se vždy vkládá **těsně před uzavírací značku**: -Podtitulek -========== -\-- +```texy +Tento text je **silný a zelený .{color:green}** jako Hulk. -U titulků zapsaných **předsazením** určuje úroveň počet předsazených znaků. A ty mohou být `#` nebo `=` +Upozornění: --Tato funkce je zastaralá .[deprecated]-- +``` -Platí: čím více znaků, tím důležitější titulek (minimum jsou dva znaky, maximum sedm). +Pokud chcete aplikovat modifikátor na text, ale nechcete ho zároveň dělat tučným nebo kurzívou, použijte jako obalovací značku uvozovky `"` nebo vlnovky `~`. Texy z toho vytvoří univerzální HTML značku `<span>` s vašimi styly: -/--code texy -=== Hlavní titulek === +```texy +Běžný text, ale "tento kousek je červený .{color: red}", a zbytek už ne. +``` -## Podtitulek -\-- -Jak vidíte v případě podtitulku, znaky vpravo je možné vynechat. +Formátování a odkazy v jednom +----------------------------- +Z formátovaného textu můžete udělat odkaz - jednoduše přidejte dvojtečku a URL adresu: -Horizontální čáry .[#horizline] -=============================== +```texy +Navštivte naši **novou galerii**:https://example.com/gallery +``` -Texy zná tyto způsoby zápisu: +Toto funguje pro tučný text, kurzívu, inline kód. -/--code texy ------------- +Psaní speciálních znaků +----------------------- -******** -\-- +Co když chcete napsat doslova `**text**` včetně hvězdiček, aniž by se z něj stal tučný text? Máte tři možnosti: +- zpětné lomítko je nejrychlejší způsob, jak "zneplatnit" jeden speciální znak `\**text\**` +- dvojité apostrofy [vypnou Texy|#Vypnutí formátování] pro celou frázi `''**text**''` +- můžete použít standardní HTML entity `**text**` -Vypnutí Texy .[#disable-texy] -============================= -Klíčové slovo `html` nebo `text` ovlivňuje, jestli obsah bude chápán jako HTML (včetně značek), nebo prostý text. +Odkazy +====== -/--code texy - /---html - <em>příklad</em>: **this is not strong** - \--- +Odkazy jsou duší internetu. V Texy je jejich tvorba navržena tak, aby byla co nejpřirozenější a nejpřehlednější přímo v textu. +Základní syntaxe pro odkaz je jednoduchá a skvěle čitelná. Odkazovaný text uzavřete do `"` (nebo jiných znaků pro [#formátování textu]) a hned připojíte dvojtečku a cílovou URL adresu: - /---text - <em>příklad</em>: **this is not strong** - \--- -\-- +```texy +Navštivte oficiální stránky projektu "Nette Framework":https://nette.org. -Pro inline vypnutí Texy je možné použít dvojitý apostrof `''` a obalit s ním část textu, který nemá být Texy zpracováván. +Pokud máte dotaz, "napište nám":info@example.com. +``` -/--code texy - Příklad: ''**this is not strong**'' -\-- +Výhodou je, že Texy je inteligentní a samo pozná, kde URL končí. Nemusíte se tedy bát, že by do odkazu omylem zahrnulo tečku nebo čárku na konci věty. Pokud ale URL obsahuje nestandardní znaky, můžete je uzavřít do hranatých závorek a tím přesně řeknete, kde adresa začíná a končí: +```texy +"Přečtěte si náš článek":[https://example.com/novinky?id=1&kategorie=články] +``` -Citace .[#blockquote] -===================== +ID syntaxe `phrase/span`, `phrase/span-alt` | [PhraseModule |configuration#phrasemodule] a [LinkModule |configuration#linkmodule] -Citace jsou odsazené, podobně jako v emailech, znakem `>` -/--code texy -> This is a blockquote with two paragraphs. -> -> 640 K should be enough for everyone -\-- +Alternativní syntaxe odkazů +--------------------------- +Jste zvyklí na formát, který používá Markdown nebo Wikipedia? Texy rozumí i jim. Můžete si vybrat styl, který vám nejvíce vyhovuje. -Odkazy .[#link] -=============== +```texy +[Text odkazu](https://adresa.cz) // Styl známý z Markdownu +[Text odkazu | https://adresa.cz] // Styl známý z MediaWiki +text:[cílová URL nebo reference] // Jednoslovný odkaz +``` -Odkazy se zapisují tak, že odkazující text uzavřete do uvozovek a následujete dvojtečkou a URL. Texy se snaží inteligentně odhadnout konec URL. Můžete mu i pomoci tím, že URI uzavřete do hranatých závorek. Část `http://` není povinná. +ID syntaxe `phrase/markdown`, `phrase/wikilink`, `phrase/quicklink` | [PhraseModule |configuration#phrasemodule] -Jako odkaz je možné vkládat i emaily, Texy je transformuje do podoby, která by měla zmást spamboty. -/--code texy -Look at homepage:[https://texy.info]. +Udržujte si pořádek s referencemi +--------------------------------- -Do you know "La Trine":https://www.latrine.cz? +Při psaní delších textů může být nepohodlné vkládat dlouhé URL adresy přímo do odstavců – zhoršuje to čitelnost a přehlednost. Pro tyto případy má Texy **referenční odkazy**. -"Write me":me@example.com +V textu použijete pouze krátký, snadno zapamatovatelný název reference. A na konci dokumentu pak všechny tyto reference přehledně definujete. -\-- +```texy +Doporučujeme si prostudovat "oficiální dokumentaci":[doc] a projít si "příklady syntaxe":[syntax]. +Celý projekt je postaven na [Nette]. +​[doc]: https://texy.nette.org/cs/ "Dokumentace Texy!" +​[syntax]: https://texy.nette.org/cs/syntax +​[Nette]: https://nette.org +``` -Obrázky .[#image] -================= +ID syntaxe `link/reference`, `link/definition` | [LinkModule |configuration#linkmodule] -Zapisují se mezi hranaté závorky s hvězdičkou: -/--code texy -[* image.gif .(alternativní text) *] -\-- +Automatické odkazy +------------------ + +Kdykoli do textu napíšete URL adresu (začínající na `http://`, `https://`, `www.`) nebo e-mail, Texy ji automaticky rozpozná a převede na klikatelný odkaz. Nemusíte dělat vůbec nic. +```texy +Náš web najdete na adrese www.example.com. +Pro podporu pište na support@example.com. +``` -V textových odstavcích je často třeba zvolit, má-li být obrázek zarovnán k levému nebo pravému kraji. Toho docílíte pomocí znaku `<` a `>` použitého před pravou závorkou: +ID syntaxe `link/url`, `link/email` | [LinkModule |configuration#linkmodule] + + +Stylování odkazů +---------------- -/--code texy -[* image.gif <] Left-aligned image. Lorem ipsum ... +S [#modifikátory] můžete odkazům snadno přidávat další vlastnosti: -[* image.gif >] Right-aligned image. Curabitur quam ... +```texy +"Externí odkaz .[external](Otevře se v novém okně){target:_blank}":https://google.com +``` + +Speciální třída `nofollow` přidá odkazu atribut `rel="nofollow"`, čímž dáváte vyhledávačům signál, aby tento odkaz nesledovaly. To se hodí například u odkazů v komentářích. + +```texy +"Odkaz, kterému nedůvěřuji .[nofollow]":https://example.com +``` + + +Automatické maskování e-mailů +----------------------------- + +Texy automaticky obfuskuje (maskuje) emailové adresy před spamboty: + +```latte +<a href="mailto:info@example.com">info@<!-- -->example.com</a> +``` + +Toto chování můžete vypnout: + +/--php +$texy->obfuscateEmail = false; \-- -Obrázek s popiskou +Přímé HTML +========== + +Texy je navržen tak, abyste HTML nemuseli psát vůbec. Ale co když narazíte na situaci, kdy je přímé vložení HTML značky jednodušší, nebo potřebujete vytvořit něco, na co syntaxe Texy nestačí? Žádný problém. Texy vám dává naprostou svobodu kombinovat oba světy. + +Můžete plynule přecházet mezi Texy syntaxí a čistým HTML, kdykoli se vám to hodí. + +```texy +Toto je **tučný text** v Texy a toto je <strong>tučný text</strong> pomocí HTML. + +<div class="info-box"> + <h3>Můžete vkládat i celé komplexní bloky</h3> +</div> +``` + +Možná si říkáte, že vkládání přímého HTML může být riskantní. Co když uděláte chybu nebo někdo vloží škodlivý kód? Texy na to myslí a funguje jako inteligentní filtr a pomocník: + +- **Opravuje chyby:** Texy zajistí, aby byl výsledný kód vždy validní a nerozbil vám stránku. +- **Hlídá bezpečnost:** Texy má ve výchozím stavu seznam povolených značek a jejich atributů. Pokud se v kódu objeví neznámá značka nebo potenciálně nebezpečný atribut (např. `onclick`), Texy ho bezpečně odstraní. Chrání tak váš web před XSS útoky. +- **Zajišťuje konzistentní výstup:** Bez ohledu na to, jaký HTML kód vložíte, Texy se postará, aby byl výsledek vždy správně strukturovaný (well-formed). + +Tento ochranný štít si můžete přizpůsobit. Pomocí konfigurace `$texy->allowedTags` můžete přesně definovat, které HTML značky a atributy jsou na vašem webu povoleny a které ne. + +Máte tak plnou kontrolu nad tím, jaké HTML mohou například redaktoři používat, a zajišťujete tak konzistenci a bezpečnost celého webu. Více informací naleznete v sekci "konfigurace":configuration#allowedtags. + +ID syntaxe `html/tag`, `html/comment` | [HtmlModule |configuration#htmlmodule] + + +Nadpisy +======= + +Texy vám nabízí dva elegantní a intuitivní způsoby, jak nadpisy vytvářet: **podtržené** a **ohraničené**. + + +Podtržené nadpisy +----------------- + +Tento styl připomíná psací stroj. Jednoduše pod nadpis vložte podtržení (alespoň 3 znaky). O důležitosti titulku rozhoduje podtrhávací znak. Od nejvyšší po nejnižší jsou to tyto: `#` `*` `=` `-` + +```texy +Toto je nejdůležitější nadpis celého dokumentu +​################################################ + +A toto je nadpis druhé úrovně +​****************************** +``` + +ID syntaxe `heading/underlined` | [HeadingModule |configuration#headingmodule] + + +Ohraničené nadpisy ------------------ -Za obrázkem uveďte tři hvězdičky a následuje popiska: +Tento způsob je velmi rychlý na psaní. Text nadpisu "zabalíte" mezi znaky `#` nebo `=`. Zde o úrovni nadpisu rozhoduje **počet** použitých znaků (2 až 7). Čím více znaků, tím důležitější nadpis. +```texy +=== Nejdůležitější nadpis (H1) -/--code texy -[* image.gif *] *** Toto je *popiska* pod obrázkem -\-- +== Méně důležitý (H2) +# Ještě méně důležitý (H3) +``` -Fráze .[#phrase] -================ +Můžete použít ohraničení na obou stranách (pro lepší vizuální přehlednost) nebo jen na začátku. Texy si s oběma variantami poradí. -Asi nejpoužívanější syntax v Texy. Téměř ve všech případech se používá zdvojený znak. +ID syntaxe `heading/surrounded` | [HeadingModule |configuration#headingmodule] -/--code texy -//kurzíva// -**tučné** +Stylování nadpisů +----------------- -x^2 + y^3 -\-- +Ke každému nadpisu můžete přidat [#modifikátory]. To vám umožní přiřadit mu konkrétní CSS třídu pro stylování nebo unikátní ID, na které pak můžete odkazovat. +```texy +Nadpis s červenou barvou .[cerveny-nadpis] +​========================================== -/--div .[output] -//kurzíva// +### Nadpis s unikátním ID pro odkazování .[#kontakt] +``` -**tučné** -x^2 + y^3 -\-- +Automatické kotvy pro snadnou navigaci +-------------------------------------- +Nechcete vymýšlet ID pro každý nadpis ručně? Texy to umí udělat za vás! V konfiguraci můžete zapnout automatické generování ID pro všechny nadpisy. To je neuvěřitelně užitečné pro přímé odkazování na konkrétní sekce. -Texy lze i dočasně vypnout - obsah nebude formátován a zobrazí se doslovně: +```php +// Povolit automatické generování ID +$texy->headingModule->generateID = true; -/--code texy -Odstraňte ''<br />'' a entitu ''&ndash'' -\-- +// Volitelně nastavit předponu pro generovaná ID (např. "sekce-") +$texy->headingModule->idPrefix = 'toc-'; +``` +S tímto nastavením nadpis `## Moje kapitola` automaticky dostane například ID `id="toc-moje-kapitola"`, aniž byste museli cokoliv psát navíc. -Přímé HTML .[#html] -=================== -Texy není náhrada za HTML. Nehledá ani alternativní způsoby zápisu HTML. Cílem je zjednodušit psaní obsahu. Pokud se Vám zdá jednodušší zapsat některou strukturu přímo v HTML, můžete tak učinit. HTML značky jsou plně podporované. +Seznamy +======= -/--code texy -This <strong class=info>is strong</strong> text. -<br> This is not. -\-- +Odrážkové seznamy +----------------- -Seznamy .[#list] -================ +Pro rychlý výčet položek, u kterých nezáleží na pořadí, se skvěle hodí odrážkový seznam. Stačí každý řádek začít pomlčkou `-`, hvězdičkou `*` nebo pluskem `+` a mezerou. Všechny tři znaky fungují stejně, takže si můžete vybrat ten, který je vám nejsympatičtější. -Odrážkové seznamy zapisujeme pomocí `*` `+` nebo `-`. Musí být zapsán hned na začátku řádku a za ním musí následovat mezera. +```texy +Co je potřeba nakoupit: -/--code texy -- Red -- Green -- Blue -\-- +- Mléko +- Chleba +* Vejce ++ Máslo +``` + +ID syntaxe `list` | [ListModule |configuration#listmodule] Číslované seznamy ----------------- -Texy zná těchto pět způsobů zápisu (první dva jsou ekvivalentní): +Texy podporuje různé styly číslování: -/--code texy -1) Učit se -2) Učit se -3) Učit se +| `1.` | Arabské číslice (s tečkou) +| `1)` | Arabské číslice (se závorkou) +| `a)` | Malá písmena abecedy +| `A)` | Velká písmena abecedy +| `I)` | Římské číslice -a) Dlouhý -b) Široký -c) Krátkozraký +Kouzlo spočívá v tom, že se vůbec nemusíte starat o správné číslování. I když všechny řádky očíslujete jedničkou, Texy je automaticky přečísluje za vás. To je obrovská výhoda, když později potřebujete nějakou položku přidat, smazat nebo přesunout. -A) DOS -B) Windows -C) Linux -I) Yesterday -II) Today -III) Tomorrow -\-- +Vnořené a kombinované seznamy +----------------------------- + +Síla seznamů se naplno projeví, když je začnete kombinovat a vnořovat. Můžete tak vytvářet přehledné, víceúrovňové struktury. Vnoření vytvoříte jednoduše tak, že daný řádek odsadíte alespoň o **dvě mezery** (nebo jeden tabulátor). + +```texy +1) První kapitola + a) Podkapitola 1.1 + - První bod + - Druhý bod + b) Podkapitola 1.2 +2) Druhá kapitola + - Hlavní myšlenka + - Další poznámka +``` -Vnořené seznamy +Seznamy definic --------------- -/--code texy -a) Bird - I) Bird - - Red - - Green - - Blue - II) McHale - III) Parish -b) McHale -c) Parish - 1) Bird - 2) McHale - 3) Parish -\-- +Pro případy, kdy potřebujete vytvořit slovníček pojmů nebo přehledně vysvětlit několik termínů, je ideální definiční seznam. +Na první řádek napište termín, který chcete definovat, a zakončete ho dvojtečkou. Na další řádky pište jeho definici, přičemž každý řádek odsaďte a začněte pomlčkou `-` -Definiční seznam ----------------- +```texy +HTML: + - Značkovací jazyk pro tvorbu webových stránek. + - Zkratka pro HyperText Markup Language. -Koncert "Divokej Bill":www.divokybill.cz: - - termín: 9. 12. 2004 - - místo: Hala Vodová, Brno - - Cena: 260 Kč +CSS: + - Jazyk pro popis způsobu zobrazení (stylování) stránek. + - Zkratka pro Cascading Style Sheets. +``` -/--code texy -Koncert Divokej Bill: - - termín: 9. 12. 2004 - - místo: Hala Vodová, Brno - - Cena: 260 Kč -\-- +ID syntaxe `list/definition` | [ListModule |configuration#listmodule] -Modifikátory .[#modifier] -========================= +Stylování seznamů +----------------- -Nejsilnější zbraň Texy. Lze použít tyto druhy modifikátorů: +Stejně jako u ostatních prvků v Texy, i seznamům můžete snadno přidávat [#modifikátory] pro změnu vzhledu. -- (titulek) popisné, přidají objektu titulek (nebo alternativní text obrázkům) -- `[class1 class2 #id]` určující třídu a / nebo ID prvku -- {class:blue} přímý zápis stylu -- {target:_blank} nebo přímý zápis HTML atributů -- horizontální zarovnání: - - doleva < - - doprava > - - vycentrovaný <> - - do bloku = +**Celý seznam:** Modifikátor napište na řádek **před** začátkem seznamu. -- vertikální zarovnání: (jen u tabulek) - - nahoru ^ - - na střed - - - dolů _ +```texy +.[barevny-seznam] +- První položka +- Druhá položka +``` -Modifikátory se zapisují spojitě (bez mezer) a **musí jim předcházet tečka**. Takže třeba `.(popis)[left]` nastavuje atribut title na `popis` a třídu na `left`. +**Jednotlivá položka:** Modifikátor přidejte na **konec** řádku dané položky nebo definičního termínu. -**Modifikátory je vždy zapisují zcela doprava**. +```texy +- Běžná položka +- Tato položka je důležitá! .{font-weight: bold} +- Další běžná položka +``` -Příklad použití modifikátoru na odstavci textu: -/--code texy -Vycentrováno modifikátorem .<> +Obrázky +======= -Obarveno modifikátorem .{color:blue; lang: cs} -\-- +Základní syntaxe je velmi jednoduchá. Cestu k obrázku (ať už lokálnímu souboru, nebo URL adrese) stačí uzavřít do hranatých závorek s hvězdičkou: -/--code html -<p style="text-align:center">Vycentrováno modifikátorem</p> +```texy +[* obrazek.jpg *] +[* https://domena.cz/logo.png *] +``` -<p style="color:blue" lang="cs">Obarveno modifikátorem</p> -\-- +Často budete chtít, aby text obrázek obtékal. K tomu slouží jednoduché zarovnávací značky, které se vkládají před uzavírací závorku: +```texy +[* obrazek.jpg <] Tento text bude plynule obtékat obrázek z pravé strany. -Typografie .[#typography] -========================= +[* obrazek.jpg >] V tomto případě bude text naopak obtékat obrázek z levé strany. -Sem patří všechny úpravy a náhrady textu, které upravují jeho vzhled v souladu s typografickými pravidly a podobně: +[* velky-obrazek.jpg <>] +Tento text bude pokračovat až pod vycentrovaným obrázkem. +``` -/--code texy -- "české" 'typografické' uvozovky -- pomlčka vs. spojovník: 10-15 vs. česko-slovenský -- pomlčka: jedna -- dvě -- typografický křížek u rozměrů 10 x 20 -- šipky <- a -> a <-> ; -- tři tečky... -- zachování HTML entit & -- náhrady(TM) nebo(R) za příslušné entity(C) -\-- +Správně vložený obrázek by měl mít i tzv. "alternativní text", který se zobrazí, pokud se obrázek nenačte. Pomocí [modifikátoru|#modifikátory] můžete přidat tento text i další prvky pro stylování. + +```texy +[* fotka-krajiny.jpg .(Krásná horská krajina při západu slunce)[main-photo] *] +``` + + +Rozměry obrázků +--------------- + +Texy umí u lokálních obrázků automaticky zjistit jejich rozměry (pokud je nastavena cesta `$texy->imageModule->fileRoot`) a doplnit je do HTML, což zrychluje načítání stránky. Pokud ale chcete rozměry nastavit ručně, máte několik možností: + +| `[* img.jpg 150x100 *]` | Přesná šířka 150px a výška 100px +| `[* img.jpg 150 *]` | Šířka bude 150px, výška se automaticky dopočítá se zachováním poměru stran +| `[* img.jpg ?x100 *]` | Výška bude 100px, šířka se automaticky dopočítá + + +Klikatelné obrázky +------------------ + +Chcete, aby se po kliknutí na malý náhled zobrazil velký obrázek? Nebo aby obrázek odkazoval na jinou stránku? Stačí za syntaxi obrázku přidat dvojtečku a cílovou URL. + +```texy +[* nahled.jpg *]:velky.jpg +[* logo-nette.png *]:https://nette.org +``` + +Pro galerie existuje i šikovná zkratka `::`. Ta automaticky vytvoří odkaz na stejný soubor umístěný na `$texy->imageModule->linkedRoot`. + + +Viditelný popisek pod obrázkem +------------------------------ + +Pokud chcete pod obrázek přidat viditelný popisek (např. jméno autora nebo popis scény), napište za něj tři hvězdičky `***` a text popisku. Texy z toho automaticky vytvoří sémanticky správnou HTML strukturu `<figure>` a `<figcaption>`. + +```texy +[* fotka.jpg <> *] *** Toto je popisek. Může obsahovat i **další formátování**. +``` + + +Udržujte si pořádek s referencemi +--------------------------------- + +Pokud v textu používáte jeden obrázek vícekrát nebo chcete mít všechny definice obrázků přehledně na jednom místě, můžete použít reference. V textu použijete jen zástupný název a na konci souboru pak definujete, co tento název znamená. + +```texy +V našem logu [* firemni-logo *] je vidět symbol naší vize. + +​[* firemni-logo *]: /images/logo.svg 200x50 .(Logo naší společnosti) +``` + +Tento přístup výrazně zpřehledňuje hlavní text a usnadňuje správu obrázků. + +ID syntaxe `image/definition` | [ImageModule |configuration#imagemodule] -/--div .[output] -- "české" 'typografické' uvozovky -- pomlčka vs. spojovník: 10-15 vs. česko-slovenský -- pomlčka: jedna -- dvě -- typografický křížek u rozměrů 10 x 20 -- šipky <- a -> a <-> ; -- tři tečky... -- zachování HTML entit & -- náhrady(TM) nebo(R) za příslušné (C)entity + +Předformátovaný text +==================== + +V Texy můžete snadno vložit bloky kódu nebo jakýkoli předformátovaný text, u kterého chcete zajistit, aby se zobrazil přesně tak, jak ho napíšete – včetně všech mezer a konců řádků. To je ideální pro ukázky zdrojových kódů, logů nebo ASCII artu. + +Pro vložení takového bloku použijte ohraničení `/--` a `\--`: + +```texy +/-- +function hello() { + echo 'Hello World'; +} \-- +``` -práce s mezerami: +Aby byl váš kód ještě čitelnější, můžete Texy sdělit, v jakém programovacím jazyce je napsaný a vytvořit si handler, který například obarví syntaxi, viz "ukázka":custom-handlers#syntax-highlighting. Stačí za úvodní značku `/--` přidat klíčové slovo `code` a název jazyka: -/--code texy -- vkládání nezalomitelných mezer za jednopísmenné předložky (v autě u okna) -- nedělitelné mezery u telefonních čísel +420 776 552 046 +```texy +/--code javascript +console.log('JavaScript'); \-- /--code html -vkládání nezalomitelných mezer za jednopísmenné předložky (v autě u okna) - -nedělitelné mezery u telefonních čísel +420 776 552 046 +<div>Tohle je HTML kód</div> \-- +``` -*Poznámka: Nahrazování se obvykle řídí dalšími pravidly, které určují, kdy -symbol nahradit a kdy ne. Například šipka `->` nemůže být na konci řádku atd. Proto nebuďte překvapeni, když v některých případech Texy náhradu neprovede.* +Obsahové bloky (divy) +===================== -Zkratky, akronymy .[#acronym] ------------------------------ +Texy umožňuje vytvářet obecné `<div>` bloky, díky kterým můžete snadno seskupovat obsah do logických celků a následně je stylovat:. -Používá se zápisu s dvojitou kulatou závorkou: +Blok vytvoříte pomocí značek `/--div` a `\--`. Navíc můžete snadno přidat [#modifikátory]: -/--code texy -jednoslovné: NATO((North Atlantic Treaty Organisation)) +```texy +/--div .[important] +## Důležité upozornění -víceslovné: "et al."((a další)) +Tento text bude uzavřen v bloku `<div class="important">`. +Díky tomu ho můžete pomocí CSS nastylovat, aby byl výraznější. \-- +``` +Síla `<div>` bloků spočívá také v možnosti je vnořovat do sebe. Tím můžete vytvářet i složitější struktury přímo v Texy, aniž byste museli psát HTML ručně. -Klikatelné webové adresy ------------------------- +```texy +/--div .[outer] + Toto je vnější blok. -Automatický převod webových adres a emailů do klikatelné formy + /--div .[inner] + A toto je vnořený, vnitřní blok. + \-- -/--code texy -další informace na www.texy.info a také ... + Zde jsme opět ve vnějším bloku. \-- +``` +Díky této jednoduché syntaxi můžete udržovat svůj obsah přehledný a sémanticky správně strukturovaný. -/--div .[output] -další informace na www.texy.info a také ... -\-- +Vypnutí formátování +=================== -Rozdělení velmi dlouhých slov .[#longwords] -=========================================== +Někdy se může hodit Texy na chvíli "vypnout" a vložit kus textu, kde nemá Texy zpracovávat své značky. -Velmi zajímavá a důležitá funkce Texy. Dlouhá slova mohou narušit vzhled stránky, proto je vhodné prohlížeči naznačit, kde je může zalomit. Texy tyto místa hledá s přihlédnutím k národním zvyklostem, tedy slovo rozděluje podle slabik: +Pokud potřebujete vložit komplexnější HTML strukturu bez parsování Texy značek, použijte blok `/--html`: -/--code texy -nejneobhospodařovávatelnějšími -\-- +```texy +/--html +<em>Tento text bude zpracován jako HTML, takže bude kurzívou.</em> -/--code html -nejneobhospoda­řovávatelnější­mi</p +**Ale tyto hvězdičky Texy ignoruje, takže tučné nebudou.** \-- +``` -*Poznámka: limit délky slova je volitelný* +V případě, že chcete zobrazit text přesně tak, jak je napsán, a ignorovat veškeré značky (jak Texy, tak HTML), použijte blok `/--text`. Vše uvnitř tohoto bloku se zobrazí jako obyčejný text. +```texy +/--text +<em>Tento text se zobrazí i se značkami, kurzívou ale nebude.</em> -Tabulky .[#table] -================= +**Ani toto nebude tučné.** +\-- +``` -Příklad jednoduché tabulky, sloupce se oddělují znakem `|` +Co když ale nechcete vypínat Texy pro celý blok textu, ale jen pro krátkou frázi uprostřed věty? Pro tyto případy existuje elegantní a rychlé řešení: obalte daný text do **dvojitých apostrofů** `''`: -/--code texy -| first col | second col | third col -| Adam | Eva | Franta -\-- +```texy +Pokud chcete ukázat, jak se píše tučný text, napíšete: Syntaxe je ''**tučný text**''. +``` -A výsledek je: +Výsledkem nebude tučný text, ale doslova se vypíše řetězec `**tučný text**`. -| first col | second col | third col -| Adam | Eva | Franta +Tabulky +======= -Hlavičku tabulky můžeme definovat tímto zápisem: +Pro vytvoření tabulky začněte každý řádek znakem `|` a jednotlivé buňky oddělujte také tímto znakem. Texy si už samo pohlídá zarovnání a správné HTML. -/--code texy -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Jesus | Christ | 33 -| Cecilie | Svobodova | 74 -\-- +```texy +| Jan | Novák | Praha +| Eva | Svobodová | Brno +``` -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Jesus | Christ | 33 -| Cecilie | Svobodova | 74 +Výsledek bude přehledná a správně naformátovaná tabulka. + +ID syntaxe `table` | [TableModule |configuration#tablemodule] -Sloučení sloupců +Hlavička tabulky ---------------- -všimněte si zdvojeného || +Každá správná tabulka by měla mít hlavičku, která popisuje, co se v jednotlivých sloupcích nachází. Hlavičku vytvoříte tak, že ji od zbytku tabulky oddělíte řádkem obsahujícím pomlčky `-`. -/--code texy -| Name || Age -|---------------------------- -| Jesus | Christ | 33 -\-- +```texy +| Jméno | Věk | Město +|----------|-----|------- +| Jan | 25 | Praha +| Eva | 30 | Brno +``` -| Name || Age -|---------------------------- -| Jesus | Christ | 33 +Alternativně můžete definovat záhlaví pro jednotlivé řádky (například pokud máte v prvním sloupci popisky). Toho dosáhnete přidáním hvězdičky `*` hned za úvodní `|`. +```texy +|* Jméno | Jan | Eva +|* Věk | 25 | 30 +|* Město | Praha | Brno +``` -Sloučení řádků + +Sloučení buněk -------------- -Všimněte si znaku `^` symbolizujícího směr nahoru: +Někdy je potřeba spojit několik buněk dohromady, ať už ve sloupcích nebo v řádcích. +**Sloučení sloupců:** Pro horizontální spojení buněk jednoduše vynechejte oddělovač a místo něj použijte zdvojenou svislou čáru `||`. Buňka napravo se tím sloučí s buňkou nalevo od ní. -/--code texy -| First Name | Last Name | Age +```texy +| Jméno || Věk |---------------------------- -| Bill || 50 -| ^| 52 -| Jim | Beam | 70 -\-- +| Jan | Novák | 25 +``` + +**Sloučení řádků:** Pro vertikální spojení buněk použijte v buňce, kterou chcete připojit k té nad ní, symbol stříšky `^`. Ta Texy říká: "Tuto buňku spoj s tou nad ní." + +```texy +| Měsíc | Prodeje | +|---------|---------- +| Leden | 150 ks | +| Únor | ^| +| Březen | 210 ks | +``` + +V tomto příkladu bude buňka s prodeji pro leden a únor spojená. + +Takto lze sloučit několik buňek napříč řádky a sloupci: +```texy | First Name | Last Name | Age |---------------------------- | Bill || 50 | ^| 52 | Jim | Beam | 70 +``` + + +Stylování tabulek +----------------- + +Stejně jako u jiných prvků v Texy můžete i tabulkám a jejich částem přidávat [#modifikátory] pro změnu vzhledu (např. CSS třídy, styly nebo ID). + +**Celá tabulka:** Modifikátor pro celou tabulku umístěte na samostatný řádek těsně před ni. + +```texy +.[data-table table-striped] +| Hlavička 1 | Hlavička 2 +|------------|------------ +| data | data +``` + +**Jednotlivé řádky:** Chcete-li nastylovat konkrétní řádek, přidejte modifikátor na jeho konec. + +```texy +| Jméno | Stav +|-------|-------------- +| Petr | Schváleno +| Jana | Zamítnuto | .{background: #ffdddd} +``` + +**Jednotlivé sloupce:** Pro nastylování celého sloupce vložte modifikátor na začátek první buňky daného sloupce. + +```texy +| Jméno | .> Cena | Skladem +|----------------|-----------|--------- +| Produkt A | 1 200 Kč | Ano +| Produkt B | 850 Kč | Ne +``` + +**Konkrétní buňka:** Modifikátor pro jednu buňku napište přímo do ní, obvykle na konec jejího obsahu. + +```texy +| Úkol | Status +|----------------------|------------------------------------- +| Připravit podklady | Hotovo +| Zkontrolovat data | Probíhá .{color: orange; font-weight: bold} +``` + + +Citace +====== + +Potřebujete-li ve svém textu zdůraznit myšlenku někoho jiného, ocitovat zdroj nebo jen vizuálně oddělit blok textu, stačí začít řádek znakem `>`. + +```texy +> Toto je citace. Slouží ke zvýraznění důležité myšlenky nebo úryvku z jiného zdroje. +``` + +Citace nemusí být jen jeden odstavec. Pokud chcete pokračovat dalším odstavcem v rámci stejné citace, jednoduše vložte prázdný řádek, který také začíná znakem `>`. + +```texy +> Toto je první odstavec citace. Lorem ipsum dolor sit amet. +> +> A toto je druhý odstavec, který stále patří do stejné citace. +> Tímto způsobem můžete strukturovat i delší texty. +``` + +Texy dokonce podporuje vnořené citace, což se hodí, pokud citujete někoho, kdo sám někoho cituje. Pro každou další úroveň vnoření přidejte další znak `>`. + +```texy +> Toto je vnější, hlavní citace. +> +> > A toto je už vnořená citace druhé úrovně. +> +> Zde se text vrací zpět do hlavní citace. +``` + +Uvnitř citací můžete samozřejmě používat i další formátování, jako je **tučný text** nebo *kurzíva*. + + +Horizontální čáry +================= + +Někdy je potřeba text vizuálně rozdělit. K tomu skvěle slouží horizontální čára. Na samostatný řádek napište tři nebo více pomlček `---` nebo hvězdiček `***`. + +```texy +První část textu o nějakém tématu. + +*** + +Druhá část textu, která začíná po vizuálním oddělení. +``` + +Abyste vytvořili horizontální čáru, **musí jí předcházet prázdný řádek**. Pokud byste ji napsali hned pod text, Texy by si myslelo, že chcete vytvořit podtržený nadpis. + +ID syntaxe `horizline` | [HorizLineModule |configuration#horizlinemodule] + + +Typografie +========== + +Síla Texy nespočívá jen ve formátování, ale také v automatických typografických korekcích. Texy se postará o detaily, které dělají text profesionálním a dobře čitelným, a to vše podle českých typografických pravidel. Vy se tak můžete soustředit jen na obsah. + +**Uvozovky:** Nemusíte řešit, jak na klávesnici napsat správné typografické uvozovky. Texy to udělá za vás. + +Klasické ''"strojopisné uvozovky"'' automaticky převede na správné české „uvozovky“ a vnořené ‚uvozovky‘. Typ uvozovek závisí na nastavení locale: + +```php +$texy->typographyModule->locale = 'cs'; // české +$texy->typographyModule->locale = 'en'; // anglické +``` + +**Pomlčky a spojovníky:** Inteligentně rozpozná, kdy použít krátký spojovník (v dělených slovech), a kdy delší pomlčku – například v rozsazích (10–15) nebo mezi slovy. + +```texy +10-15 → 10–15 (en dash pro rozsahy) +česko-slovenský → česko-slovenský (spojovník zůstává) +slovo -- slovo → slovo – slovo (en dash mezi slovy) +slovo --- slovo → slovo — slovo (em dash) +``` + +**Nezlomitelné mezery**: Jednou z největších výhod je automatické vkládání pevných (nezlomitelných) mezer tam, kde je to potřeba. Tím zabraňuje, aby na konci řádku zůstala osamocená jednopísmenná slova (jako `k`, `s`, `v`, `z`), což je častý typografický prohřešek. + +```texy +// Vy napíšete: +Navštívil jsem hrad v Praze. + +// Texy zajistí, aby "v" nikdy nezůstalo na konci řádku: +Navštívil jsem hrad v Praze. +``` + +Stejně tak se postará o správné mezery v telefonních číslech nebo datech, aby se nezalamovala. + +```texy ++420 776 552 046 → +420 776 552 046 (všechny mezery pevné) +``` + +**Automatické symboly:** Texy vám usnadní i psaní často používaných symbolů. + +| Napíšete | Texy vygeneruje | Popis +|----- +| `...` | … | Výpustka +| `(c)` | © | Copyright +| `(r)` | ® | Registrovaná známka +| `(tm)` | ™ | Trademark +| `10 x 5` | 10 × 5 | Znak násobení +| `+-` | ± | Plus-mínus +| `<-` `->` `<->` | ← → ↔ | Šipky + +Díky těmto automatickým úpravám bude váš text vždy vypadat profesionálně, aniž byste museli znát složité klávesové zkratky nebo HTML entity. + + +Dělení dlouhých slov +-------------------- + +Znáte to – v textu se objeví dlouhé slovo, jako například "nejneobhospodařovávatelnějšími", a na úzké obrazovce mobilního telefonu rozbije celý layout stránky. Texy naštěstí nabízí elegantní řešení: dokáže do slova vložit neviditelné "měkké rozdělovníky" (`­`). Tyto rozdělovníky prohlížeči napoví, na kterých místech (mezi slabikami) může slovo bezpečně zalomit, pokud se na konec řádku nevejde. Pokud se slovo na řádek vejde celé, rozdělovníky zůstanou skryté a nic se nestane. + +```latte +nejneobhospoda­řovávatelnějšími +``` + +Díky tomu se váš text vždy krásně přizpůsobí jakékoliv šířce obrazovky bez nechtěného horizontálního posouvání. + +Protože se tato funkce nehodí pro všechny typy webů, je ve výchozím stavu vypnutá. Aktivovat ji můžete v konfiguraci: + +```php +$texy->allowed['longwords'] = true; + +// Nastavit minimální délku slova, od které se má dělit (např. 20 znaků) +$texy->longWordsModule->wordLimit = 20; +``` + +ID syntaxe `longwords` | [LongWordsModule |configuration#long-wordsmodule] + + +Emotikony +========= + +Texy umí automaticky převádět klasické textové smajlíky na grafické emotikony. Jednoduše napište smajlíka tak, jak jste zvyklí, a Texy se postará o zbytek. + +| Napíšete | Texy vygeneruje +|----- +| `:-)` | 🙂 +| `:-(` | ☹ +| `;-)` | 😉 +| `:-D` | 😀 +| `:-*` | 😘 + +Podle konfigurace může Texy tyto zkratky převádět buď na moderní Unicode emoji (jako v tabulce výše), nebo na malé obrázky (`<img>`). + +Aby se předešlo nechtěným převodům například v technických textech, je tato funkce ve výchozím nastavení vypnutá. Pokud ji chcete používat, stačí ji jednoduše povolit: + +```php +$texy->allowed['emoticon'] = true; +``` + +Více informací o dostupných emotikonech a možnostech nastavení naleznete v [konfiguraci EmoticonModule |configuration#emoticonmodul]. + +ID syntaxe `emoticon` | [EmoticonModule |configuration#emoticonmodule] diff --git a/texy/cs/texy-vs-wysiwyg.texy b/texy/cs/texy-vs-wysiwyg.texy deleted file mode 100644 index c79d99679e..0000000000 --- a/texy/cs/texy-vs-wysiwyg.texy +++ /dev/null @@ -1,23 +0,0 @@ -Texy versus WYSIWYG editory -*************************** - - -WYSIWYG((What You See is What You Get)) editor zobrazuje dokument během editace takové podobě, v jaké bude vytištěn, zobrazen na webu atd. Příkladem takového editoru je Word. - -Tento druh editorů zažil v oblasti správy internetového obsahu skutečný boom. Kdo by také odolal jejich vizuálně atraktivnímu prostředí a myšoidnímu ovládání. Díky programátorům "šikovných komponent .[about](htmlArea, FCKeditor nebo tinyRTE)" se jejich implementace stala hračkou a dnes je nabízí každý CMS((Content Management Systém = Systém na správu obsahu)). V praxi se však ukazuje, jak jsou jejich **přednosti jen zdánlivé**. - - -WYSIWYG se pro web nehodí -------------------------- - -Web má totiž docela **jiná specifika** než tištěný dokument. Zatímco u tiskovin je prioritou vizuálním uspořádání (ze kterého si lidský mozek odvodí strukturu, tj. co je nadpis, co je text a co je popiska obrázku), u webu je základem struktura. - -S vizuálním editorem se vlastně neustále svádí boj. Nejprve bojuje tvůrce administračního rozhraní, který se jej snaží přinutit **generovat použitelný kód**. Poté s ním bojuje samotný uživatel, který se snaží s využitím plné škály dostupných barev a fontů vytvoří hrozivou stránku. Následuje snaha ořezat možnosti editoru, kterou střídá další touha uživatele využít jeho plný potenciál. - -Současné WYSIWYG editory se pro web zkrátka nehodí. Prohlašuji to s vědomím autora několika administračních rozhraní, které jej využívají. Potřeboval jsem přijít s vhodnějším nástrojem a tak vzniklo Texy - - -WYSIWYM -------- - -WYSIWYM znamená What You See Is What You Mean. diff --git a/texy/en/@home.texy b/texy/en/@home.texy index 73a74963b1..27b8c786c9 100644 --- a/texy/en/@home.texy +++ b/texy/en/@home.texy @@ -1,31 +1,120 @@ -What Is Texy ------------- +Texy! is sexy! +************** -Texy allows you to enter content using an **easy to read** [Texy syntax | syntax] which is filtered into *structurally valid* HTML. No knowledge of HTML is required. Texy is one of the most complex formatting tools. It allows adding of images, links, nested lists, tables and has full support for CSS. +.[perex] +Texy is a **powerful and secure markup processor** for PHP that converts simple text into valid HTML. Unlike other markup languages, Texy isn't just another Markdown variant – it's a **fully configurable system** that you can adapt to virtually any syntax. -Texy supports hyphenation of long words (which reflects language rules), clickable emails and URL (emails are obfuscated against spambots), national typographic single and double quotation marks, ellipses, em dashes, dimension sign, nonbreakable spaces (e.g. in phone numbers), acronyms, arrows and many others. -Texy is being developed by David Grudl since 2004. It is written in PHP. +Why Texy? +========= -Texy is licenced by BSD and GNU General Public License. Plugins for several content-management systems are available. +Security as a Priority +---------------------- -Features --------- +Texy is designed with security in mind. It automatically **protects against XSS attacks**, validates URLs, and filters dangerous HTML tags. The built-in `safeMode()` is ideal for processing user-generated content in comments or forums. -- simple, intuitive and human friendy markup -- generate clean and valid HTML code -- may be used together with syntax highlighter -- designed with proper typography in mind -- supports hyphenation +```php +Texy\Configurator::safeMode($texy); +// Texy is now safe for user-generated content +``` -Why Texy --------- +Full Configurability +-------------------- -- bulletproof - ensures the well-formedness of the resulting code -- predectable behaviour -- flexible configuration +Want to use Markdown syntax? Or need completely custom markup? **Texy can handle it.** You can: + +- Disable or enable any parts of the syntax +- Change default behavior using handlers +- Add entirely custom syntax elements +- Configure Texy to process Markdown or any other format + +```php +$texy = new Texy; +$texy->allowed['image'] = false; // disable images +$texy->allowed['phrase/strong'] = false; // disable bold text +``` + + +Advanced Typography +------------------- + +Texy provides **sophisticated typographic processing** that can be tailored to different languages. Depending on the configured locale, it automatically: + +- Inserts **non-breaking spaces** after single-letter prepositions and conjunctions +- Applies **word hyphenation** according to syllable rules +- Uses proper **typographic quotation marks** based on language conventions +- Applies proper **dash and hyphen distinction**: 10–15 (dash) vs. multi-word (hyphen) +- Adds **non-breaking spaces** in phone numbers: +420 776 552 046 + + +Valid and Well-Formed HTML +-------------------------- + +Texy always generates **valid HTML5 code**. It automatically corrects improperly nested tags, closes unclosed elements, and ensures proper document structure. The output is not only valid but also **beautifully formatted** with proper indentation. + + +What is Texy? +============= + +Texy is a **general-purpose markup text processor**. While it has its default syntax (similar to Markdown but much richer), you can completely change or extend it. + +**It's not just a parser** – Texy is a comprehensive system with modular architecture, where each module processes a specific part of the syntax (headings, links, images, tables...). Thanks to the handler system, you can intervene at any point in the processing and modify the result according to your needs. + + +Texy vs. Markdown +================= + +The basic syntax is similar, but Texy offers much more: + +|--------------------------- +| Feature | Markdown | Texy +|--------------------------- +| Bold text | `**text**` | `**text**` +| Italic | `*text*` or `_text_` | `*text*` or `//text//` +| Headings | `# Heading` | `# Heading` or underline +| Images | `![alt](url)` | `[* url *]` +| Tables | limited | full support including merging +| Modifiers | no | yes – `.{color:red}[class]` +| Typography | no | yes – quotes, dashes, spaces +| Word hyphenation | no | yes – by syllables +| Configurability | limited | complete – custom syntax +| Security | depends on impl. | built-in (safeMode) + +**Example of differences:** + +```texy +Markdown: +![Image](image.jpg) + +Texy: +[* image.jpg 300x200 .(Image caption)[photo] <] +``` + +Texy allows you to define dimensions, classes, alignment, and much more directly in the syntax. + + +When to Use Texy? +================= + +Texy is ideal for: + +**CMS systems** Need to safely process content from editors? Texy offers granular control over what users can use. + +**Blogs and documentation** Rich syntax for tables, images with captions, typography, and code with syntax highlighting. + +**Comments and discussion forums** SafeMode ensures users cannot insert dangerous code while still having text formatting available. + +**Projects with custom requirements** Need to embed YouTube videos? Special syntax for your macros? Custom markup language? With Texy, you can create it easily. + + +History +======= + +Texy was created by David Grudl **20 years ago** in 2004 as one of the first markup processors for PHP. Originally developed for **PHP 4**, it has undergone many updates throughout its long history and today fully leverages all the capabilities of **PHP 8**. + +Over two decades of active development mean a **proven and stable** library trusted by hundreds of projects. Texy is now a **mature solution** with extensive history, yet still actively maintained and modern. {{maintitle: Texy – human friendy markup for PHP}} diff --git a/texy/en/@menu.texy b/texy/en/@menu.texy index 080eb567c1..66743bb1f1 100644 --- a/texy/en/@menu.texy +++ b/texy/en/@menu.texy @@ -1,6 +1,6 @@ - [home | @home] -- [syntax briefly | syntax] -- [syntax in detail | syntax-full] -- [fiddle | https://fiddle.nette.org/texy/] -- [API | https://api.nette.org/texy/] -- [GitHub | https://github.com/dg/texy] +- [syntax | syntax] +- [for developers | develop] +- "Playground .[link-external]":https://fiddle.nette.org/texy/ +- "API .[link-external]":https://api.nette.org/texy/ +- "GitHub .[link-external]":https://github.com/dg/texy diff --git a/texy/en/@try.texy b/texy/en/@try.texy deleted file mode 100644 index 77c2fe7dac..0000000000 --- a/texy/en/@try.texy +++ /dev/null @@ -1,15 +0,0 @@ -Welcome! --------- - -You can use Texy if you like: -- **bold** font or *italic* -- and this is how to "link":https://texy.info -- see "syntax":[syntax] for more information - - -But you can also stay with HTML: -- like this <b>HTML</b> -- Or even <b class=xx>totally <i>stupid</b>, Texy will solve it - - -[syntax]: /en/syntax diff --git a/texy/en/architecture.texy b/texy/en/architecture.texy new file mode 100644 index 0000000000..5e8bf97c11 --- /dev/null +++ b/texy/en/architecture.texy @@ -0,0 +1,425 @@ +Architecture and Principles +########################### + +.[perex] +Texy is a tool for converting text written in its own markup language into HTML. Unlike simple converters that process text linearly through a series of replacements, Texy uses a sophisticated system based on parsing, a modular architecture, and the gradual construction of a DOM tree. + +The basic processing flow consists of four main phases: + +1. Text preprocessing - normalization, adjustment of spaces and tabs, calling notification handlers for preparation +2. Parsing - recognizing syntaxes using regular expressions and gradually building the DOM tree +3. Post-processing - typographic adjustments, handling long words, well-forming HTML +4. Final assembly - converting the DOM tree into an HTML string + +The key difference from naive approaches is the separation of the syntax recognition phase from the processing phase. The parser first identifies where each syntactic construct is located in the text, and only then passes the found parts to individual modules for processing. This allows for nesting syntaxes and their gradual expansion. + +*Note: all classes are in the `Texy` namespace, so if the document mentions a class like `HtmlElement`, its full name is `Texy\HtmlElement`. Modules are in the `Texy\Modules` namespace* + + +Key Components +============== + +The Texy architecture consists of several main components, each with a clearly defined responsibility: + +The Texy class acts as the central orchestrator of the entire system. It contains references to all modules, manages registered syntaxes and handlers, maintains the processing state, and coordinates the individual conversion phases. It is the only place where the individual components are interconnected. + +**[Modules|#Moduly]** represent functional units responsible for specific areas of the markup language. Each module, upon its construction, registers the syntaxes it recognizes and the element handlers that process them. For example, PhraseModule handles inline formatting like bold or italic text, while TableModule processes tables. Modules are designed as separate, reusable units with their own configuration accessible through public properties. + +**[Parsers|#Parsery]** exist in two variants depending on the type of content being processed. BlockParser processes block structures like paragraphs, headings, lists, or tables. It goes through the text line by line, looking for the beginnings of block constructs and passing them to *syntax handlers*. LineParser handles inline syntaxes within lines - links, images, text formatting. Unlike BlockParser, it allows for nesting syntaxes and their gradual expansion. + + +Basic Terminology +================= + +To correctly understand how Texy works, it is necessary to distinguish between several key concepts that frequently appear in the documentation. + +**Syntax** refers to a named syntactic construct of the markup language. Each syntax has a unique name, for example, `phrase/strong` for bold text or `image` for images. The syntax name is used to enable or disable it in the `Texy::$allowed` array and is passed as a parameter to syntax handlers to distinguish which specific syntax was found. + +**Pattern** is a regular expression that defines what the syntax looks like in the text. The pattern is an implementation detail of the syntax - the author of the syntax must write a regex that recognizes it, but from the perspective of a Texy user, the syntax name and its meaning are more important. One module typically registers multiple syntaxes with different patterns. + +**Syntax handler** is a function called by the parser when it finds an occurrence of a syntax in the text. It receives the found text and returns an `HtmlElement` or a string, which is inserted in the original place. The syntax handler is where the decision is made about what to do with the found syntax - it typically invokes an element handler for the actual processing. + +**Element** is an item for which an HTML representation is generated. For example, `image` is an element for images, `linkURL` for links, `phrase` for inline formatting. Each element has its default element handler that takes care of standard processing. + +**Element handler** is a function registered for a certain type of element and called through the HandlerInvocation system. A characteristic feature is the use of the `proceed()` method, which allows delegating processing to the next handler in the chain or to the module's default handler. Element handlers are used to modify or replace the default behavior. + +**Notification handler** is a function called to notify about a certain event. Unlike element handlers, it does not return any value and cannot influence the processing result. It is used for data preparation, logging, or modifying the already created DOM tree. + +The difference between the various handlers is key to understanding the architecture. A syntax handler is tightly coupled with the parser and a specific pattern - it addresses the question of *what to do when the parser finds this pattern*. Element handlers are at a higher level of abstraction - they address the question of *how to process this type of element*, regardless of which specific syntax created it. + + +Overall Processing Flow +======================= + +When Texy receives input text, it goes through the following processing procedure. + +During preprocessing, the text is normalized. Line endings are unified to the Unix format, spaces are standardized, and tabs are optionally replaced with spaces. Subsequently, *notification handlers* registered for the `beforeParse` event are invoked. These handlers can perform data preparation, such as loading reference definitions or adjusting the configuration based on the text content. + +The parsing itself begins with the creation of a root `HtmlElement`, which represents the document. Texy then decides whether to process the text as a single line or as a complete document with block structures. In the case of block processing, a BlockParser is created, which sequentially goes through the text and looks for individual block constructs. + +LineParser works differently than BlockParser. It does not traverse the text linearly but progressively searches for the nearest occurrence of any registered syntax. When it finds one, it calls the corresponding syntax handler, which creates the appropriate HTML element. This element is inserted back into the text using special masking, and the parser continues. This allows it to find and process syntaxes nested inside already processed constructs. + +After parsing is complete, a full DOM tree representing the document's structure is created. Texy invokes notification handlers for the `afterParse` event, which can perform final modifications to the tree, such as adding identifiers to headings or building a table of contents. + +Post-processing occurs during the conversion of the DOM tree to an HTML string. Each element is recursively converted to HTML code, during which typographic adjustments like replacing quotes, dashes, or inserting non-breaking spaces are applied. Furthermore, HTML well-forming is performed - automatic closing of tags, correction of improperly nested elements, and formatting and indentation of the code. + +The final phase is decoding all masked parts back to HTML tags, removing helper markers, and assembling the resulting HTML string. + + +Syntax System +************* + +In Texy terminology, a syntax represents a named syntactic construct of the markup language. It is an abstract concept connecting several elements: a unique name, a regular expression for recognition, and a method of processing. The syntax name serves as an identifier throughout the system - it is used in the `Texy::$allowed` array for enabling or disabling, passed to handlers to distinguish the type of construct, and appears in documentation and configuration files. + +Syntax naming conventions follow two main patterns. Simpler syntaxes have a single-word name corresponding to their purpose, for example, `image`, `table`, or `script`. More complex areas use hierarchical naming with a slash, for example, `phrase/strong`, `phrase/em`, or `link/reference`. The slash serves to logically group related syntaxes and facilitates bulk operations with them. + + +Line Syntax +=========== + +Line syntaxes are used to recognize inline elements within lines of text. Typically, this includes formatting like bold or italic text, links, images, or inline code. A characteristic of line syntaxes is that they can be nested within each other, and the parser expands them sequentially. + +A line syntax is registered by calling `Texy::registerLinePattern()` with several parameters. The first is the syntax handler, i.e., the callback called upon finding a match. The second parameter is the regular expression defining the syntax's appearance in the text. The third parameter is the syntax name used throughout the system. An optional fourth parameter is another regex to test if it's even worth searching for the pattern - it's used for optimization to avoid running a complex pattern on text that definitely cannot match. + +The pattern as a regular expression must adhere to certain rules. It must not be anchored to the beginning of the text because it is searched for anywhere in the line. It should be as specific as possible to avoid false matches. + +Inline syntaxes within lines of text are processed by the [LineParser|#LineParser]. When it finds a match, it calls the appropriate syntax handler. This handler receives three parameters. The first is the LineParser instance, which provides access to the Texy object and other contextual information. The second parameter is an array with the results of the regex match, including sub-expressions. The third parameter is the syntax name, which is useful when the same callback handles multiple syntaxes. The handler must return either an `HtmlElement`, a string, or null if it refuses to process. + + +Block Syntax +============ + +Block syntaxes recognize multi-line block constructs such as headings, lists, tables, quotes, or special blocks. Unlike line syntaxes, block syntaxes never overlap - each line of text belongs to at most one block construct. + +Registering a block syntax uses `Texy::registerBlockPattern()` with three parameters: a syntax handler, a regular expression, and the syntax name. The pattern as a regular expression must adhere to certain rules. It must match from the beginning of the line and often contains an anchor for the end of the line. BlockParser automatically adds the `Am` modifiers, so the pattern should not contain them. + +Block syntaxes within a document are processed by the [BlockParser|#BlockParser]. When it finds a match, it calls the appropriate syntax handler. This handler receives similar parameters as with line syntaxes - a BlockParser instance, an array with the match, and the syntax name. It returns an `HtmlElement` representing the entire processed block, or null if it refuses processing. + + +Enabling and Disabling Syntax +============================= + +The `Texy::$allowed` array provides fine-grained control over which syntaxes are active in Texy. It is a simple yet powerful mechanism for configuring behavior without needing to change the modules' code. When you disable the `phrase/strong` syntax with this setting, the parser stops looking for the bold text construct: + +```php +$texy->allowed['phrase/strong'] = false; +``` + +The check is performed once at the beginning of parsing, so dynamically changing `$allowed` during processing has no effect. + +When constructing modules, a default value is set in `$allowed` for most syntaxes. Some syntaxes are enabled by default because they form the basis of the markup language. Others are disabled because they are advanced or potentially dangerous. For example, emoticons are disabled because not every document needs them, while basic formatting is enabled. + +Safe mode is a situation where you are processing untrusted input, such as user comments. You want to allow basic formatting but disable images, scripts, or HTML tags. `Texy\Configurator::safeMode()` sets `$allowed` for a safe combination of syntaxes. It typically disables image, figure, script, and HTML tags, but leaves links and formatting enabled. + + +Parsers +******* + + +Syntax Handler +============== + +As we mentioned in the previous section, LineParser or BlockParser goes through the text and looks for all registered patterns. When it finds a match, it calls the appropriate syntax handler and passes it information about the find - particularly an array with the results of the regex match. + +The syntax handler analyzes the found text and prepares the data for processing. It can extract parts of the text from regex groups, create helper objects like `Link` or `Image`, and parse modifiers. It also decides which element handler to invoke. It calls `Texy::invokeAroundHandlers()` with the element name and the prepared parameters. This begins their execution. The returned result is passed back to the syntax handler, which returns it to the parser. + + +Element Handler +=============== + +Element handlers implement the chain of responsibility pattern, which allows the final behavior to be composed from multiple layers. + +An element handler is registered by calling `Texy::addHandler()` with two parameters - the element name and the handler function. A single element name can have multiple handlers registered, which are then executed in order from the last registered to the first. + +The element name identifies the type of processing, for example, `phrase` for formatting, `image` for images, or `link` for links (note: this is different from syntax names). Sometimes, composite names like `linkReference` or `linkEmail` are used to distinguish different kinds of links. The names are more general than syntax names - while the `phrase/strong` syntax is a specific construct, the `phrase` element covers all kinds of inline formatting. + +Invoking an element handler uses the `Texy::invokeAroundHandlers()` method. This method receives the element name, the parser instance, and an array of parameters. It creates a HandlerInvocation object that encapsulates the entire chain of registered handlers. The first handler in the chain gets control and decides whether to call `HandlerInvocation::proceed()` to continue to the next handler or to return its own result. + +The HandlerInvocation object is key to understanding how the chaining works. It contains a stack of all handlers for the given element and the current position in this stack. When a handler calls `proceed()`, HandlerInvocation moves the position back one place in the stack and calls the next handler. If a handler calls `proceed()` with modified parameters, these new parameters are passed to all subsequent handlers. If a handler does not call `proceed()` at all, the chain is interrupted, and its return value becomes the result of the entire processing. + +The order of handler execution is from the last registered to the first. This means that a user-defined handler registered additionally gets control first and can decide whether to call the module's default handler at all. This order allows users to override the default behavior without needing to change the module's code. + +A typical use of an element handler looks like this. The handler checks the input parameters and decides if it wants to intervene in the processing. If so, it modifies the data, calls `proceed()` with the new parameters, and possibly modifies the returned result further. If the handler wants to completely replace the default processing, it creates its own result and returns it without calling `proceed()`. + + +Notification Handler +==================== + +Notification handlers represent a simpler, one-way communication mechanism. Unlike element handlers, they are not used for data transformation but for performing side actions. + +Registering a notification handler uses the same `Texy::addHandler()` method as element handlers. The difference is in how the handler is used - a notification handler returns no value and does not have access to HandlerInvocation. The first parameter is the event name. Descriptive names like `beforeParse` and `afterParse` are used for global events around parsing, or more specific ones like `afterTable`, `afterList`, `afterBlockquote` for events after a specific structure is created. The before/after prefix clearly indicates the timing of the event. + +Invoking notification handlers uses the `Texy::invokeHandlers()` method. This method simply calls all registered handlers in order and ignores their return values. Notification handlers receive the parameters passed during invocation but cannot change them for other handlers in the chain. + +Typical uses for notification handlers include several scenarios. A handler for the `beforeParse` event can load reference definitions from the text before parsing begins. A handler for `afterParse` can traverse the created DOM tree and add missing attributes or build a table of contents. Handlers like `afterTable` or `afterList` allow modules to perform final adjustments to the created structures. + +An important difference from element handlers is that notification handlers cannot prevent further processing. All registered handlers are always executed; none can break the chain. This is intended behavior - notification handlers are about side effects, not flow control. + + +LineParser +========== + +LineParser processes inline syntaxes within lines of text in a sequential manner that allows for nesting and complex interactions between syntaxes. + +The basic principle lies in finding the first occurrence of any syntax. In each iteration, it goes through all syntaxes and determines which one matches closest to the current position in the text. This syntax *wins* and is processed. If multiple syntaxes match at the same position, the one that was registered earlier wins - this is a priority based on registration order. + +When the parser finds the nearest match, it calls the corresponding syntax handler. This handler returns a result, which can be an `HtmlElement` or a string. This result then overwrites the found match in the text. + +Then, it searches again from the current position. This system ensures that the parser always sees the current state of the text. When we replace a match with new text that may contain other syntaxes, these syntaxes will be found in the next iteration. + +The `$again` property on the LineParser object is used for fine-grained control over whether the just-matched syntax should be searched for again at the same position after processing the current match. The default value is false, which says: *It no longer makes sense to look for this same syntax at this position. Move on.* + +The traversal ends when the parser reaches the end of the text or when no syntax has any more matches. The result is text where all recognizable syntaxes have been processed and replaced with their results, ready for final conversion. + + +Nesting +------- + +The ability to process nested syntaxes is one of the key features of LineParser and presents a fundamental challenge - how to prevent already processed HTML tags from being mistakenly interpreted as another syntax to be processed. + +When the parser processes text containing nested syntaxes, it first finds the outer construct. For example, in the text `"link **bold** text":URL`, the parser first finds the syntax for a link with quotes. The pattern for this syntax matches the entire string from the first quote to the colon and URL. The syntax handler creates an `HtmlElement` for the `<a>` tag, and the content `link **bold** text` is added as a child of the element. This string is inserted back into the text, and the parser continues searching for other syntaxes (`**bold**`, which represents bold text). + +But now it has a problem - there are also HTML tags in the text, which could match as the beginning of another syntax. The parser would start processing the already finished HTML tags as if they were part of the original text. + +We don't want the parser to see HTML tags. We need some way to distinguish already processed parts from parts waiting to be processed. The `Texy::protect()` method solves these problems in an elegant way - it replaces HTML tags with a unique placeholder composed of control characters - special bytes outside of printable ASCII. + +So, when an `HtmlElement` is converted to a string (using `toString()`), the result doesn't look like `<a href="...">link **bold** text</a>`, but for example, like `\x17\x18\x19\x17link **bold** text\x17\x18\x1A\x17`. + +Thus, during parsing, there are never actual HTML tags present in the text. Instead, there are only placeholders. But the inner text remains, and the parser sees it normally and can search for other syntaxes within it. This allows for gradual nesting - the outer syntax is masked, but its content is still accessible for inner syntaxes. + +At the end of processing, the `Texy::unProtect()` method goes through the resulting HTML string and replaces all placeholders with their actual values. Only at this moment do the actual HTML tags get into the output. + + +Masking Levels +-------------- + +Different types of content use different control characters for their placeholders, which allows syntaxes to selectively decide what they can contain. + +- `Patterns::CONTENT_MARKUP` denotes regular HTML markup like tags for formatting or links. It is the most common type and is used by most inline elements. The placeholder begins and ends with `\x17`. +- `Patterns::CONTENT_REPLACED` denotes content that has been replaced by something else, typically images or other replaced elements. It uses `\x16` as a marker. +- `Patterns::CONTENT_TEXTUAL` denotes text that has been escaped or otherwise treated to prevent processing. It is used for constructs like code or notexy, where we want to display the original text including markup symbols, not their interpretation. +- `Patterns::CONTENT_BLOCK` denotes block elements. It is the lowest level in the hierarchy. It uses `\x14` as a marker. + +The hierarchy of these types is not just a convention but has a practical consequence. The constant Patterns::MARK is defined as `\x14-\x1F`, i.e., a range covering all these types plus a reserve. Syntaxes use this constant in their patterns to exclude masked parts. + +Different syntaxes may have different requirements for what placeholders they can contain. A pattern that wants to see only plain text without any masked parts will use the exclusion `[^\x14-\x1F]`. This will reject all placeholders of all types. An example is the pattern for images - an image URL should not contain any HTML tags or blocks. + +A pattern that accepts lower levels but rejects higher ones will use a narrower range. For example, `[^\x17-\x1F]` will only reject `CONTENT_MARKUP` and above, but will accept `CONTENT_BLOCK`, `CONTENT_TEXTUAL`, and `CONTENT_REPLACED`. This is useful if we want to allow blocks but not inline markup. A practical example is TypographyModule, which performs typographic adjustments like replacing quotes or inserting non-breaking spaces. These adjustments should be applied to regular text, but not inside code blocks or preformatted text. + + +Syntax Collisions +----------------- + +A collision occurs when multiple syntaxes can match at the same position, and the system must choose one of them. + +A typical example is different lengths of the same symbol. The `phrase/strong+em` syntax uses three asterisks for a combination of bold and italics. The `phrase/strong` syntax uses two asterisks for bold text alone. The `phrase/em-alt` syntax uses one asterisk for italics. When the parser finds text starting with three asterisks, all three syntaxes can technically match. + +PhraseModule resolves this collision by registering syntaxes in order from longest to shortest. First, it registers `phrase/strong+em` with a pattern for three asterisks. Then `phrase/strong` with a pattern for two asterisks. Finally, `phrase/em-alt` with a pattern for one asterisk. Thanks to this order, when three asterisks are found, `phrase/strong+em` is processed first, and the shorter syntaxes don't get a chance. + +Another example is links in different formats. The `phrase/wikilink` syntax uses a pattern for `[text|url]`. The `link/reference` syntax uses a pattern for `[ref]`. Both start with an opening square bracket. If the text contains `[text|url]`, both patterns can technically start to match. + +The solution, again, is the specificity of the patterns. The pattern for `phrase/wikilink` is more specific - it requires a vertical bar inside the brackets. If the text contains a vertical bar, `phrase/wikilink` will match. If not, the pattern will fail, and `link/reference` gets a chance. The order of registration also plays a role here - `phrase/wikilink` should be registered before `link/reference`. + + +BlockParser +=========== + +BlockParser uses a fundamentally different approach to processing that reflects the nature of block constructs. The basic difference is the absence of intertwining. While LineParser allows syntaxes to be nested within each other and gradually expanded, BlockParser works with the assumption that each block is a separate unit. A single line or a group of lines belongs to at most one block. Blocks do not overlap, cross, or nest at the BlockParser level. + +BlockParser starts by finding all blocks, or rather their beginnings. The parser goes through all registered block syntaxes and finds all their occurrences. If multiple syntaxes match at the same position, the registration order is used - the earlier registered syntax takes precedence. + + +API for Syntax Handler +---------------------- + +BlockParser provides syntax handlers with an API for working with multi-line structures. + +The `BlockParser::moveBackward()` method is used to return to previous lines. It accepts the number of lines to go back. The parser moves its internal position towards the beginning of the text until it passes the specified number of line endings. This allows the callback to start reading from the beginning of the structure, even if the pattern matched in the middle or at the end. + +The `BlockParser::next()` method is used to read the next line matching a certain pattern. It accepts a regex pattern (it automatically adds the `Am` modifiers) and a reference to a variable for the match result. If the next line in the text matches the provided pattern, the method fills the result, moves the internal position past this line, and returns true. If the next line does not match, the method returns false, and the position does not change. + + +Modules +******* + +Modules are the basic organizational unit in the Texy architecture. Each module encapsulates the complete functionality for a specific area of the markup language. + +The primary responsibility of a module is to register syntaxes. In its constructor, the module calls `Texy::registerLinePattern()` or `registerBlockPattern()` for all the syntaxes it wants to process. This tells the parser: *When you find these patterns, call me.* The module thus defines which constructs in the text it recognizes. + +The second responsibility is the implementation of element handlers. The module registers handlers for the elements that its syntaxes invoke. These handlers contain the logic for converting the found constructs into HTML elements. The element handler decides what element to create, what attributes to set, and how to process the content. + +The third responsibility is to provide configuration. Modules have public properties that allow Texy users to modify the module's behavior without needing to change its code. For example, ImageModule has properties for setting the root path to images or the default alt text. + +The fourth responsibility is managing module-specific state. For example, HeadingModule keeps track of all found headings in the TOC array for building a table of contents. LinkModule manages a dictionary of references for links. This state is private to the module, and other parts of the system do not access it directly. + +Modules are designed as independent units. Each module can function on its own and should not depend on the implementation details of other modules. Communication between modules occurs through shared objects like `Link` or `Image`, not through direct method calls. + + +Structure of a Typical Module +============================= + +Most modules in Texy follow a similar structure that reflects their role in the system. + +The module inherits from the base class Module, which provides access to the Texy object via the protected property `$texy`. The module's constructor accepts a Texy instance and stores it. This allows the module to access the configuration and call methods on the Texy object. + +All initialization takes place in the constructor. The module sets the default values of its configuration properties, and possibly sets default values in the `Texy::$allowed` array for its syntaxes. Then it registers its syntaxes by calling `registerLinePattern()` or `registerBlockPattern()`. Each registration associates a pattern, a syntax handler, and a syntax name. Finally, the module registers its element handlers by calling `addHandler()`. + +Syntax handlers are methods of the module that the parser calls when it finds a syntax. These methods typically extract parts from the regex match, create helper objects, and invoke element handlers. The syntax handler decides which element handler to invoke and what parameters to pass. + +Element handlers are methods that implement the actual processing. They receive a HandlerInvocation object as the first parameter, followed by parameters specific to the given element. The element handler creates an `HtmlElement`, applies modifiers, processes the content, and returns the result. This is where the final form of the HTML is decided. + +Public properties serve as the interface for configuration. A Texy user can set these properties to customize the module's behavior. The properties are typically primitive types or arrays, not complex objects, to keep configuration simple. + + +Overview of Key Modules +======================= + +The standard distribution of Texy includes several modules covering various aspects of the markup language. + +- **PhraseModule** processes inline text formatting. It registers syntaxes for bold text, italics, underline, superscript, subscript, code, and more. All these syntaxes invoke a common handler for the `phrase` element, and the handler distinguishes which tag to create based on the syntax name. The module allows configuring which tags are used for each type of formatting. + +- **LinkModule** manages links in the document. It registers syntaxes for various link formats - explicit URLs, email addresses, references to defined links. It provides factory methods for creating `Link` objects and manages a dictionary of references. The module allows configuring the root for relative links, automatic `rel="nofollow"` for external links, and shortening of long URLs. + +- **ImageModule** processes images in a similar way to how LinkModule handles links. It registers syntax for inline images and manages a dictionary of references to defined images. It provides factory methods for creating `Image` objects and automatic detection of image dimensions. Configurable options include paths to images, default alt text, and CSS classes for alignment. + +- **HeadingModule** recognizes headings in various formats - underlined with dashes or equal signs, surrounded by hash marks. It collects all headings into a TOC array for a possible table of contents. It allows configuring the generation of IDs, the top level of headings, and the level balancing mode. + +- **ListModule** processes lists - unordered, ordered, and definition lists. It recognizes different types of bullets and automatically detects nesting based on indentation. It allows configuring which characters serve as bullets and what HTML lists to generate. + +- **TableModule** is one of the most complex modules. It recognizes tables with headers, bodies, captions, and supports colspan and rowspan. It processes modifiers for both rows and cells. + +- **BlockModule** processes special blocks delimited by `/--` and `\--`. It supports various block types - code for code, html for direct HTML, div for a generic container. It allows users to define custom handlers for their own block types. + +- **TypographyModule** performs post-processing for typographic adjustments. It replaces three dots with an ellipsis, double dashes with an en-dash, straight quotes with typographic ones, and inserts non-breaking spaces. It operates at the level of the final string between block elements. + +- **HtmlOutputModule** formats the final HTML output. It ensures well-formed HTML by automatically closing tags, correcting incorrect nesting, indenting the code, and wrapping long lines. It allows configuring the indentation level and line width. + + +Interaction Between Modules +=========================== + +Although modules are designed to be independent, in some cases they need to cooperate. + +Shared objects are the main communication mechanism. A `Link` object created by LinkModule can be passed to ImageModule to create an image link. An `Image` object created by ImageModule can be passed to FigureModule to create an image with a caption. These objects encapsulate all necessary information and provide a common interface. + +The reference system allows separating definition from use. LinkModule provides `addReference()` and `getReference()` methods for managing a dictionary of named links. A user can define a reference in one part of the document and use it in another. ImageModule has an analogous system for image references. Modules using references call factory methods that themselves check whether it is a reference or a direct value. + +Element handlers can call other element handlers. When PhraseModule processes a `phrase/span` with a link, it creates a `Link` object and calls the LinkModule's element handler to create the link. This delegates the responsibility for creating and configuring the link to the specialized module. + +Relationships between modules are typically one-sided. PhraseModule knows about LinkModule and ImageModule because it creates links and images. But LinkModule and ImageModule do not know about PhraseModule. This keeps dependencies simple and allows for easy replacement or extension of modules. + + +DOM Representation +****************** + +`HtmlElement` represents a single node in the DOM tree and provides an interface for its manipulation and processing. + +The basic structure of an element includes a tag name, an associative array of attributes, and an array of children. The children can be other `HtmlElement` instances or simply text strings. This combination allows for representing any HTML structure. + +The element name is set and retrieved via the `setName()` and `getName()` methods. A special value of null as the name means a transparent element, which has no tags, only its content. + +Attributes are publicly accessible via the `$attrs` property as an associative array. Values can be strings, numbers, booleans, or arrays. A boolean `true` means an attribute without a value (like `checked`), while `false` or `null` means the attribute will not be rendered at all. If the value is an array, the different elements are joined according to the attribute type - for `class` with spaces, for `style` with semicolons. The `setAttribute()` method sets the value of an attribute. The `getAttribute()` method returns the value of an attribute or null. + +Children are managed through several methods. The `add()` method adds a child to the end. The `insert()` method inserts a child at a specified position, optionally replacing an existing child. The `create()` method creates a new `HtmlElement` as a child and returns it for further manipulation. The `removeChildren()` method removes all children. + +The element implements the ArrayAccess interface, so children can be worked with like an array. The notation `$el[0]` returns the first child, `$el[0] = $child` sets the first child. This approach is convenient for quick manipulation of specific children. + +The `toString()` method recursively traverses the element and its children and builds a string representation. HTML tags are immediately masked using `Texy::protect()`, so a placeholder is inserted into the result instead of actual HTML characters. + +The `toHtml()` and `toText()` methods return the unmasked result including post-processing. + + +Parsing Content +=============== + +`HtmlElement` can recursively parse its content, allowing for the gradual building of the DOM tree. + +The `parseLine()` method is used to parse inline syntaxes in a string. It creates a new instance of LineParser with the current element as the container. It calls `parse()` on the parser with the provided text. LineParser sequentially finds and processes all inline syntaxes, and the resulting elements or strings are added as children of the current element. The method returns the used LineParser for possible further use. + +The `parseBlock()` method parses text as block content. It creates a BlockParser and calls `parse()` on it. BlockParser finds all block constructs in the text, processes them, and adds them as children of the element. Text between blocks is processed as paragraphs, which internally use LineParser. The method accepts a boolean parameter indicating whether the text comes from an indented block, which affects the processing of paragraphs. + +These parsing methods allow for recursive processing. A syntax handler can create an element, set its basic properties, and then call `parseLine()` or `parseBlock()` to process the content. The result is that the element's content goes through the same parsing process as the main document, including syntax recognition and handler invocation. + + +Validation +========== + +`HtmlElement` provides mechanisms for validating attributes and content according to the HTML DTD (Document Type Definition). + +The DTD is a static array defining for each HTML tag which attributes are allowed and what content it can contain. Texy loads the DTD from a file upon initialization and stores it in a static array. The DTD structure maps a tag name to a pair - an array of allowed attributes and an array of allowed content. + +The `validateAttrs()` method checks the element's attributes against the DTD. For a given tag, it gets the list of allowed attributes. It goes through all the element's attributes and removes those that are not on the list. Special cases are attributes starting with `data-` or `aria-`, which are allowed if a placeholder entry `data-*` or `aria-*` is in the DTD. + +This validation is typically called when applying modifiers with the `decorate()` method. It ensures that even if a user specifies a modifier with an invalid attribute for a given tag, the attribute does not get into the final HTML. This is important for security and HTML correctness. + +The `validateChild()` method checks whether a given child can be the content of the element. It accepts a child (`HtmlElement` or a tag name) and the DTD. If the element is defined in the DTD, the method checks if the child is in the list of allowed content. If so, it returns true. If not, it returns false. + +This validation can be used when dynamically building a DOM tree to ensure a correct structure. For example, a paragraph element must not contain block elements, so `validateChild()` would refuse to add a `div` into a `p`. In practice, Texy uses this validation to a limited extent, as the structure generated by the modules is typically correct by design. + +The combination of `validateAttrs()` and `validateChild()` provides a mechanism for ensuring valid HTML, even if the input contains untrusted data or poorly formed constructs. Texy can be configured for strict validation or can disable validation for maximum flexibility. + + +Modifiers +********* + +Modifiers provide a way to add additional attributes, classes, styles, and alignment to elements without having to write direct HTML. + +The basic format of a modifier is a dot followed by a combination of different parts in round, square, and curly brackets: `.(title)[class1 class2 #id]{style:value}<align>^valign`. The entire modifier is written before or at the end of the construct to which it applies. For example, `"**text** .(Important)[highlight]{color:red}"` creates bold text with the class `highlight`, red color, and a title attribute "Important". + +Round brackets contain the title attribute or alt text. The text inside is used as the value of the title attribute on the resulting element. If the element is an image, it can be used as alt text. Inside the round brackets, it is possible to escape a bracket with a backslash. + +Square brackets contain CSS classes and optionally an ID. Classes are written as words separated by spaces. An ID is written with a hash prefix. For example, `[main-content selected #article-5]` sets two classes and one ID. If an ID is specified multiple times, the last one is used. + +Curly brackets contain CSS styles or HTML attributes. Styles are written in the standard CSS format `property:value`. Multiple styles are separated by semicolons. Some properties are recognized as HTML attributes - for example, `{href:url}` is converted to an `href` attribute, not a CSS style. This allows setting attributes that cannot be expressed otherwise. + +Alignment is specified using special characters. `<` means left, `>` right, `=` for justify, `<>` for center. Vertical alignment uses `^` for top, `-` for middle, and `_` for bottom. These shortcuts are converted to either CSS classes or inline styles depending on the configuration. + +The parts of the modifier can be in any order, and some can be omitted. A modifier containing only classes `.[highlight]`, only a title `.(Note)`, or only a style `.{color:blue}` is valid. The parser recognizes the individual parts by their delimiting characters. + + +Modifier Class +============== + +The `Modifier` class is used to parse and store information from a modifier. + +An instance of `Modifier` is typically created by a syntax handler, which passes the modifier text extracted from a regex match to the constructor. The constructor calls the `setProperties()` method, which parses the text and populates the object's properties. + +Public properties contain the individual parts of the modifier. The `$id` property contains the element's ID as a string or null. The `$classes` property is an associative array where keys are class names and values are true. The `$styles` property is an associative array mapping CSS properties to values. The `$attrs` property is an associative array with HTML attributes that are not styles or classes. + +Two special properties, `$hAlign` and `$vAlign`, contain the horizontal and vertical alignment as strings `left`, `right`, `center`, `justify` or `top`, `middle`, `bottom`. These values are later converted to CSS classes or styles according to the Texy configuration. + +The `$title` property contains the text from the round brackets, which is used as the title attribute or alt text for images. The text is automatically unescaped from HTML entities and stripped of escaped brackets. + + +Application to Elements +======================= + +A `Modifier` object is applied to an `HtmlElement` using the `Modifier::decorate()` method. + +The `decorate()` method accepts a Texy instance and an `HtmlElement` as parameters. It sequentially applies the individual parts of the modifier to the element, taking into account the Texy configuration, which may prohibit or restrict some parts. + +The application of attributes checks which attributes are allowed for the given tag according to the `Texy::$allowedTags` configuration. If all attributes are allowed, all attributes from the `Modifier` are copied to the element. If only a list of specific attributes is allowed, only those that are on the list are copied. + +The title attribute is always applied if it is set, but the text undergoes typographic post-processing to replace quotes and other adjustments. + +The application of classes and ID checks the `Texy::$allowedClasses` configuration. If all classes are allowed, all classes from the `Modifier` are added to the element, and the ID is set. If only a list of specific classes is allowed, only those that are on the list are added. The ID is added only if a string starting with a hash is on the allowed list. + +The application of styles proceeds similarly, with a check of `Texy::$allowedStyles`. Allowed CSS properties are added to the element's style attribute. If the element already had some styles, the modifier's styles are added or overwrite existing ones. + +Alignment is applied either as a CSS class or an inline style. If a mapping is configured in Texy's `Texy::$alignClasses` for the given alignment type, the corresponding CSS class is added. If not, an inline style with the `text-align` or `vertical-align` property is added. + +The result is that the element has all the attributes, classes, styles, and other properties from the modifier, but only those that are allowed by the current Texy configuration. This ensures safety when processing untrusted input. + + +Propagation of Modifiers +======================== + +Modifiers pass through the system in several phases, maintaining flexibility and allowing for modifications at different levels. + +The syntax handler extracts the modifier text from the regex match and creates a new `Modifier` instance, populating its properties. + +The `Modifier` object is passed as a parameter to element handlers. The handler receives the already parsed object, not the raw text. This allows the handler to easily access the individual parts of the modifier - classes, styles, alignment. The handler can modify the modifier before application, for example, by adding more classes or changing styles. + +The element handler creates an `HtmlElement` and passes it to the `Modifier::decorate()` method. At this point, the modifier is applied to the element. The `decorate()` method checks the Texy configurations and ensures that only allowed parts are applied. + +In some cases, a module combines multiple modifiers. For example, TableModule parses modifiers at the table, row, and cell levels. A cell's modifier is actually a clone of the column's modifier, to which additional modifications from the specific cell's modifier are then applied. This allows for default styles for an entire column with the possibility of overriding them in individual cells. diff --git a/texy/en/configuration.texy b/texy/en/configuration.texy new file mode 100644 index 0000000000..eee1a1a890 --- /dev/null +++ b/texy/en/configuration.texy @@ -0,0 +1,719 @@ +Configuration +************* + +.[perex] +A complete guide to configuring Texy. Learn how to control all modules, set up security, and customize Texy to your needs. + +Texy is configured using **public properties** of the main `Texy\Texy` class and its **modules**. Each module is responsible for processing a specific part of the syntax (images, links, headings...). + +Basic approach: + +```php +$texy = new Texy\Texy; + +// Configuration of the main class +$texy->allowedTags = Texy\Texy::NONE; + +// Configuration of a module +$texy->imageModule->root = '/images/'; +``` + + +Texy\Texy Class .[#texy-class] +============================== + +The main class contains global settings and properties affecting the entire processing. + + +Allowed Syntax ($allowed) .{toc: $allowed} +------------------------------------------ + +The `$allowed` array controls which parts of Texy syntax are active: + +```php +// Default: all syntax features allowed (except emoticons) +$texy->allowed['image'] = true; +$texy->allowed['emoticon'] = false; + +// Disable images +$texy->allowed['image'] = false; + +// Disable HTML tags in input +$texy->allowed['html/tag'] = false; +$texy->allowed['html/comment'] = false; + +// Disable various types of links +$texy->allowed['link/reference'] = false; +$texy->allowed['link/email'] = false; +$texy->allowed['link/url'] = false; +``` + +**Complete list of syntax features:** + +|--- +| Key | Default | Description +|--- +| `image` | `true` | Images `[* img.jpg *]` +| `figure` | `true` | Images with caption +| `link/reference` | `true` | References `[ref]` +| `link/email` | `true` | Email addresses +| `link/url` | `true` | Automatic URLs +| `link/definition` | `true` | Reference definitions +| `heading/underlined` | `true` | Underlined headings +| `heading/surrounded` | `true` | Surrounded headings +| `horizline` | `true` | Horizontal lines +| `blockquote` | `true` | Quotes +| `list` | `true` | Lists +| `list/definition` | `true` | Definition lists +| `table` | `true` | Tables +| `phrase/strong` | `true` | Bold text `**text**` +| `phrase/em` | `true` | Italic `//text//` +| `phrase/em-alt` | `true` | Italic `*text*` +| `phrase/code` | `true` | Code ```text``` +| `phrase/ins` | `false` | Inserted text `++text++` +| `phrase/del` | `false` | Deleted text `--text--` +| `phrase/sup` | `false` | Superscript `^^text^^` +| `phrase/sub` | `false` | Subscript `__text__` +| `html/tag` | `true` | HTML tags in input +| `html/comment` | `true` | HTML comments +| `emoticon` | `false` | Emoticons `:-)`, `:-(` +| `blocks` | `true` | Blocks `/-- \--` +| `typography` | `true` | Typographic adjustments +| `longwords` | `true` | Breaking long words + + +Allowed HTML Tags ($allowedTags) .{toc: $allowedTags} +----------------------------------------------------- + +Controls which HTML tags are allowed in output (and input): + +```php +// Default: all valid HTML5 tags allowed +$texy->allowedTags = Texy\Texy::ALL; + +// Disable all HTML tags +$texy->allowedTags = Texy\Texy::NONE; + +// Allow only specific tags +$texy->allowedTags = [ + 'strong' => [], // <strong> without attributes + 'a' => ['href', 'title'], // <a> with attributes + 'img' => Texy\Texy::ALL, // <img> with any attributes +]; +``` + +**Formats:** +- `Texy::ALL` – all tags allowed +- `Texy::NONE` – no tags allowed +- Array – allowed tags as keys, allowed attributes as values + + +Allowed CSS Classes ($allowedClasses) .{toc: $allowedClasses} +------------------------------------------------------------- + +Controls which CSS classes and IDs can be used: + +```php +// Default: all classes and IDs allowed +$texy->allowedClasses = Texy\Texy::ALL; + +// Disable classes and IDs +$texy->allowedClasses = Texy\Texy::NONE; + +// Allow specific classes and IDs +$texy->allowedClasses = [ + 'highlight', + 'important', + '#main', // IDs start with # + '#sidebar', +]; +``` + +Usage: +```texy +Text with class .[highlight] + +Text with ID .{toc: main} +``` + + +Allowed CSS Styles ($allowedStyles) .{toc: $allowedStyles} +---------------------------------------------------------- + +Controls which inline CSS properties can be used: + +```php +// Default: all styles allowed +$texy->allowedStyles = Texy\Texy::ALL; + +// Disable inline styles +$texy->allowedStyles = Texy\Texy::NONE; + +// Allow specific CSS properties +$texy->allowedStyles = [ + 'color', + 'background-color', + 'font-size', +]; +``` + +Usage: +```texy +Text with color .{color: red} +``` + + +CSS Classes for Alignment ($alignClasses) .{toc: $alignClasses} +--------------------------------------------------------------- + +As an alternative to inline styles `style="text-align:left"`, you can use CSS classes: + +```php +// Default: empty array (uses inline styles) +$texy->alignClasses = [ + 'left' => null, + 'right' => null, + 'center' => null, + 'justify' => null, + 'top' => null, + 'middle' => null, + 'bottom' => null, +]; + +// Set classes +$texy->alignClasses['left'] = 'text-left'; +$texy->alignClasses['right'] = 'text-right'; +$texy->alignClasses['center'] = 'text-center'; +``` + +Usage: +```texy +Text aligned to the left . + +Text aligned to the right .> +``` + +With `alignClasses` set, it generates `<p class="text-left">` instead of `<p style="text-align:left">`. + + +Additional Properties +--------------------- + +```php +// Merge lines into paragraphs (default: true) +$texy->mergeLines = true; + +// Tab width (default: 8) +$texy->tabWidth = 8; + +// Obfuscate emails from bots (default: true) +$texy->obfuscateEmail = true; + +// Remove soft hyphens (default: true) +$texy->removeSoftHyphens = true; + +// Element for non-textual paragraphs (default: 'div') +$texy->nontextParagraph = 'div'; +``` + + +Modules +======= + +Each module processes a specific part of the syntax. Modules are accessible as public properties of the `Texy\Texy` class. + + +HeadingModule +------------- + +Processes headings (both underlined and surrounded). + +```php +// Top heading level (default: 1) +$texy->headingModule->top = 1; // <h1> + +// Generate automatic IDs (default: false) +$texy->headingModule->generateID = true; + +// Prefix for generated IDs (default: 'toc-') +$texy->headingModule->idPrefix = 'section-'; + +// More characters = higher heading? (default: true) +$texy->headingModule->moreMeansHigher = true; + +// Balancing mode (default: DYNAMIC) +$texy->headingModule->balancing = Texy\Modules\HeadingModule::DYNAMIC; +``` + +After processing: + +```php +// First heading (for <title>) +echo $texy->headingModule->title; + +// Table of Contents +print_r($texy->headingModule->TOC); +``` + + +PhraseModule +------------ + +Processes inline formatting (bold, italic, links within text...). + +```php +// HTML tags for individual phrases (default: see below) +$texy->phraseModule->tags = [ + 'phrase/strong' => 'strong', + 'phrase/em' => 'em', + 'phrase/code' => 'code', + // ... more +]; + +// Allow links in phrases (default: true) +$texy->phraseModule->linksAllowed = true; +``` + + +LinkModule +---------- + +Processes links, references, and URLs. + +```php +// Root path for links (default: '') +$texy->linkModule->root = '/articles/'; + +// CSS class for image links (default: null) +$texy->linkModule->imageClass = 'image-link'; + +// Always add rel="nofollow" (default: false) +$texy->linkModule->forceNoFollow = false; + +// Shorten URLs to a more readable form (default: true) +$texy->linkModule->shorten = true; +``` + +**References:** + +```php +// Add a reference +$link = new Texy\Link('https://example.com'); +$link->modifier->title = 'Example page'; +$link->label = 'Example'; +$texy->linkModule->addReference('example', $link); +``` + +Usage: +```texy +Link to [example] +``` + + +ImageModule +----------- + +Processes images. + +```php +// Root path for images (default: 'images/') +$texy->imageModule->root = '/assets/images/'; + +// Root path for linked images (default: 'images/') +$texy->imageModule->linkedRoot = '/assets/images/full/'; + +// Physical path on disk (to determine dimensions) +$texy->imageModule->fileRoot = __DIR__ . '/public/images/'; + +// CSS class for floating images (default: null) +$texy->imageModule->leftClass = 'float-left'; +$texy->imageModule->rightClass = 'float-right'; + +// Default alternative text (default: '') +$texy->imageModule->defaultAlt = 'Image'; +``` + +**References:** + +```php +// Add a reference +$image = new Texy\Image; +$image->URL = 'photo.jpg'; +$image->modifier->title = 'Photo'; +$texy->imageModule->addReference('photo', $image); +``` + + +FigureModule +------------ + +Processes images with captions. + +```php +// HTML element (default: 'div') +$texy->figureModule->tagName = 'figure'; + +// CSS class (default: 'figure') +$texy->figureModule->class = 'photo-figure'; + +// Classes for floating images (default: null) +$texy->figureModule->leftClass = 'figure-left'; +$texy->figureModule->rightClass = 'figure-right'; + +// Offset for width calculation (default: 10) +$texy->figureModule->widthDelta = 20; + +// Require caption (default: true) +$texy->figureModule->requireCaption = true; +``` + + +ListModule +---------- + +Processes bulleted, numbered, and definition lists. + +```php +// Patterns for list bullets (default: see source code) +$texy->listModule->bullets = [ + '*' => ['\*[\ \t]', 0, ''], + '-' => ['[\x{2013}-](?![>-])', 0, ''], + // ... more +]; +``` + + +TableModule +----------- + +Processes tables. + +```php +// CSS classes for rows (default: null) +$texy->tableModule->oddClass = 'odd'; +$texy->tableModule->evenClass = 'even'; +``` + +*Note: `oddClass` and `evenClass` are deprecated.* + + +HorizLineModule +--------------- + +Processes horizontal lines. + +```php +// CSS classes by type (default: null) +$texy->horizLineModule->classes = [ + '-' => 'hr-line', + '*' => 'hr-star', +]; +``` + + +TypographyModule +---------------- + +Processes typographic adjustments. + +```php +// Locale (default: 'cs') +$texy->typographyModule->locale = 'en'; +``` + +**Supported locales:** +- `cs` – Czech quotes „text" and ‚text' +- `en` – English quotes "text" and 'text' +- `fr` – French quotes «text» and ‹text› +- `de` – German quotes „text" and ‚text' +- `pl` – Polish quotes „text" and ‚text' + + +LongWordsModule +--------------- + +Breaks long words using `­`. + +```php +// Maximum word length (default: 20) +$texy->longWordsModule->wordLimit = 25; +``` + + +EmoticonModule +-------------- + +Replaces emoticons with images or Unicode characters. + +```php +// CSS class (default: null) +$texy->emoticonModule->class = 'emoji'; + +// Path to images (default: null = uses imageModule->root) +$texy->emoticonModule->root = '/images/smilies/'; +$texy->emoticonModule->fileRoot = __DIR__ . '/public/smilies/'; + +// Emoticon definitions (default: basic set) +$texy->emoticonModule->icons = [ + ':-)' => '🙂', + ':-(' => '☹', + ';-)' => '😉', + // ... or paths to images + ':cool:' => 'cool.gif', +]; +``` + + +HtmlModule +---------- + +Processes HTML tags and comments in the input text. + +```php +// Display HTML comments in output (default: true) +$texy->htmlModule->passComment = true; +``` + + +HtmlOutputModule +---------------- + +Formats the output HTML. + +```php +// Format output with indentation (default: true) +$texy->htmlOutputModule->indent = true; + +// Base indentation level (default: 0) +$texy->htmlOutputModule->baseIndent = 0; + +// Maximum line width (default: 80) +$texy->htmlOutputModule->lineWrap = 100; + +// Preserve whitespace in these elements (default: list shown) +$texy->htmlOutputModule->preserveSpaces = [ + 'textarea', 'pre', 'script', 'code', +]; +``` + + +ScriptModule +------------ + +Processes `{{macro}}` calls. + +```php +// Argument separator (default: ',') +$texy->scriptModule->separator = ';'; +``` + + +Texy\Configurator Class .{toc: Texy\Configurator} +================================================= + +Ready-made configuration sets for common use cases. + + +safeMode() – Safe Mode .{toc: safeMode()} +----------------------------------------- + +Configuration for processing **untrusted content** from users. + +```php +Texy\Configurator::safeMode($texy); +``` + +**What it does:** +- Disables classes and IDs (`$allowedClasses = NONE`) +- Disables inline styles (`$allowedStyles = NONE`) +- Allows only safe HTML tags: + +```php +[ + 'a' => ['href', 'title'], + 'abbr' => ['title'], + 'b' => [], + 'br' => [], + 'cite' => [], + 'code' => [], + 'em' => [], + 'i' => [], + 'strong' => [], + 'sub' => [], + 'sup' => [], + 'q' => [], + 'small' => [], +] +``` + +- Filters URL schemes (only `http:`, `https:`, `ftp:`, `mailto:`) +- Disables images +- Disables reference definitions +- Disables HTML comments +- Adds `rel="nofollow"` to all links + + +disableLinks() – Disable Links .{toc: disableLinks()} +----------------------------------------------------- + +Disables all types of links. + +```php +Texy\Configurator::disableLinks($texy); +``` + +**What it does:** +- Disables all types of links (`link/reference`, `link/email`, `link/url`, `link/definition`) +- Disables links in phrases (`phraseModule->linksAllowed = false`) +- Removes `<a>` from allowed tags + + +disableImages() – Disable Images .{toc: disableImages()} +-------------------------------------------------------- + +Disables all types of images. + +```php +Texy\Configurator::disableImages($texy); +``` + +**What it does:** +- Disables images (`image`, `figure`, `image/definition`) +- Removes `<img>`, `<object>`, `<embed>`, `<applet>` from allowed tags + + +Security +======== + +Texy is designed with security in mind. It automatically protects against common attacks. + + +Protection Against XSS +---------------------- + +Cross-Site Scripting (XSS) is an attack where an attacker injects malicious JavaScript into a page. + +**Examples of attacks that Texy will block:** + +```texy +Attack attempt: <script>alert('XSS')</script> + +Attack attempt: <img src=x onerror="alert('XSS')"> + +Attack attempt: "click":javascript:alert('XSS') + +Attack attempt: [* image.jpg onload="alert('XSS')" *] +``` + +Texy automatically: +- **Validates HTML** – removes disallowed tags and attributes +- **Filters URLs** – allows only safe schemes (`http:`, `https:`, `mailto:`, `ftp:`) +- **Escapes content** – properly escapes text in attributes +- **Sanitizes attributes** – removes event handlers (`onclick`, `onerror`, ...) + +```php +$texy = new Texy\Texy; +Texy\Configurator::safeMode($texy); + +$input = '<script>alert("XSS")</script>'; +$output = $texy->process($input); + +// Output: empty (script tag removed) +``` + + +URL Validation +-------------- + +Texy checks URLs in all links and images: + +```php +$texy = new Texy\Texy; + +// Set allowed schemes (default in safeMode) +$texy->urlSchemeFilters[Texy\Texy::FILTER_ANCHOR] = + '#https?:|ftp:|mailto:#Ai'; +$texy->urlSchemeFilters[Texy\Texy::FILTER_IMAGE] = + '#https?:#Ai'; +``` + +**Examples of blocked URLs:** + +```texy +"attack":javascript:alert('XSS') // blocked +"attack":data:text/html,<script> // blocked +[* javascript:alert() *] // blocked +``` + + +Filtering HTML Tags +------------------- + +Control via `$allowedTags`: + +```php +$texy = new Texy\Texy; + +// Allow only safe tags +$texy->allowedTags = [ + 'p' => [], + 'strong' => [], + 'em' => [], + 'a' => ['href', 'title'], // only these attributes +]; + +$input = '<p>Text <script>alert()</script></p>'; +$output = $texy->process($input); + +// Output: <p>Text alert()</p> +// (script tag removed) +``` + + +Practical Example +----------------- + +```php +function processComment(string $userInput): string +{ + $texy = new Texy\Texy; + + // Safe mode + Texy\Configurator::safeMode($texy); + + // Additional restrictions + $texy->allowed['link/url'] = false; // disable auto-links + $texy->allowed['html/tag'] = false; // disable HTML + + // Process + return $texy->process($userInput); +} + +// Usage +$comment = $_POST['comment']; +$html = processComment($comment); +echo $html; // safe output +``` + + +Best Practices +-------------- + +1. **Always use safeMode()** for user content +2. **Validate input** before passing to Texy (length, format) +3. **Limit HTML tags** as needed +4. **Check output** – even though Texy is safe, double-checking never hurts +5. **Log suspicious attempts** – can help you identify attackers + +```php +$texy = new Texy\Texy; +Texy\Configurator::safeMode($texy); + +// Logging +$texy->addHandler('htmlTag', function($invocation, $el, $isStart) { + if ($el->getName() === 'script') { + error_log('XSS attempt detected!'); + } + return $invocation->proceed(); +}); +``` diff --git a/texy/en/custom-handlers.texy b/texy/en/custom-handlers.texy new file mode 100644 index 0000000000..c5e49dcf8b --- /dev/null +++ b/texy/en/custom-handlers.texy @@ -0,0 +1,850 @@ +Modifying Element Behavior +************************** + +.[perex] +This chapter describes how you can change the behavior of **existing elements** in Texy - for example, modify how images, links or formatting are processed. If you want to add **completely new syntax** that Texy doesn't know by default, read the chapter [Adding Custom Syntax |custom-syntax]. + +Imagine you want the standard image syntax `[* URL *]` to recognize a special address `[* youtube:dQw4w9WgXcQ *]` and create an embedded player instead of a regular image. + +Or you want to colorize source code listings using a syntax highlighter. And so on. This is exactly what **element handlers** are for - functions that Texy calls when processing specific elements. For example, you register a handler for the `image` element that checks the URL, and if it starts with `youtube:`, returns an iframe instead of a standard image. You don't change the syntax, you just modify what happens with the found construct. + + +Elements and Their Handlers +=========================== + +In Texy terminology, an **element** is the name for a type of item that can be processed in a document. For example, `image` is an element for images, `linkURL` for links, see [default elements |#Default Elements]. Each element has its **default handler**, which is implemented in the corresponding module and handles standard processing. + +When you write `[* image.jpg *]` in text, the parser finds this syntax, creates a `Texy\Image` object with data about the image and calls all handlers registered for the `image` element. If there is no custom handler, only the default handler from `ImageModule` is called, which creates an HTML `<img>` tag. + +You register a handler by calling the `addHandler()` method: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + // your logic here +}); +``` + +The first parameter is the element name, the second is a callback function. The callback always receives a `Texy\HandlerInvocation` object as the first parameter, followed by parameters specific to the given element. + +.[note] +A detailed explanation of all handler types can be found in the chapter [Architecture and Principles |architecture]. + + +How Processing Works +==================== + +When Texy needs to process an element, it creates a `HandlerInvocation` object containing all registered handlers for this type of element. **Your handler is called first** and can: + +- **Delegate** to the next handler by calling `$invocation->proceed()` +- **Modify input** by calling `proceed()` with modified parameters +- **Modify output** by processing the result from `proceed()` +- **Break the chain** by returning its own result without calling `proceed()` + +The `proceed()` method moves processing to the next handler in the chain. If there are no more custom handlers, the default implementation from the module is called. This means your handler has absolute control - it can decide whether the default logic is called at all. + +This mechanism is called **chain of responsibility**: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + // 1. Modify input data before processing + $image->modifier->title = 'Modified title'; + + // 2. Call next handler or default processing + $element = $invocation->proceed($image, $link); + + // 3. Modify resulting HTML element + $element->attrs['loading'] = 'lazy'; + + return $element; +}); +``` + +The execution order is from **last registered to first**. If a module registers its default handler during construction and you then register a custom handler, your handler is called first. This allows you to override or wrap the default behavior. + + +Default Elements +================ + +Texy provides several predefined elements for which you can register custom handlers. Here is their complete list with parameters that the handler receives. + + +image +----- + +Processes images. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +): Texy\HtmlElement|string|null +``` + +The `$image` parameter contains URL, dimensions and modifiers. The `$link` parameter is provided if the image is a link (syntax `[* img *]:url`). + + +linkReference +------------- + +Processes reference links of type `[ref]`. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, + string $content, +): Texy\HtmlElement|string|null +``` + +The `$link` parameter contains the URL and modifiers loaded from the reference definition. The `$content` parameter is the HTML content of the link (already processed by parsing inline syntax). + + +linkEmail +--------- + +Processes automatically recognized email addresses in text. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, +): Texy\HtmlElement|string|null +``` + +The `$link` parameter contains the email address in the `URL` property. + + +linkURL +------- + +Processes automatically recognized URLs in text. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, +): Texy\HtmlElement|string|null +``` + +The `$link` parameter contains the found URL. + + +phrase +------ + +Processes inline formatting. + +```php +function( + Texy\HandlerInvocation $invocation, + string $phrase, + string $content, + Texy\Modifier $modifier, + ?Texy\Link $link, +): Texy\HtmlElement|string|null +``` + +The `$phrase` parameter is the syntax name like `phrase/strong` or `phrase/em`. The `$content` parameter is the text inside the formatting. The `$modifier` parameter contains CSS classes, styles and other modifiers. The `$link` parameter is provided if the formatting has an attached link. + + +newReference +------------ + +Called when the parser finds a reference that is not defined. + +```php +function( + Texy\HandlerInvocation $invocation, + string $name, +): Texy\HtmlElement|string|null +``` + +The `$name` parameter is the reference name. The handler can create a link dynamically or return `null` to reject. + + +htmlComment +----------- + +Processes HTML comments. + +```php +function( + Texy\HandlerInvocation $invocation, + string $content, +): string +``` + +The `$content` parameter is the text between `<!--` and `-->`. + + +htmlTag +------- + +Processes HTML tags in text. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\HtmlElement $el, + bool $isStart, + ?bool $forceEmpty, +): Texy\HtmlElement|string|null +``` + +The `$el` parameter is an element with name and attributes. The `$isStart` parameter determines whether it is an opening tag. The `$forceEmpty` parameter forces an empty element. + + +script +------ + +Processes scripts `{{command: args}}`. + +```php +function( + Texy\HandlerInvocation $invocation, + string $command, + array $args, + ?string $raw, +): Texy\HtmlElement|string|null +``` + +The `$command` parameter is the command name. The `$args` parameter is an array of arguments. The `$raw` parameter is the original unparsed argument string. + + +figure +------ + +Processes images with captions. + +```php +function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, + string $content, + Texy\Modifier $modifier, +): Texy\HtmlElement|null +``` + +The `$content` parameter is the caption text below the image. + + +heading +------- + +Processes headings. + +```php +function( + Texy\HandlerInvocation $invocation, + int $level, + string $content, + Texy\Modifier $modifier, + bool $isSurrounded, +): Texy\HtmlElement +``` + +The `$level` parameter is the heading level (0-6). The `$content` parameter is the heading text. The `$isSurrounded` parameter determines whether it is a delimited heading (`###`) or underlined. + + +horizline +--------- + +Processes horizontal lines. + +```php +function( + Texy\HandlerInvocation $invocation, + string $type, + Texy\Modifier $modifier, +): Texy\HtmlElement +``` + +The `$type` parameter is the string of characters used for the line (`---` or `***`). + + +block +----- + +Processes special blocks `/--type` to `\--`. + +```php +function( + Texy\HandlerInvocation $invocation, + string $blocktype, + string $content, + ?string $param, + Texy\Modifier $modifier, +): Texy\HtmlElement|string +``` + +The `$blocktype` parameter is the block type with the `block/` prefix, e.g. `block/code` or `block/html`. The `$content` parameter is the block content. The `$param` parameter is an optional parameter after the type (e.g. language for code). + + +emoticon +-------- + +Processes emoticons (smileys). + +```php +function( + Texy\HandlerInvocation $invocation, + string $emoticon, + string $raw, +): Texy\HtmlElement|string +``` + +The `$emoticon` parameter is the recognized emoticon (e.g. `:-)` or `:-(` ). The `$raw` parameter is the original text including any repeating characters (e.g. `:-)))))` ). + +.[note] +Emoticons are **disabled** by default. You can enable them using `$texy->allowed['emoticon'] = true;` + + +Default Events +============== + +Texy provides several predefined events for which you can register handlers. These are called **notification handlers**. Unlike element handlers, these handlers **don't return anything**. They are used for side effects such as logging, collecting statistics, or modifying the already created DOM tree. + + +beforeParse +----------- + +Called before text parsing begins. Allows you to perform preprocessing or load definitions. + +```php +function( + Texy\Texy $texy, + string &$text, + bool $isSingleLine, +): void +``` + +The `$text` parameter is passed by reference, so you can modify it. The `$isSingleLine` parameter determines whether a single line or the entire document is being parsed. + + +afterParse +---------- + +Called after parsing is complete, before converting the DOM tree to HTML. Allows you to modify the created DOM. + +```php +function( + Texy\Texy $texy, + Texy\HtmlElement $DOM, + bool $isSingleLine, +): void +``` + +The `$DOM` parameter is the root element of the document, which you can traverse and modify. + + +afterList +--------- + +Called after a list (numbered or unnumbered) is created. + +```php +function( + Texy\BlockParser $parser, + Texy\HtmlElement $element, + Texy\Modifier $modifier, +): void +``` + +The `$element` parameter is the created `<ul>` or `<ol>` element. The `$modifier` parameter contains modifiers applied to the entire list. + + +afterDefinitionList +------------------- + +Called after a definition list is created. + +```php +function( + Texy\BlockParser $parser, + Texy\HtmlElement $element, + Texy\Modifier $modifier, +): void +``` + +The `$element` parameter is the created `<dl>` element. + + +afterTable +---------- + +Called after a table is created. + +```php +function( + Texy\BlockParser $parser, + Texy\HtmlElement $element, + Texy\Modifier $modifier, +): void +``` + +The `$element` parameter is the created `<table>` element. + + +afterBlockquote +--------------- + +Called after a blockquote is created. + +```php +function( + Texy\BlockParser $parser, + Texy\HtmlElement $element, + Texy\Modifier $modifier, +): void +``` + +The `$element` parameter is the created `<blockquote>` element. + + +Basic Usage +=========== + +The simplest element handler just delegates to the default processing: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + return $invocation->proceed(); +}); +``` + +This handler doesn't change anything, but shows the basic skeleton. It passes all parameters forward and returns the result. + + +Modifying Input Data +-------------------- + +A handler can modify data before processing: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + // add default dimensions if not specified + $image->width ??= 800; + $image->height ??= 600; + return $invocation->proceed(); +}); +``` + +Changes made to `$image` or `$link` objects will be reflected in further processing, including the default handler. + + +Modifying Output Element +------------------------ + +A handler can modify the HTML element returned from `proceed()`: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + $element = $invocation->proceed(); + + if ($element) { + // add lazy loading + $element->attrs['loading'] = 'lazy'; + + // add CSS class + $element->attrs['class'][] = 'responsive'; + } + + return $element; +}); +``` + + +Conditional Processing +---------------------- + +A handler can process only certain cases and delegate others: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + // special processing for YouTube videos + if (str_starts_with($image->URL, 'youtube:')) { + $id = substr($image->URL, 8); + $iframe = sprintf( + '<iframe src="https://youtube.com/embed/%s"></iframe>', + htmlspecialchars($id) + ); + return $invocation->getTexy() + ->protect($iframe, Texy\Texy::CONTENT_BLOCK); + } + + // process other images normally + return $invocation->proceed(); +}); +``` + + +Interrupting Processing +----------------------- + +A handler can refuse processing by returning `null`: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + // disallow external images + if (str_contains($image->URL, '://')) { + return null; + } + + return $invocation->proceed(); +}); +``` + + +Practical Examples +================== + +The following examples show real use cases for element handlers. + + +YouTube Embed +------------- + +Converting special syntax to embedded video: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + if (str_starts_with($image->URL, 'youtube:')) { + $id = substr($image->URL, 8); + $width = $image->width ?: 560; + $height = $image->height ?: 315; + + $iframe = sprintf( + '<iframe width="%d" height="%d" ' + . 'src="https://youtube.com/embed/%s" ' + . 'frameborder="0" allowfullscreen></iframe>', + $width, $height, htmlspecialchars($id) + ); + + $texy = $invocation->getTexy(); + return $texy->protect($iframe, $texy::CONTENT_BLOCK); + } + + return $invocation->proceed(); +}); +``` + +Usage in text: + +```texy +[* youtube:dQw4w9WgXcQ 640x360 *] +``` + + +Image Gallery +------------- + +Wrapping images in a special div for lightbox: + +```php +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) { + $element = $invocation->proceed(); + + // if image has 'gallery' class + if (isset($image->modifier->classes['gallery'])) { + // wrap in div with lightbox attributes + $wrapper = new Texy\HtmlElement('div'); + $wrapper->attrs['class'][] = 'lightbox-item'; + $wrapper->attrs['data-src'] = $image->URL; + $wrapper->add($element); + + return $wrapper; + } + + return $element; +}); +``` + +Usage: + +```texy +[* image.jpg .[gallery] *] +``` + + +Link Validation +--------------- + +Checking whether links found in text lead to allowed domains: + +```php +$allowedDomains = ['example.com', 'trusted.org']; + +$texy->addHandler('linkURL', function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, +) use ($allowedDomains) { + $host = parse_url($link->URL, PHP_URL_HOST); + + // if domain is not in whitelist, disallow link + if ($host && !in_array($host, $allowedDomains, true)) { + return null; + } + + return $invocation->proceed(); +}); +``` + + +Automatic rel="nofollow" +------------------------ + +Adding `nofollow` to all external links found in text: + +```php +$texy->addHandler('linkURL', function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, +) { + $element = $invocation->proceed(); + + // if link contains :// (i.e. is external) + if (str_contains($link->URL, '://')) { + $element->attrs['rel'] = 'nofollow'; + } + + return $element; +}); +``` + + +Syntax Highlighting +------------------- + +Integrating a syntax highlighting library: + +```php +$texy->addHandler('block', function( + Texy\HandlerInvocation $invocation, + string $blocktype, + string $content, + ?string $param, + Texy\Modifier $modifier, +) { + // process only 'code' type blocks + if ($blocktype !== 'block/code') { + return $invocation->proceed(); + } + + // apply syntax highlighting + $highlighter = new MyHighlighter(); + $highlighted = $highlighter->highlight($content, $param); + + $el = new Texy\HtmlElement('pre'); + $modifier->decorate($invocation->getTexy(), $el); + $el->attrs['class'][] = 'language-' . $param; + + $code = new Texy\HtmlElement('code'); + $code->add($highlighted); + $el->add($code); + + return $el; +}); +``` + + +Lazy Loading +------------ + +Iterating through all images and adding lazy loading: + +```php +$texy->addHandler('afterParse', function( + Texy\Texy $texy, + Texy\HtmlElement $DOM, + bool $isSingleLine, +) { + foreach ($DOM->getIterator() as $child) { + if ($child instanceof Texy\HtmlElement + && $child->getName() === 'img' + ) { + $child->attrs['loading'] = 'lazy'; + } + } +}); +``` + + +Logging Used Elements +--------------------- + +Collecting statistics about used elements in the document: + +```php +$stats = []; + +$texy->addHandler('beforeParse', function( + Texy\Texy $texy, + string &$text, + bool $isSingleLine, +) use (&$stats) { + $stats = ['images' => 0, 'links' => 0, 'headings' => 0]; +}); + +$texy->addHandler('image', function( + Texy\HandlerInvocation $invocation, + Texy\Image $image, + ?Texy\Link $link, +) use (&$stats) { + $stats['images']++; + return $invocation->proceed(); +}); + +$texy->addHandler('linkURL', function( + Texy\HandlerInvocation $invocation, + Texy\Link $link, +) use (&$stats) { + $stats['links']++; + return $invocation->proceed(); +}); + +$texy->addHandler('heading', function( + Texy\HandlerInvocation $invocation, + int $level, + string $content, + Texy\Modifier $modifier, + bool $isSurrounded, +) use (&$stats) { + $stats['headings']++; + return $invocation->proceed(); +}); +``` + + +Helper Classes +============== + +When working with handlers, you will work with several important classes. Here is an overview with their most important properties. + + +Texy\Image +---------- + +Represents an image with its parameters: + +```php +$image->URL; // string - path to image +$image->linkedURL; // ?string - link URL (if image is a link) +$image->width; // ?int - width in pixels +$image->height; // ?int - height in pixels +$image->asMax; // bool - whether dimensions are maximum +$image->modifier; // Modifier - CSS classes, styles, attributes +$image->name; // ?string - reference name +``` + + +Texy\Link +--------- + +Represents a link with its parameters: + +```php +$link->URL; // string - target URL +$link->raw; // string - original URL (before normalization) +$link->modifier; // Modifier - CSS classes, styles, attributes +$link->type; // string - link type (COMMON, BRACKET, IMAGE) +$link->label; // ?string - link text (for references) +$link->name; // ?string - reference name +``` + +Constants for link type: + +```php +Texy\Link::COMMON; // common link +Texy\Link::BRACKET; // reference link [ref] +Texy\Link::IMAGE; // link from image [* img *] +``` + + +Texy\HtmlElement +---------------- + +Represents an HTML element with its attributes and content: + +```php +$el = new Texy\HtmlElement('div'); + +// working with element name +$el->getName(); // returns 'div' +$el->setName('section'); // changes to 'section' + +// working with attributes +$el->attrs['id'] = 'main'; +$el->attrs['class'][] = 'container'; +$el->attrs['style']['color'] = 'red'; + +// working with content +$el->setText('text'); // sets text content +$el->getText(); // returns text content +$el->add($child); // adds child +$el->insert(0, $child); // inserts child at position + +// parsing content +$el->parseLine($texy, $text); // parses inline text +$el->parseBlock($texy, $text); // parses block text + +// conversion to HTML +$el->toString($texy); // internal representation +$el->toHtml($texy); // final HTML +``` + + +Texy\Modifier +------------- + +Represents modifiers for CSS classes, styles and attributes: + +```php +$mod->id; // ?string - HTML id +$mod->classes; // array - array of CSS classes +$mod->styles; // array - array of CSS styles +$mod->attrs; // array - HTML attributes +$mod->hAlign; // ?string - horizontal alignment (left, right, center, justify) +$mod->vAlign; // ?string - vertical alignment (top, middle, bottom) +$mod->title; // ?string - title attribute or alt for images + +// applying modifier to element +$mod->decorate($texy, $element); +``` diff --git a/texy/en/custom-syntax.texy b/texy/en/custom-syntax.texy new file mode 100644 index 0000000000..9a8365fc43 --- /dev/null +++ b/texy/en/custom-syntax.texy @@ -0,0 +1,519 @@ +Adding Custom Syntax +******************** + +.[perex] +This chapter describes how to add **completely new markup constructs** to Texy that don't exist by default. If you only want to modify the behavior of existing elements (for example, adjust image or link processing), read the chapter [Custom Element Behavior |custom-handlers]. + +Imagine you want to automatically create links to user profiles in documentation by writing `@@username`. Or you need special alert blocks like `:::warning`. Texy doesn't recognize these constructs, and you can't create them by modifying existing elements. + +Custom syntax allows you to define new markup constructs. You specify what the construct should look like (using a regular expression) and write a function to process it. Texy will then recognize your syntax just like its standard constructs. + + +Syntax Registration +=================== + +Texy provides two methods for registering custom syntax, depending on whether it's an inline or block element. + + +Line Syntax +----------- + +Line syntax is used for inline constructs within text lines. You register it using the `registerLinePattern()` method: + +```php +$texy->registerLinePattern( + callable $handler, + string $pattern, + string $name, + ?string $againTest = null, +); +``` + +**The `$handler` parameter** is a callback function that gets called when the syntax is found. It can be a function name, anonymous function, or array `[$object, 'method']`. + +**The `$pattern` parameter** is a regular expression (PCRE) that defines what your syntax looks like in the text. The pattern **should not be anchored** to the start of a line (`^`), since it's searched for anywhere in the text. Use capturing groups to capture the data you need to process. + +**The `$name` parameter** is a unique syntax name. It's used in the `$texy->allowed` array for enabling/disabling and passed to the handler for identification. We recommend using a prefix style like `custom/username` or `myapp/profile`. + +**The `$againTest` parameter** is an optional regex for optimization. If specified, Texy first checks whether the text contains anything that could match your pattern. Only if `$againTest` succeeds does it run the more complex pattern. This significantly speeds up processing if you have a complex pattern that's rarely used. + +Registration example: + +```php +$texy->registerLinePattern( + 'usernameHandler', + '#@@([a-z0-9_]+)#i', + 'custom/username', +); +``` + + +Block Syntax +------------ + +Block syntax is used for multi-line block constructs. You register it using the `registerBlockPattern()` method: + +```php +$texy->registerBlockPattern( + callable $handler, + string $pattern, + string $name, +); +``` + +The `$handler` and `$name` parameters have the same meaning as for line syntax. + +**The `$pattern` parameter** is a regular expression that **must be anchored** to the start of a line (`^`) and often to the end (`$`) as well. BlockParser automatically adds the `Am` modifiers (anchored, multiline), so don't add them to the pattern. The pattern should match the entire block or at least its beginning. + +Registration example: + +```php +$texy->registerBlockPattern( + 'alertHandler', + '#^:::(warning|info|danger)\n(.+)$#s', + 'custom/alert', +); +``` + + +Syntax Handler +============== + +A syntax handler is a function called by the parser when it finds an occurrence of your syntax in the text. Its job is to process the found data and return an HTML element or string. + +A detailed explanation of the syntax handler's role in Texy's architecture can be found in the chapter [Architecture and Principles |architecture#syntax-handler]. + + +For Line Syntax +--------------- + +Syntax handler signature for line syntax: + +```php +function( + Texy\LineParser $parser, + array $matches, + string $name, +): Texy\HtmlElement|string|null +``` + +**The `$parser` parameter** provides access to the parser and Texy object. You'll most often use `$parser->getTexy()` to get the Texy instance. + +**The `$matches` parameter** contains the regex match results. `$matches[0]` is the entire matched string, `$matches[1]`, `$matches[2]` etc. are the capturing groups from your pattern. + +**The `$name` parameter** is the syntax name you specified during registration. Useful if one handler processes multiple syntaxes. + +**The return value** can be `Texy\HtmlElement` for structured HTML output, `string` for direct HTML code (which you must protect), or `null` to refuse processing. + +The handler can set `$parser->again = true` if it wants the content of the created element to be parsed again to find nested syntaxes. + + +For Block Syntax +---------------- + +Syntax handler signature for block syntax: + +```php +function( + Texy\BlockParser $parser, + array $matches, + string $name, +): Texy\HtmlElement|string|null +``` + +The parameters have the same meaning as for line syntax, except you receive `Texy\BlockParser` instead of `LineParser`. + +BlockParser provides methods for working with multi-line structures: + +- **`$parser->next($pattern, &$matches)`** - matches the next line against the pattern and returns true/false +- **`$parser->moveBackward($lines)`** - moves back the specified number of lines +- **`$parser->isIndented()`** - returns true if the current block is indented + + +LineParser API +============== + +When working with line syntax, you have several useful properties and methods available. + +**The `$again` property** controls whether the currently processed syntax should be searched for again at the same position after processing. The default value is `false`. Set to `true` if you're creating an element with content that may contain other syntaxes: + +```php +function( + Texy\LineParser $parser, + array $matches, + string $name, +): Texy\HtmlElement +{ + $el = new Texy\HtmlElement('span'); + $el->setText($matches[1]); + + // content may contain additional formatting + $parser->again = true; + + return $el; +} +``` + +**The `getTexy()` method** returns the Texy object instance, which you need for working with `protect()` or accessing configuration. + + +BlockParser API +=============== + +When working with block syntax, you have methods available for working with multi-line structures. + +**The `next($pattern, &$matches)` method** tries to match the next line in the text against the specified pattern. If successful, it fills `$matches` with the result and moves the internal position past this line. Returns `true` on success, `false` on failure: + +```php +while ($parser->next('#^\-\s+(.+)$#', $matches)) { + // process next list item + $item = $matches[1]; +} +``` + +**The `moveBackward($lines = 1)` method** moves the internal position back the specified number of lines. Useful when your pattern matched more than the block's start and you want to return to the beginning: + +```php +// pattern matched 3 lines, but we want to read from the first +$parser->moveBackward(2); +``` + +**The `isIndented()` method** returns `true` if the current block is indented (starts with a space or tab). This indicates that it's nested content. + + +Practical Examples +================== + +The following examples demonstrate real use cases for custom syntax. + + +User Profiles +------------- + +Automatic creation of profile links by writing `@@username`: + +```php +$texy->registerLinePattern( + function( + Texy\LineParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $username = $matches[1]; + + $el = new Texy\HtmlElement('a'); + $el->attrs['href'] = '/user/' . urlencode($username); + $el->attrs['class'][] = 'user-profile'; + $el->setText('@' . $username); + + return $el; + }, + '#@@([a-z0-9_]+)#i', + 'custom/username' +); +``` + +Usage in text: + +```texy +Check out the profile of @@johndoe or @@jane_smith. +``` + + +Alert Boxes +----------- + +Special alert blocks with different types: + +```php +$texy->registerBlockPattern( + function( + Texy\BlockParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $type = $matches[1]; // warning, info, danger + $content = $matches[2]; + + $el = new Texy\HtmlElement('div'); + $el->attrs['class'][] = 'alert'; + $el->attrs['class'][] = 'alert-' . $type; + + $texy = $parser->getTexy(); + $el->parseBlock($texy, trim($content)); + + return $el; + }, + '#^:::(warning|info|danger)\n(.+?)(?=\n:::|$)#s', + 'custom/alert' +); +``` + +Usage in text: + +```texy +:::warning +This is an important warning! +::: + +:::info +For your information: the update will take place tomorrow. +::: +``` + + +Hashtags +-------- + +Automatic creation of links from hashtags: + +```php +$texy->registerLinePattern( + function( + Texy\LineParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $tag = $matches[1]; + + $el = new Texy\HtmlElement('a'); + $el->attrs['href'] = '/tag/' . urlencode($tag); + $el->attrs['class'][] = 'hashtag'; + $el->setText('#' . $tag); + + return $el; + }, + '#\#([a-z0-9_]+)#i', + 'custom/hashtag', + '#\##' // optimization - search only if # is in text +); +``` + +Usage: + +```texy +Article about #php and #webdesign. +``` + + +Abbreviations +------------- + +Automatic expansion of abbreviations with explanation: + +```php +$abbreviations = [ + 'HTML' => 'HyperText Markup Language', + 'CSS' => 'Cascading Style Sheets', + 'PHP' => 'PHP: Hypertext Preprocessor', +]; + +$texy->registerLinePattern( + function( + Texy\LineParser $parser, + array $matches, + string $name + ) use ($abbreviations): ?Texy\HtmlElement + { + $abbr = $matches[1]; + + if (!isset($abbreviations[$abbr])) { + return null; // unknown abbreviation + } + + $el = new Texy\HtmlElement('abbr'); + $el->attrs['title'] = $abbreviations[$abbr]; + $el->setText($abbr); + + return $el; + }, + '#\b([A-Z]{2,})\b#', + 'custom/abbreviation' +); +``` + + +Inline Icons +------------ + +Inserting icons using special syntax: + +```php +$texy->registerLinePattern( + function( + Texy\LineParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $icon = $matches[1]; + + $el = new Texy\HtmlElement('i'); + $el->attrs['class'][] = 'icon'; + $el->attrs['class'][] = 'icon-' . $icon; + $el->attrs['aria-hidden'] = 'true'; + + return $el; + }, + '#:icon-([a-z-]+):#', + 'custom/icon' +); +``` + +Usage: + +```texy +Click the button :icon-download: to download. +``` + + +Note Block +---------- + +Block for footnotes: + +```php +$texy->registerBlockPattern( + function( + Texy\BlockParser $parser, + array $matches, + string $name + ): Texy\HtmlElement + { + $parser->moveBackward(); + + $content = ''; + while ($parser->next('#^NOTE:\s*(.+)$#', $matches)) { + $content .= $matches[1] . "\n"; + } + + $el = new Texy\HtmlElement('aside'); + $el->attrs['class'][] = 'note'; + + $texy = $parser->getTexy(); + $el->parseBlock($texy, trim($content)); + + return $el; + }, + '#^NOTE:\s*(.+)$#m', + 'custom/note' +); +``` + +Usage: + +```texy +NOTE: This is an important note. +NOTE: It can be multi-line. +``` + + +Custom Quotations with Author +----------------------------- + +Extended syntax for quotations with author attribution: + +```php +$texy->registerBlockPattern( + function( + Texy\BlockParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $author = $matches[1]; + $quote = $matches[2]; + + $blockquote = new Texy\HtmlElement('blockquote'); + + $texy = $parser->getTexy(); + $blockquote->parseBlock($texy, trim($quote)); + + $cite = new Texy\HtmlElement('cite'); + $cite->setText($author); + $blockquote->add($cite); + + return $blockquote; + }, + '#^QUOTE\[([^\]]+)\]:\n(.+?)(?=\n\n|$)#s', + 'custom/quote' +); +``` + +Usage: + +```texy +QUOTE[Albert Einstein]: +Imagination is more important than knowledge, +because knowledge is limited. +``` + + +Image Gallery +------------- + +Special block for creating a gallery from multiple images: + +```php +$texy->registerBlockPattern( + function( + Texy\BlockParser $parser, + array $matches, + string $name, + ): Texy\HtmlElement + { + $parser->moveBackward(); + + $gallery = new Texy\HtmlElement('div'); + $gallery->attrs['class'][] = 'gallery'; + + while ($parser->next('#^\[G\]\s*(.+)$#', $matches)) { + $img = new Texy\HtmlElement('img'); + $img->attrs['src'] = trim($matches[1]); + $img->attrs['loading'] = 'lazy'; + $gallery->add($img); + } + + return $gallery; + }, + '#^\[G\]\s*(.+)$#m', + 'custom/gallery' +); +``` + +Usage: + +```texy +[G] image1.jpg +[G] image2.jpg +[G] image3.jpg +``` + + +Syntax Collisions +================= + +When registering custom syntax, you must be careful that it doesn't collide with existing Texy syntaxes or other custom syntaxes. + +**Registration order matters.** Line syntaxes are searched in the order they were registered. If multiple syntaxes can match at the same position, the one registered earlier wins. Therefore, register more specific syntaxes before more general ones. + +**Be specific in patterns.** The more concrete your pattern is, the lower the risk of collision. The pattern `#\#\w+#` also matches `#heading`, which could collide with headings. Better is `#(?<=\s)\#[a-z0-9_]+#i`, which requires a space before the hashtag. + +**Test combinations.** Try how your syntax works in combination with existing constructs. What happens when your markup is inside a link? What if it's inside a code block? + +**Use prefixed names.** Instead of `username`, use `custom/username` or `myapp/username`. This prevents conflicts if Texy adds syntax with the same name in the future. + + +Best Practices +============== + +**Return `null` on failure.** If the handler determines it can't or doesn't want to process the given match (for example, an unknown abbreviation), return `null`. The parser will then try other syntaxes. + +**Use `protect()` for HTML.** If you're returning a raw HTML string instead of `HtmlElement`, you must protect it using `$texy->protect($html, Texy::CONTENT_...)`. Otherwise it will be escaped. + +**Set `$parser->again` correctly.** For line syntaxes that create an element with text content that may contain other syntaxes (formatting, links), set `$parser->again = true`. + +**Respect `$texy->allowed`.** If you're creating a module with multiple syntaxes, check `$texy->allowed[$name]` before registering the pattern or in the handler before processing. diff --git a/texy/en/develop.texy b/texy/en/develop.texy new file mode 100644 index 0000000000..1b3c0eda6c --- /dev/null +++ b/texy/en/develop.texy @@ -0,0 +1,35 @@ +For Developers +************** + +.[perex] +Welcome to the Texy programmer documentation! This section will guide you from basic usage to advanced techniques of extension and custom syntax. + + +[Quick Start | quickstart] +-------------------------- + +Installation, first use and basic configuration. In 5 minutes you will have Texy functional. + + +[Configuration | configuration] +------------------------------- + +Complete overview of all modules, their properties and configuration options. Security settings, allowed tags, styles and classes. + + +[Custom Element Behavior | custom-handlers] +------------------------------------------- + +Learn how to change the behavior of existing syntax. YouTube embedding, syntax highlighting, custom validation. + + +[Adding Custom Syntax | custom-syntax] +-------------------------------------- + +Creating completely new syntactic elements. + + +[Architecture and Principles | architecture] +-------------------------------------------- + +Understanding how Texy works internally. Parsing flow, modules, pattern matching, protect/unprotect mechanism. diff --git a/texy/en/quickstart.texy b/texy/en/quickstart.texy new file mode 100644 index 0000000000..a1214045a6 --- /dev/null +++ b/texy/en/quickstart.texy @@ -0,0 +1,205 @@ +Quick Start +*********** + +.[perex] +Learn how to work with Texy in just a few minutes. This page guides you through installation, first use, and basic configuration. + + +Installation +============ + +Texy leverages modern PHP features and requires at least version 8.1. + +The simplest installation method is via Composer: + +```bash +composer require texy/texy +``` + +Composer will automatically download Texy and all its dependencies. + + +First Use +========= + + +Basic Text Processing +--------------------- + +Creating a Texy instance and processing text is extremely simple: + +```php +require __DIR__ . '/vendor/autoload.php'; + +$texy = new Texy\Texy; + +$text = 'This is **bold text** and this is //italic//.'; +$html = $texy->process($text); + +echo $html; +``` + +Output: +```latte +<p>This is <strong>bold text</strong> and this is <em>italic</em>.</p> +``` + +The `process()` method processes the entire text including block elements (paragraphs, headings, lists, tables...). + + +Single-line Text +---------------- + +If you're processing only single-line text without block elements (such as database headings or short descriptions): + +```php +$texy = new Texy\Texy; + +$text = 'Link to "homepage":https://example.com'; +$html = $texy->processLine($text); + +echo $html; +``` + +Output: +```latte +Link to <a href="https://example.com">homepage</a> +``` + +The `processLine()` method doesn't wrap the output in a `<p>` paragraph and processes only inline elements. + + +Basic Configuration +=================== + +Texy works out of the box, but you'll often want to adjust the basic settings. + + +Setting Image Paths +------------------- + +If you're using relative paths to images, set the root directory: + +```php +$texy = new Texy\Texy; + +// Web path (prepended to relative URLs) +$texy->imageModule->root = '/images/'; + +// Physical disk path (for determining dimensions) +$texy->imageModule->fileRoot = __DIR__ . '/public/images/'; +``` + +Now when you write `[* photo.jpg *]`, Texy will generate `<img src="/images/photo.jpg">` and automatically determine the image dimensions. + + +Setting Link Paths +------------------ + +Similarly, you can set the root directory for links: + +```php +$texy->linkModule->root = '/articles/'; +``` + + +Enabling and Disabling Syntaxes +------------------------------- + +Each part of the Texy syntax can be disabled or enabled using the `$allowed` array: + +```php +$texy = new Texy\Texy; + +// Disable images +$texy->allowed['image'] = false; + +// Disable HTML tags in input +$texy->allowed['html/tag'] = false; + +// Enable emoticons (disabled by default) +$texy->allowed['emoticon'] = true; +``` + +A complete list of syntax options can be found in [configuration | configuration#allowed]. + + +Safe Mode for User Content +-------------------------- + +If you're processing content from users (comments, forum posts), use safe mode: + +```php +$texy = new Texy\Texy; +Texy\Configurator::safeMode($texy); + +$userInput = $_POST['comment']; +$html = $texy->process($userInput); +``` + +SafeMode: +- Allows only **safe HTML tags** (`<strong>`, `<em>`, `<a>`, ...) +- Disables **classes and IDs** +- Disables **inline styles** +- Disables **images** +- Adds `rel="nofollow"` to all links +- Filters **URL schemes** (only `http:`, `https:`, `ftp:`, `mailto:`) + +More about security in the chapter [Configuration – Security |configuration#Security]. + + +Complete Example +================ + +```php +require __DIR__ . '/vendor/autoload.php'; + +$texy = new Texy\Texy; + +// Configuration +$texy->imageModule->root = '/images/'; +$texy->linkModule->root = '/'; +$texy->allowed['html/tag'] = false; + +// Text to process +$text = ' + + +Article Heading +=============== + +This is an **introductory paragraph** with a link to "homepage":https://example.com. + +- First item +- Second item +- Third item + +[* photo.jpg .(Photograph) *] +'; + +// Processing +$html = $texy->process($text); + +// Output +echo $html; + +// Additional information +echo "Page title: " . $texy->headingModule->title; +print_r($texy->summary['links']); +print_r($texy->summary['images']); +``` + +After processing, you have access to: +- `$texy->headingModule->title` – first heading (suitable for `<title>`) +- `$texy->summary['links']` – array of all used links +- `$texy->summary['images']` – array of all used images + + +Next Steps +========== + +Now you know how to use Texy. Continue with: + +- **[Configuration | configuration]** – detailed settings for all modules +- **[Syntax | syntax]** – learn the Texy markup +- **[Architecture | architecture]** – understand how Texy works internally diff --git a/texy/en/syntax-full.texy b/texy/en/syntax-full.texy deleted file mode 100644 index 2df749bac6..0000000000 --- a/texy/en/syntax-full.texy +++ /dev/null @@ -1,889 +0,0 @@ -Detailed Description of Syntax -****************************** - - -- [#Philosophy] -- [#Paragraphs of Text] -- [#Headings] -- [#Horizontal lines] -- [#Code] -- [#Turning off the Texy] -- [#Quotes] -- [#Links] -- [#Images] -- [#Phrases] -- [#Direct HTML] -- [#Lists] -- [#Modifiers] -- [#Typography] -- [#Long Words Hyphenation] -- [#Tables] - - -Philosophy -========== - -The Texy tool was created to allow inexperienced users to easily edit the content of web pages. Therefore, the syntax is maximally intuitive. The intention is that the text in pure (unformatted) form is clear and its format can be guessed. - -Today, Texy also serves well-experienced HTML experts. Allows you to freely combine Texy notation with HTML tags. Thus, experienced users do not have to learn a new meta-language and make full use of their knowledge. Texy only simplifies their work. - -The primary logic of the syntax is ** not to use any syntax **. Just write plain text. Inserting advanced information, such as CSS classes or links, does not disrupt the flow of text. And it is written in a way that even non-technical users can easily understand. - - -Paragraphs of Text -================== - -A paragraph is considered to be one or more consecutive lines of text. The paragraphs are separated by a blank line. - -/--code texy -First paragraph lorem ipsum dolor sit amet. - -Second paragraph, který tvoří jeden řádek. -And second line of paragraph. Texy will join the lines. -\-- - -/--texysource -First paragraph lorem ipsum dolor sit amet. - -Second paragraph, který tvoří jeden řádek. -And second line of paragraph. Texy will join the lines. -\-- - -*In the edit box (textarea) is not a division into two lines of paragraph evident. That's why Texy considers them one paragraph.* - -To wrap a line in a paragraph, insert one space to the left: - -/--code texy -April is the cruellest month, breeding - Lilacs out of the dead land, mixing - Memory and desire, stirring - Dull roots with spring rain. -\-- - -/--texysource -April is the cruellest month, breeding - Lilacs out of the dead land, mixing - Memory and desire, stirring - Dull roots with spring rain. -\-- - - -Headings -======== - -Headings can be written in two ways: **underlined** or **surrounded**. - -Each headline has its own degree. In the case of **underlined**, the importance of the title is decided by the underline character. From the highest to the lowest, these are: `#` `*` `=` `-` - -/--code texy -Head Title -********** - - -Subtitle -======== -\-- - -/--texysource -Head Title -********** - - -Subtitle -======== -\-- - -For **surrounded** titles, the level determines the number of preceding characters. It can be `#` nebo `=` - -The following applies: the more characters, the more important the title (minimum two characters, maximum seven). - -/--code texy -=== Head title === - -## Subtitle -\-- - -As you can see in the case of the subtitle, the characters on the right can be omitted. - -*Subtitle levels are always relative only! So Texy finds the highest caption used and relatively differentiates the other captions.* - - -Horizontal Lines -================ - -Texy knows these notations: - -/--code texy ------------- - -******** -\-- - -/--texysource -------------- - -******** -\-- - - -Code -==== - -Used to insert source code. Syntax highlighting can also be activated using an add-on module. - -/--code texy - /---code php - function reImage($matches) { - $content = $matches[1]; - $align = $matches[5]; - $href = $matches[6]; - } - \--- -\-- - -/--texysource - /---code php - function reImage($matches) { - $content = $matches[1]; - $align = $matches[5]; - $href = $matches[6]; - } - \--- -\-- - -*Note the words `php` to indicate the language.* - - -Turning Off the Texy -==================== - -The keyword `html` or` text` affects whether the content will be understood as HTML (including tags) or plain text. - -/--code texy - /---html - <em>example</em>: **this is not strong** - \--- - - - /---text - <em>example</em>: **this is not strong** - \--- -\-- - -To turn off Texy inline, it is possible to use a double apostrophe `''` and wrap a part of the text that is not to be Texy processed. - -/--code texy - Example: ''**this is not strong**'' -\-- - - -Division into Blocks (Div) -========================== - -Use this ability to create more complex documents. - -/--code texy - /---div .[header] - - content of div - - \--- -\-- - -/--texysource - /---div .[header] - - content of div - - \--- -\-- - - -It is also possible to nest blocks: - -/--code texy - /---div .[header] - - ## This is a header. - - /---div - inner div - \--- - - Texy is sexy! - - \--- -\-- - -/--texysource - /---div .[header] - - ## This is a header. - - /---div - inner div - \--- - - Texy is sexy! - - \--- -\-- - - -Quotes -====== - -Quotes are indented, similarly to emails, by a character `>` - -/--code texy -> This is a blockquote with two paragraphs. -> -> 640 K should be enough for everyone -\-- - -/--texysource -> This is a blockquote with two paragraphs. -> -> 640 K should be enough for everyone -\-- - - -Links -===== - -Links are written by enclosing the referencing text in quotation marks, followed by a colon and a URL. Texy tries to intelligently guess the end of the URL. You can also help it by enclosing the URL in square brackets. The `http://` section is optional. - -It is also possible to insert emails as a link, Texy transforms them into a form that should confuse spambots. - -/--code texy -Look at homepage:[https://texy.info]. - -Do you know "La Trine":https://www.latrine.cz? - -"Write me":me@example.com -\-- - -/--texysource -Look at homepage:[https://texy.info]. - -Do you know "La Trine":https://www.latrine.cz? - -"Write me":me@example.com -\-- - - -References ----------- - -In order not to "pollute" the text flow by inserting URLs, it is possible to list all addresses in one place and then just link to them. This is called a reference. In addition to the address, it is possible to add the text of the link and the [modifier | #modifier]. - -/--code texy - [homepage]: https://texy.info/ Texy .(homepage) - [nette]: http://nette.org - -This is [homepage] - -Look at "this site":[nette] -\-- - - -Images -====== - -They are written between square brackets with an asterisk: - -/--code texy -[* image.gif *] -\-- - -/--texysource line -[* image.gif *] -\-- - -In text paragraphs, you often need to choose whether the image should be left-aligned or right-aligned. To do this, use the `<` and `>` character used before the right parenthesis: - -/--code texy -[* image.gif <] Left-aligned image - -[* image.gif >] Right-aligned image -\-- - -/--texysource -[* image.gif <] Left-aligned image - -[* image.gif >] Right-aligned image -\-- - -*Note: In the example above, Texy used a straight CSS style for alignment. It is possible to configure the system to assign a selected class to images instead.* - -*Note: it is possible to set a default directory for all (relative) image URLs. In the examples given, it was `images/`, so in Texy code the directory is not listed, while in the generated HTML it is.* - -*Note: if no alternative text is specified by default (as shown below), Texy will use the default. Here's a simple `image`* - - -Dimensions ----------- - -For local images, Texy detects the dimensions automatically. If you want to specify them manually, enter them as follows: - -/--code texy -[* image.gif 10x20 *] -\-- - -/--texysource line -[* image.gif 10x20 *] -\-- - - -Modifiers ---------- - -You can learn more about them in [another chapter | #modifier], but it doesn't hurt to show how they're written on images. Let's try a modifier to specify alternate text and class: - -/--code texy -[* image.gif .(alt text)[photo] *] -\-- - -/--texysource line -[* image.gif .(alt text)[photo] *] -\-- - - -Reference ---------- - -For the same reasons as for links, images can also be written using references. You need to define URLs (or multiple URLs separated by `|`) and possibly modifiers. - -/--code texy -What a beautiful girl [* picture*] ! - -[* picture*]: image.gif .(my girl) -\-- - -/--texysource -What a beautiful girl [* picture*] ! - -[* picture*]: image.gif .(my girl) -\-- - - -Figure with Caption -------------------- - -Enter three asterisks after the image, followed by a caption: - - -/--code texy -[* image.gif *] *** This is the *caption* below the image -\-- - -/--texysource -[* image.gif *] *** This is the *caption* below the image -\-- - - -Phrases -======= - -Probably the most used syntax in Texy. In almost all cases, a double character is used. - -/--code texy -//italics// - -*too italics* - -**bold** - -***heavy bold*** - -superscript^2 vs. subscript_2 -\-- - -/--texysource -//italics// - -*too italics* - -**bold** - -***heavy bold*** - -superscript^2 vs. subscript_2 -\-- - -/--div .[output] -//italics// - -*too italics* - -**bold** - -***heavy bold*** - -superscript^2 vs. subscript_2 -\-- - -A special case of a phrase is the so-called code. It differs from the other phrases in that its content will no longer be formatted and will be displayed literally: - -/--code texy -This is `<br />` and entity `&ndash` -\-- - -/--texysource -This is `<br />` and entity `&ndash` -\-- - -*Note: whether the `<code>` element is used or another (or none) can be changed simply by configuring Texy.* - - -With Modifier -------------- - -It is possible to insert it into each phrase, always just before the closing character: - -/--code texy -**strong and green .{color:green}** like Hulk -\-- - -/--texysource -**strong and green .{color:green}** like Hulk -\-- - -/--div .[output] -**strong and green .{color:green}** like Hulk -\-- - - -Direct HTML -=========== - -Texy is not a substitute for HTML. It also doesn't look for alternative ways to write HTML. The goal is to simplify content writing. If you find it easier to write a structure directly in HTML, you can do so. HTML tags are fully supported. - -/--code texy -This <strong class=info>is strong</strong> text. -<br> This is not. -\-- - -/--texysource -This <strong class=info>is strong</strong> text. -<br> This is not. -\-- - -*Note: note that Texy adjusts the notation of attributes and tags to be valid (even for XHTML output). It also pays attention to the **well-formed notation**!* - -*Note: Deciding which tags and which attributes can be used in the text is fully user configurable. This demonstrates one example from the distribution.* - - -Lists -===== - -Bulleted lists are written using `*`, `+` or `-`. It must be written at the very beginning of the line and followed by a space. - -/--code texy -- Red -- Green -- Blue -\-- - -/--texysource -- Red -- Green -- Blue -\-- - -/--div .[output] -- Red -- Green -- Blue -\-- - - -Numbered Lists --------------- - -Texy knows these five ways of writing (the first two are equivalent): - -/--code texy -1) Learn -2) Learn -3) Learn - -a) Long -b) Wide -c) Shortsighted - -A) DOS -B) Windows -C) Linux - -I) Yesterday -II) Today -III) Tomorrow -\-- - -/--texysource -1) Learn -2) Learn -3) Learn - -a) Long -b) Wide -c) Shortsighted - -A) DOS -B) Windows -C) Linux - -I) Yesterday -II) Today -III) Tomorrow -\-- - -/--div .[output] -1) Learn -2) Learn -3) Learn - -a) Long -b) Wide -c) Shortsighted - -A) DOS -B) Windows -C) Linux - -I) Yesterday -II) Today -III) Tomorrow -\-- - - -Nested Lists ------------- - -/--code texy -a) Bird - I) Bird - - Red - - Green - - Blue - II) McHale - III) Parish -b) McHale -c) Parish - 1) Bird - 2) McHale - 3) Parish -\-- - -/--texysource -a) Bird - I) Bird - - Red - - Green - - Blue - II) McHale - III) Parish -b) McHale -c) Parish - 1) Bird - 2) McHale - 3) Parish -\-- - - -Definition List ---------------- - -/--code texy -Wild Bill concert: - - date: 9 December 2004 - - place: Vodová Hall, Brno - - price: 260 CZK -\-- - -/--texysource -Wild Bill concert: - - date: 9 December 2004 - - place: Vodová Hall, Brno - - price: 260 CZK -\-- - -/--div .[output] -Wild Bill concert: - - date: 9 December 2004 - - place: Vodová Hall, Brno - - price: 260 CZK -\-- - - -With Modifiers --------------- - -The modifier that affects the entire list is listed in the line before it. Others (classic) at the end of the line: - -/--code texy -.{color:red} -triangl: .{color:blue} - - triangle .{color:green} - - untuned percussion musical instrument - - tringulation tower -\-- - -/--div .[output] -.{color:red} -triangl: .{color:blue} - - triangle .{color:green} - - untuned percussion musical instrument - - tringulation tower -\-- - - -Modifiers .[#modifier] -====================== - -Texy's most powerful weapon. The following types of modifiers can be used: - -- (caption) adds a title to the object (or alternative text to images) -- `[class1 class2 #id]` specifying the class and / or ID of the element -- {class: blue} direct style notation -- {target: _blank} or direct entry of HTML attributes -- horizontal alignment: - - left < - - right > - - centered <> - - to block = - -- vertical alignment: (only for tables) - - up ^ - - center - - - down _ - -Modifiers are written continuously (without spaces) and **must be preceded by a dot**. So for example `.(description)[left]` sets the title attribute to `description` and the class to `left`. - -**Modifiers are always written rightmost**. - -Example of applying a modifier to a paragraph of text: - -/--code texy -Centered with a modifier .<> - -Colored by a modifier .{color:blue; lang: cs} -\-- - -/--texysource -Centered with a modifier .<> - -Colored by a modifier .{color:blue; lang: cs} -\-- - - -Typography .[#typography] -========================= - -This includes all modifications and replacements of the text that modify its appearance in accordance with typographic rules and the like: - -/--code texy -- "English" 'typographic' quotation marks -- dash vs. hyphen: 10-15 vs. česko-slovenský -- dash: one -- two -- typographic cross for dimensions 10 x 20 -- arrows <- and -> and <-> ; -- three dots... -- preservation of HTML entities & -- replacing (TM) or (R) with the relevant entities (C) -\-- - -/--div .[output] -- "English" 'typographic' quotation marks -- dash vs. hyphen: 10-15 vs. česko-slovenský -- dash: one -- two -- typographic cross for dimensions 10 x 20 -- arrows <- and -> and <-> ; -- three dots... -- preservation of HTML entities & -- replacing (TM) or (R) with the relevant entities (C) -\-- - -Spaces handling: - -/--code texy -- inserting unbreakable spaces for one-letter prepositions (a car) -- unbreakable spaces for telephone numbers +420 776 552 046 -\-- - -/--code html -inserting unbreakable spaces for one-letter prepositions (a car) - -unbreakable spaces for telephone numbers +420 776 552 046 -\-- - -*Note: Replacements are usually governed by other rules that determine when -symbol replace and when not. For example, the arrow `->` cannot be at the end of a line, etc. So don't be surprised if in some cases Texy doesn't make the substitution.* - - -Abbreviations, Acronyms ------------------------ - -Double parenthetical notation is used: - -/--code texy -one word: NATO((North Atlantic Treaty Organisation)) - -multiword: "et al."((and more)) -\-- - -/--texysource -one word: NATO((North Atlantic Treaty Organisation)) - -multiword: "et al."((and more)) -\-- - - -Clickable Web Addresses ------------------------ - -Automatic conversion of web addresses and emails into a clickable form - -/--code texy -more information at www.texy.info and also ... -\-- - -/--div .[output] -more information at www.texy.info and also ... -\-- - - -Long Words Hyphenation -====================== - -Very interesting and important function of Texy. Long words can disrupt the appearance of the page, so it's a good idea to tell your browser where to wrap them. Texy searches for these places taking into account national customs, so he divides the word according to syllables: - -/--code texy -nejneobhospodařovávatelnějšími -\-- - -/--code html -nejneobhospoda­řovávatelnější­mi</p -\-- - -*Note: the word length limit is optional* - - -Tables .[#table] -================ - -Example of a simple table, columns are separated by a character `|` - -/--code texy -| first col | second col | third col -| Adam | Eva | Franta -\-- - -And the result is: - -| first col | second col | third col -| Adam | Eva | Franta - - -The table header can be defined with this notation: - -/--code texy -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Jesus | Christ | 33 -| Cecilie | Svobodova | 74 -\-- - -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Jesus | Christ | 33 -| Cecilie | Svobodova | 74 - -If the header does not form a row (rows), we can define it at the cell level. Just insert an asterisk immediately after the `|` character - - -/--code texy -|* First Name | Jesus | Cecilie -|* Last Name | Christ | Svobodova -|* Age | 33 | 74 -\-- - - -|* First Name | Jesus | Cecilie -|* Last Name | Christ | Svobodova -|* Age | 33 | 74 - - -Merging Columns ---------------- - -Notice the double `||`: - -/--code texy -|----------------------------- -| Name || Age -|---------------------------- -| Jesus | Christ | 33 -\-- - -|----------------------------- -| Name || Age -|---------------------------- -| Jesus | Christ | 33 - - -Merging Rows ------------- - -Notice the `^` character symbolizing the upward direction: - - -/--code texy -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Bill || 50 -| ^| 52 -| Jim | Beam | 70 -\-- - -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Bill || 50 -| ^| 52 -| Jim | Beam | 70 - - -Modifiers ---------- - -The following rules apply: -- a modifier affecting the whole table is inserted immediately before the table -- the modifier affecting the line is inserted at the end of the line -- the modifier affecting the column is inserted at the beginning of the cell (left in the cell) -- and finally the modifier affecting the cell is inserted at the end of the cell (right in the cell) - -Take a look at an example. - -/--code texy -.(people) -| .{color: green} first col | second col .>| third col | .{font-style:italic} -| Adam | Eva .{color: blue}| Franta | -\-- - -There is: -- `.(people)` table modifier -- `.{color: green}` column modifier -- `.{font-style:italic}` line modifier -- `.{color: blue}` a také `.>` cell modifier - -So the resulting table looks like this: - - -.(people) -| .{color: green} first col | second col .>| third col | .{font-style:italic} -| Adam | Eva .{color: blue}| Franta | diff --git a/texy/en/syntax.texy b/texy/en/syntax.texy index 2e07405d68..5ad42c4062 100644 --- a/texy/en/syntax.texy +++ b/texy/en/syntax.texy @@ -1,463 +1,891 @@ Syntax ****** ---> "Detailed syntax description":syntax-full +.[perex] +Texy was created to allow inexperienced users to easily edit website content. Therefore, its syntax is intuitive and clear. + + +Cheat Sheet +=========== + +| [Text Formatting |#Text formatting] | Syntax +|----------------------------------------------- +| [Bold text |#Text formatting] | .[text-code] ''**bold text**'' +| [Italics |#Text formatting] | ''*italics*'' or ''//italics//'' +| [Inline code |#Text formatting] | ''`code`'' +| [#Links] | ''"text":URL'' or ''[text](URL)'' +| [#Images] | ''[* image.jpg *]'' +| [#Disabling Formatting] | ''special characters'' +|----------------------------------------------- +| Elements +|----------------------------------------------- +| [#Underlined Headings] | H1 <br> === +| [#Surrounded Headings] | ''### H1'' <br> ## H2 +| [#Bulleted Lists] | ''- first'' <br> ''- second'' +| [#Numbered Lists] | ''1) first'' <br> ''2) second'' +| [#Definition Lists] | term: <br>   ''- first'' +| [#Blockquotes] | ''> blockquote'' +| [#Horizontal Rules] | ''---'' +| [#Tables] | ''\| cell \| cell \|'' +| [Code Blocks |#Preformatted text] | ''/--'' <br> ... <br> ''\--'' +|----------------------------------------------- +| Modifiers .[#toc-modifiers] +|-------------------------------------------------------- +| title | ''.(title)'' +| CSS class | ''.[btn btn-primary]'' +| ID | ''.[#id]'' +| CSS style or HTML attribute | ''.{color: blue}'' or ''.{target: _blank}'' +| horizontal alignment | ''.< .> .<> .='' +| vertical alignment | ''.^ .- ._'' + + +Paragraphs of text +================== +Texy considers a paragraph to be one or more consecutive lines of text. As soon as you leave **one blank line** between them, Texy automatically understands that it should start a new paragraph. -The Texy tool was created to allow inexperienced users to easily edit the content of web pages. Therefore, the syntax is maximally intuitive. The intention is that the text in pure (unformatted) form is clear and its format can be guessed. +This means that Texy will join lines that belong together. You don't have to worry about a sentence breaking in the middle when you shrink the editor window. -Today, Texy also serves well-experienced HTML experts. Allows you to freely combine Texy notation with HTML tags. Thus, experienced users do not have to learn a new meta-language and make full use of their knowledge. Texy only simplifies their work. +```texy +This is the first paragraph. It can easily have multiple lines, +and Texy will join them into one continuous block of text. -The primary logic of the syntax is **not to use any syntax**. Just write plain text. Inserting advanced information, such as CSS classes or links, does not disrupt the flow of text. And it is written in a way that even non-technical users can easily understand. +Only here, after a blank line, does a completely new, second paragraph begin. +``` +However, line merging can be disabled in the configuration, after which each line is considered a separate paragraph: -Paragraphs .[#paragraph] -======================== +/--php +$texy->mergeLines = false; +\-- -A paragraph is considered to be one or more consecutive lines of text. The paragraphs are separated by a blank line. -/--code texy -First paragraph lorem ipsum dolor sit amet. +Line Breaks +----------- -Second paragraph, který tvoří jeden řádek. -And second line of paragraph. Texy will join the lines. -\-- +But what if you just need to break a line without creating a whole new paragraph? This is typically useful for poems, song lyrics, or when writing an address. **Start the new line with a single space**. -To wrap a line in a paragraph, insert one space to the left: +```texy +Karel Novák, + U Tiché pošty 5 + 150 00 Praha 5 +``` -/--code texy -April is the cruellest month, breeding - Lilacs out of the dead land, mixing - Memory and desire, stirring - Dull roots with spring rain. -\-- +Styling Paragraphs +------------------ -Headings .[#heading] -==================== +Sometimes you need to distinguish an entire paragraph—for example, to make it a lead paragraph of an article, center it, or assign it a specific style for a border. This is where [#modifiers] come in, which you can place either on a separate line **before** the paragraph or at the end of its last line. -Headings can be written in two ways: **underlined** or **surrounded**. +```texy +.[perex] +This is the lead paragraph of the article, which, thanks to the modifier, +will get the CSS class "perex" and can thus look different from the rest of the text. -Each headline has its own degree. In the case of **underlined**, the importance of the title is decided by the underline character. From the highest to the lowest, these are: `#` `*` `=` `-` +This paragraph, in turn, has a unique ID assigned. .[#section-intro] -/--code texy -Head title -********** +And this paragraph will be centered. .<> +``` -Subtitle -======== +Text formatting +=============== + +| syntax | output | Syntax ID +|----------------------------------------------------------------------------- +| .[text-code] ''**bold text**'' | **bold text** | `phrase/strong` +| ''*italics* or //italics//'' | *italics* | `phrase/em-alt`, `phrase/em` +| ''***bold italics***'' | ***bold italics*** | `phrase/strong+em` +| ''`inline code`'' | `inline code` | `phrase/code` +| ''x^2 … O_2'' | x^2 … O_2 | `phrase/sup-alt`, `phrase/sub-alt` +| ''x^^2^^ … O__2__'' | x^2 … O_2 | `phrase/sup`🔸, `phrase/sup`🔸 +| ''++inserted text++'' | <ins>inserted text</ins> | `phrase/ins`🔸 +| ''--deleted text--'' | <del>deleted text</del> | `phrase/del`🔸 +| ''>>quoted text<<'' | >>quoted text<< | `phrase/quote` +| ''"blue text .{color: blue}"'' | "blue text .{color: blue}" | `phrase/span` +| ''~blue text .{color: blue}~'' | ~blue text .{color: blue}~ | `phrase/span-alt` +| ''"et al."((and others))'' | "et al."((and others)) | `phrase/acronym` +| ''NBA((National Basketball Association))'' | NBA((National Basketball Association)) | `phrase/acronym-alt` + +Syntaxes marked with 🔸 are not enabled by default and you must turn them on. For example: + +/--php +$texy->allowed['phrase/ins'] = true; \-- -For **surrounded** titles, the level determines the number of preceding characters. It can be `#` nebo `=` +For simple numerical indices, you can use the shorthand syntax `x^2` and `O_2`, but for more complex cases, the double-character variant is more robust, or you can use the HTML tags `<sup>` and `<sub>`. -The following applies: the more characters, the more important the title (minimum two characters, maximum seven). +There **must not be spaces** inside the syntax characters: -/--code texy -=== Head title === +```texy +Wrong: ** this will not be bold ** +Correct: **this will be bold** +``` -## Subtitle -\-- -As you can see in the case of the subtitle, the characters on the right can be omitted. +Styling Text +------------ +This is one of Texy's most powerful features. You can "attach" [#modifiers] to any formatted text to add a CSS class, ID, or direct style. The modifier is always inserted **just before the closing tag**: -Horizontal Lines .[#horizline] -============================== +```texy +This text is **strong and green .{color:green}** like the Hulk. -Texy knows these notations: +Warning: --This feature is deprecated .[deprecated]-- +``` +If you want to apply a modifier to text without making it bold or italic, use quotation marks `"` or tildes `~` as the enclosing characters. Texy will then create a universal HTML `<span>` tag with your styles: -/--code texy ------------- +```texy +Regular text, but "this piece is red .{color: red}", and the rest is not. +``` -******** -\-- +Formatting and Links in One +--------------------------- -Turning Off the Texy .[#disable-Texy] -===================================== +You can turn formatted text into a link - simply add a colon and the URL: -The keyword `html` or` text` affects whether the content will be understood as HTML (including tags) or plain text. +```texy +Visit our **new gallery**:https://example.com/gallery +``` -/--code texy - /---html - <em>example</em>: **this is not strong** - \--- +This works for bold text, italics, and inline code. - /---text - <em>example</em>: **this is not strong** - \--- -\-- +Writing Special Characters +-------------------------- -To turn off Texy inline, it is possible to use a double apostrophe `''` and wrap a part of the text that is not to be Texy processed. +What if you want to write `**text**` literally, including the asterisks, without it becoming bold text? You have three options: -/--code texy - Example: ''**this is not strong**'' -\-- +- a backslash is the quickest way to escape a single special character `\**text\**` +- double apostrophes [disable Texy|#Disabling Formatting] for the entire phrase `''**text**''` +- you can use standard HTML entities `**text**` -Quotes .[#blockquote] -===================== +Links +===== -Quotes are indented, similarly to emails, by a character `>` +Links are the soul of the internet. In Texy, their creation is designed to be as natural and clear as possible directly within the text. -/--code texy -> This is a blockquote with two paragraphs. -> -> 640 K should be enough for everyone -\-- +The basic syntax for a link is simple and highly readable. Enclose the linked text in `"` (or other characters for [#text formatting]) and immediately append a colon and the target URL: +```texy +Visit the official website of the "Nette Framework":https://nette.org. -Links .[#link] -============== +If you have a question, "email us":info@example.com. +``` -Links are written by enclosing the referencing text in quotation marks, followed by a colon and a URL. Texy tries to intelligently guess the end of the URL. You can also help it by enclosing the URL in square brackets. The `http://` section is optional. +The advantage is that Texy is intelligent and automatically recognizes where the URL ends. So you don't have to worry about it accidentally including a period or comma at the end of a sentence in the link. However, if the URL contains non-standard characters, you can enclose it in square brackets to precisely define the start and end of the address: -It is also possible to insert emails as a link, Texy transforms them into a form that should confuse spambots. +```texy +"Read our article":[https://example.com/news?id=1&category=articles] +``` -/--code texy -Look at homepage:[https://texy.info]. +Syntax ID `phrase/span`, `phrase/span-alt` | [PhraseModule |configuration#phrasemodule] and [LinkModule |configuration#linkmodule] -Do you know "La Trine":https://www.latrine.cz? -"Write me":me@example.com -\-- +Alternative Link Syntax +----------------------- +Are you used to the format used by Markdown or Wikipedia? Texy understands them too. You can choose the style that suits you best. -Images .[#image] -================ +```texy +[Link text](https://address.com) // Style known from Markdown +[Link text | https://address.com] // Style known from MediaWiki +text:[target URL or reference] // Single-word link +``` -They are written between square brackets with an asterisk: +Syntax ID `phrase/markdown`, `phrase/wikilink`, `phrase/quicklink` | [PhraseModule |configuration#phrasemodule] -/--code texy -[* image.gif .(alternative text) *] -\-- +Organizing Links with References +-------------------------------- + +When writing longer texts, it can be inconvenient to insert long URLs directly into paragraphs—it can harm readability and clarity. For these cases, Texy has **reference links**. + +In the text, you use only a short, easily memorable reference name. And at the end of the document, you define all these references clearly. + +```texy +We recommend studying the "official documentation":[doc] and going through the "syntax examples":[syntax]. +The entire project is built on [Nette]. + +​[doc]: https://texy.nette.org/en/ "Texy Documentation!" +​[syntax]: https://texy.nette.org/en/syntax +​[Nette]: https://nette.org +``` -In text paragraphs, you often need to choose whether the image should be left-aligned or right-aligned. To do this, use the `<` and `>` character used before the right parenthesis: +Syntax ID `link/reference`, `link/definition` | [LinkModule |configuration#linkmodule] -/--code texy -[* image.gif <] Left-aligned image. Lorem ipsum ... -[* image.gif >] Right-aligned image. Curabitur quam ... +Automatic Links +--------------- + +Whenever you write a URL (starting with `http://`, `https://`, `www.`) or an email address in the text, Texy will automatically recognize it and convert it into a clickable link. You don't have to do anything at all. + +```texy +You can find our website at www.example.com. +For support, write to support@example.com. +``` + +Syntax ID `link/url`, `link/email` | [LinkModule |configuration#linkmodule] + + +Styling Links +------------- + +With [#modifiers], you can easily add other properties to links: + +```texy +"External link .[external](Opens in a new window){target:_blank}":https://google.com +``` + +The special class `nofollow` adds the `rel="nofollow"` attribute to the link, signaling to search engines not to follow this link. This is useful, for example, for links in comments. + +```texy +"A link I don't trust .[nofollow]":https://example.com +``` + + +Automatic Email Masking +----------------------- + +Texy automatically obfuscates (masks) email addresses from spambots: + +```latte +<a href="mailto:info@example.com">info@<!-- -->example.com</a> +``` + +You can disable this behavior: + +/--php +$texy->obfuscateEmail = false; \-- -Figure with Caption +Direct HTML +=========== + +Texy is designed so that you don't have to write HTML at all. But what if you encounter a situation where inserting a direct HTML tag is simpler, or you need to create something that Texy's syntax doesn't cover? No problem. Texy gives you complete freedom to combine both worlds. + +You can seamlessly switch between Texy syntax and pure HTML whenever it suits you. + +```texy +This is **bold text** in Texy and this is <strong>bold text</strong> using HTML. + +<div class="info-box"> + <h3>You can also insert entire complex blocks</h3> +</div> +``` + +You might think that inserting direct HTML can be risky. What if you make a mistake or someone inserts malicious code? Texy thinks about this and acts as an intelligent filter and helper: + +- **Corrects errors:** Texy ensures that the resulting code is always valid and won't break your page. +- **Monitors security:** By default, Texy has a list of allowed tags and their attributes. If an unknown tag or a potentially dangerous attribute (e.g., `onclick`) appears in the code, Texy will safely remove it. This protects your website from XSS attacks. +- **Ensures a consistent output:** No matter what HTML code you insert, Texy will make sure the result is always well-formed. + +You can customize this protective shield. Using the `$texy->allowedTags` configuration, you can precisely define which HTML tags and attributes are allowed on your website and which are not. + +This gives you full control over what HTML, for example, editors can use, ensuring the consistency and security of the entire site. For more information, see the "configuration":configuration#allowedtags section. + +Syntax ID `html/tag`, `html/comment` | [HtmlModule |configuration#htmlmodule] + + +Headings +======== + +Texy offers you two elegant and intuitive ways to create headings: **underlined** and **surrounded**. + + +Underlined Headings ------------------- -Enter three asterisks after the image, followed by a caption: +This style is reminiscent of a typewriter. Simply place an underline (at least 3 characters) below the heading. The importance of the title is determined by the underlining character. From highest to lowest, these are: `#` `*` `=` `-` +```texy +This is the most important heading of the entire document +​################################################ -/--code texy -[* image.gif *] *** This is the *caption* below the image -\-- +And this is a second-level heading +​****************************** +``` +Syntax ID `heading/underlined` | [HeadingModule |configuration#headingmodule] -Phrases .[#phrase] -================== -Probably the most used syntax in Texy. In almost all cases, a double character is used. +Surrounded Headings +------------------- -/--code texy -//italics// +This method is very quick to write. You "wrap" the heading text between `#` or `=` characters. Here, the level of the heading is determined by the **number** of characters used (2 to 7). The more characters, the more important the heading. -**bold** +```texy +=== Most important heading (H1) -x^2 + y^3 -\-- +== Less important (H2) +# Even less important (H3) +``` -/--div .[output] -//italics// +You can use borders on both sides (for better visual clarity) or just at the beginning. Texy can handle both variants. -**bold** +Syntax ID `heading/surrounded` | [HeadingModule |configuration#headingmodule] -x^2 + y^3 -\-- +Styling Headings +---------------- -Texts can also be temporarily turned off - the content will not be formatted and will be displayed literally: +You can add [#modifiers] to any heading. This allows you to assign it a specific CSS class for styling or a unique ID that you can then link to. -/--code texy -Remove ''<br />'' and entity ''&ndash'' -\-- +```texy +Heading with red color .[red-heading] +​========================================== +### Heading with a unique ID for linking .[#contact] +``` -Direct HTML .[#html] -==================== -Texy is not a substitute for HTML. It also doesn't look for alternative ways to write HTML. The goal is to simplify content writing. If you find it easier to write a structure directly in HTML, you can do so. HTML tags are fully supported. +Automatic Anchors for Easy Navigation +------------------------------------- -/--code texy -This <strong class=info>is strong</strong> text. -<br> This is not. -\-- +Don't want to come up with an ID for every heading manually? Texy can do it for you! In the configuration, you can enable automatic ID generation for all headings. This is incredibly useful for directly linking to specific sections. +```php +// Enable automatic ID generation +$texy->headingModule->generateID = true; -Lists .[#list] -============== +// Optionally set a prefix for generated IDs (e.g., "toc-") +$texy->headingModule->idPrefix = 'toc-'; +``` -Bulleted lists are written using `*`, `+` or `-`. It must be written at the very beginning of the line and followed by a space. +With this setting, the heading `## My Chapter` will automatically get an ID like `id="toc-my-chapter"` without you having to write anything extra. -/--code texy -- Red -- Green -- Blue -\-- + +Lists +===== + + +Bulleted Lists +-------------- + +For a quick list of items where the order doesn't matter, a bulleted list is perfect. Just start each line with a hyphen `-`, an asterisk `*`, or a plus sign `+`, followed by a space. All three characters work the same, so you can choose the one you prefer. + +```texy +What needs to be bought: + +- Milk +- Bread +* Eggs ++ Butter +``` + +ID syntaxe `list` | [ListModule |configuration#listmodule] Numbered Lists -------------- -Texy knows these five ways of writing (the first two are equivalent): +Texy supports various numbering styles: -/--code texy -1) Learn -2) Learn -3) Learn +| `1.` | Arabic numerals (with a period) +| `1)` | Arabic numerals (with a parenthesis) +| `a)` | Lowercase letters of the alphabet +| `A)` | Uppercase letters of the alphabet +| `I)` | Roman numerals -a) Long -b) Wide -c) Shortsighted +The beauty of this is that you don't have to worry about correct numbering at all. Even if you number all the lines with a one, Texy will automatically renumber them for you. This is a huge advantage when you later need to add, delete, or move an item. -A) DOS -B) Windows -C) Linux -I) Yesterday -II) Today -III) Tomorrow -\-- +Nested and Combined Lists +------------------------- +The power of lists truly shines when you combine and nest them. This allows you to create clear, multi-level structures. You create nesting simply by indenting the line by at least **two spaces** (or one tab). -Nested Lists ------------- +```texy +1) First chapter + a) Subchapter 1.1 + - First point + - Second point + b) Subchapter 1.2 +2) Second chapter + - Main idea + - Another note +``` -/--code texy -a) Bird - I) Bird - - Red - - Green - - Blue - II) McHale - III) Parish -b) McHale -c) Parish - 1) Bird - 2) McHale - 3) Parish -\-- +Definition Lists +---------------- -Definition List ---------------- +For cases where you need to create a glossary of terms or clearly explain several terms, a definition list is ideal. -Wild Bill concert: - - date: 9 December 2004 - - place: Vodová Hall, Brno - - price: 260 CZK +On the first line, write the term you want to define and end it with a colon. On the following lines, write its definition, indenting each line and starting it with a hyphen `-`. -/--code texy -Wild Bill concert: - - date: 9 December 2004 - - place: Vodová Hall, Brno - - price: 260 CZK -\-- +```texy +HTML: + - Markup language for creating web pages. + - Abbreviation for HyperText Markup Language. +CSS: + - Language for describing the presentation (styling) of pages. + - Abbreviation for Cascading Style Sheets. +``` -Modifiers .[#modifier] -====================== +Syntax ID `list/definition` | [ListModule |configuration#listmodule] -Texy's most powerful weapon. The following types of modifiers can be used: -- (caption) adds a title to the object (or alternative text to images) -- `[class1 class2 #id]` specifying the class and / or ID of the element -- {class: blue} direct style notation -- {target: _blank} or direct entry of HTML attributes -- horizontal alignment: - - left < - - right > - - centered <> - - to block = +Styling Lists +------------- -- vertical alignment: (only for tables) - - up ^ - - center - - - down _ +Just like with other elements in Texy, you can easily add [#modifiers] to lists to change their appearance. -Modifiers are written continuously (without spaces) and **must be preceded by a dot**. So for example `.(description)[left]` sets the title attribute to `description` and the class to `left`. +**Entire list:** Write the modifier on the line **before** the start of the list. -**Modifiers are always written rightmost**. +```texy +.[colored-list] +- First item +- Second item +``` -Example of applying a modifier to a paragraph of text: +**Individual item:** Add the modifier to the **end** of the line of the given item or definition term. -/--code texy -Centered with a modifier .<> +```texy +- Regular item +- This item is important! .{font-weight: bold} +- Another regular item +``` -Colored by a modifier .{color:blue; lang: cs} -\-- -/--code html -<p style="text-align:center">Centered with a modifier</p> +Images +====== -<p style="color:blue" lang="cs">Colored by a modifier</p> -\-- +The basic syntax is very simple. Just enclose the path to the image (whether a local file or a URL) in square brackets with an asterisk: +```texy +[* image.jpg *] +[* https://domain.com/logo.png *] +``` -Typography .[#typography] -========================= +Often you will want the text to wrap around the image. For this, there are simple alignment tags that are inserted before the closing bracket: -This includes all modifications and replacements of the text that modify its appearance in accordance with typographic rules and the like: +```texy +[* image.jpg <] This text will flow smoothly around the image from the right side. -/--code texy -- "English" 'typographic' quotation marks -- dash vs. hyphen: 10-15 vs. česko-slovenský -- dash: one -- two -- typographic cross for dimensions 10 x 20 -- arrows <- and -> and <-> ; -- three dots... -- preservation of HTML entities & -- replacing (TM) or (R) with the relevant entities (C) -\-- +[* image.jpg >] In this case, the text will instead flow around the image from the left side. + +[* large-image.jpg <>] +This text will continue below the centered image. +``` + +A properly inserted image should also have "alternative text," which is displayed if the image fails to load. Using a [modifier|#modifiers], you can add this text and other elements for styling. + +```texy +[* landscape-photo.jpg .(A beautiful mountain landscape at sunset)[main-photo] *] +``` + + +Image Dimensions +---------------- + +Texy can automatically detect the dimensions of local images (if the `$texy->imageModule->fileRoot` path is set) and add them to the HTML, which speeds up page loading. However, if you want to set the dimensions manually, you have several options: + +| `[* img.jpg 150x100 *]` | Exact width 150px and height 100px +| `[* img.jpg 150 *]` | Width will be 150px, height will be automatically calculated while maintaining the aspect ratio +| `[* img.jpg ?x100 *]` | Height will be 100px, width will be automatically calculated + + +Clickable Images +---------------- + +Do you want a large image to be displayed when a small thumbnail is clicked? Or for an image to link to another page? Just add a colon and the target URL after the image syntax. + +```texy +[* thumbnail.jpg *]:large.jpg +[* nette-logo.png *]:https://nette.org +``` + +For galleries, there is also a handy shortcut `::`. This automatically creates a link to the same file located at `$texy->imageModule->linkedRoot`. + + +Visible Caption Below the Image +------------------------------- + +If you want to add a visible caption under an image (e.g., the author's name or a description of the scene), write three asterisks `***` and the caption text after it. Texy will automatically create a semantically correct HTML structure `<figure>` and `<figcaption>` from this. + +```texy +[* photo.jpg <> *] *** This is a caption. It can also contain **other formatting**. +``` + + +Organizing Images with References +--------------------------------- + +If you use one image multiple times in the text or want to have all image definitions neatly in one place, you can use references. In the text, you use only a placeholder name, and at the end of the file, you define what this name means. + +```texy +In our logo [* company-logo *] you can see the symbol of our vision. -/--div .[output] -- "English" 'typographic' quotation marks -- dash vs. hyphen: 10-15 vs. česko-slovenský -- dash: one -- two -- typographic cross for dimensions 10 x 20 -- arrows <- and -> and <-> ; -- three dots... -- preservation of HTML entities & -- replacing (TM) or (R) with the relevant entities (C) +​[* company-logo *]: /images/logo.svg 200x50 .(Our company logo) +``` + +This approach significantly clarifies the main text and simplifies image management. + +Syntax ID `image/definition` | [ImageModule |configuration#imagemodule] + + +Preformatted text +================= + +In Texy, you can easily insert blocks of code or any preformatted text where you want to ensure it is displayed exactly as you write it—including all spaces and line breaks. This is ideal for examples of source code, logs, or ASCII art. + +To insert such a block, use the `/--` and `\--` delimiters: + +```texy +/-- +function hello() { + echo 'Hello World'; +} \-- +``` -Spaces handling: +To make your code even more readable, you can tell Texy which programming language it is written in and create a handler that, for example, syntax highlights it, see the "example":custom-handlers#syntax-highlighting. Just add the keyword `code` and the language name after the initial `/--` tag: -/--code texy -- inserting unbreakable spaces for one-letter prepositions (a car) -- unbreakable spaces for telephone numbers +420 776 552 046 +```texy +/--code javascript +console.log('JavaScript'); \-- /--code html -inserting unbreakable spaces for one-letter prepositions (a car) - -unbreakable spaces for telephone numbers +420 776 552 046 +<div>This is HTML code</div> \-- +``` -*Note: Replacements are usually governed by other rules that determine when -symbol replace and when not. For example, the arrow `->` cannot be at the end of a line, etc. So don't be surprised if in some cases Texy doesn't make the substitution.* +Content Blocks (divs) +===================== -Abbreviations, Acronyms .[#acronym] ------------------------------------ +Texy allows you to create generic `<div>` blocks, thanks to which you can easily group content into logical units and then style them. -Double parenthetical notation is used: +You create a block using the `/--div` and `\--` tags. In addition, you can easily add [#modifiers]: -/--code texy -one word: NATO((North Atlantic Treaty Organisation)) +```texy +/--div .[important] +## Important Notice -multiword: "et al."((and more)) +This text will be enclosed in a `<div class="important">` block. +This allows you to style it with CSS to make it stand out. \-- +``` +The power of `<div>` blocks also lies in the ability to nest them. This allows you to create even more complex structures directly in Texy without having to write HTML manually. -Clickable Web Addresses ------------------------ +```texy +/--div .[outer] + This is the outer block. -Automatic conversion of web addresses and emails into a clickable form + /--div .[inner] + And this is a nested, inner block. + \-- -/--code texy -more information at www.texy.info and also ... + Here we are back in the outer block. \-- +``` +Thanks to this simple syntax, you can keep your content clear and semantically well-structured. -/--div .[output] -more information at www.texy.info and also ... -\-- +Disabling Formatting +==================== + +Sometimes it can be useful to "turn off" Texy for a moment and insert a piece of text where Texy should not process its tags. -Long Words Hyphenation .[#longwords] -==================================== +If you need to insert a more complex HTML structure without Texy parsing the tags, use the `/--html` block: -Very interesting and important function of Texy. Long words can disrupt the appearance of the page, so it's a good idea to tell your browser where to wrap them. Texy searches for these places taking into account national customs, so he divides the word according to syllables: +```texy +/--html +<em>This text will be processed as HTML, so it will be in italics.</em> -/--code texy -nejneobhospodařovávatelnějšími +**But Texy ignores these asterisks, so they won't be bold.** \-- +``` -/--code html -nejneobhospoda­řovávatelnější­mi</p +If you want to display text exactly as it is written, ignoring all tags (both Texy and HTML), use the `/--text` block. Everything inside this block will be displayed as plain text. + +```texy +/--text +<em>This text will be displayed with the tags, but it will not be in italics.</em> + +**This won't be bold either.** \-- +``` -*Note: the word length limit is optional* +But what if you don't want to disable Texy for an entire block of text, but just for a short phrase in the middle of a sentence? For these cases, there is an elegant and quick solution: wrap the text in **double apostrophes** `''`: +```texy +If you want to show how to write bold text, you would write: The syntax is ''**bold text**''. +``` -Tables .[#table] -================ +The result will not be bold text; instead, the string `**bold text**` will be printed literally. -Example of a simple table, columns are separated by a character `|` -/--code texy -| first col | second col | third col -| Adam | Eva | Franta -\-- +Tables +====== -And the result is: +To create a table, start each row with a `|` character and also separate the individual cells with this character. Texy will take care of the alignment and correct HTML on its own. -| first col | second col | third col -| Adam | Eva | Franta +```texy +| Jan | Novák | Praha +| Eva | Svobodová | Brno +``` +The result will be a clear and correctly formatted table. -The table header can be defined with this notation: +Syntax ID `table` | [TableModule |configuration#tablemodule] -/--code texy -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Jesus | Christ | 33 -| Cecilie | Svobodova | 74 -\-- -|----------------------------- -| First Name | Last Name | Age -|---------------------------- -| Jesus | Christ | 33 -| Cecilie | Svobodova | 74 +Table Header +------------ +Every proper table should have a header that describes what is in each column. You create the header by separating it from the rest of the table with a line containing hyphens `-`. -Merging Columns ---------------- +```texy +| Name | Age | City +|----------|-----|------- +| Jan | 25 | Prague +| Eva | 30 | Brno +``` -Notice the double `||`: +Alternatively, you can define headers for individual rows (for example, if you have labels in the first column). You achieve this by adding an asterisk `*` immediately after the initial `|`. -/--code texy -| Name || Age -|---------------------------- -| Jesus | Christ | 33 -\-- +```texy +|* Name | Jan | Eva +|* Age | 25 | 30 +|* City | Praha | Brno +``` + + +Merging Cells +------------- + +Sometimes it is necessary to merge several cells together, either in columns or in rows. -| Name || Age +**Merging columns:** For horizontal cell merging, simply omit the separator and use a double vertical bar `||` instead. The cell to the right will be merged with the cell to its left. + +```texy +| First Name || Age |---------------------------- -| Jesus | Christ | 33 +| Jan | Novák | 25 +``` +**Merging rows:** For vertical cell merging, use the caret symbol `^` in the cell you want to attach to the one above it. This tells Texy: "Merge this cell with the one above it." -Merging Rows ------------- +```texy +| Month | Sales | +|---------|---------- +| January | 150 pcs | +| February | ^| +| March | 210 pcs | +``` -Notice the `^` character symbolizing the upward direction: +In this example, the cell with sales for January and February will be merged. +This way, several cells can be merged across rows and columns: -/--code texy +```texy | First Name | Last Name | Age |---------------------------- | Bill || 50 | ^| 52 | Jim | Beam | 70 -\-- +``` -| First Name | Last Name | Age -|---------------------------- -| Bill || 50 -| ^| 52 -| Jim | Beam | 70 + +Styling Tables +-------------- + +As with other elements in Texy, you can also add [#modifiers] to tables and their parts to change their appearance (e.g., CSS classes, styles, or IDs). + +**Entire table:** Place the modifier for the entire table on a separate line just before it. + +```texy +.[data-table table-striped] +| Header 1 | Header 2 +|------------|------------ +| data | data +``` + +**Individual rows:** If you want to style a specific row, add the modifier to its end. + +```texy +| Name | Status +|-------|-------------- +| Petr | Approved +| Jana | Rejected | .{background: #ffdddd} +``` + +**Individual columns:** To style an entire column, insert the modifier at the beginning of the first cell of that column. + +```texy +| Name | .> Price | In Stock +|----------------|-----------|--------- +| Product A | 1 200 Kč | Yes +| Product B | 850 Kč | No +``` + +**Specific cell:** Write the modifier for a single cell directly in it, usually at the end of its content. + +```texy +| Task | Status +|----------------------|------------------------------------- +| Prepare documents | Done +| Check data | In progress .{color: orange; font-weight: bold} +``` + + +Blockquotes +=========== + +If you need to emphasize someone else's idea in your text, cite a source, or just visually separate a block of text, simply start the line with the `>` character. + +```texy +> This is a blockquote. It serves to highlight an important idea or an excerpt from another source. +``` + +A blockquote doesn't have to be just one paragraph. If you want to continue with another paragraph within the same blockquote, simply insert a blank line that also starts with the `>` character. + +```texy +> This is the first paragraph of the blockquote. Lorem ipsum dolor sit amet. +> +> And this is the second paragraph, which still belongs to the same blockquote. +> This way you can structure even longer texts. +``` + +Texy even supports nested blockquotes, which is useful if you are quoting someone who is quoting someone else. For each additional level of nesting, add another `>` character. + +```texy +> This is the outer, main blockquote. +> +> > And this is a nested, second-level blockquote. +> +> Here the text returns to the main blockquote. +``` + +Inside blockquotes, you can of course use other formatting, such as **bold text** or *italics*. + + +Horizontal Rules +================ + +Sometimes it is necessary to visually divide the text. A horizontal rule is great for this. On a separate line, write three or more hyphens `---` or asterisks `***`. + +```texy +The first part of the text on a certain topic. + +*** + +The second part of the text, which begins after the visual separation. +``` + +To create a horizontal rule, it **must be preceded by a blank line**. If you were to write it immediately after the text, Texy would think you wanted to create an underlined heading. + +Syntax ID `horizline` | [HorizLineModule |configuration#horizlinemodule] + + +Typography +========== + +The power of Texy lies not only in formatting, but also in automatic typographic corrections. Texy takes care of the details that make text professional and easy to read, all according to Czech typographic rules. This allows you to focus solely on the content. + +**Quotes:** You don't have to worry about how to type correct typographic quotes on the keyboard. Texy will do it for you. + +It automatically converts classic ''"typewriter quotes"'' into the correct English “quotes” and nested ‘quotes’. The type of quotes depends on the locale setting: + +```php +$texy->typographyModule->locale = 'cs'; // Czech +$texy->typographyModule->locale = 'en'; // English +``` + +**Dashes and hyphens:** It intelligently recognizes when to use a short hyphen (in hyphenated words) and when to use a longer en dash—for example, in ranges (10–15) or between words. + +```texy +10-15 → 10–15 (en dash for ranges) +czech-slovak → czech-slovak (hyphen remains) +word -- word → word – word (en dash between words) +word --- word → word — word (em dash) +``` + +**Non-breaking spaces**: One of the biggest advantages is the automatic insertion of non-breaking spaces where needed. This prevents single-letter words (like `a` or `I`) from being left alone at the end of a line, which is a common typographic error. + +```texy +// You write: +I visited a castle in Prague. + +// Texy ensures that "a" never remains at the end of a line: +I visited a castle in Prague. +``` + +It also takes care of correct spacing in phone numbers or dates to prevent them from breaking. + +```texy ++420 776 552 046 → +420 776 552 046 (all spaces non-breaking) +``` + +**Automatic symbols:** Texy also makes it easier for you to write frequently used symbols. + +| You write | Texy generates | Description +|----- +| `...` | … | Ellipsis +| `(c)` | © | Copyright +| `(r)` | ® | Registered trademark +| `(tm)` | ™ | Trademark +| `10 x 5` | 10 × 5 | Multiplication sign +| `+-` | ± | Plus-minus +| `<-` `->` `<->` | ← → ↔ | Arrows + +Thanks to these automatic adjustments, your text will always look professional without you having to know complex keyboard shortcuts or HTML entities. + + +Hyphenation of Long Words +------------------------- + +You know it—a long word appears in the text, such as "antidisestablishmentarianism," and on a narrow mobile phone screen, it breaks the entire page layout. Fortunately, Texy offers an elegant solution: it can insert invisible "soft hyphens" (`­`) into the word. These hyphens tell the browser where (between syllables) it can safely break the word if it doesn't fit at the end of the line. If the word fits on the line, the hyphens remain hidden and nothing happens. + +```latte +antidisestablish­mentarianism +``` + +Thanks to this, your text will always adapt beautifully to any screen width without unwanted horizontal scrolling. + +Because this feature is not suitable for all types of websites, it is disabled by default. You can activate it in the configuration: + +```php +$texy->allowed['longwords'] = true; + +// Set the minimum word length from which to hyphenate (e.g., 20 characters) +$texy->longWordsModule->wordLimit = 20; +``` + +Syntax ID `longwords` | [LongWordsModule |configuration#long-wordsmodule] + + +Emoticons +========= + +Texy can automatically convert classic text smileys into graphical emoticons. Simply type the smiley as you are used to, and Texy will take care of the rest. + +| You write | Texy generates +|----- +| `:-)` | 🙂 +| `:-(` | ☹ +| `;-)` | 😉 +| `:-D` | 😀 +| `:-*` | 😘 + +Depending on the configuration, Texy can convert these shortcuts either to modern Unicode emoji (as in the table above) or to small images (`<img>`). + +To prevent unwanted conversions, for example in technical texts, this feature is disabled by default. If you want to use it, you just need to enable it: + +```php +$texy->allowed['emoticon'] = true; +``` + +More information about available emoticons and configuration options can be found in the [EmoticonModule configuration |configuration#emoticonmodul]. + +Syntax ID `emoticon` | [EmoticonModule |configuration#emoticonmodule] From 6a97a0c69d0ad1260bff34cb29d8b66ef729b244 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Fri, 31 Oct 2025 03:02:48 +0100 Subject: [PATCH 02/25] removed null as an array offset --- application/bg/routing.texy | 2 +- application/cs/routing.texy | 4 ++-- application/de/routing.texy | 2 +- application/el/routing.texy | 2 +- application/en/routing.texy | 4 ++-- application/es/routing.texy | 2 +- application/fr/routing.texy | 2 +- application/hu/routing.texy | 2 +- application/it/routing.texy | 2 +- application/ja/routing.texy | 2 +- application/pl/routing.texy | 2 +- application/pt/routing.texy | 2 +- application/ro/routing.texy | 2 +- application/ru/routing.texy | 2 +- application/sl/routing.texy | 2 +- application/tr/routing.texy | 2 +- application/uk/routing.texy | 2 +- utils/bg/arrays.texy | 4 ++-- utils/cs/arrays.texy | 4 ++-- utils/de/arrays.texy | 4 ++-- utils/el/arrays.texy | 4 ++-- utils/en/arrays.texy | 4 ++-- utils/es/arrays.texy | 4 ++-- utils/fr/arrays.texy | 4 ++-- utils/hu/arrays.texy | 4 ++-- utils/it/arrays.texy | 4 ++-- utils/ja/arrays.texy | 4 ++-- utils/pl/arrays.texy | 4 ++-- utils/pt/arrays.texy | 4 ++-- utils/ro/arrays.texy | 4 ++-- utils/ru/arrays.texy | 4 ++-- utils/sl/arrays.texy | 4 ++-- utils/tr/arrays.texy | 4 ++-- utils/uk/arrays.texy | 4 ++-- 34 files changed, 53 insertions(+), 53 deletions(-) diff --git a/application/bg/routing.texy b/application/bg/routing.texy index f6d0f09e15..d3a66e32fa 100644 --- a/application/bg/routing.texy +++ b/application/bg/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/cs/routing.texy b/application/cs/routing.texy index 826bed4363..3a2e753731 100644 --- a/application/cs/routing.texy +++ b/application/cs/routing.texy @@ -306,7 +306,7 @@ Parametry `presenter`, `action` a `module` už mají předdefinované filtry, kt Obecné filtry ------------- -Vedle filtrů určených pro konkrétní parametry můžeme definovat též obecné filtry, které obdrží asociativní pole všech parametrů, které mohou jakkoliv modifikovat a poté je vrátí. Obecné filtry definujeme pod klíčem `null`. +Vedle filtrů určených pro konkrétní parametry můžeme definovat též obecné filtry, které obdrží asociativní pole všech parametrů, které mohou jakkoliv modifikovat a poté je vrátí. Obecné filtry definujeme pod prázdným klíčem. ```php use Nette\Routing\Route; @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/de/routing.texy b/application/de/routing.texy index 251247119d..ff2f7378e0 100644 --- a/application/de/routing.texy +++ b/application/de/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/el/routing.texy b/application/el/routing.texy index 4886294f8b..31800ef023 100644 --- a/application/el/routing.texy +++ b/application/el/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/en/routing.texy b/application/en/routing.texy index daa1c181d8..4b83f317f2 100644 --- a/application/en/routing.texy +++ b/application/en/routing.texy @@ -306,7 +306,7 @@ The parameters `presenter`, `action`, and `module` already have predefined filte General Filters --------------- -Besides filters intended for specific parameters, we can also define general filters that receive an associative array of all parameters, which they can modify in any way and then return. General filters are defined under the key `null`. +Besides filters intended for specific parameters, we can also define general filters that receive an associative array of all parameters, which they can modify in any way and then return. General filters are defined under the empty key. ```php use Nette\Routing\Route; @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/es/routing.texy b/application/es/routing.texy index 829edb3c7d..d906f83f48 100644 --- a/application/es/routing.texy +++ b/application/es/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/fr/routing.texy b/application/fr/routing.texy index 41a575cac4..66bfebc31b 100644 --- a/application/fr/routing.texy +++ b/application/fr/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/hu/routing.texy b/application/hu/routing.texy index 0887f12764..4fb937eba5 100644 --- a/application/hu/routing.texy +++ b/application/hu/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/it/routing.texy b/application/it/routing.texy index c308d57f62..36b8f5968c 100644 --- a/application/it/routing.texy +++ b/application/it/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/ja/routing.texy b/application/ja/routing.texy index d3c524f7ab..4dd36be16c 100644 --- a/application/ja/routing.texy +++ b/application/ja/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/pl/routing.texy b/application/pl/routing.texy index 49f68aa093..34577ae078 100644 --- a/application/pl/routing.texy +++ b/application/pl/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/pt/routing.texy b/application/pt/routing.texy index 659307ca93..6becfb0d3b 100644 --- a/application/pt/routing.texy +++ b/application/pt/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/ro/routing.texy b/application/ro/routing.texy index e0b8840500..0d580d0565 100644 --- a/application/ro/routing.texy +++ b/application/ro/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/ru/routing.texy b/application/ru/routing.texy index dbe0c6a228..4e2896a131 100644 --- a/application/ru/routing.texy +++ b/application/ru/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/sl/routing.texy b/application/sl/routing.texy index dbdd549664..06c2ccee69 100644 --- a/application/sl/routing.texy +++ b/application/sl/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/tr/routing.texy b/application/tr/routing.texy index 5b0b81e30a..8fccd6c0b1 100644 --- a/application/tr/routing.texy +++ b/application/tr/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/application/uk/routing.texy b/application/uk/routing.texy index a9db693215..ab3b132ccd 100644 --- a/application/uk/routing.texy +++ b/application/uk/routing.texy @@ -314,7 +314,7 @@ use Nette\Routing\Route; $router->addRoute('<presenter>/<action>', [ 'presenter' => 'Home', 'action' => 'default', - null => [ + '' => [ Route::FilterIn => function (array $params): array { /* ... */ }, Route::FilterOut => function (array $params): array { /* ... */ }, ], diff --git a/utils/bg/arrays.texy b/utils/bg/arrays.texy index d73bfc5b6e..c2f5884283 100644 --- a/utils/bg/arrays.texy +++ b/utils/bg/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Връща и премахва стойността на елемент от масива. Ако не съществува, хвърля изключение или връща стойността `$default`, ако е посочена. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/cs/arrays.texy b/utils/cs/arrays.texy index 9351bba355..baa1c82d10 100644 --- a/utils/cs/arrays.texy +++ b/utils/cs/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Vrátí a odstraní hodnotu prvku z pole. Pokud neexistuje, vyhodí výjimku, nebo vrátí hodnotu `$default`, pokud je uvedena. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/de/arrays.texy b/utils/de/arrays.texy index 9167248df2..472f4b1c8e 100644 --- a/utils/de/arrays.texy +++ b/utils/de/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Gibt den Wert eines Elements aus dem Array zurück und entfernt ihn. Wenn er nicht existiert, wird eine Ausnahme geworfen, oder der Wert `$default` zurückgegeben, falls angegeben. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/el/arrays.texy b/utils/el/arrays.texy index 835c2658eb..8fdc7e0860 100644 --- a/utils/el/arrays.texy +++ b/utils/el/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Επιστρέφει και αφαιρεί την τιμή του στοιχείου από τον πίνακα. Αν δεν υπάρχει, ρίχνει μια εξαίρεση, ή επιστρέφει την τιμή `$default`, αν έχει καθοριστεί. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/en/arrays.texy b/utils/en/arrays.texy index a14be68d17..c6abf68903 100644 --- a/utils/en/arrays.texy +++ b/utils/en/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, mixed $default=null): mixed .[method] Returns and removes the value of an item with key `$key` from an array. If the item does not exist, it throws an exception, or returns `$default` if provided. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/es/arrays.texy b/utils/es/arrays.texy index 062f6a39d4..39497de06e 100644 --- a/utils/es/arrays.texy +++ b/utils/es/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Devuelve y elimina el valor de un elemento del array. Si no existe, lanza una excepción, o devuelve el valor `$default` si se especifica. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/fr/arrays.texy b/utils/fr/arrays.texy index a084df7709..140b2e2cc8 100644 --- a/utils/fr/arrays.texy +++ b/utils/fr/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Retourne et supprime la valeur de l'élément du tableau. S'il n'existe pas, lève une exception, ou retourne la valeur `$default` si elle est spécifiée. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/hu/arrays.texy b/utils/hu/arrays.texy index 2a76d0ea3b..a25bf3e74f 100644 --- a/utils/hu/arrays.texy +++ b/utils/hu/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Visszaadja és eltávolítja az elem értékét a tömbből. Ha nem létezik, kivételt dob, vagy visszaadja a `$default` értéket, ha meg van adva. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/it/arrays.texy b/utils/it/arrays.texy index 1d60931cee..c2b6c5ae5c 100644 --- a/utils/it/arrays.texy +++ b/utils/it/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Restituisce e rimuove il valore di un elemento dall'array. Se non esiste, lancia un'eccezione o restituisce il valore `$default`, se specificato. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/ja/arrays.texy b/utils/ja/arrays.texy index adb4a9385c..090e2f6646 100644 --- a/utils/ja/arrays.texy +++ b/utils/ja/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] 配列から要素の値を返して削除します。存在しない場合は例外をスローするか、`$default` が指定されている場合はその値を返します。 ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/pl/arrays.texy b/utils/pl/arrays.texy index 03b7309a65..7273b72ee4 100644 --- a/utils/pl/arrays.texy +++ b/utils/pl/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Zwraca i usuwa wartość elementu z tablicy. Jeśli nie istnieje, rzuca wyjątek lub zwraca wartość `$default`, jeśli jest podana. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/pt/arrays.texy b/utils/pt/arrays.texy index a5081dbad6..bef58ccebb 100644 --- a/utils/pt/arrays.texy +++ b/utils/pt/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Retorna e remove o valor de um elemento do array. Se não existir, lança uma exceção ou retorna o valor `$default`, se fornecido. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/ro/arrays.texy b/utils/ro/arrays.texy index 2f998eb83a..cae59f62dd 100644 --- a/utils/ro/arrays.texy +++ b/utils/ro/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Returnează și elimină valoarea unui element din array. Dacă nu există, aruncă o excepție sau returnează valoarea `$default`, dacă este specificată. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/ru/arrays.texy b/utils/ru/arrays.texy index 4ba819ae5b..dec3f21649 100644 --- a/utils/ru/arrays.texy +++ b/utils/ru/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, mixed $default=null): mixed .[method] Возвращает и удаляет значение элемента из массива. Если он не существует, выбрасывает исключение, или возвращает значение `$default`, если оно указано. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/sl/arrays.texy b/utils/sl/arrays.texy index c9c044f98c..9c82a7842c 100644 --- a/utils/sl/arrays.texy +++ b/utils/sl/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Vrne in odstrani vrednost elementa iz polja. Če ne obstaja, sproži izjemo ali vrne vrednost `$default`, če je podana. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/tr/arrays.texy b/utils/tr/arrays.texy index aa7941b653..e47a08a863 100644 --- a/utils/tr/arrays.texy +++ b/utils/tr/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Bir öğenin değerini diziden döndürür ve kaldırır. Eğer mevcut değilse, istisna fırlatır veya belirtilmişse `$default` değerini döndürür. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' diff --git a/utils/uk/arrays.texy b/utils/uk/arrays.texy index 6d3a4bf31f..2a0e7fc547 100644 --- a/utils/uk/arrays.texy +++ b/utils/uk/arrays.texy @@ -360,8 +360,8 @@ pick(array &$array, string|int $key, ?mixed $default=null): mixed .[method] Повертає та видаляє значення елемента з масиву. Якщо він не існує, викликає виняток або повертає значення `$default`, якщо воно вказане. ```php -$array = [1 => 'foo', null => 'bar']; -$a = Arrays::pick($array, null); +$array = [1 => 'foo', 'x' => 'bar']; +$a = Arrays::pick($array, 'x'); // $a = 'bar' $b = Arrays::pick($array, 'not-exists', 'foobar'); // $b = 'foobar' From af993016975890c8233901e1c898ecb3ac1e4fd6 Mon Sep 17 00:00:00 2001 From: Jan Tojnar <jtojnar@gmail.com> Date: Sun, 28 Sep 2025 01:44:13 +0200 Subject: [PATCH 03/25] typo [Closes #1081] --- latte/bg/type-system.texy | 4 ++-- latte/cs/type-system.texy | 4 ++-- latte/de/type-system.texy | 4 ++-- latte/el/type-system.texy | 4 ++-- latte/en/type-system.texy | 4 ++-- latte/es/type-system.texy | 4 ++-- latte/fr/type-system.texy | 4 ++-- latte/hu/type-system.texy | 4 ++-- latte/it/type-system.texy | 4 ++-- latte/ja/type-system.texy | 4 ++-- latte/pl/type-system.texy | 4 ++-- latte/pt/type-system.texy | 4 ++-- latte/ro/type-system.texy | 4 ++-- latte/ru/type-system.texy | 4 ++-- latte/sl/type-system.texy | 4 ++-- latte/tr/type-system.texy | 4 ++-- latte/uk/type-system.texy | 4 ++-- 17 files changed, 34 insertions(+), 34 deletions(-) diff --git a/latte/bg/type-system.texy b/latte/bg/type-system.texy index 8f052cf074..aa1be010a5 100644 --- a/latte/bg/type-system.texy +++ b/latte/bg/type-system.texy @@ -21,7 +21,7 @@ class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -След това в началото на шаблона поставете тага `{templateType}` с пълното име на класа (включително namespace). Това дефинира, че в шаблона има променливи `$langs` и `$products`, включително съответните типове. Типовете на локалните променливи можете да посочите с помощта на таговете [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Дефиниции]. +След това в началото на шаблона поставете тага `{templateType}` с пълното име на класа (включително namespace). Това дефинира, че в шаблона има променливи `$lang` и `$products`, включително съответните типове. Типовете на локалните променливи можете да посочите с помощта на таговете [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Дефиниции]. От този момент IDE може да ви подсказва правилно. diff --git a/latte/cs/type-system.texy b/latte/cs/type-system.texy index 0454b2743d..7dcf6e8dce 100644 --- a/latte/cs/type-system.texy +++ b/latte/cs/type-system.texy @@ -21,7 +21,7 @@ Jak začít používat typy? Vytvořte si třídu šablony, např. `CatalogTempl class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -A dále na začátek šablony vložte značku `{templateType}` s plným názvem třídy (včetně namespace). To definuje, že v šabloně jsou proměnné `$langs` a `$products` včetně příslušných typů. Typy lokálních proměnných můžete uvést pomocí značek [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definice]. +A dále na začátek šablony vložte značku `{templateType}` s plným názvem třídy (včetně namespace). To definuje, že v šabloně jsou proměnné `$lang` a `$products` včetně příslušných typů. Typy lokálních proměnných můžete uvést pomocí značek [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definice]. Od té chvíle vám může IDE správně našeptávat. diff --git a/latte/de/type-system.texy b/latte/de/type-system.texy index 88beabecd4..f8994c6ef0 100644 --- a/latte/de/type-system.texy +++ b/latte/de/type-system.texy @@ -21,7 +21,7 @@ Wie beginnt man mit der Verwendung von Typen? Erstellen Sie eine Template-Klasse class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -Fügen Sie dann am Anfang des Templates das Tag `{templateType}` mit dem vollständigen Klassennamen (einschließlich Namespace) ein. Dies definiert, dass im Template die Variablen `$langs` und `$products` einschließlich der entsprechenden Typen vorhanden sind. Die Typen lokaler Variablen können Sie mit den Tags [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definition] angeben. +Fügen Sie dann am Anfang des Templates das Tag `{templateType}` mit dem vollständigen Klassennamen (einschließlich Namespace) ein. Dies definiert, dass im Template die Variablen `$lang` und `$products` einschließlich der entsprechenden Typen vorhanden sind. Die Typen lokaler Variablen können Sie mit den Tags [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definition] angeben. Von diesem Moment an kann Ihnen die IDE korrekt Vorschläge machen. diff --git a/latte/el/type-system.texy b/latte/el/type-system.texy index 91b4f29239..26dff347f3 100644 --- a/latte/el/type-system.texy +++ b/latte/el/type-system.texy @@ -21,7 +21,7 @@ class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -Και στη συνέχεια, στην αρχή του προτύπου, εισαγάγετε το tag `{templateType}` με το πλήρες όνομα της κλάσης (συμπεριλαμβανομένου του namespace). Αυτό ορίζει ότι στο πρότυπο υπάρχουν οι μεταβλητές `$langs` και `$products` συμπεριλαμβανομένων των αντίστοιχων τύπων τους. Μπορείτε να δηλώσετε τους τύπους των τοπικών μεταβλητών χρησιμοποιώντας τα tags [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Ορισμοί define]. +Και στη συνέχεια, στην αρχή του προτύπου, εισαγάγετε το tag `{templateType}` με το πλήρες όνομα της κλάσης (συμπεριλαμβανομένου του namespace). Αυτό ορίζει ότι στο πρότυπο υπάρχουν οι μεταβλητές `$lang` και `$products` συμπεριλαμβανομένων των αντίστοιχων τύπων τους. Μπορείτε να δηλώσετε τους τύπους των τοπικών μεταβλητών χρησιμοποιώντας τα tags [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Ορισμοί define]. Από εκείνη τη στιγμή, το IDE σας μπορεί να παρέχει σωστή αυτόματη συμπλήρωση. diff --git a/latte/en/type-system.texy b/latte/en/type-system.texy index 8b7dfd642e..eac2758138 100644 --- a/latte/en/type-system.texy +++ b/latte/en/type-system.texy @@ -21,7 +21,7 @@ How to start using types? Create a template class, e.g., `CatalogTemplateParamet class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -Then insert the `{templateType}` tag with the full class name (including the namespace) at the beginning of the template. This defines that the variables `$langs` and `$products` exist in the template, including their respective types. You can also specify the types of local variables using the [`{var}` |tags#var-default], `{varType}`, and [`{define}` |template-inheritance#Definitions] tags. +Then insert the `{templateType}` tag with the full class name (including the namespace) at the beginning of the template. This defines that the variables `$lang` and `$products` exist in the template, including their respective types. You can also specify the types of local variables using the [`{var}` |tags#var-default], `{varType}`, and [`{define}` |template-inheritance#Definitions] tags. From this point on, your IDE can correctly provide autocompletion. diff --git a/latte/es/type-system.texy b/latte/es/type-system.texy index b3b4097727..d1cacbcb12 100644 --- a/latte/es/type-system.texy +++ b/latte/es/type-system.texy @@ -21,7 +21,7 @@ Los tipos declarados son informativos y Latte no los verifica en este momento. class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -Y luego, al principio de la plantilla, inserte la etiqueta `{templateType}` con el nombre completo de la clase (incluido el namespace). Esto define que en la plantilla existen las variables `$langs` y `$products` con sus tipos correspondientes. Puede indicar los tipos de las variables locales usando las etiquetas [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definiciones define]. +Y luego, al principio de la plantilla, inserte la etiqueta `{templateType}` con el nombre completo de la clase (incluido el namespace). Esto define que en la plantilla existen las variables `$lang` y `$products` con sus tipos correspondientes. Puede indicar los tipos de las variables locales usando las etiquetas [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definiciones define]. A partir de ese momento, su IDE puede sugerir correctamente. diff --git a/latte/fr/type-system.texy b/latte/fr/type-system.texy index 8419906504..0cac1c1ac0 100644 --- a/latte/fr/type-system.texy +++ b/latte/fr/type-system.texy @@ -21,7 +21,7 @@ Comment commencer à utiliser les types ? Créez une classe de template, par exe class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -Ensuite, au début du template, insérez la balise `{templateType}` avec le nom complet de la classe (y compris le namespace). Cela définit que les variables `$langs` et `$products` existent dans le template, y compris leurs types respectifs. Vous pouvez spécifier les types des variables locales à l'aide des balises [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Définitions]. +Ensuite, au début du template, insérez la balise `{templateType}` avec le nom complet de la classe (y compris le namespace). Cela définit que les variables `$lang` et `$products` existent dans le template, y compris leurs types respectifs. Vous pouvez spécifier les types des variables locales à l'aide des balises [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Définitions]. À partir de ce moment, l'IDE peut correctement vous faire des suggestions. diff --git a/latte/hu/type-system.texy b/latte/hu/type-system.texy index b2dcceee31..b2e09d3024 100644 --- a/latte/hu/type-system.texy +++ b/latte/hu/type-system.texy @@ -21,7 +21,7 @@ Hogyan kezdjük el használni a típusokat? Hozzon létre egy sablonosztályt, p class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -Ezután a sablon elejére illessze be a `{templateType}` taget az osztály teljes nevével (beleértve a névteret is). Ez definiálja, hogy a sablonban a `$langs` és `$products` változók a megfelelő típusokkal együtt léteznek. A lokális változók típusait a [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definíciók define] tagekkel adhatja meg. +Ezután a sablon elejére illessze be a `{templateType}` taget az osztály teljes nevével (beleértve a névteret is). Ez definiálja, hogy a sablonban a `$lang` és `$products` változók a megfelelő típusokkal együtt léteznek. A lokális változók típusait a [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definíciók define] tagekkel adhatja meg. Ettől kezdve az IDE helyesen tud súgni. diff --git a/latte/it/type-system.texy b/latte/it/type-system.texy index ae98829c21..a5fb971b3c 100644 --- a/latte/it/type-system.texy +++ b/latte/it/type-system.texy @@ -21,7 +21,7 @@ Come iniziare a usare i tipi? Create una classe di template, ad es. `CatalogTemp class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -Quindi, all'inizio del template, inserite il tag `{templateType}` con il nome completo della classe (incluso il namespace). Questo definisce che nel template ci sono le variabili `$langs` e `$products` con i rispettivi tipi. Potete specificare i tipi delle variabili locali usando i tag [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definizioni define]. +Quindi, all'inizio del template, inserite il tag `{templateType}` con il nome completo della classe (incluso il namespace). Questo definisce che nel template ci sono le variabili `$lang` e `$products` con i rispettivi tipi. Potete specificare i tipi delle variabili locali usando i tag [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definizioni define]. Da quel momento, l'IDE può suggerire correttamente. diff --git a/latte/ja/type-system.texy b/latte/ja/type-system.texy index 74a8f15022..0f93ddc900 100644 --- a/latte/ja/type-system.texy +++ b/latte/ja/type-system.texy @@ -21,7 +21,7 @@ class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -次に、テンプレートの先頭に、クラスの完全な名前(名前空間を含む)を持つ `{templateType}` タグを挿入します。これにより、テンプレート内に変数 `$langs` と `$products` が、対応する型とともに定義されます。ローカル変数の型は、[`{var}` |tags#var default]、`{varType}`、[`{define}` |template-inheritance#Definitions] タグを使用して指定できます。 +次に、テンプレートの先頭に、クラスの完全な名前(名前空間を含む)を持つ `{templateType}` タグを挿入します。これにより、テンプレート内に変数 `$lang` と `$products` が、対応する型とともに定義されます。ローカル変数の型は、[`{var}` |tags#var default]、`{varType}`、[`{define}` |template-inheritance#Definitions] タグを使用して指定できます。 その時点から、IDEは正しく補完できるようになります。 diff --git a/latte/pl/type-system.texy b/latte/pl/type-system.texy index 3715e6a9d5..9f19e935d9 100644 --- a/latte/pl/type-system.texy +++ b/latte/pl/type-system.texy @@ -21,7 +21,7 @@ Jak zacząć używać typów? Utwórz klasę szablonu, np. `CatalogTemplateParam class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -A następnie na początku szablonu wstaw tag `{templateType}` z pełną nazwą klasy (włącznie z namespace). To definiuje, że w szablonie są zmienne `$langs` i `$products` wraz z odpowiednimi typami. Typy zmiennych lokalnych możesz podać za pomocą tagów [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definicje define]. +A następnie na początku szablonu wstaw tag `{templateType}` z pełną nazwą klasy (włącznie z namespace). To definiuje, że w szablonie są zmienne `$lang` i `$products` wraz z odpowiednimi typami. Typy zmiennych lokalnych możesz podać za pomocą tagów [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definicje define]. Od tego momentu IDE może poprawnie podpowiadać. diff --git a/latte/pt/type-system.texy b/latte/pt/type-system.texy index 110047b4b4..9baf8bb9f8 100644 --- a/latte/pt/type-system.texy +++ b/latte/pt/type-system.texy @@ -21,7 +21,7 @@ Como começar a usar tipos? Crie uma classe de template, por exemplo, `CatalogTe class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -E, em seguida, no início do template, insira a tag `{templateType}` com o nome completo da classe (incluindo o namespace). Isso define que no template existem as variáveis `$langs` e `$products`, incluindo os tipos correspondentes. Você pode especificar os tipos de variáveis locais usando as tags [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definições]. +E, em seguida, no início do template, insira a tag `{templateType}` com o nome completo da classe (incluindo o namespace). Isso define que no template existem as variáveis `$lang` e `$products`, incluindo os tipos correspondentes. Você pode especificar os tipos de variáveis locais usando as tags [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definições]. A partir desse momento, o IDE pode sugerir corretamente. diff --git a/latte/ro/type-system.texy b/latte/ro/type-system.texy index fc64e5ef84..508abcd9ee 100644 --- a/latte/ro/type-system.texy +++ b/latte/ro/type-system.texy @@ -21,7 +21,7 @@ Cum să începeți să utilizați tipurile? Creați o clasă de șablon, de exem class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -Și apoi, la începutul șablonului, introduceți tag-ul `{templateType}` cu numele complet al clasei (inclusiv namespace). Acest lucru definește că în șablon există variabilele `$langs` și `$products` inclusiv tipurile corespunzătoare. Tipurile variabilelor locale pot fi specificate folosind tag-urile [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definiții define]. +Și apoi, la începutul șablonului, introduceți tag-ul `{templateType}` cu numele complet al clasei (inclusiv namespace). Acest lucru definește că în șablon există variabilele `$lang` și `$products` inclusiv tipurile corespunzătoare. Tipurile variabilelor locale pot fi specificate folosind tag-urile [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definiții define]. Din acel moment, IDE-ul vă poate oferi sugestii corecte. diff --git a/latte/ru/type-system.texy b/latte/ru/type-system.texy index 875667cde2..ab6a5a5d6f 100644 --- a/latte/ru/type-system.texy +++ b/latte/ru/type-system.texy @@ -21,7 +21,7 @@ class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -А затем в начало шаблона вставьте тег `{templateType}` с полным именем класса (включая пространство имен). Это определяет, что в шаблоне есть переменные `$langs` и `$products` с соответствующими типами. Типы локальных переменных можно указать с помощью тегов [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Определения]. +А затем в начало шаблона вставьте тег `{templateType}` с полным именем класса (включая пространство имен). Это определяет, что в шаблоне есть переменные `$lang` и `$products` с соответствующими типами. Типы локальных переменных можно указать с помощью тегов [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Определения]. С этого момента IDE сможет правильно подсказывать. diff --git a/latte/sl/type-system.texy b/latte/sl/type-system.texy index 09f661778b..e814b63fd0 100644 --- a/latte/sl/type-system.texy +++ b/latte/sl/type-system.texy @@ -21,7 +21,7 @@ Kako začeti uporabljati tipe? Ustvarite si razred predloge, npr. `CatalogTempla class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -Nato na začetek predloge vstavite značko `{templateType}` s polnim imenom razreda (vključno z imenskim prostorom). To definira, da so v predlogi spremenljivke `$langs` in `$products` vključno z ustreznimi tipi. Tipe lokalnih spremenljivk lahko navedete s pomočjo značk [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definicije]. +Nato na začetek predloge vstavite značko `{templateType}` s polnim imenom razreda (vključno z imenskim prostorom). To definira, da so v predlogi spremenljivke `$lang` in `$products` vključno z ustreznimi tipi. Tipe lokalnih spremenljivk lahko navedete s pomočjo značk [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Definicije]. Od takrat vam lahko IDE pravilno predlaga. diff --git a/latte/tr/type-system.texy b/latte/tr/type-system.texy index 005ce71f91..eaf4424ee5 100644 --- a/latte/tr/type-system.texy +++ b/latte/tr/type-system.texy @@ -21,7 +21,7 @@ Tipleri kullanmaya nasıl başlanır? İletilen parametreleri, tiplerini ve muht class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -Ve ardından şablonun başına `{templateType}` etiketini sınıfın tam adıyla (ad alanı dahil) ekleyin. Bu, şablonda `$langs` ve `$products` değişkenlerinin ilgili tipleriyle birlikte bulunduğunu tanımlar. Yerel değişkenlerin tiplerini [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Tanımlar] etiketlerini kullanarak belirtebilirsiniz. +Ve ardından şablonun başına `{templateType}` etiketini sınıfın tam adıyla (ad alanı dahil) ekleyin. Bu, şablonda `$lang` ve `$products` değişkenlerinin ilgili tipleriyle birlikte bulunduğunu tanımlar. Yerel değişkenlerin tiplerini [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Tanımlar] etiketlerini kullanarak belirtebilirsiniz. O andan itibaren IDE size doğru önerilerde bulunabilir. diff --git a/latte/uk/type-system.texy b/latte/uk/type-system.texy index 9d6c08535b..14ea265b99 100644 --- a/latte/uk/type-system.texy +++ b/latte/uk/type-system.texy @@ -21,7 +21,7 @@ class CatalogTemplateParameters { public function __construct( - public string $langs, + public string $lang, /** @var ProductEntity[] */ public array $products, public Address $address, @@ -35,7 +35,7 @@ $latte->render('template.latte', new CatalogTemplateParameters( )); ``` -А далі на початку шаблону вставте тег `{templateType}` з повною назвою класу (включаючи простір імен). Це визначає, що в шаблоні є змінні `$langs` та `$products` з відповідними типами. Типи локальних змінних можна вказати за допомогою тегів [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Визначення]. +А далі на початку шаблону вставте тег `{templateType}` з повною назвою класу (включаючи простір імен). Це визначає, що в шаблоні є змінні `$lang` та `$products` з відповідними типами. Типи локальних змінних можна вказати за допомогою тегів [`{var}` |tags#var default], `{varType}`, [`{define}` |template-inheritance#Визначення]. З цього моменту ваше IDE може правильно підказувати. From b07d9ff5844bb57b26d220a8213f81e4c4c98510 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Wed, 26 Nov 2025 14:24:57 +0100 Subject: [PATCH 04/25] latte 3.1.0 --- application/cs/templates.texy | 2 +- application/en/templates.texy | 2 +- latte/cs/@left-menu.texy | 1 + latte/cs/cookbook/@home.texy | 1 + .../cs/cookbook/migration-from-latte-30.texy | 109 +++++++++++++ latte/cs/custom-filters.texy | 24 --- latte/cs/custom-tags.texy | 2 +- latte/cs/develop.texy | 32 +++- latte/cs/extending-latte.texy | 7 - latte/cs/filters.texy | 41 +++++ latte/cs/html-attributes.texy | 151 ++++++++++++++++++ latte/cs/syntax.texy | 39 ++++- latte/cs/tags.texy | 19 ++- latte/en/@left-menu.texy | 1 + latte/en/cookbook/@home.texy | 1 + .../en/cookbook/migration-from-latte-30.texy | 110 +++++++++++++ latte/en/custom-filters.texy | 24 --- latte/en/custom-tags.texy | 2 +- latte/en/develop.texy | 32 +++- latte/en/extending-latte.texy | 7 - latte/en/filters.texy | 41 +++++ latte/en/html-attributes.texy | 151 ++++++++++++++++++ latte/en/syntax.texy | 39 ++++- latte/en/tags.texy | 21 ++- latte/files/html-attributes.webp | Bin 0 -> 11126 bytes 25 files changed, 772 insertions(+), 87 deletions(-) create mode 100644 latte/cs/cookbook/migration-from-latte-30.texy create mode 100644 latte/cs/html-attributes.texy create mode 100644 latte/en/cookbook/migration-from-latte-30.texy create mode 100644 latte/en/html-attributes.texy create mode 100644 latte/files/html-attributes.webp diff --git a/application/cs/templates.texy b/application/cs/templates.texy index 4ded503e34..feb0346ea4 100644 --- a/application/cs/templates.texy +++ b/application/cs/templates.texy @@ -215,7 +215,7 @@ public function beforeRender(): void // nebo konfigurujeme přímo objekt Latte\Engine $latte = $this->template->getLatte(); - $latte->addFilterLoader(/* ... */); + $latte->setFeature(Latte\Feature::MigrationWarnings); } ``` diff --git a/application/en/templates.texy b/application/en/templates.texy index 2950a8f35c..90f641d221 100644 --- a/application/en/templates.texy +++ b/application/en/templates.texy @@ -215,7 +215,7 @@ public function beforeRender(): void // or configure the Latte\Engine object directly $latte = $this->template->getLatte(); - $latte->addFilterLoader(/* ... */); + $latte->setFeature(Latte\Feature::MigrationWarnings); } ``` diff --git a/latte/cs/@left-menu.texy b/latte/cs/@left-menu.texy index 31a85bcaf7..4556b26913 100644 --- a/latte/cs/@left-menu.texy +++ b/latte/cs/@left-menu.texy @@ -5,6 +5,7 @@ - [Dědičnost šablon |Template Inheritance] - [Typový systém |type-system] - [Sandbox] + - [HTML attributy |html-attributes] - Pro designéry 🎨 - [Syntaxe |syntax] diff --git a/latte/cs/cookbook/@home.texy b/latte/cs/cookbook/@home.texy index b4d54b0b7e..b6af15e673 100644 --- a/latte/cs/cookbook/@home.texy +++ b/latte/cs/cookbook/@home.texy @@ -8,6 +8,7 @@ Příklady kódů a receptů pro provádění běžných úkolů pomocí Latte. - [Předávání proměnných napříč šablonami |passing-variables] - [Všechno, co jste kdy chtěli vědět o seskupování |grouping] - [Jak psát SQL queries v Latte? |how-to-write-sql-queries-in-latte] +- [Migrace z Latte 3.0 |migration-from-latte-30] - [Migrace z Latte 2 |migration-from-latte2] - [Migrace z PHP |migration-from-php] - [Migrace z Twigu |migration-from-twig] diff --git a/latte/cs/cookbook/migration-from-latte-30.texy b/latte/cs/cookbook/migration-from-latte-30.texy new file mode 100644 index 0000000000..03211a226d --- /dev/null +++ b/latte/cs/cookbook/migration-from-latte-30.texy @@ -0,0 +1,109 @@ +Migrace z Latte 3.0 +******************* + +.[perex] +Latte 3.1 přináší několik vylepšení a změn, díky kterým je psaní šablon bezpečnější a pohodlnější. Většina změn je zpětně kompatibilní, ale některé vyžadují pozornost při přechodu. Tento průvodce shrnuje BC breaky a jak je řešit. + +Latte 3.1 vyžaduje **PHP 8.2** nebo novější. + + +Chytré atributy a migrace +========================= + +Nejvýznamnější změnou v Latte 3.1 je nové chování [chytrých atributů |/html-attributes]. To ovlivňuje, jak se vykreslují hodnoty `null` a logické hodnoty v `data-` atributech. + +1. **Hodnoty `null`:** Dříve se `title={$null}` vykresloval jako `title=""`. Nyní se atribut zcela vynechá. +2. **`data-` atributy:** Dříve se `data-foo={=true}` / `data-foo={=false}` vykreslovaly jako `data-foo="1"` / `data-foo=""`. Nyní se vykreslují jako `data-foo="true"` / `data-foo="false"`. + +Abychom vám pomohli identifikovat místa, kde se výstup ve vaší aplikaci změnil, Latte poskytuje migrační nástroj. + + +Migrační varování +----------------- + +Můžete zapnout [migrační varování |/develop#Migrační varování], která vás během vykreslování upozorní, pokud se výstup liší od Latte 3.0. + +```php +$latte = new Latte\Engine; +$latte->setFeature(Latte\Feature::MigrationWarnings); +``` + +Pokud jsou povolena, sledujte logy aplikace nebo Tracy bar pro `E_USER_WARNING`. Každé varování bude ukazovat na konkrétní řádek a sloupec v šabloně. + +**Jak varování vyřešit:** + +Pokud je nové chování správné (např. chcete, aby prázdný atribut zmizel), potvrďte jej použitím filtru `|accept` pro potlačení varování: + +```latte +<div class="{$var|accept}"></div> +``` + +Pokud chcete atribut zachovat jako prázdný (např. `title=""`) místo jeho vynechání, použijte null coalescing operátor: + +```latte +<div title={$var ?? ''}></div> +``` + +Nebo, pokud striktně vyžadujete staré chování (např. `"1"` pro `true`), explicitně přetypujte hodnotu na string: + +```latte +<div data-foo={(string) $bool}></div> +``` + +**Poté, co vyřešíte všechna varování:** + +Jakmile vyřešíte všechna varování, vypněte migrační varování a **odstraňte všechny** filtry `|accept` ze svých šablon, protože již nejsou potřeba. + + +Strict Types +============ + +Latte 3.1 zapíná `declare(strict_types=1)` ve výchozím nastavení pro všechny kompilované šablony. To zlepšuje typovou bezpečnost, ale může způsobit typové chyby v PHP výrazech uvnitř šablon, pokud jste spoléhali na volné typování. + +Pokud typy nemůžete opravit okamžitě, můžete toto chování vypnout: + +```php +$latte->setFeature(Latte\Feature::StrictTypes, false); +``` + + +Globální konstanty +================== + +Parser šablon byl vylepšen, aby lépe rozlišoval mezi jednoduchými řetězci a konstantami. V důsledku toho musí být globální konstanty nyní prefixovány zpětným lomítkem `\`. + +```latte +{* Starý způsob (vyhodí varování, v budoucnu bude interpretováno jako string 'PHP_VERSION') *} +{if PHP_VERSION > ...} + +{* Nový způsob (správně interpretováno jako konstanta) *} +{if \PHP_VERSION > ...} +``` + +Tato změna předchází nejednoznačnostem a umožňuje volnější používání neuvodzovkovaných řetězců. + + +Odstraněné funkce +================= + +**Rezervované proměnné:** Proměnné začínající na `$__` (dvou podtržítko) a proměnná `$this` jsou nyní vyhrazeny pro vnitřní použití Latte. Nemůžete je používat v šablonách. + +**Undefined-safe operátor:** Operátor `??->`, což byla specifická funkce Latte vytvořená před PHP 8, byl odstraněn. Jde o historický relikt. Používejte prosím standardní PHP nullsafe operátor `?->`. + +**Filter Loader** +Metoda `Engine::addFilterLoader()` byla označena jako zastaralá a odstraněna. Šlo o nekonzistentní koncept, který se jinde v Latte nevyskytoval. + +**Date Format** +Statická vlastnost `Latte\Runtime\Filters::$dateFormat` byla odstraněna, aby se předešlo globálnímu stavu. + + +Nové funkce +=========== + +Během migrace si můžete začít užívat nové funkce: + +- **Chytré HTML atributy:** Předávání polí do `class` a `style`, automatické vynechání `null` atributů. +- **Nullsafe filtry:** Použijte `{$var?|filter}` pro přeskočení filtrování null hodnot. +- **`n:elseif`:** Nyní můžete používat `n:elseif` společně s `n:if` a `n:else`. +- **Zjednodušená syntaxe:** Pište `<div n:if={$cond}>` bez uvozovek. +- **Toggle filtr:** Použijte `|toggle` pro ruční ovládání boolean atributů. diff --git a/latte/cs/custom-filters.texy b/latte/cs/custom-filters.texy index 562d8fd802..7f7cab0c0c 100644 --- a/latte/cs/custom-filters.texy +++ b/latte/cs/custom-filters.texy @@ -117,30 +117,6 @@ $latte->addExtension(new App\Latte\MyLatteExtension); Tento přístup udrží logiku vašeho filtru zapouzdřenou a registraci jednoduchou. -Použití načítače filtrů ------------------------ - -Latte umožňuje registrovat načítač filtrů pomocí `addFilterLoader()`. Jde o jediné volatelné callable, které Latte požádá o jakýkoliv neznámý název filtru během kompilace. Načítač vrací PHP callable filtru nebo `null`. - -```php -$latte = new Latte\Engine; - -// Načítač může dynamicky vytvářet/získávat callable filtry -$latte->addFilterLoader(function (string $name): ?callable { - if ($name === 'myLazyFilter') { - // Představte si zde náročnou inicializaci... - $service = get_some_expensive_service(); - return fn($value) => $service->process($value); - } - return null; -}); -``` - -Tato metoda byla primárně určena pro líné načítání filtrů s velmi **náročnou inicializací**. Avšak moderní praktiky vkládání závislostí (dependency injection) obvykle zvládají líné služby efektivněji. - -Načítače filtrů přidávají složitost a obecně se nedoporučují ve prospěch přímé registrace pomocí `addFilter()` nebo v rámci rozšíření pomocí `getFilters()`. Používejte načítače pouze pokud máte závažný, specifický důvod související s výkonnostními problémy při inicializaci filtrů, které nelze řešit jinak. - - Filtry používající třídu s atributy ----------------------------------- diff --git a/latte/cs/custom-tags.texy b/latte/cs/custom-tags.texy index bb55513f56..7d41e7799d 100644 --- a/latte/cs/custom-tags.texy +++ b/latte/cs/custom-tags.texy @@ -1004,7 +1004,7 @@ Zástupné symboly `PrintContext::format()` - `$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. - - `$context->format('echo "Hi" %line;', $this->position)` -> `echo "Hi" /* line 42 */;` + - `$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)` - **`%modify(...)`**: Argument musí být `ModifierNode`. Generuje PHP kód, který aplikuje filtry specifikované v `ModifierNode` na vnitřní obsah, včetně kontextově uvědomělého escapování, pokud není zakázáno pomocí `|noescape`. diff --git a/latte/cs/develop.texy b/latte/cs/develop.texy index cd9df1f54b..c37408b424 100644 --- a/latte/cs/develop.texy +++ b/latte/cs/develop.texy @@ -15,7 +15,8 @@ Podporované verze PHP (platí pro poslední setinkové verze Latte): | verze | kompatibilní s PHP |-----------------|------------------- -| Latte 3.0 | PHP 8.0 – 8.2 +| Latte 3.1 | PHP 8.2 – 8.5 +| Latte 3.0 | PHP 8.0 – 8.5 Jak vykreslit šablonu @@ -193,6 +194,27 @@ $latte = new Latte\Engine; $latte->setStrictTypes(); ``` +.[note] +Od verze Latte 3.1 jsou strict types povoleny ve výchozím nastavení. Můžete je deaktivovat pomocí `$latte->setStrictTypes(false)`. + + +Migrační varování .{data-version:3.1} +===================================== + +Latte 3.1 mění chování některých [HTML atributů|html-attributes]. Například hodnoty `null` nyní odstraní atribut namísto vypsání prázdného řetězce. Abyste snadno našli místa, kde tato změna ovlivňuje vaše šablony, můžete zapnout varování o migraci: + +```php +$latte->setFeature(Latte\Feature::MigrationWarnings); +``` + +Pokud je toto zapnuto, Latte kontroluje vykreslované atributy a vyvolá uživatelské varování (`E_USER_WARNING`), pokud se výstup liší od toho, co by Latte 3.0 vygenerovalo. Když narazíte na varování, použijte jedno z těchto řešení: + +1. Pokud je nový výstup pro váš případ použití správný (např. preferujete, aby atribut zmizel při `null`), potlačte varování přidáním filtru `|accept` +2. Pokud chcete, aby byl atribut vykreslen jako prázdný (např. `title=""`) namísto odstranění, když je proměnná `null`, poskytněte prázdný řetězec jako zálohu: `title={$val ?? ''}` +3. Pokud striktně vyžadujete staré chování (např. vypsání `"1"` pro `true` namísto `"true"`), explicitně přetypujte hodnotu na řetězec: `data-foo={(string) $val}` + +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. + Překládání v šablonách .{toc: TranslatorExtension} ================================================== @@ -266,7 +288,9 @@ Jelikož Latte kompiluje šablony do přehledného PHP kódu, můžete je pohodl Linter: validace syntaxe šablon .{toc: Linter} ============================================== -Projít všechny šablony a zkontrolovat, zda neobsahují syntaktické chyby, vám pomůže nástroj Linter. Spouští se z konzole: +Ke kontrole všech šablon slouží nástroj **Linter**. Jeho úkolem je projít zadané soubory a ověřit, že neobsahují syntaktické chyby ani odkazy na neexistující značky, filtry, funkce, třídy apod. + +Linter se spouští z příkazové řádky: ```shell vendor/bin/latte-lint <cesta> @@ -274,7 +298,7 @@ vendor/bin/latte-lint <cesta> Parametrem `--strict` aktivujete [#striktní režim]. -Pokud používáte vlastní značky, vytvořte si také vlastní verzi Linteru, např. `custom-latte-lint`: +Pokud používáte vlastní značky, filtry nebo další rozšíření Latte, je potřeba vytvořit si vlastní variantu Linteru, například `custom-latte-lint`. V té zaregistrujete všechna potřebná rozšíření ještě před samotnou validací šablon: ```php #!/usr/bin/env php @@ -302,6 +326,8 @@ $latte = new Latte\Engine; $linter = new Latte\Tools\Linter(engine: $latte); ``` +Takto přizpůsobený linter pak můžete používat stejným způsobem jako standardní nástroj, ale s plnou znalostí vašich vlastních rozšíření. + Načítání šablon z řetězce ========================= diff --git a/latte/cs/extending-latte.texy b/latte/cs/extending-latte.texy index e155953867..a378ff8cf0 100644 --- a/latte/cs/extending-latte.texy +++ b/latte/cs/extending-latte.texy @@ -44,13 +44,6 @@ $latte->addFilter('truncate', $myTruncate); // Použití v šabloně: {$text|truncate} nebo {$text|truncate:100} ``` -Můžete také zaregistrovat **Filter Loader**, funkci, která dynamicky poskytuje volatelné objekty filtrů podle požadovaného názvu: - -```php -$latte->addFilterLoader(fn(string $name) => /* vrátí volatelný objekt nebo null */); -``` - - Pro registraci funkce použitelné ve výrazech šablony použijte `addFunction()`. ```php diff --git a/latte/cs/filters.texy b/latte/cs/filters.texy index f7159ffd25..bcce55ae3e 100644 --- a/latte/cs/filters.texy +++ b/latte/cs/filters.texy @@ -55,6 +55,11 @@ V šablonách můžeme používat funkce, které pomáhají upravit nebo přefor | `floor` | [zaokrouhlí číslo dolů na danou přesnost |#floor] | `round` | [zaokrouhlí číslo na danou přesnost |#round] +.[table-latte-filters] +|## HTML atributy +| `accept` | [potvrzuje nové chování chytrých atributů |#accept] +| `toggle` | [přepíná přítomnost HTML atributu |#toggle] + .[table-latte-filters] |## Escapování | `escapeUrl` | [escapuje parametr v URL |#escapeUrl] @@ -117,10 +122,31 @@ V šabloně se potom volá takto: ``` +Nullsafe filtry .{data-version:3.1} +----------------------------------- + +Jakýkoliv filtr lze učinit nullsafe použitím `?|` místo `|`. Pokud je hodnota `null`, filtr se nevykoná a vrátí se `null`. Filtry následující v řetězci jsou také přeskočeny. + +To je užitečné v kombinaci s HTML atributy, které jsou vynechány, pokud je hodnota `null`. + +```latte +<div title={$title?|upper}> +{* Pokud je $title null: <div> *} +{* Pokud je $title 'hello': <div title="HELLO"> *} +``` + + Filtry ====== +accept .[filter]{data-version:3.1} +---------------------------------- +Filtr se používá při [migraci z Latte 3.0|cookbook/migration-from-latte-30] k potvrzení, že jste zkontrolovali změnu chování atributu a akceptujete ji. Nemění hodnotu. + +Jde o dočasný nástroj. Jakmile je migrace dokončena a varování při migraci jsou vypnuta, měli byste tento filtr ze svých šablon odstranit. + + batch(int $length, mixed $item): array .[filter] ------------------------------------------------ Filtr, který zjednodušuje výpis lineárních dat do podoby tabulky. Vrací pole polí se zadaným počtem položek. Pokud zadáte druhý parametr, použije se k doplnění chybějících položek na posledním řádku. @@ -827,6 +853,21 @@ Extrahuje část řetězce. Tento filtr byl nahrazen filtrem [#slice]. ``` +toggle .[filter]{data-version:3.1} +---------------------------------- +Filtr `toggle` ovládá přítomnost atributu na základě boolean hodnoty. Pokud je hodnota truthy, atribut je přítomen; pokud je falsy, atribut je zcela vynechán: + +```latte +<div uk-grid={$isGrid|toggle}> +{* Pokud je $isGrid truthy: <div uk-grid> *} +{* Pokud je $isGrid falsy: <div> *} +``` + +Tento filtr je užitečný pro vlastní atributy nebo atributy JavaScriptových knihoven, které vyžadují kontrolu přítomnosti/nepřítomnosti podobně jako HTML boolean atributy. + +Filtr lze použít pouze uvnitř HTML atributů. + + translate(...$args) .[filter] ----------------------------- Překládá výrazy do jiných jazyků. Aby byl filtr k dispozici, je potřeba [nastavit překladač |develop#TranslatorExtension]. Můžete také použít [tagy pro překlad |tags#Překlady]. diff --git a/latte/cs/html-attributes.texy b/latte/cs/html-attributes.texy new file mode 100644 index 0000000000..e9632b2586 --- /dev/null +++ b/latte/cs/html-attributes.texy @@ -0,0 +1,151 @@ +Chytré HTML atributy +******************** + +.[perex] +Latte 3.1 přichází se sadou vylepšení, která se zaměřuje na jednu z nejčastějších činností v šablonách – vypisování HTML atributů. Přináší více pohodlí, flexibility a bezpečnosti. + + +Boolean atributy +================ + +HTML používá speciální atributy jako `checked`, `disabled`, `selected` nebo `hidden`, u kterých nezáleží na konkrétní hodnotě – pouze na jejich přítomnosti. Fungují jako jednoduché příznaky. + +Latte je zpracovává automaticky. Atributu můžete předat jakýkoliv výraz. Pokud je pravdivý (truthy), atribut se vykreslí. Pokud je nepravdivý (falsey - např. `false`, `null`, `0` nebo prázdný řetězec), atribut se zcela vynechá. + +To znamená, že se můžete rozloučit se složitými podmínkami nebo `n:attr` a jednoduše použít: + +```latte +<input type="text" disabled={$isDisabled} readonly={$isReadOnly}> +``` + +Pokud `$isDisabled` je `false` a `$isReadOnly` je `true`, vykreslí se: + +```latte +<input type="text" readonly> +``` + +Pokud potřebujete přepínací chování pro standardní atributy, které nemají toto automatické zpracování (tedy např. atributy `data-` nebo `aria-`), použijte filtr [toggle |filters#toggle]. + + +Hodnoty null +============ + +Toto je jedna z nejpříjemnějších změn. Dříve, pokud byla proměnná `null`, vypsala se jako prázdný řetězec `""`. To často vedlo k prázdným atributům v HTML jako `class=""` nebo `title=""`. + +V Latte 3.1 platí nové univerzální pravidlo: **Hodnota `null` znamená, že atribut neexistuje.** + +```latte +<div title="{$title}"></div> +``` + +Pokud `$title` je `null`, výstupem je `<div></div>`. Pokud obsahuje řetězec, např. "Ahoj", výstupem je `<div title="Ahoj"></div>`. Díky tomu nemusíte obalovat atributy do podmínek. + +Pokud používáte filtry, mějte na paměti, že obvykle převádějí `null` na řetězec (např. prázdný řetězec). Abyste tomu zabránili, použijte [nullsafe filtr |filters#Nullsafe filtry] `?|`: + +```latte +<div title="{$title?|upper}"></div> +``` + + +Třídy (Classes) +=============== + +Atributu `class` můžete předat pole. To je ideální pro podmíněné třídy: pokud je pole asociativní, klíče se použijí jako názvy tříd a hodnoty jako podmínky. Třída se vykreslí pouze v případě, že je podmínka splněna. + +```latte +<button class={[ + btn, + btn-primary, + active => $isActive, +]}>Stiskni mě</button> +``` + +Pokud je `$isActive` true, vykreslí se: + +```latte +<button class="btn btn-primary active">Stiskni mě</button> +``` + +Toto chování není omezeno pouze na `class`. Funguje pro jakýkoliv HTML atribut, který očekává seznam hodnot oddělených mezerou, jako jsou `itemprop`, `rel`, `sandbox` atd. + +```latte +<a rel={[nofollow, noopener, external => $isExternal]}>odkaz</a> +``` + + +Styly (Styles) +============== + +Atribut `style` také podporuje pole. Je to obzvláště užitečné pro podmíněné styly. Pokud položka pole obsahuje klíč (CSS vlastnost) a hodnotu, vlastnost se vykreslí pouze v případě, že hodnota není `null`. + +```latte +<div style={[ + background => lightblue, + display => $isVisible ? block : null, + font-size => '16px', +]}></div> +``` + +Pokud je `$isVisible` false, vykreslí se: + +```latte +<div style="background: lightblue; font-size: 16px"></div> +``` + + +Data atributy +============= + +Často potřebujeme do HTML předat konfiguraci pro JavaScript. Dříve se to dělalo přes `json_encode`. Nyní můžete atributu `data-` jednoduše předat pole nebo objekt stdClass a Latte jej serializuje do JSONu: + +```latte +<div data-config={[ theme: dark, version: 2 ]}></div> +``` + +Vypíše: + +```latte +<div data-config='{"theme":"dark","version":2}'></div> +``` + +Také `true` a `false` se vykreslují jako řetězce `"true"` a `"false"` (tj. validní JSON). + + +Aria atributy +============= + +Specifikace WAI-ARIA vyžaduje textové hodnoty `"true"` a `"false"` pro logické hodnoty. Latte to pro atributy `aria-` řeší automaticky: + +```latte +<button aria-expanded={=true} aria-checked={=false}></button> +``` + +Vypíše: + +```latte +<button aria-expanded="true" aria-checked="false"></button> +``` + + +Typová kontrola +=============== + +Už jste někdy viděli `<input value="Array">` ve svém vygenerovaném HTML? Je to klasická chyba, která často projde bez povšimnutí. Latte zavádí přísnou typovou kontrolu pro HTML atributy, aby byly vaše šablony vůči takovým přehlédnutím odolnější. + +Latte ví, které atributy jsou které a jaké hodnoty očekávají: + +- **Standardní atributy** (jako `href`, `id`, `value`, `placeholder`...) očekávají hodnotu, kterou lze vykreslit jako text. To zahrnuje řetězce, čísla nebo stringable objekty. Také je akceptováno `null` (atribut vynechá). Pokud však omylem předáte pole, boolean nebo obecný objekt, Latte vyvolá varování a neplatnou hodnotu inteligentně ignoruje. +- **Boolean atributy** (jako `checked`, `disabled`...) akceptují jakýkoliv typ, protože jejich přítomnost je určena logikou pravdivý/nepravdivý. +- **Chytré atributy** (jako `class`, `style`, `data-`...) specificky zpracovávají pole jako validní vstupy. + +Tato kontrola zajišťuje, že vaše aplikace nebude produkovat neočekávané HTML. + + +Migrace z Latte 3.0 +=================== + +Protože se změnilo chování `null` (dříve vypisovalo `""`, nyní atribut vynechá) a atributů `data-` (boolean hodnoty vypisovaly `"1"`/`""`, nyní `"true"`/`"false"`), možná budete muset aktualizovat své šablony. + +Pro hladký přechod poskytuje Latte migrační režim, který upozorňuje na rozdíly. Přečtěte si podrobného průvodce [Migrace z Latte 3.0 na 3.1 |cookbook/migration-from-latte-30]. + +[* html-attributes.webp *] diff --git a/latte/cs/syntax.texy b/latte/cs/syntax.texy index 0913d20fc7..1e21ab6dcf 100644 --- a/latte/cs/syntax.texy +++ b/latte/cs/syntax.texy @@ -111,6 +111,34 @@ Což vypíše v závislosti na proměnné `$url`: Avšak n:atributy nejsou jen zkratkou pro párové značky. Existují i ryzí n:atributy, jako třeba [n:href |application:creating-links#V šabloně presenteru] nebo velešikovný pomocník kodéra [n:class |tags#n:class]. +Kromě syntaxe s uvozovkami `<div n:if="$foo">` můžete použít alternativní syntaxi se složenými závorkami `<div n:if={$foo}>`. Hlavní výhodou je, že uvnitř `{...}` můžete volně používat jednoduché i dvojité uvozovky: + +```latte +<div n:if={str_contains($val, "foo")}> ... </div> +``` + + +Chytré HTML atributy .{data-version:3.1} +======================================== + +Latte dělá práci se standardními HTML atributy neuvěřitelně snadnou. Za vás řeší boolean atributy jako `checked`, odstraňuje atributy obsahující `null` a umožňuje vám skládat hodnoty `class` a `style` pomocí polí. Dokonce automaticky serializuje data pro `data-` atributy do JSON. + +```latte +{* null odstraní atribut *} +<div title={$title}> + +{* boolean ovládá přítomnost boolean atributů *} +<input type="checkbox" checked={$isChecked}> + +{* pole fungují v class *} +<div class={['btn', 'btn-primary', active => $isActive]}> + +{* pole jsou JSON-enkódována v data- atributech *} +<div data-config={[theme: dark, version: 2]}> +``` + +Více informací v samostatné kapitole [Chytré HTML atributy|html-attributes]. + Filtry ====== @@ -148,10 +176,17 @@ Na blok: ``` Nebo přímo na hodnotu (v kombinaci s tagem [`{=expr}` |tags#Vypisování]): + ```latte <h1>{=' Hello world '|trim}<h1> ``` +Pokud může být hodnota `null` a chcete v takovém případě zabránit použití filtru, použijte [nullsafe filter |filters#Nullsafe Filters] `?|`: + +```latte +<h1>{$heading?|upper}</h1> +``` + Dynamické HTML značky .{data-version:3.0.9} =========================================== @@ -204,7 +239,7 @@ Jednoduché řetězce jsou ty, které jsou tvořeny čistě z písmen, číslic, Konstanty --------- -Jelikož lze u jednoduchých řetězců vynechávat uvozovky, doporučujeme pro odlišení zapisovat globální konstanty s lomítkem na začátku: +K rozlišení globálních konstant od jednoduchých řetězců použijte oddělovač globálního jmenného prostoru: ```latte {if \PROJECT_ID === 1} ... {/if} @@ -265,8 +300,6 @@ Historické okénko Latte přišlo v průběhu své historie s celou řadou syntaktických cukříků, které se po pár letech objevily v samotném PHP. Například v Latte bylo možné psát pole jako `[1, 2, 3]` místo `array(1, 2, 3)` nebo používat nullsafe operátor `$obj?->foo` dávno předtím, než to bylo možné v samotném PHP. Latte také zavedlo operátor pro rozbalení pole `(expand) $arr`, který je ekvivalentem dnešního operátoru `...$arr` z PHP. -Undefined-safe operator `??->`, což je obdoba nullsafe operatoru `?->`, který ale nevyvolá chybu, pokud proměnná neexistuje, vznikl z historických důvodů a dnes doporučujeme používat standardní PHP operátor `?->`. - Omezení PHP v Latte =================== diff --git a/latte/cs/tags.texy b/latte/cs/tags.texy index d302666d5a..598b2b73dd 100644 --- a/latte/cs/tags.texy +++ b/latte/cs/tags.texy @@ -16,7 +16,7 @@ Přehled a popis všech tagů (neboli značek či maker) šablonovacího systém | `{ifset}` … `{elseifset}` … `{/ifset}` | [podmínka ifset |#ifset elseifset] | `{ifchanged}` … `{/ifchanged}` | [test jestli došlo ke změně |#ifchanged] | `{switch}` `{case}` `{default}` `{/switch}` | [podmínka switch |#switch case default] -| `n:else` | [alternativní obsah pro podmínky |#n:else] +| `n:else`, `n:elseif` | [alternativní obsah pro podmínky |#n:else] .[table-latte-tags language-latte] |## Cykly @@ -252,14 +252,16 @@ Víte, že k n:atributům můžete připojit prefix `tag-`? Pak se bude podmínk Boží. -`n:else` .{data-version:3.0.11} -------------------------------- +`n:else` `n:elseif` .{data-version:3.0.11} +------------------------------------------ -Pokud podmínku `{if} ... {/if}` zapíšete v podobě [n:attributu |syntax#n:atributy], máte možnost uvést i alternativní větev pomocí `n:else`: +Pokud podmínku `{if} ... {/if}` zapíšete v podobě [n:attributu |syntax#n:atributy], máte možnost uvést i alternativní větev pomocí `n:else` a `n:elseif` (od Latte 3.1): ```latte <strong n:if="$count > 0">Skladem {$count} kusů</strong> +<em n:elseif="$count < 0">Neplatný počet</em> + <em n:else>není dostupné</em> ``` @@ -947,6 +949,9 @@ Pomocníci HTML kodéra n:class ------- +.[note] +Od verze Latte 3.1 získal standardní HTML atribut `class` [stejnou funkcionalitu |html-attributes#třídy-classes]. Není tedy již nutné používat n:class. + Díky `n:class` velice snadno vygenerujete HTML atribut `class` přesně podle představ. Příklad: potřebuji, aby aktivní prvek měl třídu `active`: @@ -997,6 +1002,12 @@ V závislosti na vrácených hodnotách vypíše např.: <input type="checkbox" value="Hello" checked> ``` +Funkce inteligentních atributů v Latte 3.1, jako je vynechání hodnot `null` nebo předávání polí do `class` nebo `style`, fungují také v rámci `n:attr`: + +```latte +<div n:attr="class: [a, b], title: $title"></div> +``` + n:tag ----- diff --git a/latte/en/@left-menu.texy b/latte/en/@left-menu.texy index eebab75253..88fc173a96 100644 --- a/latte/en/@left-menu.texy +++ b/latte/en/@left-menu.texy @@ -5,6 +5,7 @@ - [Template Inheritance] - [Type System] - [Sandbox] + - [HTML attributes] - For Designers 🎨 - [Syntax] diff --git a/latte/en/cookbook/@home.texy b/latte/en/cookbook/@home.texy index 7c2738769a..58b23d4136 100644 --- a/latte/en/cookbook/@home.texy +++ b/latte/en/cookbook/@home.texy @@ -8,6 +8,7 @@ Example codes and recipes for accomplishing common tasks with Latte. - [Passing variables across templates |passing-variables] - [Everything you always wanted to know about grouping |grouping] - [How to write SQL queries in Latte? |how-to-write-sql-queries-in-latte] +- [Migration from Latte 3.0 |migration-from-latte-30] - [Migration from Latte 2 |migration-from-latte2] - [Migration from PHP |migration-from-php] - [Migration from Twig |migration-from-twig] diff --git a/latte/en/cookbook/migration-from-latte-30.texy b/latte/en/cookbook/migration-from-latte-30.texy new file mode 100644 index 0000000000..0c73bfce8f --- /dev/null +++ b/latte/en/cookbook/migration-from-latte-30.texy @@ -0,0 +1,110 @@ +Migration from Latte 3.0 +************************ + +.[perex] +Latte 3.1 brings several improvements and changes that make templates safer and more convenient to write. Most changes are backward compatible, but some require attention during migration. This guide summarizes the breaking changes and how to handle them. + +Latte 3.1 requires **PHP 8.2** or newer. + + +Smart Attributes and Migration +============================== + +The most significant change in Latte 3.1 is the new behavior of [Smart Attributes |/html-attributes]. This affects how `null` values and boolean values in `data-` attributes are rendered. + +1. **`null` values:** Previously, `title={$null}` rendered as `title=""`. Now, the attribute is completely dropped. +2. **`data-` attributes:** Previously, `data-foo={=true}` / `data-foo={=false}` rendered as `data-foo="1"` / `data-foo=""`. Now, it renders as `data-foo="true"` / `data-foo="false"`. + +To help you identify places where the output has changed in your application, Latte provides a migration tool. + + +Migration Warnings +------------------ + +You can enable [migration warnings |/develop#Migration Warnings], which will warn you during rendering if the output differs from Latte 3.0. + +```php +$latte = new Latte\Engine; +$latte->setFeature(Latte\Feature::MigrationWarnings); +``` + +When enabled, check your application logs or Tracy bar for `E_USER_WARNING`s. Each warning will point to the specific line, and column in template. + +**How to resolve warnings:** + +If the new behavior is correct (e.g. you want the empty attribute to disappear), confirm it using the `|accept` filter to suppress the warning: + +```latte +<div class="{$var|accept}"></div> +``` + +If you want to keep the attribute as empty (e.g. `title=""`) instead of dropping it, use the null coalescing operator: + +```latte +<div title={$var ?? ''}></div> +``` + +Or, if you strictly require the old behavior (e.g. `"1"` for `true`), explicitly cast the value to string: + +```latte +<div data-foo={(string) $bool}></div> +``` + +**After you resolve all warnings:** + +Once all warnings are resolved, disable migration warnings and **remove all** `|accept` filters from your templates, as they are no longer needed. + + +Strict Types +============ + +Latte 3.1 enables `declare(strict_types=1)` by default for all compiled templates. This improves type safety but might cause type errors in PHP expressions inside your templates if you were relying on loose typing. + +If you cannot fix the types immediately, you can disable this behavior: + +```php +$latte->setFeature(Latte\Feature::StrictTypes, false); +``` + + +Global Constants +================ + +The template parser has been improved to better distinguish between simple strings and constants. As a result, global constants must now be prefixed with a backslash `\`. + +```latte +{* Old way (throws a warning; in the future will be interpreted as the string 'PHP_VERSION') *} +{if PHP_VERSION > ...} + +{* New way (correctly interpreted as constant) *} +{if \PHP_VERSION > ...} +``` + +This change prevents ambiguity and allows you to use unquoted strings more freely. + + +Removed Features +================ + +**Reserved Variables:** Variables starting with `$__` (double underscore) and the variable `$this` are now strictly reserved for Latte's internal use. You cannot use them in your templates. + +**Undefined-safe Operator:** The `??->` operator, which was a Latte-specific feature created before PHP 8, has been removed. It is a historical relic. Please use the standard PHP nullsafe operator `?->`. + +**Filter Loader** +The `Engine::addFilterLoader()` method has been deprecated and removed. It was an inconsistent concept not found elsewhere in Latte. + +**Date Format** +The static property `Latte\Runtime\Filters::$dateFormat` was removed to avoid global state. + + +New Features +============ + +While migrating, you can start enjoying the new features: + +- **Smart HTML +Attributes:** Pass arrays to `class` and `style`, auto-drop `null` attributes. +- **Nullsafe filters:** Use `{$var?|filter}` to skip filtering null values. +- **`n:elseif`:** You can now use `n:elseif` alongside `n:if` and `n:else`. +- **Simplified syntax:** Write `<div n:if={$cond}>` without quotes. +- **Toggle filter:** Use `|toggle` for manual control over boolean attributes. diff --git a/latte/en/custom-filters.texy b/latte/en/custom-filters.texy index ee38269043..9fc3ea58b6 100644 --- a/latte/en/custom-filters.texy +++ b/latte/en/custom-filters.texy @@ -117,30 +117,6 @@ $latte->addExtension(new App\Latte\MyLatteExtension); This approach keeps your filter logic encapsulated and makes registration straightforward. -Using a Filter Loader ---------------------- - -Latte allows registering a filter loader via `addFilterLoader()`. This is a single callable that Latte asks for any unknown filter name during compilation. The loader returns the filter's PHP callable or `null`. - -```php -$latte = new Latte\Engine; - -// Loader might dynamically create/fetch filter callables -$latte->addFilterLoader(function (string $name): ?callable { - if ($name === 'myLazyFilter') { - // Imagine expensive initialization here... - $service = get_some_expensive_service(); - return fn($value) => $service->process($value); - } - return null; -}); -``` - -This method was primarily intended for lazy loading filters with very **expensive initialization**. However, modern dependency injection practices usually handle lazy services more effectively. - -Filter loaders add complexity and are generally discouraged in favor of direct registration via `addFilter()` or within an Extension using `getFilters()`. Use loaders only if you have a strong, specific reason related to performance bottlenecks in filter initialization that cannot be addressed otherwise. - - Filters Using a Class with Attributes .{toc: Filters Using the Class} --------------------------------------------------------------------- diff --git a/latte/en/custom-tags.texy b/latte/en/custom-tags.texy index ed99d6f014..b427e74a27 100644 --- a/latte/en/custom-tags.texy +++ b/latte/en/custom-tags.texy @@ -1004,7 +1004,7 @@ We've frequently used `PrintContext::format()` to generate PHP code in the `prin - `$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. - - `$context->format('echo "Hi" %line;', $this->position)` -> `echo "Hi" /* line 42 */;` + - `$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)` - **`%modify(...)`**: Argument must be a `ModifierNode`. It generates PHP code that applies the filters specified in the `ModifierNode` to the inner content, including context-aware escaping if not disabled by `|noescape`. diff --git a/latte/en/develop.texy b/latte/en/develop.texy index 1f8d88b7ff..968c797320 100644 --- a/latte/en/develop.texy +++ b/latte/en/develop.texy @@ -15,7 +15,8 @@ Supported PHP versions (applies to the latest patch Latte versions): | version | compatible with PHP |-----------------|------------------- -| Latte 3.0 | PHP 8.0 – 8.2 +| Latte 3.1 | PHP 8.2 – 8.5 +| Latte 3.0 | PHP 8.0 – 8.5 How to Render a Template @@ -193,6 +194,27 @@ $latte = new Latte\Engine; $latte->setStrictTypes(); ``` +.[note] +Since Latte 3.1, strict types are enabled by default. You can disable them with `$latte->setStrictTypes(false)`. + + +Migration Warnings .{data-version:3.1} +====================================== + +Latte 3.1 changes the behavior of some [HTML attributes|html-attributes]. For example, `null` values now drop the attribute instead of printing an empty string. To easily find places where this change affects your templates, you can enable migration warnings: + +```php +$latte->setFeature(Latte\Feature::MigrationWarnings); +``` + +When enabled, Latte checks rendered attributes and triggers a user warning (`E_USER_WARNING`) if the output differs from what Latte 3.0 would have produced. When you encounter a warning, apply one of the solutions: + +1. If the new output is correct for your use case (e.g., you prefer the attribute to disappear when `null`), suppress the warning by adding the `|accept` filter +2. If you want the attribute to be rendered as empty (e.g. `title=""`) instead of being dropped when the variable is `null`, provide an empty string as a fallback: `title={$val ?? ''}` +3. If you strictly require the old behavior (e.g., printing `"1"` for `true` instead of `"true"`), explicitly cast the value to a string: `data-foo={(string) $val}` + +Once all warnings are resolved, disable migration warnings and **remove all** `|accept` filters from your templates, as they are no longer needed. + Translation in Templates .{toc: TranslatorExtension} ==================================================== @@ -266,7 +288,9 @@ Since Latte compiles templates into readable PHP code, you can conveniently step Linter: Validating the Template Syntax .{toc: Linter} ===================================================== -The Linter tool will help you go through all templates and check for syntax errors. It is launched from the console: +The **Linter** tool is used to validate all templates. Its purpose is to scan the specified files and ensure that they contain no syntax errors and no references to non-existent tags, filters, functions, classes, or similar constructs. + +The Linter is executed from the command line: ```shell vendor/bin/latte-lint <path> @@ -274,7 +298,7 @@ vendor/bin/latte-lint <path> Use the `--strict` parameter to activate [#strict mode]. -If you use custom tags, also create your customized Linter, e.g. `custom-latte-lint`: +If you use custom tags, filters, or other Latte extensions, you need to create your own variant of the Linter, for example `custom-latte-lint`. In this script, you register all required extensions before the actual template validation takes place: ```php #!/usr/bin/env php @@ -302,6 +326,8 @@ $latte = new Latte\Engine; $linter = new Latte\Tools\Linter(engine: $latte); ``` +The resulting customized linter can then be used in the same way as the standard tool, but with full knowledge of all your custom extensions. + Loading Templates from a String =============================== diff --git a/latte/en/extending-latte.texy b/latte/en/extending-latte.texy index 9f63a3a280..22499f514a 100644 --- a/latte/en/extending-latte.texy +++ b/latte/en/extending-latte.texy @@ -44,13 +44,6 @@ $latte->addFilter('truncate', $myTruncate); // Template usage: {$text|truncate} or {$text|truncate:100} ``` -You can also register a **Filter Loader**, a function that dynamically provides filter callables based on the requested name: - -```php -$latte->addFilterLoader(fn(string $name) => /* return callable or null */); -``` - - Use `addFunction()` to register a function usable within template expressions. ```php diff --git a/latte/en/filters.texy b/latte/en/filters.texy index ef92176edb..c87f8ffceb 100644 --- a/latte/en/filters.texy +++ b/latte/en/filters.texy @@ -55,6 +55,11 @@ In templates, we can use functions that help modify or reformat data into its fi | `floor` | [rounds a number down to a given precision |#floor] | `round` | [rounds a number to a given precision |#round] +.[table-latte-filters] +|## HTML Attributes +| `accept` | [accepts the new behavior of smart attributes |#accept] +| `toggle` | [toggles the presence of an HTML attribute |#toggle] + .[table-latte-filters] |## Escaping | `escapeUrl` | [escapes a parameter in a URL |#escapeUrl] @@ -117,10 +122,31 @@ It is then called in the template like this: ``` +Nullsafe Filters .{data-version:3.1} +------------------------------------ + +Any filter can be made nullsafe by using `?|` instead of `|`. If the value is `null`, the filter is not executed and `null` is returned. Subsequent filters in the chain are also skipped. + +This is useful in combination with HTML attributes, which are omitted if the value is `null`. + +```latte +<div title={$title?|upper}> +{* If $title is null: <div> *} +{* If $title is 'hello': <div title="HELLO"> *} +``` + + Filters ======= +accept .[filter]{data-version:3.1} +---------------------------------- +The filter is used during [migration from Latte 3.0|cookbook/migration-from-latte-30] to acknowledge that you've reviewed the attribute behavior change and accept it. It does not modify the value. + +This is a temporary tool. Once the migration is complete and migration warnings are disabled, you should remove this filter from your templates. + + batch(int $length, mixed $item): array .[filter] ------------------------------------------------ A filter that simplifies listing linear data in a table format. It returns an array of arrays with the specified number of items. If you provide a second parameter, it will be used to fill in missing items in the last row. @@ -827,6 +853,21 @@ Extracts a portion of a string. This filter has been replaced by the [#slice] fi ``` +toggle .[filter]{data-version:3.1} +---------------------------------- +The `toggle` filter controls the presence of an attribute based on a boolean value. If the value is truthy, the attribute is present; if falsy, the attribute is omitted entirely: + +```latte +<div uk-grid={$isGrid|toggle}> +{* If $isGrid is truthy: <div uk-grid> *} +{* If $isGrid is falsy: <div> *} +``` + +This filter is useful for custom attributes or JavaScript library attributes that require presence/absence control similar to HTML boolean attributes. + +The filter can only be used within HTML attributes. + + translate(...$args) .[filter] ----------------------------- Translates expressions into other languages. To make the filter available, you need to [set up the translator |develop#TranslatorExtension]. You can also use the [tags for translation |tags#Translation]. diff --git a/latte/en/html-attributes.texy b/latte/en/html-attributes.texy new file mode 100644 index 0000000000..1107579e99 --- /dev/null +++ b/latte/en/html-attributes.texy @@ -0,0 +1,151 @@ +Smart HTML Attributes +********************* + +.[perex] +Latte 3.1 comes with a set of improvements that focuses on one of the most common activities in templates – printing HTML attributes. It brings more convenience, flexibility and security. + + +Boolean Attributes +================== + +HTML uses special attributes like `checked`, `disabled`, `selected`, or `hidden`, where the specific value is irrelevant—only their presence matters. They act as simple flags. + +Latte handles them automatically. You can pass any expression to the attribute. If it is truthy, the attribute is rendered. If it is falsey (e.g. `false`, `null`, `0`, or an empty string), the attribute is completely omitted. + +This means you can say goodbye to cumbersome macro conditions or `n:attr` and simply use: + +```latte +<input type="text" disabled={$isDisabled} readonly={$isReadOnly}> +``` + +If `$isDisabled` is `false` and `$isReadOnly` is `true`, it renders: + +```latte +<input type="text" readonly> +``` + +If you need this toggling behavior for standard attributes that don't have this automatic handling (like `data-` or `aria-` attributes), use the [toggle |filters#toggle] filter. + + +Null Values +=========== + +This is one of the most pleasant changes. Previously, if a variable was `null`, it printed as an empty string `""`. This often led to empty attributes in HTML like `class=""` or `title=""`. + +In Latte 3.1, a new universal rule applies: **A value of `null` means the attribute does not exist.** + +```latte +<div title="{$title}"></div> +``` + +If `$title` is `null`, the output is `<div></div>`. If it contains a string, e.g. "Hello", the output is `<div title="Hello"></div>`. Thanks to this, you don't have to wrap attributes in conditions. + +If you use filters, keep in mind that they usually convert `null` to a string (e.g. empty string). To prevent this, use the [nullsafe filter |filters#Nullsafe Filters] `?|`: + +```latte +<div title="{$title?|upper}"></div> +``` + + +Classes +======= + +You can pass an array to the `class` attribute. This is perfect for conditional classes: if the array is associative, the keys are used as class names and the values as conditions. The class is rendered only if the condition is true. + +```latte +<button class={[ + btn, + btn-primary, + active => $isActive, +]}>Press me</button> +``` + +If `$isActive` is true, it renders: + +```latte +<button class="btn btn-primary active">Press me</button> +``` + +This behavior is not limited to `class`. It works for any HTML attribute that expects a space-separated list of values, such as `itemprop`, `rel`, `sandbox`, etc. + +```latte +<a rel={[nofollow, noopener, external => $isExternal]}>link</a> +``` + + +Styles +====== + +The `style` attribute also supports arrays. It is especially useful for conditional styles. If an array item contains a key (CSS property) and a value, the property is rendered only if the value is not `null`. + +```latte +<div style={[ + background => lightblue, + display => $isVisible ? block : null, + font-size => '16px', +]}></div> +``` + +If `$isVisible` is false, it renders: + +```latte +<div style="background: lightblue; font-size: 16px"></div> +``` + + +Data Attributes +=============== + +Often we need to pass configuration for JavaScript into HTML. Previously this was done via `json_encode`. Now you can simply pass an array or stdClass object to a `data-` attribute and Latte will serialize it to JSON: + +```latte +<div data-config={[ theme: dark, version: 2 ]}></div> +``` + +Outputs: + +```latte +<div data-config='{"theme":"dark","version":2}'></div> +``` + +Also, `true` and `false` are rendered as strings `"true"` and `"false"` (i.e. valid JSON). + + +Aria Attributes +=============== + +The WAI-ARIA specification requires text values `"true"` and `"false"` for boolean values. Latte handles this automatically for `aria-` attributes: + +```latte +<button aria-expanded={=true} aria-checked={=false}></button> +``` + +Outputs: + +```latte +<button aria-expanded="true" aria-checked="false"></button> +``` + + +Type Checking +============= + +Have you ever seen `<input value="Array">` in your generated HTML? It's a classic bug that often goes unnoticed. Latte introduces strict type checking for HTML attributes to make your templates more resilient against such oversight. + +Latte knows which attributes are which and what values they expect: + +- **Standard attributes** (like `href`, `id`, `value`, `placeholder`...) expect a value that can be rendered as text. This includes strings, numbers, or stringable objects. `null` is also accepted (it drops the attribute). However, if you accidentally pass an array, boolean or a generic object, Latte triggers a warning and intelligently ignores the invalid value. +- **Boolean attributes** (like `checked`, `disabled`...) accept any type, as their presence is determined by truthy/falsey logic. +- **Smart attributes** (like `class`, `style`, `data-`...) specifically handle arrays as valid inputs. + +This check ensures that your application doesn't produce unexpected HTML. + + +Migration from Latte 3.0 +======================== + +Since the behavior of `null` (it used to print `""`, now it drops the attribute) and `data-` attributes (booleans used to print `"1"`/`""`, now `"true"`/`"false"`) has changed, you might need to update your templates. + +For a smooth transition, Latte provides a migration mode that highlights differences. Read the detailed guide [Migration from Latte 3.0 to 3.1|cookbook/migration-from-latte-30]. + +[* html-attributes.webp *] diff --git a/latte/en/syntax.texy b/latte/en/syntax.texy index a65b056772..ef926e75be 100644 --- a/latte/en/syntax.texy +++ b/latte/en/syntax.texy @@ -111,6 +111,34 @@ Which outputs, depending on the variable `$url`: However, n:attributes are not only a shortcut for pair tags, there are some pure n:attributes as well, for example the coder's best friend [n:class|tags#n:class] or the very handy [n:href |application:creating-links#In the Presenter Template]. +In addition to the syntax using quotes `<div n:if="$foo">`, you can use alternative syntax with curly braces `<div n:if={$foo}>`. The main advantage is that you can freely use both single and double quotes inside `{...}`: + +```latte +<div n:if={str_contains($val, "foo")}> ... </div> +``` + + +Smart HTML Attributes .{data-version:3.1} +========================================= + +Latte makes working with standard HTML attributes incredibly easy. It handles boolean attributes like `checked` for you, removes attributes containing `null`, and allows you to compose `class` and `style` values using arrays. It even automatically serializes data for `data-` attributes into JSON. + +```latte +{* null removes the attribute *} +<div title={$title}> + +{* boolean controls presence of boolean attributes *} +<input type="checkbox" checked={$isChecked}> + +{* arrays work in class *} +<div class={['btn', 'btn-primary', active => $isActive]}> + +{* arrays are JSON-encoded in data- attributes *} +<div data-config={[theme: dark, version: 2]}> +``` + +Read more in the separate chapter [Smart HTML Attributes|html-attributes]. + Filters ======= @@ -148,10 +176,17 @@ On a block: ``` Or directly on a value (in combination with the [`{=expr}` |tags#Printing] tag): + ```latte <h1>{=' Hello world '|trim}<h1> ``` +If the value can be `null` and you want to avoid applying the filter in that case, use the [nullsafe filter |filters#Nullsafe Filters] `?|`: + +```latte +<h1>{$heading?|upper}</h1> +``` + Dynamic HTML Tags .{data-version:3.0.9} ======================================= @@ -204,7 +239,7 @@ Simple strings are those composed purely of letters, digits, underscores, hyphen Constants --------- -Since quotes can be omitted for simple strings, we recommend writing global constants with a leading slash to distinguish them: +Use the global namespace separator to distinguish global constants from simple strings: ```latte {if \PROJECT_ID === 1} ... {/if} @@ -265,8 +300,6 @@ A Window into History Over its history, Latte introduced several syntactic sugar features that appeared in PHP itself a few years later. For example, in Latte, it was possible to write arrays as `[1, 2, 3]` instead of `array(1, 2, 3)` or use the nullsafe operator `$obj?->foo` long before it was possible in PHP itself. Latte also introduced the array expansion operator `(expand) $arr`, which is equivalent to today's `...$arr` operator from PHP. -The undefined-safe operator `??->`, which is similar to the nullsafe operator `?->` but does not raise an error if the variable does not exist, was created for historical reasons, and today we recommend using the standard PHP operator `?->`. - PHP Limitations in Latte ======================== diff --git a/latte/en/tags.texy b/latte/en/tags.texy index e9cb9309ae..cfbc94ae6f 100644 --- a/latte/en/tags.texy +++ b/latte/en/tags.texy @@ -16,7 +16,7 @@ An overview and description of all the tags available by default in the Latte te | `{ifset}` … `{elseifset}` … `{/ifset}` | [ifset condition |#ifset elseifset] | `{ifchanged}` … `{/ifchanged}` | [tests if a value has changed |#ifchanged] | `{switch}` `{case}` `{default}` `{/switch}` | [switch condition |#switch case default] -| `n:else` | [alternative content for conditions |#n:else] +| `n:else`, `n:elseif` | [alternative content for conditions |#n:else] .[table-latte-tags language-latte] |## Loops @@ -252,18 +252,20 @@ Did you know you can add the `tag-` prefix to n:attributes? Then the condition w Awesome. -`n:else` .{data-version:3.0.11} -------------------------------- +`n:else` `n:elseif` .{toc: n:else}{data-version:3.0.11} +------------------------------------------------------- -If you write the `{if} ... {/if}` condition in the form of an [n:attribute |syntax#n:attributes], you have the option to specify an alternative branch using `n:else`: +If you write the `{if} ... {/if}` condition in the form of an [n:attribute |syntax#n:attributes], you have the option to specify alternative branches using `n:else` and `n:elseif` (since Latte 3.1): ```latte <strong n:if="$count > 0">In stock {$count} items</strong> +<em n:elseif="$count < 0">Invalid count</em> + <em n:else>not available</em> ``` -The `n:else` attribute can also be used in conjunction with [`n:ifset` |#ifset elseifset], [`n:foreach` |#foreach], [`n:try` |#try], [#`n:ifcontent`], and [`n:ifchanged` |#ifchanged]. +The `n:else` attribute can also be used in conjunction with [`n:ifset` |#ifset elseifset], [`n:foreach` |#foreach], [`n:try` |#try], [`n:ifcontent`|#nifcontent], and [`n:ifchanged` |#ifchanged]. `{/if $cond}` @@ -947,6 +949,9 @@ HTML Coder Helpers `n:class` --------- +.[note] +Since Latte 3.1, the standard HTML class attribute has gained the [same functionality |html-attributes#classes]. So you don't need to use n:class anymore + Thanks to `n:class`, it's very easy to generate the HTML `class` attribute exactly as needed. Example: I need the active element to have the class `active`: @@ -997,6 +1002,12 @@ Depending on the returned values, it prints, for example: <input type="checkbox" value="Hello" checked> ``` +Smart attribute features in Latte 3.1, such as dropping `null` values or passing arrays into `class` or `style`, also work within `n:attr`: + +```latte +<div n:attr="class: [a, b], title: $title"></div> +``` + `n:tag` ------- diff --git a/latte/files/html-attributes.webp b/latte/files/html-attributes.webp new file mode 100644 index 0000000000000000000000000000000000000000..56e729541b6ca3c84e678b115842be201c429e10 GIT binary patch literal 11126 zcmZ9y1ymeOvoO5);=Z^o7GSXi5AF^jxVyVMi@UpfaDoO6!Ciy9OM*i}fFS=q?{mL< z?|;vk(=}CHHq|vf)zhseCoS#B0sv@9iK}X=@+q3Y?%8dJz5>W)9m(WUYTeuSUY({Z zCogm^a5>=~r%r#h`Hav)lf&pP(XUPc>@&B5XPcWm#-auqLrC`f!(9d%y-DWHyYx_$ zP{UNdF1B~@U-*Ad8fr+v!P)CkhdDSU*~!WE*Do~+ow(MurnsMOPyW!=H4=E$IsSsl z82t`QRU;WOsWrp@gSX<B{Yk6qN})FBqokVSD&th%Bg2mifQAPA6v-T8Dq?4s3F-M6 z3jfmQh?OtNbT+rxd!`+eo_}6y{5H=?@XXgIcV6C9)QD2AZaWn`UWUcf$=nD}oy<7| z<z+zDJx{+}TmRjh^Fbq7QW%6o-31qA=w_p4g=sVBQkHrr4<>RDo0I)$r#uNW+>fXH z^qko%n?lEJV^hA_B_Xsrh3$8U(qPVAM7omlnbF!p0|75d6_rmEJ{#WZ#D0xEWrD$Y zodd^K@I9tw<|<-*M^<bSqH3NYdJn@Jx(f;{ujfcaIoEZ|v|I5nDjd$x`vK6<n&zo2 z$ORYY`VDEy2aej{iFEtS&5Ie|L{Vso0rIL@{JUXloFeN?u{-VkIL=4*;CQ-52Wcoe zYDTB89MO;G8-t7dmL!dJ$4%YbBp_nA>aTTKu8;wMcpo)ZR)VZv(>mGqGEuo~3Zh&} zpR1ezTZ*}<*JaT?OY;3^=wLG{ylhjpc@qDyw%O>5g7La^3f=koc3zEDQk7E0kubt$ z(n@<Wo!ra&mWyXUtrLNRN^vCpSEsw}AX)o$*_0#RZNE#xJvw^XdWSa0nuYWbHI?di ziVZAPc+2c!1a+tsd>J{Ze_?hhz;VE`9sZuU5{M}F?RiiXh{5<pBN*3`BN;@|k_^Qx z*%L4(q~7&f-vuE3qQYC}NugV`yHh8rR5XZxc%KZ@e)kO*mxcxxmzK4L_Q$H69t)Kv zF_bhYnO2rM1eFd)Y=#7={V6Ef#ohmvqS0YrcCF_<$-Q8#{`y_d_m<e#`^5jb5>6>S zeIPIUBCzY2w22ZKS-;gOVp90ro|u?;K7D4OdZ0Qucn+6aGD4AObsz*!IM|)%=5rlq z{polPc>Wzn*1I(5N)^-R{I@#g^Creu_4q%WN-&1TLtP0v;MFuc;^wLV{JI#AjIw#I z1L8=zZs(N4W#`HM>co{gqbztJc_!NsFB+duojx8~o&*(lTiy8Z^mMCdo#GbFBul4< zi`tFR^)<3WIG$olA&~VXWd&uYm8xWgO3iP%)RZF@Bb=)Tqpb9-Nt39I3L$5FKAr>; zsQfC5Kf0a!`OmlR(sMi*%T%8aNr!g!7B7Vy&V2vq1z&NnZhhJSk;H7d&wEJ9%~u1R z`k2wq@V0bwO6@!3S@JQd1m-AYs)~CJ5XQxKb!C~VmUBf5TDFc*tL;Obd)Pp-0lova zaYb^VpHLjfG}EU21acNmeXZ|{-6M{Y`M7FF8eR8;7F$+La-n_`NVoN!(xZM&;-j;R zN}4tvPfktUztt&llgcKJy?^s`(U4Y@a7()C6Q!t)+<j(K%_|fWXD{QZKAVVK#Qjbl zJcYad&`mW}4K+%unU-~AjQ|F7TacGtL%9hSpXt!2e#$1-{CZ;>NtBT!*ti0Z>_$gp zcXxE4o4Nk0|Ei_P+EVygn|apu#5&Mhm}JEzz5m_N!DJo?;qbuO)mw9%Ncl|!o~g(B z@o2y&qFkHZTxxoWZJVRtXKAU(!b+b`En0TJFQJb*YR6-B@1rsMh_mPDoA|o8>c4^e z^g<fO?lyWTC$^Cq&<EX-6^vQiRqpsA<LyY%vkN5nEE`A4T~H@?)&2sei3RFqt1`__ zh9^6APDRv~({YCp=l=F){%*@_{Nq?e<n`eJ`<2yIsq23y=>26THe9esi>K1g%*fT@ zTI&VPKdmd?u>#5c<tsi8mws$u1`gM)px{2CSxdrqP9b?Q0^%uxaBMhUt69WA495E7 zb@8NC%Nmn*s-P#$X=Cf_k&0koyw>E@cqJ*pOuY`Wy#z@+kD)L_RTG$F0A2)Mf}G%C z?E~KX?bb-ptN)OGFN~8IQHJXEZ=X}TIuKY{=K5=Wqm_EgYgp0aeTDY<?fn(aaMx~J z>hS6a%h88q{5gs%yWLXNmlj7_Gazv{mSVo&)Afwg0(eK9jfLgA?FdVT)pZQqJ|;MV z4A!Hwh%`(YRI<{n&S!)fC4DhO^^F}24B3bk^6>Yq3%NYezPVS=0M-8~!>{*pkil5c z5TcqMRMkrfy<1ZvQRWqv1g61b!hd*~%P^-YugpR^fj=HI@FzA`F|)!@)qNC|vwcQM zuckSr6D40ft<c;oBI@lFEn=Wu))DI)IQ1sheYzeY^3!EZ_;dYkFV}Be4wHUX>+)ug zoc;ST$ZtM;z+aL}{^e=!)f<10-_&_GM^r}IUW+i`YVeGanIM8hDy$d7;$y0hByk!I zrDa0P=MQ|Qh!NK%(nw7eE(l2nL*fMpAFE8&q?zKxe}R}06)_~xiU>!=Bcu6`W|NM8 zvLlGSW4}Gy+VIB>wkkDFKeB5?3x-!plVZliuGOj{)g?hGWkOYlzx_a4Y1k=|8{zt~ zbeqahWk&$)yqofVAsP3qIuE-b>sgWpIqQIHMS_d-w$d7ncuHJP?2fBmjFY-d4vERq zmV1bp<7;>?qy@l(MwfsbPTWv!#EX7Q&c1X5U$#cn`=I(kxh({zFVM}vc>bza%!g_s zjW&#^-aAX+t+>JBtr&h|Zo}=Kqg<VFI-;$K`d5V9?g|&o@sdwEit-c^eKJ3DWWvZw z+5(u0kQ(%;uRL}g;@XXf+DbKDi2bRD#gh38e2M%};z?JyYUi`a^t-mx0!Ew$JqN`N zET}SZ3G!n_A8%cR>PH?bqX7nuB`$C=lOl<Q*g*p{;%?KLc_>C8inChYki;PGdp$Z+ z_IKuoeCDpK9O=_SFAm1-?70csz<-n>QEB0|7^1aNawiO&y6OX{8p>RvpR^VAMf7pm zwpfGu{*Iiw^!_cKC4<X<5(kwvS^a)HXL7~e@SvlCf}-i=*vBH~H2&gb2y`C|F_5BK z#$B-cZ7wm5Xe1(RBDcR+QLP@l?)ePprs71&yj4BH1S^vugzFUCE}`~SprBM`;QD7r zGR(*fQml64E+!aFtl$!vSzQ&QaZQAZS#Wa{>hc-Yg+=@s_}fX48;xx_!&Tp(Zhe^R z6cpG#dWy31NJU<+P|j<OlKH@V^7q|<6YfX>p2*W22e-_l{kxIXy=H*8{OX9vVR+o$ z#oyOw|H99I{ysQfRG+;Mq}fO=#$3_(RXoz9<9rNluMl-e<-C4i4*&9=olieA<aU5x zM3Yx<ff&qhIMR7sZ2jGZkA0}Q-=*l+=qbFn2-;T@V}&i=o3#5PO?*3sTJqeuKPYJn z4;FfJcN|{!Gw7X97fKEy&(uECl|9c-8$nygo1Ra@?C&Y~De>IX15O(4LGB3q1#k@& z_fy9#gvvPGR0N|&k%f`V7iy&U(9;jgx0Jo53n1<kStq!i?!3hPD}kM)Q=b_WtQP*Z zcj2!@X})GzDD|r9-rh7S*?wR7*x|RT4sm>BCzth{zScY{i|L&5YqeS?46<leUv-#c za~oYp<~CL{KVV%lHy`dZ8eKYVy}5r8wy?es2xOeaoku=le#9|fg|xe=Z@Bf!o;CQ4 z`lqYKbsS3Aqv&U<^LfH@fxJ9p92>-KnatS&`QazzO|2U^Krh6U7b)+AV^&$oKmbDA z=x+Tp#yxKM@W{~Pv;UXbwVcA{g=KiBj<ZF;Z%C^v&^{{2(T&i|-O+7Epj?J2D*j{k zfZ}a$vxDG%kd0{Q!b2Y`s>qI$+?H^W@8fnm5jXq?!`xDep#lpEEB^;g0}<}fG0Ru7 zNxb6GGx7yNc`<vAoFA083%QAh6J9~K0XfgdY*xXIXa0Tx*kq5|E)T+&-1>50|E?Tm z(VgFCO!ya{_P@#a)FNkT-Nr5k)WA&*#O$2BE6Jf(oW(aK0yLA;Dmz#%W=uu{e-#B! zAs#(r7}YhpYxSfFm1_=9>rT9^<63bHnz^DUQ^xcWy`A)FV=|g3P+Oip(IVC(rYYUt zt0+(WCR|K+N0C!xq@jzX`l#BoV9MXQT-~7?hPTJAVnm167Zbw~P(Ez6n2^GnF0yF7 z+qQF($ssl)l_!q}4=CzqA=JY_NwQS37lQ*NUCG+Y;d7`j<ea$9$%n3rN|sYKf{z3b zhP3#Tsi}>9%%O`k!ATwAix41?IFW2LC5pPGGS`}Wj2USa_s(8Hd4+>_{q6F3r>7c0 zc52mwrUU>wN#$oJ34nqljZgKW%;JdwqDjWN@motY69eZfKY;K6oQ~t-`E97o^v_{s z+RWOYC(ZtNl&<FO;X|HfbsC}phE<e)jwn@-oM?^x7^J?BDpxY|ie}xWfJ-rn@ZxMX z60bCl^gKP)NKTUYAYt%lnvjB6J_|q<PvIIlyreoFvz+$iPcAocuE?U%IvxyB-!f|* zr4&FPntF<QE)VsmxI!HYQ=l$4!!r=Xc}T<Bb0a33Kc}0BXflJBLA*ZXY7g#CX+MFW zB83fOTIhfo;c1W*;uhwzBx6Wo4`std6~3;gyyKbt`eib_t3I2+LK1Ib10(@x`(Tf< za4cC{VSDMz5<1b2i(2G*E2xja-R89<>V0sbr$CLkzR(1GCK%LcRF}aKmsYuHcE-A& zJO9Mi7@=ohF3?)4nhh0j9&+XS^E;;039Da*o@3!+A+X1`>cB_${6iI2ixRye{W0il zdg>`pK79$vN?ed(B1H`?s<JFaH#5DGP0ZK)ZL6Acc|d009N1E@%;HR2jWCw}+HLSx zX&7REK|0uDcR7wZIohBmO+!F^(|0!x)9o{>+ity`XBzHX-z~O&yLn*ZCQYdmb<Pn$ zhOymA(w6j(zfVcjWbG%La_2~bE}$`Y8z{4?MP2DUix>a2h%h!dh=)jSY79KRocEFJ zoN+Y}-TqB6^L2f#-!JGBxp;XlrZ5w=kXlaxD~jzRbIo+gY?d|df!Gd5Sc<BLAuG*y z{@Td;6ru5IB-I996y8b9wn>k!dMV$?0WPVdoT%xv;T_&34RMOYG%l<M9c>z^s729$ z3pH8qRc1$fCPbJE=-*5Fe&XdITz@QL_6t|3v*dQDUGSM94+k(qNK^Gw@%~h+$lUUW z8EfINipS3Bnnn-f-;eR*^TSPe{>BPiGIlI0jd%Pd3$*FK#vEu<6iQVix>ce7+Pv@` zWX0^kL@P*R#gcP0VJfHjjOkn&pRz)BZNrCD-J}4qqF0X!RqoArQV3)i^DzWB@#Dd@ zZi+Pk39l}{Kv;S75Lv1}HI6bFb-ziUWTfqM?vE(e`;;eF75BqFB%$kJm#cLMe!&}a z5M|{t!@uV3t7e(|8TZB-+Tu18>+(Vxt;qiBTt_o6aG1k<FU^D^TFY4=DmF<JP?IUz zsNW}JJvNs&nTs=<HFI>LsFA8hWyTUJgVQHO^*1ge4fEwYBM9~;#?O$jPHyW|3Lxq> z{xoS>xb$*m0;<&J`ssiW$)Hi=QhDfmZ>b}xIAqkpL@79TwS;i-5B3Su>fB677tRSh zVtsk{YiBrH)q3&UwMx{(R!AnZF&0ji@<`Y5yI4884kXIK)oBdEHIZ7kIvCre@vHBG zWZiY-P(&h$zY$p}#?*r1-oBZLe#~e&ANxUF;MxwTgC?o+3-0;vroI6(c=7>FrJ^@8 zqK2(Kjt1wRv(s{jpW)Si;E-nU<nC9Q;3}b0*dhdNh%M6W#Bhrgv~dqvu)5C#SEV7F znMXkRIj?4q$#6Tg>Xaqu!{jq3veux0tnwA@DO-$zBE#W@;qID_@|Ehk{qxy*vF;R! zeWDUF8}iNxHG;l<A8rQRZ1TZDBlkw*EqB4s!#?eoXgD>~pGIz_xuk2}F(%Y!r`dWw zYAS}|Bs*v-Repb`Fc>9?tB+9BAtl6)vwS;^`nzgJi%FBBF_b0a8NzL}aMiUi9bC0w zDGVO3{ma^~&hbaWZ?GbXtEP}h)K`)SN20&PrW7+~fywYUY8m5R+^55LDv_byOp!|> z{iFJoZ+<T2NLn}8MbBkPRf!pR<E_K>G_3xHv_glY{)rEFsv2e;sKe@O*S32*eOe9h zZ6fXcN{kM1IS<WdB_sC<Pt{Kx=OM3-+XGizw#-H0TUP5KUjFu(iTwRYyp}yZ@{R1@ zL<%SQ^rldm7u66=|H7`){GRws0D@Ziu}Xm8Q0={@U!mnI_9q|H)n{Yjr%a@|qGA8m zG0@>RE3~~^PtPDbs1wx_&bgoSi*D46)+OE(+Z8T&UvjsKZ)N#ng}c|kB1&&vyN$)l zzn!0!OXiO&t-=SnGoi39dMh-MV35f(QxqBTjj0!=msQx&iJ@DI6ZQi;rl3yX<ovXF zzb4kRulR$tpr<aE+gWoIB$utv`l;b2Bm(8W7Cd2jbnV7!1{NO)!`hiE(>hSn`NBL? zP(NXE_~pttuyCodhlkFQo?9d{-1bt#UWi;Yo44`z(+cQ&fdZ=yTttj7W2bKIcVnK} zIlU9IK{={NlD>F3X7KXK&B2=P5#@FcHb#0S!VzwY!pc}NmE0Su_z`8CW)0GMUR#b> zT4vNN!R~ST8RxI9Z@wvbUkRSo@n+-aq=V84Fo|5-_>7z+-%Q(k?()e_{F*Et)A~_Z zq?<V~uC&`nE_<qOL<ZtOR{eQhcP<7^Fcc@Wnb4-f?Df|vm7Eb60HhtqWO&da;V=}( zr1`dBavD4hKT<~&dMvM+?o%s2&Q1d>IQgaM$PDuID=lD3t|2LhBjCDyye0l$rKpFc z*DI(cbe`P=EQNjN`t9~5McKUTLWMy?eJynKE&3h%Ncv>#KCDV_{S6YBMXZjWZCVXH z9)+sa^)e4oeH!(aHq=CA*wYufY8O4YCo<|kX>?ZInG_az;4lVQTZPSM#er|)*%aB9 z#1ozToV~Lj-{d7C-!x`Rh{TBnx1SJ(E*<Lz?>QHCf5{l);!?+d6-O^SMxo>KPUrZ! zd+MtCAlEHYceFo$DOTy(jKqwTc09h^Y{68c+W}z@QE9>2TZt@?=8GSD1KvK#8S}6W z9%%~z(8}-NLgud0cwA4G@yGuF<Em0mKbw2%u({!yw0NFOBI=&$PaykWboiW}SHAWw zTPQsI=w_-@xT^E0Ej>>c;LP(t6K%CH%dBy|={{Yb^T!s?%Cw;vWTGM^-Q{8^jq)$G zV_2@T(A#lYohv&1FEaors-)~bMx}1P(~zMf^L!J+<fc)Z>Z*M!qCk<e%)>)BW8GbC zq?~$eF57=*f7&XaBDx%%YC!8(<x}eEhuIm}SdmB_kQu!1(4WX~u)g=CEA~&k75!sm zpUKJf$pXc7l@XdsgsnHOvJLZ>ED8RNUXfo0cgHpdMs~Y}w!-2BEFyW>g|D9f#$^7a zGc~pq(B<JR3|@I#X!h1(E)aP-Lt%o)fgB6jnA4~DNrJCni8T%F_h9F9wugKc+{wvv z%92>#?e6XNKdA$AhkSFlb+9-C(<B)p0<xjVY)F>)R-h^zb27!@Pj9#l{rq>hCb_D; z<{c{m*C~f`B@em30T#1}p*H`nCF6I2Sy(+<oq`pJZTe)m!9ky++b6%uEc2vy_ihws z=T~t)uCm$Nm{HzNjt;)*t^IW@`=h`HA*Ze;r{gS0v45M4=(Vhjfz_j|T|b|#b(S6d zI=64G$GReVg*)c*`$92R>%4qkTXj6nmZgHWrNahS0?NN*h$}I=b|V4sbGP-s=Two3 z*A6?Yzt8}VSa_Biu?9FQ+ca8*oSHA*H2qxNICdZ(+&iacG;K6-jTz<XG@mKg<w<Cp zu@;2r1pf7^ayXbxq)WTJXcw`>^=Z?$gG8{`_MKY_b@jw-l<H~@)<}ysko3**w$kGg z(ODmLu*C?zukQ{k49Z;VtZQiHG2?dtTNr-R0vz%iNMN8&e2V|Vq*md*Xw^a)MV7NE zVG<vz-9_<B62haXj5{WsdDfvmF@1e!qct%`=JF?S<<A(B-g^&bQMh1s+vR|#<DGvd zS`~bKA#Kk{P*R7Z${E_LkVM1Jz1?lKtse?Lx5-?^U4Zk?0xYM>gfBJ<I6E~o#*<S? z7MvNhfA@qNF7`~^Pp$-PW(tG#)wDf>y%0NQJFt5jjymuW@0U@XmA}lx4HAh~h(hIX zIo2St@8p;^6Ss*7N&4xFQMo<V^yBb7Jjs{f2DQL29x+^w9+PIsG+jA?AUMv`C3>s} z*g?$r)1I#dA&Q+m`6G325s-MBqXWv2qSj<hlo=<6$VpUcWHnGsA?i=#iiUVE03`Ov zl9gYL|Lren-`_#JDexZa2VhU(AK7bEVx+#ZwBsGgnhk(93XV~99z2|~N^d2QzFZJ} za;P0)7K^$EhaU-^yb$`qIdtyZv~RC|To*vT{XfJ!=!fjgN1NU51r<AyYn|Q-dw0M! z{U{TTu_|JmQ&wgy6VoBGhhXOjv74bO+c0W`I3rc+Itfq9kX9Qk3|iFx^N8YV<Cs_8 zbXjMyX|XOLjk9AT2f_s(F!uHAA@8C0-~$362NS$>t_5z2nxpDW>3{Qej2!44__*%Y z-zVbux|QYQYss2apntV{Yk4vrmKwfgdue_Akp3Ep!?9)SMfv)$9n@n{`;k3`qd(9A zYz#?|&;CAvqA%@u<%bWqPd`8_)2pojswAq*@p&zadibi?4`zoSKC^Ai5?74Rk7b?- zBO#>~5O-4esS|m2kga-aXZb2g-8F&s8+<y`=z$0Fz~zwEiXBoUjF<ZH%Y}&<6V)w@ zt^du0zyUH&d}9YhmA&6TNsg5Bln(L}Y>ZJ4iZ%P`f(enF(o*sidH1+GG;g2bG0L4) z-q`k(@7uWQKia(~jR)Hf;fT<RmEwBTSI6WV53#Uk2*R|qu|Jzu`-{tChIPGYPiN9d zh8VXOWb07}B9^?K-(x6AaZzl->OioxIb%g%h;5)_xr%<bF53@ggC9PyZEQz?MCS`@ zEhs=vlN9bfT-|g^3~P-)MP_7cm~~eH;mm|`hpaUf8)_vSh5e-?MGCy)eK{+<4biOe zKxE>*+A}5s%zOfv(wwJBDBe|tOD0Q|M4TLQ!TEHMJ+QH29VphHhpl?;Hf3%%%9%<m z{YoECp|p3FLt$SYZy3waBHS1EC8X(?Oip`2syEAk71SuiPAGMb#Di;KMWGh$wb*0R zUaD#0N<WC##KlH$V!xvSsv<IB#L*5a0{Y-`9BIZ%xn{13tJ}@2$F<H$Ezu~5ce#VY z1ne`0%jx2TgnVdC4>ND3$Z`#~$rGX;lVpi=W562K!uPz{EMOD+w+)bXwFjxAIJ&s6 z3_TxEpZ0F<VObu5N*pBdnz~PccO4=P`vH9y>yc+%L2ou^{iIpI4(TeP)?)8CZ5suJ z6n>EN=jj&ElW6yw{;9dGwB_Vw@0im+h{MQbdsu8eeinT1uL&otwf=qrn9y_D1vdfq z$4>vaY9=T{D7`Z{(GxCy2Ze-pYp&Gze<SQi;N)qwKMPupd)=#4o8MT<%6l1fi;T#l zd=rvL*HRS)og-^x>-T;Wd;5{pTYWL+p6gfBt2~E*R;*(DHuFczirq0-LSxdDzGBJk z4Y)}DQqrz+8b|5c=S3EFdNx*y^Nfr=XSePFTuMNJ`@6iuXe@t^OMKqmN~MQ+{&7p3 zOL+4G=UCdzw;f2#G7tdDaG=RL1CP?oFNNo4LUXj_{NF;vLoZPZN2l>24Z2}|@6QsB zcW65jo|Qgkk>bge3=Oa{23Z?Y-xZm@v2k*g2m|gHgQ$JX{^VQApDFf%AEm!73lVR2 z@^{Xub7<UZq=rPtn(jr{?bO&+SB!g@tm2kzK5rCL05&7m*mQhVh~Uf&TE>M&P&yjI zsn?|%!t!XXs}cb6&4U#PHKz`-G-2AdI){3Fu*s_-BqIv$r`t`k-pI=*rnHq7v$=qJ zYF0%7t?sG+_(8zoLyNwY%w#;rdjp`<yCe4Fl`#wUA~etl9R03VE`Jg-7og^cakUu! zvlw1u_PKj+m9eF(a2=>2KH8tB_hjbl)lC1IOWy^wOxf($9(4>y7W}}kl0<D+5T12Z zb-%LA6)q5|6n>$8FwCvQq+0RCE1R6A-?tJ`c}uqEo&yQ;C{5W*s}O9A9!v9bUxlW+ zD9<DOwD98AY%se>?`uP`W+G8V7#WqmW?mg8L?#^Sq-jWf7EnogLqWFAVJjn#sYF|y zt9sp7XM-jM1vd3v^CgN7UrI|U`DLzxCAF~^$%omV=%ggkV`a8wL;HW!vhwC*tl4Pu z>A`2_)7L!@?>qJ^i;U1CR+s%W?hXFDw`!?w;)Z)IO(TL5k=yT!Q0%VKbc={zj7;si z0{A#IGOzTp66NLK0q7bO>AC5T50->R3KSj0b1`s!f=X}85`V1laO29c`xH{(Oa-lX zoL5M<YkuKpqrss^bUhtpp~vtWbIMiYQ%Rz6l70|Y^*Gb;7u?_qw@q>nKK{_j9l~%- zDH%nLZ9zVK5MSFTuGOnV@*NEX+Z0Nu53ba1<`+i@Jo{O7g9c#yN_r2}DpA8?Mg%(j zrs-<%P(V9I)f~hixm%@D;Z-Uo-te>+LOP!mioy}29EYaxevec_BExRUEp;<y&ZsRg zZSA-sAibjfpmM4zgfSGo(@n10&-l#<D$~PxAlOGT)?m*pTws?YE?51%wOJR(u7iEF zRu6{;UWgz#w>x<e<8lGiJcjXD+-}UQnd(I(sg+q-;W4^x|JzMJq*3w%y-IfM5NUCl zom>;dh$n)Ykj#E;&?kh;T}isSiT6O=KW>35+&ZLp2bkm2Tuj!c42?kxy$2yi8Dp1= z+WE|;cp=rXkqIlI3PMMg+?GkR$6<CltXrD8J^BOqC@N|cetj1hgw4Q#*Ug3dc%)dg zA8#IA8?_Vrm>p<FuL&zVSV6^aCF_qJ8nqT2`5Jcw#b`3Zu@qQ6?53*kiK}eTngfDY znROLD!lj;i&KEmu;s|mH0xtSGfp@A%$~V&1L+HsoZ~skFvk6LCPd9gVQS~{E-*??H zkC=TSx~pc;M0<~D!)pDz=7k*+;aKwLz2o+>$br#xL36}7?zhe1)L(}&lGde?WUA)) z8d<HDr|~gRg>4<qbRpodxUVsZ$vK~JFf76|v_~j=nlS@p(pQI6pl&UC4Uqm@)d7vX zgZP83meqGKCx)c}iL8Ecb-AVMXk3Jn8c(-XT?<FFrVl*4ti3W#onO1^s&4dl#>k#~ zrhG1%h5q>+oF6P86Jx<UixP{uc187SG4huzq~{=thw#K0qe9LI%UmV~%DJUp)VveG z$-piV15Nf)A42~o-o#ER2)92iW@I!rO=RXOdMBlV7}x(XcsMQ=hTpi}Nqs<!>-iXb z^e^uF|8UQ@Yzf#-{<3hZM6e<aUImF%jlI)Z=v&m;%Ww}c20G`(Je%e+hxC&s@abn5 z1YUZlN>t*f6jA+g;#F)5tR|~~$8<s}FaiGsbNr`sU|FjPeZdS)y7!GBq4n+W^fRh2 zzX82K;_wQ1hj(n9qCy~J#JG_ZG<PXVMxyDMZSj)=PsA$GOE;7xH1|cv7PpM!?GkNI zja;b9e$POWmaSZL1w+}>NKiJXyl~agb?cx8-F^MwN;7K^NnmI6t5uhXwoP<lFBha} zH^`l5NacbT{!rdb_|T8mkW&=hF#3AXKx0E;HX)Q%W_Ewt!rcuf^z5sK@a`ia7{_VO ztEnyX!_*Ik=^cCN#?BHb)=I(5)k}P~FUew_;~s`6DWV(&GBT3MueZ$nn}a&Nd?kPs zs;1L7d+zv=CFL>ks+;QmG(s>|!3H{@IELp!nUO4RMJ^v6@lVskylaWd9d~@jP(+Z0 zMQVp{?m%iE?viU~v;^Q!MX+39VidQKTBVFnUo}iOoHJEMM8pkk<&itS)_EV<L*lDr z%$Ilzt(FRw|FenwzfL^BzU>DIul4#>YfSqiL9jSId2xC53+puC?%s@rgxq|EgS4j? zLf$ZK|80ZnJ&c8@xiO09sM<LmGNQ}N8|n-mZ;Y#eS`vL{(zTT3l5^wDH_>Lt!NR%# z^rjb@&*Ad9HSh1oLM-u!bT4MXP)*P^HbHL)+`!Vn@2y@Hka}u%-Rtl2^d*U(dAt9L z;kn+IJWMUR1U41bgn8tXL*UPiEpQ82ph)bGOu}<2Aa_EeyGxTp_Za}lKP7Sx|Jhtj z$44i9n|Ixe>UG5UnelNfWY4Xv5K_rThEF%iHxDzDiHV@iv8O7cGpo;oVcZub0TXgt z<$xm5V*O4<nkqL3pd;s>Frs%V*buLjnLU|{#aBg<dQ`%moXz-VxC34r%D<rkA^>su z?CZlpphU`lQjr3XRJQoE0qAeFZc>qu4i8i3`{1>cI+6<^Ck!hEgcyXq@RR8<Rg1{l zd;gp$d3i4}`@avIB%I%0qFqk3i_O0%lCWvCvJC}2ExR%^>?0p7i3u<SGd>ah>H2D& z=@L3rVfxl__J!T?%Kw#w*P4Sq)dit%rvI1QDVBu(Fy<1?@>|CSx{eH&P<px6I5LR* zwE2cHx+qLj7xWzo?+Uy#%nIkmWE@$`1M`~2MiKe~4+(zZ=LxM{wtZwJKw$l+2Y-Ve zG*1#<oMT(djiQOj)`|AMA+Pk9vG%@iuQKe8&LZy|XN_C_LoL|&hrIo#gowAjn1r`| zajRl=Uqs>i0`Myt1Gp&p@1e-c-%lyy;+)amh&Mf1oAFH_uf~jiJ%lxGoos!}2{%ni zj`re}|17(OgMLF1+)eGtD}NKAopcF2Kx*vz;L1A~5+*~<{%JElBna;tg^3P|Xg_Td z(cp#j*k(Br9_7Ch7chw{;6Z+%SE>GZ?mq%ioAD`KG$1-bcptGd$FvR1M(SPMQFL3c zQoy};7pj1rVM%Oo)q#K2x2_NW$L{Zrt`CN@2P~Hd@iA_xLs8zM5~HZZ`yFQYmkWSb zSojRgH=wT;8#^`Q?Qk%>kB5YJCy0die#GKG^diRz7+t7lO=xPm-h>5iU-W4ptp19M z5lC7uB@(z{|JIQM2X21~HYNxrt3QN8*<Qxm+K)Qi8nbe8m4-2q!N%RIuu-FiEwmE< z#s%@M!40|}tUvy>_kofA4@WJG1JHm)edMnfRLF`g*-QM}r7#%)6G?MAy=*=dge_@# zoupqM7f+`2<CsKI|0M?liJf3>6GuU>EmK`W{}-yA@HZ|-P?;@{@LTw-&^Zu>23!DO zZS|&gWGrPI-oFb+xH{y~b~rBh;qR~@h(o&Q5lG?8h+u(%RjDNA8>0UM$jmnYFzo*i zFwHl5iQ&w!VFEDlUw!{2_pg@!#0M!KO|>VE4B9oC>4M-)bpO`zDt2N&2-39=Gs%{% z?-nGKNbs`%jdZ0Qv(flnhJk6!N2&_#MUR>N6&+Tv5t;UPoidLDoo!@D?3K_NrUH!& zz=!Raff1fDX*>|`eYueE7gIpUf~FoT5L*4i^g`{2bZsmLr7qM|0@7|VtQ_H<z^IX< zB2w~%jzeknhekB)dKPZnBns(o<5`o$U*9wL4gGJ!N52SET!!LExP}Tpx6z2hcqpF8 zO#1>MT~E1t$EeVUba>zHdkg+!o<guBG4^@zHyY5{K+`;bt?lLhtQM~RgYIx@RQ)+y zGcBxg-TbH99@Y*0T&4bFJhxf+^6Q!auGczSu2fV5C8-1oMx!wuq)QG-Y`c}OzCvrc zD6Wq|G75lQR}6^I=D^F??AD&J6^ZEN>PsyBPYtLW8YBVnCY?~m?|cu^9P?WGPo<gb z%4f(4oSrC_r!JW2NRc#UY<87U+BY+`br1^!{+i9ny@tJh$qi@i4N#fu-##(L&Bger z;}uR~P~hEcPU7*QQxp=d$*NC4@ETZ<Q`zjBdIC+GQZ6RYvOTd2<rKUk;k5HF%ZDQM z^d|VPgt3OJp8_?q3p99@Db`uFLA!qKiv``+*HkZEQek<}#`l!<JMgct*cIj<T*1QN z?;`=(f%#tfBSO3PHA4I9|H0nBB7-5Y^ekcCI>w@|-uovzU+MU3lzQ7$wk9>iZ-WMc zo-#>FpHpqI^34`Deb93FAC5I%*4H$KJjm!QNOi3rj;sxK!XgT<Lbo-BD3V=5Ey4=o z$h;G)S75;Tzrj5i)|E<0cL*^awWK(k9Z+hK(TGzS#)N@d<zdWO8I4I8nEY>|fAVO% z*demqYJfH=>3C_LrTP$~u<W>o-<32hRS9>#Ed(Co8``YF=J<cVv#_>JOdk&L{{ce4 BGL`@U literal 0 HcmV?d00001 From 2659b49f78e2781ab81a794a403ac912ba98510b Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Sat, 29 Nov 2025 20:59:05 +0100 Subject: [PATCH 05/25] application: improved info about attributes, added #[Deprecated] --- application/cs/presenters.texy | 8 ++++++++ application/en/presenters.texy | 8 ++++++++ nette/cs/vulnerability-protection.texy | 13 +------------ nette/en/vulnerability-protection.texy | 13 +------------ 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/application/cs/presenters.texy b/application/cs/presenters.texy index ca1a69f0dd..04cd942e32 100644 --- a/application/cs/presenters.texy +++ b/application/cs/presenters.texy @@ -493,6 +493,14 @@ class MyPresenter extends Nette\Application\UI\Presenter Je důležité zdůraznit, že pokud povolíte metodu `OPTIONS`, musíte ji následně také patřičně obsloužit v rámci svého presenteru. Metoda je často používána jako tzv. preflight request, který prohlížeč automaticky odesílá před skutečným požadavkem, když je potřeba zjistit, zda je požadavek povolený z hlediska CORS (Cross-Origin Resource Sharing) politiky. Pokud metodu povolíte, ale neimplementujete správnou odpověď, může to vést k nekonzistencím a potenciálním bezpečnostním problémům. +Označení zastaralých akcí .{data-version:3.2.3} +----------------------------------------------- + +Atribut `#[Deprecated]` slouží k označení akcí, signálů nebo celých presenterů, které jsou zastaralé a měly by být v budoucnu odstraněny. Při generování odkazů na takto označené části aplikace Nette vyhodí varování, které vývojáře upozorní. + +Atribut lze aplikovat jak na celou třídu presenteru, tak na jednotlivé metody `action<Action>()`, `render<View>()` a `handle<Signal>()`. + + Další četba =========== diff --git a/application/en/presenters.texy b/application/en/presenters.texy index daa9cc87c9..7658686bbf 100644 --- a/application/en/presenters.texy +++ b/application/en/presenters.texy @@ -493,6 +493,14 @@ class MyPresenter extends Nette\Application\UI\Presenter It's important to emphasize that if you enable the `OPTIONS` method, you must subsequently handle it appropriately within your presenter. This method is often used as a so-called preflight request, which the browser automatically sends before the actual request when it's necessary to determine if the request is permissible according to the CORS (Cross-Origin Resource Sharing) policy. If you enable the method but don't implement the correct response, it can lead to inconsistencies and potential security problems. +Marking Deprecated Actions .{data-version:3.2.3} +------------------------------------------------ + +The `#[Deprecated]` attribute marks actions, signals, or entire presenters as deprecated and scheduled for future removal. When generating links to deprecated parts of the application, Nette throws a warning to alert developers. + +You can apply the attribute to either the entire presenter class or to individual `action<Action>()`, `render<View>()`, and `handle<Signal>()` methods. + + Further Reading =============== diff --git a/nette/cs/vulnerability-protection.texy b/nette/cs/vulnerability-protection.texy index d5323d3566..b28e24cf5a 100644 --- a/nette/cs/vulnerability-protection.texy +++ b/nette/cs/vulnerability-protection.texy @@ -48,18 +48,7 @@ Nette Framework **automaticky chrání formuláře a signály v presenterech** p $form->allowCrossOrigin(); ``` -nebo v případě signálu přidejte anotaci `@crossOrigin`: - -```php -/** - * @crossOrigin - */ -public function handleXyz() -{ -} -``` - -V Nette Application 3.2 můžete použít také atributy: +nebo v případě signálu přidejte atribut: ```php use Nette\Application\Attributes\Requires; diff --git a/nette/en/vulnerability-protection.texy b/nette/en/vulnerability-protection.texy index 721d493f5b..ceda856ccc 100644 --- a/nette/en/vulnerability-protection.texy +++ b/nette/en/vulnerability-protection.texy @@ -48,18 +48,7 @@ Nette Framework **automatically protects forms and signals in presenters** again $form->allowCrossOrigin(); ``` -or in the case of a signal, add the `@crossOrigin` annotation: - -```php -/** - * @crossOrigin - */ -public function handleXyz() -{ -} -``` - -In Nette Application 3.2, you can also use attributes: +or in the case of a signal, add the attribute: ```php use Nette\Application\Attributes\Requires; From 3007b820d3d140d0ce10aaf6f0a6e3a82fff33ea Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Sat, 29 Nov 2025 21:41:39 +0100 Subject: [PATCH 06/25] application: improved info about template variables and extensions --- application/cs/templates.texy | 115 +++++++++++++++++++++++--------- application/en/templates.texy | 119 +++++++++++++++++++++++++--------- 2 files changed, 174 insertions(+), 60 deletions(-) diff --git a/application/cs/templates.texy b/application/cs/templates.texy index feb0346ea4..72b2f1e0cf 100644 --- a/application/cs/templates.texy +++ b/application/cs/templates.texy @@ -114,15 +114,34 @@ Soubory, kde se dohledávají šablony layoutu, lze změnit překrytím metody [ Proměnné v šabloně ------------------ -Proměnné do šablony předáváme tak, že je zapíšeme do `$this->template` a potom je máme k dispozici v šabloně jako lokální proměnné: +Proměnné do šablony předáváme zápisem do `$this->template`. V šabloně jsou pak dostupné jako lokální proměnné: ```php $this->template->article = $this->articles->getById($id); ``` -Takto jednoduše můžeme do šablon předat jakékoliv proměnné. Při vývoji robustních aplikací ale bývá užitečnější se omezit. Například tak, že explicitně nadefinujeme výčet proměnných, které šablona očekává, a jejich typů. Díky tomu nám bude moci PHP kontrolovat typy, IDE správně našeptávat a statická analýza odhalovat chyby. -A jak takový výčet nadefinujeme? Jednoduše v podobě třídy a její properties. Pojmenujeme ji podobně jako presenter, jen s `Template` na konci: +Výchozí proměnné +---------------- + +Presentery a komponenty předávají do šablon několik užitečných proměnných automaticky: + +- `$basePath` je absolutní URL cesta ke kořenovému adresáři (např. `/eshop`) +- `$baseUrl` je absolutní URL ke kořenovému adresáři (např. `http://localhost/eshop`) +- `$user` je objekt [reprezentující uživatele |security:authentication] +- `$presenter` je aktuální presenter +- `$control` je aktuální komponenta nebo presenter +- `$flashes` pole [zpráv |presenters#Flash zprávy] zaslaných funkcí `flashMessage()` + +Pokud používáte vlastní třídu šablony, tyto proměnné se předají, pokud pro ně vytvoříte property. + + +Typově bezpečné šablony +----------------------- + +Při vývoji robustních aplikací je užitečné explicitně nadefinovat, jaké proměnné šablona očekává a jakého jsou typu. Získáte tak typovou kontrolu v PHP, chytré našeptávání v IDE a schopnost statické analýzy odhalovat chyby. + +Jak takový výčet nadefinovat? Jednoduše v podobě třídy s properties reprezentujícími proměnné šablony. Pojmenujeme ji podobně jako presenter, jen s `Template` na konci: ```php /** @@ -141,22 +160,22 @@ class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template } ``` -Objekt `$this->template` v presenteru bude nyní instancí třídy `ArticleTemplate`. Takže PHP bude při zápisu kontrolovat deklarované typy. A počínaje verzí PHP 8.2 upozorní i na zápis do neexistující proměnné, v předchozích verzích lze téhož dosáhnout použitím traity [Nette\SmartObject |utils:smartobject]. +Objekt `$this->template` v presenteru bude nyní instancí třídy `ArticleTemplate`. PHP tak bude při zápisu kontrolovat deklarované typy. -Anotace `@property-read` je určená pro IDE a statickou analýzu, díky ní bude fungovat našeptávání, viz "PhpStorm and code completion for $this⁠-⁠>⁠template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template. +Anotace `@property-read` slouží pro IDE a statickou analýzu, díky ní bude fungovat našeptávání, viz "PhpStorm and code completion for $this⁠-⁠>⁠template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template. [* phpstorm-completion.webp *] -Luxusu našeptávání si můžete dopřát i v šablonách, stačí do PhpStorm nainstalovat plugin pro Latte a uvést na začátek šablony název třídy, více v článku "Latte: jak na typový systém":https://blog.nette.org/cs/latte-jak-na-typovy-system: +Našeptávání můžete využít i přímo v šablonách. Stačí do PhpStorm nainstalovat plugin pro Latte a uvést na začátek šablony název třídy parametrů šablony, více v článku "Latte: jak na typový systém":https://blog.nette.org/cs/latte-jak-na-typovy-system: ```latte {templateType App\Presentation\Article\ArticleTemplate} ... ``` -Takto fungují i šablony v komponentách, stačí jen dodržet jmennou konvenci a pro komponentu např. `FifteenControl` vytvořit třídu šablony `FifteenTemplate`. +Totéž platí i pro komponenty. Stačí dodržet jmennou konvenci a pro komponentu např. `FifteenControl` vytvořit třídu parametrů `FifteenTemplate`. -Pokud potřebujete vytvořit `$template` jako instanci jiné třídy, využijte metodu `createTemplate()`: +Pokud potřebujete použít jinou třídu parametrů, využijte metodu `createTemplate()`: ```php public function renderDefault(): void @@ -169,21 +188,6 @@ public function renderDefault(): void ``` -Výchozí proměnné ----------------- - -Presentery a komponenty předávají do šablon několik užitečných proměnných automaticky: - -- `$basePath` je absolutní URL cesta ke kořenovému adresáři (např. `/eshop`) -- `$baseUrl` je absolutní URL ke kořenovému adresáři (např. `http://localhost/eshop`) -- `$user` je objekt [reprezentující uživatele |security:authentication] -- `$presenter` je aktuální presenter -- `$control` je aktuální komponenta nebo presenter -- `$flashes` pole [zpráv |presenters#Flash zprávy] zaslaných funkcí `flashMessage()` - -Pokud používáte vlastní třídu šablony, tyto proměnné se předají, pokud pro ně vytvoříte property. - - Vytváření odkazů ---------------- @@ -205,21 +209,67 @@ Více informací najdete v kapitole [Vytváření odkazů URL|creating-links]. Vlastní filtry, značky apod. ---------------------------- -Šablonovací systém Latte lze rozšířit o vlastní filtry, funkce, značky apod. Lze tak učinit přímo v metodě `render<View>` nebo `beforeRender()`: +Šablonovací systém Latte lze rozšířit o vlastní filtry, funkce, značky a další prvky. K dispozici jsou tři způsoby, jak to udělat, od nejrychlejších ad-hoc řešení až po architektonický přístup pro celou aplikaci. + +**Ad-hoc v metodách presenteru** + +Nejrychlejší způsob je přidat filtr nebo funkci přímo v kódu presenteru či komponenty. V presenteru je k tomu vhodná metoda `beforeRender()` nebo `render<View>()`: ```php -public function beforeRender(): void +protected function beforeRender(): void { // přidání filtru - $this->template->addFilter('foo', /* ... */); + $this->template->addFilter('money', fn($val) => round($val) . ' Kč'); + + // přidání funkce + $this->template->addFunction('isWeekend', fn($date) => $date->format('N') >= 6); +} +``` - // nebo konfigurujeme přímo objekt Latte\Engine +V šabloně pak: + +```latte +<p>Cena: {$price|money}</p> + +{if isWeekend($now)} ... {/if} +``` + +Pro složitější logiku můžete konfigurovat přímo objekt `Latte\Engine`: + +```php +protected function beforeRender(): void +{ $latte = $this->template->getLatte(); $latte->setFeature(Latte\Feature::MigrationWarnings); } ``` -Latte ve verzi 3 nabízí pokročilejší způsob a to vytvoření si [extension |latte:extending-latte#Latte Extension] pro každý webový projekt. Kusý příklad takové třídy: +**Pomocí atributů** + +Elegantní způsob je definovat filtry a funkce jako metody přímo ve [třídě parametrů šablony|#Typově bezpečné šablony] presenteru nebo komponenty a označit je atributy: + +```php +class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template +{ + #[Latte\Attributes\TemplateFilter] + public function money(float $val): string + { + return round($val) . ' Kč'; + } + + #[Latte\Attributes\TemplateFunction] + public function isWeekend(DateTimeInterface $date): bool + { + return $date->format('N') >= 6 + } +} +``` + +Latte automaticky rozpozná a zaregistruje metody označené těmito atributy. Název filtru nebo funkce v šabloně odpovídá názvu metody. Tyto metody nesmí být privátní. + +**Globálně pomocí Extension** + +Předchozí způsoby jsou vhodné pro filtry a funkce, které potřebujete jen v konkrétním presenteru nebo komponentě, nikoliv v celé aplikaci. Pro celou aplikaci je nejvhodnější vytvořit si [extension |latte:extending-latte#Latte Extension]. Jde o třídu, která centralizuje všechna rozšíření Latte pro celý projekt. Kusý příklad: ```php namespace App\Presentation\Accessory; @@ -251,11 +301,16 @@ final class LatteExtension extends Latte\Extension ]; } + private function filterTimeAgoInWords(DateTimeInterface $time): string + { + // ... + } + // ... } ``` -Zaregistrujeme ji pomocí [konfigurace |configuration#Šablony Latte]: +Extension zaregistrujeme pomocí [konfigurace |configuration#Šablony Latte]: ```neon latte: @@ -263,6 +318,8 @@ latte: - App\Presentation\Accessory\LatteExtension ``` +Výhodou extension je, že lze využít dependency injection, mít přístup k modelové vrstvě aplikace a všechna rozšíření mít přehledně na jednom místě. Extension umožnuje definovat i vlastní značky, providery, průchody pro Latte kompilátor a další. + Překládání ---------- diff --git a/application/en/templates.texy b/application/en/templates.texy index 90f641d221..1c4d0315eb 100644 --- a/application/en/templates.texy +++ b/application/en/templates.texy @@ -111,18 +111,37 @@ Using `$this->setLayout(false)` or the `{layout none}` tag inside the template d The files where layout templates are looked up can be changed by overriding the [formatLayoutTemplateFiles() |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()] method, which returns an array of possible file names. -Variables in the Template -------------------------- +Template Variables +------------------ -Variables are passed to the template by writing them to `$this->template`, and then they are available in the template as local variables: +Variables are passed to templates by writing them to `$this->template`. They then become available in the template as local variables: ```php $this->template->article = $this->articles->getById($id); ``` -This way, we can easily pass any variables to templates. However, when developing robust applications, it is often more useful to impose limitations. For example, by explicitly defining a list of variables that the template expects and their types. This allows PHP to perform type checking, the IDE to provide correct autocompletion, and static analysis to detect errors. -And how do we define such a list? Simply in the form of a class and its properties. We name it similarly to the presenter, but with `Template` at the end: +Default Variables +----------------- + +Presenters and components automatically pass several useful variables to templates: + +- `$basePath` is the absolute URL path to the root directory (e.g., `/eshop`) +- `$baseUrl` is the absolute URL to the root directory (e.g., `http://localhost/eshop`) +- `$user` is an object [representing the user |security:authentication] +- `$presenter` is the current presenter +- `$control` is the current component or presenter +- `$flashes` is an array of [messages |presenters#Flash Messages] sent by the `flashMessage()` function + +If you use a custom template class, these variables are passed if you create a property for them. + + +Type-Safe Templates +------------------- + +When developing robust applications, it's useful to explicitly define which variables the template expects and their types. This provides type checking in PHP, smart hints in your IDE, and enables static analysis to catch errors. + +How do you define such a list? Simply as a class with properties representing template variables. Name it similarly to the presenter, just with `Template` at the end: ```php /** @@ -141,22 +160,22 @@ class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template } ``` -The `$this->template` object in the presenter will now be an instance of the `ArticleTemplate` class. So PHP will check the declared types upon writing. And starting from PHP 8.2, it will also warn about writing to a non-existent variable; in previous versions, the same can be achieved using the [Nette\SmartObject |utils:smartobject] trait. +The `$this->template` object in the presenter will now be an instance of the `ArticleTemplate` class. PHP will thus check the declared types when writing. -The `@property-read` annotation is intended for IDEs and static analysis; thanks to it, autocompletion will work, see "PhpStorm and code completion for $this->template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template. +The `@property-read` annotation is for the IDE and static analysis, enabling code completion, see "PhpStorm and code completion for $this⁠-⁠>⁠template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template. [* phpstorm-completion.webp *] -You can enjoy the luxury of autocompletion in templates too; just install the Latte plugin for PhpStorm and specify the class name at the beginning of the template, more in the article "Latte: How to Use Type System":https://blog.nette.org/en/latte-how-to-use-type-system: +You can also use code completion directly in templates. Just install the Latte plugin for PhpStorm and specify the template parameter class name at the beginning of the template, more in the article "Latte: how to use the type system":https://blog.nette.org/en/latte-how-to-use-type-system: ```latte {templateType App\Presentation\Article\ArticleTemplate} ... ``` -This is also how templates work in components; just follow the naming convention and create a template class `FifteenTemplate` for a component like `FifteenControl`. +The same applies to components. Just follow the naming convention and create a parameter class `FifteenTemplate` for a component like `FifteenControl`. -If you need to create `$template` as an instance of another class, use the `createTemplate()` method: +If you need to use a different parameter class, use the `createTemplate()` method: ```php public function renderDefault(): void @@ -169,21 +188,6 @@ public function renderDefault(): void ``` -Default Variables ------------------ - -Presenters and components automatically pass several useful variables to templates: - -- `$basePath` is the absolute URL path to the root directory (e.g., `/eshop`) -- `$baseUrl` is the absolute URL to the root directory (e.g., `http://localhost/eshop`) -- `$user` is an object [representing the user |security:authentication] -- `$presenter` is the current presenter -- `$control` is the current component or presenter -- `$flashes` is an array of [messages |presenters#Flash Messages] sent by the `flashMessage()` function - -If you use a custom template class, these variables are passed if you create a property for them. - - Creating Links -------------- @@ -205,21 +209,67 @@ More information can be found in the chapter [Creating URL Links|creating-links] Custom Filters, Tags, etc. -------------------------- -The Latte templating system can be extended with custom filters, functions, tags, etc. This can be done directly in the `render<View>` or `beforeRender()` method: +The Latte templating system can be extended with custom filters, functions, tags, and other elements. There are three approaches available, ranging from quick ad-hoc solutions to architectural patterns for entire applications. + +**Ad-hoc in Presenter Methods** + +The quickest approach is adding filters or functions directly in presenter or component code. In presenters, the `beforeRender()` or `render<View>()` methods work well for this: ```php -public function beforeRender(): void +protected function beforeRender(): void { // adding a filter - $this->template->addFilter('foo', /* ... */); + $this->template->addFilter('money', fn($val) => '$' . number_format($val, 2)); + + // adding a function + $this->template->addFunction('isWeekend', fn($date) => $date->format('N') >= 6); +} +``` + +In the template: + +```latte +<p>Price: {$price|money}</p> + +{if isWeekend($now)} ... {/if} +``` - // or configure the Latte\Engine object directly +For more complex logic, you can configure the `Latte\Engine` object directly: + +```php +protected function beforeRender(): void +{ $latte = $this->template->getLatte(); $latte->setFeature(Latte\Feature::MigrationWarnings); } ``` -Latte version 3 offers a more advanced way by creating an [extension |latte:extending-latte#Latte Extension] for each web project. Here is a brief example of such a class: +**Using Attributes** + +A more elegant approach is defining filters and functions as methods directly in the presenter or component's [template parameter class|#Type-safe templates], marked with attributes: + +```php +class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template +{ + #[Latte\Attributes\TemplateFilter] + public function money(float $val): string + { + return '$' . number_format($val, 2); + } + + #[Latte\Attributes\TemplateFunction] + public function isWeekend(DateTimeInterface $date): bool + { + return $date->format('N') >= 6 + } +} +``` + +Latte automatically discovers and registers methods marked with these attributes. The filter or function name in templates matches the method name. These methods must not be private. + +**Globally Using Extensions** + +The previous approaches suit filters and functions needed only in specific presenters or components, not application-wide. For the entire application, creating an [extension |latte:extending-latte#Latte Extension] works best. This class centralizes all Latte extensions for your project. A brief example: ```php namespace App\Presentation\Accessory; @@ -251,11 +301,16 @@ final class LatteExtension extends Latte\Extension ]; } + private function filterTimeAgoInWords(DateTimeInterface $time): string + { + // ... + } + // ... } ``` -We register it using [configuration |configuration#Latte Templates]: +Register the extension through [configuration |configuration#Latte Templates]: ```neon latte: @@ -263,6 +318,8 @@ latte: - App\Presentation\Accessory\LatteExtension ``` +Extensions offer several advantages: dependency injection support, access to your application's model layer, and centralized management of all extensions. They also support custom tags, providers, compiler passes, and more. + Translating ----------- From 07f569b4e500ca5aeed3474461ac7f158fa69dcd Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Mon, 1 Dec 2025 19:05:14 +0100 Subject: [PATCH 07/25] nette/utils 4.0.10 --- utils/cs/datetime.texy | 14 +++++++++++ utils/cs/helpers.texy | 11 +++++++++ utils/cs/iterables.texy | 21 +++++++++++++++++ utils/cs/type.texy | 51 ++++++++++++++++++++++++++++++++++++++--- utils/en/datetime.texy | 14 +++++++++++ utils/en/helpers.texy | 11 +++++++++ utils/en/iterables.texy | 21 +++++++++++++++++ utils/en/type.texy | 51 ++++++++++++++++++++++++++++++++++++++--- 8 files changed, 188 insertions(+), 6 deletions(-) diff --git a/utils/cs/datetime.texy b/utils/cs/datetime.texy index 6b2788d3e1..f685af7909 100644 --- a/utils/cs/datetime.texy +++ b/utils/cs/datetime.texy @@ -4,6 +4,9 @@ Datum a čas .[perex] [api:Nette\Utils\DateTime] je třída, která rozšiřuje nativní [php:DateTime] o další funkce. +Oproti svému předchůdci je striktní. Zatímco PHP **tiše akceptuje** nesmyslná data jako `0000-00-00` (převede na `-0001-11-30`) nebo `2024-02-31` (převede na `2024-03-02`), `Nette\Utils\DateTime` v takových případech vyhodí výjimku. + +Zároveň **opravuje chování** při přechodu na letní/zimní čas, kdy v nativním PHP může přičtení relativního času (např. `+100 minutes`) "vést k dřívějšímu výslednému času":https://phpfashion.com/cs/100-minut-je-mene-nez-50-paradoxy-php-pri-zmene-casu než přičtení kratšího úseku (např. `+50 minutes`). Nette zajišťuje, že aritmetika funguje intuitivně a `+100 minutes` je vždy více než `+50 minutes`. Instalace: @@ -46,6 +49,17 @@ DateTime::createFromFormat('d.m.Y', '26.02.1994', 'Europe/London'); ``` +static relativeToSeconds(string $str): int .[method]{data-version:4.0.7} +------------------------------------------------------------------------ +Převede relativní časový údaj na sekundy. Hodí se pro převod časů jako `5 minutes` nebo `2 hours` na číselnou hodnotu. + +```php +DateTime::relativeToSeconds('1 minute'); // 60 +DateTime::relativeToSeconds('10 minutes'); // 600 +DateTime::relativeToSeconds('-1 hour'); // -3600 +``` + + modifyClone(string $modify=''): static .[method] ------------------------------------------------ Vytvoří kopii s upraveným časem. diff --git a/utils/cs/helpers.texy b/utils/cs/helpers.texy index 2b5bcbe27e..49e37ebaed 100644 --- a/utils/cs/helpers.texy +++ b/utils/cs/helpers.texy @@ -84,3 +84,14 @@ Helpers::getSuggestion($items, 'fo'); // 'foo' Helpers::getSuggestion($items, 'barr'); // 'bar' Helpers::getSuggestion($items, 'baz'); // 'bar', ne 'baz' ``` + + +splitClassName(string $name): array .[method]{data-version:4.0.10} +------------------------------------------------------------------ + +Rozdělí celý název třídy v PHP na jmenný prostor a zkrácený název třídy. Vrací pole dvou řetězců, kde první prvek je namespace a druhý název třídy. + +```php +Helpers::splitClassName('Nette\Utils\Helpers'); // ['Nette\Utils', 'Helpers'] +Helpers::splitClassName('Foo'); // ['', 'Foo'] +``` diff --git a/utils/cs/iterables.texy b/utils/cs/iterables.texy index 77af7696df..ebf73344b7 100644 --- a/utils/cs/iterables.texy +++ b/utils/cs/iterables.texy @@ -140,6 +140,27 @@ $memoized = Iterables::memoize($iterator); Tato metoda je užitečná v situacích, kdy potřebujete vícekrát projít stejnou sadu dat, ale původní iterátor neumožňuje opakovanou iteraci nebo by opakované procházení bylo nákladné (např. při čtení dat z databáze nebo souboru). +repeatable(callable $factory): IteratorAggregate .[method]{data-version:4.0.10} +------------------------------------------------------------------------------- + +Umožňuje opakovanou iteraci objektů, které to běžně nedovolují, typicky [PHP generátorů |https://www.php.net/manual/en/language.generators.overview.php]. Metoda `repeatable()` tento problém elegantně řeší: místo samotného iterátoru jí předáte funkci, která iterátor vytváří. Tato továrna je pak zavolána automaticky při každém novém průchodu cyklem. + +```php +// Běžný generátor, který nelze projít dvakrát +$generator = function () { + yield 'A'; + yield 'B'; +}; + +$iterator = Iterables::repeatable($generator); + +foreach ($iterator as $v) echo $v; // Vypíše: AB +foreach ($iterator as $v) echo $v; // Vypíše: AB (generátor se spustil znovu) +``` + +Tato metoda je alternativou k [#memoize()] v situacích, kdy pracujete s **velkým objemem dat**, jelikož `repeatable()` si data neukládá, ale při každém průchodu je generuje znovu. + + some(iterable $iterable, callable $predicate): bool .[method] ------------------------------------------------------------- diff --git a/utils/cs/type.texy b/utils/cs/type.texy index d4c539ee67..f2af19e5f8 100644 --- a/utils/cs/type.texy +++ b/utils/cs/type.texy @@ -2,8 +2,13 @@ PHP Typ ******* .[perex] -[api:Nette\Utils\Type] je třída pro práci s datovými typy PHP. +[api:Nette\Utils\Type] reprezentuje datový typ PHP. Slouží k analýze, porovnávání a manipulaci s typy, ať už pocházejí z řetězce nebo z reflexe. +PHP má dnes velmi bohatý typový systém: od skalárních typů (`int`, `string`) přes objekty a rozhraní až po složené typy (union `A|B`, intersection `A&B` nebo disjunktivní normální formy `(A&B)|D`). Navíc existují speciální typy jako `void`, `never`, `mixed` nebo relativní `self` či `static`. + +Práce s těmito typy nativně, zejména přes `ReflectionType`, je často zdlouhavá, protože musíte rekurzivně rozlišovat mezi `ReflectionNamedType`, `ReflectionUnionType` a dalšími objekty. Třída `Nette\Utils\Type` toto vše zapouzdřuje a poskytuje **jednotné a srozumitelné API** pro práci s jakýmkoliv typem, který PHP podporuje. + +Umožňuje například snadno zjistit, zda jeden typ [akceptuje|#allows] druhý (kompatibilita), [rozšiřovat typy|#with] nebo převádět reflexe na čitelný zápis. Instalace: @@ -45,6 +50,25 @@ echo $type; // 'Foo|Bar' ``` +fromValue(mixed $value): Type .[method]{data-version:4.0.10} +------------------------------------------------------------ + +Statická metoda, která vytvoří objekt Type podle typu předané hodnoty. + +```php +$type = Type::fromValue('hello'); // 'string' +$type = Type::fromValue(123); // 'int' +$type = Type::fromValue(new stdClass); // 'stdClass' +``` + +Pro resources vrací `mixed`, protože PHP typ `resource` nezná. U anonymních tříd vrací nejbližšího předka nebo typ `object`. + +```php +$obj = new class extends Foo { }; +$type = Type::fromValue($obj); // 'Foo' +``` + + getNames(): (string|array)[] .[method] -------------------------------------- @@ -183,8 +207,8 @@ $type->isClassKeyword(); // false ``` -allows(string $type): bool .[method] ------------------------------------- +allows(string|Type $type): bool .[method] +----------------------------------------- Metoda `allows()` ověřuje kompatibilitu typů. Například umožní zjistit, jestli hodnota určitého typu by mohla být předaná jako parametr. @@ -197,3 +221,24 @@ $type->allows('Foo'); // false $type = Type::fromString('mixed'); $type->allows('null'); // true ``` + + +with(string|Type $type): Type .[method]{data-version:4.0.10} +------------------------------------------------------------ + +Vrací objekt Type, který akceptuje jak původní typ, tak i nově přidaný. Vytváří tzv. union type. + +Metoda je chytrá a typy zbytečně nezdvojuje. Pokud přidáte typ, který je již obsažen, nebo je nadmnožinou stávajícího typu (např. přidání `mixed` k `string`), vrátí se zjednodušený výsledek. + +```php +$type = Type::fromString('string'); + +// Rozšíření na nullable string +echo $type->with('null'); // '?string' + +// Vytvoření union typu +echo $type->with('int'); // 'string|int' + +// Přidání typu, který "přebije" vše ostatní +echo $type->with('mixed'); // 'mixed' +``` diff --git a/utils/en/datetime.texy b/utils/en/datetime.texy index 3fc7cdf0ac..5b00d789d3 100644 --- a/utils/en/datetime.texy +++ b/utils/en/datetime.texy @@ -4,6 +4,9 @@ Date and Time .[perex] [api:Nette\Utils\DateTime] is a class that extends the native [php:DateTime] with additional useful features. +Compared to the native class, it is strict. While PHP **silently accepts** invalid dates like `0000-00-00` (converts to `-0001-11-30`) or `2024-02-31` (converts to `2024-03-02`), `Nette\Utils\DateTime` throws an exception in such cases. + +It also **fixes the behavior** during Daylight Saving Time (DST) transitions, where in native PHP adding a relative time (e.g., `+100 minutes`) can "result in an earlier time":https://phpfashion.com/en/100-minutes-is-less-than-50-php-paradoxes-during-time-changes than adding a shorter period (e.g., `+50 minutes`). Nette ensures that arithmetic works intuitively and `+100 minutes` is always more than `+50 minutes`. Installation: @@ -46,6 +49,17 @@ DateTime::createFromFormat('d.m.Y', '26.02.1994', 'Europe/London'); // create wi ``` +static relativeToSeconds(string $str): int .[method]{data-version:4.0.7} +------------------------------------------------------------------------ +Converts a relative time string to seconds. It is useful for converting times like `5 minutes` or `2 hours` to a numeric value. + +```php +DateTime::relativeToSeconds('1 minute'); // 60 +DateTime::relativeToSeconds('10 minutes'); // 600 +DateTime::relativeToSeconds('-1 hour'); // -3600 +``` + + modifyClone(string $modify=''): static .[method] ------------------------------------------------ Creates a copy with a modified time. diff --git a/utils/en/helpers.texy b/utils/en/helpers.texy index 280cdb8e4c..4c69556b64 100644 --- a/utils/en/helpers.texy +++ b/utils/en/helpers.texy @@ -84,3 +84,14 @@ Helpers::getSuggestion($items, 'fo'); // 'foo' Helpers::getSuggestion($items, 'barr'); // 'bar' Helpers::getSuggestion($items, 'baz'); // 'bar', not 'baz' ``` + + +splitClassName(string $name): array .[method]{data-version:4.0.10} +------------------------------------------------------------------ + +Splits a PHP class name into a namespace and a short class name. Returns an array of two strings where the first is the namespace and the second is the class name. + +```php +Helpers::splitClassName('Nette\Utils\Helpers'); // ['Nette\Utils', 'Helpers'] +Helpers::splitClassName('Foo'); // ['', 'Foo'] +``` diff --git a/utils/en/iterables.texy b/utils/en/iterables.texy index a5f06f8be0..56c86f60d7 100644 --- a/utils/en/iterables.texy +++ b/utils/en/iterables.texy @@ -140,6 +140,27 @@ $memoized = Iterables::memoize($iterator); This method is useful in situations where you need to iterate over the same dataset multiple times, but the original iterator doesn't allow repeated iteration, or re-traversing would be costly (e.g., reading data from a database or file). +repeatable(callable $factory): IteratorAggregate .[method]{data-version:4.0.10} +------------------------------------------------------------------------------- + +Allows repeated iteration of objects that otherwise do not support it, typically [PHP generators |https://www.php.net/manual/en/language.generators.overview.php]. The `repeatable()` method solves this problem elegantly: instead of passing the iterator itself, you pass a function that creates it. This factory is then called automatically during each iteration loop. + +```php +// A standard generator that cannot be iterated twice +$generator = function () { + yield 'A'; + yield 'B'; +}; + +$iterator = Iterables::repeatable($generator); + +foreach ($iterator as $v) echo $v; // Prints: AB +foreach ($iterator as $v) echo $v; // Prints: AB (generator ran again) +``` + +This method is an alternative to [#memoize()] in situations where you are working with **large amounts of data**, since `repeatable()` does not cache data, but generates it again during each iteration. + + some(iterable $iterable, callable $predicate): bool .[method] ------------------------------------------------------------- diff --git a/utils/en/type.texy b/utils/en/type.texy index 08886b2e91..f462bbb31c 100644 --- a/utils/en/type.texy +++ b/utils/en/type.texy @@ -2,8 +2,13 @@ PHP Type ******** .[perex] -[api:Nette\Utils\Type] is a class for working with PHP data types. +[api:Nette\Utils\Type] represents a PHP data type. It is used for analyzing, comparing, and manipulating types, whether obtained from a string or reflection. +PHP currently has a very rich type system: from scalar types (`int`, `string`), through objects and interfaces, to complex types (union `A|B`, intersection `A&B`, or disjunctive normal forms `(A&B)|D`). Additionally, there are special types like `void`, `never`, `mixed`, or relative types `self` and `static`. + +Working with these types natively, especially via `ReflectionType`, is often cumbersome because you must recursively distinguish between `ReflectionNamedType`, `ReflectionUnionType`, and other objects. The `Nette\Utils\Type` class encapsulates all of this and provides a **unified and intuitive API** for working with any type supported by PHP. + +For example, it allows you to easily check if one type [accepts|#allows] another (compatibility), [extend types|#with], or convert reflections into a readable notation. Installation: @@ -45,6 +50,25 @@ echo $type; // 'Foo|Bar' ``` +fromValue(mixed $value): Type .[method]{data-version:4.0.10} +------------------------------------------------------------ + +Static method that creates a Type object based on the type of the passed value. + +```php +$type = Type::fromValue('hello'); // 'string' +$type = Type::fromValue(123); // 'int' +$type = Type::fromValue(new stdClass); // 'stdClass' +``` + +For resources, it returns `mixed`, as PHP does not support the `resource` type. For anonymous classes, it returns the name of the nearest ancestor or `object`. + +```php +$obj = new class extends Foo { }; +$type = Type::fromValue($obj); // 'Foo' +``` + + getNames(): (string|array)[] .[method] -------------------------------------- @@ -183,8 +207,8 @@ $type->isClassKeyword(); // false ``` -allows(string $type): bool .[method] ------------------------------------- +allows(string|Type $type): bool .[method] +----------------------------------------- The `allows()` method checks type compatibility. For example, it can determine if a value of a certain type could be passed as a parameter to a function expecting this type. @@ -197,3 +221,24 @@ $type->allows('Foo'); // false $type = Type::fromString('mixed'); $type->allows('null'); // true ``` + + +with(string|Type $type): Type .[method]{data-version:4.0.10} +------------------------------------------------------------ + +Returns a Type object that accepts both the original type and the one being added. It creates a so-called union type. + +The method is clever and does not duplicate types unnecessarily. If you add a type that is already present, or is a superset of the current type (e.g., adding `mixed` to `string`), the result is simplified. + +```php +$type = Type::fromString('string'); + +// Extending to nullable string +echo $type->with('null'); // '?string' + +// Creating a union type +echo $type->with('int'); // 'string|int' + +// Adding a type that supersedes everything +echo $type->with('mixed'); // 'mixed' +``` From 6782ded7db8a696adf51b90dfc402ebe704e8f2e Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Sat, 29 Nov 2025 20:59:05 +0100 Subject: [PATCH 08/25] nette/application 3.2.9 --- application/cs/templates.texy | 14 ++++++++++++++ application/en/templates.texy | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/application/cs/templates.texy b/application/cs/templates.texy index 72b2f1e0cf..568b40837e 100644 --- a/application/cs/templates.texy +++ b/application/cs/templates.texy @@ -120,6 +120,20 @@ Proměnné do šablony předáváme zápisem do `$this->template`. V šabloně j $this->template->article = $this->articles->getById($id); ``` +Pokud chcete, aby se hodnota určité property automaticky předala do šablony jako proměnná, označte ji atributem `#[TemplateVariable]` a viditelností public nebo protected: .{data-version:3.2.9} + +```php +use Nette\Application\Attributes\TemplateVariable; + +class ArticlePresenter extends Nette\Application\UI\Presenter +{ + #[TemplateVariable] + public string $siteName = 'Můj blog'; +} +``` + +Pokud do šablony vložíte proměnnou se stejným názvem, `#[TemplateVariable]` ji nepřepíše. + Výchozí proměnné ---------------- diff --git a/application/en/templates.texy b/application/en/templates.texy index 1c4d0315eb..6a5ba43fd3 100644 --- a/application/en/templates.texy +++ b/application/en/templates.texy @@ -120,6 +120,20 @@ Variables are passed to templates by writing them to `$this->template`. They the $this->template->article = $this->articles->getById($id); ``` +To automatically pass a property value to the template as a variable, mark it with the `#[TemplateVariable]` attribute and public or protected visibility: .{data-version:3.2.9} + +```php +use Nette\Application\Attributes\TemplateVariable; + +class ArticlePresenter extends Nette\Application\UI\Presenter +{ + #[TemplateVariable] + public string $siteName = 'My blog'; +} +``` + +If you pass a variable with the same name to the template, `#[TemplateVariable]` won’t override it. + Default Variables ----------------- From 3bf5b651d3e91fce369666d7b537800fd793ac43 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Sat, 27 Dec 2025 19:02:41 +0100 Subject: [PATCH 09/25] adding missing stuff in nette/http --- http/cs/request.texy | 29 +++++++++++++++++++++++++++++ http/cs/response.texy | 10 +++++----- http/cs/urls.texy | 6 ++++++ http/en/request.texy | 29 +++++++++++++++++++++++++++++ http/en/response.texy | 10 +++++----- http/en/urls.texy | 6 ++++++ nette/cs/glossary.texy | 7 +++++++ nette/en/glossary.texy | 7 +++++++ 8 files changed, 94 insertions(+), 10 deletions(-) diff --git a/http/cs/request.texy b/http/cs/request.texy index 5dc90de308..1827f489e0 100644 --- a/http/cs/request.texy +++ b/http/cs/request.texy @@ -184,6 +184,30 @@ $body = $httpRequest->getRawBody(); ``` +getOrigin(): ?UrlImmutable .[method] +------------------------------------ +Vrací origin, ze kterého požadavek přišel. Origin se skládá z protokolu, hostname a portu - například `https://example.com:8080`. Vrací `null`, pokud hlavička origin není přítomna nebo je nastavena na `'null'`. + +```php +$origin = $httpRequest->getOrigin(); +echo $origin; // https://example.com:8080 +echo $origin?->getHost(); // example.com +``` + +Prohlížeč posílá hlavičku `Origin` v následujících případech: +- Požadavky mezi doménami (AJAX volání na jinou doménu) +- POST, PUT, DELETE a další modifikující požadavky +- Požadavky provedené pomocí Fetch API + +Prohlížeč NEPOSÍLÁ hlavičku `Origin` při: +- Běžných GET požadavcích na stejnou doménu (navigace v rámci téže domény) +- Přímé navigaci zadáním URL do adresního řádku +- Požadavcích z jiných klientů než prohlížeče (pokud není ručně přidána) + +.[note] +Na rozdíl od hlavičky `Referer` obsahuje `Origin` pouze schéma, host a port - nikoli celou cestu URL. To ji činí vhodnější pro bezpečnostní kontroly při zachování soukromí uživatele. Hlavička `Origin` se primárně používá pro validaci [CORS |nette:glossary#Cross-Origin Resource Sharing (CORS)] (Cross-Origin Resource Sharing). + + detectLanguage(array $langs): ?string .[method] ----------------------------------------------- Detekuje jazyk. Jako parametr `$lang` předáme pole s jazyky, které aplikace podporuje, a ona vrátí ten, který by viděl návštěvníkův prohlížeč nejraději. Nejsou to žádná kouzla, jen se využívá hlavičky `Accept-Language`. Pokud nedojde k žádné shodě, vrací `null`. @@ -389,6 +413,11 @@ getTemporaryFile(): string .[method] Vrací cestu k dočasné lokaci uploadovaného souboru. V případě, že upload nebyl úspěšný, vrací `''`. +__toString(): string .[method] +------------------------------ +Vrací cestu k dočasnému umístění nahraného souboru. To umožňuje objekt `FileUpload` použít přímo jako řetězec. + + isImage(): bool .[method] ------------------------- Vrací `true`, pokud nahraný soubor je obrázek ve formátu JPEG, PNG, GIF, WebP nebo AVIF. Detekce probíhá na základě jeho signatury a neověřuje se integrita celého souboru. Zda není obrázek poškozený lze zjistit například pokusem o jeho [načtení |#toImage]. diff --git a/http/cs/response.texy b/http/cs/response.texy index baea11f900..4732bc7a76 100644 --- a/http/cs/response.texy +++ b/http/cs/response.texy @@ -34,9 +34,9 @@ isSent(): bool .[method] Vrací, zda už došlo k odeslání hlaviček ze serveru do prohlížeče, a tedy již není možné odesílat hlavičky či měnit stavový kód. -setHeader(string $name, string $value) .[method] ------------------------------------------------- -Odešle HTTP hlavičku a **přepíše** dříve odeslanou hlavičkou stejného jména. +setHeader(string $name, ?string $value) .[method] +------------------------------------------------- +Odešle HTTP hlavičku a **přepíše** dříve odeslanou hlavičkou stejného jména. Pokud je `$value` `null`, bude záhlaví odstraněno. ```php $httpResponse->setHeader('Pragma', 'no-cache'); @@ -115,8 +115,8 @@ $httpResponse->sendAsFile('faktura.pdf'); ``` -setCookie(string $name, string $value, $time, ?string $path=null, ?string $domain=null, ?bool $secure=null, ?bool $httpOnly=null, ?string $sameSite=null) .[method] -------------------------------------------------------------------------------------------------------------------------------------------------------------------- +setCookie(string $name, string $value, $time, ?string $path=null, ?string $domain=null, ?bool $secure=null, ?bool $httpOnly=null, ?string $sameSite='Lax') .[method] +-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Odešle cookie. Výchozí hodnoty parametrů: | `$path` | `'/'` | cookie má dosah na všechny cesty v (sub)doméně *(konfigurovatelné)* diff --git a/http/cs/urls.texy b/http/cs/urls.texy index 9e722a9d7c..e40a157e18 100644 --- a/http/cs/urls.texy +++ b/http/cs/urls.texy @@ -82,6 +82,7 @@ Můžeme pracovat i s jednotlivými query parametry pomocí: |--------------------------------------------------- | `setQuery(string\|array $query)` | `getQueryParameters(): array` | `setQueryParameter(string $name, $val)` | `getQueryParameter(string $name)` +| `appendQuery(string|array $query)` | getDomain(int $level = 2): string .[method] @@ -107,6 +108,11 @@ $url->isEqual('https://nette.org'); ``` +canonicalize() .[method] +------------------------ +Převede URL do kanonického tvaru. To zahrnuje například seřazení parametrů v query stringu podle abecedy, převod hostname na malá písmena a odstranění nadbytečných znaků. + + Url::isAbsolute(string $url): bool .[method]{data-version:3.3.2} ---------------------------------------------------------------- Ověřuje, zda je URL absolutní. URL je považována za absolutní, pokud začíná schématem (např. http, https, ftp) následovaným dvojtečkou. diff --git a/http/en/request.texy b/http/en/request.texy index ae45438508..47e0232f54 100644 --- a/http/en/request.texy +++ b/http/en/request.texy @@ -184,6 +184,30 @@ $body = $httpRequest->getRawBody(); ``` +getOrigin(): ?UrlImmutable .[method] +------------------------------------ +Returns the origin from which the request came. An origin consists of the scheme (protocol), hostname, and port - for example, `https://example.com:8080`. Returns `null` if the origin header is not present or is set to `'null'`. + +```php +$origin = $httpRequest->getOrigin(); +echo $origin; // https://example.com:8080 +echo $origin?->getHost(); // example.com +``` + +The browser sends the `Origin` header in the following cases: +- Cross-origin requests (AJAX calls to a different domain) +- POST, PUT, DELETE, and other modifying requests +- Requests made using the Fetch API + +The browser does NOT send the `Origin` header for: +- Regular GET requests to the same domain (same-origin navigation) +- Direct navigation by typing a URL into the address bar +- Requests from non-browser clients + +.[note] +Unlike the `Referer` header, `Origin` contains only the scheme, host, and port - not the full URL path. This makes it more suitable for security checks while preserving user privacy. The `Origin` header is primarily used for [CORS |nette:glossary#Cross-Origin Resource Sharing (CORS)] (Cross-Origin Resource Sharing) validation. + + detectLanguage(array $langs): ?string .[method] ----------------------------------------------- Detects the language. Pass an array of languages supported by the application as the `$langs` parameter, and it will return the one preferred by the visitor's browser. It's not magic; it just uses the `Accept-Language` header. If no match is found, it returns `null`. @@ -389,6 +413,11 @@ getTemporaryFile(): string .[method] Returns the path to the temporary location of the uploaded file. If the upload was not successful, it returns `''`. +__toString(): string .[method] +------------------------------ +Returns the path to the temporary location of the uploaded file. This allows the `FileUpload` object to be used directly as a string. + + isImage(): bool .[method] ------------------------- Returns `true` if the uploaded file is a JPEG, PNG, GIF, WebP, or AVIF image. Detection is based on its signature and does not verify the integrity of the entire file. Whether an image is corrupted can be determined, for example, by trying to [load it |#toImage]. diff --git a/http/en/response.texy b/http/en/response.texy index 7654f039e9..7dbd4dca00 100644 --- a/http/en/response.texy +++ b/http/en/response.texy @@ -34,9 +34,9 @@ isSent(): bool .[method] Returns whether headers have already been sent from the server to the browser, meaning it is no longer possible to send headers or change the status code. -setHeader(string $name, string $value) .[method] ------------------------------------------------- -Sends an HTTP header and **overwrites** a previously sent header of the same name. +setHeader(string $name, ?string $value) .[method] +------------------------------------------------- +Sends an HTTP header and **overwrites** a previously sent header of the same name. If `$value` is `null`, the header will be removed. ```php $httpResponse->setHeader('Pragma', 'no-cache'); @@ -115,8 +115,8 @@ $httpResponse->sendAsFile('invoice.pdf'); ``` -setCookie(string $name, string $value, $time, ?string $path=null, ?string $domain=null, ?bool $secure=null, ?bool $httpOnly=null, ?string $sameSite=null) .[method] -------------------------------------------------------------------------------------------------------------------------------------------------------------------- +setCookie(string $name, string $value, $time, ?string $path=null, ?string $domain=null, ?bool $secure=null, ?bool $httpOnly=null, ?string $sameSite='Lax') .[method] +-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Sends a cookie. Default parameter values: | `$path` | `'/'` | cookie is available for all paths within the (sub)domain *(configurable)* diff --git a/http/en/urls.texy b/http/en/urls.texy index 84a947c12f..608fa0064f 100644 --- a/http/en/urls.texy +++ b/http/en/urls.texy @@ -82,6 +82,7 @@ We can also work with individual query parameters using: |--------------------------------------------------- | `setQuery(string\|array $query)` | `getQueryParameters(): array` | `setQueryParameter(string $name, $val)` | `getQueryParameter(string $name)` +| `appendQuery(string|array $query)` | getDomain(int $level = 2): string .[method] @@ -107,6 +108,11 @@ $url->isEqual('https://nette.org'); ``` +canonicalize() .[method] +------------------------ +Converts the URL to canonical form. This includes, for example, sorting the parameters in the query string alphabetically, converting the hostname to lowercase, and removing redundant characters. + + Url::isAbsolute(string $url): bool .[method]{data-version:3.3.2} ---------------------------------------------------------------- Checks if a URL is absolute. A URL is considered absolute if it begins with a scheme (e.g., http, https, ftp) followed by a colon. diff --git a/nette/cs/glossary.texy b/nette/cs/glossary.texy index 05ade5cdbc..7b273ba481 100644 --- a/nette/cs/glossary.texy +++ b/nette/cs/glossary.texy @@ -36,6 +36,13 @@ Cross-Site Request Forgery (CSRF) Nette Framework **automaticky chrání formuláře a signály v presenterech** před tímto typem útoku. A to tím, že zabraňuje jejich odeslání či vyvolání z jiné domény. +Cross-Origin Resource Sharing (CORS) +------------------------------------ +CORS je bezpečnostní mechanismus, který umožňuje webové stránce provádět JavaScriptové požadavky na jinou doménu, než ze které byla stránka načtena. Bez CORS prohlížeče takové požadavky z bezpečnostních důvodů blokují. + +Například pokud vaše webová stránka běží na `https://myapp.com` a pokusí se pomocí JavaScriptu (AJAX, Fetch API) načíst data z `https://api.example.com`, prohlížeč ověří, zda API server tento požadavek mezi doménami povoluje. API server musí odpovědět speciálními HTTP hlavičkami, jako je `Access-Control-Allow-Origin: https://myapp.com`, aby udělil povolení. + + Dependency Injection -------------------- Dependency Injection (DI) je návrhový vzor, který říká, jak oddělit vytváření objektů od jejich závislostí. Tedy že třída není zodpovědná za vytváření nebo inicializaci svých závislostí, ale místo toho jsou jí tyto závislosti poskytovány externím kódem (tím může i [DI kontejner |#Dependency Injection kontejner]). Výhoda spočívá v tom, že umožňuje větší flexibilitu kódu, lepší srozumitelnost a snazší testování aplikace, protože závislosti jsou snadno nahraditelné a izolované od ostatních částí kódu. Více v kapitole [Co je Dependency Injection? |dependency-injection:introduction] diff --git a/nette/en/glossary.texy b/nette/en/glossary.texy index e23d51d384..bc555933a0 100644 --- a/nette/en/glossary.texy +++ b/nette/en/glossary.texy @@ -36,6 +36,13 @@ A Cross-Site Request Forgery attack involves the attacker luring a victim to a p Nette Framework **automatically protects forms and signals in presenters** against this type of attack by preventing them from being submitted or triggered from another domain. +Cross-Origin Resource Sharing (CORS) +------------------------------------ +CORS is a security mechanism that allows a web page to make JavaScript requests to a different domain than the one from which the page was loaded. Without CORS, browsers block such requests for security reasons. + +For example, if your website runs at `https://myapp.com` and tries to fetch data from `https://api.example.com` using JavaScript (AJAX, Fetch API), the browser will check if the API server allows this cross-origin request. The API server must respond with special HTTP headers, such as `Access-Control-Allow-Origin: https://myapp.com`, to grant permission. + + Dependency Injection -------------------- Dependency Injection (DI) is a design pattern that dictates how to separate the creation of objects from their dependencies. This means a class is not responsible for creating or initializing its dependencies; instead, these dependencies are provided by external code (which could be a [DI container |#Dependency Injection Container]). The advantage lies in increased code flexibility, better understandability, and easier application testing, as dependencies are easily replaceable and isolated from other code parts. More in the chapter [What is Dependency Injection? |dependency-injection:introduction] From a7c68f413a8501769910a5a80f84b95df15e9e34 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Sun, 4 Jan 2026 06:13:49 +0100 Subject: [PATCH 10/25] used first-class callables --- best-practices/cs/creating-editing-form.texy | 16 ++++++++-------- best-practices/cs/form-reuse.texy | 8 ++++---- best-practices/cs/lets-create-contact-form.texy | 4 ++-- best-practices/cs/restore-request.texy | 4 ++-- best-practices/en/creating-editing-form.texy | 16 ++++++++-------- best-practices/en/form-reuse.texy | 8 ++++---- best-practices/en/lets-create-contact-form.texy | 4 ++-- best-practices/en/restore-request.texy | 4 ++-- dependency-injection/cs/services.texy | 2 +- dependency-injection/en/services.texy | 2 +- forms/cs/in-presenter.texy | 14 +++++++------- forms/cs/validation.texy | 4 ++-- forms/en/in-presenter.texy | 16 ++++++++-------- forms/en/validation.texy | 4 ++-- 14 files changed, 53 insertions(+), 53 deletions(-) diff --git a/best-practices/cs/creating-editing-form.texy b/best-practices/cs/creating-editing-form.texy index 8babb5c58c..4530f060d4 100644 --- a/best-practices/cs/creating-editing-form.texy +++ b/best-practices/cs/creating-editing-form.texy @@ -29,11 +29,11 @@ class RecordPresenter extends Nette\Application\UI\Presenter // ... přidáme políčka formuláře ... - $form->onSuccess[] = [$this, 'recordFormSucceeded']; + $form->onSuccess[] = $this->recordFormSucceeded(...); return $form; } - public function recordFormSucceeded(Form $form, array $data): void + private function recordFormSucceeded(Form $form, array $data): void { $this->facade->add($data); // přidání záznamu do databáze $this->flashMessage('Successfully added'); @@ -91,11 +91,11 @@ class RecordPresenter extends Nette\Application\UI\Presenter // ... přidáme políčka formuláře ... $form->setDefaults($this->record); // nastavení výchozích hodnot - $form->onSuccess[] = [$this, 'recordFormSucceeded']; + $form->onSuccess[] = $this->recordFormSucceeded(...); return $form; } - public function recordFormSucceeded(Form $form, array $data): void + private function recordFormSucceeded(Form $form, array $data): void { $this->facade->update($this->record->id, $data); // aktualizace záznamu $this->flashMessage('Successfully updated'); @@ -153,7 +153,7 @@ class RecordPresenter extends Nette\Application\UI\Presenter public function actionAdd(): void { $form = $this->getComponent('recordForm'); - $form->onSuccess[] = [$this, 'addingFormSucceeded']; + $form->onSuccess[] = $this->addingFormSucceeded(...); } public function actionEdit(int $id): void @@ -168,7 +168,7 @@ class RecordPresenter extends Nette\Application\UI\Presenter $form = $this->getComponent('recordForm'); $form->setDefaults($record); // nastavení výchozích hodnot - $form->onSuccess[] = [$this, 'editingFormSucceeded']; + $form->onSuccess[] = $this->editingFormSucceeded(...); } protected function createComponentRecordForm(): Form @@ -185,14 +185,14 @@ class RecordPresenter extends Nette\Application\UI\Presenter return $form; } - public function addingFormSucceeded(Form $form, array $data): void + private function addingFormSucceeded(Form $form, array $data): void { $this->facade->add($data); // přidání záznamu do databáze $this->flashMessage('Successfully added'); $this->redirect('...'); } - public function editingFormSucceeded(Form $form, array $data): void + private function editingFormSucceeded(Form $form, array $data): void { $id = (int) $this->getParameter('id'); $this->facade->update($id, $data); // aktualizace záznamu diff --git a/best-practices/cs/form-reuse.texy b/best-practices/cs/form-reuse.texy index bd6c3b8afd..ac95ca2617 100644 --- a/best-practices/cs/form-reuse.texy +++ b/best-practices/cs/form-reuse.texy @@ -193,11 +193,11 @@ class EditFormFactory $form->addText('title', 'Titulek:'); // zde se přidávají další formulářová pole $form->addSubmit('send', 'Odeslat'); - $form->onSuccess[] = [$this, 'processForm']; + $form->onSuccess[] = $this->processForm(...); return $form; } - public function processForm(Form $form, array $data): void + private function processForm(Form $form, array $data): void { try { // zpracování odeslaných dat @@ -284,12 +284,12 @@ class EditControl extends Nette\Application\UI\Control $form->addText('title', 'Titulek:'); // zde se přidávají další formulářová pole $form->addSubmit('send', 'Odeslat'); - $form->onSuccess[] = [$this, 'processForm']; + $form->onSuccess[] = $this->processForm(...); return $form; } - public function processForm(Form $form, array $data): void + private function processForm(Form $form, array $data): void { try { // zpracování odeslaných dat diff --git a/best-practices/cs/lets-create-contact-form.texy b/best-practices/cs/lets-create-contact-form.texy index ec298951be..e5d44812fa 100644 --- a/best-practices/cs/lets-create-contact-form.texy +++ b/best-practices/cs/lets-create-contact-form.texy @@ -24,11 +24,11 @@ class HomePresenter extends Presenter $form->addTextarea('message', 'Zpráva:') ->setRequired('Zadejte zprávu'); $form->addSubmit('send', 'Odeslat'); - $form->onSuccess[] = [$this, 'contactFormSucceeded']; + $form->onSuccess[] = $this->contactFormSucceeded(...); return $form; } - public function contactFormSucceeded(Form $form, $data): void + private function contactFormSucceeded(Form $form, $data): void { // odeslání emailu } diff --git a/best-practices/cs/restore-request.texy b/best-practices/cs/restore-request.texy index 46a25c7f87..05a54331ae 100644 --- a/best-practices/cs/restore-request.texy +++ b/best-practices/cs/restore-request.texy @@ -41,11 +41,11 @@ class SignPresenter extends Nette\Application\UI\Presenter { $form = new Nette\Application\UI\Form; // ... přidáme políčka formuláře ... - $form->onSuccess[] = [$this, 'signInFormSubmitted']; + $form->onSuccess[] = $this->signInFormSubmitted(...); return $form; } - public function signInFormSubmitted($form) + private function signInFormSubmitted($form) { // ... tady uživatele přihlásíme ... diff --git a/best-practices/en/creating-editing-form.texy b/best-practices/en/creating-editing-form.texy index 9349473ca3..27e0af202b 100644 --- a/best-practices/en/creating-editing-form.texy +++ b/best-practices/en/creating-editing-form.texy @@ -29,11 +29,11 @@ class RecordPresenter extends Nette\Application\UI\Presenter // ... add form fields ... - $form->onSuccess[] = [$this, 'recordFormSucceeded']; + $form->onSuccess[] = $this->recordFormSucceeded(...); return $form; } - public function recordFormSucceeded(Form $form, array $data): void + private function recordFormSucceeded(Form $form, array $data): void { $this->facade->add($data); // add record to the database $this->flashMessage('Successfully added'); @@ -91,11 +91,11 @@ class RecordPresenter extends Nette\Application\UI\Presenter // ... add form fields ... $form->setDefaults($this->record); // set default values - $form->onSuccess[] = [$this, 'recordFormSucceeded']; + $form->onSuccess[] = $this->recordFormSucceeded(...); return $form; } - public function recordFormSucceeded(Form $form, array $data): void + private function recordFormSucceeded(Form $form, array $data): void { $this->facade->update($this->record->id, $data); // update record $this->flashMessage('Successfully updated'); @@ -153,7 +153,7 @@ class RecordPresenter extends Nette\Application\UI\Presenter public function actionAdd(): void { $form = $this->getComponent('recordForm'); - $form->onSuccess[] = [$this, 'addingFormSucceeded']; + $form->onSuccess[] = $this->addingFormSucceeded(...); } public function actionEdit(int $id): void @@ -168,7 +168,7 @@ class RecordPresenter extends Nette\Application\UI\Presenter $form = $this->getComponent('recordForm'); $form->setDefaults($record); // set default values - $form->onSuccess[] = [$this, 'editingFormSucceeded']; + $form->onSuccess[] = $this->editingFormSucceeded(...); } protected function createComponentRecordForm(): Form @@ -185,14 +185,14 @@ class RecordPresenter extends Nette\Application\UI\Presenter return $form; } - public function addingFormSucceeded(Form $form, array $data): void + private function addingFormSucceeded(Form $form, array $data): void { $this->facade->add($data); // add record to the database $this->flashMessage('Successfully added'); $this->redirect('...'); } - public function editingFormSucceeded(Form $form, array $data): void + private function editingFormSucceeded(Form $form, array $data): void { $id = (int) $this->getParameter('id'); $this->facade->update($id, $data); // update record diff --git a/best-practices/en/form-reuse.texy b/best-practices/en/form-reuse.texy index c2027ab6f0..2d91c006fc 100644 --- a/best-practices/en/form-reuse.texy +++ b/best-practices/en/form-reuse.texy @@ -193,11 +193,11 @@ class EditFormFactory $form->addText('title', 'Title:'); // additional form fields are added here $form->addSubmit('send', 'Save'); - $form->onSuccess[] = [$this, 'processForm']; + $form->onSuccess[] = $this->processForm(...); return $form; } - public function processForm(Form $form, array $data): void + private function processForm(Form $form, array $data): void { try { // processing of submitted data @@ -284,12 +284,12 @@ class EditControl extends Nette\Application\UI\Control $form->addText('title', 'Title:'); // additional form fields are added here $form->addSubmit('send', 'Save'); - $form->onSuccess[] = [$this, 'processForm']; + $form->onSuccess[] = $this->processForm(...); return $form; } - public function processForm(Form $form, array $data): void + private function processForm(Form $form, array $data): void { try { // processing of submitted data diff --git a/best-practices/en/lets-create-contact-form.texy b/best-practices/en/lets-create-contact-form.texy index dab7982265..0fd677048d 100644 --- a/best-practices/en/lets-create-contact-form.texy +++ b/best-practices/en/lets-create-contact-form.texy @@ -24,11 +24,11 @@ class HomePresenter extends Presenter $form->addTextarea('message', 'Message:') ->setRequired('Please enter a message'); $form->addSubmit('send', 'Send'); - $form->onSuccess[] = [$this, 'contactFormSucceeded']; + $form->onSuccess[] = $this->contactFormSucceeded(...); return $form; } - public function contactFormSucceeded(Form $form, $data): void + private function contactFormSucceeded(Form $form, $data): void { // sending an email } diff --git a/best-practices/en/restore-request.texy b/best-practices/en/restore-request.texy index 6a95370d1c..cea14d7b66 100644 --- a/best-practices/en/restore-request.texy +++ b/best-practices/en/restore-request.texy @@ -41,11 +41,11 @@ class SignPresenter extends Nette\Application\UI\Presenter { $form = new Nette\Application\UI\Form; // ... add form fields ... - $form->onSuccess[] = [$this, 'signInFormSubmitted']; + $form->onSuccess[] = $this->signInFormSubmitted(...); return $form; } - public function signInFormSubmitted($form) + private function signInFormSubmitted($form) { // ... log the user in here ... diff --git a/dependency-injection/cs/services.texy b/dependency-injection/cs/services.texy index 00a55fed56..8a86b230e9 100644 --- a/dependency-injection/cs/services.texy +++ b/dependency-injection/cs/services.texy @@ -181,7 +181,7 @@ public function createServiceFoo(): Foo { $service = new Foo; $service->value = 123; - $service->onClick[] = [$this->getService('bar'), 'clickHandler']; + $service->onClick[] = $this->getService('bar')->clickHandler(...); return $service; } ``` diff --git a/dependency-injection/en/services.texy b/dependency-injection/en/services.texy index 7b3ad6b364..d724a229cf 100644 --- a/dependency-injection/en/services.texy +++ b/dependency-injection/en/services.texy @@ -181,7 +181,7 @@ public function createServiceFoo(): Foo { $service = new Foo; $service->value = 123; - $service->onClick[] = [$this->getService('bar'), 'clickHandler']; + $service->onClick[] = $this->getService('bar')->clickHandler(...); return $service; } ``` diff --git a/forms/cs/in-presenter.texy b/forms/cs/in-presenter.texy index 33ac7fa95d..2c827d31f7 100644 --- a/forms/cs/in-presenter.texy +++ b/forms/cs/in-presenter.texy @@ -19,7 +19,7 @@ $form = new Form; $form->addText('name', 'Jméno:'); $form->addPassword('password', 'Heslo:'); $form->addSubmit('send', 'Registrovat'); -$form->onSuccess[] = [$this, 'formSucceeded']; +$form->onSuccess[] = $this->formSucceeded(...); ``` a v prohlížeči se zobrazí takto: @@ -42,11 +42,11 @@ class HomePresenter extends Nette\Application\UI\Presenter $form->addText('name', 'Jméno:'); $form->addPassword('password', 'Heslo:'); $form->addSubmit('send', 'Registrovat'); - $form->onSuccess[] = [$this, 'formSucceeded']; + $form->onSuccess[] = $this->formSucceeded(...); return $form; } - public function formSucceeded(Form $form, $data): void + private function formSucceeded(Form $form, $data): void { // tady zpracujeme data odeslaná formulářem // $data->name obsahuje jméno @@ -303,16 +303,16 @@ Pokud má formulář více než jedno tlačítko, potřebujeme zpravidla rozliš ```php $form->addSubmit('save', 'Uložit') - ->onClick[] = [$this, 'saveButtonPressed']; + ->onClick[] = $this->saveButtonPressed(...); $form->addSubmit('delete', 'Smazat') - ->onClick[] = [$this, 'deleteButtonPressed']; + ->onClick[] = $this->deleteButtonPressed(...); ``` Tyto handlery se volají pouze v případě validně vyplněného formuláře, stejně jako v případě události `onSuccess`. Rozdíl je v tom, že jako první parametr se místo formulář může předat odesílací tlačítko, záleží na typu, který uvedete: ```php -public function saveButtonPressed(Nette\Forms\Controls\Button $button, $data) +private function saveButtonPressed(Nette\Forms\Controls\Button $button, $data) { $form = $button->getForm(); // ... @@ -403,7 +403,7 @@ protected function createComponentSignInForm(): Form $form = $this->formFactory->create(); // můžeme formulář pozměnit, zde například měníme popisku na tlačítku $form['send']->setCaption('Pokračovat'); - $form->onSuccess[] = [$this, 'signInFormSuceeded']; // a přidáme handler + $form->onSuccess[] = $this->signInFormSuceeded(...); // a přidáme handler return $form; } ``` diff --git a/forms/cs/validation.texy b/forms/cs/validation.texy index e313be57b9..479e12df74 100644 --- a/forms/cs/validation.texy +++ b/forms/cs/validation.texy @@ -223,11 +223,11 @@ protected function createComponentSignInForm(): Form { $form = new Form; // ... - $form->onValidate[] = [$this, 'validateSignInForm']; + $form->onValidate[] = $this->validateSignInForm(...); return $form; } -public function validateSignInForm(Form $form, \stdClass $data): void +private function validateSignInForm(Form $form, \stdClass $data): void { if ($data->foo > 1 && $data->bar > 5) { $form->addError('Tato kombinace není možná.'); diff --git a/forms/en/in-presenter.texy b/forms/en/in-presenter.texy index a1543bcaa1..b4cde43641 100644 --- a/forms/en/in-presenter.texy +++ b/forms/en/in-presenter.texy @@ -19,14 +19,14 @@ $form = new Form; $form->addText('name', 'Name:'); $form->addPassword('password', 'Password:'); $form->addSubmit('send', 'Sign up'); -$form->onSuccess[] = [$this, 'formSucceeded']; +$form->onSuccess[] = $this->formSucceeded(...); ``` and in the browser, it will be displayed like this: [* form-en.webp *] -A form in a presenter is an object of the `Nette\Application\UI\Form` class; its predecessor `Nette\Forms\Form` is intended for standalone use. We added controls named name, password, and a submit button. Finally, the line `$form->onSuccess[] = [$this, 'formSucceeded'];` states that after submission and successful validation, the method `$this->formSucceeded()` should be called. +A form in a presenter is an object of the `Nette\Application\UI\Form` class; its predecessor `Nette\Forms\Form` is intended for standalone use. We added controls named name, password, and a submit button. Finally, the line `$form->onSuccess` states that after submission and successful validation, the method `$this->formSucceeded()` should be called. From the presenter's perspective, the form is a regular component. Therefore, it is treated as a component and integrated into the presenter using a [factory method |application:components#Factory Methods]. It will look like this: @@ -42,11 +42,11 @@ class HomePresenter extends Nette\Application\UI\Presenter $form->addText('name', 'Name:'); $form->addPassword('password', 'Password:'); $form->addSubmit('send', 'Sign up'); - $form->onSuccess[] = [$this, 'formSucceeded']; + $form->onSuccess[] = $this->formSucceeded(...); return $form; } - public function formSucceeded(Form $form, $data): void + private function formSucceeded(Form $form, $data): void { // here we will process the data sent by the form // $data->name contains name @@ -303,16 +303,16 @@ If the form has more than one button, we usually need to distinguish which one w ```php $form->addSubmit('save', 'Save') - ->onClick[] = [$this, 'saveButtonPressed']; + ->onClick[] = $this->saveButtonPressed(...); $form->addSubmit('delete', 'Delete') - ->onClick[] = [$this, 'deleteButtonPressed']; + ->onClick[] = $this->deleteButtonPressed(...); ``` These handlers are called only if the form is validly filled (unless validation is disabled for the button), just like the `onSuccess` event. The difference is that the first parameter passed can be the submit button object instead of the form, depending on the type hint you specify: ```php -public function saveButtonPressed(Nette\Forms\Controls\Button $button, $data) +private function saveButtonPressed(Nette\Forms\Controls\Button $button, $data) { $form = $button->getForm(); // ... @@ -403,7 +403,7 @@ protected function createComponentSignInForm(): Form $form = $this->formFactory->create(); // we can change the form, here for example we change the label on the button $form['login']->setCaption('Continue'); - $form->onSuccess[] = [$this, 'signInFormSubmitted']; // and add handler + $form->onSuccess[] = $this->signInFormSubmitted(...); // and add handler return $form; } ``` diff --git a/forms/en/validation.texy b/forms/en/validation.texy index 0c32b3bb85..d9a9626cc6 100644 --- a/forms/en/validation.texy +++ b/forms/en/validation.texy @@ -223,11 +223,11 @@ protected function createComponentSignInForm(): Form { $form = new Form; // ... - $form->onValidate[] = [$this, 'validateSignInForm']; + $form->onValidate[] = $this->validateSignInForm(...); return $form; } -public function validateSignInForm(Form $form, \stdClass $data): void +private function validateSignInForm(Form $form, \stdClass $data): void { if ($data->foo > 1 && $data->bar > 5) { $form->addError('This combination is not possible.'); From a241df7e041139fb6331178f17860a2a6d4bfeed Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Tue, 6 Jan 2026 15:54:33 +0100 Subject: [PATCH 11/25] added coding standard section about global functions and constants --- contributing/cs/coding-standard.texy | 17 +++++++++++++++++ contributing/en/coding-standard.texy | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/contributing/cs/coding-standard.texy b/contributing/cs/coding-standard.texy index d9e3cc8c85..aaef617655 100644 --- a/contributing/cs/coding-standard.texy +++ b/contributing/cs/coding-standard.texy @@ -109,6 +109,23 @@ public function find(string $dir, array $options): array ``` +Globální funkce a konstanty +=========================== + +Globální funkce a konstanty se píší bez úvodního zpětného lomítka, tedy `count($arr)` nikoliv `\count($arr)`. Pro funkce, které umí PHP optimalizovat, uvedeme na začátku souboru `use function`, aby je kompilátor mohl přeložit efektivněji. Jedná se zejména o funkce jako `count`, `strlen`, `is_array`, `is_string`, `is_scalar`, `sprintf` aj. Funkce se uvádějí na jednom řádku, aby úvodní blok importů nebyl zbytečně velký: + +```php +use Nette; +use function count, is_array, is_scalar, sprintf; +``` + +Výjimečně takto uvádíme i konstanty, u kterých může znalost hodnoty posloužit kompilátoru: + +```php +use const PHP_OS_FAMILY; +``` + + Tabulátory místo mezer ====================== diff --git a/contributing/en/coding-standard.texy b/contributing/en/coding-standard.texy index e98f821d4d..df9a0a86cb 100644 --- a/contributing/en/coding-standard.texy +++ b/contributing/en/coding-standard.texy @@ -109,6 +109,23 @@ public function find(string $dir, array $options): array ``` +Global Functions and Constants +============================== + +Global functions and constants are written without a leading backslash, i.e., `count($arr)` not `\count($arr)`. For functions that PHP can optimize, add `use function` at the beginning of the file so the compiler can translate them more efficiently. These include functions like `count`, `strlen`, `is_array`, `is_string`, `is_scalar`, `sprintf`, etc. Functions are listed on a single line to keep the import block compact: + +```php +use Nette; +use function count, is_array, is_scalar, sprintf; +``` + +Occasionally, we also import constants whose value knowledge may help the compiler: + +```php +use const PHP_OS_FAMILY; +``` + + Tabs Instead of Spaces ====================== From 01baf81999f02a4570f61e28d8fd968a1c2d4f31 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Tue, 6 Jan 2026 23:36:00 +0100 Subject: [PATCH 12/25] Tester 2.6: updated documentation for php.ini loading behavior change --- tester/cs/guide.texy | 8 ++++---- tester/cs/running-tests.texy | 27 +++++++++++---------------- tester/en/guide.texy | 8 ++++---- tester/en/running-tests.texy | 27 +++++++++++---------------- 4 files changed, 30 insertions(+), 40 deletions(-) diff --git a/tester/cs/guide.texy b/tester/cs/guide.texy index c2fc76c3fe..bc75c64ec9 100644 --- a/tester/cs/guide.texy +++ b/tester/cs/guide.texy @@ -116,10 +116,9 @@ Výstup může vypadat takto: /--pre .[terminal] _____ ___ ___ _____ ___ ___ |_ _/ __)( __/_ _/ __)| _ ) - |_| \___ /___) |_| \___ |_|_\ v2.5.2 + |_| \___ /___) |_| \___ |_|_\ v2.6.0 -Note: No php.ini is used. -PHP 8.3.2 (cli) | php -n | 8 threads +PHP 8.5.2 (cli) | php | 8 threads ........s................<span style="color: #FFF; background-color: #900">F</span>......... @@ -160,7 +159,8 @@ Podporované verze PHP | verze | kompatibilní s PHP |------------------|------------------- -| Tester 2.5 | PHP 8.0 – 8.3 +| Tester 2.6 | PHP 8.0 – 8.5 +| Tester 2.5 | PHP 8.0 – 8.5 | Tester 2.4 | PHP 7.2 – 8.2 | Tester 2.3 | PHP 7.1 – 8.0 | Tester 2.1 – 2.2 | PHP 7.1 – 7.3 diff --git a/tester/cs/running-tests.texy b/tester/cs/running-tests.texy index c2afe8df0a..667d8253de 100644 --- a/tester/cs/running-tests.texy +++ b/tester/cs/running-tests.texy @@ -23,10 +23,9 @@ Výstup může vypadat třeba takto: /--pre .[terminal] _____ ___ ___ _____ ___ ___ |_ _/ __)( __/_ _/ __)| _ ) - |_| \___ /___) |_| \___ |_|_\ v2.5.2 + |_| \___ /___) |_| \___ |_|_\ v2.6.0 -Note: No php.ini is used. -PHP 8.3.2 (cli) | php -n | 8 threads +PHP 8.5.2 (cli) | php | 8 threads ........s.......................... @@ -37,9 +36,6 @@ Při opakovaném spuštění nejprve provádí testy, které při předchozím b Pokud žádný test neselže, návratový kód Testeru je nula. Jinak je návratový kód nenulový. -.[warning] -Tester spouští PHP procesy bez `php.ini`. Detailněji v části [#Vlastní php.ini]. - Parametry příkazové řádky ========================= @@ -56,8 +52,8 @@ Usage: Options: -p <path> Specify PHP interpreter to run (default: php). - -c <path> Look for php.ini file (or look in directory) <path>. - -C Use system-wide php.ini. + -c <path> Use custom php.ini, ignore system configuration. + -C With -c, include system configuration as well. -d <key=value>... Define INI entry 'key' with value 'value'. -s Show information about skipped tests. --stop-on-fail Stop execution upon the first failure. @@ -86,12 +82,12 @@ tester -p /home/user/php-7.2.0-beta/php-cgi tests -c <path> .[filter] ------------------- -Určuje, který `php.ini` se bude používat při spouštění testů. Ve výchozím stavu se žádný php.ini nepoužije. Více v části [#Vlastní php.ini]. +Použije vlastní `php.ini` soubor a ignoruje systémovou konfiguraci. Více v části [#Vlastní php.ini]. -C .[filter] ------------ -Použije se systémové `php.ini`. Na UNIXu také všechny příslušné INI soubory `/etc/php/{sapi}/conf.d/*.ini`. Více v části [#Vlastní php.ini]. +Při použití společně s `-c` zachová i systémovou konfiguraci (neignoruje ji). Více v části [#Vlastní php.ini]. -d <key=value> .[filter] @@ -221,11 +217,7 @@ Použijeme současně s volbou `--coverage`. `<path>` je cesta ke zdrojovým kó Vlastní php.ini =============== -Tester spouští PHP procesy s parametrem `-n`, což znamená, že žádné `php.ini` není načteno. V UNIXu ani ty z `/etc/php/conf.d/*.ini`. To zajistí shodné prostředí pro běh testů, ale také vyřadí všechna PHP rozšíření běžně načtená systémovým PHP. - -Chcete-li načítání systémových php.ini souborů zachovat, použijte parametr `-C`. - -Pokud nějaká rozšíření nebo speciální INI nastavení pro testy potřebujete, doporučujeme vytvoření vlastního `php.ini` souboru, který bude distribuován s testy. Tester pak spouštíme s parametrem `-c`, například `tester -c tests/php.ini tests`, kde INI soubor může vypadat takto: +Pro testy můžete použít vlastní `php.ini` soubor. Pokud potřebujete specifická rozšíření nebo speciální INI nastavení, doporučujeme vytvořit vlastní `php.ini`, který bude distribuován s testy. Tester pak spouštíme s parametrem `-c`, například `tester -c tests/php.ini tests`, kde INI soubor může vypadat takto: ```ini [PHP] @@ -236,4 +228,7 @@ extension=php_pdo_pgsql.dll memory_limit=512M ``` -Spuštění Testeru v UNIXu se systémovým `php.ini`, například `tester -c /etc/php/cli/php.ini` nenačte ostatní INI z `/etc/php/conf.d/*.ini`. To je vlastnost PHP, ne Testeru. +Při použití `-c` Tester **ignoruje systémovou konfiguraci** (spouští PHP s příznakem `-n`). Chcete-li zachovat i systémovou konfiguraci, přidejte volbu `-C`: `tester -c tests/php.ini -C tests`. Ani při kombinaci `-c` a `-C` se nenačtou v UNIXu ostatní INI soubory z `/etc/php/conf.d/*.ini`. To je vlastnost PHP, ne Testeru. + +.[note] +Do verze 2.6 Tester bez uvedení `-c` spouštěl PHP s parametrem `-n`, tedy bez php.ini; volba `-C` toto potlačila. Od verze 2.6 se systémové php.ini načítá automaticky. Chování při použití `-c` zůstává stejné. diff --git a/tester/en/guide.texy b/tester/en/guide.texy index ed2dcdd8b8..e5361308ac 100644 --- a/tester/en/guide.texy +++ b/tester/en/guide.texy @@ -116,10 +116,9 @@ The output may look like this: /--pre .[terminal] _____ ___ ___ _____ ___ ___ |_ _/ __)( __/_ _/ __)| _ ) - |_| \___ /___) |_| \___ |_|_\ v2.5.2 + |_| \___ /___) |_| \___ |_|_\ v2.6.0 -Note: No php.ini is used. -PHP 8.3.2 (cli) | php -n | 8 threads +PHP 8.5.2 (cli) | php | 8 threads ........s................<span style="color: #FFF; background-color: #900">F</span>......... @@ -160,7 +159,8 @@ Supported PHP versions | Version | Compatible with PHP |------------------|------------------- -| Tester 2.5 | PHP 8.0 – 8.3 +| Tester 2.6 | PHP 8.0 – 8.5 +| Tester 2.5 | PHP 8.0 – 8.5 | Tester 2.4 | PHP 7.2 – 8.2 | Tester 2.3 | PHP 7.1 – 8.0 | Tester 2.1 – 2.2 | PHP 7.1 – 7.3 diff --git a/tester/en/running-tests.texy b/tester/en/running-tests.texy index 553ba3e3be..0f89235870 100644 --- a/tester/en/running-tests.texy +++ b/tester/en/running-tests.texy @@ -23,10 +23,9 @@ The output may look like this: /--pre .[terminal] _____ ___ ___ _____ ___ ___ |_ _/ __)( __/_ _/ __)| _ ) - |_| \___ /___) |_| \___ |_|_\ v2.5.2 + |_| \___ /___) |_| \___ |_|_\ v2.6.0 -Note: No php.ini is used. -PHP 8.3.2 (cli) | php -n | 8 threads +PHP 8.5.2 (cli) | php | 8 threads ........s.......................... @@ -37,9 +36,6 @@ When run again, it first executes the tests that failed in the previous run, so The Tester's exit code is zero if no test fails. Otherwise, it is non-zero. -.[warning] -The Tester runs PHP processes without `php.ini`. More details in the [#Own php.ini] section. - Command-Line Options ==================== @@ -56,8 +52,8 @@ Usage: Options: -p <path> Specify PHP interpreter to run (default: php). - -c <path> Look for php.ini file (or look in directory) <path>. - -C Use system-wide php.ini. + -c <path> Use custom php.ini, ignore system configuration. + -C With -c, include system configuration as well. -d <key=value>... Define INI entry 'key' with value 'value'. -s Show information about skipped tests. --stop-on-fail Stop execution upon the first failure. @@ -86,12 +82,12 @@ tester -p /home/user/php-7.2.0-beta/php-cgi tests -c <path> .[filter] ------------------- -Specifies which `php.ini` will be used when running tests. By default, no php.ini is used. See [#Own php.ini] for more information. +Uses a custom `php.ini` file and ignores the system configuration. This is useful for running tests with specific settings. See [#Own php.ini] for more information. -C .[filter] ------------ -A system-wide `php.ini` is used. So on UNIX platform, all the `/etc/php/{sapi}/conf.d/*.ini` files too. See [#Own php.ini] section. +When used together with `-c`, includes the system configuration as well (does not ignore it). See [#Own php.ini] section. -d <key=value> .[filter] @@ -221,11 +217,7 @@ Used in conjunction with the `--coverage` option. `<path>` is the path to the so Own php.ini =========== -Tester runs PHP processes with the `-n` option, which means that no `php.ini` is loaded (not even those from `/etc/php/conf.d/*.ini` on UNIX systems). This ensures a consistent environment for running tests, but it also disables all external PHP extensions normally loaded by the system's PHP. - -To preserve the loading of system php.ini files, use the `-C` parameter. - -If you need specific extensions or special INI settings for your tests, we recommend creating your own `php.ini` file and distributing it with your tests. Then, run Tester with the `-c` option, for example, `tester -c tests/php.ini tests`. The INI file might look like this: +You can use a custom `php.ini` file for your tests. If you need specific extensions or special INI settings, we recommend creating your own `php.ini` file and distributing it with your tests. Then, run Tester with the `-c` option, for example, `tester -c tests/php.ini tests`. The INI file might look like this: ```ini [PHP] @@ -236,4 +228,7 @@ extension=php_pdo_pgsql.dll memory_limit=512M ``` -Running Tester on UNIX with a system `php.ini`, like `tester -c /etc/php/cli/php.ini`, does not load other INIs from `/etc/php/conf.d/*.ini`. This is a PHP behavior, not specific to Tester. +When using `-c`, Tester **ignores the system configuration** (runs PHP with `-n` flag). If you want to include system configuration as well, add the `-C` option: `tester -c tests/php.ini -C tests`. Even when combining `-c` and `-C`, other INI files from `/etc/php/conf.d/*.ini` are not loaded on UNIX. This is a PHP behavior, not specific to Tester. + +.[note] +Prior to version 2.6, without `-c`, Tester ran PHP with `-n` flag, i.e., without php.ini; the `-C` option suppressed this. From version 2.6, system php.ini is loaded by default. The behavior when using `-c` remains unchanged. From 6d6830c43ddf2e29097daf340dad9bdb4b363f07 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Thu, 8 Jan 2026 03:31:42 +0100 Subject: [PATCH 13/25] added ai.nette.org --- ai/cs/@home.texy | 11 + ai/cs/@meta.texy | 1 + ai/en/@home.texy | 11 + ai/en/@left-menu.texy | 5 + ai/en/@meta.texy | 1 + ai/en/claude-code.texy | 251 +++++++++++++++++++++ ai/en/getting-started.texy | 260 +++++++++++++++++++++ ai/en/guide.texy | 128 +++++++++++ ai/en/mcp-inspector.texy | 448 +++++++++++++++++++++++++++++++++++++ ai/en/tips.texy | 296 ++++++++++++++++++++++++ ai/meta.json | 1 + 11 files changed, 1413 insertions(+) create mode 100644 ai/cs/@home.texy create mode 100644 ai/cs/@meta.texy create mode 100644 ai/en/@home.texy create mode 100644 ai/en/@left-menu.texy create mode 100644 ai/en/@meta.texy create mode 100644 ai/en/claude-code.texy create mode 100644 ai/en/getting-started.texy create mode 100644 ai/en/guide.texy create mode 100644 ai/en/mcp-inspector.texy create mode 100644 ai/en/tips.texy create mode 100644 ai/meta.json 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 +****************** + +<div class=perex> + +The Nette plugin gives Claude deep knowledge of the framework. Instead of generic PHP advice, you get recommendations that follow Nette conventions – from presenters and forms to Latte templates and database queries. + +The plugin includes: +- **10 skills** covering all major areas of Nette development +- **Automatic validation** that catches errors in Latte and NEON files +- **MCP Inspector integration** for real-time application introspection + +</div> + + +Installation +============ + +If you haven't installed Claude Code yet, see the [complete setup guide |getting-started]. Once Claude Code is running, install the Nette plugin: + +```shell +/plugin marketplace add nette/claude-code +/plugin install nette@nette +``` + + +How Skills Work +=============== + +You don't need to activate skills manually. They turn on automatically based on what you're talking about. + +- Ask about "presenter structure" → the `nette-architecture` skill activates +- Ask about "form validation" → the `nette-forms` skill activates +- Ask about "Latte filters" → the `latte-templates` skill activates + +This means you get relevant, context-aware help without having to think about which skill you need. + + +Available Skills +================ + +Here's what each skill covers: + + +nette-architecture +------------------ + +When you're designing your application structure, this skill guides you through: + +- **Directory organization** – Where to put presenters, services, entities, and components +- **Module design** – How to split your application into logical modules (Admin, Front, Api) +- **Presenter patterns** – When to use base presenters, how to handle authentication +- **Evolution strategy** – Start minimal, grow organically, refactor when needed + +The key principle: Don't over-engineer. Create subdirectories when you have 5+ related files, not before. + + +nette-configuration +------------------- + +Everything about the DI container and NEON configuration: + +- **Service registration** – How to define services in `services.neon` +- **Autowiring** – When it works automatically and when you need explicit configuration +- **Parameters** – How to use configuration parameters across your application +- **Extensions** – Working with DI extensions from Nette and third parties + + +nette-database +-------------- + +Covers both the raw SQL approach and the Database Explorer: + +- **Database Explorer** – Using `Selection` for queries, `ActiveRow` for entities +- **Entity conventions** – The `Row` suffix pattern, type hints with `@property-read` +- **Relationships** – Navigating foreign keys with colon notation +- **When to use what** – Explorer for CRUD, raw SQL for complex analytics + +Example: Claude knows that `->where('category.slug', $slug)` automatically joins the category table. + + +nette-forms +----------- + +Creating and handling forms the Nette way: + +- **Controls** – All built-in controls from text inputs to file uploads +- **Validation** – Built-in rules, custom validators, conditional validation +- **Rendering** – Manual rendering, Bootstrap integration, custom renderers +- **Patterns** – Create/edit forms, form components, AJAX submissions + + +nette-schema +------------ + +Data validation and normalization with the Schema component: + +- **Expect class** – Building validation schemas for arrays and objects +- **Configuration schemas** – Validating NEON configuration in DI extensions +- **Type coercion** – Automatic conversion of strings to integers, dates, etc. +- **Custom validators** – Adding your own validation rules + +Example: Claude knows how to create schemas like: + +```php +$schema = Expect::structure([ + 'name' => Expect::string()->required(), + 'age' => Expect::int()->min(0)->max(120), + 'email' => Expect::string(), + 'roles' => Expect::listOf('string')->default([]), +]); +``` + + +nette-testing +------------- + +Writing tests with Nette Tester: + +- **Test structure** – The `.phpt` format, `@testCase` annotation, file organization +- **Assertions** – All `Assert::*` methods: `same()`, `equal()`, `exception()`, `match()`, and more +- **Fixtures** – Setting up test data, mocking dependencies, database transactions +- **Running tests** – Command-line options, parallel execution, code coverage + +Example: Claude can generate proper test files: + +```php +/** @testCase */ +class UserServiceTest extends TestCase +{ + public function testCreateUser(): void + { + $service = new UserService($this->mockDatabase()); + $user = $service->create(['name' => 'John']); + Assert::same('John', $user->name); + } +} +``` + + +nette-utils +----------- + +The utility classes that make PHP development easier: + +- **Arrays** – `Nette\Utils\Arrays` with `get()`, `getRef()`, `map()`, `flatten()`, and more +- **Strings** – Unicode-safe operations: `webalize()`, `truncate()`, `contains()`, `startsWith()` +- **Finder** – File system traversal with filtering by name, size, date +- **Image** – Resize, crop, sharpen with automatic format detection +- **Json** – Safe JSON encoding/decoding with proper error handling +- **Validators** – Email, URL, numeric validation helpers +- **DateTime** – Immutable date/time with Czech locale support + + +frontend-development +-------------------- + +Integrating frontend tools with Nette: + +- **Vite** – Setting up Vite for modern JavaScript/TypeScript development, HMR configuration +- **Nette Assets** – Using the asset system for cache-busted URLs in production +- **Tailwind CSS** – Configuration for Tailwind with Latte templates, purging unused styles +- **ESLint & Prettier** – Code quality tools integration +- **Build scripts** – npm/package.json scripts for development and production builds + +Claude can help configure `vite.config.js` to work with Nette's directory structure and generate proper asset references in Latte templates. + + +latte-templates +--------------- + +Everything about the Latte templating engine: + +- **Syntax** – Tags, filters, blocks, and inheritance +- **Security** – Auto-escaping, content-aware output +- **Custom filters** – Creating and registering your own filters +- **Template classes** – Using typed templates for better IDE support + + +neon-format +----------- + +The NEON configuration format: + +- **Syntax** – Mappings, sequences, entities, and multiline strings +- **Common patterns** – Service definitions, parameter references +- **Debugging** – Finding and fixing syntax errors + + +Automatic Validation +==================== + +One of the most useful features is automatic validation. After every file edit, the plugin checks for errors: + +| What | How It Works | +|------|--------------| +| **Latte templates** | Runs `latte-lint` to check syntax after every `.latte` edit | +| **NEON files** | Validates NEON syntax after every `.neon` edit | +| **Tracy errors** | Watches the exception log and alerts Claude about new errors | + +If there's a syntax error in your Latte template, Claude knows about it immediately and can suggest a fix. No more discovering errors in the browser. + + +Plugin for Framework Contributors +================================= + +If you're contributing to Nette itself, there's an additional plugin with coding standards: + +```shell +/plugin install nette-dev@nette +``` + +| Skill | What It Covers | +|-------|----------------| +| `php-coding-standards` | Nette's PHP coding style – indentation, naming, structure | +| `php-doc` | PHPDoc conventions – when to document, what format to use | +| `commit-messages` | How to write commit messages for Nette repositories | + + +Automatic PHP Style Fixing +========================== + +For automatic code style fixing after every PHP file edit, install the optional php-fixer plugin: + +```shell +/plugin install php-fixer@nette +/install-php-fixer +``` + +The second command installs `nette/coding-standard` globally. After that, every PHP file you edit will be automatically formatted according to Nette coding standards. + + +MCP Inspector Integration +========================= + +The plugin works even better with [MCP Inspector |mcp-inspector] – a tool that lets Claude see your actual application state. With MCP Inspector, Claude can: + +- Query your real database schema instead of guessing +- List your registered DI services and their configuration +- Read Tracy error logs for debugging +- Match URLs to presenters using your actual routes + +Install it with a single command: + +```shell +/install-mcp-inspector +``` + +Then restart Claude Code. See the [MCP Inspector documentation |mcp-inspector] for all 20 available tools. + +{{composer: nette/claude-code}} diff --git a/ai/en/getting-started.texy b/ai/en/getting-started.texy new file mode 100644 index 0000000000..fa20b5da1f --- /dev/null +++ b/ai/en/getting-started.texy @@ -0,0 +1,260 @@ +Getting Started +*************** + +<div class=perex> + +Ready to try [vibe coding |guide] with Nette? This guide walks you through the complete setup: + +- Choosing and installing an AI tool +- Setting up MCP Inspector so AI can see your application +- Making your first AI-assisted changes + +The whole process takes about 10 minutes. Let's get started! + +</div> + + +Choosing Your AI Tool +===================== + +Nette AI tools work with any MCP-compatible AI assistant. We recommend **Claude Code** for the best experience – it has a dedicated Nette plugin with deep framework knowledge and automatic code validation. + +Other options include **Cursor**, **VS Code with Continue**, and other MCP-compatible tools. See [Other AI Tools |#other-ai-tools] at the end of this guide. + + +What You'll Need +================ + +Before we begin, make sure you have: + +- **A Nette project** – existing or new (`composer create-project nette/web-project`) +- **PHP 8.2+** – required for MCP Inspector +- **An AI tool** – we'll install Claude Code below (Claude Pro costs $20/month) + + +Installation on macOS and Linux +=============================== + +```shell +curl -fsSL https://claude.ai/install.sh | bash +``` + +On macOS, you can alternatively use Homebrew: + +```shell +brew install --cask claude-code +``` + +After installation, skip to [Starting Claude Code |#starting-claude-code]. + + +Installation on Windows via WSL +=============================== + +Claude Code requires a Unix environment. On Windows, use WSL: + +```shell +wsl --install +``` + +This installs Ubuntu. **Restart your computer** after installation. + +After restart, launch Ubuntu: + +```shell +ubuntu +``` + +First launch will ask for a username and password – you'll need it for `sudo` later. + +Installing Claude Code in WSL in the Ubuntu terminal: + +```shell +curl -fsSL https://claude.ai/install.sh | bash +``` + +Your Windows drives are mounted under `/mnt/`: +- `C:\Users\Jan\Projects` → `/mnt/c/Users/Jan/Projects` +- `D:\Work` → `/mnt/d/Work` + +From Windows, access Linux files via `\\wsl$\Ubuntu\home\username` in Explorer. + + +Starting Claude Code +==================== + +Great, you have Claude Code installed! Let's start it up. + +Navigate to your project directory: + +```shell +# Windows (WSL) +cd /mnt/c/Users/Jan/Projects/my-app + +# macOS/Linux +cd ~/projects/my-app +``` + +Start Claude Code: + +```shell +claude +``` + +The first time you run it, Claude Code will ask you to authenticate. It will open a browser window where you can log in to your Anthropic account. After successful authentication, you'll see the `claude>` prompt and you're ready to go. + +You can also use Claude Code "on the web":https://claude.ai/code or via the "desktop app":https://claude.com/download, but local installation provides the best experience with direct file access. + + +Adding the Nette Plugin +======================= + +Now let's give Claude deep knowledge of Nette. First, add the Nette marketplace and enable auto-updating: + +```shell +/plugin marketplace add nette/claude-code +``` + +Then install the plugin: + +```shell +/plugin install nette@nette +``` + +That's it! The plugin is now active. It includes 10 specialized skills that automatically activate based on what you're working on. When you ask about forms, it knows about Nette Forms. When you ask about templates, it knows about Latte. + + +Setting Up MCP Inspector +======================== + +The final piece is MCP Inspector, which lets Claude see your actual application – your services, database schema, routes, and error logs. + +The easiest way to install it is through Claude Code: + +```shell +/install-mcp-inspector +``` + +This command adds the `nette/mcp-inspector` package to your project and configures everything automatically. + +Alternatively, you can install it manually with Composer: + +```shell +composer require nette/mcp-inspector +``` + +**Important:** After installing MCP Inspector, restart Claude Code (type `/exit` and run `claude` again) to activate the connection. + + +Testing Your Setup +================== + +Let's verify everything works. Try these prompts: + + +Test the Plugin Knowledge +------------------------- + +Type: + +``` +What's the recommended directory structure for a Nette application? +``` + +Claude should respond with detailed information about presenters, models, templates, and configuration – knowledge that comes from the `nette-architecture` skill. + + +Test MCP Inspector +------------------ + +Type: + +``` +What services do I have registered in my DI container? +``` + +If MCP Inspector is working, Claude will call `di_get_services()` and show you the actual services from your application. If you see a list of your real services, congratulations – everything is set up correctly! + + +Test Database Introspection +--------------------------- + +If your application uses a database, try: + +``` +What tables do I have? Show me the columns in the user table. +``` + + +Your First Real Task +==================== + +Now that everything is set up, let's do something useful. Try this prompt: + +``` +I need a simple ArticlePresenter with list and detail actions. +Generate the presenter, templates, and tell me what routes I need. +``` + +Watch as Claude generates a complete, working presenter following Nette conventions. It will: +- Create the presenter class with proper type hints +- Generate Latte templates for both actions +- Suggest the appropriate route configuration + +If you have MCP Inspector set up and an `article` table in your database, try: + +``` +Look at my article table and generate an ArticleRow entity with proper type hints. +``` + + +Other AI Tools +============== + +While we recommend Claude Code for the best Nette experience, MCP Inspector works with any MCP-compatible tool. + + +Cursor +------ + +Cursor is a popular AI-first code editor. To use MCP Inspector with Cursor: + +1. Install MCP Inspector: `composer require nette/mcp-inspector` +2. Create `.cursor/mcp.json` in your project: + +```json +{ + "mcpServers": { + "nette-inspector": { + "command": "php", + "args": ["vendor/bin/mcp-inspector"] + } + } +} +``` + +3. Restart Cursor + +Note: Cursor doesn't have the Nette-specific skills that the Claude Code plugin provides, but MCP Inspector will still give it access to your application's services, database, routes, and logs. + + +VS Code + Continue +------------------ + +Continue is an open-source AI coding assistant for VS Code. Configure MCP Inspector in Continue's settings following their MCP documentation. + + +Other MCP Tools +--------------- + +Any tool supporting the Model Context Protocol can use MCP Inspector. See the [MCP Inspector manual configuration |mcp-inspector#manual-mcp-configuration] for setup instructions. + + +What's Next +=========== + +You're now ready for AI-assisted Nette development! Here's where to go from here: + +- [MCP Inspector |mcp-inspector] – Learn about all 20 introspection tools +- [Claude Code Plugin |claude-code] – Explore all 13 skills (Claude Code users) +- [Tips & Best Practices |tips] – Get the most out of your AI assistant diff --git a/ai/en/guide.texy b/ai/en/guide.texy new file mode 100644 index 0000000000..b0d98486c5 --- /dev/null +++ b/ai/en/guide.texy @@ -0,0 +1,128 @@ +Vibe Coding +*********** + +<div class=perex> + +Vibe coding is a new way of programming where you describe what you want in plain language and AI writes the code for you. Nette is ideal for this style of development – strict dependency injection, strong typing, and clear conventions allow AI to generate precise, working code. + +- **MCP Inspector** – Gives any AI tool real-time access to your application +- **Claude Code Plugin** – Deep Nette knowledge for Claude Code users +- **Best Practices** – Proven patterns for effective AI collaboration + +</div> + + +What is Vibe Coding? +==================== + +"The hottest new programming language is English." + +That's the core idea behind vibe coding – instead of writing every line yourself, you describe your intent and let AI handle the implementation. Want a presenter for managing products? Just say so. Need a form with validation? Describe the fields and rules. + +But here's the important part: **AI doesn't replace programmers**. It's a powerful assistant that accelerates routine work: + +- Generate boilerplate code (presenters, forms, entities) in seconds +- Understand existing code and explain how it works +- Find bugs and suggest fixes +- Write tests based on your implementation + +The catch? AI doesn't truly know your application. It sees only what you show it and guesses the rest based on patterns it learned during training. That's where Nette AI tools come in. + + +Why Nette is Perfect for AI +=========================== + +Not all frameworks work equally well with AI. Nette has properties that make it exceptionally suited for AI-assisted development: + +**Strict Dependency Injection** + +In Nette, all services are registered in the DI container. AI can inspect exactly what services exist and how they're configured – no guessing required. + +**Strong Typing** + +Type hints on methods and properties mean AI generates code that actually works. Fewer runtime errors, less debugging. + +**Clear Conventions** + +Presenters, components, templates – everything has its place. AI can follow these patterns and produce code that looks like it was written by an experienced Nette developer. + +The key principle: + +.**"Without MCP, AI guesses. With MCP, AI knows."** + + +How It Works +============ + +The magic happens through **MCP (Model Context Protocol)** – an open standard for connecting AI assistants to external data sources. Instead of guessing based on training data, AI can query your actual application state. + +Here's the flow: + +1. **You** describe what you want: "Create an entity for the product table" +2. **AI tool** (Claude, Cursor, etc.) needs to know your database schema +3. **MCP Inspector** queries your application and returns the actual schema +4. **AI** generates code that matches your real database + +No hallucinations. No guessing. Just accurate code. + + +Nette AI Tools +============== + + +MCP Inspector +------------- + +The core of Nette's AI integration. MCP Inspector is an MCP server that gives **any compatible AI tool** real-time access to your application: + +| What AI Can See | Examples | +|-----------------|----------| +| **DI Container** | Services, parameters, extensions | +| **Database** | Tables, columns, relationships | +| **Router** | Routes, URL matching, generation | +| **Tracy** | Exceptions, warnings, logs | + +MCP Inspector works with Claude Code, Cursor, VS Code with Continue, and any other tool that supports the MCP protocol. + +[Learn more about MCP Inspector |mcp-inspector] + + +Claude Code Plugin +------------------ + +For users of Claude Code, there's an additional plugin that gives Claude deep knowledge of Nette conventions. It includes 10 specialized "skills" that activate automatically: + +| Skill | What It Covers | +|-------|----------------| +| nette-architecture | Presenters, modules, directory structure | +| nette-database | Database Explorer, entities, queries | +| nette-forms | Controls, validation, rendering | +| latte-templates | Syntax, filters, security | +| + 6 more... | [See complete list |claude-code] | + +The plugin also automatically validates Latte templates and NEON files after every edit. + +[Learn more about Claude Code Plugin |claude-code] + + +Other AI Tools +-------------- + +MCP Inspector works with any MCP-compatible tool. Setup guides for additional tools are coming soon: + +- **Cursor** – Popular AI-first code editor +- **VS Code + Continue** – Open-source AI coding assistant +- **Gemini CLI** – Google's command-line AI tool + + +Getting Started +=============== + +Ready to try vibe coding with Nette? The setup takes about 10 minutes: + +1. **Choose your AI tool** – We recommend Claude Code for the best Nette experience +2. **Install MCP Inspector** – The core that gives AI access to your application +3. **Start coding** – Describe what you want and let AI help + +[Complete setup guide |getting-started] + diff --git a/ai/en/mcp-inspector.texy b/ai/en/mcp-inspector.texy new file mode 100644 index 0000000000..5965a94677 --- /dev/null +++ b/ai/en/mcp-inspector.texy @@ -0,0 +1,448 @@ +MCP Inspector +************* + +<div class=perex> + +MCP Inspector is the bridge between **any AI tool** and your Nette application. It allows AI assistants to look directly at your running app – to see what services you have registered, what your database schema looks like, which routes are defined, and what errors have occurred. + +This is what makes the difference between AI that guesses and AI that knows. + +</div> + + +Supported AI Tools +================== + +MCP Inspector works with any tool that supports the **Model Context Protocol (MCP)**: + +- **[Claude Code |claude-code]** – Full support with dedicated Nette plugin +- **Cursor** – Configure via `.cursor/mcp.json` +- **VS Code + Continue** – Configure via Continue settings +- **Any MCP-compatible tool** – See [manual configuration |#manual-mcp-configuration] + + +Why MCP Matters +=============== + +Imagine you ask your AI: "Generate an entity for the product table." + +Without MCP Inspector, the AI has to guess what columns your table has. It might assume common patterns like `id`, `name`, `price` – but what if your table has different columns? What if `price` is called `unit_price`? What if you have a `currency_id` foreign key? + +With MCP Inspector, the AI doesn't guess. It calls `db_get_columns("product")` and sees your actual schema: + +The result is code that actually works with your database, not code you have to fix. + + +Installation +============ + +If you're using the [Nette plugin for Claude Code |claude-code], installation is simple: + +```shell +/install-mcp-inspector +``` + +This command adds `nette/mcp-inspector` to your project and configures everything automatically. + +For other AI tools or manual installation: + +```shell +composer require nette/mcp-inspector +``` + +Then configure your AI tool to use the MCP server – see [manual configuration |#manual-mcp-configuration] below. + +**Important:** After installation, restart your AI tool. The MCP server only connects when the tool starts. + + +How It Works +============ + +MCP Inspector runs as a background process that your AI tool can communicate with. When AI needs information about your application, it sends a request to MCP Inspector, which: + +1. Loads your application's DI container (using `App\Bootstrap`) +2. Executes the requested query (get services, read database schema, etc.) +3. Returns the result to the AI + +All operations are **read-only**. MCP Inspector can't modify your database, change configuration, or execute commands. + + +DI Container Tools +================== + +These tools let AI explore your service definitions. + + +di_get_services +--------------- + +Lists all registered services. You can filter by name or type. + +When AI asks "What mail services do I have?", it calls: + +``` +di_get_services("mail") +``` + +And gets a list like: + +``` +- mail.mailer (Nette\Mail\Mailer) +- App\Model\QueueMailer +- App\Core\SmtpTransport +``` + + +di_get_service +-------------- + +Gets detailed information about a specific service – how it's created, what setup methods are called, what tags it has. + + +di_get_parameters +----------------- + +Reads configuration parameters. Want to know what your database settings are? + +``` +di_get_parameters("database") +``` + +Note: Sensitive values (passwords, tokens, API keys) are automatically masked. + + +di_find_by_tag +-------------- + +Finds services with a specific tag. Useful for discovering CLI commands: + +``` +di_find_by_tag("console.command") +``` + + +di_find_by_type +--------------- + +Finds services implementing a specific interface: + +``` +di_find_by_type("Nette\\Security\\Authenticator") +``` + + +di_get_extensions +----------------- + +Lists all registered DI extensions with their configuration. + + +Database Tools +============== + +These tools give AI visibility into your database structure. + + +db_get_tables +------------- + +Lists all tables in your database. + + +db_get_columns +-------------- + +Gets detailed column information for a table – types, whether they're nullable, default values, and foreign key relationships. + +``` +db_get_columns("order") +``` + +Returns something like: + +``` +- id: int (PRIMARY KEY) +- customer_id: int (FK → customer.id) +- status: varchar(20) +- total: decimal(10,2) +- created_at: datetime +``` + + +db_get_relationships +-------------------- + +Shows all foreign key relationships in your database – which tables reference which other tables. + + +db_get_indexes +-------------- + +Lists indexes for a specific table. + + +db_explain_query +---------------- + +Runs `EXPLAIN` on a SELECT query to analyze its performance. AI can use this to suggest query optimizations. + + +db_generate_entity +------------------ + +The most useful tool for quick development. Given a table name, it generates a complete PHP entity class with proper type hints: + +``` +db_generate_entity("product") +``` + +Generates: + +```php +/** + * @property-read int $id + * @property-read string $name + * @property-read float $unit_price + * @property-read ?CategoryRow $category + * @property-read DateTimeImmutable $created_at + */ +final class ProductRow extends Table\ActiveRow +{ +} +``` + + +Router Tools +============ + +These tools help AI understand your URL structure. + + +router_get_routes +----------------- + +Lists all registered routes with their masks and default values. + + +router_match_url +---------------- + +Given a URL, finds which presenter and action handles it: + +``` +router_match_url("/admin/products/edit/5") +``` + +Returns: + +``` +Presenter: Admin:Product +Action: edit +Parameters: id=5 +``` + + +router_generate_url +------------------- + +Generates a URL for a given presenter and action: + +``` +router_generate_url("Admin:Product:edit", {"id": 5}) +``` + + +Tracy Tools +=========== + +These tools let AI see error logs and help with debugging. They're incredibly useful when something goes wrong – instead of you describing the error, AI can read it directly. + + +tracy_get_last_exception +------------------------ + +Gets the most recent exception from Tracy's log, including the full stack trace. When something breaks, this is the first thing AI checks. + +``` +tracy_get_last_exception() +``` + +Returns the exception class, message, file, line number, and complete stack trace. AI can analyze this to identify the root cause and suggest a fix. + +Example response: +``` +Exception: Nette\Database\UniqueConstraintViolationException +Message: Duplicate entry 'john@example.com' for key 'email' +File: /app/Model/UserService.php:45 +Stack trace: + #0 /app/Presentation/Admin/UserPresenter.php:32 + #1 /vendor/nette/application/src/... +``` + + +tracy_get_exceptions +-------------------- + +Lists recent exception files from Tracy's log directory. Useful for finding patterns or recurring issues. + +``` +tracy_get_exceptions(5) +``` + +Returns the 5 most recent exception files with timestamps. You can then use `tracy_get_exception_detail` to examine any of them. + + +tracy_get_exception_detail +-------------------------- + +Gets the complete details of a specific exception file. Use this when you want to examine an older exception, not just the latest one. + +``` +tracy_get_exception_detail("exception-2024-01-15-143022-abc123.html") +``` + + +tracy_get_warnings +------------------ + +Shows recent PHP warnings and notices from Tracy's log. These often indicate problems that don't crash the application but should be fixed. + +``` +tracy_get_warnings(10) +``` + +Common warnings AI can help fix: +- Undefined array key +- Deprecated function calls +- Type mismatch warnings + + +tracy_get_log +------------- + +Reads entries from any Tracy log level. Tracy supports multiple log files: `error.log`, `warning.log`, `info.log`, and custom levels. + +``` +tracy_get_log("error", 20) +``` + +This reads the last 20 entries from the error log. Useful for seeing a history of issues, not just the most recent one. + + +Creating Custom Tools +===================== + +You can extend MCP Inspector with your own tools. This is useful if you have application-specific data that AI should be able to query. + +Create a class implementing the `Toolkit` interface: + +```php +use Mcp\Capability\Attribute\McpTool; +use Nette\McpInspector\Toolkit; +use Nette\McpInspector\Bridge\BootstrapBridge; + +class OrderToolkit implements Toolkit +{ + public function __construct( + private BootstrapBridge $bridge, + ) {} + + /** + * Get pending orders count and total value. + */ + #[McpTool(name: 'orders_get_pending_summary')] + public function getPendingSummary(): array + { + $db = $this->bridge->getContainer() + ->getByType(Nette\Database\Explorer::class); + + $result = $db->table('order') + ->where('status', 'pending') + ->select('COUNT(*) AS count, SUM(total) AS total') + ->fetch(); + + return [ + 'count' => $result->count, + 'total' => $result->total, + ]; + } +} +``` + +Register it in `mcp-config.neon`: + +```neon +toolkits: + - App\Mcp\OrderToolkit +``` + +Now AI can call `orders_get_pending_summary()` to get real-time order statistics. + + +Configuration +============= + +MCP Inspector works out of the box with the default Nette project structure. If your setup is different, create `mcp-config.neon` in your project root: + +```neon +# Path to Bootstrap file (if not in default location) +bootstrap: src/Bootstrap.php + +# Bootstrap class name (if different from default) +bootstrapClass: MyApp\Bootstrap + +# Additional custom toolkits +toolkits: + - App\Mcp\OrderToolkit + - App\Mcp\CustomerToolkit +``` + + +Manual MCP Configuration +------------------------ + +For AI tools other than Claude Code (which configures automatically via the plugin), add MCP Inspector to your tool's configuration: + +**For most MCP-compatible tools**, create `.mcp.json` in your project root: + +```json +{ + "mcpServers": { + "nette-inspector": { + "command": "php", + "args": ["vendor/bin/mcp-inspector"] + } + } +} +``` + +**For Cursor**, add to `.cursor/mcp.json`: + +```json +{ + "mcpServers": { + "nette-inspector": { + "command": "php", + "args": ["vendor/bin/mcp-inspector"] + } + } +} +``` + +Consult your AI tool's documentation for the exact configuration location. + + +Security Considerations +======================= + +MCP Inspector is designed for development environments. Here's what you should know: + +**Read-only by design** – All tools only read data, never modify it. + +**Database protection** – The `db_explain_query` tool only accepts SELECT, SHOW, DESCRIBE, and EXPLAIN queries. INSERT, UPDATE, DELETE, and other modifying queries are rejected. + +**Sensitive data masking** – Configuration values containing words like "password", "secret", "token", or "apikey" are automatically masked with `***MASKED***`. + +**Do not expose in production** – MCP Inspector should only run on development machines. It provides detailed information about your application internals that you don't want exposed publicly. + +{{composer: nette/mcp-inspector}} diff --git a/ai/en/tips.texy b/ai/en/tips.texy new file mode 100644 index 0000000000..586f558519 --- /dev/null +++ b/ai/en/tips.texy @@ -0,0 +1,296 @@ +Tips for AI-Assisted Development +******************************** + +<div class=perex> + +AI is a powerful tool, but like any tool, you get better results when you know how to use it well. This page collects practical advice from real-world experience with AI-assisted Nette development. + +- How to write prompts that get better results +- Making the most of MCP Inspector +- Proven workflows for common tasks +- Mistakes to avoid + +</div> + + +Writing Better Prompts +====================== + + +The Art of Being Specific +------------------------- + +The single biggest improvement you can make is being specific. Compare these two prompts: + +**Vague prompt:** + +``` +Create a form +``` + +This gives the AI almost no context. What form? What fields? What validation? The AI has to make assumptions, and those assumptions might not match what you need. + +**Specific prompt:** + +``` +Create a ProductForm with: +- name: text field, required, max 100 characters +- price: float field, required, must be positive +- description: textarea, optional +- category: select from CategoryRow entities + +Use Bootstrap 5 rendering. The form should work for both creating new products and editing existing ones. +``` + +Now the AI knows exactly what you need and can generate code that works on the first try. + + +Point to Existing Patterns +-------------------------- + +Your codebase already has patterns. Instead of explaining them, point the AI to examples: + +``` +Create an OrderPresenter. Follow the same patterns as ProductPresenter – +same structure, same way of handling forms, same template organization. +``` + +The AI will read ProductPresenter and replicate the patterns you're already using. + + +Let MCP Do the Heavy Lifting +---------------------------- + +If you have MCP Inspector installed (and you should!), don't explain your application – let the AI discover it: + +**Instead of:** + +``` +My product table has columns: id (int), name (varchar), price (decimal), +category_id (int, foreign key to category), created_at (datetime)... +``` + +**Just say:** + +``` +Generate an entity for the product table. +``` + +The AI will call `db_get_columns("product")` and see the actual schema. The generated entity will match your real database, including any columns you might have forgotten to mention. + + +Give Context About Your Goals +----------------------------- + +AI can't read your mind. If there's a reason behind your request, share it: + +``` +I need to optimize the product listing page. It's currently loading +all products at once, which is slow when there are thousands of items. +The page needs to support filtering by category and sorting by price or name. +``` + +This helps the AI suggest an appropriate solution (pagination, lazy loading, caching) rather than just blindly implementing what you asked for. + + +Working with MCP Inspector +========================== + +MCP Inspector is most powerful when you use it strategically. + + +Explore Before You Build +------------------------ + +Starting a new feature? Let the AI understand the context first: + +``` +I'm going to add order tracking. Before we start: +1. What services do I have related to orders? +2. What does my order table look like? +3. What routes handle order-related pages? +``` + +This gives the AI context about your existing code, so the new feature fits naturally. + + +Debug Smarter +------------- + +When something goes wrong, don't describe the error – let the AI see it: + +``` +Something broke. Check the Tracy log for the last exception +and tell me what went wrong. +``` + +The AI will call `tracy_get_last_exception()`, read the stack trace, and can often identify the problem faster than you could explain it. + + +Verify Before You Create +------------------------ + +Before adding new routes or links, verify what exists: + +``` +What presenter handles /admin/products/edit? I want to make sure +I'm not creating something that conflicts. +``` + + +Common Workflows +================ + +Here are proven approaches for common tasks. + + +Creating a Complete CRUD +------------------------ + +Don't ask for one piece at a time. Give the AI the full picture: + +``` +Create a complete CRUD for managing products: + +1. ProductPresenter with actions: list, add, edit, delete +2. ProductForm as a component (works for both add and edit) +3. Latte templates for all actions +4. Route suggestions + +Use the actual product table schema. Follow the patterns +in CategoryPresenter if it exists. +``` + + +Adding a New Feature +-------------------- + +Break it down into phases that build on each other: + +``` +I need to add customer reviews to products. + +Phase 1 - Data layer: +- Look at the product table +- Suggest the review table schema +- Create ReviewRow entity + +Phase 2 - Business logic: +- Create ReviewService for CRUD operations +- Add methods to get reviews for a product + +Phase 3 - UI: +- Add review display to ProductPresenter:detail +- Create ReviewForm for submitting reviews +``` + + +Refactoring Existing Code +------------------------- + +Let the AI understand before it changes: + +``` +Analyze the OrderService class. What does each method do? +Are there any code smells or improvements you'd suggest? +``` + +Then: + +``` +The calculateTotal method is doing too much. Split it into +smaller methods while keeping the same public interface. +``` + + +Mistakes to Avoid +================= + + +Not Reviewing Generated Code +---------------------------- + +AI generates code quickly, but that doesn't mean every line is perfect. Always review: + +- **Database queries** – Are they efficient? Do they need indexes? +- **Security** – Is input validated? Are there authorization checks? +- **Edge cases** – What happens with empty data? Null values? + +AI is very good, but it's still an assistant. You're the developer responsible for the final code. + + +Ignoring Validation Feedback +---------------------------- + +If you're using the [Claude Code plugin |claude-code], it validates your Latte templates and NEON files automatically. When it reports an error, the AI knows about it. Instead of manually fixing the error, just say: + +``` +Fix the error you just created. +``` + +The AI will read the validation output and correct the mistake. + + +Forgetting Service Registration +------------------------------- + +When the AI creates a new service class, it sometimes forgets to register it in the DI container. If you get "Service not found" errors, ask: + +``` +What changes do I need in services.neon for the new OrderExportService? +``` + + +Asking for Too Much at Once +--------------------------- + +While AI can handle complex tasks, sometimes it helps to break them down: + +**Too ambitious:** + +``` +Build me a complete e-commerce system with product catalog, +shopping cart, checkout, payments, order tracking, and admin panel. +``` + +**Better approach:** + +``` +Let's build an e-commerce system step by step. Start with the product +catalog – I need to list, view, and admin products. +``` + + +When AI Excels (and When It Doesn't) +==================================== + + +AI is Great For +--------------- + +- **Boilerplate code** – Presenters, forms, entities, basic templates +- **Following patterns** – "Do it like X but for Y" +- **Understanding code** – "What does this method do?" +- **Generating tests** – Given implementation, create tests +- **Refactoring** – Improving code structure while keeping behavior + + +Consider Manual Coding For +-------------------------- + +- **Complex business logic** – Domain rules that require careful thinking +- **Performance-critical code** – Algorithms that need optimization +- **Security-sensitive code** – Authentication, authorization, encryption +- **Novel solutions** – Things that don't follow existing patterns + +AI is a multiplier, not a replacement. It makes good developers faster, but it still needs a good developer guiding it. + + +Final Thoughts +============== + +The best way to learn AI-assisted development is to practice. Start with simple tasks, pay attention to what works, and gradually take on more complex projects. + +And remember: the AI is your assistant. You're still the developer. You make the decisions, you review the code, and you're responsible for the quality of the final product. + +Happy coding! diff --git a/ai/meta.json b/ai/meta.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/ai/meta.json @@ -0,0 +1 @@ +{} From e65bfffa73924718618d41e570710b1498002a6d Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Fri, 23 Jan 2026 16:21:31 +0100 Subject: [PATCH 14/25] latte 3.1.2 --- latte/cs/develop.texy | 8 ++++---- latte/en/develop.texy | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/latte/cs/develop.texy b/latte/cs/develop.texy index c37408b424..08f3a72544 100644 --- a/latte/cs/develop.texy +++ b/latte/cs/develop.texy @@ -27,7 +27,7 @@ Jak vykreslit šablonu? Stačí k tomu tento jednoduchý kód: ```php $latte = new Latte\Engine; // adresář pro cache -$latte->setTempDirectory('/path/to/tempdir'); +$latte->setCacheDirectory('/path/to/tempdir'); $params = [ /* proměnné šablony */ ]; // or $params = new TemplateParameters(/* ... */); @@ -184,18 +184,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} diff --git a/latte/en/develop.texy b/latte/en/develop.texy index 968c797320..d7b537fd2f 100644 --- a/latte/en/develop.texy +++ b/latte/en/develop.texy @@ -27,7 +27,7 @@ How to render a template? Just use this simple code: ```php $latte = new Latte\Engine; // cache directory -$latte->setTempDirectory('/path/to/tempdir'); +$latte->setCacheDirectory('/path/to/tempdir'); $params = [ /* template variables */ ]; // or $params = new TemplateParameters(/* ... */); @@ -184,18 +184,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} From e129efd716e01b8ec62ea8646f3764bc3f035a8b Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Tue, 17 Mar 2026 21:23:48 +0100 Subject: [PATCH 15/25] latte: info about whitespace --- latte/cs/syntax.texy | 33 +++++++++++++++++++++++++++++++++ latte/en/syntax.texy | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/latte/cs/syntax.texy b/latte/cs/syntax.texy index 1e21ab6dcf..b36e9e7c9a 100644 --- a/latte/cs/syntax.texy +++ b/latte/cs/syntax.texy @@ -218,6 +218,39 @@ 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 +<ul> + {foreach $items as $item} + <li>{$item}</li> + {/foreach} +</ul> +``` + +Vypíše: + +```html +<ul> + <li>foo</li> + <li>bar</li> +</ul> +``` + +A co když tag není na řádku sám, ale je tam i další obsah? Bílé znaky před tagem pak patří *dovnitř* tagu: + +```latte +<div> + {if $foo}hello{/if} +</div> +``` + +Odsazení je tedy fakticky uvnitř `{if}`: pokud je `$foo` false, nevypíše se nic – ani odsazení, ani prázdný řádek. Pokud je `$foo` true, výstup přirozeně obsahuje odsazení. Prostě pište přehledně odsazené šablony a výstup bude vždy čistý. + + Syntaktický cukr ================ diff --git a/latte/en/syntax.texy b/latte/en/syntax.texy index ef926e75be..31faaf9fa5 100644 --- a/latte/en/syntax.texy +++ b/latte/en/syntax.texy @@ -218,6 +218,39 @@ 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 +<ul> + {foreach $items as $item} + <li>{$item}</li> + {/foreach} +</ul> +``` + +Outputs: + +```html +<ul> + <li>foo</li> + <li>bar</li> +</ul> +``` + +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 +<div> + {if $foo}hello{/if} +</div> +``` + +The indentation is effectively inside `{if}`: when `$foo` is false, nothing is output – not even the indentation or a blank line. When `$foo` is true, the output naturally includes the indentation. You simply write well-structured templates and the output is always clean. + + Syntactic Sugar =============== From 59d2a04b32ba8b9f86037d0da710415636671ad2 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Tue, 23 Dec 2025 22:05:06 +0100 Subject: [PATCH 16/25] latte 3.1.3 --- latte/cs/develop.texy | 64 +++++++++++++++++++++++++++++++++++++++++++ latte/cs/filters.texy | 58 ++++++++++++++++++++++++++++++++++++--- latte/cs/syntax.texy | 2 ++ latte/en/develop.texy | 64 +++++++++++++++++++++++++++++++++++++++++++ latte/en/filters.texy | 58 ++++++++++++++++++++++++++++++++++++--- latte/en/syntax.texy | 2 ++ 6 files changed, 240 insertions(+), 8 deletions(-) diff --git a/latte/cs/develop.texy b/latte/cs/develop.texy index 08f3a72544..eec8ece1c3 100644 --- a/latte/cs/develop.texy +++ b/latte/cs/develop.texy @@ -216,6 +216,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..edad8271e9 100644 --- a/latte/cs/filters.texy +++ b/latte/cs/filters.texy @@ -10,6 +10,8 @@ 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] | `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 +261,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 +453,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 +685,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 +797,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. diff --git a/latte/cs/syntax.texy b/latte/cs/syntax.texy index b36e9e7c9a..c5346ba1f8 100644 --- a/latte/cs/syntax.texy +++ b/latte/cs/syntax.texy @@ -250,6 +250,8 @@ A co když tag není na řádku sám, ale je tam i další obsah? Bílé znaky p Odsazení je tedy fakticky uvnitř `{if}`: pokud je `$foo` false, nevypíše se nic – ani odsazení, ani prázdný řádek. Pokud je `$foo` true, výstup přirozeně obsahuje odsazení. Prostě pište přehledně odsazené šablony a výstup bude vždy čistý. +Pro ještě čistší výstup lze aktivovat funkci [Dedent |develop#Dedent], která odstraní i odsazení vzniklé zanořením v párových značkách jako `{if}` nebo `{foreach}`. + Syntaktický cukr ================ diff --git a/latte/en/develop.texy b/latte/en/develop.texy index d7b537fd2f..db4336cfdf 100644 --- a/latte/en/develop.texy +++ b/latte/en/develop.texy @@ -216,6 +216,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..a6365b3be6 100644 --- a/latte/en/filters.texy +++ b/latte/en/filters.texy @@ -10,6 +10,8 @@ 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] | `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 +261,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 +453,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 +685,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 +797,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. diff --git a/latte/en/syntax.texy b/latte/en/syntax.texy index 31faaf9fa5..24656e0571 100644 --- a/latte/en/syntax.texy +++ b/latte/en/syntax.texy @@ -250,6 +250,8 @@ What if a tag isn't alone on a line, but appears alongside other content? The wh The indentation is effectively inside `{if}`: when `$foo` is false, nothing is output – not even the indentation or a blank line. When `$foo` is true, the output naturally includes the indentation. You simply write well-structured templates and the output is always clean. +For even cleaner output, you can enable the [Dedent |develop#Dedent] feature, which also removes indentation caused by nesting within paired tags like `{if}` or `{foreach}`. + Syntactic Sugar =============== From c5bd2907c84159eb4f236a94ab0a4fc556646f44 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Mon, 23 Feb 2026 02:46:24 +0100 Subject: [PATCH 17/25] typo --- application/cs/creating-links.texy | 2 +- application/cs/templates.texy | 2 +- application/en/templates.texy | 2 +- best-practices/cs/creating-editing-form.texy | 1 - .../cs/lets-create-contact-form.texy | 3 --- best-practices/en/creating-editing-form.texy | 1 - .../en/lets-create-contact-form.texy | 3 --- caching/cs/@home.texy | 4 ++-- caching/en/@home.texy | 4 ++-- database/cs/explorer.texy | 2 +- database/en/explorer.texy | 2 +- dependency-injection/cs/faq.texy | 2 +- dependency-injection/cs/global-state.texy | 2 +- dependency-injection/en/faq.texy | 2 +- dependency-injection/en/global-state.texy | 2 +- latte/bg/custom-tags.texy | 2 +- latte/bg/safety-first.texy | 20 +++++++++---------- latte/cs/custom-filters.texy | 2 +- latte/cs/custom-functions.texy | 2 +- latte/cs/custom-tags.texy | 4 ++-- latte/cs/safety-first.texy | 20 +++++++++---------- latte/cs/syntax.texy | 2 +- latte/cs/tags.texy | 2 +- latte/de/custom-tags.texy | 2 +- latte/de/safety-first.texy | 20 +++++++++---------- latte/el/custom-tags.texy | 2 +- latte/el/safety-first.texy | 20 +++++++++---------- latte/en/custom-filters.texy | 2 +- latte/en/custom-functions.texy | 2 +- latte/en/custom-tags.texy | 4 ++-- latte/en/safety-first.texy | 20 +++++++++---------- latte/en/syntax.texy | 2 +- latte/en/tags.texy | 2 +- latte/es/custom-tags.texy | 2 +- latte/es/safety-first.texy | 20 +++++++++---------- latte/fr/custom-tags.texy | 2 +- latte/fr/safety-first.texy | 20 +++++++++---------- latte/hu/custom-tags.texy | 2 +- latte/hu/safety-first.texy | 20 +++++++++---------- latte/it/custom-tags.texy | 2 +- latte/it/safety-first.texy | 20 +++++++++---------- latte/ja/custom-tags.texy | 2 +- latte/ja/safety-first.texy | 20 +++++++++---------- latte/pl/custom-tags.texy | 2 +- latte/pl/safety-first.texy | 20 +++++++++---------- latte/pt/custom-tags.texy | 2 +- latte/pt/safety-first.texy | 20 +++++++++---------- latte/ro/custom-tags.texy | 2 +- latte/ro/safety-first.texy | 20 +++++++++---------- latte/ru/custom-tags.texy | 2 +- latte/ru/safety-first.texy | 20 +++++++++---------- latte/sl/custom-tags.texy | 2 +- latte/sl/safety-first.texy | 20 +++++++++---------- latte/tr/custom-tags.texy | 2 +- latte/tr/safety-first.texy | 20 +++++++++---------- latte/uk/custom-tags.texy | 2 +- latte/uk/safety-first.texy | 20 +++++++++---------- quickstart/cs/@home.texy | 3 +-- quickstart/cs/comments.texy | 2 +- quickstart/en/@home.texy | 3 +-- quickstart/en/comments.texy | 2 +- security/cs/authentication.texy | 2 +- security/en/authentication.texy | 2 +- tracy/cs/guide.texy | 2 +- tracy/cs/stopwatch.texy | 2 +- tracy/en/stopwatch.texy | 2 +- utils/cs/filesystem.texy | 2 +- utils/en/filesystem.texy | 2 +- 68 files changed, 221 insertions(+), 231 deletions(-) diff --git a/application/cs/creating-links.texy b/application/cs/creating-links.texy index 011beea750..8658cb3646 100644 --- a/application/cs/creating-links.texy +++ b/application/cs/creating-links.texy @@ -12,7 +12,7 @@ Tvořit odkazy v Nette je jednoduché, jako ukazovat prstem. Stačí jen namíř </div> -Díky [obousměrnému routování |routing] nebudete nikdy muset do šablon či kódu zapisovat navrdo URL adresy vaší aplikace, které se mohou později měnit, nebo je komplikovaně skládat. V odkazu stačí uvést presenter a akci, předat případné parametry a framework už URL vygeneruje sám. Vlastně je to velice podobné, jako když voláte funkci. To se vám bude líbit. +Díky [obousměrnému routování |routing] nebudete nikdy muset do šablon či kódu zapisovat natvrdo URL adresy vaší aplikace, které se mohou později měnit, nebo je komplikovaně skládat. V odkazu stačí uvést presenter a akci, předat případné parametry a framework už URL vygeneruje sám. Vlastně je to velice podobné, jako když voláte funkci. To se vám bude líbit. V šabloně presenteru diff --git a/application/cs/templates.texy b/application/cs/templates.texy index 568b40837e..5e4452a586 100644 --- a/application/cs/templates.texy +++ b/application/cs/templates.texy @@ -274,7 +274,7 @@ class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template #[Latte\Attributes\TemplateFunction] public function isWeekend(DateTimeInterface $date): bool { - return $date->format('N') >= 6 + return $date->format('N') >= 6; } } ``` diff --git a/application/en/templates.texy b/application/en/templates.texy index 6a5ba43fd3..c5de1f1484 100644 --- a/application/en/templates.texy +++ b/application/en/templates.texy @@ -274,7 +274,7 @@ class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template #[Latte\Attributes\TemplateFunction] public function isWeekend(DateTimeInterface $date): bool { - return $date->format('N') >= 6 + return $date->format('N') >= 6; } } ``` diff --git a/best-practices/cs/creating-editing-form.texy b/best-practices/cs/creating-editing-form.texy index 4530f060d4..d43c67faf8 100644 --- a/best-practices/cs/creating-editing-form.texy +++ b/best-practices/cs/creating-editing-form.texy @@ -130,7 +130,6 @@ Záznam si uložíme do property `$record`, abychom jej měli k dispozici v meto $this->facade->update($id, $data); // ... } -} ``` Nicméně, a to by mělo být **nejdůležitejším poznatkem celého kódu**, musíme se při tvorbě formuláře ujistit, že akce je skutečně `edit`. Protože jinak by ověření v metodě `actionEdit()` vůbec neproběhlo! diff --git a/best-practices/cs/lets-create-contact-form.texy b/best-practices/cs/lets-create-contact-form.texy index e5d44812fa..23fde0f0c5 100644 --- a/best-practices/cs/lets-create-contact-form.texy +++ b/best-practices/cs/lets-create-contact-form.texy @@ -48,9 +48,6 @@ Komponentu `contactForm` necháme vykreslit v šabloně `Home/default.latte`: Pro samotné odeslání emailu vytvoříme novou třídu, kterou nazveme `ContactFacade` a umístíme ji do souboru `app/Model/ContactFacade.php`: ```php -<?php -declare(strict_types=1); - namespace App\Model; use Nette\Mail\Mailer; diff --git a/best-practices/en/creating-editing-form.texy b/best-practices/en/creating-editing-form.texy index 27e0af202b..86003dffc6 100644 --- a/best-practices/en/creating-editing-form.texy +++ b/best-practices/en/creating-editing-form.texy @@ -130,7 +130,6 @@ We store the record in the `$record` property, making it available in the `creat $this->facade->update($id, $data); // ... } -} ``` However, and this should be **the most important takeaway from the entire code**, we must ensure the action is indeed `edit` when creating the form. Otherwise, the verification in the `actionEdit()` method would not occur at all! diff --git a/best-practices/en/lets-create-contact-form.texy b/best-practices/en/lets-create-contact-form.texy index 0fd677048d..695985f569 100644 --- a/best-practices/en/lets-create-contact-form.texy +++ b/best-practices/en/lets-create-contact-form.texy @@ -48,9 +48,6 @@ Let's render the `contactForm` component in the `Home/default.latte` template: For sending the email itself, we'll create a new class named `ContactFacade` and place it in the file `app/Model/ContactFacade.php`: ```php -<?php -declare(strict_types=1); - namespace App\Model; use Nette\Mail\Mailer; diff --git a/caching/cs/@home.texy b/caching/cs/@home.texy index 17ddcaa49f..1d3f3eed17 100644 --- a/caching/cs/@home.texy +++ b/caching/cs/@home.texy @@ -125,7 +125,7 @@ Nebo pomocí 3. parametru v metodě `load()`, např: ```php $value = $cache->load($key, function () { - return ...; + return /* ... */; }, [Cache::Expire => '20 minutes']); ``` @@ -293,7 +293,7 @@ Velmi elegantně lze zachytávat a cachovat výstup: ```php if ($capture = $cache->capture($key)) { - echo ... // vypisujeme data + // echo ... vypisujeme data $capture->end(); // uložíme výstup do cache } diff --git a/caching/en/@home.texy b/caching/en/@home.texy index d8a7533643..e3557a0539 100644 --- a/caching/en/@home.texy +++ b/caching/en/@home.texy @@ -125,7 +125,7 @@ Or by using the 3rd parameter of the `load()` method itself, e.g.: ```php $value = $cache->load($key, function () { - return ...; + return /* ... */; }, [Cache::Expire => '20 minutes']); ``` @@ -293,7 +293,7 @@ Output can be captured and cached very elegantly: ```php if ($capture = $cache->capture($key)) { - echo ... // printing some data + // echo ... printing some data $capture->end(); // save the output to the cache } diff --git a/database/cs/explorer.texy b/database/cs/explorer.texy index 24044ef827..646e033f48 100644 --- a/database/cs/explorer.texy +++ b/database/cs/explorer.texy @@ -263,7 +263,7 @@ Usnadňuje stránkování výsledků. Přijímá číslo stránky (počítané o ```php $numOfPages = null; -$table->page(page: 3, itemsPerPage: 10, $numOfPages); +$table->page(page: 3, itemsPerPage: 10, numOfPages: $numOfPages); echo "Celkem stránek: $numOfPages"; ``` diff --git a/database/en/explorer.texy b/database/en/explorer.texy index dc5e7ef7af..d08f43a51e 100644 --- a/database/en/explorer.texy +++ b/database/en/explorer.texy @@ -263,7 +263,7 @@ Facilitates pagination of results. It accepts the page number (starting from 1) ```php $numOfPages = null; -$table->page(page: 3, itemsPerPage: 10, $numOfPages); +$table->page(page: 3, itemsPerPage: 10, numOfPages: $numOfPages); echo "Total pages: $numOfPages"; ``` diff --git a/dependency-injection/cs/faq.texy b/dependency-injection/cs/faq.texy index e9842c0bba..a64b806cbe 100644 --- a/dependency-injection/cs/faq.texy +++ b/dependency-injection/cs/faq.texy @@ -88,7 +88,7 @@ Mějme na paměti [Pravidlo č. 1: nech si to předat |introduction#Pravidlo č. V této ukázce je `%myParameter%` zástupný symbol pro hodnotu parametru `myParameter`, který se předá do konstruktoru třídy `MyClass`: -```php +```neon # config.neon parameters: myParameter: Some value diff --git a/dependency-injection/cs/global-state.texy b/dependency-injection/cs/global-state.texy index d152d69973..2f7ddf5caf 100644 --- a/dependency-injection/cs/global-state.texy +++ b/dependency-injection/cs/global-state.texy @@ -93,7 +93,7 @@ Musíte podrobně procházet kód, abyste zjistili, že objekt `PaymentGateway` ```php $db = new DB('mysql:', 'user', 'password'); -$gateway = new PaymentGateway($db, ...); +$gateway = new PaymentGateway($db, /* ... */); ``` Podobný problém se objevuje i při použití globálního přístupu k databázovému spojení: diff --git a/dependency-injection/en/faq.texy b/dependency-injection/en/faq.texy index b7f65321ba..6f8b836dc9 100644 --- a/dependency-injection/en/faq.texy +++ b/dependency-injection/en/faq.texy @@ -88,7 +88,7 @@ Keep in mind [Rule #1: Let It Be Passed to You |introduction#Rule #1: Let It Be In this example, `%myParameter%` is a placeholder for the value of the `myParameter` parameter, which will be passed to the `MyClass` constructor: -```php +```neon # config.neon parameters: myParameter: Some value diff --git a/dependency-injection/en/global-state.texy b/dependency-injection/en/global-state.texy index f626392831..794f2814b3 100644 --- a/dependency-injection/en/global-state.texy +++ b/dependency-injection/en/global-state.texy @@ -93,7 +93,7 @@ You must meticulously trace the code to discover that the `PaymentGateway` objec ```php $db = new DB('mysql:', 'user', 'password'); -$gateway = new PaymentGateway($db, ...); +$gateway = new PaymentGateway($db, /* ... */); ``` A similar problem arises when using global access to a database connection: diff --git a/latte/bg/custom-tags.texy b/latte/bg/custom-tags.texy index 46bbf08171..23b5b4e001 100644 --- a/latte/bg/custom-tags.texy +++ b/latte/bg/custom-tags.texy @@ -923,7 +923,7 @@ class MyLatteExtension extends Extension Генериран HTML: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Наистина ли искате да изтриете елемент 123?")">Изтриване</a> ``` diff --git a/latte/bg/safety-first.texy b/latte/bg/safety-first.texy index 56a3b9fd91..6c7b2d9e1f 100644 --- a/latte/bg/safety-first.texy +++ b/latte/bg/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>Резултати от търсенето за <em>' . $search . '</em Нападателят може в полето за търсене и съответно в променливата `$search` да запише произволен низ, т.е. и HTML код като `<script>alert("Hacked!")</script>`. Тъй като изходът не е обработен по никакъв начин, той става част от показаната страница: -```html +```latte <p>Резултати от търсенето за <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; На нападателя е достатъчно като описание да вмъкне умело съставен низ `" onload="alert('Hacked!')` и ако изписването не е обработено, резултатният код ще изглежда така: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -91,7 +91,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Какво точно се разбира под думата контекст? Това е място в документа със собствени правила за обработка на извежданите данни. Зависи от типа на документа (HTML, XML, CSS, JavaScript, plain text, ...) и може да се различава в конкретните му части. Например в HTML документ има цяла редица такива места (контексти), където важат много различни правила. Може би ще се изненадате колко са. Ето първите четири: -```html +```latte <p>#текст</p> <img src="#атрибут"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Контекстите също могат да се наслояват, което се случва, когато вмъкнем JavaScript или CSS в HTML. Това може да се направи по два различни начина, с елемент и с атрибут: -```html +```latte <script>#js-element</script> <img onclick="#js-атрибут"> @@ -132,7 +132,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Ако го извеждате в HTML текст, точно в този случай не е необходимо да правите никакви замени, защото низът не съдържа нито един знак със специално значение. Друга ситуация възниква, ако го изведете вътре в HTML атрибут, ограден с единични кавички. В такъв случай е необходимо да екранирате кавичките в HTML ентичности: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Ако този код вмъкнем в HTML документ с помощта на `<script>`, не е необходимо да се променя нищо друго, защото в него не се среща забранената последователност `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Ако обаче искахме да го вмъкнем в HTML атрибут, трябва още да екранираме кавичките в HTML ентичности: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll И когато този низ изведем в атрибут, ще приложим още екраниране според този контекст и ще заменим `&` с `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Latte вижда шаблона по същия начин като вас. Ра Нападателят като описание на изображението вмъква умело съставен низ `foo onload=alert('Hacked!')`. Вече знаем, че Twig не може да разпознае дали променливата се извежда в потока на HTML текста, вътре в атрибут, HTML коментар и т.н., накратко не разграничава контексти. И само механично преобразува знаците `< > & ' "` в HTML ентичности. Така резултатният код ще изглежда така: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Latte вижда шаблона по същия начин като вас. Ра Latte вижда шаблона по същия начин като вас. За разлика от Twig, разбира HTML и знае, че променливата се извежда като стойност на атрибут, който не е в кавички. Затова ги допълва. Когато нападателят вмъкне същото описание, резултатният код ще изглежда така: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/cs/custom-filters.texy b/latte/cs/custom-filters.texy index 7f7cab0c0c..75aff4b35e 100644 --- a/latte/cs/custom-filters.texy +++ b/latte/cs/custom-filters.texy @@ -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..bb3677923d 100644 --- a/latte/cs/custom-functions.texy +++ b/latte/cs/custom-functions.texy @@ -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..415ed73fdd 100644 --- a/latte/cs/custom-tags.texy +++ b/latte/cs/custom-tags.texy @@ -489,7 +489,7 @@ class MyLatteExtension extends Extension // Při registraci rozšíření: $isDev = true; // Určete toto na základě prostředí vaší aplikace -$latte->addExtension(new App\Latte\MyLatteExtension($isDev)); +$latte->addExtension(new MyLatteExtension($isDev)); ``` A jeho použití v šabloně: @@ -923,7 +923,7 @@ Nyní můžete použít `n:confirm` na odkazech, tlačítkách nebo prvcích for Vygenerované HTML: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Opravdu chcete smazat položku 123?")">Smazat</a> ``` 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 '<p>Výsledky vyhledávání pro <em>' . $search . '</em></p>'; Ú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 `<script>alert("Hacked!")</script>`. Protože výstup není nijak ošetřen, stane se součástí zobrazené stránky: -```html +```latte <p>Výsledky vyhledávání pro <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Útočníkovi stačí jako popisek vložit šikovně sestavený řetězec `" onload="alert('Hacked!')` a když vypsání nebude ošetřeno, výsledný kód bude vypadat takto: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -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 <p>#text</p> <img src="#atribut"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ Zajímavé je to uvnitř HTML komentářů. Tady se totiž k escapování nepou Kontexty se také mohou vrstvit, k čemuž dochází, když vložíme JavaScript nebo CSS do HTML. To lze udělat dvěma odlišnými způsoby, elementem a atributem: -```html +```latte <script>#js-element</script> <img onclick="#js-atribut"> @@ -132,7 +132,7 @@ Mějme řetězec `Rock'n'Roll`. Pokud jej budete vypisovat v HTML textu, zrovna v tomhle případě netřeba dělat žádné záměny, protože řetězec neobsahuje žádný znak se speciálním významem. Jiná situace nastane, pokud jej vypíšete uvnitř HTML atributu uvozeného do jednoduchých uvozovek. V takovém případě je potřeba escapovat uvozovky na HTML entity: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Pokud tento kód vložíme do HTML dokumentu pomocí `<script>`, netřeba nic dalšího upravovat, protože se v něm nevyskytuje zakázaná sekvence `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Pokud bychom jej však chtěli vložit do HTML atributu, musíme ještě escapovat uvozovky na HTML entity: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll A když tento řetězec vypíšeme v atributu, ještě aplikujeme escapování podle tohoto kontextu a nahradíme `&` za `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Všimněte si, že okolo hodnot atributů nejsou uvozovky. Kodér na ně mohl za Útočník jako popisek obrázku vloží šikovně sestavený řetězec `foo onload=alert('Hacked!')`. Už víme, že Twig nemůže poznat, jestli se proměnná vypisuje v toku HTML textu, uvnitř atributu, HTML komentáře, atd., zkrátka nerozlišuje kontexty. A jen mechanicky převádí znaky `< > & ' "` na HTML entity. Takže výsledný kód bude vypadat takto: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -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 <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/cs/syntax.texy b/latte/cs/syntax.texy index c5346ba1f8..39f1c621de 100644 --- a/latte/cs/syntax.texy +++ b/latte/cs/syntax.texy @@ -233,7 +233,7 @@ Latte zachází s bílými znaky inteligentně. Kód můžete volně odsazovat p Vypíše: -```html +```latte <ul> <li>foo</li> <li>bar</li> diff --git a/latte/cs/tags.texy b/latte/cs/tags.texy index 598b2b73dd..cb8b03378a 100644 --- a/latte/cs/tags.texy +++ b/latte/cs/tags.texy @@ -137,7 +137,7 @@ Jako výraz můžete zapsat cokoliv, co znáte z PHP. Nemusíte se zkrátka uči ```latte -{='0' . ($num ?? $num * 3) . ', ' . PHP_VERSION} +{='0' . ($num ?? $num * 3) . ', ' . \PHP_VERSION} ``` Prosím, nehledejte v předchozím příkladu žádný smysl, ale kdybyste tam nějaký našli, napište nám :-) diff --git a/latte/de/custom-tags.texy b/latte/de/custom-tags.texy index 483b5f5820..89089b627d 100644 --- a/latte/de/custom-tags.texy +++ b/latte/de/custom-tags.texy @@ -923,7 +923,7 @@ Jetzt können Sie `n:confirm` auf Links, Schaltflächen oder Formularelementen v Generiertes HTML: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Möchten Sie den Eintrag 123 wirklich löschen?")">Löschen</a> ``` diff --git a/latte/de/safety-first.texy b/latte/de/safety-first.texy index 2f5b0697a1..bdc0cb85fa 100644 --- a/latte/de/safety-first.texy +++ b/latte/de/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>Suchergebnisse für <em>' . $search . '</em></p>'; Ein Angreifer kann in das Suchfeld und somit in die Variable `$search` eine beliebige Zeichenkette eingeben, also auch HTML-Code wie `<script>alert("Gehackt!")</script>`. Da die Ausgabe nicht bereinigt wird, wird sie Teil der angezeigten Seite: -```html +```latte <p>Suchergebnisse für <em><script>alert("Gehackt!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Dem Angreifer genügt es, als Beschreibung eine geschickt konstruierte Zeichenkette `" onload="alert('Gehackt!')` einzufügen, und wenn die Ausgabe nicht bereinigt wird, sieht der resultierende Code so aus: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Gehackt!')"> ``` @@ -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 <p>#text</p> <img src="#attribut"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ Interessant ist es innerhalb von HTML-Kommentaren. Hier wird nämlich kein Escap Kontexte können sich auch verschachteln, was passiert, wenn wir JavaScript oder CSS in HTML einbetten. Dies kann auf zwei verschiedene Arten geschehen, mit einem Element und einem Attribut: -```html +```latte <script>#js-element</script> <img onclick="#js-attribut"> @@ -132,7 +132,7 @@ Nehmen wir die Zeichenkette `Rock'n'Roll`. Wenn Sie sie im HTML-Text ausgeben, müssen in diesem Fall keine Ersetzungen vorgenommen werden, da die Zeichenkette kein Zeichen mit besonderer Bedeutung enthält. Eine andere Situation ergibt sich, wenn Sie sie innerhalb eines HTML-Attributs ausgeben, das in einfache Anführungszeichen eingeschlossen ist. In diesem Fall müssen die Anführungszeichen in HTML-Entitäten escapet werden: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Wenn wir diesen Code mit `<script>` in ein HTML-Dokument einfügen, muss nichts weiter geändert werden, da die verbotene Sequenz `</script` nicht darin vorkommt: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Wenn wir ihn jedoch in ein HTML-Attribut einfügen wollten, müssten wir die Anführungszeichen noch in HTML-Entitäten escapen: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll Und wenn wir diese Zeichenkette in einem Attribut ausgeben, wenden wir noch das Escaping gemäß diesem Kontext an und ersetzen `&` durch `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Beachten Sie, dass um die Attributwerte keine Anführungszeichen stehen. Der Pro Ein Angreifer fügt als Bildbeschreibung eine geschickt konstruierte Zeichenkette `foo onload=alert('Gehackt!')` ein. Wir wissen bereits, dass Twig nicht erkennen kann, ob die Variable im Fluss des HTML-Textes, innerhalb eines Attributs, eines HTML-Kommentars usw. ausgegeben wird, kurz gesagt, es unterscheidet keine Kontexte. Und konvertiert nur mechanisch die Zeichen `< > & ' "` in HTML-Entitäten. Der resultierende Code sieht also so aus: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Gehackt!')> ``` @@ -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 <img src="photo0145.webp" alt="foo onload=alert('Gehackt!')"> ``` 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 <a href="delete.php?id=123" onclick="return confirm("Θέλετε πραγματικά να διαγράψετε το στοιχείο 123?")">Διαγραφή</a> ``` 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 '<p>Αποτελέσματα αναζήτησης για <em>' . $search . Ένας εισβολέας μπορεί να γράψει στο πεδίο αναζήτησης και κατ' επέκταση στη μεταβλητή `$search` οποιοδήποτε string, δηλαδή και κώδικα HTML όπως `<script>alert("Hacked!")</script>`. Επειδή η έξοδος δεν επεξεργάζεται με κανέναν τρόπο, γίνεται μέρος της εμφανιζόμενης σελίδας: -```html +```latte <p>Αποτελέσματα αναζήτησης για <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Αρκεί ο εισβολέας να εισάγει ως λεζάντα ένα έξυπνα κατασκευασμένο string `" onload="alert('Hacked!')` και αν η εκτύπωση δεν επεξεργαστεί, ο προκύπτων κώδικας θα μοιάζει ως εξής: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -91,7 +91,7 @@ Context-Aware Escaping Τι ακριβώς εννοούμε με τη λέξη context; Πρόκειται για ένα μέρος στο έγγραφο με τους δικούς του κανόνες για την επεξεργασία των εκτυπωμένων δεδομένων. Εξαρτάται από τον τύπο του εγγράφου (HTML, XML, CSS, JavaScript, plain text, ...) και μπορεί να διαφέρει σε συγκεκριμένα μέρη του. Για παράδειγμα, σε ένα έγγραφο HTML, υπάρχουν πολλά τέτοια μέρη (contexts) όπου ισχύουν πολύ διαφορετικοί κανόνες. Ίσως εκπλαγείτε πόσα είναι. Εδώ έχουμε την πρώτη τετράδα: -```html +```latte <p>#κείμενο</p> <img src="#attribute"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ Context-Aware Escaping Τα contexts μπορούν επίσης να στρωματοποιηθούν, κάτι που συμβαίνει όταν ενσωματώνουμε JavaScript ή CSS σε HTML. Αυτό μπορεί να γίνει με δύο διαφορετικούς τρόπους, με στοιχείο και με attribute: -```html +```latte <script>#js-element</script> <img onclick="#js-attribute"> @@ -132,7 +132,7 @@ Context-Aware Escaping Αν το εκτυπώσετε σε κείμενο HTML, σε αυτή τη συγκεκριμένη περίπτωση δεν χρειάζεται να κάνετε καμία αντικατάσταση, επειδή το string δεν περιέχει κανέναν χαρακτήρα με ειδική σημασία. Η κατάσταση αλλάζει αν το εκτυπώσετε μέσα σε ένα attribute HTML που περικλείεται σε απλά εισαγωγικά. Σε αυτή την περίπτωση, πρέπει να κάνετε escape τα εισαγωγικά σε οντότητες HTML: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Αν εισάγουμε αυτόν τον κώδικα σε ένα έγγραφο HTML χρησιμοποιώντας το `<script>`, δεν χρειάζεται να τροποποιήσουμε τίποτα άλλο, επειδή δεν περιέχει την απαγορευμένη ακολουθία `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Αν όμως θέλαμε να το εισάγουμε σε ένα attribute HTML, πρέπει ακόμα να κάνουμε escape τα εισαγωγικά σε οντότητες HTML: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll Και όταν εκτυπώνουμε αυτό το string σε ένα attribute, εφαρμόζουμε επιπλέον το escaping σύμφωνα με αυτό το context και αντικαθιστούμε το `&` με `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Latte εναντίον απλοϊκών συστημάτων Ένας εισβολέας εισάγει ως λεζάντα της εικόνας ένα έξυπνα κατασκευασμένο string `foo onload=alert('Hacked!')`. Γνωρίζουμε ήδη ότι το Twig δεν μπορεί να αναγνωρίσει αν η μεταβλητή εκτυπώνεται στη ροή του κειμένου HTML, μέσα σε ένα attribute, σε ένα σχόλιο HTML κ.λπ., με λίγα λόγια δεν διακρίνει τα contexts. Και απλώς μετατρέπει μηχανικά τους χαρακτήρες `< > & ' "` σε οντότητες HTML. Έτσι, ο προκύπτων κώδικας θα μοιάζει ως εξής: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Latte εναντίον απλοϊκών συστημάτων Το Latte βλέπει το πρότυπο όπως εσείς. Σε αντίθεση με το Twig, καταλαβαίνει HTML και ξέρει ότι η μεταβλητή εκτυπώνεται ως τιμή ενός attribute που δεν βρίσκεται σε εισαγωγικά. Γι' αυτό τα συμπληρώνει. Όταν ο εισβολέας εισάγει την ίδια λεζάντα, ο προκύπτων κώδικας θα μοιάζει ως εξής: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/en/custom-filters.texy b/latte/en/custom-filters.texy index 9fc3ea58b6..186caf6fb4 100644 --- a/latte/en/custom-filters.texy +++ b/latte/en/custom-filters.texy @@ -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..f4616ceea1 100644 --- a/latte/en/custom-functions.texy +++ b/latte/en/custom-functions.texy @@ -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..6fcc3cd9aa 100644 --- a/latte/en/custom-tags.texy +++ b/latte/en/custom-tags.texy @@ -489,7 +489,7 @@ class MyLatteExtension extends Extension // When registering the extension: $isDev = true; // Determine this based on your application's environment -$latte->addExtension(new App\Latte\MyLatteExtension($isDev)); +$latte->addExtension(new MyLatteExtension($isDev)); ``` And use it in a template: @@ -923,7 +923,7 @@ Now you can use `n:confirm` on links, buttons, or form elements: Generated HTML: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Do you really want to delete item 123?")">Delete</a> ``` 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 '<p>Search results for <em>' . $search . '</em></p>'; An attacker can enter any string into the search box, and thus into the `$search` variable, including HTML code like `<script>alert("Hacked!")</script>`. Since the output is not sanitized, it becomes part of the displayed page: -```html +```latte <p>Search results for <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; 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 <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -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 <p>#text</p> <img src="#attribute"> <textarea>#rawtext</textarea> @@ -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 <script>#js-element</script> <img onclick="#js-attribute"> @@ -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 <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); If we insert this code into an HTML document using `<script>`, no further modification is needed because it does not contain the forbidden sequence `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` However, if we wanted to insert it into an HTML attribute, we still need to escape the quotes into HTML entities: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -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 <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -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 <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -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 <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/en/syntax.texy b/latte/en/syntax.texy index 24656e0571..aa7e274f5f 100644 --- a/latte/en/syntax.texy +++ b/latte/en/syntax.texy @@ -233,7 +233,7 @@ Latte handles whitespace intelligently. You can freely indent your code for read Outputs: -```html +```latte <ul> <li>foo</li> <li>bar</li> diff --git a/latte/en/tags.texy b/latte/en/tags.texy index cfbc94ae6f..ffb90c69c1 100644 --- a/latte/en/tags.texy +++ b/latte/en/tags.texy @@ -137,7 +137,7 @@ You can write anything you know from PHP as an expression. You simply don't have ```latte -{='0' . ($num ?? $num * 3) . ', ' . PHP_VERSION} +{='0' . ($num ?? $num * 3) . ', ' . \PHP_VERSION} ``` Please don't look for any meaning in the previous example, but if you find one, let us know :-) diff --git a/latte/es/custom-tags.texy b/latte/es/custom-tags.texy index f32e309910..fbb0646b5b 100644 --- a/latte/es/custom-tags.texy +++ b/latte/es/custom-tags.texy @@ -923,7 +923,7 @@ Ahora puede usar `n:confirm` en enlaces, botones o elementos de formulario: HTML generado: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("¿Realmente quieres eliminar el elemento 123?")">Eliminar</a> ``` diff --git a/latte/es/safety-first.texy b/latte/es/safety-first.texy index 437dd39aac..6fe173ea9c 100644 --- a/latte/es/safety-first.texy +++ b/latte/es/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>Resultados de la búsqueda para <em>' . $search . '</em></p>'; Un atacante puede escribir en el campo de búsqueda y, por extensión, en la variable `$search` cualquier cadena, incluido código HTML como `<script>alert("Hacked!")</script>`. Dado que la salida no está saneada de ninguna manera, se convierte en parte de la página mostrada: -```html +```latte <p>Resultados de la búsqueda para <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; 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 <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -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 <p>#texto</p> <img src="#atributo"> <textarea>#rawtext</textarea> @@ -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 <script>#js-elemento</script> <img onclick="#js-atributo"> @@ -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 <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Si insertamos este código en un documento HTML usando `<script>`, no es necesario modificar nada más, porque no contiene la secuencia prohibida `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Sin embargo, si quisiéramos insertarlo en un atributo HTML, aún debemos escapar las comillas a entidades HTML: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -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 <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -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 <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -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 <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` 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 <a href="delete.php?id=123" onclick="return confirm("Voulez-vous vraiment supprimer l'élément 123 ?")">Supprimer</a> ``` 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 '<p>Résultats de la recherche pour <em>' . $search . '</em></p>'; Un attaquant peut entrer dans le champ de recherche et donc dans la variable `$search` n'importe quelle chaîne, y compris du code HTML comme `<script>alert("Piraté !")</script>`. Comme la sortie n'est pas traitée, elle devient partie intégrante de la page affichée : -```html +```latte <p>Résultats de la recherche pour <em><script>alert("Piraté !")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; 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 <img src="photo0145.webp" alt="" onload="alert('Piraté !')"> ``` @@ -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 <p>#texte</p> <img src="#attribut"> <textarea>#rawtext</textarea> @@ -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 <script>#js-element</script> <img onclick="#js-attribut"> @@ -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 <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Si nous insérons ce code dans un document HTML à l'aide de `<script>`, il n'est pas nécessaire de le modifier davantage, car il ne contient pas la séquence interdite `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Cependant, si nous voulions l'insérer dans un attribut HTML, nous devrions encore échapper les guillemets en entités HTML : -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -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 <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -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 <img src=photo0145.webp alt=foo onload=alert('Piraté !')> ``` @@ -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 <img src="photo0145.webp" alt="foo onload=alert('Piraté !')"> ``` 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 <a href="delete.php?id=123" onclick="return confirm("Valóban törölni szeretné a(z) 123 elemet?")">Törlés</a> ``` 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 '<p>Keresési eredmények erre: <em>' . $search . '</em></p>'; A támadó a keresőmezőbe és ezáltal a `$search` változóba bármilyen stringet beírhat, tehát HTML kódot is, mint `<script>alert("Hacked!")</script>`. Mivel a kimenet nincs semmilyen módon kezelve, a megjelenített oldal részévé válik: -```html +```latte <p>Keresési eredmények erre: <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; 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 <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -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 <p>#szöveg</p> <img src="#attribútum"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ Talán meglepő, de speciális szabályok érvényesek a `<textarea>` és `<titl A kontextusok rétegződhetnek is, ami akkor következik be, amikor JavaScriptet vagy CSS-t illesztünk be HTML-be. Ezt két különböző módon lehet megtenni, elemmel és attribútummal: -```html +```latte <script>#js-elem</script> <img onclick="#js-attribútum"> @@ -132,7 +132,7 @@ Vegyünk egy `Rock'n'Roll` stringet. Ha HTML szövegben írja ki, ebben az esetben nincs szükség semmilyen cserére, mert a string nem tartalmaz semmilyen speciális jelentéssel bíró karaktert. Más a helyzet, ha egy aposztrófokkal határolt HTML attribútumon belül írja ki. Ebben az esetben az aposztrófokat HTML entitásokra kell escapelni: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Ha ezt a kódot a `<script>` segítségével illesztjük be a HTML dokumentumba, nincs szükség további módosításra, mert nem tartalmazza a tiltott `</script` szekvenciát: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Ha azonban HTML attribútumba szeretnénk beilleszteni, még escapelnünk kell az idézőjeleket HTML entitásokra: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll És amikor ezt a stringet egy attribútumban írjuk ki, még alkalmazzuk az escapelést ennek a kontextusnak megfelelően, és helyettesítjük az `&`-t `&`-pal: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Figyelje meg, hogy az attribútumok értékei körül nincsenek idézőjelek. A A támadó a kép leírásaként egy ügyesen összeállított `foo onload=alert('Hacked!')` stringet illeszt be. Már tudjuk, hogy a Twig nem tudja megállapítani, hogy a változó a HTML szövegfolyamban, egy attribútumon belül, HTML kommentárban stb. íródik-e ki, röviden nem különbözteti meg a kontextusokat. És csak mechanikusan alakítja át a `< > & ' "` karaktereket HTML entitásokká. Így az eredményül kapott kód így fog kinézni: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Most nézzük meg, hogyan bánik el ugyanezzel a sablonnal a Latte: A Latte ugyanúgy látja a sablont, mint Ön. A Twiggel ellentétben érti a HTML-t, és tudja, hogy a változó egy attribútum értékeként íródik ki, amely nincs idézőjelek között. Ezért kiegészíti őket. Ha a támadó ugyanazt a leírást illeszti be, az eredményül kapott kód így fog kinézni: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/it/custom-tags.texy b/latte/it/custom-tags.texy index 40b065cc9a..3200404f53 100644 --- a/latte/it/custom-tags.texy +++ b/latte/it/custom-tags.texy @@ -923,7 +923,7 @@ Ora potete usare `n:confirm` su link, pulsanti o elementi del form: HTML generato: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Vuoi davvero eliminare l'elemento 123?")">Elimina</a> ``` diff --git a/latte/it/safety-first.texy b/latte/it/safety-first.texy index a13fdc6a16..29fc90f37f 100644 --- a/latte/it/safety-first.texy +++ b/latte/it/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>Risultati della ricerca per <em>' . $search . '</em></p>'; Un aggressore può inserire nella casella di ricerca e quindi nella variabile `$search` qualsiasi stringa, quindi anche codice HTML come `<script>alert("Hacked!")</script>`. Poiché l'output non è in alcun modo trattato, diventa parte della pagina visualizzata: -```html +```latte <p>Risultati della ricerca per <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; All'aggressore basta inserire come didascalia una stringa abilmente costruita `" onload="alert('Hacked!')` e se la stampa non viene trattata, il codice risultante sarà simile a questo: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -91,7 +91,7 @@ Escaping sensibile al contesto Cosa si intende esattamente con la parola contesto? È un luogo nel documento con le proprie regole per il trattamento dei dati stampati. Dipende dal tipo di documento (HTML, XML, CSS, JavaScript, testo semplice, ...) e può differire nelle sue parti specifiche. Ad esempio, in un documento HTML, ci sono molti posti (contesti) in cui si applicano regole molto diverse. Potresti essere sorpreso di quanti ce ne siano. Ecco i primi quattro: -```html +```latte <p>#text</p> <img src="#atribut"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ Potresti essere sorpreso, ma regole speciali si applicano all'interno degli elem I contesti possono anche essere annidati, cosa che accade quando inseriamo JavaScript o CSS in HTML. Questo può essere fatto in due modi diversi, con un elemento e con un attributo: -```html +```latte <script>#js-element</script> <img onclick="#js-atribut"> @@ -132,7 +132,7 @@ Prendiamo la stringa `Rock'n'Roll`. Se la stampi nel testo HTML, proprio in questo caso non è necessario effettuare alcuna sostituzione, perché la stringa non contiene alcun carattere con significato speciale. La situazione cambia se la stampi all'interno di un attributo HTML racchiuso tra apici singoli. In tal caso, è necessario eseguire l'escaping degli apici in entità HTML: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Se inseriamo questo codice nel documento HTML usando `<script>`, non è necessario modificare nient'altro, perché non contiene la sequenza proibita `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Se però volessimo inserirlo in un attributo HTML, dobbiamo ancora eseguire l'escaping degli apici in entità HTML: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll E quando stampiamo questa stringa in un attributo, applichiamo ancora l'escaping secondo questo contesto e sostituiamo `&` con `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Notate che non ci sono virgolette attorno ai valori degli attributi. Il codifica Un aggressore inserisce come didascalia dell'immagine una stringa abilmente costruita `foo onload=alert('Hacked!')`. Sappiamo già che Twig non può riconoscere se la variabile viene stampata nel flusso del testo HTML, all'interno di un attributo, di un commento HTML, ecc., in breve non distingue i contesti. E converte meccanicamente solo i caratteri `< > & ' "` in entità HTML. Quindi il codice risultante sarà simile a questo: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Ora vediamo come Latte gestisce lo stesso template: Latte vede il template come lo vedi tu. A differenza di Twig, capisce HTML e sa che la variabile viene stampata come valore di un attributo che non è tra virgolette. Pertanto le aggiunge. Quando l'aggressore inserisce la stessa didascalia, il codice risultante sarà simile a questo: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/ja/custom-tags.texy b/latte/ja/custom-tags.texy index a3114b7964..347975a50b 100644 --- a/latte/ja/custom-tags.texy +++ b/latte/ja/custom-tags.texy @@ -923,7 +923,7 @@ class MyLatteExtension extends Extension 生成されたHTML: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("本当にアイテム 123 を削除しますか?")">削除</a> ``` diff --git a/latte/ja/safety-first.texy b/latte/ja/safety-first.texy index 15f0190eb4..1e2c48e606 100644 --- a/latte/ja/safety-first.texy +++ b/latte/ja/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>検索結果: <em>' . $search . '</em></p>'; 攻撃者は、検索ボックス、ひいては変数 `$search` に任意の文字列、つまり `<script>alert("Hacked!")</script>` のようなHTMLコードを入力できます。出力がサニタイズされていないため、表示されるページの一部になります: -```html +```latte <p>検索結果: <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; 攻撃者は、説明として巧妙に作成された文字列 `" onload="alert('Hacked!')` を挿入するだけで、出力がサニタイズされていない場合、結果のコードは次のようになります: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -91,7 +91,7 @@ XSSからどのように防御しますか? コンテキストという言葉で正確には何を意味しますか?それは、出力されるデータのサニタイズに関する独自のルールを持つドキュメント内の場所です。それはドキュメントのタイプ(HTML、XML、CSS、JavaScript、プレーンテキストなど)に依存し、その特定の場所によって異なる場合があります。 例えば、HTMLドキュメントには、非常に異なるルールが適用される多くの場所(コンテキスト)があります。いくつあるか驚くかもしれません。ここに最初の4つがあります: -```html +```latte <p>#テキスト</p> <img src="#属性"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ HTMLコメント内では興味深いです。ここでは、エスケープにH コンテキストはネストすることもできます。これは、JavaScriptまたはCSSをHTMLに埋め込むときに発生します。これは2つの異なる方法、要素と属性で行うことができます: -```html +```latte <script>#js-element</script> <img onclick="#js-attribute"> @@ -132,7 +132,7 @@ HTMLコメント内では興味深いです。ここでは、エスケープにH HTMLテキストに出力する場合、この特定のケースでは、文字列に特別な意味を持つ文字が含まれていないため、置換を行う必要はありません。状況は、単一引用符で囲まれたHTML属性内に出力する場合に異なります。その場合、引用符をHTMLエンティティにエスケープする必要があります: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); このコードを `<script>` を使用してHTMLドキュメントに挿入する場合、禁止されているシーケンス `</script` が含まれていないため、それ以上変更する必要はありません: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` しかし、HTML属性に挿入したい場合は、さらに引用符をHTMLエンティティにエスケープする必要があります: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll そして、この文字列を属性に出力するとき、このコンテキストに従ってエスケープを適用し、`&` を `&` に置き換えます: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Latteはテンプレートをあなたと同じように見ます。HTML、XML 攻撃者は、画像の説明として巧妙に作成された文字列 `foo onload=alert('Hacked!')` を挿入します。Twigは、変数がHTMLテキストの流れの中、属性内、HTMLコメント内など、どこに出力されているかを判断できないこと、つまりコンテキストを区別しないことをすでに知っています。そして、文字 `< > & ' "` を機械的にHTMLエンティティに変換するだけです。 したがって、結果のコードは次のようになります: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Latteはテンプレートをあなたと同じように見ます。HTML、XML Latteはテンプレートをあなたと同じように見ます。Twigとは異なり、HTMLを理解し、変数が引用符で囲まれていない属性の値として出力されていることを知っています。したがって、それらを補完します。攻撃者が同じ説明を挿入すると、結果のコードは次のようになります: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/pl/custom-tags.texy b/latte/pl/custom-tags.texy index 50be9f0562..8dd42d7929 100644 --- a/latte/pl/custom-tags.texy +++ b/latte/pl/custom-tags.texy @@ -923,7 +923,7 @@ Teraz możesz użyć `n:confirm` na linkach, przyciskach lub elementach formular Wygenerowany HTML: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Czy na pewno chcesz usunąć element 123?")">Usuń</a> ``` diff --git a/latte/pl/safety-first.texy b/latte/pl/safety-first.texy index dc1f49265c..d8a666e1d7 100644 --- a/latte/pl/safety-first.texy +++ b/latte/pl/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>Wyniki wyszukiwania dla <em>' . $search . '</em></p>'; Atakujący może w polu wyszukiwania, a tym samym w zmiennej `$search`, wpisać dowolny ciąg znaków, czyli również kod HTML, taki jak `<script>alert("Hacked!")</script>`. Ponieważ wyjście nie jest w żaden sposób oczyszczone, stanie się częścią wyświetlonej strony: -```html +```latte <p>Wyniki wyszukiwania dla <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Atakującemu wystarczy jako opis wstawić sprytnie skonstruowany ciąg `" onload="alert('Hacked!')`, a jeśli wyświetlanie nie zostanie oczyszczone, wynikowy kod będzie wyglądał tak: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -91,7 +91,7 @@ Escapowanie kontekstowe Co dokładnie oznacza słowo kontekst? Jest to miejsce w dokumencie z własnymi zasadami oczyszczania wyświetlanych danych. Zależy od typu dokumentu (HTML, XML, CSS, JavaScript, plain text, ...) i może się różnić w jego poszczególnych częściach. Na przykład w dokumencie HTML istnieje wiele takich miejsc (kontekstów), gdzie obowiązują bardzo różne zasady. Być może będziesz zaskoczony, ile ich jest. Oto pierwsza czwórka: -```html +```latte <p>#text</p> <img src="#atrybut"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ Ciekawie jest wewnątrz komentarzy HTML. Tutaj bowiem do escapowania nie używa Konteksty mogą się również nakładać, co ma miejsce, gdy wstawiamy JavaScript lub CSS do HTML. Można to zrobić na dwa różne sposoby, elementem i atrybutem: -```html +```latte <script>#js-element</script> <img onclick="#js-atrybut"> @@ -132,7 +132,7 @@ Miejmy ciąg `Rock'n'Roll`. Jeśli będziesz go wyświetlać w tekście HTML, akurat w tym przypadku nie trzeba dokonywać żadnych zamian, ponieważ ciąg nie zawiera żadnego znaku o specjalnym znaczeniu. Inna sytuacja nastąpi, jeśli wyświetlisz go wewnątrz atrybutu HTML ujętego w pojedyncze cudzysłowy. W takim przypadku trzeba escapować cudzysłowy na encje HTML: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Jeśli ten kod wstawimy do dokumentu HTML za pomocą `<script>`, nie trzeba niczego więcej modyfikować, ponieważ nie występuje w nim zakazana sekwencja `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Jeśli jednak chcielibyśmy go wstawić do atrybutu HTML, musimy jeszcze escapować cudzysłowy na encje HTML: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll A kiedy ten ciąg wyświetlimy w atrybucie, jeszcze zastosujemy escapowanie zgodnie z tym kontekstem i zastąpimy `&` na `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Zwróć uwagę, że wokół wartości atrybutów nie ma cudzysłowów. Koder mó Atakujący jako opis obrazka wstawia sprytnie skonstruowany ciąg `foo onload=alert('Hacked!')`. Już wiemy, że Twig nie może rozpoznać, czy zmienna jest wyświetlana w przepływie tekstu HTML, wewnątrz atrybutu, komentarza HTML itp., krótko mówiąc, nie rozróżnia kontekstów. I tylko mechanicznie konwertuje znaki `< > & ' "` na encje HTML. Więc wynikowy kod będzie wyglądał tak: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Teraz zobaczymy, jak z tym samym szablonem poradzi sobie Latte: Latte widzi szablon tak samo jak Ty. W przeciwieństwie do Twiga rozumie HTML i wie, że zmienna jest wyświetlana jako wartość atrybutu, który nie jest w cudzysłowach. Dlatego je uzupełni. Kiedy atakujący wstawi ten sam opis, wynikowy kod będzie wyglądał tak: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/pt/custom-tags.texy b/latte/pt/custom-tags.texy index b080e9e20b..1e523ed866 100644 --- a/latte/pt/custom-tags.texy +++ b/latte/pt/custom-tags.texy @@ -923,7 +923,7 @@ Agora você pode usar `n:confirm` em links, botões ou elementos de formulário: HTML gerado: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Realmente deseja excluir o item 123?")">Excluir</a> ``` diff --git a/latte/pt/safety-first.texy b/latte/pt/safety-first.texy index 049ff1ae25..5e0351605c 100644 --- a/latte/pt/safety-first.texy +++ b/latte/pt/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>Resultados da pesquisa para <em>' . $search . '</em></p>'; Um atacante pode inserir na caixa de pesquisa e, consequentemente, na variável `$search`, qualquer string, ou seja, também código HTML como `<script>alert("Hacked!")</script>`. Como a saída não é tratada de forma alguma, ela torna-se parte da página exibida: -```html +```latte <p>Resultados da pesquisa para <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Basta ao atacante inserir como legenda uma string habilmente construída `" onload="alert('Hacked!')` e, se a exibição não for tratada, o código resultante será assim: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -91,7 +91,7 @@ Escaping sensível ao contexto O que exatamente se entende pela palavra contexto? É um local no documento com as suas próprias regras para o tratamento dos dados exibidos. Depende do tipo de documento (HTML, XML, CSS, JavaScript, texto simples, ...) e pode diferir nas suas partes específicas. Por exemplo, num documento HTML, existem muitos desses locais (contextos), onde regras muito diferentes se aplicam. Pode surpreender-se com quantos existem. Aqui temos os quatro primeiros: -```html +```latte <p>#texto</p> <img src="#atributo"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ Pode surpreender-se, mas regras especiais aplicam-se dentro dos elementos `<text Os contextos também podem ser aninhados, o que ocorre quando inserimos JavaScript ou CSS em HTML. Isso pode ser feito de duas maneiras diferentes, por elemento e por atributo: -```html +```latte <script>#js-elemento</script> <img onclick="#js-atributo"> @@ -132,7 +132,7 @@ Tenha a string `Rock'n'Roll`. Se a exibir em texto HTML, neste caso específico não é necessário fazer nenhuma substituição, pois a string não contém nenhum caractere com significado especial. A situação muda se a exibir dentro de um atributo HTML delimitado por aspas simples. Nesse caso, é necessário escapar as aspas para entidades HTML: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Se inserirmos este código num documento HTML usando `<script>`, não é necessário modificar mais nada, pois ele não contém a sequência proibida `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` No entanto, se quiséssemos inseri-lo num atributo HTML, ainda precisaríamos de escapar as aspas para entidades HTML: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll E quando exibimos esta string num atributo, ainda aplicamos o escaping de acordo com este contexto e substituímos `&` por `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Observe que não há aspas em torno dos valores dos atributos. O codificador pod Um atacante insere como legenda da imagem uma string habilmente construída `foo onload=alert('Hacked!')`. Já sabemos que o Twig não pode saber se a variável está a ser exibida no fluxo de texto HTML, dentro de um atributo, comentário HTML, etc., em suma, não distingue contextos. E apenas converte mecanicamente os caracteres `< > & ' "` em entidades HTML. Então, o código resultante será assim: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Agora veremos como o Latte lida com o mesmo template: O Latte vê o template da mesma forma que você. Ao contrário do Twig, ele entende HTML e sabe que a variável está a ser exibida como o valor de um atributo que não está entre aspas. Portanto, ele adiciona-as. Quando o atacante insere a mesma legenda, o código resultante será assim: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/ro/custom-tags.texy b/latte/ro/custom-tags.texy index bc12920a37..3c353caabb 100644 --- a/latte/ro/custom-tags.texy +++ b/latte/ro/custom-tags.texy @@ -923,7 +923,7 @@ Acum puteți utiliza `n:confirm` pe linkuri, butoane sau elemente de formular: HTML generat: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Sigur doriți să ștergeți elementul 123?")">Șterge</a> ``` diff --git a/latte/ro/safety-first.texy b/latte/ro/safety-first.texy index 8140ba61d3..b4948a948d 100644 --- a/latte/ro/safety-first.texy +++ b/latte/ro/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>Rezultatele căutării pentru <em>' . $search . '</em></p>'; Un atacator poate introduce în câmpul de căutare și, implicit, în variabila `$search`, orice șir, deci și cod HTML precum `<script>alert("Hacked!")</script>`. Deoarece ieșirea nu este tratată în niciun fel, aceasta devine parte a paginii afișate: -```html +```latte <p>Rezultatele căutării pentru <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Atacatorului îi este suficient să introducă ca descriere un șir inteligent construit `" onload="alert('Hacked!')` și dacă afișarea nu este tratată, codul rezultat va arăta astfel: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -91,7 +91,7 @@ Escapare contextuală sensibilă Ce se înțelege exact prin cuvântul context? Este un loc în document cu propriile reguli pentru tratarea datelor afișate. Depinde de tipul documentului (HTML, XML, CSS, JavaScript, plain text, ...) și poate diferi în părțile sale specifice. De exemplu, într-un document HTML există o serie întreagă de astfel de locuri (contexte) unde se aplică reguli foarte diferite. Poate veți fi surprinși câte sunt. Iată primele patru: -```html +```latte <p>#text</p> <img src="#atribut"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ Interesant este în interiorul comentariilor HTML. Aici, escaparea nu se face fo Contexturile se pot și stratifica, ceea ce se întâmplă când inserăm JavaScript sau CSS în HTML. Acest lucru se poate face în două moduri diferite, prin element și atribut: -```html +```latte <script>#js-element</script> <img onclick="#js-atribut"> @@ -132,7 +132,7 @@ Să avem șirul `Rock'n'Roll`. Dacă îl veți afișa în text HTML, în acest caz particular nu este nevoie de nicio înlocuire, deoarece șirul nu conține niciun caracter cu semnificație specială. Situația se schimbă dacă îl afișați în interiorul unui atribut HTML delimitat de apostrofuri simple. În acest caz, este necesar să escapați apostrofurile în entități HTML: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Dacă inserăm acest cod într-un document HTML folosind `<script>`, nu mai este nevoie de nicio altă modificare, deoarece nu conține secvența interzisă `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Dacă am dori însă să îl inserăm într-un atribut HTML, trebuie să mai escapăm ghilimelele în entități HTML: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll Și când afișăm acest șir într-un atribut, aplicăm și escaparea conform acestui context și înlocuim `&` cu `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Observați că în jurul valorilor atributelor nu există ghilimele. Coderul le- Atacatorul introduce ca descriere a imaginii un șir inteligent construit `foo onload=alert('Hacked!')`. Știm deja că Twig nu poate recunoaște dacă variabila se afișează în fluxul textului HTML, în interiorul unui atribut, comentariu HTML etc., pe scurt, nu distinge contextele. Și doar convertesc mecanic caracterele `< > & ' "` în entități HTML. Deci, codul rezultat va arăta astfel: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Acum să vedem cum se descurcă Latte cu același șablon: Latte vede șablonul la fel ca dvs. Spre deosebire de Twig, înțelege HTML și știe că variabila se afișează ca valoare a unui atribut care nu este între ghilimele. De aceea, le completează. Când atacatorul introduce aceeași descriere, codul rezultat va arăta astfel: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/ru/custom-tags.texy b/latte/ru/custom-tags.texy index dc35fdad01..3c0cd691eb 100644 --- a/latte/ru/custom-tags.texy +++ b/latte/ru/custom-tags.texy @@ -923,7 +923,7 @@ class MyLatteExtension extends Extension Сгенерированный HTML: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Действительно хотите удалить элемент 123?")">Удалить</a> ``` diff --git a/latte/ru/safety-first.texy b/latte/ru/safety-first.texy index 96b91313cc..ffd79d93c4 100644 --- a/latte/ru/safety-first.texy +++ b/latte/ru/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>Результаты поиска для <em>' . $search . '</em></p>'; Злоумышленник может в поле поиска и, соответственно, в переменную `$search` записать любую строку, в том числе и HTML-код, например `<script>alert("Hacked!")</script>`. Поскольку вывод никак не обрабатывается, он станет частью отображаемой страницы: -```html +```latte <p>Результаты поиска для <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Злоумышленнику достаточно в качестве описания вставить хитро составленную строку `" onload="alert('Hacked!')`, и если вывод не будет обработан, результирующий код будет выглядеть так: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -91,7 +91,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Что именно подразумевается под словом контекст? Это место в документе со своими правилами обработки выводимых данных. Зависит от типа документа (HTML, XML, CSS, JavaScript, plain text, ...) и может отличаться в его конкретных частях. Например, в HTML-документе таких мест (контекстов), где действуют очень разные правила, целое множество. Возможно, вы удивитесь, сколько их. Вот первая четверка: -```html +```latte <p>#текст</p> <img src="#атрибут"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Контексты также могут вкладываться, что происходит, когда мы вставляем JavaScript или CSS в HTML. Это можно сделать двумя разными способами, элементом и атрибутом: -```html +```latte <script>#js-элемент</script> <img onclick="#js-атрибут"> @@ -132,7 +132,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Если вы будете выводить ее в HTML-тексте, то в данном случае не нужно делать никаких замен, потому что строка не содержит ни одного символа со специальным значением. Другая ситуация возникнет, если вы выведете ее внутри HTML-атрибута, заключенного в одинарные кавычки. В таком случае необходимо экранировать кавычки в HTML-сущности: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Если этот код вставить в HTML-документ с помощью `<script>`, ничего больше изменять не нужно, потому что в нем не встречается запрещенная последовательность `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Однако, если бы мы хотели вставить его в HTML-атрибут, нужно еще экранировать кавычки в HTML-сущности: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll А когда мы выводим эту строку в атрибуте, еще применяем экранирование в соответствии с этим контекстом и заменяем `&` на `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Latte видит шаблон так же, как и вы. Он понимает Злоумышленник в качестве описания изображения вставляет хитро составленную строку `foo onload=alert('Hacked!')`. Мы уже знаем, что Twig не может определить, выводится ли переменная в потоке HTML-текста, внутри атрибута, HTML-комментария и т. д., короче говоря, не различает контексты. И просто механически преобразует символы `< > & ' "` в HTML-сущности. Так что результирующий код будет выглядеть так: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Latte видит шаблон так же, как и вы. Он понимает Latte видит шаблон так же, как и вы. В отличие от Twig, он понимает HTML и знает, что переменная выводится как значение атрибута, который не заключен в кавычки. Поэтому он их добавит. Когда злоумышленник вставит то же описание, результирующий код будет выглядеть так: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/sl/custom-tags.texy b/latte/sl/custom-tags.texy index 1066b5eb86..1bb1570146 100644 --- a/latte/sl/custom-tags.texy +++ b/latte/sl/custom-tags.texy @@ -923,7 +923,7 @@ Zdaj lahko uporabite `n:confirm` na povezavah, gumbih ali elementih obrazca: Generirana HTML koda: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Ali res želite izbrisati element 123?")">Izbriši</a> ``` diff --git a/latte/sl/safety-first.texy b/latte/sl/safety-first.texy index 509802d6c8..29adc447e2 100644 --- a/latte/sl/safety-first.texy +++ b/latte/sl/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>Rezultati iskanja za <em>' . $search . '</em></p>'; Napadalec lahko v iskalno polje in posledično v spremenljivko `$search` zapiše poljuben niz, torej tudi HTML kodo kot `<script>alert("Hacked!")</script>`. Ker izpis ni nikakor obdelan, postane del prikazane strani: -```html +```latte <p>Rezultati iskanja za <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Napadalcu zadostuje, da kot opis vstavi spretno sestavljen niz `" onload="alert('Hacked!')` in če izpis ne bo obdelan, bo rezultatna koda izgledala takole: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -91,7 +91,7 @@ Kontekstno občutljivo ubežanje Kaj točno se misli z besedo kontekst? Gre za mesto v dokumentu z lastnimi pravili za obdelavo izpisanih podatkov. Odvisno je od vrste dokumenta (HTML, XML, CSS, JavaScript, navadno besedilo, ...) in se lahko razlikuje v njegovih konkretnih delih. Na primer, v dokumentu HTML je takih mest (kontekstov), kjer veljajo zelo različna pravila, cela vrsta. Morda boste presenečeni, koliko jih je. Tukaj imamo prvo četverico: -```html +```latte <p>#text</p> <img src="#atribut"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ Zanimivo je znotraj komentarjev HTML. Tukaj se namreč za ubežanje ne uporablja Konteksti se lahko tudi plastijo, do česar pride, ko vstavimo JavaScript ali CSS v HTML. To je mogoče storiti na dva različna načina, z elementom in atributom: -```html +```latte <script>#js-element</script> <img onclick="#js-atribut"> @@ -132,7 +132,7 @@ Imejmo niz `Rock'n'Roll`. Če ga boste izpisovali v besedilu HTML, ravno v tem primeru ni treba delati nobenih zamenjav, ker niz ne vsebuje nobenega znaka s posebnim pomenom. Druga situacija nastane, če ga izpišete znotraj atributa HTML, omejenega z enojnimi narekovaji. V takem primeru je treba narekovaje ubežati v HTML entitete: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Če to kodo vstavimo v dokument HTML s pomočjo `<script>`, ni treba ničesar dodatno urejati, ker se v njej ne pojavlja prepovedano zaporedje `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Če pa bi jo želeli vstaviti v atribut HTML, moramo še ubežati narekovaje v HTML entitete: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll In ko ta niz izpišemo v atributu, še uporabimo ubežanje po tem kontekstu in zamenjamo `&` za `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Opazite, da okoli vrednosti atributov ni narekovajev. Koder jih je lahko pozabil Napadalec kot opis slike vstavi spretno sestavljen niz `foo onload=alert('Hacked!')`. Že vemo, da Twig ne more prepoznati, ali se spremenljivka izpisuje v toku besedila HTML, znotraj atributa, komentarja HTML, itd., skratka ne razlikuje kontekstov. In samo mehansko pretvarja znake `< > & ' "` v HTML entitete. Torej bo rezultatna koda izgledala takole: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Zdaj si poglejmo, kako se z enako predlogo spopade Latte: Latte vidi predlogo enako kot vi. Za razliko od Twiga razume HTML in ve, da se spremenljivka izpisuje kot vrednost atributa, ki ni v narekovajih. Zato jih dopolni. Ko napadalec vstavi enak opis, bo rezultatna koda izgledala takole: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/latte/tr/custom-tags.texy b/latte/tr/custom-tags.texy index 402e361e28..8f582915f0 100644 --- a/latte/tr/custom-tags.texy +++ b/latte/tr/custom-tags.texy @@ -923,7 +923,7 @@ class MyLatteExtension extends Extension Oluşturulan HTML: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("123 öğesini gerçekten silmek istiyor musunuz?")">Sil</a> ``` diff --git a/latte/tr/safety-first.texy b/latte/tr/safety-first.texy index a4943315fd..9e550024e2 100644 --- a/latte/tr/safety-first.texy +++ b/latte/tr/safety-first.texy @@ -33,7 +33,7 @@ echo '<p><em>' . $search . '</em> için arama sonuçları</p>'; Saldırgan, arama kutusuna ve dolayısıyla `$search` değişkenine `<script>alert("Hacklendiniz!")</script>` gibi HTML kodu da dahil olmak üzere herhangi bir karakter dizisi yazabilir. Çıktı hiçbir şekilde işlenmediği için, görüntülenen sayfanın bir parçası haline gelir: -```html +```latte <p><em><script>alert("Hacklendiniz!")</script></em> için arama sonuçları</p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Saldırganın açıklama olarak akıllıca oluşturulmuş bir karakter dizisi `" onload="alert('Hacklendiniz!')` eklemesi yeterlidir ve yazdırma işlenmezse, sonuç kodu şöyle görünecektir: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacklendiniz!')"> ``` @@ -91,7 +91,7 @@ Bağlama Duyarlı Kaçış Bağlam kelimesiyle tam olarak ne kastediliyor? Belgenin içinde, yazdırılan verilerin işlenmesi için kendi kuralları olan bir yerdir. Belgenin türüne (HTML, XML, CSS, JavaScript, düz metin, ...) bağlıdır ve belirli bölümlerinde farklılık gösterebilir. Örneğin, bir HTML belgesinde, çok farklı kuralların geçerli olduğu birçok yer (bağlam) vardır. Kaç tane olduğuna şaşırabilirsiniz. İşte ilk dördü: -```html +```latte <p>#metin</p> <img src="#nitelik"> <textarea>#hammetin</textarea> @@ -108,7 +108,7 @@ HTML yorumlarının içi ilginçtir. Burada kaçış için HTML varlıkları kul Bağlamlar ayrıca katmanlanabilir, bu da HTML'e JavaScript veya CSS eklediğimizde olur. Bu iki farklı şekilde yapılabilir, öğe ve nitelik ile: -```html +```latte <script>#js-öğe</script> <img onclick="#js-nitelik"> @@ -132,7 +132,7 @@ Bir Örnek İster misiniz? Bunu HTML metninde yazdırırsanız, bu özel durumda herhangi bir değiştirme yapmaya gerek yoktur, çünkü dize özel anlamı olan hiçbir karakter içermez. Tek tırnak içine alınmış bir HTML niteliği içinde yazdırırsanız durum farklıdır. Bu durumda, tırnak işaretlerini HTML varlıklarına kaçış işlemine tabi tutmak gerekir: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Bu kodu `<script>` kullanarak HTML belgesine eklersek, başka bir şey ayarlamaya gerek yoktur, çünkü içinde yasaklı `</script` dizisi bulunmaz: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Ancak bunu bir HTML niteliğine eklemek istersek, tırnak işaretlerini de HTML varlıklarına kaçış işlemine tabi tutmamız gerekir: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll Ve bu dizeyi bir nitelikte yazdırdığımızda, bu bağlama göre kaçış işlemini de uygular ve `&` yerine `&` yazarız: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Nitelik değerlerinin etrafında tırnak işareti olmadığına dikkat edin. Kod Saldırgan, resim açıklaması olarak akıllıca oluşturulmuş bir karakter dizisi `foo onload=alert('Hacklendiniz!')` ekler. Twig'in değişkenin HTML metin akışında mı, bir nitelik içinde mi, HTML yorumunda mı vb. yazdırıldığını anlayamayacağını, kısacası bağlamları ayırt etmediğini zaten biliyoruz. Ve yalnızca `< > & ' "` karakterlerini mekanik olarak HTML varlıklarına dönüştürür. Yani sonuç kodu şöyle görünecektir: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacklendiniz!')> ``` @@ -330,7 +330,7 @@ Sahte `onload` niteliği sayfanın bir parçası haline geldi ve tarayıcı resm Latte şablonu sizin gördüğünüz gibi görür. Twig'in aksine, HTML'i anlar ve değişkenin tırnak içinde olmayan bir niteliğin değeri olarak yazdırıldığını bilir. Bu yüzden onları ekler. Saldırgan aynı açıklamayı eklediğinde, sonuç kodu şöyle görünecektir: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacklendiniz!')"> ``` diff --git a/latte/uk/custom-tags.texy b/latte/uk/custom-tags.texy index 96af747f17..c2e63f4ab1 100644 --- a/latte/uk/custom-tags.texy +++ b/latte/uk/custom-tags.texy @@ -923,7 +923,7 @@ class MyLatteExtension extends Extension Згенерований HTML: -```html +```latte <a href="delete.php?id=123" onclick="return confirm("Справді хочете видалити елемент 123?")">Видалити</a> ``` diff --git a/latte/uk/safety-first.texy b/latte/uk/safety-first.texy index 60ff086270..b5fd634f04 100644 --- a/latte/uk/safety-first.texy +++ b/latte/uk/safety-first.texy @@ -33,7 +33,7 @@ echo '<p>Результати пошуку для <em>' . $search . '</em></p>'; Зловмисник може в поле пошуку і, відповідно, в змінну `$search` записати будь-який рядок, тобто і HTML-код, як `<script>alert("Hacked!")</script>`. Оскільки вивід ніяк не обробляється, він стане частиною відображеної сторінки: -```html +```latte <p>Результати пошуку для <em><script>alert("Hacked!")</script></em></p> ``` @@ -59,7 +59,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Зловмиснику достатньо як опис вставити хитро складений рядок `" onload="alert('Hacked!')`, і якщо виведення не буде оброблено, кінцевий код виглядатиме так: -```html +```latte <img src="photo0145.webp" alt="" onload="alert('Hacked!')"> ``` @@ -91,7 +91,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Що саме мається на увазі під словом контекст? Це місце в документі з власними правилами обробки виведених даних. Воно залежить від типу документа (HTML, XML, CSS, JavaScript, plain text, ...) і може відрізнятися в його конкретних частинах. Наприклад, в HTML-документі є ціла низка таких місць (контекстів), де діють дуже різні правила. Можливо, ви будете здивовані, скільки їх є. Ось перша четвірка: -```html +```latte <p>#text</p> <img src="#атрибут"> <textarea>#rawtext</textarea> @@ -108,7 +108,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Контексти також можуть нашаровуватися, що відбувається, коли ми вставляємо JavaScript або CSS в HTML. Це можна зробити двома різними способами: елементом та атрибутом: -```html +```latte <script>#js-елемент</script> <img onclick="#js-атрибут"> @@ -132,7 +132,7 @@ echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">'; Якщо ви будете виводити його в HTML-тексті, саме в цьому випадку не потрібно робити жодних замін, оскільки рядок не містить жодного символу зі спеціальним значенням. Інша ситуація виникне, якщо ви виведете його всередині HTML-атрибута, взятого в одинарні лапки. У такому випадку потрібно екранувати лапки на HTML-сутності: -```html +```latte <div title='Rock'n'Roll'></div> ``` @@ -152,13 +152,13 @@ alert('Rock\'n\'Roll'); Якщо цей код вставити в HTML-документ за допомогою `<script>`, не потрібно нічого додатково змінювати, оскільки в ньому не зустрічається заборонена послідовність `</script`: -```html +```latte <script> alert('Rock\'n\'Roll'); </script> ``` Однак, якби ми хотіли вставити його в HTML-атрибут, ми повинні ще екранувати лапки на HTML-сутності: -```html +```latte <div onclick='alert('Rock\'n\'Roll')'></div> ``` @@ -170,7 +170,7 @@ https://example.org/?a=Jazz&b=Rock%27n%27Roll І коли ми виводимо цей рядок в атрибуті, ще застосовуємо екранування відповідно до цього контексту і замінюємо `&` на `&`: -```html +```latte <a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll"> ``` @@ -314,7 +314,7 @@ Latte бачить шаблон так само, як і ви. Розуміє HT Зловмисник як опис зображення вставляє хитро складений рядок `foo onload=alert('Hacked!')`. Ми вже знаємо, що Twig не може розпізнати, чи виводиться змінна в потоці HTML-тексту, всередині атрибута, HTML-коментаря тощо, коротше кажучи, не розрізняє контексти. І лише механічно перетворює символи `< > & ' "` на HTML-сутності. Отже, кінцевий код виглядатиме так: -```html +```latte <img src=photo0145.webp alt=foo onload=alert('Hacked!')> ``` @@ -330,7 +330,7 @@ Latte бачить шаблон так само, як і ви. Розуміє HT Latte бачить шаблон так само, як і ви. На відміну від Twig, він розуміє HTML і знає, що змінна виводиться як значення атрибута, який не взятий у лапки. Тому він їх доповнить. Коли зловмисник вставить той самий опис, кінцевий код виглядатиме так: -```html +```latte <img src="photo0145.webp" alt="foo onload=alert('Hacked!')"> ``` diff --git a/quickstart/cs/@home.texy b/quickstart/cs/@home.texy index 8ed549342e..c704dc7885 100644 --- a/quickstart/cs/@home.texy +++ b/quickstart/cs/@home.texy @@ -89,9 +89,8 @@ Tracy nám ohromně pomůže, až budeme hledat chyby v aplikaci. Také si všim V produkčním módu je Tracy samozřejmě vypnuta a nezobrazuje žádné citlivé informace. Všechny chyby jsou v tomto případě uloženy ve složce `log/`. Pojďme si to vyzkoušet. V souboru `app/Bootstrap.php` odkomentujeme následující řádek a změníme parametr volání na `false`, aby kód vypadal takto: ```php .{file:app/Bootstrap.php} -... +// ... $this->configurator->setDebugMode(false); -... ``` Po obnovení stránky již Tracy neuvidíme. Místo ní se zobrazí uživatelsky přívětivá zpráva: diff --git a/quickstart/cs/comments.texy b/quickstart/cs/comments.texy index a33c122d0c..949ec97bc4 100644 --- a/quickstart/cs/comments.texy +++ b/quickstart/cs/comments.texy @@ -136,7 +136,7 @@ Jak si jistě pamatujete, do šablony jsme předali proměnnou `$post` pomocí m ```php .{file:app/Presentation/Post/PostPresenter.php} public function renderShow(int $id): void { - ... + // ... $this->template->post = $post; $this->template->comments = $post->related('comments')->order('created_at'); } diff --git a/quickstart/en/@home.texy b/quickstart/en/@home.texy index 8fd028c07f..9abc77dc8b 100644 --- a/quickstart/en/@home.texy +++ b/quickstart/en/@home.texy @@ -89,9 +89,8 @@ Tracy will significantly help you when debugging errors. Also, notice the floati In production mode, Tracy is, of course, disabled and does not display any sensitive information. All errors are saved in the `log/` directory instead. Let's try it out. In the `app/Bootstrap.php` file, find the following piece of code, uncomment the line, and change the method call parameter to `false`, so it looks like this: ```php .{file:app/Bootstrap.php} -... +// ... $this->configurator->setDebugMode(false); -... ``` After refreshing the web page, you will no longer see the red screen. Instead, a user-friendly message will be displayed: diff --git a/quickstart/en/comments.texy b/quickstart/en/comments.texy index 9c30a5aacb..fa9ac7ff50 100644 --- a/quickstart/en/comments.texy +++ b/quickstart/en/comments.texy @@ -136,7 +136,7 @@ As you might recall, we passed the `$post` variable to the template in `PostPres ```php .{file:app/Presentation/Post/PostPresenter.php} public function renderShow(int $id): void { - ... + // ... $this->template->post = $post; $this->template->comments = $post->related('comments')->order('created_at'); } diff --git a/security/cs/authentication.texy b/security/cs/authentication.texy index bec4b9f528..689b757bd5 100644 --- a/security/cs/authentication.texy +++ b/security/cs/authentication.texy @@ -212,7 +212,7 @@ final class Authenticator implements { $row = $this->db->fetch('SELECT * FROM user WHERE username = ?', $username); // ověříme heslo - ... + // ... // vrátíme identitu se všemi údaji z databáze return new SimpleIdentity($row->id, null, (array) $row); } diff --git a/security/en/authentication.texy b/security/en/authentication.texy index d15187f3d2..4fbd946ba6 100644 --- a/security/en/authentication.texy +++ b/security/en/authentication.texy @@ -212,7 +212,7 @@ final class Authenticator implements { $row = $this->db->fetch('SELECT * FROM user WHERE username = ?', $username); // verify password - ... + // ... // return the identity with all data from the database return new SimpleIdentity($row->id, null, (array) $row); } diff --git a/tracy/cs/guide.texy b/tracy/cs/guide.texy index 23343cc56a..8ce469764f 100644 --- a/tracy/cs/guide.texy +++ b/tracy/cs/guide.texy @@ -161,7 +161,7 @@ try { } catch (Exception $e) { Debugger::log($e); // logovat lze i výjimku // nebo - Debugger::log($e, Debugger::ERROR); odešle i e-mailovou notifikaci + Debugger::log($e, Debugger::ERROR); // odešle i e-mailovou notifikaci } ``` diff --git a/tracy/cs/stopwatch.texy b/tracy/cs/stopwatch.texy index 8120595cfa..95b8cf24b4 100644 --- a/tracy/cs/stopwatch.texy +++ b/tracy/cs/stopwatch.texy @@ -29,7 +29,7 @@ $pageElapsed = Debugger::timer('page-generating'); ```php Debugger::timer(); // zapne stopky -... // časově náročná operace +// ... časově náročná operace echo Debugger::timer(); // vypíše uplynulý čas v sekundách ``` diff --git a/tracy/en/stopwatch.texy b/tracy/en/stopwatch.texy index 6546ac6e71..7608a61b03 100644 --- a/tracy/en/stopwatch.texy +++ b/tracy/en/stopwatch.texy @@ -29,7 +29,7 @@ $pageElapsed = Debugger::timer('page-generating'); ```php Debugger::timer(); // runs the timer -... // some time-consuming operation +// ... some time-consuming operation echo Debugger::timer(); // elapsed time in seconds ``` diff --git a/utils/cs/filesystem.texy b/utils/cs/filesystem.texy index 1f68e7313b..0b3e31e9ac 100644 --- a/utils/cs/filesystem.texy +++ b/utils/cs/filesystem.texy @@ -209,6 +209,6 @@ class AnyClassUsingFileSystem return $this->fileSystem->read(/* ... */); } - ... + // ... } ``` diff --git a/utils/en/filesystem.texy b/utils/en/filesystem.texy index 7c66d4c3e3..f8e43ddf3e 100644 --- a/utils/en/filesystem.texy +++ b/utils/en/filesystem.texy @@ -209,6 +209,6 @@ class AnyClassUsingFileSystem return $this->fileSystem->read(/* ... */); } - ... + // ... } ``` From 028623a66879b3b589431a3a8d474dd65a98c2a3 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Mon, 13 Apr 2026 16:44:40 +0200 Subject: [PATCH 18/25] latte: changed user template --- latte/cs/custom-filters.texy | 2 +- latte/cs/custom-functions.texy | 2 +- latte/cs/custom-tags.texy | 20 ++++++++++---------- latte/en/custom-filters.texy | 2 +- latte/en/custom-functions.texy | 2 +- latte/en/custom-tags.texy | 20 ++++++++++---------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/latte/cs/custom-filters.texy b/latte/cs/custom-filters.texy index 75aff4b35e..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; diff --git a/latte/cs/custom-functions.texy b/latte/cs/custom-functions.texy index bb3677923d..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; diff --git a/latte/cs/custom-tags.texy b/latte/cs/custom-tags.texy index 415ed73fdd..d5ad47f22e 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 <?php -namespace App\Latte; +namespace App\Templating; use Latte\Compiler\Nodes\StatementNode; use Latte\Compiler\PrintContext; @@ -173,7 +173,7 @@ Nakonec informujme Latte o novém tagu. Vytvořte [třídu rozšíření |extend ```php <?php -namespace App\Latte; +namespace App\Templating; use Latte\Extension; @@ -197,7 +197,7 @@ Poté zaregistrujte toto rozšíření v Latte Engine: ```php $latte = new Latte\Engine; -$latte->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 <?php -namespace App\Latte; +namespace App\Templating; use Latte\Compiler\Nodes\Php\ExpressionNode; use Latte\Compiler\Nodes\Php\Scalar\StringNode; @@ -383,7 +383,7 @@ Vytvořme třídu `DebugNode` a její metodu `create` využívající `yield`. ```php <?php -namespace App\Latte; +namespace App\Templating; use Latte\Compiler\Nodes\AreaNode; use Latte\Compiler\Nodes\StatementNode; @@ -555,7 +555,7 @@ Upravme `DebugNode::create()` tak, aby očekával `{else}`: ```php <?php -namespace App\Latte; +namespace App\Templating; use Latte\Compiler\Nodes\AreaNode; use Latte\Compiler\Nodes\NopNode; @@ -715,7 +715,7 @@ Pojďme vytvořit třídu uzlu. ```php <?php -namespace App\Latte; +namespace App\Templating; use Latte\CompileException; use Latte\Compiler\Nodes\AreaNode; @@ -797,7 +797,7 @@ Registrace a použití Zaregistrujte tag ve vašem rozšíření: ```php -use App\Latte\RepeatNode; +use App\Templating\RepeatNode; class MyLatteExtension extends Extension { @@ -850,7 +850,7 @@ Potřebujeme třídu Node a parsovací funkci. ```php <?php -namespace App\Latte; +namespace App\Templating; use Latte\Compiler\Nodes\StatementNode; use Latte\Compiler\PrintContext; @@ -1023,7 +1023,7 @@ Zatímco `parseExpression()`, `parseArguments()`, atd., pokrývají mnoho příp ```php <?php -namespace App\Latte; +namespace App\Templating; class YoutubeNode extends StatementNode { diff --git a/latte/en/custom-filters.texy b/latte/en/custom-filters.texy index 186caf6fb4..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; diff --git a/latte/en/custom-functions.texy b/latte/en/custom-functions.texy index f4616ceea1..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; diff --git a/latte/en/custom-tags.texy b/latte/en/custom-tags.texy index 6fcc3cd9aa..8d8a7cf7c6 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 <?php -namespace App\Latte; +namespace App\Templating; use Latte\Compiler\Nodes\StatementNode; use Latte\Compiler\PrintContext; @@ -173,7 +173,7 @@ Finally, tell Latte about the new tag. Create an [Extension class |extending-lat ```php <?php -namespace App\Latte; +namespace App\Templating; use Latte\Extension; @@ -197,7 +197,7 @@ Then, register this extension with the Latte Engine: ```php $latte = new Latte\Engine; -$latte->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 <?php -namespace App\Latte; +namespace App\Templating; use Latte\Compiler\Nodes\Php\ExpressionNode; use Latte\Compiler\Nodes\Php\Scalar\StringNode; @@ -383,7 +383,7 @@ Let's create the `DebugNode` class and its `create` method using `yield`. ```php <?php -namespace App\Latte; +namespace App\Templating; use Latte\Compiler\Nodes\AreaNode; use Latte\Compiler\Nodes\StatementNode; @@ -555,7 +555,7 @@ Let's modify `DebugNode::create()` to expect `{else}`: ```php <?php -namespace App\Latte; +namespace App\Templating; use Latte\Compiler\Nodes\AreaNode; use Latte\Compiler\Nodes\NopNode; @@ -715,7 +715,7 @@ Let's create the node class. ```php <?php -namespace App\Latte; +namespace App\Templating; use Latte\CompileException; use Latte\Compiler\Nodes\AreaNode; @@ -797,7 +797,7 @@ Registration and Usage Register the tag in your extension: ```php -use App\Latte\RepeatNode; +use App\Templating\RepeatNode; class MyLatteExtension extends Extension { @@ -850,7 +850,7 @@ We need a Node class and a parsing function. ```php <?php -namespace App\Latte; +namespace App\Templating; use Latte\Compiler\Nodes\StatementNode; use Latte\Compiler\PrintContext; @@ -1023,7 +1023,7 @@ While `parseExpression()`, `parseArguments()`, etc., cover many cases, sometimes ```php <?php -namespace App\Latte; +namespace App\Templating; class YoutubeNode extends StatementNode { From 6eb116aad15fc75d4fee507353b7f5df65ba62e1 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Mon, 23 Feb 2026 02:46:32 +0100 Subject: [PATCH 19/25] php-generator: v4.2.2 --- php-generator/cs/@home.texy | 16 ++++++++++++---- php-generator/en/@home.texy | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/php-generator/cs/@home.texy b/php-generator/cs/@home.texy index 8100978308..77e0c5083b 100644 --- a/php-generator/cs/@home.texy +++ b/php-generator/cs/@home.texy @@ -512,6 +512,8 @@ class MyPrinter extends Nette\PhpGenerator\Printer public bool $singleParameterOnOneLine = false; // omits namespaces that do not contain any class or function public bool $omitEmptyNamespaces = true; + // umístí declare(strict_types) na stejný řádek jako <?php + public bool $declareOnOpenTag = false; // oddělovač mezi pravou závorkou a návratovým typem funkcí a metod public string $returnTypeColon = ': '; } @@ -727,7 +729,7 @@ $class = $namespace->addClass('Task'); $interface = $namespace->addInterface('Countable'); $trait = $namespace->addTrait('NameAware'); -// nebo vložíme existující třídu do namespace +// nebo vložíme existující třídu nebo funkci do namespace $class = new Nette\PhpGenerator\ClassType('Task'); $namespace->add($class); ``` @@ -840,14 +842,12 @@ echo $file; Výsledek: ```php -<?php +<?php declare(strict_types=1); /** * This file is auto-generated. */ -declare(strict_types=1); - namespace Foo; class A @@ -859,6 +859,14 @@ function foo() } ``` +Do souboru lze vkládat i existující objekty tříd, funkcí a jmenných prostorů pomocí metody `add()`: + +```php +$file = new Nette\PhpGenerator\PhpFile; +$class = new Nette\PhpGenerator\ClassType('Demo'); +$file->add($class); +``` + **Upozornění:** Do souborů není možné přidávat žádný další kód mimo funkce a třídy. diff --git a/php-generator/en/@home.texy b/php-generator/en/@home.texy index 436f8d3fa7..219c18eafe 100644 --- a/php-generator/en/@home.texy +++ b/php-generator/en/@home.texy @@ -512,6 +512,8 @@ class MyPrinter extends Nette\PhpGenerator\Printer public bool $singleParameterOnOneLine = false; // omits namespaces that do not contain any class or function public bool $omitEmptyNamespaces = true; + // places declare(strict_types) on the same line as <?php + public bool $declareOnOpenTag = false; // separator between the right parenthesis and the return type of functions and methods public string $returnTypeColon = ': '; } @@ -727,7 +729,7 @@ $class = $namespace->addClass('Task'); $interface = $namespace->addInterface('Countable'); $trait = $namespace->addTrait('NameAware'); -// or insert an existing class into the namespace +// or insert an existing class or function into the namespace $class = new Nette\PhpGenerator\ClassType('Task'); $namespace->add($class); ``` @@ -840,14 +842,12 @@ echo $file; Result: ```php -<?php +<?php declare(strict_types=1); /** * This file is auto-generated. */ -declare(strict_types=1); - namespace Foo; class A @@ -859,6 +859,14 @@ function foo() } ``` +You can also insert existing class, function, and namespace objects into the file using the `add()` method: + +```php +$file = new Nette\PhpGenerator\PhpFile; +$class = new Nette\PhpGenerator\ClassType('Demo'); +$file->add($class); +``` + **Please note:** No additional code (like `echo 'hello'`) can be added to the files outside of functions, classes, or namespaces. From 3ffbfe1926e2d92b4167c2773774e67a9a09ff56 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Thu, 5 Mar 2026 15:50:14 +0100 Subject: [PATCH 20/25] di: documented @Type as service key for modifications --- dependency-injection/cs/services.texy | 10 ++++++++-- dependency-injection/en/services.texy | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/dependency-injection/cs/services.texy b/dependency-injection/cs/services.texy index 8a86b230e9..6f4ec1bacb 100644 --- a/dependency-injection/cs/services.texy +++ b/dependency-injection/cs/services.texy @@ -433,8 +433,14 @@ services: application.application: create: MyApplication alteration: true - setup: - - '$onStartup[]' = [@resource, init] +``` + +Službu nemusíte identifikovat interním názvem, můžete na ni odkázat i jejím typem. Předchozí příklad tak lze zapsat i takto: + +```neon +services: + @Nette\Application\Application: + create: MyApplication ``` Při přepisování služby můžeme chtít odstranit původní argumenty, položky setup nebo tagy, k čemuž slouží `reset`: diff --git a/dependency-injection/en/services.texy b/dependency-injection/en/services.texy index d724a229cf..8da30d74ed 100644 --- a/dependency-injection/en/services.texy +++ b/dependency-injection/en/services.texy @@ -433,8 +433,14 @@ services: application.application: create: MyApplication alteration: true - setup: - - '$onStartup[]' = [@resource, init] +``` + +You don't have to identify a service by its internal name — you can refer to it by type instead. The previous example can also be written as: + +```neon +services: + @Nette\Application\Application: + create: MyApplication ``` When modifying a service, we might want to remove original arguments, setup items, or tags, using the `reset` key: From 6953c86ac77953e8d1b71e6f8822a53833aa6984 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Fri, 27 Mar 2026 02:22:12 +0100 Subject: [PATCH 21/25] nette/mail 4.1.0 --- mail/cs/@home.texy | 72 ++++++++++++++++++++++++++++++++++++++++++++++ mail/en/@home.texy | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/mail/cs/@home.texy b/mail/cs/@home.texy index c2db2cff9a..fb72bf092d 100644 --- a/mail/cs/@home.texy +++ b/mail/cs/@home.texy @@ -176,6 +176,78 @@ V šabloně potom vytváříme odkazy tak, jak jsme zvyklí. Všechny odkazy vyt ``` +Inlinování CSS +============== + +[api:Nette\Mail\CssInliner] převádí CSS pravidla na inline atributy `style`, aby se e-maily zobrazovaly správně ve všech klientech. Zároveň generuje HTML atributy pro kompatibilitu s Outlookem. + +.[note] +Vyžaduje PHP 8.4 nebo novější a rozšíření `dom`. + +Většina e-mailových klientů má omezenou podporu značky `<style>` nebo ji zcela ignoruje. Pro správné zobrazení je proto potřeba převést CSS pravidla na inline atributy `style` u jednotlivých elementů. Stačí HTML předat metodě `inline()`: + +```php +$inliner = new Nette\Mail\CssInliner; +$html = $inliner->inline($html); +``` + +Pokud HTML obsahuje například: + +```latte +<style> +p { margin: 0; color: #333; } +a { color: #a0704e; } +</style> +<p>Hello <a href="#">world</a></p> +``` + +Výsledek po inlinování bude (značka `<style>` se zachová, zde ji vynecháváme pro stručnost): + +```latte +<p style="margin: 0; color: #333">Hello <a href="#" style="color: #a0704e">world</a></p> +``` + +Značka `<style>` zůstane ve výstupu vždy zachována, takže `@media` dotazy a další pravidla, která nelze inlinovat, budou nadále fungovat. + +Kromě extrakce stylů ze značek `<style>` lze CSS dodat i metodou `addCss()`. Inlinování je třeba provést před předáním HTML do `setHtmlBody()`: + +```php +$latte = new Latte\Engine; +$params = [ + 'orderId' => 123, +]; + +$html = $latte->renderToString('/path/to/email.latte', $params); +$html = (new Nette\Mail\CssInliner) + ->addCss(file_get_contents('/path/to/email.css')) + ->inline($html); + +$mail = new Nette\Mail\Message; +$mail->setHtmlBody($html); +``` + +Pokud je stejná CSS vlastnost nastavena z více zdrojů, pozdější přepisují dřívější. Pravidla ze značek `<style>` mají nejnižší prioritu, následují pravidla přidaná přes `addCss()`. Pokud element již má inline atribut `style` v HTML, ten má vždy nejvyšší prioritu. + +At-rules jako `@media` nebo `@font-face` se při inlinování přeskakují. Pseudo-třídy jako `:hover` nelze smysluplně inlinovat, protože inline styly nepodporují dynamické stavy. + + +HTML atributy pro Outlook +------------------------- + +Desktopové verze Microsoft Outlooku používají vykreslovací jádro Wordu, které nerozumí mnoha CSS vlastnostem. Pro zajištění kompatibility `CssInliner` automaticky generuje odpovídající HTML atributy z CSS pravidel vedle inline stylů: + +| CSS vlastnost | HTML atribut | Aplikuje se na +|----------------------------------------------------- +| `background-color` | `bgcolor` | `<table>`, `<td>`, `<th>`, `<body>`, `<tr>` +| `width` | `width` | `<table>`, `<td>`, `<th>`, `<img>` +| `height` | `height` | `<table>`, `<td>`, `<th>`, `<img>` +| `border-spacing` | `cellspacing` | `<table>` + +U `width`, `height` a `cellspacing` se automaticky odstraní jednotka `px` (např. `width: 600px` se převede na `width="600"`). Inline styl i HTML atribut se nastaví vždy společně, takže se e-mail zobrazí správně v moderních klientech i v Outlooku. + +HTML atributy se generují pouze z CSS pravidel zpracovaných třídou `CssInliner`, nikoliv z atributů `style` již přítomných v původním HTML. + + Odeslání e-mailu ================ diff --git a/mail/en/@home.texy b/mail/en/@home.texy index 97ed765b81..f867677234 100644 --- a/mail/en/@home.texy +++ b/mail/en/@home.texy @@ -176,6 +176,78 @@ In the template, you then create links as you are used to. All links created via ``` +CSS Inlining +============ + +[api:Nette\Mail\CssInliner] converts CSS rules into inline `style` attributes so that emails render consistently across all clients. It also generates HTML attributes for Outlook compatibility. + +.[note] +Requires PHP 8.4 or later and the `dom` extension. + +Most email clients have limited support for `<style>` tags or ignore them entirely. To ensure correct rendering, CSS rules need to be converted to inline `style` attributes on individual elements. Simply pass your HTML through `inline()`: + +```php +$inliner = new Nette\Mail\CssInliner; +$html = $inliner->inline($html); +``` + +For example, if the HTML contains: + +```latte +<style> +p { margin: 0; color: #333; } +a { color: #a0704e; } +</style> +<p>Hello <a href="#">world</a></p> +``` + +The result after inlining will be (the `<style>` tag is preserved but omitted here for brevity): + +```latte +<p style="margin: 0; color: #333">Hello <a href="#" style="color: #a0704e">world</a></p> +``` + +The `<style>` tag is always preserved in the output, so `@media` queries and other rules that cannot be inlined keep working. + +In addition to extracting styles from `<style>` tags, you can also provide CSS via the `addCss()` method. You need to inline CSS before passing the HTML to `setHtmlBody()`: + +```php +$latte = new Latte\Engine; +$params = [ + 'orderId' => 123, +]; + +$html = $latte->renderToString('/path/to/email.latte', $params); +$html = (new Nette\Mail\CssInliner) + ->addCss(file_get_contents('/path/to/email.css')) + ->inline($html); + +$mail = new Nette\Mail\Message; +$mail->setHtmlBody($html); +``` + +When the same CSS property is set by multiple sources, later sources override earlier ones. Rules from `<style>` tags have the lowest priority, followed by rules added via `addCss()`. If an element already has an inline `style` attribute in the HTML, it always takes the highest precedence. + +At-rules like `@media` or `@font-face` are skipped during inlining. Note that pseudo-classes like `:hover` cannot be meaningfully inlined, since inline styles do not support dynamic states. + + +HTML Attributes for Outlook +--------------------------- + +Desktop versions of Microsoft Outlook use the Word rendering engine, which doesn't understand many CSS properties. To ensure compatibility, `CssInliner` automatically generates corresponding HTML attributes from CSS rules alongside inline styles: + +| CSS Property | HTML Attribute | Applied To +|----------------------------------------------------- +| `background-color` | `bgcolor` | `<table>`, `<td>`, `<th>`, `<body>`, `<tr>` +| `width` | `width` | `<table>`, `<td>`, `<th>`, `<img>` +| `height` | `height` | `<table>`, `<td>`, `<th>`, `<img>` +| `border-spacing` | `cellspacing` | `<table>` + +For `width`, `height`, and `cellspacing`, the `px` unit is automatically stripped (e.g., `width: 600px` becomes `width="600"`). Both the inline style and the HTML attribute are always set, so the email renders correctly in modern clients and Outlook alike. + +HTML attributes are generated only from CSS rules processed by `CssInliner`, not from `style` attributes already present in the original HTML. + + Sending Emails ============== From 94c726c5383a4ee64498541c00889cabf7e9eff6 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Sun, 12 Apr 2026 20:54:51 +0200 Subject: [PATCH 22/25] updated links to PhpStorm plugins --- best-practices/cs/editors-and-tools.texy | 6 +++--- best-practices/en/editors-and-tools.texy | 6 +++--- latte/cs/recipes.texy | 2 +- latte/en/recipes.texy | 2 +- neon/cs/format.texy | 2 +- neon/en/format.texy | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/best-practices/cs/editors-and-tools.texy b/best-practices/cs/editors-and-tools.texy index 13e3d10138..efbbd772ba 100644 --- a/best-practices/cs/editors-and-tools.texy +++ b/best-practices/cs/editors-and-tools.texy @@ -13,9 +13,9 @@ Rozhodně doporučujeme pro vývoj používat plnohodnotné IDE, jako je třeba **NetBeans IDE** má podporu pro Nette, Latte a NEON už vestavěnou. **PhpStorm**: nainstalujte si tyto pluginy v `Settings > Plugins > Marketplace` -- Nette framework helpers -- Latte -- NEON support +- [Nette |https://plugins.jetbrains.com/plugin/28342-nette] +- [Latte |https://plugins.jetbrains.com/plugin/24218-latte-support] nebo [Latte Pro |https://plugins.jetbrains.com/plugin/19661-latte-pro] +- [NEON |https://plugins.jetbrains.com/plugin/28338-neon] nebo [NEON / Nette support |https://plugins.jetbrains.com/plugin/18387-neon-nette-support] - Nette Tester **VS Code**: najděte v marketplace "Nette Latte + Neon" plugin. diff --git a/best-practices/en/editors-and-tools.texy b/best-practices/en/editors-and-tools.texy index c6109dcbab..e81b575eb6 100644 --- a/best-practices/en/editors-and-tools.texy +++ b/best-practices/en/editors-and-tools.texy @@ -13,9 +13,9 @@ We strongly recommend using a full-featured IDE for development, like PhpStorm, **NetBeans IDE** has built-in support for Nette, Latte, and NEON. **PhpStorm**: Install these plugins via `Settings > Plugins > Marketplace`: -- Nette framework helpers -- Latte -- NEON support +- [Nette |https://plugins.jetbrains.com/plugin/28342-nette] +- [Latte |https://plugins.jetbrains.com/plugin/24218-latte-support] or [Latte Pro |https://plugins.jetbrains.com/plugin/19661-latte-pro] +- [NEON |https://plugins.jetbrains.com/plugin/28338-neon] or [NEON / Nette support |https://plugins.jetbrains.com/plugin/18387-neon-nette-support] - Nette Tester **VS Code**: Find the "Nette Latte + Neon" plugin in the marketplace. 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/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/neon/cs/format.texy b/neon/cs/format.texy index 1f7f06bfcf..f95e597fd9 100644 --- a/neon/cs/format.texy +++ b/neon/cs/format.texy @@ -13,7 +13,7 @@ Integrace ========= - NetBeans (má vestavěnou podporu) -- PhpStorm ([plugin |https://plugins.jetbrains.com/plugin/7060?pr]) +- PhpStorm ([plugin |https://plugins.jetbrains.com/plugin/28338-neon]) - Visual Studio Code ([Nette Latte + Neon |https://marketplace.visualstudio.com/items?itemName=Kasik96.latte]) nebo [Nette for VS Code |https://marketplace.visualstudio.com/items?itemName=franken-ui.nette-for-vscode]) - Sublime Text 3 ([plugin |https://github.com/FilipStryk/Nette-Latte-Neon-for-Sublime-Text-3]) - Sublime Text 2 ([plugin |https://github.com/Michal-Mikolas/Nette-package-for-Sublime-Text-2]) diff --git a/neon/en/format.texy b/neon/en/format.texy index 5f6a51d2c8..4958ce227a 100644 --- a/neon/en/format.texy +++ b/neon/en/format.texy @@ -13,7 +13,7 @@ Integration =========== - NetBeans (has built-in support) -- PhpStorm ([plugin |https://plugins.jetbrains.com/plugin/7060?pr]) +- PhpStorm ([plugin |https://plugins.jetbrains.com/plugin/28338-neon]) - Visual Studio Code ([Nette Latte + Neon |https://marketplace.visualstudio.com/items?itemName=Kasik96.latte] or [Nette for VS Code |https://marketplace.visualstudio.com/items?itemName=franken-ui.nette-for-vscode]) - Sublime Text 3 ([plugin |https://github.com/FilipStryk/Nette-Latte-Neon-for-Sublime-Text-3]) - Sublime Text 2 ([plugin |https://github.com/Michal-Mikolas/Nette-package-for-Sublime-Text-2]) From e9ba74fb7a4d27fbe0bd8d808de8a1706ce68286 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Sun, 12 Apr 2026 23:43:14 +0200 Subject: [PATCH 23/25] tracy: open-files-in-ide updated Chrome policy to AutoLaunchProtocolsFromOrigins --- tracy/cs/open-files-in-ide.texy | 6 +++--- tracy/en/open-files-in-ide.texy | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tracy/cs/open-files-in-ide.texy b/tracy/cs/open-files-in-ide.texy index 37ce131750..d3b8e22605 100644 --- a/tracy/cs/open-files-in-ide.texy +++ b/tracy/cs/open-files-in-ide.texy @@ -135,10 +135,10 @@ V Google Chrome od verze 77 již neuvidíte zatržítko „Tento typ odkazů vž ``` Windows Registry Editor Version 5.00 -[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\URLWhitelist] -"123"="editor://*" +[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome] +"AutoLaunchProtocolsFromOrigins"="[{\"allowed_origins\": [\"*\"],\"protocol\": \"editor\"}]" ``` -Importujte jej dvojitým kliknutím a restartujte prohlížeč Chrome. +Importujte jej dvojitým kliknutím a restartujte prohlížeč Chrome. Politika `AutoLaunchProtocolsFromOrigins` úplně potlačí potvrzovací dialog prohlížeče. Skript `install.cmd` toto nastavení provádí automaticky, a to včetně zakomentovaného řádku pro Vivaldi (stejná politika ve větvi `HKLM\SOFTWARE\Policies\Vivaldi`). S případnými dotazy nebo připomínkami se prosím obraťte na [fórum |https://forum.nette.org]. diff --git a/tracy/en/open-files-in-ide.texy b/tracy/en/open-files-in-ide.texy index 2c8d9112ba..53c2b863c7 100644 --- a/tracy/en/open-files-in-ide.texy +++ b/tracy/en/open-files-in-ide.texy @@ -135,10 +135,10 @@ Starting from Google Chrome version 77 you will no longer see the checkbox “Al ``` Windows Registry Editor Version 5.00 -[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\URLWhitelist] -"123"="editor://*" +[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome] +"AutoLaunchProtocolsFromOrigins"="[{\"allowed_origins\": [\"*\"],\"protocol\": \"editor\"}]" ``` -Import it by double clicking and restart Chrome. +Import it by double clicking and restart Chrome. The `AutoLaunchProtocolsFromOrigins` policy fully suppresses the browser confirmation dialog. The `install.cmd` script sets this automatically, including a commented-out line for Vivaldi (same policy under `HKLM\SOFTWARE\Policies\Vivaldi`). For further questions or suggestions, please visit the [forum |https://forum.nette.org]. From 35f5b9c2afc946a1ed723f8cc96d254cfdbbe677 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Mon, 6 Apr 2026 23:02:16 +0200 Subject: [PATCH 24/25] coding standard: added enums.php convention --- contributing/cs/coding-standard.texy | 2 +- contributing/en/coding-standard.texy | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contributing/cs/coding-standard.texy b/contributing/cs/coding-standard.texy index aaef617655..5bac29274f 100644 --- a/contributing/cs/coding-standard.texy +++ b/contributing/cs/coding-standard.texy @@ -14,7 +14,7 @@ Obecná pravidla - Dva prázdné řádky se používají k oddělení metod pro lepší čitelnost. - Důvod použití shut-up operátoru musí být zdokumentován: `@mkdir($dir); // @ - adresář může existovat`. - Pokud je použit slabě typizovaný operátor porovnání (tj. `==`, `!=`, ...), musí být zdokumentován záměr: `// == přijmout null` -- Do jednoho souboru `exceptions.php` můžete zapsat více výjimek. +- Do jednoho souboru `exceptions.php` můžete zapsat více výjimek, do souboru `enums.php` více enumů. - U rozhraní se nespecifikuje viditelnost metod, protože jsou vždy veřejné. - Každá property, návratová hodnota a parametr musí mít uvedený typ. Naopak u finálních konstant typ nikdy neuvádíme, protože je zjevný. - K ohraničení řetězce by se měly používat jednoduché uvozovky, s výjimkou případů, kdy samotný literál obsahuje apostrofy. diff --git a/contributing/en/coding-standard.texy b/contributing/en/coding-standard.texy index df9a0a86cb..2883d9634d 100644 --- a/contributing/en/coding-standard.texy +++ b/contributing/en/coding-standard.texy @@ -14,7 +14,7 @@ General Rules - Two empty lines are used to separate methods for better readability - The reason for using the shut-up operator (`@`) must be documented: `@mkdir($dir); // @ - directory may exist` - If a weak typed comparison operator is used (i.e., `==`, `!=`, ...), the intention must be documented: `// == to accept null` -- You can write multiple exception classes into a single file named `exceptions.php` +- You can write multiple exception classes into a single file named `exceptions.php`, and multiple enums into `enums.php` - The visibility of methods is not specified for interfaces because they are always public - Each property, return value, and parameter must have a type specified. Conversely, for final constants, we never specify the type because it is obvious - Single quotes should be used to delimit strings, except when the literal itself contains apostrophes From 98f8ba1827ef1488dd18c648bc08d297f72cf037 Mon Sep 17 00:00:00 2001 From: CodeWithPetr <petr.ondracek90@gmail.com> Date: Sat, 18 Apr 2026 17:29:46 +0700 Subject: [PATCH 25/25] Update @home.texy Change wrong parameter projectId for remoteIp --- bootstrap/en/@home.texy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/en/@home.texy b/bootstrap/en/@home.texy index 0e80ab16c2..202b427f3e 100644 --- a/bootstrap/en/@home.texy +++ b/bootstrap/en/@home.texy @@ -93,4 +93,4 @@ $configurator->addDynamicParameters([ ]); ``` -The `projectId` parameter can be referenced in the configuration using the `%projectId%` notation. +The `remoteIp` parameter can be referenced in the configuration using the `%remoteIp%` notation.