Settings > Mail - sending (an SMTP server, or Google Workspace / * Microsoft 365 by OAuth), receiving the mailbox email to ticket reads, the * OAuth app both share, the addresses and names emails come from, tests, and * a preview of every email ITFlow sends. Each tab's form saves on its own * (admin/post/settings_mail.php), with the frame's save bar. Passwords, the * OAuth secret and its refresh token are never put back into the page: left * empty, they stay as saved. */ require_once "includes/inc_all_admin.php"; // ---- Tiny status dot for tab labels ---------------------------------------- function renderMailStatusDot($on) { return $on ? '' : ''; } $smtp_on = !empty($config_smtp_provider); $imap_on = !empty($config_imap_provider); $oauth_needed = in_array($config_smtp_provider, ['google_oauth', 'microsoft_oauth'], true) || in_array($config_imap_provider, ['google_oauth', 'microsoft_oauth'], true); // ---- Active tab ------------------------------------------------------------- // The tab lives in the URL (?tab=receiving) so it can be linked, bookmarked, survives a // reload, and lets the POST handlers send you back to the tab you saved from $mail_tabs = ['sending', 'receiving', 'oauth', 'from', 'tests', 'previews']; $active_tab = isset($_GET['tab']) && in_array($_GET['tab'], $mail_tabs, true) ? $_GET['tab'] : 'sending'; // A direct link to the OAuth tab reveals it even when no OAuth provider is selected yet if ($active_tab === 'oauth') { $oauth_needed = true; } // ---- OAuth callback URI (for Entra App Registration) ------------------------ if (defined('BASE_URL') && !empty(BASE_URL)) { $mail_oauth_callback_uri = rtrim((string) BASE_URL, '/') . '/admin/oauth_microsoft_mail_callback.php'; } else { $mail_oauth_callback_uri = 'https://' . rtrim((string) $config_base_url, '/') . '/admin/oauth_microsoft_mail_callback.php'; } // ---- Readiness checks (drive the Tests tab) -------------------------------- $smtp_standard_ready = !empty($config_smtp_host) && !empty($config_smtp_port) && !empty($config_mail_from_email) && !empty($config_mail_from_name); $smtp_oauth_ready = in_array($config_smtp_provider, ['google_oauth', 'microsoft_oauth'], true) && !empty($config_mail_from_email) && !empty($config_mail_from_name) && !empty($config_mail_oauth_client_id) && !empty($config_mail_oauth_client_secret) && !empty($config_mail_oauth_refresh_token) && ($config_smtp_provider !== 'microsoft_oauth' || !empty($config_mail_oauth_tenant_id)); $imap_standard_ready = !empty($config_imap_username) && !empty($config_imap_password) && !empty($config_imap_host) && !empty($config_imap_port); $imap_oauth_ready = in_array($config_imap_provider, ['google_oauth', 'microsoft_oauth'], true) && !empty($config_imap_username) && !empty($config_mail_oauth_client_id) && !empty($config_mail_oauth_client_secret) && !empty($config_mail_oauth_refresh_token) && ($config_imap_provider !== 'microsoft_oauth' || !empty($config_mail_oauth_tenant_id)); $oauth_provider_for_test = ''; if (in_array($config_imap_provider, ['google_oauth', 'microsoft_oauth'], true)) { $oauth_provider_for_test = $config_imap_provider; } elseif (in_array($config_smtp_provider, ['google_oauth', 'microsoft_oauth'], true)) { $oauth_provider_for_test = $config_smtp_provider; } $oauth_has_required_fields = !empty($oauth_provider_for_test) && !empty($config_mail_oauth_client_id) && !empty($config_mail_oauth_client_secret) && !empty($config_mail_oauth_refresh_token) && ($oauth_provider_for_test !== 'microsoft_oauth' || !empty($config_mail_oauth_tenant_id)); $send_ready = $smtp_standard_ready || $smtp_oauth_ready; $imap_ready = $imap_standard_ready || $imap_oauth_ready; // ---- What the fields offer ------------------------------------------------- // Sending or receiving through - off, a server signed in to with a password, or Google / Microsoft by OAuth $provider_choices = function ($server) { return [ '' => $server === 'smtp' ? 'Off - ITFlow sends no email' : 'Off - no mailbox is read', "standard_$server" => ($server === 'smtp' ? 'An SMTP server' : 'An IMAP server') . ' - with a username and password', 'google_oauth' => 'Google Workspace - signed in with OAuth', 'microsoft_oauth' => 'Microsoft 365 - signed in with OAuth', ]; }; // A server's encryption - and anything else saved long ago, kept as it is until changed $encryption_choices = function ($saved, $starttls_port, $ssl_port) { $choices = ['' => 'None', 'tls' => "STARTTLS - usually port $starttls_port", 'ssl' => "SSL/TLS - usually port $ssl_port"]; if ((string) $saved !== '' && !isset($choices[(string) $saved])) { $choices[(string) $saved] = (string) $saved; } return $choices; }; // A password or secret field says whether one is saved, never what it is $secret_placeholder = function ($saved, $empty) { return trim((string) $saved) !== '' ? 'Saved - type a new one to replace it' : $empty; }; // The OAuth app, as far as it is set up - and what to say about it where a provider uses it $oauth_app_set = !empty($config_mail_oauth_client_id) && !empty($config_mail_oauth_client_secret); $oauth_connected = $oauth_app_set && !empty($config_mail_oauth_refresh_token); $oauth_pointer = function ($server) use ($oauth_connected) { ob_start(); ?>
' . settingsInput("config_{$key}_from_email", $email, ['icon' => 'fa-envelope', 'type' => 'email', 'maxlength' => 200, 'label' => 'Address', 'placeholder' => $email_placeholder]) . '
' . settingsInput("config_{$key}_from_name", $name, ['icon' => 'fa-signature', 'maxlength' => 200, 'label' => 'Name', 'placeholder' => $name_placeholder]) . '
' . settingsSectionEnd(); }; // Whether email to ticket reads the Receiving mailbox if (!$config_module_enable_ticketing) { $parse_state = 'Ticketing is off in Modules.'; } elseif (!$config_ticket_email_parse) { $parse_state = 'Email to ticket is off - this mailbox isn\'t read until it is turned on in Tickets.'; } else { $parse_state = 'Email to ticket is on in Tickets.'; } ?>
Mail Queue and goes within a minute.') ?> 'config_smtp_provider', 'icon' => 'fa-paper-plane', 'label' => 'Send through', 'help' => '']) ?> 'fa-server', 'label' => 'Server', 'required' => true, 'maxlength' => 200, 'placeholder' => 'smtp.example.com']) ?>
'fa-plug', 'type' => 'number', 'label' => 'Port', 'required' => true, 'min' => 1, 'max' => 65535, 'inputmode' => 'numeric', 'placeholder' => '587']) ?>
'fa-lock', 'label' => 'Encryption']) ?>
'config_smtp_username', 'icon' => 'fa-user', 'label' => 'Username', 'maxlength' => 200, 'placeholder' => 'Usually the mailbox\'s own address', 'help' => '']) ?> 'fa-key', 'type' => 'password', 'label' => 'Password', 'maxlength' => 200, 'autocomplete' => 'new-password', 'placeholder' => $secret_placeholder($config_smtp_password, 'The mailbox\'s password, or an app password'), 'help' => 'Many hosts want an app password here rather than the account\'s own.']) ?>
'config_imap_provider', 'icon' => 'fa-inbox', 'label' => 'Read mail from', 'help' => '']) ?> 'fa-server', 'label' => 'Server', 'required' => true, 'maxlength' => 200, 'placeholder' => 'imap.example.com']) ?>
'fa-plug', 'type' => 'number', 'label' => 'Port', 'required' => true, 'min' => 1, 'max' => 65535, 'inputmode' => 'numeric', 'placeholder' => '993']) ?>
'fa-lock', 'label' => 'Encryption']) ?>
'config_imap_username', 'icon' => 'fa-user', 'label' => 'Username', 'maxlength' => 200, 'required' => true, 'placeholder' => 'tickets@example.com', 'help' => '']) ?> 'fa-key', 'type' => 'password', 'label' => 'Password', 'maxlength' => 200, 'autocomplete' => 'new-password', 'placeholder' => $secret_placeholder($config_imap_password, 'The mailbox\'s password, or an app password')]) ?>
'config_mail_oauth_client_id', 'icon' => 'fa-id-badge', 'label' => 'Client ID', 'maxlength' => 255, 'help' => 'Empty forgets the app - its secret and refresh token too.']) ?> 'config_mail_oauth_client_secret', 'icon' => 'fa-key', 'type' => 'password', 'label' => 'Client secret', 'maxlength' => 255, 'autocomplete' => 'new-password', 'placeholder' => $secret_placeholder($config_mail_oauth_client_secret, 'The secret\'s value'), 'help' => 'A secret runs out - put the new one here before it does.']) ?>
A refresh token is saved. time() ? ' What ITFlow signs in with now is good until ' . escapeHtml(date('g:i A', strtotime($config_mail_oauth_access_token_expires_at))) . ', then renewed.' : '' ?> Set up the app first. No refresh token yet.
'fa-sync-alt', 'type' => 'password', 'label' => 'Refresh token', 'autocomplete' => 'new-password', 'placeholder' => $secret_placeholder($config_mail_oauth_refresh_token, 'Paste one in'), 'help' => '']) ?>
Each address has to be one the sending mailbox may send as - itself, an alias of it, or a shared mailbox it can send as.
Mail Queue - waiting retrying failed in the last day Open the Mail Queue
Finish configuring Sending, Receiving, or OAuth (plus at least one From address) to unlock the tests.
Send a Test Email
Test IMAP Connection
Test OAuth Token Refresh

Validates the refresh token and stores a new access token for .

$preview_entry) { $preview_groups[$preview_entry['group']][$preview_key] = $preview_entry; } $preview_first = isset($_GET['email'], $preview_catalog[$_GET['email']]) ? (string) $_GET['email'] : (string) array_key_first($preview_catalog); ?>

Every email ITFlow sends, as it goes out - your logo, your details and your From addresses. Each is made just now on sample records (, its contact and an agent, ) that are thrown away again, so looking sends and saves nothing.

$preview_entries) { ?>
$preview_entry) { ?>
From
To
Calendar file attached