| assets | by dg | DIExtension: mapper can be referenced as @service in mapping A bare service reference like `cloud: @s3mapper` in the assets mapping now resolves to that service used directly as the mapper (previously it was mistaken for a path and failed). Entity notation `@s3mapper()` and class names keep working. Matches the documented 'Service reference' format. | | | |
| latte | by dg | added |json attribute modifier |json is an attribute-only modifier: as the last modifier of a dynamic HTML attribute it JSON-encodes the value via HtmlHelpers::formatJsonAttribute() with smart-quoting, overriding the default class/aria/data/bool handling. Anywhere else (outside an attribute, or not as the last modifier) |json is an unknown filter. formatJsonAttribute() is extracted from formatDataAttribute() and shared. | | | |
| latte | by dg | ExpressionAttributeNode: no longer mutates the modifier | | | |
| latte | by dg | Linter: added static checking of template paths New TemplateReferenceCheck validates that statically named templates referenced by {include}, {import}, {extends}/{layout}, {embed file}, {sandbox} and {include block from} exist, via the engine's loader. A loader that cannot resolve references (a bare StringLoader) is silently skipped. Registered as a built-in check. | | | |
| latte | by dg | Linter: rewritten as first-class checks driven by the Linter A lint check is now a Latte\Linting\Check that inspects the parsed template and returns Issue objects, run and owned by the Linter instead of riding the compiler pass pipeline via trigger_error. The engine only compiles; the Linter parses each file, runs the checks (each isolated in try/catch so a faulty check cannot abort the file or mask the others) and reports the collected issues. The symbol validation becomes SymbolCheck; the deprecated LinterExtension is now an inert no-op. Third parties add checks via addCheck(). | | | |
| assets | by dg | AssetNode: render full HTML element only in HTML text context {asset} and {preload} used to render the complete HTML element in every escaper state except html/attr, so inside <style> or <script> contents (html/raw) and in css/js/text templates they injected a whole <link>/<script> element into CSS or JS code, breaking it: <style> .hero { background: url('{asset 'bg.jpg'}'); } /* url('<link rel="preload" href=...>') */ </style> Now the element is rendered only in the html text state and everywhere else just the URL is printed, matching the existing behavior inside HTML attributes. | | | |
| latte | by dg | Linter: moved into Latte\Linting namespace Latte\Tools\Linter and Latte\Tools\LinterExtension are relocated to the Latte\Linting namespace under src/Latte/Linting. The original class names are kept as deprecated subclasses in a single src/compatibility.php for backward compatibility, so existing scripts keep working. No behavior change. | | | |
| latte | by dg | spaceless: rewritten as streaming WhitespaceMinifier The new minifier understands HTML structure and works in three modes driven by the content type: - HTML: whitespace around whitespace-insensitive elements (div, p, li, br, ...) is removed entirely on both sides, other runs collapse to a single space; whitespace between attributes collapses while quoted attribute values stay verbatim; content of pre, textarea, script and style passes through untouched; comments, doctype, CDATA and PI pass through verbatim - XML: all tags are treated as whitespace-insensitive - text (and js/css/ical): spaces and tabs collapse to a single space, newlines are preserved including their count; */attr subcontexts collapse everything to a single space It runs as an ob_start() handler processing 4kB chunks: only an undecidable tail (split tag, comment or closing raw-text tag) is held back, pending whitespace is tracked as a flag, so output is streamed and byte-identical for any chunking. {spaceless} now uses a process-wide singleton with a nesting counter; nested blocks are ignored (the outer handler minifies everything), which also fixes the state collision of the former static $strip. Using {spaceless} inside raw-text elements or attributes is a compile error; <script n:spaceless> keeps its content verbatim. BC breaks: - Filters::strip(), spacelessHtml(), spacelessHtmlHandler() and spacelessText() have been removed; the |spaceless filter is handled by Filters::spaceless(), |strip remains an obsolete alias - quoted attribute values are no longer collapsed - word separation around inline tags is preserved (`</i> bb` is no longer glued) - text mode preserves newlines and normalizes \r\n to \n - leading/trailing whitespace of the region is trimmed - compiled template cache must be purged after upgrade | | | |
| latte | by dg | added TagParser::consumeCommaBeforeArguments() | | | |
| latte | by daun | implicit default block for {embed} (#419) | | | |
| latte | by dg | cs | | | |
| forms | by dg | Form: submission detection redesigned around SubmissionSource objects A form no longer inherits its way of receiving submitted data, it is given a source: - interface SubmissionSource { receiveData(Form): ?array; prepare(Form) } - Sources\HttpSource - reads the form's own HTTP request: method match, Sec-Fetch origin policy (?FetchSite $allowedOrigin, null disables the check), POST+FILES/GET merge and the '_form_' tracker of a named form, which it installs itself in the prepare() render hook and validates against the form name at receive time - Sources\NullSource - never submitted; used by Repeater item templates and Blueprint dummy forms instead of the former anonymous-class trick, so introspective code gets false from isSubmitted() instead of hacks Form gains setSubmissionSource() and a protected createDefaultSource() seam: the default HttpSource materializes lazily at the first submission query or render, so a bare `new Form` keeps its behavior. Descendants anchored elsewhere (Nette\Application\UI\Form) return null from createDefaultSource() and set their source later; the write-once setter runs a catch-up loadHttpData() pass so that controls attached before the source load their values. The presenter signal field of UI\Form is the same kind of marker as the tracker and moves to the prepare() hook of its source on the application side. Behavior notes: the tracker is installed at render time, so it renders after user fields and $form[TrackerId] exists only after fireRenderEvents(); Form::$httpRequest was replaced by injecting `new HttpSource($request)`; the cross-origin flag lives in the source, allowCrossOrigin() delegates to it. | | | |
| forms | by dg | Form: initialize() removed, the HTTP request resolves lazily per instance (BC break) The static method was a leftover of the SameSite-cookie era, when it had to run before headers were sent to plant the _nss cookie; after the switch to Sec-Fetch protection its only real callers were tests resetting the static default request between test blocks. The request is now created from globals lazily per form instance, which also removes the shared static state, so tests need no resets at all. An explicit request is injected via the $httpRequest property. | | | |
| forms | by dg | added Repeater example | | | |
| forms | by dg | netteForms: added repeater support | | | |
| forms | by dg | Latte: added {repeater}, n:repeater-item, n:repeater-add & n:repeater-remove tags | | | |
| forms | by dg | added Repeater: container with dynamically repeating items | | | |
| forms | by dg | deprecated Form::getHttpData(), BaseControl::getHttpData() and Helpers::extractHttpData() | | | |
| forms | by dg | Form, Container: submitted data are accessed layer by layer via getSubmittedData() & getSubmittedValue() (BC break) | | | |
| forms | by dg | fixed PHPStan errors: setValue() accepts mixed, simplified Container::addComponent() | | | |
| forms | by dg | netteForms: restructured package, includes UMD and ESM (BC break) | | | |
| forms | by dg | netteForms: removed distribution files (BC break) | | | |
| forms | by dg | Latte: {formContext}, {formContainer}, {formPrint} & {formClassPrint) are deprecated | | | |
| forms | by dg | BaseControl::$disabled is bool, added $disabledChoices | | | |
| forms | by dg | Container::getControls() returns list instead of iterator with names (BC break) | | | |
| forms | by dg | Container: only Control/Container can be added to form (BC break) | | | |
| forms | by dg | added type hints (BC break) | | | |
| forms | by dg | added #[\Deprecated] attributes and trigger_error() to deprecated members | | | |
| forms | by dg | InputNode, LabelNode: deprecate missing comma before arguments Extends the {form} deprecation to {input} and {label}, which share the same "name[:part] followed by bare arguments without a comma" syntax. Triggers E_USER_DEPRECATED when a token follows the name (and optional :part) without a separating comma, pointing to the offending column; the tag still compiles. A genuinely malformed {input foo: class=>val} emits the deprecation alongside the existing CompileException, which is correct: the comma really is missing (canonical: {input foo:, class=>val}). All three Forms tags route through Latte's shared TagParser::consumeCommaBeforeArguments() helper, so {form} is converted from its inline check too -- no duplicated logic. | | | |
| forms | by dg | removed deprecated CsrfProtection control and related code Deleted CsrfProtection class, its tests, ProtectorId constant, and validation message. The addProtection() method now only triggers a deprecation warning. | | | |
| forms | by dg | removed compatibility for old class names | | | |
| forms | by dg | Latte: removed BC alias for the removed formsStack provider Reverts the formsStack compatibility alias. Runtime::begin() no longer takes the template $global nor binds $global->formsStack, and the {form}/ <form n:name> tags stop passing global: to it. Intended for the next major version, where the legacy formsStack provider support is dropped for good. | | | |