Skip to content

Commit fc31d0d

Browse files
Fix webform email handler configuration (#29)
1 parent a1e84cb commit fc31d0d

File tree

5 files changed

+257
-23
lines changed

5 files changed

+257
-23
lines changed

.github/workflows/composer-validate.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ jobs:
1818
run: composer validate
1919

2020
- name: Install dependencies
21-
run: composer install --prefer-dist --no-progress
21+
run: composer install --prefer-dist --no-progress

.github/workflows/lint.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Drupal Coding Standards
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
phpcs:
11+
name: PHPCS
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- name: Setup PHP
18+
uses: shivammathur/setup-php@v2
19+
with:
20+
coverage: none
21+
22+
- name: Install PHPCS with Drupal standards
23+
run: |
24+
composer global config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
25+
composer global require drupal/coder squizlabs/php_codesniffer dealerdirect/phpcodesniffer-composer-installer slevomat/coding-standard
26+
phpcs --config-set installed_paths $HOME/.composer/vendor/slevomat/coding-standard,$HOME/.composer/vendor/drupal/coder/coder_sniffer
27+
28+
- name: Run PHPCS
29+
run: |
30+
phpcs --standard=Drupal,DrupalPractice --extensions=php,module,inc,install,test,profile,theme,css,info,txt,md,yml --ignore=vendor/,node_modules/ .

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ Drupal configuration for Bay hosting platform integrations.
1515
This module handles patching of the [Redis](https://www.drupal.org/project/redis) module with a few key features
1616

1717
1. Adds support for RedisCluster client.
18-
1. Adds NewRelic transactions for RedisCluster operations (please see docs in patches directory for how to reroll this patch)
18+
1. Adds NewRelic transactions for RedisCluster operations (please see docs in
19+
patches directory for how to reroll this patch)

bay_platform_dependencies.install

Lines changed: 205 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
/**
44
* @file
5-
* Install, update and uninstall functions for the bay-platform-dependencies module.
5+
* Install, update, uninstall functions for bay-platform-dependencies module.
66
*/
77

88
use Drush\Drush;
@@ -22,20 +22,20 @@ function bay_platform_dependencies_uninstall() {
2222
}
2323

2424
/**
25-
* Update section purger plugin to sectionbundled
25+
* Update section purger plugin to sectionbundled.
2626
*/
2727
function bay_platform_dependencies_update_10001() {
2828
$config_factory = \Drupal::configFactory();
2929

30-
// Update section purger plugin settings
30+
// Update section purger plugin settings.
3131
$purge_plugins_config = $config_factory->getEditable('purge.plugins');
3232
$purgers = $purge_plugins_config->get('purgers');
3333

3434
if (is_array($purgers)) {
3535
foreach ($purgers as &$purger) {
36-
// Only update plugin_id for section purger
36+
// Only update plugin_id for section purger.
3737
if (isset($purger['plugin_id']) && $purger['plugin_id'] === "section") {
38-
// Update the plugin_id to section bundled
38+
// Update the plugin_id to section bundled.
3939
$purger['plugin_id'] = 'sectionbundled';
4040
$purge_plugins_config->set('purgers', $purgers);
4141
$purge_plugins_config->save();
@@ -44,15 +44,15 @@ function bay_platform_dependencies_update_10001() {
4444
}
4545
}
4646

47-
// Update section purger logger channels settings
47+
// Update section purger logger channels settings.
4848
$purge_logger_channels_config = $config_factory->getEditable('purge.logger_channels');
4949
$channels = $purge_logger_channels_config->get('channels');
5050
if (is_array($channels)) {
5151
foreach ($channels as &$channel) {
52-
// Only update channel id for section purger
52+
// Only update channel id for section purger.
5353
if (isset($channel['id']) && strpos($channel['id'], "purger_section_") === 0) {
5454
// Update the id to section bundled
55-
// channel id is in the format of purger_section_{purger_id}
55+
// channel id is in the format of purger_section_{purger_id}.
5656
$parts = explode('_', $channel['id']);
5757
$purger_id = end($parts);
5858
$channel['id'] = "purger_sectionbundled_{$purger_id}";
@@ -97,4 +97,200 @@ function bay_platform_dependencies_update_10003() {
9797
function bay_platform_dependencies_update_10004() {
9898
$process = Drush::drush(Drush::aliasManager()->getSelf(), 'section_purger:install_sensor');
9999
$process->run();
100-
}
100+
}
101+
102+
/**
103+
* Fix webform email handler configurations.
104+
*
105+
* - Ensures valid values for from_mail, return_path, and sender_mail fields.
106+
* - Preserves original from_mail in reply_to.
107+
*/
108+
function bay_platform_dependencies_update_10005(&$sandbox) {
109+
$message = [];
110+
$translation = \Drupal::translation();
111+
112+
// Initialize batch processing if this is the first pass.
113+
if (!isset($sandbox['progress'])) {
114+
$sandbox['progress'] = 0;
115+
$sandbox['current_id'] = 0;
116+
$sandbox['max'] = \Drupal::entityTypeManager()->getStorage('webform')->getQuery()->count()->execute();
117+
118+
// If there are no webforms, return immediately.
119+
if ($sandbox['max'] == 0) {
120+
$message[] = $translation->translate('No webforms found to update.');
121+
return $message;
122+
}
123+
124+
// Initialize counters.
125+
$sandbox['fixed_count'] = 0;
126+
$sandbox['webforms_processed'] = 0;
127+
}
128+
129+
// List of valid values for the settings.
130+
$validValues = ['[site:mail]', '', '_default'];
131+
132+
// Add values from SMTP_FROM_WHITELIST environment variable if it exists.
133+
$whitelist = getenv('SMTP_FROM_WHITELIST');
134+
if ($whitelist) {
135+
$whitelistEmails = explode(',', $whitelist);
136+
// Trim whitespace from each email.
137+
$whitelistEmails = array_map('trim', $whitelistEmails);
138+
// Add whitelisted emails to valid values.
139+
$validValues = array_merge($validValues, $whitelistEmails);
140+
$message[] = $translation->translate('Added @count email addresses from SMTP_FROM_WHITELIST to valid values.',
141+
['@count' => count($whitelistEmails)]);
142+
}
143+
144+
// Process webforms in chunks.
145+
// Number of webforms to process per batch.
146+
$limit = 20;
147+
$webform_ids = \Drupal::entityTypeManager()->getStorage('webform')
148+
->getQuery()
149+
->condition('id', $sandbox['current_id'], '>')
150+
->sort('id')
151+
->range(0, $limit)
152+
->execute();
153+
154+
if (empty($webform_ids)) {
155+
// If no webforms were found, we're done.
156+
$sandbox['#finished'] = 1;
157+
return $message;
158+
}
159+
160+
$webforms = \Drupal::entityTypeManager()->getStorage('webform')->loadMultiple($webform_ids);
161+
162+
foreach ($webforms as $webform) {
163+
$webformName = $webform->label();
164+
$webformId = $webform->id();
165+
$webformChanged = FALSE;
166+
167+
// Get email handlers from the webform.
168+
$handlers = $webform->getHandlers();
169+
170+
foreach ($handlers as $handler) {
171+
// Check if it's an email handler.
172+
if ($handler->getPluginId() === 'email') {
173+
$configuration = $handler->getConfiguration();
174+
$settings = &$configuration['settings'];
175+
$handlerChanged = FALSE;
176+
177+
$handlerLabel = $handler->getLabel();
178+
179+
// Check if return_path is not in the valid values list.
180+
if (isset($settings['return_path']) && !in_array($settings['return_path'], $validValues)) {
181+
$message[] = $translation->translate(
182+
'Webform: @name (ID: @id), Email Handler: @handler, return_path is set to "@value" instead of a valid value',
183+
[
184+
'@name' => $webformName,
185+
'@id' => $webformId,
186+
'@handler' => $handlerLabel,
187+
'@value' => $settings['return_path'],
188+
],
189+
);
190+
191+
// Set return_path to empty string.
192+
$oldValue = $settings['return_path'];
193+
$settings['return_path'] = '';
194+
$message[] = $translation->translate(' - FIXED: Changed return_path from "@old" to ""', ['@old' => $oldValue]);
195+
$handlerChanged = TRUE;
196+
$sandbox['fixed_count']++;
197+
}
198+
199+
// Check if sender_mail is not in the valid values list.
200+
if (isset($settings['sender_mail']) && !in_array($settings['sender_mail'], $validValues)) {
201+
$message[] = $translation->translate(
202+
'Webform: @name (ID: @id), Email Handler: @handler, sender_mail is set to "@value" instead of a valid value',
203+
[
204+
'@name' => $webformName,
205+
'@id' => $webformId,
206+
'@handler' => $handlerLabel,
207+
'@value' => $settings['sender_mail'],
208+
],
209+
);
210+
211+
// Set sender_mail to empty string.
212+
$oldValue = $settings['sender_mail'];
213+
$settings['sender_mail'] = '';
214+
$message[] = $translation->translate(' - FIXED: Changed sender_mail from "@old" to ""', ['@old' => $oldValue]);
215+
$handlerChanged = TRUE;
216+
$sandbox['fixed_count']++;
217+
}
218+
219+
// Check if from_mail is not in the valid values list and fix it by
220+
// setting to '_default'.
221+
if (isset($settings['from_mail']) && !in_array($settings['from_mail'], $validValues)) {
222+
$message[] = $translation->translate(
223+
'Webform: @name (ID: @id), Email Handler: @handler, from_mail is set to "@value" instead of a valid value',
224+
[
225+
'@name' => $webformName,
226+
'@id' => $webformId,
227+
'@handler' => $handlerLabel,
228+
'@value' => $settings['from_mail'],
229+
],
230+
);
231+
232+
// Store the current from_mail value before changing it.
233+
$oldFromMail = $settings['from_mail'];
234+
235+
// Set from_mail to '_default'.
236+
$settings['from_mail'] = '_default';
237+
$message[] = $translation->translate(' - FIXED: Changed from_mail from "@old" to "_default"', ['@old' => $oldFromMail]);
238+
239+
// Also update reply_to to preserve the original email for replies.
240+
$oldReplyTo = $settings['reply_to'] ?? '';
241+
$settings['reply_to'] = $oldFromMail;
242+
$message[] = $translation->translate(' - UPDATED: Set reply_to from "@old" to "@new"', [
243+
'@old' => $oldReplyTo,
244+
'@new' => $oldFromMail,
245+
]);
246+
247+
$handlerChanged = TRUE;
248+
$sandbox['fixed_count']++;
249+
}
250+
251+
// Update the handler configuration if changes were made.
252+
if ($handlerChanged) {
253+
$handler->setConfiguration($configuration);
254+
$webformChanged = TRUE;
255+
}
256+
}
257+
}
258+
259+
// Save the webform if any handlers were updated.
260+
if ($webformChanged) {
261+
$webform->save();
262+
$message[] = $translation->translate('Saved changes to webform: @name (ID: @id)', [
263+
'@name' => $webformName,
264+
'@id' => $webformId,
265+
]);
266+
}
267+
268+
// Update progress information.
269+
$sandbox['progress']++;
270+
$sandbox['webforms_processed']++;
271+
$sandbox['current_id'] = $webformId;
272+
}
273+
274+
// Set the value for finished. If there are no webforms, we're done.
275+
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
276+
277+
// If we're done, provide a summary message.
278+
if ($sandbox['#finished'] >= 1) {
279+
if ($sandbox['fixed_count'] > 0) {
280+
$message[] = $translation->translate('Summary: Fixed @count issues with the following settings:', ['@count' => $sandbox['fixed_count']]);
281+
$message[] = $translation->translate('- return_path and sender_mail set to "" (empty string)');
282+
$message[] = $translation->translate('- from_mail set to "_default" and moved original from_mail value to reply_to');
283+
284+
$validValuesList = "'[site:mail]', '' (empty string), '_default'";
285+
if ($whitelist) {
286+
$validValuesList .= ', and whitelisted emails from SMTP_FROM_WHITELIST';
287+
}
288+
$message[] = $translation->translate('Valid values include: @values.', ['@values' => $validValuesList]);
289+
}
290+
else {
291+
$message[] = $translation->translate('No issues found. All email handlers in webforms have correct return_path, sender_mail, and from_mail settings.');
292+
}
293+
}
294+
295+
return implode("\n", $message);
296+
}

bay_platform_dependencies.module

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
* Primary module hooks for bay-platform-dependencies module.
66
*/
77

8+
use Drupal\Core\Form\FormStateInterface;
89
use Drupal\Core\Render\BubbleableMetadata;
910

10-
const BPD_ENV_SMTP_ALLOWLIST = "SMTP_FROM_WHITELIST";
11-
const K8S_SERVICE_ACCOUNT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token";
11+
// phpcs:disable
12+
const BAY_PLATFORM_DEPENDENCIES_ENV_SMTP_ALLOWLIST = "SMTP_FROM_WHITELIST";
13+
const BAY_PLATFORM_DEPENDENCIES_K8S_SERVICE_ACCOUNT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token";
14+
// phpcs:enable
1215

1316
/**
1417
* Implements hook_mail_alter().
@@ -30,28 +33,31 @@ function bay_platform_dependencies_mail_alter(&$message) {
3033
/**
3134
* Implements hook_form_FORM_ID_alter().
3235
*/
33-
function bay_platform_dependencies_form_webform_handler_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
36+
function bay_platform_dependencies_form_webform_handler_form_alter(&$form, FormStateInterface $form_state) {
3437
$smtp_allowlist = _bay_platform_dependencies_smtp_allowlist();
3538
if (!$smtp_allowlist) {
3639
return;
3740
}
38-
41+
3942
_bay_platform_dependencies_form_webform_handler_form_options($form["settings"]["from"]["from_mail"]["from_mail"], $smtp_allowlist);
4043
_bay_platform_dependencies_form_webform_handler_form_options($form["settings"]["additional"]["return_path"]["return_path"], $smtp_allowlist);
4144
_bay_platform_dependencies_form_webform_handler_form_options($form["settings"]["additional"]["sender_mail"]["sender_mail"], $smtp_allowlist);
4245
}
4346

47+
/**
48+
* Helper to alter form elements related to webform email handlers.
49+
*/
4450
function _bay_platform_dependencies_form_webform_handler_form_options(&$element, array $smtp_allowlist) {
4551
// Remove ability to choose element values.
4652
unset($element['#options']["Elements"]);
4753
unset($element['#options']["Options"]);
48-
54+
4955
// Remove ability to choose contextual values.
5056
$element['#options']["Other"] = [];
5157
foreach ($smtp_allowlist as $email) {
5258
$element['#options']["Other"][$email] = $email;
5359
}
54-
60+
5561
// Add validation to ensure "other" values meet allowlist.
5662
$element["#element_validate"][] = "bay_platform_dependencies_form_webform_handler_form_element_validate";
5763
}
@@ -64,7 +70,7 @@ function bay_platform_dependencies_form_webform_handler_form_element_validate($e
6470
if (empty($value) || $value == "_default") {
6571
return;
6672
}
67-
73+
6874
if (!in_array($value, _bay_platform_dependencies_smtp_allowlist())) {
6975
$error = \Drupal::translation()->translate("Disallowed email address submitted - %email", ["%email" => $value]);
7076
$form_state->setErrorByName(implode("][", $element['#parents']), $error);
@@ -79,7 +85,7 @@ function bay_platform_dependencies_form_webform_handler_form_element_validate($e
7985
* FALSE is not configured.
8086
*/
8187
function _bay_platform_dependencies_smtp_allowlist() {
82-
$list = getenv(BPD_ENV_SMTP_ALLOWLIST);
88+
$list = getenv(BAY_PLATFORM_DEPENDENCIES_ENV_SMTP_ALLOWLIST);
8389
if (empty($list)) {
8490
return FALSE;
8591
}
@@ -109,13 +115,14 @@ function bay_platform_dependencies_tokens($type, $tokens, array $data, array $op
109115
foreach ($tokens as $name => $original) {
110116
switch ($name) {
111117
case 'k8s-service-account-token':
112-
if (file_exists(K8S_SERVICE_ACCOUNT_PATH)) {
113-
$token = file_get_contents(K8S_SERVICE_ACCOUNT_PATH);
118+
if (file_exists(BAY_PLATFORM_DEPENDENCIES_K8S_SERVICE_ACCOUNT_PATH)) {
119+
$token = file_get_contents(BAY_PLATFORM_DEPENDENCIES_K8S_SERVICE_ACCOUNT_PATH);
114120
if ($token === FALSE) {
115-
throw new \Exception(sprintf("Failed to read service account token at %s", K8S_SERVICE_ACCOUNT_PATH));
121+
throw new \Exception(sprintf("Failed to read service account token at %s", BAY_PLATFORM_DEPENDENCIES_K8S_SERVICE_ACCOUNT_PATH));
116122
}
117123
$replacements[$original] = $token;
118-
} else {
124+
}
125+
else {
119126
$replacements[$original] = '';
120127
}
121128
break;

0 commit comments

Comments
 (0)