Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- “Field” and “Section” condition rules now show field/section handles for users with the “Show field handles in edit forms” preference enabled. ([#17909](https://github.com/craftcms/cms/pull/17909))
- “Remove” actions on the Plugins index page now show a confirmation dialog. ([#17922](https://github.com/craftcms/cms/pull/17922))
- `entrify` commands no longer require a category group/tag group/global set handle to be passed.
- Added the `useIdnaNontransitionalToUnicode` config setting. ([#17946](https://github.com/craftcms/cms/pull/17946))

### Development
- Reference tags now support fallback values when no attribute is specified. ([#17688](https://github.com/craftcms/cms/pull/17688))
Expand Down
56 changes: 56 additions & 0 deletions src/config/GeneralConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -3212,6 +3212,33 @@ class GeneralConfig extends BaseConfig
*/
public bool $useEmailAsUsername = false;

/**
* @var bool Whether the [`IDNA_NONTRANSITIONAL_TO_UNICODE`](https://www.php.net/manual/en/intl.constants.php#constant.idna-nontransitional-to-unicode)
* flag should be passed to [idn_to_utf8()](https://www.php.net/manual/en/function.idn-to-utf8.php) when converting
* email addresses from IDNA ASCII to Unicode.
*
* `INTL_IDNA_VARIANT_UTS46` by default, which uses the UTS 46 algorithm, consistent with the requirements of the
* IDNA2008 protocol and mostly compatible with IDNA2003 (deprecated in PHP 7.2).
*
* There are a handful of characters which result in different resolution of IDNs between IDNA2008 and IDNA2003,
* including ß, ς, and joiner characters (ZWJ and ZWNJ). ([More info](https://unicode.org/reports/tr46/#Deviations))
*
* For example, `ß` will be converted to `ss` by default. Enabling this setting will ensure it gets preserved as `ß`.
*
* ::: code
* ```php Static Config
* ->useIdnaNontransitionalToUnicode(true)
* ```
* ```shell Environment Override
* CRAFT_USE_IDNA_NONTRANSITIONAL_TO_UNICODE=true
* ```
* :::
*
* @group System
* @since 5.9.0
*/
public bool $useIdnaNontransitionalToUnicode = false;

/**
* @var bool Whether [iFrame Resizer options](http://davidjbradshaw.github.io/iframe-resizer/#options) should be used for Live Preview.
*
Expand Down Expand Up @@ -6962,6 +6989,35 @@ public function useEmailAsUsername(bool $value = true): self
return $this;
}

/**
* Whether the [`IDNA_NONTRANSITIONAL_TO_UNICODE`](https://www.php.net/manual/en/intl.constants.php#constant.idna-nontransitional-to-unicode)
* flag should be passed to [idn_to_utf8()](https://www.php.net/manual/en/function.idn-to-utf8.php) when converting
* email addresses from IDNA ASCII to Unicode.
*
* `INTL_IDNA_VARIANT_UTS46` by default, which uses the UTS 46 algorithm, consistent with the requirements of the
* IDNA2008 protocol and mostly compatible with IDNA2003 (deprecated in PHP 7.2).
*
* There are a handful of characters which result in different resolution of IDNs between IDNA2008 and IDNA2003,
* including ß, ς, and joiner characters (ZWJ and ZWNJ). ([More info](https://unicode.org/reports/tr46/#Deviations))
*
* For example, `ß` will be converted to `ss` by default. Enabling this setting will ensure it gets preserved as `ß`.
*
* ```php
* ->useIdnaNontransitionalToUnicode(true)
* ```
*
* @group System
* @param bool $value
* @return self
* @see $useIdnaNontransitionalToUnicode
* @since 5.9.0
*/
public function useIdnaNontransitionalToUnicode(bool $value = false): self
{
$this->useIdnaNontransitionalToUnicode = $value;
return $this;
}

/**
* Whether [iFrame Resizer options](http://davidjbradshaw.github.io/iframe-resizer/#options) should be used for Live Preview.
*
Expand Down
2 changes: 1 addition & 1 deletion src/elements/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,7 @@ protected function defineRules(): array
$rules[] = [['lastLoginDate', 'lastInvalidLoginDate', 'lockoutDate', 'lastPasswordChangeDate', 'verificationCodeIssuedDate'], DateTimeValidator::class];
$rules[] = [['invalidLoginCount', 'photoId', 'affiliatedSiteId'], 'number', 'integerOnly' => true];
$rules[] = [['username', 'email', 'unverifiedEmail', 'fullName', 'firstName', 'lastName'], 'trim', 'skipOnEmpty' => true];
$rules[] = [['email', 'unverifiedEmail'], 'email', 'enableIDN' => App::supportsIdn(), 'enableLocalIDN' => false];
$rules[] = [['email', 'unverifiedEmail'], 'email', 'enableIDN' => App::supportsIdn(), 'enableLocalIDN' => App::supportsIdn()];
$rules[] = [['email', 'username', 'fullName', 'firstName', 'lastName', 'password', 'unverifiedEmail'], 'string', 'max' => 255];
$rules[] = [['verificationCode'], 'string', 'max' => 100];
$rules[] = [['email'], 'required', 'when' => fn() => !$this->getIsDraft()];
Expand Down
2 changes: 1 addition & 1 deletion src/fields/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public function getElementValidationRules(): array
{
return [
['trim'],
['email', 'enableIDN' => App::supportsIdn(), 'enableLocalIDN' => false],
['email', 'enableIDN' => App::supportsIdn(), 'enableLocalIDN' => App::supportsIdn()],
];
}

Expand Down
7 changes: 6 additions & 1 deletion src/helpers/StringHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -1949,9 +1949,14 @@ public static function idnToUtf8Email(string $email): string
return $email;
}

if (Craft::$app->getConfig()->getGeneral()->useIdnaNontransitionalToUnicode && defined('IDNA_NONTRANSITIONAL_TO_UNICODE')) {
$variant = IDNA_NONTRANSITIONAL_TO_UNICODE;
} else {
$variant = INTL_IDNA_VARIANT_UTS46;
}
$parts = explode('@', $email, 2);
foreach ($parts as &$part) {
if (($part = idn_to_utf8($part, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46)) === false) {
if (($part = idn_to_utf8($part, IDNA_DEFAULT, $variant)) === false) {
return $email;
}
}
Expand Down