Framework

Repository Author Commit message Committed SHA  
 
application by dg Presenter::argsToParams() now distinguish between NULL and ''
application by dg removed {templatePrint}
application by dg uses nette/routing 4.0
application by dg RoutingPanel: added support for AI agents
application by dg removed support for @annotations (BC break)
application by dg Presenter: removed constructor (BC break!)
application by dg Component: only UI components can be added to presenter/component (BC break) WIP
application by dg Component: method checkRequirements() is called for createComponent<Name>() methods (BC break)
application by dg Revert "UI\PresenterComponent: removed references created by loadState() for persistent parameters. [Closes nette/nette#703][Closes nette/nette#703][Closes #69]" (possible BC break)

This reverts commit cda17f460d020b0f042364d4e140742022a7e94d.

See https://forum.nette.org/cs/35528-stejne-pojmenovany-parametr-akce-presenteru-a-persistentni-odlisne-chovani-v-nette-2-0-oproti-aktualnimu#p221742

BC break: Property must be nullable, ie: #[Persistent] public ?int $foo

application by dg Presenter::handleInvalidLink() -> processInvalidLink() (BC break)
application by dg added type hints (BC break)
application by dg composer: increased dependencies versions
application by dg opened 4.0-dev
application by dg added support for backed enums in parameters [Closes nette/nette#1558]

Persistent, action, render and signal parameters can be typed as a backed
enum. The URL carries the case value: link() unwraps BackedEnum instances
before the URL is built (http_build_query would otherwise decompose the
object into an array), and incoming scalars are losslessly converted back
via tryFrom(); an invalid value yields 4xx, not 500.

application by dg typo
application by dg UI\Form: adapted to the SubmissionSource model of nette/forms 4.0

The form no longer overrides receiveHttpData(), isAnchored() and
beforeRender(); instead it overrides createDefaultSource() to return
null and sets the @internal FormSubmissionSource when anchored to
a presenter. The source detects submission via the 'submit' signal,
reads the application request and installs the hidden signal field in
its prepare() render hook; it resolves the presenter from the form
lazily, so a form re-anchored to another presenter keeps working. The
catch-up pass of setSubmissionSource() replaces the manual
loadHttpData() loop in validateParent().

The form owns its cross-origin state (private ?FetchSite $allowedOrigin
+ allowCrossOrigin()) independently of nette/forms; the same-origin
check intentionally stays in signalReceived(), where a failure is
reported via Presenter::detectedCsrf().

Requires nette/forms 4.0.

application by dg UI\Form: compatible with both nette/forms 3.x and the upcoming 4.0

Two forward-compatible additions to the classic presenter form:

- The form owns its cross-origin state (private ?FetchSite
$allowedOrigin + allowCrossOrigin()) instead of relying on the
protected $crossOrigin inherited from Nette\Forms\Form. Forms 3.x
declares that property untyped while 4.0 removes it, so no
redeclaration can satisfy both; a differently named private property
works with either version.
- createDefaultSource() returns null, which under forms 4.0 prevents
the lazy default HttpSource from materializing (the form is anchored
via the presenter). Under 3.x the method is unused; its return type
references an interface that only exists in 4.0, which is safe
because without a parent method there is no variance check.

The classic receiveHttpData()/isAnchored()/beforeRender() overrides
double as the documented BC seam of forms 4.0, so a single class works
with both major versions.

application by dg ControlNode: deprecate missing comma before arguments

Brings the {form}/{input}/{label} deprecation to {control} via the
shared TagParser::consumeCommaBeforeArguments() helper. Triggers
E_USER_DEPRECATED when a token follows the name (and optional :method)
without a separating comma, pointing to the offending column; the tag
still compiles. A trailing |modifier is not mistaken for arguments.

nette by dg added AGENTS.md
nette by dg improved readme.md
nette by dg composer: relaxed dependencies to minimum required versions

Caret constraints replaced with >=, so the metapackage never holds back
a newer major of any component. Minimums raised to the versions Nette 4.0
actually needs, added nette/command-line, dropped nette/tokenizer.

nette by dg composer: removed obsolete nette/tokenizer
nette by dg singleline declare statements
nette by dg updated .gitattributes
latte by dg HtmlHelpers: contenteditable, draggable, spellcheck & hidden are not boolean attributes (BC break)

None of them is a boolean attribute in HTML; they are enumerated ones, so
rendering them as a bare name was wrong in three separate ways:

- a string 'false' is truthy in PHP, so contenteditable={$s} rendered the
attribute present, i.e. the exact opposite of what was asked for
- the empty value is not a keyword of draggable, it maps to the invalid value
default 'auto', so draggable=true could not be expressed at all
- 'attribute missing' and 'attribute false' are different states; the false
value is what disables editing inside a contenteditable region, and dropping
the attribute silently inherited from the parent instead

Two new categories replace them. 'tristate' (contenteditable, draggable,
spellcheck, writingsuggestions) maps bool to the true/false keywords, exactly
like aria-* already does, so formatAriaAttribute now delegates to it. The
'valuedBool' one (hidden, popover) stays a flag but keeps a non-empty value,
so hidden="until-found" survives. Falsy values are tested first there: hidden
with a '0' coming from a database must not render hidden="0", which is an
invalid value and would map back to the hidden state.

mail by dg CssInliner: resolves declarations by the CSS cascade

Rules were applied in the order they appeared, so the last one to mention a
property won. A browser does not work that way: p.intro { color: red } followed
by p { color: blue } paints the intro red, while the inliner painted it blue.
The inlined mail therefore looked different from the page the CSS was written
for, and the more carefully the stylesheet was written, the more it diverged.

Declarations now compete the way they do in an author stylesheet: !important
first, then an existing inline style, then specificity, ties going to the later
rule. Each selector in a comma-separated list carries its own specificity, and
one part the DOM engine rejects (::marker) no longer discards the whole rule.
The argument of an ordinary functional pseudo-class is a keyword or an An+B
expression, not a selector, so the idents in :nth-child(odd) or :nth-child(-n+3)
do not count as type selectors; counting them would inflate specificity enough
to flip a winner.

Only the winner of each property is written out, so a property appears in the
style attribute once rather than several times with the earlier values trailing.
HTML attributes generated for Outlook (bgcolor, width) drop the !important
marker, which has no meaning in an attribute.

A '}' inside a style attribute would close the block the attribute is wrapped in
for parsing and turn the remainder into rules of its own, read back as the
element's inline declarations. Such an attribute is not a plain declaration list
and is kept verbatim.

mail by dg CssInliner: do not emit an attribute a value cannot express, and fold property case

- width: auto (or inherit, or calc(...)) was cast to an integer and emitted as
width="0", collapsing the cell in Outlook. The inliner thus broke a layout the
source CSS had left perfectly fine. A numeric attribute is now written only for
a plain length or percentage, and calc() no longer slips through on its '%'.
- Property names are case-insensitive in CSS but were compared verbatim, so COLOR
and color ended up as two separate declarations, and a WIDTH: 600px never
produced the width attribute for Outlook. Custom properties keep their case,
being genuinely case-sensitive.

mail by dg FileMailer: writes emails to .eml files instead of sending them

In development and in tests, mail must not leave the machine, yet it still needs
looking at. Redirecting everything to a developer's inbox (Interceptor) needs a
working transport, and the Tracy panel only shows the envelope, not the message.

FileMailer drops each message into a directory as a complete .eml file, which any
mail client opens -- the rendered HTML, the attachments, the headers, all of it.
It is a Mailer like any other, so it plugs in wherever a real transport goes, and
it signs with DKIM if given a signer, which makes the signature inspectable too.

mail by dg FallbackMailer: stops retrying a mailer that refused for good

Every failure was treated as worth another go. A message the server rejected
outright -- 550 no such user, bad credentials -- was resent to the same server
retryCount times, sleeping between rounds, for an answer that could not change.
Sending was slow to fail exactly when failing fast was the only useful outcome.

SendException now knows whether it is permanent, and SmtpException derives that
from the reply: 5xx is a permanent negative completion, 4xx a transient one
(RFC 5321, §4.2.1). A mailer that fails permanently drops out of the remaining
rounds, so the fallback is tried immediately and the others keep their retries.
A failure with no reply behind it (timeout, dropped connection) stays retryable,
as does a plain SendException, so existing mailers behave exactly as before.

mail by dg Message: added setUnsubscribe() for one-click unsubscribe

Gmail and Yahoo require bulk senders to offer one-click unsubscribe, and getting
it right means two headers that have to agree: List-Unsubscribe with the target,
and List-Unsubscribe-Post to announce that a bare POST is enough. A lone
List-Unsubscribe does not satisfy the requirement, which is easy to get wrong by
hand -- exactly the kind of knowledge the library should hold.

setUnsubscribe($url, $email) writes both, announcing one-click only alongside a
URL (a mailto target has nothing to POST to). The unsubscribe headers also join
the DKIM defaults, so the address a click goes to cannot be swapped in transit;
they are only signed when present, so no existing signature changes.

mail by dg Message: converts internationalized domains to punycode

An address like jan@příklad.cz passed validation and then travelled with a UTF-8
domain in the headers and, worse, in the SMTP envelope. That is only legal when
both peers negotiate SMTPUTF8 (RFC 6531); an ordinary server rejects the RCPT TO
or mangles the address, and the failure is puzzling because the address looked
fine going in.

The domain of every address -- From, To, Cc, Bcc, Reply-To, Return-Path -- is now
encoded to its ASCII form, so jan@xn--pklad-zsa96e.cz goes on the wire. Display
names keep their diacritics (they are MIME-encoded anyway), and so does the local
part: a non-ASCII local part really does need SMTPUTF8 and cannot be encoded away.

Needs ext-intl; without it the address is passed through unchanged, as before.

mail by dg MimePart: header names are case-insensitive

RFC 5322 header names are case-insensitive, but they were stored and looked up
by exact spelling: getHeader('from') did not find 'From', and setHeader('SUBJECT')
added a second Subject header next to the existing one rather than replacing it.
Everything that reads headers back -- DkimSigner, SmtpMailer, Interceptor -- had
to spell them exactly as Message writes them or silently see nothing.

Lookups now ignore case and the spelling first used is the one kept, so generated
messages are unchanged. DkimSigner matches the headers it signs the same way; it
compares names taken from the raw message against the configured list, which
would otherwise miss a header the configuration spells differently and produce a
signature the receiver cannot verify.

The remaining exact-spelling lookups follow suit: getEncodedMessage() finds the
Content-Type header to append the multipart boundary to -- an oddly spelled one
would have kept its multipart type but lost the boundary, a body no mail client
can parse -- and SendmailMailer strips the To and Subject lines that mail() adds
itself whatever their case.

Last synchronization: 2026-08-05 21:02:35