Skip to content

Commit 94e585d

Browse files
committed
Add autocomplete options to form fields: token, section, purpose, type
1 parent 40b899f commit 94e585d

File tree

8 files changed

+692
-81
lines changed

8 files changed

+692
-81
lines changed

Classes/Domain/Model/Field.php

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,26 @@ class Field extends AbstractEntity
143143
*/
144144
protected int $l10nParent = 0;
145145

146+
/**
147+
* @var string
148+
*/
149+
protected string $autocompleteToken = '';
150+
151+
/**
152+
* @var string
153+
*/
154+
protected string $autocompleteSection = '';
155+
156+
/**
157+
* @var string
158+
*/
159+
protected string $autocompleteType = '';
160+
161+
/**
162+
* @var string
163+
*/
164+
protected string $autocompletePurpose = '';
165+
146166
/**
147167
* @var Page
148168
* This property can hold Page|int|null (depending on the context). "@var" must set to Page for property mapping.
@@ -830,4 +850,78 @@ protected function getExportableTypesFromTypoScript(): array
830850
}
831851
return $types;
832852
}
853+
854+
/**
855+
* @return string
856+
*/
857+
public function getAutocompleteToken(): string
858+
{
859+
return $this->autocompleteToken;
860+
}
861+
862+
/**
863+
* @param string $autocompleteToken
864+
*
865+
* @return void
866+
*/
867+
public function setAutocompleteToken(string $autocompleteToken): void
868+
{
869+
$this->autocompleteToken = $autocompleteToken;
870+
}
871+
872+
/**
873+
* @return string
874+
*/
875+
public function getAutocompleteSection(): string
876+
{
877+
return $this->autocompleteSection;
878+
}
879+
880+
/**
881+
* @param string $autocompleteSection
882+
*
883+
* @return void
884+
*/
885+
public function setAutocompleteSection(string $autocompleteSection): void
886+
{
887+
$this->autocompleteSection = $autocompleteSection;
888+
}
889+
890+
/**
891+
* @return string
892+
*/
893+
public function getAutocompleteType(): string
894+
{
895+
return $this->autocompleteType;
896+
}
897+
898+
/**
899+
* @param string $autocompleteType
900+
*
901+
* @return void
902+
*/
903+
public function setAutocompleteType(string $autocompleteType): void
904+
{
905+
$this->autocompleteType = $autocompleteType;
906+
}
907+
908+
/**
909+
* @return string
910+
*/
911+
public function getAutocompletePurpose(): string
912+
{
913+
return $this->autocompletePurpose;
914+
}
915+
916+
/**
917+
* @param string $autocompletePurpose
918+
*
919+
* @return void
920+
*/
921+
public function setAutocompletePurpose(string $autocompletePurpose): void
922+
{
923+
$this->autocompletePurpose = $autocompletePurpose;
924+
}
925+
926+
833927
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace In2code\Powermail\Tca;
5+
6+
/**
7+
* Class AddAutocompleteTokens
8+
* @package In2code\Powermail\Tca
9+
*/
10+
class AddAutocompleteTokens
11+
{
12+
/**
13+
* @var string
14+
*/
15+
public static string $LLL = 'LLL:EXT:powermail/Resources/Private/Language/locallang_db.xlf:autocomplete_token.';
16+
17+
/**
18+
* @param array $config
19+
*
20+
* @return void
21+
*/
22+
public function getAutocompleteTokens(array &$config)
23+
{
24+
if ($config['config']['itemsProcConfig']['useDefaultItems'] ?? true) {
25+
$defaultSelectItems = self::getDefaultAutocompleteTokens();
26+
$config['items'] = array_merge(
27+
$config['items'],
28+
$defaultSelectItems
29+
);
30+
}
31+
}
32+
33+
/**
34+
* https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill-field
35+
* @return array[]
36+
*/
37+
public static function getDefaultAutocompleteTokens(): array
38+
{
39+
return [
40+
['label' => self::$LLL . 'name', 'value' => 'name', 'icon' => '', 'group' => 'name'],
41+
['label' => self::$LLL . 'honorific-prefix', 'value' => 'honorific-prefix', 'icon' => '', 'group' => 'name'],
42+
['label' => self::$LLL . 'given-name', 'value' => 'given-name', 'icon' => '', 'group' => 'name'],
43+
['label' => self::$LLL . 'additional-name', 'value' => 'additional-name', 'icon' => '', 'group' => 'name'],
44+
['label' => self::$LLL . 'family-name', 'value' => 'family-name', 'icon' => '', 'group' => 'name'],
45+
['label' => self::$LLL . 'honorific-suffix', 'value' => 'honorific-suffix', 'icon' => '', 'group' => 'name'],
46+
['label' => self::$LLL . 'nickname', 'value' => 'nickname', 'icon' => '', 'group' => 'name'],
47+
['label' => self::$LLL . 'sex', 'value' => 'sex', 'icon' => '', 'group' => 'name'],
48+
['label' => self::$LLL . 'email', 'value' => 'email', 'icon' => '', 'group' => 'contact'],
49+
['label' => self::$LLL . 'impp', 'value' => 'impp', 'icon' => '', 'group' => 'contact'],
50+
['label' => self::$LLL . 'url', 'value' => 'url', 'icon' => '', 'group' => 'contact'],
51+
['label' => self::$LLL . 'organization-title', 'value' => 'organization-title', 'icon' => '', 'group' => 'contact'],
52+
['label' => self::$LLL . 'organization', 'value' => 'organization', 'icon' => '', 'group' => 'contact'],
53+
['label' => self::$LLL . 'street-address', 'value' => 'street-address', 'icon' => '', 'group' => 'contact'],
54+
['label' => self::$LLL . 'country', 'value' => 'country', 'icon' => '', 'group' => 'contact'],
55+
['label' => self::$LLL . 'country-name', 'value' => 'country-name', 'icon' => '', 'group' => 'contact'],
56+
['label' => self::$LLL . 'postal-code', 'value' => 'postal-code', 'icon' => '', 'group' => 'contact'],
57+
['label' => self::$LLL . 'address-line1', 'value' => 'address-line1', 'icon' => '', 'group' => 'address'],
58+
['label' => self::$LLL . 'address-line2', 'value' => 'address-line2', 'icon' => '', 'group' => 'address'],
59+
['label' => self::$LLL . 'address-line3', 'value' => 'address-line3', 'icon' => '', 'group' => 'address'],
60+
['label' => self::$LLL . 'address-level1', 'value' => 'address-level1', 'icon' => '', 'group' => 'address'],
61+
['label' => self::$LLL . 'address-level2', 'value' => 'address-level2', 'icon' => '', 'group' => 'address'],
62+
['label' => self::$LLL . 'address-level3', 'value' => 'address-level3', 'icon' => '', 'group' => 'address'],
63+
['label' => self::$LLL . 'address-level4', 'value' => 'address-level4', 'icon' => '', 'group' => 'address'],
64+
['label' => self::$LLL . 'tel', 'value' => 'tel', 'icon' => '', 'group' => 'tel'],
65+
['label' => self::$LLL . 'tel-country-code', 'value' => 'tel-country-code', 'icon' => '', 'group' => 'tel'],
66+
['label' => self::$LLL . 'tel-area-code', 'value' => 'tel-area-code', 'icon' => '', 'group' => 'tel'],
67+
['label' => self::$LLL . 'tel-national', 'value' => 'tel-national', 'icon' => '', 'group' => 'tel'],
68+
['label' => self::$LLL . 'tel-local', 'value' => 'tel-local', 'icon' => '', 'group' => 'tel'],
69+
['label' => self::$LLL . 'tel-local-prefix', 'value' => 'tel-local-prefix', 'icon' => '', 'group' => 'tel'],
70+
['label' => self::$LLL . 'tel-local-suffix', 'value' => 'tel-local-suffix', 'icon' => '', 'group' => 'tel'],
71+
['label' => self::$LLL . 'tel-extension', 'value' => 'tel-extension', 'icon' => '', 'group' => 'tel'],
72+
['label' => self::$LLL . 'username', 'value' => 'username', 'icon' => '', 'group' => 'user'],
73+
['label' => self::$LLL . 'new-password', 'value' => 'new-password', 'icon' => '', 'group' => 'user'],
74+
['label' => self::$LLL . 'current-password', 'value' => 'current-password', 'icon' => '', 'group' => 'user'],
75+
['label' => self::$LLL . 'one-time-code', 'value' => 'one-time-code', 'icon' => '', 'group' => 'user'],
76+
['label' => self::$LLL . 'bday', 'value' => 'bday', 'icon' => '', 'group' => 'bday'],
77+
['label' => self::$LLL . 'bday-day', 'value' => 'bday-day', 'icon' => '', 'group' => 'bday'],
78+
['label' => self::$LLL . 'bday-month', 'value' => 'bday-month', 'icon' => '', 'group' => 'bday'],
79+
['label' => self::$LLL . 'bday-year', 'value' => 'bday-year', 'icon' => '', 'group' => 'bday'],
80+
['label' => self::$LLL . 'cc-name', 'value' => 'cc-name', 'icon' => '', 'group' => 'cc'],
81+
['label' => self::$LLL . 'cc-given-name', 'value' => 'cc-given-name', 'icon' => '', 'group' => 'cc'],
82+
['label' => self::$LLL . 'cc-additional-name', 'value' => 'cc-additional-name', 'icon' => '', 'group' => 'cc'],
83+
['label' => self::$LLL . 'cc-family-name', 'value' => 'cc-family-name', 'icon' => '', 'group' => 'cc'],
84+
['label' => self::$LLL . 'cc-number', 'value' => 'cc-number', 'icon' => '', 'group' => 'cc'],
85+
['label' => self::$LLL . 'cc-exp', 'value' => 'cc-exp', 'icon' => '', 'group' => 'cc'],
86+
['label' => self::$LLL . 'cc-exp-month', 'value' => 'cc-exp-month', 'icon' => '', 'group' => 'cc'],
87+
['label' => self::$LLL . 'cc-exp-year', 'value' => 'cc-exp-year', 'icon' => '', 'group' => 'cc'],
88+
['label' => self::$LLL . 'cc-csc', 'value' => 'cc-csc', 'icon' => '', 'group' => 'cc'],
89+
['label' => self::$LLL . 'cc-type', 'value' => 'cc-type', 'icon' => '', 'group' => 'cc'],
90+
['label' => self::$LLL . 'transaction-currency', 'value' => 'transaction-currency', 'icon' => '', 'group' => 'cc'],
91+
['label' => self::$LLL . 'transaction-amount', 'value' => 'transaction-amount', 'icon' => '', 'group' => 'cc'],
92+
['label' => self::$LLL . 'language', 'value' => 'language', 'icon' => '', 'group' => 'other'],
93+
['label' => self::$LLL . 'photo', 'value' => 'photo', 'icon' => '', 'group' => 'other'],
94+
];
95+
}
96+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
namespace In2code\Powermail\ViewHelpers\String;
5+
6+
use In2code\Powermail\Domain\Model\Field;
7+
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
8+
9+
/**
10+
* Class AutocompleteViewHelper
11+
*/
12+
class AutocompleteViewHelper extends AbstractViewHelper
13+
{
14+
public function initializeArguments()
15+
{
16+
parent::initializeArguments();
17+
$this->registerArgument('field', Field::class, 'Field', true);
18+
}
19+
20+
/**
21+
* render the value for the autocomplete attribute
22+
*
23+
* @return string
24+
*/
25+
public function render(): string
26+
{
27+
$field = $this->arguments['field'];
28+
29+
list($autocompleteTokens, $token, $section, $type, $purpose)
30+
= [
31+
'',
32+
$field->getAutocompleteToken(),
33+
trim($field->getAutocompleteSection()),
34+
$field->getAutocompleteType(),
35+
$field->getAutocompletePurpose(),
36+
];
37+
38+
//if token is empty or 'on'/'off' other tokens are not allowed
39+
if (empty($token) || in_array($token, ['on', 'off'])) {
40+
return $token;
41+
}
42+
43+
//optional section token must begin with the string 'section-'
44+
if (!empty($section)) {
45+
$autocompleteTokens = 'section-' . $section . ' ';
46+
}
47+
48+
//optional type token must be either shipping or billing
49+
if (!empty($type) && in_array($type, ['shipping', 'billing'])) {
50+
$autocompleteTokens .= $type . ' ';
51+
}
52+
53+
//optional purpose token is only allowed for certain autofill-field tokens
54+
if (!empty($purpose)) {
55+
if ($this->tokenIsAllowedForPurpose($token, $purpose)) {
56+
$autocompleteTokens .= $purpose . ' ';
57+
}
58+
}
59+
60+
return $autocompleteTokens . $token;
61+
}
62+
63+
/**
64+
* hardcoded check:
65+
* purpose is only allowed for email, imp, tel and tel-*
66+
*
67+
* @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill-detail-tokens
68+
*
69+
* @param string $token
70+
* @param string $purpose
71+
*
72+
* @return bool
73+
*/
74+
protected function tokenIsAllowedForPurpose(string $token, string $purpose): bool
75+
{
76+
return in_array($purpose, ['home', 'work', 'mobile', 'fax', 'pager'])
77+
&& in_array($token, ['tel', 'tel-country-code', 'tel-national', 'tel-area-code', 'tel-local', 'tel-local-prefix', 'tel-local-suffix', 'tel-extension', 'email', 'impp']);
78+
}
79+
}

0 commit comments

Comments
 (0)