Improved password handling on profile page
This commit is contained in:
parent
be8139659f
commit
8bc9e566db
|
|
@ -3,7 +3,7 @@
|
|||
* Plugin Name: FECA Mailshots
|
||||
* Plugin URI: https://fenedge.co.uk/
|
||||
* Description: FECA mailshots plugin.
|
||||
* Version: 1.1.8
|
||||
* Version: 1.1.10
|
||||
* Requires at least: 6.0
|
||||
* Requires PHP: 7.4
|
||||
* Author: FECA
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ trait AdminRequestHelpers
|
|||
. '.feca-mailshots-admin .feca-form .form-table th,.feca-mailshots-admin .feca-form .form-table td{padding-top:12px;padding-bottom:12px;}'
|
||||
. '.feca-mailshots-admin .feca-form .form-table th{width:220px;}'
|
||||
. '.feca-mailshots-admin .feca-form .description{margin-top:var(--feca-space-1);}'
|
||||
. '.feca-mailshots-admin .feca-password-control{display:flex;align-items:center;gap:8px;max-width:34rem;}'
|
||||
. '.feca-mailshots-admin .feca-password-control .regular-text{flex:1;min-width:220px;margin:0;}'
|
||||
. '.feca-mailshots-admin .feca-password-toggle{min-width:74px;text-align:center;}'
|
||||
. '.feca-mailshots-admin .feca-inline-form{display:inline;}'
|
||||
. '.feca-mailshots-admin .feca-hidden{display:none;}'
|
||||
. '.feca-mailshots-admin .feca-meta-line{margin:4px 0 var(--feca-space-3) 0;color:#6b7280;font-size:12px;}'
|
||||
|
|
|
|||
|
|
@ -46,16 +46,39 @@ final class ProfileAdminPage
|
|||
}
|
||||
|
||||
$uid = $this->wp->currentUserId();
|
||||
$saved = $uid > 0 ? ($this->repo()->findByUserId($uid) ?? []) : [];
|
||||
$saved = [];
|
||||
$loadError = '';
|
||||
if ($uid > 0) {
|
||||
try {
|
||||
$saved = $this->repo()->findByUserId($uid) ?? [];
|
||||
} catch (\Throwable $e) {
|
||||
$loadError = 'Unable to load stored profile credentials: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
$status = $this->wp->requestParam('saved', '') === '1' ? 'Profile credentials saved.' : '';
|
||||
$test = $uid > 0 ? $this->testResult($uid) : null;
|
||||
$action = htmlspecialchars($this->wp->adminUrl('admin-post.php'));
|
||||
$missingPasswords = $loadError === '' ? $this->missingPasswordLabels($saved) : [];
|
||||
|
||||
echo '<div class="wrap feca-mailshots-admin"><h1>Mailshot Profile</h1>';
|
||||
echo $this->renderAdminUiStyles();
|
||||
if ($loadError !== '') {
|
||||
echo '<div class="feca-banner feca-banner-error">' . htmlspecialchars($loadError) . '</div>';
|
||||
}
|
||||
if ($status !== '') {
|
||||
echo '<div class="feca-banner feca-banner-success">' . htmlspecialchars($status) . '</div>';
|
||||
}
|
||||
if ($missingPasswords !== []) {
|
||||
echo '<div class="feca-banner feca-banner-error">';
|
||||
echo '<strong>' . htmlspecialchars('Missing password configuration.') . '</strong>';
|
||||
echo '<p>' . htmlspecialchars('Enter and save the missing password before running mailshot tests or sends.') . '</p>';
|
||||
echo '<ul>';
|
||||
foreach ($missingPasswords as $label) {
|
||||
echo '<li>' . htmlspecialchars($label) . '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
echo '</div>';
|
||||
}
|
||||
if ($test !== null) {
|
||||
$ok = !empty($test['ok']);
|
||||
$kind = strtoupper((string) ($test['kind'] ?? 'CREDENTIAL'));
|
||||
|
|
@ -92,7 +115,7 @@ final class ProfileAdminPage
|
|||
$this->field('SMTP Host', 'smtp_host', $saved['smtp_host'] ?? '');
|
||||
$this->field('SMTP Port', 'smtp_port', (string) ($saved['smtp_port'] ?? ''));
|
||||
$this->field('SMTP User', 'smtp_user', $saved['smtp_user'] ?? '');
|
||||
$this->field('SMTP Password', 'smtp_password', '', 'password', 'Required on each save/test request');
|
||||
$this->field('SMTP Password', 'smtp_password', (string) ($saved['smtp_password'] ?? ''), 'password');
|
||||
$this->field('From Email', 'smtp_from_email', $saved['smtp_from_email'] ?? '');
|
||||
$this->field('From Name', 'smtp_from_name', $saved['smtp_from_name'] ?? '');
|
||||
|
||||
|
|
@ -105,7 +128,7 @@ final class ProfileAdminPage
|
|||
$this->field('IMAP Host', 'imap_host', $saved['imap_host'] ?? '');
|
||||
$this->field('IMAP Port', 'imap_port', (string) ($saved['imap_port'] ?? ''));
|
||||
$this->field('IMAP User', 'imap_user', $saved['imap_user'] ?? '');
|
||||
$this->field('IMAP Password', 'imap_password', '', 'password', 'Required on each save/test request');
|
||||
$this->field('IMAP Password', 'imap_password', (string) ($saved['imap_password'] ?? ''), 'password');
|
||||
$this->field('IMAP Sent Folder', 'imap_sent_folder', $saved['imap_sent_folder'] ?? '');
|
||||
$this->field('IMAP Mailbox Flags', 'imap_mailbox_flags', $saved['imap_mailbox_flags'] ?? '');
|
||||
echo '</table>';
|
||||
|
|
@ -116,7 +139,9 @@ final class ProfileAdminPage
|
|||
echo '<button type="submit" class="button" name="action" value="feca_mailshots_profile_test" formaction="' . $action . '" formmethod="post" title="Test IMAP login" onclick="this.form.test_kind.value=\'imap\';">Test IMAP</button>';
|
||||
echo '<input type="hidden" name="test_kind" value="">';
|
||||
echo '</p>';
|
||||
echo '</form></div>';
|
||||
echo '</form>';
|
||||
echo $this->renderPasswordToggleScript();
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
public function handleSave(): void
|
||||
|
|
@ -210,6 +235,8 @@ final class ProfileAdminPage
|
|||
'imap_mailbox_flags' => trim((string) ($this->wp->requestParam('imap_mailbox_flags', '') ?? '')),
|
||||
];
|
||||
|
||||
$payload = $this->applyStoredPasswordsForTest($uid, $payload);
|
||||
|
||||
$result = $kind === 'smtp' ? $this->runSmtpTest($payload) : $this->runImapTest($payload);
|
||||
$this->wp->updateOption($this->testResultOptionKey($uid), $result);
|
||||
|
||||
|
|
@ -225,13 +252,76 @@ final class ProfileAdminPage
|
|||
{
|
||||
echo '<tr>';
|
||||
echo '<th scope="row"><label for="' . htmlspecialchars($name) . '">' . htmlspecialchars($label) . '</label></th>';
|
||||
echo '<td><input class="regular-text" type="' . htmlspecialchars($type) . '" id="' . htmlspecialchars($name) . '" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value, ENT_QUOTES) . '">';
|
||||
echo '<td>';
|
||||
if ($type === 'password') {
|
||||
echo '<span class="feca-password-control">';
|
||||
echo '<input class="regular-text" type="password" autocomplete="off" id="' . htmlspecialchars($name) . '" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value, ENT_QUOTES) . '">';
|
||||
echo '<button type="button" class="button feca-password-toggle" data-feca-password-toggle="' . htmlspecialchars($name) . '" aria-controls="' . htmlspecialchars($name) . '" aria-pressed="false">' . htmlspecialchars('Show') . '</button>';
|
||||
echo '</span>';
|
||||
} else {
|
||||
echo '<input class="regular-text" type="' . htmlspecialchars($type) . '" id="' . htmlspecialchars($name) . '" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value, ENT_QUOTES) . '">';
|
||||
}
|
||||
if ($hint !== '') {
|
||||
echo '<p class="description">' . htmlspecialchars($hint) . '</p>';
|
||||
}
|
||||
echo '</td></tr>';
|
||||
}
|
||||
|
||||
private function renderPasswordToggleScript(): string
|
||||
{
|
||||
return <<<'HTML'
|
||||
<script>
|
||||
(function () {
|
||||
var buttons = document.querySelectorAll('[data-feca-password-toggle]');
|
||||
Array.prototype.forEach.call(buttons, function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
var fieldId = button.getAttribute('data-feca-password-toggle');
|
||||
var field = fieldId ? document.getElementById(fieldId) : null;
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
var visible = field.type === 'text';
|
||||
field.type = visible ? 'password' : 'text';
|
||||
button.setAttribute('aria-pressed', visible ? 'false' : 'true');
|
||||
button.textContent = visible ? 'Show' : 'Hide';
|
||||
});
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
HTML;
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $saved @return list<string> */
|
||||
private function missingPasswordLabels(array $saved): array
|
||||
{
|
||||
$missing = [];
|
||||
if (trim((string) ($saved['smtp_password'] ?? '')) === '') {
|
||||
$missing[] = 'SMTP password is missing.';
|
||||
}
|
||||
if (trim((string) ($saved['imap_password'] ?? '')) === '') {
|
||||
$missing[] = 'IMAP password is missing.';
|
||||
}
|
||||
return $missing;
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $payload @return array<string, mixed> */
|
||||
private function applyStoredPasswordsForTest(int $uid, array $payload): array
|
||||
{
|
||||
try {
|
||||
$saved = $this->repo()->findByUserId($uid) ?? [];
|
||||
} catch (\Throwable $e) {
|
||||
return $payload;
|
||||
}
|
||||
|
||||
foreach (['smtp_password', 'imap_password'] as $key) {
|
||||
if (trim((string) ($payload[$key] ?? '')) === '' && trim((string) ($saved[$key] ?? '')) !== '') {
|
||||
$payload[$key] = (string) $saved[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
private function repo(): MailCredentialRepository
|
||||
{
|
||||
return ($this->repoFactory)();
|
||||
|
|
|
|||
Loading…
Reference in New Issue