From a96324628ae2c1f4b4c77c0d56574d6253066c31 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Sun, 9 Aug 2026 13:27:51 -0400 Subject: [PATCH 1/6] Fix broken cron page due to the select * sweep, note thi sdid not break cron itself --- admin/cron.php | 4 +++- api/v1/credentials/read.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/admin/cron.php b/admin/cron.php index 620c6e528..b2556072f 100644 --- a/admin/cron.php +++ b/admin/cron.php @@ -22,7 +22,9 @@ foreach (cronJobRegistry() as $job) { $cron_jobs[$job['name']]['row'] = null; } -$sql = mysqli_query($mysqli, "SELECT cron_job_name FROM cron_jobs"); +$sql = mysqli_query($mysqli, "SELECT cron_job_daily_at, cron_job_enabled, cron_job_id, cron_job_interval_minutes, + cron_job_last_duration, cron_job_last_error, cron_job_last_error_at, cron_job_last_run_at, cron_job_last_status, + cron_job_name, cron_job_run_now, cron_job_schedule FROM cron_jobs"); while ($job_row = mysqli_fetch_assoc($sql)) { if (isset($cron_jobs[$job_row['cron_job_name']])) { $cron_jobs[$job_row['cron_job_name']]['row'] = $job_row; diff --git a/api/v1/credentials/read.php b/api/v1/credentials/read.php index 01ee1ce2e..584f6d8e9 100644 --- a/api/v1/credentials/read.php +++ b/api/v1/credentials/read.php @@ -25,7 +25,7 @@ if (isset($_GET['credential_id']) && !empty($api_key_decrypt_password)) { } elseif (!empty($api_key_decrypt_password)) { // All credentials ("credentials") - $sql = mysqli_query($mysqli, "SELECT credential_password, credential_username FROM credentials WHERE 1=1 " . apiClientScopeSql('credential_client_id') . " ORDER BY credential_id LIMIT $limit OFFSET $offset"); + $sql = mysqli_query($mysqli, "SELECT * FROM credentials WHERE 1=1 " . apiClientScopeSql('credential_client_id') . " ORDER BY credential_id LIMIT $limit OFFSET $offset"); } From f237993fd79678f01cc5891486185bb84e250a13 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Sun, 9 Aug 2026 13:29:46 -0400 Subject: [PATCH 2/6] DB Structure update for AI temperature --- db.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db.sql b/db.sql index e039a630c..c63684e88 100644 --- a/db.sql +++ b/db.sql @@ -3150,4 +3150,4 @@ CREATE TABLE `vendors` ( /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2026-08-02 17:22:01 +-- Dump completed on 2026-08-09 13:29:14 From de46184a5b6035693583a0a624591023d9e82867 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Sun, 9 Aug 2026 13:41:01 -0400 Subject: [PATCH 3/6] Fix calendar queries missing columns lost in the SELECT * sweep --- agent/calendar.php | 4 ++-- guest/guest_calendar_feed.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agent/calendar.php b/agent/calendar.php index 09ef36f8f..1d6444178 100644 --- a/agent/calendar.php +++ b/agent/calendar.php @@ -304,8 +304,8 @@ while ($row = mysqli_fetch_assoc($sql)) { }, events: [ Date: Sun, 9 Aug 2026 14:33:06 -0400 Subject: [PATCH 4/6] Fix Export Missing coluns due to the select star sweep --- admin/post/users.php | 2 +- agent/post/quote.php | 3 ++- agent/post/recurring_invoice.php | 4 +++- agent/post/software.php | 4 +++- agent/post/ticket.php | 4 +++- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/admin/post/users.php b/admin/post/users.php index b670ed8b3..55eaf280f 100644 --- a/admin/post/users.php +++ b/admin/post/users.php @@ -355,7 +355,7 @@ if (isExportRequest('export_users')) { $sql = mysqli_query( $mysqli, - "SELECT user_status FROM users + "SELECT role_name, user_created_at, user_email, user_name, user_status FROM users LEFT JOIN user_roles ON user_role_id = role_id WHERE (user_name LIKE '%$q%' OR user_email LIKE '%$q%') AND user_type = 1 diff --git a/agent/post/quote.php b/agent/post/quote.php index e41deae9e..ef62fd8d9 100644 --- a/agent/post/quote.php +++ b/agent/post/quote.php @@ -734,7 +734,8 @@ if (isExportRequest('export_quotes')) { $sql = mysqli_query( $mysqli, - "SELECT quote_number, quote_prefix FROM quotes + "SELECT client_name, quote_amount, quote_date, quote_number, quote_prefix, quote_scope, + quote_status FROM quotes LEFT JOIN clients ON quote_client_id = client_id LEFT JOIN categories ON quote_category_id = category_id WHERE (CONCAT(quote_prefix,quote_number) LIKE '%$q%' OR quote_scope LIKE '%$q%' OR category_name LIKE '%$q%' OR quote_status LIKE '%$q%' OR quote_amount LIKE '%$q%' OR client_name LIKE '%$q%') diff --git a/agent/post/recurring_invoice.php b/agent/post/recurring_invoice.php index 62b2c9462..63266ea16 100644 --- a/agent/post/recurring_invoice.php +++ b/agent/post/recurring_invoice.php @@ -671,7 +671,9 @@ if (isExportRequest('export_recurring_invoices')) { $sql = mysqli_query( $mysqli, - "SELECT recurring_invoice_frequency, recurring_invoice_number, recurring_invoice_prefix FROM recurring_invoices + "SELECT client_name, recurring_invoice_amount, recurring_invoice_created_at, + recurring_invoice_frequency, recurring_invoice_number, recurring_invoice_prefix, + recurring_invoice_scope FROM recurring_invoices LEFT JOIN clients ON recurring_invoice_client_id = client_id LEFT JOIN categories ON recurring_invoice_category_id = category_id WHERE (CONCAT(recurring_invoice_prefix,recurring_invoice_number) LIKE '%$q%' OR recurring_invoice_frequency LIKE '%$q%' OR recurring_invoice_scope LIKE '%$q%' OR client_name LIKE '%$q%' OR category_name LIKE '%$q%') diff --git a/agent/post/software.php b/agent/post/software.php index ec07d5892..f5914b1f7 100644 --- a/agent/post/software.php +++ b/agent/post/software.php @@ -292,7 +292,9 @@ if (isExportRequest('export_software')) { $sql = mysqli_query( $mysqli, - "SELECT software_id FROM software + "SELECT software_description, software_expire, software_id, software_key, software_license_type, + software_name, software_notes, software_purchase, software_seats, software_type, + software_version FROM software LEFT JOIN clients ON client_id = software_client_id LEFT JOIN vendors ON vendor_id = software_vendor_id WHERE (software_name LIKE '%$q%' OR software_type LIKE '%$q%' OR software_key LIKE '%$q%' OR client_name LIKE '%$q%') diff --git a/agent/post/ticket.php b/agent/post/ticket.php index 85513ed6f..479fd2aa9 100644 --- a/agent/post/ticket.php +++ b/agent/post/ticket.php @@ -2906,7 +2906,9 @@ if (isExportRequest('export_tickets')) { // Get records from database - same shape as the tickets page list query $sql = mysqli_query( $mysqli, - "SELECT category_name, ticket_billable, ticket_number, ticket_prefix, user_name FROM tickets + "SELECT category_name, client_name, contact_name, ticket_billable, ticket_closed_at, + ticket_created_at, ticket_number, ticket_prefix, ticket_priority, ticket_resolved_at, + ticket_status_name, ticket_subject, user_name FROM tickets LEFT JOIN clients ON ticket_client_id = client_id LEFT JOIN contacts ON ticket_contact_id = contact_id LEFT JOIN users ON ticket_assigned_to = user_id From 076f45abb04b04cb0ddca53bcc5297638c1ee8f4 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Sun, 9 Aug 2026 23:59:54 -0400 Subject: [PATCH 5/6] Remove Stale SMTP IMAP host if switching Standard SMTP.IMAP to OAUTH --- admin/post/settings_mail.php | 48 ++++++++++++++++++++++-------------- cron/mail_queue.php | 23 +++++++++++------ 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/admin/post/settings_mail.php b/admin/post/settings_mail.php index f22ffd61b..21a7df080 100644 --- a/admin/post/settings_mail.php +++ b/admin/post/settings_mail.php @@ -91,6 +91,18 @@ if (isset($_POST['edit_mail_smtp_settings'])) { $config_smtp_username = escapeSql($_POST['config_smtp_username'] ?? $config_smtp_username); $config_smtp_password = escapeSql($_POST['config_smtp_password'] ?? $config_smtp_password); + // The host/port/encryption/password inputs are hidden and disabled for OAuth + // providers, so they never post and the ?? fallbacks above keep whatever the + // install used before. Clear them instead: the endpoint is fixed by provider, + // and the stored values would otherwise be both wrong and unreachable from + // the settings page. The mailbox password is no longer used either. + if ($config_smtp_provider === 'google_oauth' || $config_smtp_provider === 'microsoft_oauth') { + $config_smtp_host = ''; + $config_smtp_port = 0; + $config_smtp_encryption = ''; + $config_smtp_password = ''; + } + mysqli_query($mysqli, " UPDATE settings SET config_smtp_provider = '$config_smtp_provider', @@ -121,6 +133,15 @@ if (isset($_POST['edit_mail_imap_settings'])) { $config_imap_username = escapeSql($_POST['config_imap_username'] ?? $config_imap_username); $config_imap_password = escapeSql($_POST['config_imap_password'] ?? $config_imap_password); + // Same as the SMTP handler above - the connection fields are hidden for OAuth + // providers and never post, so drop the leftovers rather than carrying them. + if ($config_imap_provider === 'google_oauth' || $config_imap_provider === 'microsoft_oauth') { + $config_imap_host = ''; + $config_imap_port = 0; + $config_imap_encryption = ''; + $config_imap_password = ''; + } + mysqli_query($mysqli, " UPDATE settings SET config_imap_provider = '$config_imap_provider', @@ -259,26 +280,17 @@ if (isset($_POST['test_email_imap'])) { $is_oauth = ($provider === 'google_oauth' || $provider === 'microsoft_oauth'); + // Override, don't default - a leftover standard-IMAP host from before the + // switch to OAuth would otherwise make this test fail against the old server + // while the cron parser (which overrides unconditionally) connects fine. if ($provider === 'google_oauth') { - if (empty($host)) { - $host = 'imap.gmail.com'; - } - if (empty($port)) { - $port = 993; - } - if (empty($encryption)) { - $encryption = 'ssl'; - } + $host = 'imap.gmail.com'; + $port = 993; + $encryption = 'ssl'; } elseif ($provider === 'microsoft_oauth') { - if (empty($host)) { - $host = 'outlook.office365.com'; - } - if (empty($port)) { - $port = 993; - } - if (empty($encryption)) { - $encryption = 'ssl'; - } + $host = 'outlook.office365.com'; + $port = 993; + $encryption = 'ssl'; } if (empty($host) || empty($port) || empty($username)) { diff --git a/cron/mail_queue.php b/cron/mail_queue.php index 9675d5c65..ebf483d33 100644 --- a/cron/mail_queue.php +++ b/cron/mail_queue.php @@ -198,16 +198,25 @@ function sendQueueEmail( string $oauth_access_token, string $oauth_access_token_expires_at ) { - // Sensible defaults for OAuth providers if fields were left blank + // Google and Microsoft each expose exactly one XOAUTH2 SMTP endpoint, so the + // stored host/port/encryption are overridden rather than used as a fallback - + // the same thing cron/ticket_email_parser.php already does on the IMAP side. + // + // These used to apply only when the columns were blank, which quietly broke + // every install that moved from Standard SMTP to OAuth: the old server is + // still sitting in config_smtp_host/port/encryption, the settings page hides + // those fields for OAuth providers so there is no way to clear them, and the + // send then pointed an M365 bearer token at the previous mail host. Only a + // fresh install, where the columns happened to be empty, ever worked. if ($provider === 'google_oauth') { - if (!$host) $host = 'smtp.gmail.com'; - if (!$port) $port = 587; - if (!$encryption) $encryption = 'tls'; + $host = 'smtp.gmail.com'; + $port = 587; + $encryption = 'tls'; if (!$username) $username = $from_email; } elseif ($provider === 'microsoft_oauth') { - if (!$host) $host = 'smtp.office365.com'; - if (!$port) $port = 587; - if (!$encryption) $encryption = 'tls'; + $host = 'smtp.office365.com'; + $port = 587; + $encryption = 'tls'; if (!$username) $username = $from_email; } From 41900191afb425344c892357f0bc477daf0bd24e Mon Sep 17 00:00:00 2001 From: johnnyq Date: Mon, 10 Aug 2026 11:33:38 -0400 Subject: [PATCH 6/6] Bump ITFlow version and update Changelog --- CHANGELOG.md | 15 +++++++++++++++ includes/app_version.php | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4b9d3283..bce08774c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ This file documents all notable changes made to ITFlow. +## [26.08.2] Maint Release + +### Upgrading to 26.08.2 + +Update the files from Settings > Update as normal. There is no database change in this release, so nothing else is required. + +### Bug Fixes +- Calendar: fixed the agent calendar showing no events. +- Calendar: shared calendar feeds set to publish busy blocks only were publishing full event titles, locations and descriptions to anyone holding the subscription link. +- Cron: fixed Maintenance > Cron failing to load. Scheduled jobs themselves were unaffected and kept running. +- Exports: restored the missing columns on the ticket, quote, recurring invoice, software and user exports. +- API: restored the full record on the credentials list endpoint. +- Mail: switching an existing install from Standard SMTP/IMAP to Microsoft 365 or Google OAuth no longer leaves the old mail server behind, which stopped sending and ticket email fetching from working. The connection settings for OAuth providers are now fixed by the provider and cleared on save. + + ## [26.08.1] Maint Release ### Upgrading to 26.08.1 diff --git a/includes/app_version.php b/includes/app_version.php index 188420bc1..84f04e0a3 100644 --- a/includes/app_version.php +++ b/includes/app_version.php @@ -5,4 +5,4 @@ * Update this file each time we merge develop into master. Format is YY.MM (add a .v if there is more than one release a month. */ -DEFINE("APP_VERSION", "26.08.1"); +DEFINE("APP_VERSION", "26.08.2");