Problem/Motivation
A module that implements hook_mail() but provides no EmailBuilder is registered with an auto-generated "legacy" definition whose only sub-type is the wildcard *, labelled "Unknown" (mailer_override, OverrideManager::getLegacyMailerDefinition()). Because of that single wildcard sub-type, it is impossible to create a Mailer Policy scoped to such a module through the Add policy UI: the save always throws a ConfigNameException.
Steps to reproduce
- Enable any module that implements
hook_mail()and has no EmailBuilder. - Go to
/admin/config/system/mailer/policy/add. - Tag 1: select that module.
- Tag 2: the only option offered is "Unknown" (no "All" empty option appears).
- Click "Add and configure".
Result
The save fails with:
Drupal\Core\Config\ConfigNameException: Invalid character in Config object name mailer_policy.mailer_policy.MODULENAME.* in Drupal\Core\Config\ConfigBase::validateName()
MODULENAME is the module machine name; the final token is the literal * wildcard.
Root cause
- A legacy module is registered with a single sub-type whose id is
*(OverrideManager, line 448:sub_defs: ['*' => $this->t('Unknown')]). - In
PolicyAddForm::form()the empty "All" option is added to Tag 2 only when there is more than one sub-type (if (count($options2) > 1), around line 86). With a single sub-type the user is forced to select the wildcard. PolicyAddForm::validateForm()then builds the id withimplode('.', array_filter($id_array))(around line 150).array_filter()keeps the truthy*, producingMODULENAME.*.*is not a legal character in a config object name, so the save throws.
Proposed resolution
Treat the wildcard sub-type as "all sub-types" when building the policy id, so a legacy module maps to a clean type-level id (just the module name) rather than MODULENAME.*. The attached merge request changes the id builder in validateForm() to drop empty values and the * wildcard before imploding.
Versions
Reproduced on 2.0.1 and confirmed unchanged on the 2.x branch (HEAD): the same three lines are present.
Issue fork symfony_mailer-3593926
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
mably commentedComment #4
adamps commentedThanks. I feel that instead of writing code to deal with the problematic sub-def with a *, we can just avoid adding it in the first place.
Idea 1 (not working)
The most obvious way to do this is in in
OverrideManager::getLegacyMailerDefinition(). Having written this description, I eventually realised it is isn't the right fix. I've left the explanation in so that you or anyone else can see why it doesn't work. Change the first line to:The add form then allows you to optionally type in a "tag 2" if you happen to know the available values, or leave it blank for all. This comes from the code below which first of all creates the field as a textfield, which is changed later to a select if we know the options.
So the correct code is basically all there, it was just obscured by that one bad line.
This does create a minor new problem. If I do manually type a sub-tag, then I can create a policy as expected. However it displays with tag as "Unknown". It should be "Module name >> Unknown". The is because the code in
MailerLookup::getTagDefinition()is specifically looking for the subdef ending with a * that we no longer create. This presumably explains how I introduced the main bug in the first place - whilst trying to get the code working for this new problem. We could try to adjust this function, but I don't see a clear simple alternative so perhaps we could instead try a new idea...Idea 2
In
PolicyAddForm::form()around line 75, add an if, with commentThis seems to work.
Could you check it works for you, and if so update the MR?
Comment #5
mably commentedThanks, that's cleaner. I've gone with idea 2.
In
PolicyAddForm::form()the wildcard sub-type is now skipped when building the Tag 2 options:foreach ($definition['sub_defs'] as $id => $sub_def) { if ($id !== '*') { $options2[$id] = $sub_def['label']; } }For a legacy hook_mail() module whose only sub-def is "*",
$options2is then empty, so the select overlay is skipped and Tag 2 stays the free text field: leaving it blank means "all" and produces a valid config name. The "*" sub-def is left untouched in the definition, soMailerLookupstill resolves a manually typed unknown value to "Module name >> Unknown" rather than just "Unknown".Since "*" can no longer reach validation, the previous validateForm guard became redundant, so I reverted that method to its upstream version. It's a single additive commit on top of the branch.
Tested locally: adding a policy for a legacy hook_mail() module now shows the free text Tag 2 field, and saving it blank creates the policy without the ConfigNameException.
Comment #6
adamps commentedThanks that was quick! I've set it to merge.